feat(hooks): add agent:turn:end and agent:turn:save internal hook events#71399
feat(hooks): add agent:turn:end and agent:turn:save internal hook events#71399mantisai-bot wants to merge 8 commits into
Conversation
Greptile SummaryThis PR adds an
Confidence Score: 4/5Safe to merge with minor improvements; no correctness or security issues found. Only P2 findings: a missing errorCode field in the hook context and use of the unbounded fireAndForgetHook instead of fireAndForgetBoundedHook. No logic errors, no security concerns, and the new tests cover both success and failure paths. src/acp/control-plane/manager.core.ts (fireAndForgetHook choice) and src/hooks/internal-hooks.ts (missing errorCode in context type) Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/hooks/internal-hooks.ts
Line: 171-178
Comment:
**`errorCode` missing from hook context despite PR description**
The PR description explicitly states "The hook carries `sessionKey`, `success`, `durationMs`, and optional `errorCode` on failures," but `AgentTurnEndHookContext` has no `errorCode` field and the `createInternalHookEvent` call in `manager.core.ts` does not pass it. Hook consumers receiving `success: false` cannot determine the failure reason (e.g., `ACP_TURN_FAILED` vs `ACP_SESSION_INIT_FAILED`) without re-examining state elsewhere. Adding the optional field now avoids a future breaking change to the hook contract.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: src/acp/control-plane/manager.core.ts
Line: 1685-1694
Comment:
**No timeout protection on hook execution**
`fireAndForgetHook` simply attaches a `.catch()` to the already-running promise — it has no timeout, no concurrency cap, and no queue limit. `triggerInternalHook` awaits each handler sequentially, so a single misbehaving or slow handler will keep the promise pending indefinitely, silently accumulating stuck microtasks across many turns. `fireAndForgetBoundedHook` already exists and enforces a 2 s timeout, max-16 concurrency, and max-256 queue depth. It expects a factory `() => Promise<unknown>`, so the call site would need to wrap `triggerInternalHook(...)` in an arrow function instead of passing the already-constructed promise directly.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "test(acp): cover agent turn end hook emi..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 72e59f8151
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
Codex review: needs changes before merge. Keep open. Current main still does not implement the intended Required change before merge: Keep this PR open for maintainer review or an in-place update. The best path is to emit Best possible solution: Keep this PR open for maintainer review or an in-place update. The best path is to emit Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 213f92a9ef0e. |
|
@mantisai-bot Hi! This is a great hook which I also need for an implementation I have for a security plugin. I'd love to see the relatively minor issues flagged addressed and am happy to help/collaborate or just cheerlead. Hope this one goes in! |
|
Codex review: needs real behavior proof before merge. Reviewed June 1, 2026, 11:32 AM ET / 15:32 UTC. Summary PR surface: Source +338, Tests +952, Docs +9. Total +1299 across 17 files. Reproducibility: not applicable. this is a feature PR rather than a bug report. Source checks show current main and Review metrics: 2 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land only after maintainers accept the split hook contract and awaited save timing, and after the contributor adds real direct or dispatch ACP turn proof showing both event payloads. Do we have a high-confidence way to reproduce the issue? Not applicable; this is a feature PR rather than a bug report. Source checks show current main and Is this the best way to solve the issue? Unclear until maintainer sign-off; the split between runtime completion and durable-save readiness is a maintainable shape, but it is not proven as the best final contract until API/timing acceptance and real ACP hook proof are added. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 1d7e5f48ed02. Label changesLabel justifications:
Evidence reviewedPR surface: Source +338, Tests +952, Docs +9. Total +1299 across 17 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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
|
72e59f8 to
72ca7a4
Compare
|
Hi maintainers — I’ve gone back through the earlier review feedback and believe the gaps are now covered in the current head Covered since the earlier comments:
Fresh local validation run:
Current PR checks are green, merge state is Could you please take another look when you get a chance? Happy to adjust if there’s any remaining API/timing concern around the ACP completion hook seam. |
72ca7a4 to
cf85787
Compare
|
@zeroaltitude Appreciate the cheerleading 🙂 genuinely helps. Same here — hope this one lands! |
a0a9bf9 to
dd6e572
Compare
|
Hey :) |
|
@JonathanBraverDev @zeroaltitude — adding a use-case perspective from the field. We run OpenClaw as a persistent agent runtime (Cophy) with a tiered memory system (Core/Episodic/Archive layers). Right now, post-turn memory consolidation is wired to session-level signals, which means we occasionally miss the write window when a turn ends abruptly or the session compacts mid-conversation. An Concretely, the pattern we want: Without a reliable end-of-turn signal, we have to either poll file state or rely on the agent remembering to flush before context compaction — both are fragile. The One question for the implementation: does Hope this lands — it fills a real gap for anyone building stateful agent workflows on top of OpenClaw. Context: Running OpenClaw as a persistent agent runtime, exploring memory continuity across sessions. |
876aa61 to
2fe9901
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@icophy I've Split the hooks by the guarantee to avoid ambiguity. Note that you will need agent:turn:save post split. |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Closing this battlefield branch in favor of two review-friendly sibling PRs based on current main:
This PR bundled lifecycle notification, save durability semantics, direct ACP transcript persistence, and several rounds of review hardening into one branch. The split keeps the safe lifecycle hook separate from the more delicate save-outcome contract. |
Summary
agent:turn:endfor ACP runtime turn completion or failure.agent:turn:savefor the save-layer outcome after transcript persistence is saved, skipped, or fails.context.saveOutcome(saved,skipped,failed) and optionalcontext.saveSkipReason;context.successis true only whensaveOutcome === "saved".saved; missing callbacks and no-op callbacks report skipped outcomes instead.skippedinstead ofsaved.Change Type
Scope
Linked Issue/PR
agent:turn:endinternal hook event #37833Behavior Changes
Internal hook consumers can subscribe to two ACP turn boundaries:
agent:turn:endagent:turn:savecontext.saveOutcome === "saved"agent:turn:endcarriessessionKey,success,durationMs, and optionalerrorCode.agent:turn:savecarriessessionKey,success,saveOutcome,turnSuccess,durationMs, optionalturnErrorCode, optionalsaveError, and optionalsaveSkipReason.For direct ACP and dispatch-backed ACP turns, transcript persistence now runs inside the awaited save callback before the save outcome is emitted. This is an internal lifecycle ordering change; there are no user-facing messaging or config behavior changes.
Security Impact
NoNoNoNoNoVerification
Commands run on head
32a0e90de50369b363e7caed6e420710235dce54:Result:
Real behavior proof
saveOutcome: "saved"means the application-level transcript append path completed and returned durable write evidence;skippedandfailedare reported separately. Missing save callbacks and no-op save callbacks report skipped outcomes.32a0e90de50369b363e7caed6e420710235dce54, macOS, Nodev25.6.0, pnpm11.2.2.pnpm exec tsx --evalcommand importing productionpersistAcpTurnTranscript, writing a prompt/reply pair through the ACP transcript persistence helper into a temporary session store, then reading the resulting transcript file.{ "saveOutcome": "saved", "saveSkipReason": null, "hasSessionFile": true, "transcriptHasPrompt": true, "transcriptHasReply": true, "transcriptLineCount": 3 }saveOutcome: "saved"only after producing a session file whose transcript contained both the prompt and reply text. Separate focused tests coverno_save_callback,no_save_evidence, malformed callback results, blocked message writes, dispatch ACP, and direct ACP command callback wiring.acpxadapter process.Compatibility / Migration
YesNoNoFailure Recovery
Revert this PR to remove the new internal hook events and return ACP turn completion to stats-only behavior.
Risks and Mitigations
agent:turn:endfromagent:turn:save, withsaveOutcomeidentifyingsaved,skipped, orfailed.