Commit Graph

863 Commits

Author SHA1 Message Date
Teknium 94e23fe6bf Merge branch 'simp/r3-29-F' into simp/integration3 2026-09-02 22:41:06 -07:00
Teknium aa9143f040 Merge branch 'simp/r3-29-E' into simp/integration3 2026-09-02 22:41:05 -07:00
Teknium a833865ecf Merge branch 'simp/r3-29-D' into simp/integration3 2026-09-02 22:41:03 -07:00
Teknium adada20660 Merge branch 'simp/r3-29-C' into simp/integration3 2026-09-02 22:41:02 -07:00
Teknium 87a33b39f5 Merge branch 'simp/r3-29-B' into simp/integration3 2026-09-02 22:41:01 -07:00
Teknium 792eb4a3c1 Merge branch 'simp/r3-29-A' into simp/integration3 2026-09-02 22:41:00 -07:00
Teknium 7fa82c1f69 refactor(matrix): dedupe device-key queries, room-identity invalidation, inbound MessageEvent build, reaction handlers; compact E2EE/connect log calls (3737->3327) 2026-09-02 22:40:07 -07:00
Teknium 625644d8f4 refactor(photon): auth.py 814->742; _raise_on_error_key helper (4 sites), flatten token-candidate collection, compact docstrings 2026-09-02 22:39:49 -07:00
Teknium 47117f51ba refactor(line): batch-loop outbound sends, unify env credential check 2026-09-02 22:36:24 -07:00
Teknium f58da7c156 refactor(sms,google_chat): compact module docstrings (env tables kept) 2026-09-02 22:32:44 -07:00
Teknium 7f9a9b84a3 refactor(google_chat): oauth _write_private_json reuses utils.atomic_write_text 2026-09-02 22:29:00 -07:00
Teknium a5d9baeebf refactor(line): flatten postback delivery state machine 2026-09-02 22:28:53 -07:00
Teknium dcb0ee4665 refactor(photon): adapter.py 1848->1656; drop base-equivalent send_animation, collapse defensive layers, compact comments/docstrings 2026-09-02 22:28:19 -07:00
Teknium c847ab67c6 refactor(simplex,teams): AST-neutral bracket layout compaction 2026-09-02 22:21:52 -07:00
Teknium a6f1d3cd0a refactor(homeassistant): shared _close for ws/session teardown 2026-09-02 22:21:45 -07:00
Teknium 50e588d18c refactor(teams): extract attachment caching + approval card body helpers, compact docs 2026-09-02 22:19:04 -07:00
Teknium 15fad75a29 refactor(raft,mattermost): _authorized helper, class as adapter_factory; inline thread-root resolution, media-type table, hoisted imports 2026-09-02 22:19:02 -07:00
Teknium 270c9626e0 refactor(irc): _handle_privmsg extraction, _MARKDOWN_RULES table, _fail/_sa_error/_encode_line helpers, class as adapter_factory 2026-09-02 22:19:01 -07:00
Teknium 2a387b246c refactor(slack): dedupe interactive prompt senders, extract inbound phase helpers, drop dead wrappers, hug brackets 2026-09-02 22:17:45 -07:00
Teknium 843adc5520 refactor(homeassistant,sms,google_chat): HA template table for state descriptions; drop single-use wrappers 2026-09-02 22:15:02 -07:00
Teknium 2b14ffe0cb refactor(ntfy): shared publish-header builder, compact module docstring 2026-09-02 22:07:53 -07:00
Teknium 89b232df4c refactor(simplex): dict-dispatch daemon events, unify display-name lookups, compact docs 2026-09-02 22:05:16 -07:00
Teknium 38845e0820 refactor(mattermost): shared _post_with_file/_auth_header/_url_filename helpers, class as adapter_factory, compact layout 2026-09-02 22:02:11 -07:00
Teknium c1cbdce416 refactor(google_chat): setup_files — shared helper runner + per-user creds slot setter 2026-09-02 21:58:44 -07:00
Teknium e15793580f Merge branch 'simp/r3-29-F' into simp/integration3 2026-09-02 21:58:43 -07:00
Teknium 8821e2f922 Merge branch 'simp/r3-29-D' into simp/integration3 2026-09-02 21:58:42 -07:00
Teknium 8e86877437 refactor(line): AST-neutral bracket layout compaction 2026-09-02 21:55:05 -07:00
Teknium d9d1aa4468 refactor(photon): AST-neutral layout compaction (hug closers/brackets) 3292->3188 LOC 2026-09-02 21:54:23 -07:00
Teknium d145686f26 refactor(line): table-driven markdown strip, collapse defensive layers and comment sprawl 2026-09-02 21:52:45 -07:00
Teknium 6406cfe408 refactor(google_chat): oauth — unify refresh paths, token-path helper, chmod helper, drop dead hermes_constants shim 2026-09-02 21:46:45 -07:00
Teknium d0ee4b3017 refactor(google_chat): cards — shared optional-key copy + required-list helpers, section/header renderers 2026-09-02 21:43:26 -07:00
Teknium 78453b958d refactor(sms): shared session factory, precompiled markdown sub table, drop _build_adapter shim 2026-09-02 21:37:04 -07:00
Teknium ede7bf1869 refactor(homeassistant): domain describer dispatch table, shared session factory, drop _build_adapter shim 2026-09-02 21:31:40 -07:00
Teknium 0bc8d4e2d3 refactor(ntfy): fold fatal-status handling into one helper, dedupe headers/env seeding, compact layout 2026-09-02 21:17:47 -07:00
Teknium 4d1880e0bf fix(integration): restore subprocess encoding/stdin guards dropped in simplification
Simplification workers collapsed subprocess call sites into shared kwargs
helpers and dropped the Windows/TUI safety kwargs on the way:

