Skip to content

fix(agents): prefer target agent's bound Matrix account for subagent spawns#67508

Merged
gumadeiras merged 24 commits into
openclaw:mainfrom
lukeboyett:fix/matrix-bound-account-subagent-spawn-squashed
Apr 18, 2026
Merged

fix(agents): prefer target agent's bound Matrix account for subagent spawns#67508
gumadeiras merged 24 commits into
openclaw:mainfrom
lukeboyett:fix/matrix-bound-account-subagent-spawn-squashed

Conversation

@lukeboyett

@lukeboyett lukeboyett commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes Matrix (and other-channel) subagent spawns inheriting the caller's accountId instead of the target agent's bound account, and hardens resolveFirstBoundAccountId with kind-aware multi-tier matching for peer bindings.

  • src/agents/subagent-spawn.ts: adds resolveRequesterOriginForChild(...) that prefers the target agent's bound account via resolveFirstBoundAccountId({ cfg, channelId, agentId: targetAgentId, peerId, peerKind }) before falling back to the caller's accountId. Same-agent spawns short-circuit the lookup and preserve the caller's account. A new extractRequesterPeer helper peels known delivery-target prefixes (<channel>: namespace, then a generic <word>: loop) from agentTo to produce the raw peer id and — for channels whose plugin does not implement inferTargetChatType — infer peerKind from kind-prefixes (room:/channel:/chat: → channel, group:/team: → group, user:/dm:/pm: → direct) and id-embedded markers (Matrix !/@, IRC #). Id-embedded kind markers take precedence over prefix-derived kind so wrappers like room:@user:server classify correctly.
  • src/routing/bound-account-read.ts: resolveFirstBoundAccountId now accepts optional peerId and peerKind, and uses a four-tier precedence model:
    1. Exact peer id match (with kind cross-check when both sides declare one) wins immediately.
    2. Wildcard peer (peer.id: "*") match — only when the caller supplies a peer AND both sides declare a matching kind (treating group/channel as equivalent, mirroring peerKindMatches in resolve-route.ts).
    3. Channel-only binding (no peer) as the default fallback.
    4. Peerless last-resort fallback (first peer-specific or wildcard binding found) for callers that pass no peerId — preserves the pre-existing first-match semantics for cron delivery resolution.
  • Existing cron delivery-target.ts caller is unaffected: it still passes no peerId/peerKind, and the helper returns the same binding it would have before on realistic configs.

Why

A Matrix-bound parent session spawning a child for another agent could seed deliveryContext.accountId with the caller's account before target bindings were consulted. A child intended to post as the target agent's bound identity could end up posting as the caller — broken speaker identity in shared rooms, nondeterministic debugging, and wrong attribution in multi-agent deployments.

The original resolveFirstBoundAccountId also matched only on channel + agent, so multi-room-bound agents with different accounts per room got "first binding wins" regardless of active room. The peer-id/peer-kind aware matching fixes that while staying backward-compatible for callers that don't pass a peer (cron).

Regression coverage

Twelve unit tests in src/routing/bound-account-read.test.ts (new file) exercise the four-tier lookup: exact peer match, wildcard peer beats channel-only (with caller kind), channel-only beats wildcard (no caller kind), peer-specific fallback for peerless callers, non-matching peer skipped, channel mismatch, kind filtering, kind-unknown wildcard skip, exact id with unknown kind, group/channel equivalence, and the agent-on-different-channel no-match case.

Fifteen lifecycle tests in src/agents/openclaw-tools.subagents.sessions-spawn.lifecycle.test.ts (eleven new):

  1. Matrix room-bound route uses the target agent's bound account over the caller's.
  2. Peer-specific binding wins over channel-only binding for the same agent.
  3. Non-matching peer falls back to the channel-only binding.
  4. Wildcard peer binding matches any peer and beats channel-only.
  5. Exact peer binding wins over a wildcard peer binding.
  6. Same-agent subagent spawn preserves the caller's account (no re-resolution).
  7. Channel-side prefixes stripped from agentTo before lookup (Matrix room:<id>).
  8. <channel>:<kind>:<id> targets peel both prefixes (LINE line:group:<id>) and pick the group-kinded binding over a conflicting direct-kinded wildcard.
  9. conversation:<id> prefix stripped for Teams-style targets.
  10. Matrix room:@user:server classified as direct, not channel, so direct-peer bindings match.

Validation

  • pnpm build
  • pnpm check ✓ (typecheck, lint, import cycles, boundary checks)
  • pnpm test:changed ✓ (3777 passed on round 1)
  • Targeted vitest across all nine rounds ✓ (27/27 on current HEAD, 12 unit + 15 lifecycle)
  • Live Matrix readback on a downstream deployment (original round-1 fix): five bound target agents posted from their correct sender identities after rebuilt runtime.

Review iterations

This PR went through eight rounds of bot review (Codex + Greptile) before final approval. Commit history reflects each iteration:

Commit Addressed
5d11d7bb1e Initial fix (Matrix bound-account propagation)
5d0027479e Codex P1: wildcard * treated as literal → three-tier precedence
00b393064c Codex+Greptile P1s: same-agent spawn preserve caller; peerless wildcard; peerless peer-specific fallback
f8430fd70b Codex P1: drop peer.kind → kind-aware matching + plugin inference
d662dc529b Codex P1: unnormalized delivery-target prefixes → normalizeRequesterPeerId
3f673354a4 Three findings: LINE <channel>:<kind>:<id> parsing; peerless wildcard fallback restored; group/channel kind equivalence
173827d6a9 Codex P1: Teams conversation:<id> → generic ^[a-z][a-z0-9_-]*: peeler
249b292111 Codex P1: id-embedded kind markers override prefix-derived kind (Matrix room:@user → direct)

Final state: all twelve bot review threads resolved, Codex Didn't find any major issues on the current HEAD.

AI-assisted

Authored with Claude Code (Claude Opus 4.6, 1M-context). Fully tested locally. I've reviewed the change and understand what it does.

Related / prior art

Known-red CI

  • Install Smoke is red due to a pre-existing build-system gap (build:docker in package.json doesn't run scripts/write-npm-update-compat-sidecars.ts, unlike build-all.mjs). Root cause documented in this comment with a proposed one-line fix — happy to include here or split out per maintainer preference.
  • Run the GPT-5.4 / Opus 4.6 parity gate against the qa-lab mock is red and looks unrelated to this PR.

@greptile-apps

greptile-apps Bot commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug where Matrix (and other-channel) subagent spawns inherited the caller's accountId instead of the target agent's bound account. It adds resolveRequesterOriginForChild in subagent-spawn.ts to look up the target agent's binding before falling back to the caller's account, and extends resolveFirstBoundAccountId in bound-account-read.ts with a four-tier peer-aware precedence model (exact peer → kind-matched wildcard → channel-only → peerless fallback). Both the algorithm and its edge-case handling are well reasoned. The previous reviewer concern about peerless callers regressing to undefined (cron delivery) is correctly addressed via peerlessPeerSpecificFallback.

Confidence Score: 5/5

Safe to merge — no P0 or P1 issues found; the bug fix is correct and comprehensively tested.

The four-tier precedence model in resolveFirstBoundAccountId is correctly implemented and backward-compatible with peerless cron callers. The prefix-peeling logic in extractRequesterPeer handles all documented delivery-target formats. The same-agent short-circuit and the fallback chain are sound. All previous reviewer concerns are addressed. Twenty-seven tests pass and the implementation was validated against a live Matrix deployment.

No files require special attention.

Reviews (2): Last reviewed commit: "fix(agents): let id-embedded kind marker..." | Re-trigger Greptile

Comment thread src/routing/bound-account-read.ts

@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: 7a0506b007

ℹ️ 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".

Comment thread src/agents/subagent-spawn.ts Outdated
Comment thread src/routing/bound-account-read.ts
@lukeboyett

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 00b393064c

ℹ️ 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".

Comment thread src/routing/bound-account-read.ts
@lukeboyett

Copy link
Copy Markdown
Contributor Author

@codex review

@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: f8430fd70b

ℹ️ 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".

Comment thread src/agents/subagent-spawn.ts Outdated
@lukeboyett

Copy link
Copy Markdown
Contributor Author

@codex review

@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: d662dc529b

ℹ️ 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".

Comment thread src/agents/subagent-spawn.ts Outdated
Comment thread src/routing/bound-account-read.ts Outdated

@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: f3d8c0f635

ℹ️ 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".

Comment thread src/agents/subagent-spawn.ts Outdated
Comment thread src/routing/bound-account-read.ts Outdated
@lukeboyett

Copy link
Copy Markdown
Contributor Author

@codex review

@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: f3d8c0f635

ℹ️ 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".

Comment thread src/routing/bound-account-read.ts Outdated

@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: 3f673354a4

ℹ️ 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".

Comment thread src/agents/subagent-spawn.ts Outdated
@lukeboyett

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

ℹ️ 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".

@lukeboyett

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 173827d6a9

ℹ️ 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".

Comment thread src/agents/subagent-spawn.ts Outdated
@lukeboyett

Copy link
Copy Markdown
Contributor Author

@codex review

@lukeboyett

lukeboyett commented Apr 16, 2026

Copy link
Copy Markdown
Contributor Author

Install Smoke root cause identified — pre-existing build-system gap, not from this PR.

dist/extensions/qa-channel/runtime-api.js is a hardcoded 109-byte compat stub produced by scripts/write-npm-update-compat-sidecars.ts. That step is listed in scripts/build-all.mjs (run by pnpm build) but is missing from the build:docker script in package.json (run by the CI Dockerfile). I verified by running the script directly — the file comes right back with the exact expected contents.

Why it surfaced here and not elsewhere: most recent main runs of the Install Smoke workflow have "conclusion":"skipped" — the preflight gates on changed-path scope and doesn't trigger install-smoke on typical commits. The files touched by this PR happen to fall in the smoke scope, so it actually executed and hit the longstanding gap. (Earlier hypothesis that our getChannelPlugin import in src/agents/subagent-spawn.ts caused this — retracted. The correlation with commit d662dc529b was coincidence: that commit is simply where the Dockerfile layer cache got invalidated and a fresh build:docker had to run, exposing the gap.)

Proposed one-line fix for package.json:

-    "build:docker": "node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && node scripts/build-stamp.mjs && …",
+    "build:docker": "node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && node --import tsx scripts/write-npm-update-compat-sidecars.ts && node scripts/build-stamp.mjs && …",

(Insert scripts/write-npm-update-compat-sidecars.ts between runtime-postbuild and build-stamp, matching the order in build-all.mjs.)

Happy to include that fix in this PR if you'd like — it's pure build-script, trivially reviewable, and gets Install Smoke green. Otherwise I'll leave this PR focused on the routing fix and file the build gap separately. Your call.

The CI Run the GPT-5.4 / Opus 4.6 parity gate against the qa-lab mock failure still looks unrelated to this PR.

@lukeboyett

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

ℹ️ 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".

@lukeboyett lukeboyett marked this pull request as draft April 16, 2026 15:39
@lukeboyett lukeboyett marked this pull request as ready for review April 16, 2026 16:34
@gumadeiras gumadeiras force-pushed the fix/matrix-bound-account-subagent-spawn-squashed branch from ec114e7 to 9300111 Compare April 18, 2026 18:01
@gumadeiras gumadeiras merged commit c39314c into openclaw:main Apr 18, 2026
9 checks passed
@gumadeiras

Copy link
Copy Markdown
Member

Merged via squash.

Thanks @lukeboyett!

ender-wiggin-ai pushed a commit to stroupaloop/openclaw that referenced this pull request Apr 18, 2026
…spawns (openclaw#67508)

Merged via squash.

Prepared head SHA: 9300111
Co-authored-by: lukeboyett <46942646+lukeboyett@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
Mquarmoc pushed a commit to Mquarmoc/openclaw that referenced this pull request Apr 20, 2026
…spawns (openclaw#67508)

Merged via squash.

Prepared head SHA: 9300111
Co-authored-by: lukeboyett <46942646+lukeboyett@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…spawns (openclaw#67508)

Merged via squash.

Prepared head SHA: 9300111
Co-authored-by: lukeboyett <46942646+lukeboyett@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…spawns (openclaw#67508)

Merged via squash.

Prepared head SHA: 9300111
Co-authored-by: lukeboyett <46942646+lukeboyett@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…spawns (openclaw#67508)

Merged via squash.

Prepared head SHA: 9300111
Co-authored-by: lukeboyett <46942646+lukeboyett@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
…spawns (openclaw#67508)

Merged via squash.

Prepared head SHA: 9300111
Co-authored-by: lukeboyett <46942646+lukeboyett@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…spawns (openclaw#67508)

Merged via squash.

Prepared head SHA: 9300111
Co-authored-by: lukeboyett <46942646+lukeboyett@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
…spawns (openclaw#67508)

Merged via squash.

Prepared head SHA: 9300111
Co-authored-by: lukeboyett <46942646+lukeboyett@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling channel: discord Channel integration: discord channel: msteams Channel integration: msteams channel: slack Channel integration: slack size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants