Skip to content

feat(hooks): add ACP turn transcript save hook#89154

Closed
mantisai-bot wants to merge 13 commits into
openclaw:mainfrom
mantisai-bot:feat/agent-turn-save-outcome-hook
Closed

feat(hooks): add ACP turn transcript save hook#89154
mantisai-bot wants to merge 13 commits into
openclaw:mainfrom
mantisai-bot:feat/agent-turn-save-outcome-hook

Conversation

@mantisai-bot

@mantisai-bot mantisai-bot commented Jun 1, 2026

Copy link
Copy Markdown

Summary

Adds a separate agent:turn:transcript:save internal hook event for the ACP turn transcript save phase. This PR is scoped to transcript-save durability outcomes and does not include the sibling agent:turn:end lifecycle hook from #89152.

Current implementation

  • Adds agent:turn:transcript:save event typing, validation, and docs with explicit saved, skipped, and failed outcomes.
  • Routes both chat-dispatch ACP turns and direct ACP command turns through the manager save callback before the hook event is emitted.
  • Reports saveOutcome: saved only when the transcript append path reports a fresh durable append; no callback, declined boundaries, idempotency replays, blocked writes, empty turns, and save failures become non-saved outcomes.
  • Preserves the lower append API's appended flag through the user-turn transcript wrapper so replay/no-op writes are not counted as save evidence.
  • Covers manager outcomes, dispatch/direct ACP persistence, invalid callback results, skipped outcomes, failed runtime turns, blocked writes, and idempotency replay.
  • Hardens gateway test fixtures surfaced by changed-path OpenGrep: env restoration avoids bulk-copying host env back into process.env, and noServer WebSocket test servers set an explicit preauth maxPayload.

Real behavior proof

  • Behavior or issue addressed: proves that a source-built OpenClaw ACP manager turn with an installed internal hook handler observes agent:turn:transcript:save only after transcript persistence reports saveOutcome: saved; also verifies idempotency replay is not counted as a fresh save.
  • Real environment tested: local OpenClaw source checkout on macOS at /Users/mantis/Projects/openclaw-pr-turn-save, branch feat/agent-turn-save-outcome-hook, head 4a7985a37c89030d877f1145d344dacf9d3436cc.
  • Exact steps or command run after this patch: created an isolated temp OPENCLAW_STATE_DIR, installed a managed internal hook with HOOK.md + handler.js, loaded it through loadInternalHooks, ran AcpSessionManager.runTurn from source with an onBeforeTurnSaveHook that calls the real persistAcpTurnTranscript, then read both the hook log and transcript JSONL.
  • Evidence after fix:
$ git rev-parse HEAD
4a7985a37c89030d877f1145d344dacf9d3436cc

$ node --import tsx --input-type=module <source-built transcript-save proof script>
{
  "loadedHooks": 1,
  "stateDir": "/Users/mantis/.openclaw/state/tmp/oc-89154-proof-clean-jy246I/state",
  "hookEventCount": 1,
  "hookEvent": {
    "type": "agent",
    "action": "turn:transcript:save",
    "sessionKey": "agent:codex:acp:proof-transcript-save",
    "context": {
      "sessionKey": "agent:codex:acp:proof-transcript-save",
      "turnSuccess": true,
      "durationMs": 1,
      "success": true,
      "saveOutcome": "saved"
    }
  },
  "transcriptPath": "/Users/mantis/.openclaw/state/tmp/oc-89154-proof-clean-jy246I/state/agents/codex/sessions/proof-session-89154.jsonl",
  "messageCount": 2,
  "transcriptRoles": ["user", "assistant"],
  "assistantText": "PROOF_TRANSCRIPT_SAVE_OK"
}

$ node scripts/test-projects.mjs src/agents/command/attempt-execution.cli.test.ts
[test] passed 1 Vitest shard in 4.95s
  • Observed result after fix: the installed internal hook saw one agent:turn:transcript:save event with success: true and saveOutcome: saved, and the corresponding transcript file contained the persisted user and assistant messages. The idempotency replay regression now returns saveOutcome: skipped / saveSkipReason: no_transcript_write and leaves the transcript at one user message.
  • What was not tested: a full production-account ACP backend session was not run from this branch.
  • Proof limitations or environment constraints: the runtime was source-built and isolated with a temp state dir; the ACP runtime itself was a local proof runtime so the proof focuses on OpenClaw manager/save/hook behavior rather than an external harness provider.

