Commit Graph

18 Commits

Author SHA1 Message Date
Teknium d63e380324 compat(plugins): warn once per name when a plugin resolves an old import path; lint step restored in CI
Every PLUGIN-COMPAT __getattr__ now calls hermes_cli.plugin_compat.warn_once(facade, name, target) before
resolving, emitting a HermesPluginCompatWarning (FutureWarning) once per process per name: old path, new
path, removal target. Importing a facade for its live API stays silent; only resolving a moved name warns.
COMPAT_MANIFEST.md documents the warning and how to silence it during migration.

Verified the runtime never routes through a pointer: every entry point (run_agent, cli, hermes_cli.main,
gateway.run, tui_gateway.server, web_server, model_tools + tool discovery, hermes_state, cron.scheduler,
browser_tool, mcp_tool, kanban, auth) imports clean and `hermes doctor` runs end to end with the warning
promoted to an error.

Also restores the check_compat_pointers CI step to .github/workflows/lint.yml, which a0be177aac dropped
when the compat layer was regenerated (the lint script itself was present; the workflow step was not).

hermes_cli/plugin_compat.py, tests/test_plugin_compat_warning.py and the two-line insert per facade are
part of the compat layer and go away with it.
2026-09-04 00:15:16 -07:00
Teknium 2776813df3 compat(plugins): temporary import-path shims for external plugins — ONE commit, revert on schedule
The Sep 2026 decomposition (PR #102117) makes internal import paths a non-API: names now live in
the focused modules that define them. This commit is the ONLY thing keeping the old paths alive,
so external plugins have time to update. It is deliberately a single, unsquashed commit:

    git revert <this sha>

removes every shim, stub and manifest at once on the announced date. Nothing in-tree may depend on
these pointers: scripts/check_compat_pointers.py (wired into lint.yml) fails CI if it does.

What it adds (see COMPAT_MANIFEST.md, compat_manifest.json):
- 332 facade modules get one delimited `PLUGIN-COMPAT` block appended at the end of the file
- 1,172 moved names resolved lazily via a module `__getattr__` (PEP 562) — never a top-level import,
  so no import cycles; facades that already had `__getattr__` get a chained one
- 592 third-party/stdlib names the old modules used to expose, with their original import statements
- 266 public definitions that had been deleted as unused, restored byte-for-byte from the pre-decomposition
  tree (+40 private helpers and 16 imports pulled in only because a restored definition needs them)
- 3 deleted modules recreated as re-export stubs (gateway/startup_watchdog, hermes_cli/observability/
  relay_runtime, tools/environments/modal_utils)
- private names (`_x`) get no pointer: they were never API (3,792 skipped)

Verified: all 335 touched modules import under a fresh HERMES_HOME and every manifest name resolves;
the lint reports zero in-tree uses; ruff clean; targeted suites unchanged.
2026-09-03 17:13:22 -07:00
Teknium b610e603db simplify(compat): plugins/platforms+web — drop 7 re-exports + 3 aliases, repoint 1 caller + 11 tests, re-remove credential_summary
dingtalk: drop DINGTALK_TYPE_MAPPING/EXT_MAP re-exports. google_chat: card_spec_to_cards_v2 test -> .cards.
matrix: drop module-level MAX_MESSAGE_LENGTH alias (no importers). teams: drop TeamsSummaryWriter re-export
(teams_pipeline/runtime + tests -> summary_writer). wecom: drop WeComStreamExpiredError/STREAM_EXPIRED_ERRCODE/
MAX_INTERMEDIATE_FRAMES re-exports (tests -> .streaming). parallel: drop _get_parallel_client/_get_async_parallel_client
aliases (tests -> _get_sync_client). email: drop stale 'alias' comment (_esecret_int is the only name).
photon: re-remove credential_summary() (shim-only, cb9b7c36f3); its no-leak test now drives print_credential_summary.
2026-09-03 13:04:17 -07:00
Teknium 44023a3d23 refactor(plugins/teams_pipeline,spotify,security-guidance): phase-split pipeline, dedupe Graph/Spotify clients, compact docs (3140->2631 LOC, --help/schema/pattern parity verified) 2026-09-03 00:33:14 -07:00
Teknium eb60dd519e refactor(plugins/teams_pipeline): subcommand dispatch, unified download/Graph helpers, compact models 2026-09-02 13:30:10 -07:00
Gille 7c5c994397 fix(teams): request supported transcript content format 2026-08-26 02:54:35 +05:30
xxxigm b577353636 fix(teams-pipeline): parse Graph users('id') meeting paths
getAllTranscripts @odata.id often uses users('{organizer}')/onlineMeetings('{id}'),
which the slash-only parser missed, so new jobs still stored the transcript id
and hit the refuse-GET guard. Accept the quoted-user form and re-parse the
stored notification on run so replay picks the meeting id.
2026-08-21 12:15:36 +05:30
Teknium d03fe2adf4 fix: detect base64-encoded transcript markers in Graph ids
The getAllTranscripts resourceData.id from the field report is a base64url
blob whose DECODED payload ends in "-TranscriptV2" while the encoded form
contains no readable marker, so the substring heuristic in
looks_like_transcript_id missed it. Add a best-effort base64 decode hint so
degraded notifications (no @odata.id) are still refused with the clear
guidance error instead of a cryptic Graph 400.
2026-08-18 12:37:40 -07:00
kyssta-exe db004d1801 fix(teams-pipeline): support organizer-scoped meeting lookup (#83422) 2026-08-18 12:37:40 -07:00
xxxigm 182a96645a fix(teams-pipeline): resolve Graph transcript notifications to the meeting id
getAllTranscripts webhooks put the callTranscript id in resourceData.id. Using that as an onlineMeeting id makes Graph v1.0 return 400 Unexpected id format. Parse meeting and organizer ids from @odata.id and use the organizer-scoped users path.
2026-08-18 12:37:40 -07:00
Teknium ebd61ce5ac fix(secrets): scope tier-3 credential reads (teams_pipeline, deepinfra models, FAL/XAI/VERCEL/DAYTONA/GITHUB presence, HERMES_API_KEY display)
NOTION_API_KEY/LINEAR_API_KEY defaults, DEEPINFRA_API_KEY model gate, FAL_KEY via the file's own _scoped_credential helper, XAI/VERCEL/DAYTONA presence checks, GITHUB_TOKEN/GH_TOKEN + GitHub App creds in tirith/skills_hub, and the masked HERMES_API_KEY display in tui_gateway config.show all route through get_secret.
2026-08-02 10:04:48 -07:00
Teknium 259e6b87a7 fix(teams-pipeline): reject dot-only recording display_name
Path(raw).name reduces '..'/'.'/'' to themselves, so basename
extraction alone still let a Graph-provided display_name of '..' or
'../' escape the temp recording directory (tmp_dir / '..' resolves to
the parent). Reject the dot-only basenames explicitly and fall back to
the artifact id. Extends @outsourc-e's regression coverage with the
dot-only cases.
2026-07-01 02:03:48 -07:00
memosr 3590543312 fix(security): strip directory components from Teams recording display_name to prevent path traversal 2026-07-01 02:03:48 -07:00
kshitijk4poor 66827f8947 chore: prune unused imports and duplicate import redefinitions
Remove unused imports (F401) and duplicate/shadowed import
redefinitions (F811) across the codebase using ruff's safe
autofixes. No behavioral changes -- imports only.

- ~1400 safe autofixes applied across 644 files (net -1072 lines)
- __init__.py re-exports preserved (excluded from F401 removal so
  public re-export surfaces stay intact)
- Re-exports that are imported or monkeypatched by tests but look
  unused in their defining module are kept with explicit # noqa:
  F401 (gateway/run.py load_dotenv; run_agent re-exports from
  agent.message_sanitization, agent.context_compressor,
  agent.retry_utils, agent.prompt_builder, agent.process_bootstrap,
  agent.codex_responses_adapter)
- Unsafe F841 (unused-variable) fixes deliberately skipped -- those
  can change behavior when the RHS has side effects
- ruff lints remain disabled in pyproject.toml (only PLW1514 is
  selected); this is a one-time cleanup, not a config change

Verification:
- python -m compileall: clean
- pytest --collect-only: all 27161 tests collect (zero import errors)
- core entry points import clean (run_agent, model_tools, cli,
  toolsets, hermes_state, batch_runner, gateway)
- static scan: every name any test imports directly from an edited
  module still resolves
2026-05-28 22:26:25 -07:00
kshitij 5fba236644 chore: ruff auto-fix PLR6201 resweep — tuple → set in membership tests (#27355)
Six days after #23937 (608 fixes) the codebase had accumulated 241 new
PLR6201 violations. Same mechanical `x in (...)` → `x in {...}` fix,
same zero-risk profile: set lookup is O(1) vs O(n) for tuple and the
two are semantically equivalent for hashable scalar membership tests.

All 241 instances fixed via `ruff check --select PLR6201 --fix
--unsafe-fixes`, zero remaining. Every changed value is a hashable
scalar (str/int/None/enum/signal); no risk of unhashable runtime
errors. No behavior change.

Test plan:
- 119 files changed, +244/-244 (net zero) — exactly one-line edits
- `ruff check` clean afterward
- Compile checks pass on the largest touched files (cli.py, run_agent.py,
  gateway/run.py, gateway/platforms/discord.py, model_tools.py)
- Subset broad test run on tests/gateway/ tests/hermes_cli/ tests/agent/
  tests/tools/: 18187 passed, 59 pre-existing failures (verified against
  origin/main with the same shape — identical failure count, identical
  category — all xdist test-order flakes unrelated to this change)

Follows the same template as PR #23937 ([tracker: #23972](https://github.com/NousResearch/hermes-agent/issues/23972)).
2026-05-17 02:29:41 -07:00
Dilee 397f750bb4 feat(teams): add pipeline outbound delivery via existing adapter 2026-05-08 12:00:09 -07:00
Teknium a99547740d fix(teams-pipeline): drop-scheduler fallback + test wiring for enablement gate
Two salvage follow-ups on top of @dlkakbs's plugin runtime.

1. Install a drop-scheduler when the runtime fails to build.

   Previously when ``build_pipeline_runtime()`` raised (e.g. missing
   Graph env vars, subscription store path unwritable), ``bind_gateway_runtime``
   logged a warning and returned False, leaving the msgraph_webhook
   adapter with no scheduler at all. Incoming Graph notifications
   would then fall back to the adapter's default ``handle_message``
   path, which produces a raw JSON dump as a user-role message — not
   useful and fires every time Graph retries.

   Now a no-op drop-scheduler is installed instead, so:
   - Graph notifications ack cleanly (202) so Graph stops retrying.
   - The failure is surfaced once in the log with the error.
   - No user-role messages get manufactured from raw change payloads.

   The adapter is still bindable later once the runtime becomes
   available (e.g. after the operator runs ``hermes teams-pipeline
   validate`` and fixes the config), since the gateway's
   ``_teams_pipeline_runtime`` sentinel wasn't set to a non-None value.

2. Test wiring for ``_teams_pipeline_plugin_enabled()`` gate.

   The happy-path runner-wiring tests monkeypatched ``bind_gateway_runtime``
   but not ``_load_gateway_config``. In the hermetic test environment
   the real config read ran, saw no enabled plugins, and short-circuited
   the bind call before the test could observe it — so the test
   expected ``calls == [runner]`` but got ``calls == []``.

   Adds a ``_load_gateway_config`` monkeypatch with
   ``plugins.enabled = ["teams_pipeline"]`` to the happy-path tests.
   The explicit-disabled test ``test_gateway_runner_skips_wiring_when_teams_pipeline_plugin_disabled``
   already patches the config correctly.

   Also renames ``test_bind_gateway_runtime_leaves_scheduler_unchanged_on_failure``
   to ``test_bind_gateway_runtime_installs_drop_scheduler_on_failure``
   and updates the assertion — this test contradicted the drop-scheduler
   test in ``tests/plugins/test_teams_pipeline_plugin.py`` which
   expected the scheduler to be installed. The plugin-test name
   (``test_bind_gateway_runtime_drops_notifications_when_unavailable``)
   clearly describes the intended behavior; fixing the wiring-test
   assertion aligns both tests.

Validation:
- ``scripts/run_tests.sh tests/plugins/test_teams_pipeline_plugin.py
  tests/gateway/test_teams_pipeline_runtime_wiring.py
  tests/hermes_cli/test_teams_pipeline_plugin_cli.py`` — 25/25 passed.
2026-05-08 11:18:14 -07:00
Dilee 07bbd93337 feat(teams-pipeline): add plugin runtime and operator cli
Third slice of the Microsoft Teams meeting pipeline stack, salvaged
onto current main. Adds the standalone teams_pipeline plugin that
consumes Graph change notifications from the webhook listener,
resolves meeting artifacts (transcript first, recording + STT fallback
later), persists job state in a durable store, and exposes an operator
CLI for inspection, replay, subscription management, and validation.

Design choices follow maintainer review feedback on PR #19815:

- Standalone plugin rather than bolted-on core surface
  (plugins/teams_pipeline/, kind: standalone in plugin.yaml).
- Zero new model tools. The agent drives the pipeline by invoking
  the operator CLI via the terminal tool, guided by the skill that
  ships with a follow-up PR.
- Reuses the existing msgraph_webhook gateway platform for Graph
  ingress. Pipeline runtime is wired in via bind_gateway_runtime and
  gated on plugins.enabled so gateways that don't run the plugin
  boot cleanly.

Additions:

- plugins/teams_pipeline/: runtime (gateway wiring + config builder),
  pipeline core, durable SQLite store, subscription maintenance
  helpers, Graph artifact resolution, operator CLI (list, show,
  run/replay, fetch dry-run, subscriptions list, subscribe,
  renew-subscription, delete-subscription, maintain-subscriptions,
  token-health, validate).
- hermes_cli/main.py: second-pass plugin CLI discovery so any
  standalone plugin registered via ctx.register_cli_command()
  outside the memory-plugin convention path gets its subcommand
  wired into argparse without touching core.
- gateway/run.py: _teams_pipeline_plugin_enabled() config gate,
  _wire_teams_pipeline_runtime() binding after adapter setup, and
  the two runner attributes used by the runtime.

Credit to @dlkakbs for the entire plugin implementation.
2026-05-08 11:18:14 -07:00