Skip to content

feat(session): add session.groupScope to fold group chats into the agent main session#91702

Open
ikenraf wants to merge 4 commits into
openclaw:mainfrom
ikenraf:feat/session-group-scope
Open

feat(session): add session.groupScope to fold group chats into the agent main session#91702
ikenraf wants to merge 4 commits into
openclaw:mainfrom
ikenraf:feat/session-group-scope

Conversation

@ikenraf

@ikenraf ikenraf commented Jun 9, 2026

Copy link
Copy Markdown

What

Adds session.groupScope, mirroring the existing session.dmScope:

  • "per-group" (default) — each group keeps its own session (unchanged behavior)
  • "main" — group chats route into the agent's main session (agent:<id>:main)

Also supported as a per-binding override (bindings[].session.groupScope) so a specific conversation can opt in/out independently of the global default — the same shape as the existing per-binding dmScope.

Why

Closes #7524. There is currently no way to fold a group chat into the agent's main session for unified context; dmScope only covers DMs. This mirrors that capability for groups.

How

  • src/routing/session-key.tsbuildAgentPeerSessionKey returns the main key for a non-direct (group) peer when groupScope === "main", mirroring the existing dmScope === "main" direct-peer fold.
  • src/routing/resolve-route.tsresolveAgentRoute reads session.groupScope and the per-binding override (effectiveGroupScope).
  • src/infra/outbound/base-session-key.ts — outbound replies honor it too, so they route consistently with inbound.
  • Schema/types — zod-schema.session.ts, zod-schema.agents.ts (per-binding override), types.base.ts, types.agents.ts.

Tests

  • src/routing/session-key.test.ts and src/routing/resolve-route.test.ts cover: global main folds a group peer into main; default per-group keeps agent:<id>:telegram:group:<id>; per-binding override in both directions (fold one group against a per-group default; keep one group separate against a main default); and a guard that groupScope does not affect direct peers.
  • Both routing test files pass locally (133 tests, including 9 new groupScope cases). The config-doc baseline was regenerated (config:docs:gen) and config:docs:check passes.

Compatibility

Optional; unset = per-group = identical to current behavior. No migration needed.

ikenraf and others added 2 commits June 9, 2026 21:55
…main session

Mirrors session.dmScope. groupScope: per-group (default, current behavior) | main
(route group/channel chats into the agent main session). Also supported as a
per-binding override (bindings[].session.groupScope) so specific conversations
can opt in/out independently of the global default.

Addresses openclaw#7524.
…baseline

Add tests, documentation, and config-doc baseline for the session.groupScope
option (folds group chats into the agent main session, mirroring dmScope).

- routing tests: global groupScope:"main" folds a group peer to the agent main
  key; default per-group keeps the group's own key; per-binding override beats
  the global default in both directions; buildAgentPeerSessionKey unit cases.
- docs: groupScope subsection in concepts/session.md, routing-table note, and a
  session-key note in reference/session-management-compaction.md.
- changelog: entry under 2026.6.1 Changes.
- regenerate config-doc baseline hash to capture the new config surface.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation size: M triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 9, 2026
@clawsweeper

clawsweeper Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 9, 2026, 10:46 AM ET / 14:46 UTC.

Summary
Adds global session.groupScope and per-binding bindings[].session.groupScope across routing, outbound session-key helpers, config schema/types/help/labels, docs, generated config baselines, and tests.

PR surface: Source +124, Tests +351, Docs +27, Generated 0. Total +502 across 16 files.

Reproducibility: yes. for the review findings by source inspection: PR head calls the outbound binding matcher with no parent, guild, team, or role metadata while exposing groupScope on route bindings that can match those fields. I did not run tests because this review is read-only, and real behavior proof remains missing.

Review metrics: 1 noteworthy metric.

  • Config/session surfaces: 2 added. The PR adds global session.groupScope and per-binding bindings[].session.groupScope, which directly affect transcript bucket and shared-context behavior.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦪 silver shellfish
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Attach redacted real group-chat proof showing groupScope: "main", per-group, and at least one per-binding override outcome; avoid IPs, keys, phone numbers, and private endpoints.
  • [P1] Fix or explicitly narrow outbound binding-scope support so supported inbound and outbound-only paths resolve the same session key.
  • [P1] Add shared-context security/audit docs coverage and remove the CHANGELOG.md edit.

Proof guidance:

  • [P1] Needs real behavior proof before merge: No inspectable after-fix real behavior proof is attached; the PR only has a verbal local-observation comment, so the contributor should add a redacted screenshot, recording, terminal output, session-key trace, or logs and then update the PR body to trigger re-review.