Validation

  • node scripts/test-projects.mjs src/acp/control-plane/manager.test.ts src/hooks/internal-hooks.test.ts src/agents/command/attempt-execution.cli.test.ts src/commands/agent.acp.test.ts src/commands/agent.test.ts src/auto-reply/reply/dispatch-acp.test.ts src/auto-reply/reply/dispatch-from-config.test.ts src/agents/agent-command.live-model-switch.test.ts — passed 5 Vitest shards in 33.76s.
  • node scripts/test-projects.mjs src/gateway/server-discovery-runtime.test.ts src/gateway/server.plugin-node-capability-auth.test.ts src/gateway/server.preauth-hardening.test.ts — passed 1 Vitest shard in 41.59s.
  • pnpm exec oxlint src/acp/control-plane/manager.core.ts src/acp/control-plane/manager.types.ts src/acp/control-plane/manager.test.ts src/acp/control-plane/manager.turn-runner.ts src/hooks/internal-hooks.ts src/hooks/internal-hooks.test.ts src/auto-reply/reply/dispatch-acp.ts src/auto-reply/reply/dispatch-acp.test.ts src/auto-reply/reply/dispatch-from-config.test.ts src/auto-reply/reply/dispatch-acp-transcript.runtime.ts src/agents/agent-command.ts src/agents/agent-command.live-model-switch.test.ts src/agents/command/attempt-execution.ts src/agents/command/attempt-execution.cli.test.ts src/commands/agent.acp.test.ts src/commands/agent.test.ts src/sessions/user-turn-transcript.ts — passed.
  • pnpm exec oxfmt --check --threads=1 docs/automation/hooks.md src/acp/control-plane/manager.core.ts src/acp/control-plane/manager.types.ts src/acp/control-plane/manager.test.ts src/acp/control-plane/manager.turn-runner.ts src/hooks/internal-hooks.ts src/hooks/internal-hooks.test.ts src/auto-reply/reply/dispatch-acp.ts src/auto-reply/reply/dispatch-acp.test.ts src/auto-reply/reply/dispatch-from-config.test.ts src/auto-reply/reply/dispatch-acp-transcript.runtime.ts src/agents/agent-command.ts src/agents/agent-command.live-model-switch.test.ts src/agents/command/attempt-execution.ts src/agents/command/attempt-execution.cli.test.ts src/commands/agent.acp.test.ts src/commands/agent.test.ts src/sessions/user-turn-transcript.ts src/gateway/server-discovery-runtime.test.ts src/gateway/server.plugin-node-capability-auth.test.ts src/gateway/server.preauth-hardening.test.ts — passed.
  • pnpm tsgo:core:test — passed.
  • git diff --check origin/main...HEAD && git diff --check — passed.

Related

Related #37833
Sibling to #89152
Split from #71399

Latest head refresh (2026-06-22)

Current head: bbc4673b17f175529b491e534b600d79f2df2a8b

Merged current origin/main (3ff59df960) into the PR branch, resolved Plugin SDK surface report/test conflicts against the merged hook surface, and aligned the slash-command browser-import expectation with the current shared-registry import order.

Validation on the current head:

node scripts/test-projects.mjs src/acp/control-plane/manager.test.ts src/hooks/internal-hooks.test.ts src/agents/command/attempt-execution.cli.test.ts src/commands/agent.acp.test.ts src/commands/agent.test.ts src/auto-reply/reply/dispatch-acp.test.ts src/auto-reply/reply/dispatch-from-config.test.ts src/agents/agent-command.live-model-switch.test.ts
pnpm exec vitest run ui/src/ui/chat/slash-commands.browser-import.test.ts test/scripts/plugin-sdk-surface-report.test.ts

Observed results:

  • Focused ACP/save-hook validation passed 5 Vitest shards / 507 tests.
  • Focused slash-command import + Plugin SDK surface tests passed 2 files / 7 tests.

Limitations: the original source-built transcript-save proof harness was not present in this worktree during this refresh, so I did not rerun live transcript-save behavior proof on this head; the prior source-built proof remains from the earlier proof head.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation commands Command implementations agents Agent runtime and tooling size: XL triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 1, 2026
@clawsweeper

clawsweeper Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the contribution. ClawSweeper proposes closing this for now: the implementation may be reasonable, but passing review and proof does not establish that OpenClaw should add this product surface.