- encoding='utf-8', errors='replace' restored on text=True runs in
  copilot_acp_client, hermes_cli/setup (vercel install), managed_uv
  (codesign steps), local_runtime/hardware._stdout, a2a adapter.
- stdin=subprocess.DEVNULL restored on copilot probe, verify/runner
  _SUBPROCESS_KW, iron_proxy._run, google_meet playwright/system_profiler,
  simplex convert, whatsapp _RUN_TEXT, mem0 ollama serve Popen.
  google_meet sudo/brew install keeps inherited stdin (user-confirmed,
  may prompt) — marked noqa: subprocess-stdin.
- Windows-safe SIGKILL: getattr(signal, 'SIGKILL', SIGTERM) in
  verify/runner; photon _kill call re-marked windows-footgun: ok
  (unreachable on win32).
- scripts/check_subprocess_stdin.py now recognizes **kwargs splats
  (**_KW / **_kw(...)) ONLY when the same-file definition provably sets
  stdin= — covers tui_gateway _capture_run_kwargs/run_kw. Parity test added.
2026-09-02 16:36:10 -07:00
Teknium ea6644e1dc fix(integration): telegram dm-topic persist reads config.yaml via read_user_config_raw()
Raw yaml.safe_load of config.yaml is only legal in owner modules; the
write-back round-trip uses the sanctioned raw primitive. Behavior-identical
(same file, same parse, {} on missing).
2026-09-02 15:41:16 -07:00
Teknium a07dceb01f refactor(adapters/small_group): 5147->3565; line/email/ntfy/homeassistant/sms send-family and standalone-send dedupe, god-method extraction, dead find_pending_for_chat removed 2026-09-02 14:06:48 -07:00
Teknium c4f96565d0 refactor(adapters/whatsapp_webhook): 6449->4465; whatsapp_cloud/common/plugin send + media unification, webhook route/filter dispatch tables 2026-09-02 14:06:38 -07:00
Teknium 791417590d refactor(adapters/matrix_group): 9272->6311; matrix dead fetch_history/_send_simple_message/HTMLParser stubs removed, simplex/mattermost/irc one-verb API helpers and send builders 2026-09-02 14:06:37 -07:00
Teknium 8f1ef66b1f refactor(adapters/p2p_group): 12752->8794; buzz/photon/a2a/raft dedupe (sidecar paths unified in photon package, JSON-RPC helpers, Nostr event builders), dead a2a security getters + stream_message removed 2026-09-02 14:06:36 -07:00
Teknium b8bea7199c refactor(adapters/cn_group): 12368->8549; wecom split into streaming/media/send_queue mixins, dingtalk inbound parsers -> inbound.py, google_chat cards.py + setup_files.py, teams summary_writer.py; dedupe token/URL/credential helpers 2026-09-02 14:06:34 -07:00
Teknium 513ba5b846 refactor(adapters/feishu): 8095->5665; unify request/response wrappers, card builders, comment timeline selectors; dead symbols dropped; compact docs 2026-09-02 14:06:33 -07:00
Teknium 33c74a26c6 refactor(adapters/slack): 10667->7456; drop F811 _ssrf_redirect_guard shadow, split _handle_slack_message_impl (1135->~500) and connect(), unify Block Kit handlers, client/DM lookups, ts-eviction, upload retry; compact docs 2026-09-02 14:06:33 -07:00
Teknium 6e5b084b8b refactor(adapters/discord): 11250->7733; _HermesView base for 6 component views (1074-line factory -> 634), _send_prompt/_resolve_channel/_send_url_media/_send_local_file unification, opus loading and setup helpers extracted, dead _discord_allow_any_attachment removed 2026-09-02 14:06:32 -07:00
Teknium cc11c6bfdd refactor(adapters/telegram): 11957->8819; unify send/edit retry classification, media caching, auth gates, model-picker branches, polling recovery; compact docs 2026-09-02 14:06:32 -07:00
Teknium 001b8abbd4 fix(matrix): pin the E2EE crypto store per profile at connect(), not import
The multiplex gateway imports plugins/platforms/matrix/adapter.py once, so
the module-level _STORE_DIR/_CRYPTO_DB_PATH resolved against the root
HERMES_HOME for every profile: all bots' Olm identities landed in one
crypto.db and inbound E2EE failed with "no session found" (#89168).

connect() runs inside _profile_runtime_scope, so resolve the store dir
there via get_hermes_dir (honors the context-local HERMES_HOME) and cache
it on the instance -- diagnostics and error-log paths read outside the
scope then still report the store actually in use. Mirrors the
pairing-store fix (a6397c379).

Salvage of #89169 (per-call resolvers collapsed into one cached resolve;
dead `_CRYPTO_DB_PATH = None` alias dropped -- no external importers).
Also routes the last raw MATRIX_HOMESERVER read in check_matrix_requirements
through _startup_env_secret like its token/password neighbours (#69943).

Fixes #89168

Co-authored-by: Michael Short <18595461+mjshorty@users.noreply.github.com>
2026-09-02 07:01:23 -07:00
Teknium 9be1168cd3 fix(wecom): scope WECOM_WEBSOCKET_URL like its neighbours
Same class as #100627's WECOM_BOT_ID: the one remaining raw os.getenv in
WeComAdapter.__init__ let a secondary multiplex profile pick up the
default profile's bridged websocket URL. Route it through
_get_scoped_secret; folded into the existing scoped-miss test.
2026-09-02 07:01:23 -07:00
Teknium 2bcbdb61a7 fix(simplex): scope SIMPLEX_* reads to the active profile under multiplexing
SimplexAdapter.__init__ (auto_accept, group_allowed), the registry gates
check_requirements/validate_config/is_connected, _env_enablement and
_standalone_send all read SIMPLEX_* via raw os.getenv. Under
gateway.multiplex_profiles those paths run inside a secondary profile's
scope where os.environ holds the DEFAULT profile's YAML-to-env bridge
output -- so a secondary profile that never configured SimpleX was
auto-enabled on the default's daemon URL and inherited its group
allowlist / auto-accept setting.

Route every read through the module-local `_get_scoped_secret` wrapper
(get_secret; UnscopedSecretError -> os.getenv for the default profile,
which constructs unscoped) -- the same helper the IRC/ntfy/Photon/
Mattermost siblings use. Unlike the extra-only `_scoped_platform_setting`
shape proposed in #100241, this honors BOTH the secondary profile's own
.env (the scope) and its config.yaml extra, and needs no config.yaml
re-read in check_requirements.

Rewrite of #100241.

Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
2026-09-02 07:01:23 -07:00
nftpoetrist 07ee457a21 fix(wecom): scope WECOM_BOT_ID reads to the active profile under multiplexing
WeComAdapter.__init__ read WECOM_BOT_ID via a raw os.getenv() call, while
the immediately adjacent line for WECOM_SECRET already used the module's
_get_scoped_secret() helper. Under gateway.multiplex_profiles, a secondary
profile's adapter is constructed inside a scoped context where os.environ
still holds the DEFAULT profile's env-bridge output -- so a secondary
profile's bot would silently connect using the default profile's bot_id
while (correctly) using its own secret, or vice versa on a scope miss.

Switch the bot_id read to _get_scoped_secret(), matching the sibling
_secret/_dm_policy/_group_policy/allow_from reads in the same __init__
that were already migrated in #76664/#93545. _standalone_send's
out-of-process fallback branch constructs a fresh WeComAdapter(pconfig)
and therefore inherits this fix automatically -- no separate change
needed there.

Adds two regression tests to the existing TestWeComAdapterAuthzScope
class (already covering dm_policy/allow_from scoping per #93522),
mirroring its established fixture/assertion style. Mutation-verified:
both fail against the pre-fix code (asserting the default profile's
bot_id leaks into a secondary profile's scope) and pass with the fix.
2026-09-02 07:01:23 -07:00
nftpoetrist 56d869d2d9 fix(mattermost): scope url/reply_mode/require_mention/free_response_channels/allowed_channels to the active profile under multiplexing
MattermostAdapter.__init__, validate_mattermost_config, _standalone_send,
and _handle_ws_event's mention-gating block all read MATTERMOST_URL/
MATTERMOST_REPLY_MODE/MATTERMOST_REQUIRE_MENTION/
MATTERMOST_FREE_RESPONSE_CHANNELS/MATTERMOST_ALLOWED_CHANNELS via raw
os.getenv -- only MATTERMOST_TOKEN was already scoped via
_get_scoped_secret. _apply_yaml_config additionally wrote
MATTERMOST_REQUIRE_MENTION/MATTERMOST_FREE_RESPONSE_CHANNELS/
MATTERMOST_ALLOWED_CHANNELS into the process-global os.environ
unconditionally (guarded only by `not os.getenv(...)`, first-writer-wins),
the same apply_yaml_config_fn bug class already fixed for the
Discord/Telegram/WhatsApp/DingTalk adapters in this series.

Under gateway.multiplex_profiles, os.environ holds the DEFAULT profile's
env-bridge output. A secondary profile with its own (or no) Mattermost
config could silently connect to the default profile's server, thread
its replies per the default profile's reply_mode, or -- since
_handle_ws_event's mention-gating block runs on every LIVE inbound
message, not just at construction -- have its require_mention/
free_response_channels/allowed_channels decisions driven by the default
profile's settings for the adapter's entire runtime lifetime.

Fix, mirroring the WhatsApp/DingTalk apply_yaml_config_fn pattern:
- Add _profile_scoped_config_load() (same helper as DingTalk).
- Rewrite _apply_yaml_config to skip the env-bridge write under a
  multiplexed secondary profile's scope, and instead return the YAML
  values as a dict merged into this profile's own PlatformConfig.extra.
- Make require_mention/free_response_channels read extra first (matching
  the existing allowed_channels precedent), falling back to
  _get_scoped_secret() instead of raw os.getenv when extra is absent --
  fixing a residual gap the DingTalk fix (#100615, this series' item 6)
  left in its own analogous extra-first-with-raw-fallback read sites
  (_dingtalk_require_mention et al. still fall back to bare os.getenv).
- Switch __init__'s url/reply_mode, validate_mattermost_config's url, and
  _standalone_send's url to _get_scoped_secret().
- Leave check_mattermost_requirements() (no longer reads any MATTERMOST_*
  var on current main -- just an aiohttp-importability probe) and
  _is_connected() (already scope-aware via hermes_cli.gateway.get_env_value,
  which itself routes through agent.secret_scope.get_secret) untouched.

Adds a new TestMultiplexProfileScope class to tests/gateway/test_mattermost.py
(7 tests) mirroring the fixture/assertion style established in
tests/gateway/test_line_plugin.py's TestMultiplexProfileScope, plus two
tests exercising _apply_yaml_config's new seeded-dict return directly.
Mutation-verified: stashed the production fix and confirmed 5 of 7 new
tests fail against pre-fix code (the other 2 are non-differentiating
regression guards -- extra-wins-over-env and unscoped-default-profile-
precedence -- which correctly pass either way). Restored the fix;
all 30 tests in the file, the plugin-setup test, and the full 75-test
tests/gateway/test_adapter_startup_secret_scope.py suite pass.
2026-09-02 07:01:23 -07:00