Mantis proof suggestion
A native Telegram proof would materially help reviewers verify the user-visible group session routing beyond unit tests. A maintainer can ask Mantis to capture proof by posting a new PR comment that starts with the OpenClaw Mantis account mention, followed by:

telegram desktop proof: verify session.groupScope "main" folds a Telegram group into the agent main session and per-group keeps a separate session.

Risk before merge

  • [P1] Guild-, team-, parent-, or role-bound bindings[].session.groupScope overrides can split inbound and outbound-only session keys because the new outbound matcher erases those metadata fields.
  • [P1] groupScope: "main" intentionally merges multi-user group context into the agent main session without a matching security-audit or warning posture.
  • [P1] The contributor reports real behavior locally, but reviewers cannot inspect any redacted trace, screenshot, recording, terminal output, or logs yet.
  • [P1] The PR still edits CHANGELOG.md, which is release-owned in this repository.

Maintainer options:

  1. Finish routing metadata and audit before merge (recommended)
    Carry the same binding metadata needed by outbound-only sends or narrow the supported override shape, add shared-context audit/docs coverage, remove the changelog edit, and require inspectable real behavior proof.
  2. Accept explicit shared-context risk
    Maintainers may intentionally accept group-to-main context sharing, but should record the security posture and still require proof that all supported inbound and outbound paths resolve the same key.
  3. Pause for product scope
    If group-to-main routing needs a broader shared-session design with named sessions or channel-owned group config, pause this PR and keep the linked feature issue as the decision point.

Next step before merge

  • [P1] Maintainers should decide the shared-context/audit posture and require contributor-supplied real behavior proof before any automation takes over this PR.

Security
Needs attention: The diff adds a shared group-to-main session mode that can cross a multi-user context boundary without matching audit or warning coverage.

Review findings

  • [P2] Carry route metadata into outbound binding scope — src/routing/resolve-route.ts:760-765
  • [P2] Add groupScope shared-context audit coverage — src/config/zod-schema.session.ts:40
  • [P3] Remove the release-owned changelog entry — CHANGELOG.md:110
Review details

Best possible solution:

Land an additive opt-in group-scope contract only after inbound and outbound route matching share the same available binding metadata, shared group/main context has maintainer-approved audit/docs posture, the changelog edit is removed, and redacted real group-chat proof is attached.

Do we have a high-confidence way to reproduce the issue?

Yes for the review findings by source inspection: PR head calls the outbound binding matcher with no parent, guild, team, or role metadata while exposing groupScope on route bindings that can match those fields. I did not run tests because this review is read-only, and real behavior proof remains missing.

Is this the best way to solve the issue?

No as submitted. The feature direction is plausible, but the maintainable fix needs one supported binding-aware session-scope path for inbound and outbound behavior plus explicit security/audit posture before merge.

Full review comments:

  • [P2] Carry route metadata into outbound binding scope — src/routing/resolve-route.ts:760-765
    resolveOutboundBindingSessionScope reuses the inbound matcher but always passes parentPeer: null, empty guildId, empty teamId, and no roles. Since the PR exposes bindings[].session.groupScope on route bindings that can match guild/team/role/parent constraints, a Discord or Slack binding can fold inbound messages into agent:<id>:main while outbound-only sends miss that binding and use the global session scope instead.
    Confidence: 0.88
  • [P2] Add groupScope shared-context audit coverage — src/config/zod-schema.session.ts:40
    Accepting session.groupScope: "main" enables group participants to share the agent main transcript, but the existing security audit only warns for analogous dmScope === "main" multi-user DM exposure. Add a focused audit and docs warning, or record an explicit maintainer-approved decision that this shared-context mode should not warn.
    Confidence: 0.86
  • [P3] Remove the release-owned changelog entry — CHANGELOG.md:110
    Normal feature PRs should not edit CHANGELOG.md; release generation owns that file in this repo. Keep the release-note context in the PR body or squash message and drop this line from the branch.
    Confidence: 0.96

Overall correctness: patch is incorrect
Overall confidence: 0.88

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 7cdec2870604.

Label changes

Label justifications:

  • P2: This is a bounded feature/config PR with meaningful session and privacy impact but no active outage or regression.
  • merge-risk: 🚨 session-state: Merging as-is can mis-associate session state when binding-scoped group routing differs between inbound and outbound-only paths.
  • merge-risk: 🚨 security-boundary: The new opt-in mode can merge multi-user group context into the main session without a matching audit or warning posture.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: No inspectable after-fix real behavior proof is attached; the PR only has a verbal local-observation comment, so the contributor should add a redacted screenshot, recording, terminal output, session-key trace, or logs and then update the PR body to trigger re-review.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The PR changes visible Telegram group session behavior that can be demonstrated in a short Telegram Desktop proof showing whether a group folds into main or stays isolated.
Evidence reviewed

PR surface:

Source +124, Tests +351, Docs +27, Generated 0. Total +502 across 16 files.

View PR surface stats
Area Files Added Removed Net
Source 9 219 95 +124
Tests 3 351 0 +351
Docs 3 35 8 +27
Config 0 0 0 0
Generated 1 4 4 0
Other 0 0 0 0
Total 16 609 107 +502

Security concerns:

  • [medium] Missing shared group context audit warning — src/config/zod-schema.session.ts:40
    session.groupScope: "main" can merge group participants' context into the agent main session, but PR head only documents the option and does not extend the existing security audit pattern used for main-scoped multi-user DMs.
    Confidence: 0.86

What I checked:

  • Repository policy applied: Root policy treats session-state and config/default additions as compatibility-sensitive review surfaces, requires whole-surface review, and says normal PRs must not edit CHANGELOG.md. (AGENTS.md:30, 7cdec2870604)
  • Scoped policy read: Docs and outbound scoped guides were read; Telegram maintainer notes require real Telegram proof for Telegram behavior involving reply context or related transport-visible behavior. (.agents/maintainer-notes/telegram.md:37, 7cdec2870604)
  • Current main behavior: Current main only applies dmScope inside the direct-peer branch; non-direct peers still resolve to agent:<agentId>:<channel>:<peerKind>:<peerId>, so the linked feature is not already implemented on main. (src/routing/session-key.ts:223, 7cdec2870604)
  • PR branch implementation: PR head adds groupScope to session-key construction and returns the agent main session key when a non-direct peer has groupScope: "main". (src/routing/session-key.ts:258, 66350914e52c)
  • Outbound metadata gap: PR head resolves outbound binding overrides with parentPeer: null, empty guildId, empty teamId, and no roles, even though route bindings support those match constraints and the new session override is attached to the binding. (src/routing/resolve-route.ts:760, 66350914e52c)
  • Binding surface: The PR adds groupScope under AgentRouteBinding.session, whose match shape already supports peer, guild, team, and role constraints; outbound-only resolution currently cannot mirror all of those inbound matches. (src/config/types.agents.ts:59, 66350914e52c)

Likely related people:

  • steipete: GitHub path history for src/routing/session-key.ts and src/routing/resolve-route.ts shows recent session/routing commits, and the linked issue has prior keep-open review context from this account. (role: recent routing/session contributor and prior reviewer; confidence: high; commits: 9b30ff181c14, 6b940ed3ca8f, 45d9a0948571; files: src/routing/session-key.ts, src/routing/resolve-route.ts, docs/concepts/session.md)
  • Vincent Koc: Local blame on the current checkout attributes the central routing, outbound, docs, and security-audit files to the current snapshot commit authored by Vincent Koc. (role: recent area contributor from current-main blame; confidence: medium; commits: b3c946999d71; files: src/routing/session-key.ts, src/routing/resolve-route.ts, src/infra/outbound/base-session-key.ts)
  • giodl73-repo: Recent GitHub path history for src/security/audit-channel.ts includes a data-handling conformance/security-policy commit, making this account relevant to the missing audit posture. (role: adjacent security audit contributor; confidence: medium; commits: 1d3cfc4b0168; files: src/security/audit-channel.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 35c0e8e23d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const memberRoleIds = input.memberRoleIds ?? [];
const memberRoleIdSet = new Set(memberRoleIds);
const dmScope = input.cfg.session?.dmScope ?? "main";
const groupScope = input.cfg.session?.groupScope;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include groupScope in route-cache key

When cfg.session.groupScope is changed on an existing config/session object, this new value is read here but the resolved-route cache key still only includes dmScope (buildResolvedRouteCacheKey receives no groupScope). A cached group route can therefore keep returning agent:<id>:main after switching back to per-group (or the reverse) until the session object reference changes, which makes live config updates route group chats to the wrong transcript bucket.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 326ac5a4dfgroupScope is now included in buildResolvedRouteCacheKey alongside dmScope, so changing session.groupScope on an existing config object yields a distinct cache key and the route is recomputed rather than served stale. Thanks for catching it.

Comment thread src/infra/outbound/base-session-key.ts Outdated
accountId: params.accountId,
peer: params.peer,
dmScope: params.cfg.session?.dmScope ?? "main",
groupScope: params.cfg.session?.groupScope,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor binding groupScope for outbound route keys

For outbound-only sends to a group that has a matching bindings[].session.groupScope override, this helper only applies the global cfg.session.groupScope and never resolves the route binding. Inbound resolveAgentRoute can now put that selected group in agent:<id>:main, while the outbound mirror path writes the same conversation to agent:<id>:<channel>:group:<id> when the global default is per-group, so the advertised per-binding override is inconsistent across inbound and outbound state.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accurate — thanks. buildOutboundBaseSessionKey applies the global scope for both dmScope and groupScope and doesn't resolve per-binding overrides today, so groupScope here mirrors the existing dmScope behavior.

Normal replies inherit the inbound-resolved key (which does honor the per-binding override via resolveAgentRoute), so the inconsistency is limited to outbound-only / proactive sends to a group that carries a per-binding session.groupScope override.

Closing that gap means having this helper resolve per-binding overrides — but that would also change dmScope's outbound-only behavior, so I left it out to keep this PR scoped to groupScope. Happy to add it here (covering both dmScope and groupScope symmetrically) if you'd prefer the outbound-only path resolve bindings.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update — implemented in 66350914e5.

buildOutboundBaseSessionKey now resolves the matching binding's session override (both dmScope and groupScope) and applies override ?? global, so inbound and outbound resolve to the same session for a peer/group carrying a per-binding override. To avoid a second copy of the matching logic, I extracted the binding-matching tier loop from resolveAgentRoute into a shared findMatchingBinding, and added resolveOutboundBindingSessionScope that reuses it — so the two paths can't drift. The caller's explicit agentId is preserved; only the scope now follows the binding.

Tests: new src/infra/outbound/base-session-key.test.ts covers a per-binding groupScope override in both directions (fold-in vs keep-separate), a dmScope override, the no-binding global fallback, and that the caller's agentId is preserved over the binding's agent. Full routing + outbound suites pass (137 + 31).

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. labels Jun 9, 2026
…onfig metadata

- include groupScope in buildResolvedRouteCacheKey (parity with dmScope)

- add session.groupScope + bindings[].session.groupScope to schema.help and schema.labels

- regenerate config-doc baseline
@ikenraf

ikenraf commented Jun 9, 2026

Copy link
Copy Markdown
Author

Thanks for the review — addressed both points in 326ac5a4df:

  • Route cache key: groupScope is now included in buildResolvedRouteCacheKey, mirroring dmScope. (Previously dmScope was in the key but groupScope wasn't. In the current logic global scope is constant per-config and per-binding overrides are already determined by the keyed inputs, so it wasn't a live miss — but the asymmetry is worth removing, and it guards against future divergence.)
  • Config metadata: added session.groupScope and bindings[].session.groupScope to schema.help.ts and schema.labels.ts, paralleling the dmScope entries. The schema.help.quality suite passes and the config-doc baseline was regenerated (config:docs:check green).

Behavior: the tests in resolve-route.test.ts / session-key.test.ts drive the real routing functions and assert the exact session-key outcomes — global main folds a group peer to agent:<id>:main; default per-group keeps agent:<id>:telegram:group:<id>; the per-binding override flips a single group either way; and groupScope leaves direct peers unchanged. We also run this on our own instance and have observed it end-to-end: a Telegram group folding into the agent main session under groupScope: "main", per-group keeping each group separate, the per-binding override toggling one group either way, all resolving to a single session with no duplicate writes. Glad to add an integration-level test or attach a captured session-key trace if that's the preferred proof form here.

One related note for your call: groupScope: "main" folds a whole group's messages into the agent main session, which carries similar multi-user / shared-context implications to dmScope: "main" (which doctor-security.ts warns on). I kept this PR focused and didn't add a parallel advisory, but I'm happy to if you'd like one.

@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. labels Jun 9, 2026
…on keys

buildOutboundBaseSessionKey now resolves the matching binding's dmScope/groupScope override (via a shared findMatchingBinding extracted from resolveAgentRoute) instead of using only the global session config, so inbound and outbound resolve to the same session for peers/groups with a per-binding override. Addresses PR review.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: L status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: groupScope option to consolidate group sessions into main

1 participant