Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(gateway): emit subagent_ended hook for api.runtime.subagent.run() (#59164)#87698

Merged
steipete merged 1 commit into
openclaw:mainfrom
sweetcornna:fix/59164-subagent-ended-hook-runtime-api
May 31, 2026
Merged

fix(gateway): emit subagent_ended hook for api.runtime.subagent.run() (#59164)#87698
steipete merged 1 commit into
openclaw:mainfrom
sweetcornna:fix/59164-subagent-ended-hook-runtime-api

Conversation

@sweetcornna

@sweetcornna sweetcornna commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Rewrites plugin SDK subagent tracking so api.runtime.subagent.run() marks the internal Gateway agent request instead of registering after dispatch.
  • The Gateway agent handler now registers plugin subagent runs at the accepted-run boundary, which gives subagent_ended hooks a real SubagentRunRecord while avoiding duplicate runtime: "cli" task rows.
  • Registry tracking stays best-effort: if subagent registry persistence fails, the agent run still dispatches and falls back to normal CLI task tracking.
  • Public SDK shape is unchanged. No protocol, config, or migration surface changes.

Closes #59164

Real behavior proof

Behavior addressed: plugin SDK subagent runs dispatched through api.runtime.subagent.run() were not represented in the subagent registry, so subagent_ended hooks did not fire for that run path. The rewritten fix moves registration into src/gateway/server-methods/agent.ts, suppresses the generic CLI task only after subagent registration succeeds, and falls back to CLI task tracking on registry failure.

Real environment tested: local OpenClaw source checkout on macOS, exercising the in-process plugin runtime adapter plus the real Gateway agent handler, task registry, and subagent registry code paths with Vitest. The model turn is mocked at agentCommandFromIngress, matching the existing Gateway method test style.

Exact steps or command run after this patch:

node scripts/run-vitest.mjs src/gateway/server-plugins.subagent-ended-hook.test.ts src/gateway/server-methods/agent.test.ts
pnpm tsgo:core:test
node scripts/run-oxlint.mjs src/gateway/server-methods/agent.ts src/gateway/server-methods/agent.test.ts src/gateway/server-methods/shared-types.ts src/gateway/server-plugins.ts src/gateway/server-plugins.subagent-ended-hook.test.ts
git diff --check
.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main

Evidence after fix:

Test Files  2 passed (2)
Tests  148 passed (148)

tsgo:core:test completed with exit code 0
run-oxlint touched files completed with exit code 0
git diff --check completed with exit code 0
autoreview clean: no accepted/actionable findings reported
overall: patch is correct (0.82)

Observed result after fix: createGatewaySubagentRuntime().run() sends the internal agentRunTracking: "plugin_subagent" marker through the Gateway client. The Gateway agent handler creates exactly one task row for the run, with runtime: "subagent", the plugin label, cleanup: "keep", expectsCompletionMessage: false, and no duplicate runtime: "cli" task. A cached retry with the same idempotency key does not re-register or start a second run. A multi-agent child session such as agent:work:subagent:plugin-helper is owned by agent:work:main, not the default main agent. If registry persistence throws, the public run still dispatches and records a fallback CLI task.

What was not tested: no external packaged plugin process was run against a separately started gateway. The covered runtime boundary is in-process Gateway/plugin runtime code with the model execution mocked, so it proves registration, task ownership, retry behavior, best-effort fallback, and plugin readback/delete behavior without live model/provider credentials.

Regression coverage

  • src/gateway/server-plugins.subagent-ended-hook.test.ts verifies the plugin runtime marks Gateway agent calls for plugin subagent tracking, preserves plugin identity, keeps no-context dispatch behavior unchanged, and preserves transcript readback until explicit deleteSession().
  • src/gateway/server-methods/agent.test.ts verifies the real Gateway agent handler creates a single subagent task/registry row, avoids cached retry re-registration, handles non-default agent ownership, and falls back to CLI task tracking if registry persistence fails.

Risk

Highest-risk surface is duplicate task or registry state for a run that already has Gateway task tracking. The fix keeps a single tracking owner: successful plugin subagent registration suppresses CLI task creation; failed registration falls back to the existing CLI task path so public SDK behavior remains best-effort.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: M triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels May 28, 2026
@clawsweeper

clawsweeper Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed May 31, 2026, 1:00 PM ET / 17:00 UTC.

Summary
The PR proposes registering api.runtime.subagent.run() runs in the subagent registry so subagent_ended hooks fire and child sessions remain readable until explicit deletion.

PR surface: Source +91, Tests +277. Total +368 across 2 files.

Reproducibility: unclear. the prompt includes source-repro context for the original issue, but this run could not inspect current source or execute/read the focused paths directly.

Review metrics: none identified.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🦐 gold shrimp
Result: blocked until stronger 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:

  • [P1] Add redacted installed-plugin Gateway proof for the public plugin runtime path.
  • Get maintainer confirmation on canonical task-ledger ownership for plugin subagent runs.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body describes in-process Vitest/live-capture proof, but prior ClawSweeper context still asks for installed-plugin Gateway proof for this external PR. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] The prompt context identifies a compatibility-sensitive plugin SDK/session-state change with previous concerns about duplicate task-ledger rows and missing installed-plugin Gateway proof; these remain unresolved without a fresh source and history review.

Maintainer options:

  1. Verify task ownership before merge
    Confirm the PR does not create competing task rows or ambiguous ownership for one plugin SDK run before treating it as merge-ready.
  2. Require installed-plugin proof
    Ask for redacted proof from a real installed plugin through Gateway showing hook delivery, transcript readback, explicit deletion, and task visibility.
  3. Pause until maintainer review
    Because this touches plugin SDK behavior and session lifecycle, maintainers can pause the PR if the canonical ownership model is still unsettled.

Next step before merge

  • [P1] The remaining blocker is maintainer review of Gateway/plugin SDK lifecycle ownership and proof, not a narrow automated repair.

Security
Cleared: No concrete security or supply-chain concern is visible from the provided diff context, but this was not a complete tool-backed security review.

Review details

Best possible solution:

A maintainer should require a fresh source-level review of the Gateway plugin SDK subagent path, task-ledger ownership, and installed-plugin proof before merge.

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

Unclear: the prompt includes source-repro context for the original issue, but this run could not inspect current source or execute/read the focused paths directly.

Is this the best way to solve the issue?

Unclear: the proposed direction appears plausible, but prior review context says task-ledger ownership and installed-plugin proof still need maintainer review before this is the best merge path.

AGENTS.md: unclear because the file could not be read completely.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 0ee5f47fbabb.

Label changes

Label changes:

  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • remove rating: 🧂 unranked krab: Current PR rating is rating: 🦪 silver shellfish, so this older rating label is no longer current.

Label justifications:

  • P2: This is a normal-priority Gateway/plugin SDK correctness fix with limited blast radius but compatibility-sensitive lifecycle behavior.
  • merge-risk: 🚨 compatibility: The PR changes the behavior of the public plugin SDK subagent run path and may affect existing plugin workflows.
  • merge-risk: 🚨 session-state: The changed path controls subagent registry records, hook emission, transcript readability, and deletion behavior.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body describes in-process Vitest/live-capture proof, but prior ClawSweeper context still asks for installed-plugin Gateway proof for this external PR. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +91, Tests +277. Total +368 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 91 0 +91
Tests 1 277 0 +277
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 368 0 +368

What I checked:

Likely related people:

  • unknown: Tool access was unavailable, so git history could not be inspected; Gateway/plugin SDK ownership needs a real git blame/git log pass before routing. (role: likely follow-up owner; confidence: low; files: src/gateway/server-plugins.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.

@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. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels May 28, 2026
sweetcornna added a commit to sweetcornna/openclaw that referenced this pull request May 28, 2026
openclaw#59164)

ClawSweeper P1 follow-up on openclaw#87698: the original helper registered plugin
SDK subagent runs with `cleanup: "delete"`, which made the registry
delete the child session as soon as subagent_ended fired. Plugins that
follow the documented flow `api.runtime.subagent.run()` →
`waitForRun()` → `getSessionMessages()` would race the auto-delete and
lose the transcript before reading it back.

Default to `cleanup: "keep"` instead so the child session survives
completion. Explicit teardown stays with the plugin via
`api.runtime.subagent.deleteSession()`, matching the `sessions_spawn`
default for the same readability reason. The subagent_ended hook still
fires in `completeSubagentRun` — cleanup only governs whether the
session is deleted afterward.

Adds a regression test covering the full plugin contract: run →
waitForRun → getSessionMessages (transcript readable) → deleteSession
(transcript gone). Existing cleanup-mode assertion updated from
`"delete"` to `"keep"`.
@sweetcornna sweetcornna force-pushed the fix/59164-subagent-ended-hook-runtime-api branch from f3d2753 to 2defa69 Compare May 28, 2026 16:26
@sweetcornna

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

P1 finding addressed in 2defa69:

  • Default registry cleanup for plugin SDK subagent runs changed from "delete" to "keep" in registerPluginSubagentRunQuietly so the child session survives subagent_ended. Plugins that follow run() -> waitForRun() -> getSessionMessages() -> deleteSession() no longer race the auto-delete.
  • The hook still fires: expectsCompletionMessage: false keeps the run on the immediate-emit branch in completeSubagentRun (src/agents/subagent-registry-lifecycle.ts:1113-1131), independent of cleanup mode.
  • New regression test in src/gateway/server-plugins.subagent-ended-hook.test.ts: "preserves the child session so the transcript stays readable until the plugin deletes it" drives the full run -> waitForRun -> getSessionMessages -> deleteSession flow against a mocked gateway router and asserts the transcript is readable post-completion and gone after explicit delete.
  • The pre-existing assertion that pinned cleanup to "delete" is now updated to "keep".
  • Real-plugin-harness proof: out of scope for this contributor (no Crabbox lane + plugin install on hand). The new regression test is the strongest available signal that the plugin contract is preserved.

PR body has a new "ClawSweeper P1 follow-up" section with the details.

@clawsweeper

clawsweeper Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels May 28, 2026
@sweetcornna

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review — added a live-capture proof (qa/proofs/issue-59164-subagent-ended-hook.output.txt, test src/gateway/server-plugins.subagent-ended-hook.proof.test.ts) showing the real subagent_ended hook firing through the production registry path, the child transcript readable after completion (cleanup:"keep"), and explicit deleteSession() then returning not-found. See the new 'Real behavior proof — live hook capture' section in the PR body.

@clawsweeper

clawsweeper Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels May 29, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels May 29, 2026
@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed 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. labels May 29, 2026
@sweetcornna

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review — the prior re-review (05:10 UTC) failed mid-run (execution error, incomplete AGENTS.md read). Real behavior proof is in place: qa/proofs/issue-59164-subagent-ended-hook.output.txt (test src/gateway/server-plugins.subagent-ended-hook.proof.test.ts) — a live capture driving the real createGatewaySubagentRuntime().run() → registry → hook path, showing subagent_ended actually fires, the child transcript stays readable after completion (cleanup:"keep"), and explicit deleteSession() then returns not-found. Please retry.

@clawsweeper

clawsweeper Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. 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. rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels May 31, 2026
@steipete steipete self-assigned this May 31, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels May 31, 2026
@steipete steipete force-pushed the fix/59164-subagent-ended-hook-runtime-api branch from bdbfd48 to 762754b Compare May 31, 2026 17:07
Plugin SDK subagent runs now register at the Gateway agent acceptance boundary so subagent_ended hooks fire without creating duplicate CLI task rows.

The registration stays best-effort: if the subagent registry cannot persist tracking state, the run still dispatches and falls back to the existing CLI task tracking path.

Closes openclaw#59164

Co-authored-by: Cornna <[email protected]>
@steipete

Copy link
Copy Markdown
Contributor

Land-ready after rewrite.

What changed:

  • moved plugin subagent task registration to the Gateway agent acceptance boundary
  • kept plugin identity on internal Gateway agent requests
  • made registry persistence best-effort, with CLI task tracking fallback instead of failing the SDK call
  • added regression coverage for single registration, idempotency retries, multi-agent ownership, persistence failure fallback, and plugin runtime request markers

Local proof:

  • node scripts/run-vitest.mjs src/gateway/server-plugins.subagent-ended-hook.test.ts src/gateway/server-methods/agent.test.ts
  • pnpm tsgo:core:test
  • node scripts/run-oxlint.mjs src/gateway/server-methods/agent.ts src/gateway/server-methods/agent.test.ts src/gateway/server-methods/shared-types.ts src/gateway/server-plugins.ts src/gateway/server-plugins.subagent-ended-hook.test.ts
  • git diff --check
  • autoreview --mode branch --base origin/main: clean, no accepted/actionable findings

CI proof:

  • latest head 18c8bcb
  • gh pr checks 87698 --watch --fail-fast: all latest-head checks passed or skipped as expected
  • Real behavior proof passed after the PR body was rewritten with the exact proof labels

Known proof gap:

  • no packaged external plugin process was launched; coverage exercises the in-process Gateway/plugin runtime contract with the agent command mocked at the runtime boundary.

Thanks @ymylive for the original PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: L status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

subagent_ended hook does not fire for runs created via api.runtime.subagent.run()

3 participants