Close as unconfirmed product direction: the branch appears technically coherent and current main lacks this ACP transcript-save hook, but it adds a new internal hook/API contract and ACP save-ordering semantics without maintainer sponsorship.

Root-cause cluster
Relationship: canonical
Canonical: #89154
Summary: This PR is the current split candidate for the ACP transcript-save hook; the older umbrella PR was closed unmerged and the open sibling covers the separate turn-end hook.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

This is a proposal only until the separate default-off apply policy is enabled and all live maintainer-signal checks pass. A maintainer can sponsor the direction, request a narrower version, or apply clawsweeper:human-review to keep it open.

Review details

Best possible solution:

Close unless a maintainer sponsors this hook contract; if sponsored, reopen or refresh with explicit event/order acceptance and green current-head validation.

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

Not applicable: this is a feature/API PR, not a bug report; source inspection shows current main lacks the hook and the PR body includes terminal proof for the new behavior.

Is this the best way to solve the issue?

Unclear as a product direction: the implementation is coherent, but the new event contract and ACP save ordering need maintainer sponsorship before release.

Security review:

Security review cleared: No concrete security or supply-chain concern was found in the reviewed hook, transcript persistence, docs, test fixture, script, and incidental formatting changes.

AGENTS.md: found and applied where relevant.

What I checked:

  • Repository policy: Root AGENTS.md and scoped guides were read; root policy treats plugin APIs and session state/order changes as compatibility-sensitive maintainer decision surfaces. (AGENTS.md:14, 99551c499b01)
  • Current main lacks the hook: Current main passes a synchronous recordTurnCompletion into runManagerTurn, and that completion method only updates latency/error counters; rg found no agent:turn:transcript:save surface on main. (src/acp/control-plane/manager.core.ts:407, 99551c499b01)
  • Current dispatch ordering: Current main dispatch ACP path calls acpManager.runTurn, flushes the projector, and then persists the dispatch transcript afterward, so the PR intentionally changes the ordering boundary. (src/auto-reply/reply/dispatch-acp.ts:596, 99551c499b01)
  • PR hook contract: The PR diff adds AgentTurnSaveHookContext, AgentTurnSaveHookEvent, and isAgentTurnSaveEvent for agent:turn:transcript:save with saved/skipped/failed semantics. (src/hooks/internal-hooks.ts:164, bbc4673b17f1)
  • PR manager save boundary: The PR diff adds onBeforeTurnSaveHook, awaits save-result normalization, and emits the transcript-save hook from recordTurnCompletion. (src/acp/control-plane/manager.core.ts:317, bbc4673b17f1)
  • PR ordering change: The PR diff changes recordTurnCompletion to allow a promise and awaits it on both failure and success paths before later terminal/session-state work continues. (src/acp/control-plane/manager.turn-runner.ts:62, bbc4673b17f1)

Likely related people:

  • @vincentkoc: Current-main blame and recent history attribute the ACP manager, turn-runner, and hook registry lines this PR extends to Vincent Koc in fix(agents): freeze prompt projection candidates. (role: recent area contributor; confidence: high; commits: 62dcc9bc3b4f, c645ec4555c0; files: src/acp/control-plane/manager.core.ts, src/acp/control-plane/manager.turn-runner.ts, src/hooks/internal-hooks.ts)
  • @steipete: History shows Peter Steinberger introduced the internal hooks system and split agent command execution helpers used by ACP transcript persistence. (role: introduced adjacent hook and transcript helper surfaces; confidence: medium; commits: faba508fe0ae, 046a950877f6; files: src/hooks/internal-hooks.ts, src/agents/command/attempt-execution.ts)
  • @osolmaz: ACP thread-bound agents and the control-plane manager feature history appear in Onur Solmaz's merged ACP work, including manager and runtime lifecycle code this PR changes. (role: introduced ACP manager feature area; confidence: medium; commits: a7d56e3554d0; files: src/acp/control-plane/manager.core.ts, src/acp/control-plane/manager.turn-runner.ts)
  • Eric Lytle: History shows Eric Lytle changed the internal hook registry singleton behavior, which is adjacent to the hook delivery contract this PR expands. (role: adjacent hook registry contributor; confidence: medium; commits: 0d8beeb4e5f5; files: src/hooks/internal-hooks.ts)

Codex review notes: model internal, reasoning high; reviewed against 99551c499b01.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 1, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. feature: ✨ showcase ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. 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. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jun 1, 2026
@mantisai-bot mantisai-bot changed the title feat(hooks): add ACP turn save outcome hook feat(hooks): add ACP turn transcript save hook Jun 2, 2026
@mantisai-bot
mantisai-bot requested a review from a team as a code owner June 2, 2026 03:20
@openclaw-barnacle openclaw-barnacle Bot added the gateway Gateway runtime label Jun 2, 2026
@mantisai-bot
mantisai-bot force-pushed the feat/agent-turn-save-outcome-hook branch from 9db27aa to 2e6e45e Compare June 2, 2026 03:44
@mantisai-bot

Copy link
Copy Markdown
Author

ClawSweeper: please re-review this head. The previous append-evidence finding was addressed by preserving the append API appended flag through appendUserTurnTranscriptMessage, counting only fresh appended: true writes as saveOutcome: saved, and adding an idempotency replay regression that now returns saveOutcome: skipped / saveSkipReason: no_transcript_write. The PR body also now includes source-built installed-hook proof at head 4a7985a37c89030d877f1145d344dacf9d3436cc.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 2, 2026
@openclaw-barnacle openclaw-barnacle Bot added channel: discord Channel integration: discord channel: whatsapp-web Channel integration: whatsapp-web labels Jun 4, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 17, 2026
…outcome-hook

# Conflicts:
#	extensions/memory-core/src/memory/manager-fts-state.test.ts
#	scripts/plugin-sdk-surface-report.mjs
#	src/agents/agent-command.live-model-switch.test.ts
#	src/agents/agent-command.ts
#	src/agents/command/attempt-execution.cli.test.ts
#	src/agents/command/attempt-execution.ts
#	src/agents/embedded-agent-runner/run/llm-idle-timeout.test.ts
#	src/commands/agent.acp.test.ts
#	src/commands/agent.test.ts
#	src/gateway/server-discovery-runtime.test.ts
#	src/gateway/session-utils.ts
#	src/proxy-capture/store.sqlite.test.ts
#	ui/src/ui/views/workboard.ts
…outcome-hook

# Conflicts:
#	extensions/slack/src/monitor/send.runtime.ts
#	scripts/plugin-sdk-surface-report.mjs
#	src/agents/agent-tools.schema.test.ts
#	src/commands/onboard-channels.e2e.test.ts
…outcome-hook

# Conflicts:
#	scripts/plugin-sdk-surface-report.mjs
#	test/scripts/plugin-sdk-surface-report.test.ts
@mantisai-bot

Copy link
Copy Markdown
Author

@vincentkoc @steipete @osolmaz @Drickon ClawSweeper is proposing close here unless a maintainer sponsors the transcript-save hook contract or marks it for human review.

From our side, we still think this hook adds meaningful functionality: it exposes whether ACP transcript persistence actually saved, skipped, or failed, which is useful for integrations and diagnostics that care about durable turn capture rather than just turn completion.

What would you prefer we do with this PR?

  • close it as an unsponsored product/API surface
  • narrow the event/order contract further
  • keep it open under maintainer sponsorship / clawsweeper:human-review

Happy to rebase and clean up current-head CI once the direction is clear; the main question seems to be product/API ownership and ACP save-ordering acceptance rather than a concrete code blocker.

@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Closing this for now: it adds a new core hook/API surface plus transcript-save ordering semantics without maintainer sponsorship. The broader lifecycle-hook direction remains in #43454.

Happy to reconsider a focused replacement if maintainers explicitly sponsor the event name, payload, ordering, and compatibility contract. Thanks for doing the split and proof work here.

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

Labels

agents Agent runtime and tooling app: web-ui App: web-ui channel: discord Channel integration: discord channel: feishu Channel integration: feishu channel: irc channel: line Channel integration: line channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: qqbot channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: voice-call Channel integration: voice-call cli CLI command changes commands Command implementations docker Docker and sandbox tooling docs Improvements or additions to documentation extensions: amazon-bedrock extensions: chutes extensions: codex extensions: copilot extensions: deepinfra extensions: diffs extensions: kilocode extensions: kimi-coding extensions: memory-wiki extensions: minimax extensions: openrouter extensions: qa-lab feature: ✨ showcase ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. 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: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. scripts Repository scripts size: XL stale Marked as stale due to inactivity status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants