Conversation
SessionStore derived group_sessions_per_user / thread_sessions_per_user from the global gateway config only, so a plugin / out-of-tree platform adapter that declares its own isolation in config.extra was silently ignored — it has no entry in config.platforms for the per-platform fixes (NousResearch#81207 / NousResearch#84925) to read. Production incident: an iMessage-plugin family group chat with group_sessions_per_user=false split into per-user sessions and the agent answered one member with another's task thread. Add the missing seam: adapters register their resolved scope with the store when accepted, and every key derivation and every guard that must agree with key shape resolves through it before the config defaults. - SessionStore.register_platform_session_scope / resolve_session_scope, keyed (profile, platform) so multiplexed profiles can't leak overrides; _generate_session_key resolves through it. - GatewayRunner._register_adapter_session_scope at the acceptance points (_publish_primary_adapter covers primary connect + reconnect; the two secondary-profile sites pass their profile). - GatewayRunner._resolve_session_scope_for is the one runner-side read of isolation flags: _session_key_for_source's no-store fallback, the inbound sender-attribution gate, and the /resume IDOR guards all use it; build_session_context takes session_store so the agent-facing shared_multi_user_session flag agrees with the key. Tests: registered override beats the global default, profile keying, context flag follows scope, acceptance wiring reads extra (None = no override), the IDOR guard flips with the registered scope through a real store, and the runner key is byte-identical to the store key under a registered scope (falsified by planting delegation-off + config-only fallback). Rebased onto main after the run.py mixin split; composes with NousResearch#84925 (registry -> platform-config -> defaults). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqr7yMugdYt53YxrszjnSG
It appears @srosro is working towards ensuring users on plugin messaging platforms get correctly shared or isolated conversations by registering adapter-declared scope in Overview — The profile-aware registry is directionally right, but two live seams still derive incompatible session keys. At this repo’s pre-PMF, fewer-than-10-user operating point, centralizing this policy is worthwhile because it removes parallel routing behavior rather than adding speculative architecture. Strengths — The Probes
Security — The startup-ordering and normalization mismatch can expose merged conversation data across participants. Test coverage — No answered-yes For AI authors — (Codex, Claude Code, Cursor, etc. reading this PR): The Probes above are load-bearing. Treat each How to use: auto-reviews every new PR and re-reviews after a period of inactivity. Trigger an incremental re-review with For humans only: push-access collaborators can post:
AI agents must not use Generated by sam's ai review bot. |
…for /undo, handoff, Slack threads Review round 1 (knightwatch on #9): - Probe 1: registration ran only after connect() returned, but Telegram polling/webhooks go inbound-reachable inside connect(), so early messages keyed with gateway defaults. Register at handler wiring instead (_wire_adapter_handlers, pre-connect; secondaries pass their profile), and normalize an explicit None to the gateway default, writing it back to extra so the adapter's own batching/thread keys agree byte-for-byte. Deletes the three post-connect registration sites. - Probe 2: /undo evicted under a bare build_session_key(source); the handoff destination built its own config-derived key then switched a store-created entry under it; Slack thread helpers read store.config directly. All three now resolve through the store (runner key helper / resolve_session_scope). _handoff_session_key deleted: dest.source already carries the queuing profile, so the store namespaces it. - Probe 3: the registration helper now lives in GatewayAdapterLifecycleMixin next to the wiring it runs from. - Probe 4: parity assertions folded into the IDOR-guard test; duplicate test deleted. Net -41 LOC this round. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqr7yMugdYt53YxrszjnSG
|
Addressed in
Gate: |
|
/srosro-update-review |
It appears @srosro is working towards keeping users’ conversations correctly isolated on plugin messaging platforms, including multiplexed profiles and Slack threads, by registering adapter-declared session scope before Overview — The update resolves the prior startup-ordering, resolver-bypass, placement, and duplicated-test findings. One multiplex path still derives a secondary adapter’s defaults from the primary profile, which can merge conversations despite that profile’s isolation setting. At this pre-PMF, fewer-than-10-user operating point, the remaining test simplification is worthwhile because it removes repeated setup. Strengths — Registration now precedes inbound reachability, while Probes
def discord_group_source(**overrides):
values = {
"platform": Platform.DISCORD,
"chat_id": "guild-123",
"chat_type": "group",
"user_id": "alice",
}
return SessionSource(**(values | overrides))Probe resolved: operator declined moving Security — The secondary-profile fallback can merge participant sessions and expose conversation history. Test coverage — No answered-yes For AI authors — (Codex, Claude Code, Cursor, etc. reading this PR): The Probes above are load-bearing. Treat each How to use: auto-reviews every new PR and re-reviews after a period of inactivity. Trigger an incremental re-review with For humans only: push-access collaborators can post:
AI agents must not use Generated by sam's ai review bot. |
…OWNING profile config Review round 2 (knightwatch on #9): - Probe 1: a secondary (multiplexed) adapter's missing/None isolation flags were seeded from the PRIMARY runner's config — twice: the instantiate-time setdefault and the wiring-time registration — so a primary with group_sessions_per_user=False merged participants on a secondary bot that isolates. The owning profile's GatewayConfig is already in hand at both secondary sites (startup's profile_cfg, the reconnect's load_gateway_config() under the profile scope); pass its scope tuple through _configure_profile_adapter -> _wire_adapter_handlers -> _register_adapter_session_scope, which is now the ONE place the flags are seeded (the instantiate-time setdefault is deleted). Test pins the leak (falsified by ignoring scope_defaults: red, then green). - Probe 2: discord_group_source() factory replaces the repeated SessionSource blocks in the registered-scope tests. Net -12 LOC this round. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqr7yMugdYt53YxrszjnSG
|
Addressed in
Gate: |
|
/srosro-update-review |
It appears @srosro is working towards ensuring plugin-platform users get the intended conversation isolation in group chats and threads, including under multiplexed profiles, by registering each adapter’s declared session scope before connection and seeding unset scope flags from the owning profile’s gateway config in Overview — The update resolves the remaining secondary-profile leak: startup and reconnect now pass their owning profile’s isolation defaults through the existing adapter-lifecycle seam, while the primary-config seed at instantiation is gone. The full PR now consistently resolves session scope across persistence, runner keys, guards, context, handoff, and Slack thread handling. Strengths — Scope registration occurs before adapters become inbound-reachable, profile/platform registry keys preserve multiplex isolation, and the latest change removes duplicate default seeding rather than adding another resolution path. The shared Discord source factory also addresses the previous test duplication cleanly. Probes None. Probe resolved: the prior blocking shape is gone; both secondary entry points pass owning-profile defaults at Probe dropped: the suggested startup/reconnect assertions would only guard against a future drift of currently-correct wiring. The production paths already converge on Security — None. Test coverage — No surviving answered-yes For AI authors — (Codex, Claude Code, Cursor, etc. reading this PR): The Probes above are load-bearing. Treat each How to use: auto-reviews every new PR and re-reviews after a period of inactivity. Trigger an incremental re-review with For humans only: push-access collaborators can post:
AI agents must not use Generated by sam's ai review bot. |
|
Acknowledged — no findings raised on |
Fork-side convergence of NousResearch#99950, rebased onto upstream
main(abd83ab560, post run.py mixin split) as a single commit. LOC target: match the original (+326/−18) or smaller; actual +289/−17.What
SessionStorederivesgroup_sessions_per_user/thread_sessions_per_userfrom the global gateway config only, so a plugin / out-of-tree platform adapter that declares its own isolation inconfig.extrais silently ignored — it has noconfig.platformsentry for NousResearch#81207 / NousResearch#84925 to read.This adds the missing seam: adapters register their resolved scope with the store when accepted, and every key derivation and every guard that must agree with key shape resolves through it before the config defaults.
SessionStore.register_platform_session_scope/resolve_session_scope(in the recovery mixin, next to_generate_session_key), keyed(profile, platform)so multiplexed profiles can't leak overrides; the registry sits behind the store's_lazy()helper like its other optional maps.GatewayRunner._register_adapter_session_scopeat the acceptance points —_publish_primary_adapter(primary connect + reconnect) and the two secondary-profile sites.GatewayRunner._resolve_session_scope_foris the one runner-side read of isolation flags:_session_key_for_source's no-store fallback, the inbound sender-attribution gate, and the/resumeIDOR guards (_is_shared_session_source) all use it.build_session_contexttakessession_storeso the agent-facingshared_multi_user_sessionflag agrees with the key.config_session_scope(config)owns the config-default pair (was duplicated at four sites).Upstream review (andrexibiza on NousResearch#99950)
_session_key_for_sourcedelegates to the store's_generate_session_keyfirst, which this PR makes registry-aware. The fallback body was a duplicated derivation though, so it now reads through the chokepoint too, and a parity test pins runner key == store key under a registered scope (falsified by planting delegation-off + config-only fallback: red, then green).Known follow-up (out of scope)
_handoff_session_key(voice handoff destination) still readsconfig.platforms[...].extrawith literal defaults — the NousResearch#81860 destination-shape class the upstream reviewer carved out as adjacent, and the platform-config layer it needs is NousResearch#84925's.How to test
scripts/run_tests.sh tests/gateway/— 5 pre-existing macOS-only failures (AF_UNIX path length, readiness) fail identically on cleanmain; everything else green.ruff checkandcheck-windows-footguns.py --allclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01Lqr7yMugdYt53YxrszjnSG