harnery 0.40.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commander.js +4 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +34 -20
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +113 -12
- package/dist/commands/browse-ai.d.ts.map +1 -1
- package/dist/commands/browse-ai.js +4 -0
- package/dist/commands/browse.d.ts +164 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +388 -45
- package/dist/commands/checkpoint.js +8 -12
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +52 -0
- package/dist/commands/deinit.d.ts +1 -1
- package/dist/commands/deinit.d.ts.map +1 -1
- package/dist/commands/deinit.js +8 -4
- package/dist/commands/disk.d.ts +6 -0
- package/dist/commands/disk.d.ts.map +1 -0
- package/dist/commands/disk.js +80 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +2 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +58 -3
- package/dist/commands/fetch.d.ts.map +1 -1
- package/dist/commands/fetch.js +10 -0
- package/dist/commands/files.d.ts +16 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -2
- package/dist/commands/init.d.ts +21 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +271 -109
- package/dist/commands/instructions.js +5 -4
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +2 -1
- package/dist/commands/rm.d.ts +4 -0
- package/dist/commands/rm.d.ts.map +1 -0
- package/dist/commands/rm.js +87 -0
- package/dist/commands/servers.d.ts +7 -0
- package/dist/commands/servers.d.ts.map +1 -0
- package/dist/commands/servers.js +394 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +82 -2
- package/dist/commands/webview.d.ts +4 -0
- package/dist/commands/webview.d.ts.map +1 -0
- package/dist/commands/webview.js +94 -0
- package/dist/core/adapter.d.ts +50 -1
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +59 -1
- package/dist/core/adapters/attestation.d.ts.map +1 -1
- package/dist/core/adapters/attestation.js +3 -2
- package/dist/core/adapters/profiles.d.ts +29 -3
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +53 -1
- package/dist/core/adapters/registry.d.ts.map +1 -1
- package/dist/core/adapters/registry.js +33 -0
- package/dist/core/adapters/types.d.ts +9 -0
- package/dist/core/adapters/types.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.d.ts +1 -1
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +2 -7
- package/dist/core/agents/cli.js +25 -16
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +7 -2
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +2 -0
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +3 -9
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +2 -8
- package/dist/core/agents/rules/stop-hook.d.ts +2 -0
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +1 -1
- package/dist/core/agents/session-events.d.ts +6 -2
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +105 -26
- package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -1
- package/dist/core/agents/session-finalization-state-v3.js +2 -1
- package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
- package/dist/core/agents/session-finalizer-v3.js +5 -4
- package/dist/core/agents/session-name-display.d.ts +28 -0
- package/dist/core/agents/session-name-display.d.ts.map +1 -1
- package/dist/core/agents/session-name-display.js +43 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.d.ts +10 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +21 -2
- package/dist/core/agents/state/live-coordination-view.d.ts +20 -0
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +61 -0
- package/dist/core/agents/state/live-coordination-writer.js +3 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +2 -10
- package/dist/core/agents/turn-changes.d.ts +17 -0
- package/dist/core/agents/turn-changes.d.ts.map +1 -0
- package/dist/core/agents/turn-changes.js +76 -0
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +11 -3
- package/dist/core/artifacts/index.d.ts +140 -4
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +526 -52
- package/dist/core/config.d.ts +73 -11
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +142 -3
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +2 -1
- package/dist/core/conversations/archive.d.ts.map +1 -1
- package/dist/core/conversations/archive.js +4 -3
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +5 -4
- package/dist/core/events/legacy-storage/canary.d.ts.map +1 -1
- package/dist/core/events/legacy-storage/canary.js +5 -4
- package/dist/core/events/legacy-storage/compression.d.ts.map +1 -1
- package/dist/core/events/legacy-storage/compression.js +2 -1
- package/dist/core/events/v3/adapter-id.d.ts +10 -2
- package/dist/core/events/v3/adapter-id.d.ts.map +1 -1
- package/dist/core/events/v3/adapter-id.js +16 -0
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +2 -1
- package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
- package/dist/core/events/v3/bootstrap.js +5 -4
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +17 -0
- package/dist/core/events/v3/contract.d.ts +5 -0
- package/dist/core/events/v3/contract.d.ts.map +1 -1
- package/dist/core/events/v3/contract.js +7 -1
- package/dist/core/events/v3/control-witness.d.ts.map +1 -1
- package/dist/core/events/v3/control-witness.js +3 -2
- package/dist/core/events/v3/coordination-transaction-recovery.d.ts.map +1 -1
- package/dist/core/events/v3/coordination-transaction-recovery.js +4 -3
- package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -1
- package/dist/core/events/v3/fingerprint-keys.js +7 -6
- package/dist/core/events/v3/generated.d.ts +1 -1
- package/dist/core/events/v3/generated.js +1 -1
- package/dist/core/events/v3/live-feed.d.ts.map +1 -1
- package/dist/core/events/v3/live-feed.js +5 -4
- package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/command-recorder.js +8 -7
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +8 -7
- package/dist/core/events/v3/producers/diagnostic-summaries.d.ts.map +1 -1
- package/dist/core/events/v3/producers/diagnostic-summaries.js +5 -4
- package/dist/core/events/v3/producers/hook-base.d.ts +1 -0
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +10 -4
- package/dist/core/events/v3/producers/hook.d.ts +2 -0
- package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook.js +16 -1
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +16 -6
- package/dist/core/events/v3/producers/recorder.d.ts +2 -4
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +125 -101
- package/dist/core/events/v3/projection.js +9 -1
- package/dist/core/events/v3/recovery.d.ts.map +1 -1
- package/dist/core/events/v3/recovery.js +4 -3
- package/dist/core/events/v3/support-storage/inventory.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/inventory.js +2 -1
- package/dist/core/events/v3/support-storage/maintenance.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/maintenance.js +4 -3
- package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-reader.js +4 -3
- package/dist/core/events/v3/support-storage/pack-writer.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-writer.js +6 -5
- package/dist/core/events/v3/tool-line-changes.d.ts +8 -0
- package/dist/core/events/v3/tool-line-changes.d.ts.map +1 -0
- package/dist/core/events/v3/tool-line-changes.js +135 -0
- package/dist/core/events/v3/writer.d.ts.map +1 -1
- package/dist/core/events/v3/writer.js +8 -7
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +6 -5
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +12 -11
- package/dist/core/governor/state.d.ts.map +1 -1
- package/dist/core/governor/state.js +7 -6
- package/dist/core/guard/evaluator.js +1 -0
- package/dist/core/guard/storage.d.ts.map +1 -1
- package/dist/core/guard/storage.js +3 -2
- package/dist/core/hooks/adapter/behaviors/claude-code.d.ts +4 -0
- package/dist/core/hooks/adapter/behaviors/claude-code.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/claude-code.js +31 -0
- package/dist/core/hooks/adapter/behaviors/codex.d.ts +15 -0
- package/dist/core/hooks/adapter/behaviors/codex.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/codex.js +82 -0
- package/dist/core/hooks/adapter/behaviors/cursor.d.ts +7 -0
- package/dist/core/hooks/adapter/behaviors/cursor.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/cursor.js +35 -0
- package/dist/core/hooks/adapter/behaviors/index.d.ts +11 -0
- package/dist/core/hooks/adapter/behaviors/index.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/index.js +20 -0
- package/dist/core/hooks/adapter/behaviors/openclaw.d.ts +7 -0
- package/dist/core/hooks/adapter/behaviors/openclaw.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/openclaw.js +34 -0
- package/dist/core/hooks/adapter/behaviors/opencode.d.ts +7 -0
- package/dist/core/hooks/adapter/behaviors/opencode.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/opencode.js +34 -0
- package/dist/core/hooks/adapter/behaviors/types.d.ts +89 -0
- package/dist/core/hooks/adapter/behaviors/types.d.ts.map +1 -0
- package/dist/core/hooks/adapter/behaviors/types.js +1 -0
- package/dist/core/hooks/adapter/detect.d.ts +1 -1
- package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
- package/dist/core/hooks/adapter/detect.js +2 -3
- package/dist/core/hooks/adapter/events.d.ts +46 -1
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +36 -0
- package/dist/core/hooks/adapter/opencode-plugin.d.ts +60 -0
- package/dist/core/hooks/adapter/opencode-plugin.d.ts.map +1 -0
- package/dist/core/hooks/adapter/opencode-plugin.js +129 -0
- package/dist/core/hooks/adapter/output.d.ts +7 -0
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +8 -1
- package/dist/core/hooks/adapter/runtime-telemetry.js +3 -1
- package/dist/core/hooks/adapter/wiring.d.ts +52 -8
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +133 -12
- package/dist/core/hooks/cli.js +676 -531
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +2 -1
- package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -1
- package/dist/core/hooks/prompt-context/contract.js +1 -0
- package/dist/core/hooks/resolve/transcript.d.ts +1 -1
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +35 -4
- package/dist/core/hooks/run-markers.d.ts +37 -0
- package/dist/core/hooks/run-markers.d.ts.map +1 -0
- package/dist/core/hooks/run-markers.js +109 -0
- package/dist/core/hooks/session-name-presence.d.ts +3 -1
- package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
- package/dist/core/hooks/session-name-presence.js +7 -2
- package/dist/core/hooks/shell-waiter.d.ts +2 -0
- package/dist/core/hooks/shell-waiter.d.ts.map +1 -0
- package/dist/core/hooks/shell-waiter.js +173 -0
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +5 -4
- package/dist/core/semantic/review.d.ts.map +1 -1
- package/dist/core/semantic/review.js +5 -4
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +15 -14
- package/dist/core/semantic/storage.d.ts.map +1 -1
- package/dist/core/semantic/storage.js +5 -4
- package/dist/core/servers/index.d.ts +211 -0
- package/dist/core/servers/index.d.ts.map +1 -0
- package/dist/core/servers/index.js +560 -0
- package/dist/core/servers/net.d.ts +58 -0
- package/dist/core/servers/net.d.ts.map +1 -0
- package/dist/core/servers/net.js +270 -0
- package/dist/core/servers/tunnels.d.ts +14 -0
- package/dist/core/servers/tunnels.d.ts.map +1 -0
- package/dist/core/servers/tunnels.js +33 -0
- package/dist/core/storage/atomic-json.d.ts.map +1 -1
- package/dist/core/storage/atomic-json.js +7 -3
- package/dist/core/storage/builtins.js +1 -0
- package/dist/core/storage/durable-history.d.ts.map +1 -1
- package/dist/core/storage/durable-history.js +6 -5
- package/dist/core/storage/maintenance-providers.d.ts.map +1 -1
- package/dist/core/storage/maintenance-providers.js +2 -1
- package/dist/core/storage/maintenance.d.ts.map +1 -1
- package/dist/core/storage/maintenance.js +7 -6
- package/dist/core/storage/metrics.d.ts.map +1 -1
- package/dist/core/storage/metrics.js +5 -1
- package/dist/core/storage/modes.d.ts +17 -0
- package/dist/core/storage/modes.d.ts.map +1 -0
- package/dist/core/storage/modes.js +47 -0
- package/dist/core/storage/process-log.d.ts.map +1 -1
- package/dist/core/storage/process-log.js +3 -2
- package/dist/core/storage/receipts.d.ts.map +1 -1
- package/dist/core/storage/receipts.js +4 -3
- package/dist/core/storage/segments.d.ts.map +1 -1
- package/dist/core/storage/segments.js +7 -6
- package/dist/core/supervisor/hook-health.d.ts +2 -1
- package/dist/core/supervisor/hook-health.d.ts.map +1 -1
- package/dist/core/supervisor/hook-health.js +4 -3
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +7 -6
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +8 -7
- package/dist/core/workflow/adapters.d.ts +7 -0
- package/dist/core/workflow/adapters.d.ts.map +1 -1
- package/dist/core/workflow/adapters.js +9 -0
- package/dist/core/workflow/approvals.d.ts.map +1 -1
- package/dist/core/workflow/approvals.js +6 -5
- package/dist/core/workflow/billing.d.ts.map +1 -1
- package/dist/core/workflow/billing.js +33 -4
- package/dist/core/workflow/durable-record.d.ts.map +1 -1
- package/dist/core/workflow/durable-record.js +4 -3
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +30 -1
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +2 -3
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +3 -2
- package/dist/core/workflow/sandbox-projection.d.ts +13 -2
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +23 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +6 -1
- package/dist/core/workflow/spawn-codex.d.ts +2 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +20 -1
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +6 -1
- package/dist/core/workflow/spawn-opencode.d.ts +36 -0
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -0
- package/dist/core/workflow/spawn-opencode.js +135 -0
- package/dist/core/workflow/transcript.d.ts.map +1 -1
- package/dist/core/workflow/transcript.js +3 -2
- package/dist/core/workflow/types.d.ts +47 -2
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/worker-access.d.ts +92 -0
- package/dist/core/workflow/worker-access.d.ts.map +1 -0
- package/dist/core/workflow/worker-access.js +450 -0
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +5 -4
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +3 -2
- package/dist/core/workflow/workspaces/paths.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/paths.js +2 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +10 -9
- package/dist/lazy-commands.js +8 -1
- package/dist/lib/agent-browser/client.d.ts +9 -0
- package/dist/lib/agent-browser/client.d.ts.map +1 -1
- package/dist/lib/agent-browser/client.js +13 -0
- package/dist/lib/browser/batch-steps.d.ts +18 -0
- package/dist/lib/browser/batch-steps.d.ts.map +1 -0
- package/dist/lib/browser/batch-steps.js +82 -0
- package/dist/lib/browser/client.d.ts +35 -1
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +117 -8
- package/dist/lib/browser/collect/collector.d.ts +99 -0
- package/dist/lib/browser/collect/collector.d.ts.map +1 -0
- package/dist/lib/browser/collect/collector.js +202 -0
- package/dist/lib/browser/collect/fields.d.ts +31 -0
- package/dist/lib/browser/collect/fields.d.ts.map +1 -0
- package/dist/lib/browser/collect/fields.js +75 -0
- package/dist/lib/browser/collect/index.d.ts +6 -0
- package/dist/lib/browser/collect/index.d.ts.map +1 -0
- package/dist/lib/browser/collect/index.js +5 -0
- package/dist/lib/browser/collect/page-driver.d.ts +14 -0
- package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
- package/dist/lib/browser/collect/page-driver.js +306 -0
- package/dist/lib/browser/collect/presets.d.ts +52 -0
- package/dist/lib/browser/collect/presets.d.ts.map +1 -0
- package/dist/lib/browser/collect/presets.js +176 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
- package/dist/lib/browser/collect/scroll-plan.js +118 -0
- package/dist/lib/browser/identity.d.ts +25 -0
- package/dist/lib/browser/identity.d.ts.map +1 -0
- package/dist/lib/browser/identity.js +66 -0
- package/dist/lib/browser/index.d.ts +3 -2
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -1
- package/dist/lib/browser/launch-args.d.ts +26 -0
- package/dist/lib/browser/launch-args.d.ts.map +1 -1
- package/dist/lib/browser/launch-args.js +41 -1
- package/dist/lib/browser/page-review-judge.d.ts +2 -0
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +13 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +1 -0
- package/dist/lib/browser/user-agent.d.ts +53 -0
- package/dist/lib/browser/user-agent.d.ts.map +1 -0
- package/dist/lib/browser/user-agent.js +79 -0
- package/dist/lib/browser/windows-chrome.d.ts +59 -0
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
- package/dist/lib/browser/windows-chrome.js +179 -0
- package/dist/lib/claude-desktop-share.d.ts +136 -0
- package/dist/lib/claude-desktop-share.d.ts.map +1 -0
- package/dist/lib/claude-desktop-share.js +394 -0
- package/dist/lib/disk-usage.d.ts +50 -0
- package/dist/lib/disk-usage.d.ts.map +1 -0
- package/dist/lib/disk-usage.js +303 -0
- package/dist/lib/docs-lint.d.ts +6 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +8 -3
- package/dist/lib/docs-metadata-managed.d.ts +1 -0
- package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
- package/dist/lib/docs-metadata-managed.js +17 -1
- package/dist/lib/guarded-remove.d.ts +34 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -0
- package/dist/lib/guarded-remove.js +310 -0
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +4 -9
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +2 -0
- package/dist/lib/instructions/templates.js +8 -8
- package/dist/lib/pace/index.d.ts +2 -0
- package/dist/lib/pace/index.d.ts.map +1 -0
- package/dist/lib/pace/index.js +1 -0
- package/dist/lib/pace/pace.d.ts +95 -0
- package/dist/lib/pace/pace.d.ts.map +1 -0
- package/dist/lib/pace/pace.js +313 -0
- package/dist/lib/tunnel/error-page.d.ts +1 -1
- package/dist/lib/tunnel/error-page.d.ts.map +1 -1
- package/dist/lib/tunnel/error-page.js +19 -8
- package/dist/lib/tunnel/forward-headers.d.ts +11 -0
- package/dist/lib/tunnel/forward-headers.d.ts.map +1 -0
- package/dist/lib/tunnel/forward-headers.js +17 -0
- package/dist/lib/tunnel/gate.js +34 -10
- package/dist/lib/tunnel/path-scope.d.ts +22 -0
- package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
- package/dist/lib/tunnel/path-scope.js +62 -0
- package/dist/lib/tunnel/state.d.ts +5 -0
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +17 -3
- package/dist/lib/webview/index.d.ts +2 -0
- package/dist/lib/webview/index.d.ts.map +1 -0
- package/dist/lib/webview/index.js +1 -0
- package/dist/lib/webview/webview.d.ts +116 -0
- package/dist/lib/webview/webview.d.ts.map +1 -0
- package/dist/lib/webview/webview.js +289 -0
- package/opencode-plugin/package.json +19 -0
- package/opencode-plugin/src/index.ts +791 -0
- package/package.json +16 -4
- package/schemas/config.schema.json +104 -0
- package/schemas/event-v3.schema.json +164 -0
- package/src/commander.ts +26 -0
- package/src/commands/agents.ts +47 -18
- package/src/commands/artifacts.ts +171 -19
- package/src/commands/browse-ai.ts +8 -0
- package/src/commands/browse.ts +570 -48
- package/src/commands/checkpoint.ts +7 -9
- package/src/commands/claude-desktop.ts +61 -0
- package/src/commands/deinit.ts +9 -4
- package/src/commands/disk.ts +114 -0
- package/src/commands/docs.ts +2 -0
- package/src/commands/doctor.ts +63 -3
- package/src/commands/fetch.ts +14 -0
- package/src/commands/files.ts +93 -2
- package/src/commands/init.ts +334 -122
- package/src/commands/instructions.ts +8 -4
- package/src/commands/ledger-v3.ts +2 -1
- package/src/commands/rm.ts +115 -0
- package/src/commands/servers.ts +506 -0
- package/src/commands/tunnel.ts +102 -2
- package/src/commands/webview.ts +159 -0
- package/src/core/adapter.ts +102 -1
- package/src/core/adapters/attestation.ts +3 -2
- package/src/core/adapters/profiles.ts +63 -1
- package/src/core/adapters/registry.ts +37 -0
- package/src/core/adapters/types.ts +10 -0
- package/src/core/agents/canonical-emit.ts +2 -5
- package/src/core/agents/cli.ts +26 -17
- package/src/core/agents/coord-client.ts +9 -3
- package/src/core/agents/render/session-context.ts +1 -0
- package/src/core/agents/rules/claim-conflict.ts +3 -8
- package/src/core/agents/rules/commit-conflict.ts +2 -7
- package/src/core/agents/rules/stop-hook.ts +3 -1
- package/src/core/agents/session-events.ts +120 -26
- package/src/core/agents/session-finalization-state-v3.ts +2 -1
- package/src/core/agents/session-finalizer-v3.ts +5 -4
- package/src/core/agents/session-name-display.ts +48 -0
- package/src/core/agents/state/heartbeat-reader.ts +7 -0
- package/src/core/agents/state/heartbeat-writer.ts +24 -2
- package/src/core/agents/state/live-coordination-view.ts +74 -0
- package/src/core/agents/state/live-coordination-writer.ts +3 -0
- package/src/core/agents/state/stale-sweep.ts +2 -9
- package/src/core/agents/turn-changes.ts +97 -0
- package/src/core/artifacts/delivery-card.ts +12 -3
- package/src/core/artifacts/index.ts +701 -47
- package/src/core/config.ts +206 -4
- package/src/core/context/index.ts +2 -1
- package/src/core/conversations/archive.ts +4 -3
- package/src/core/diagnostics/bundle.ts +5 -3
- package/src/core/events/legacy-storage/canary.ts +5 -4
- package/src/core/events/legacy-storage/compression.ts +2 -1
- package/src/core/events/v3/adapter-id.ts +29 -1
- package/src/core/events/v3/authority-outbox.ts +2 -1
- package/src/core/events/v3/bootstrap.ts +5 -4
- package/src/core/events/v3/capabilities.ts +17 -0
- package/src/core/events/v3/contract.ts +9 -1
- package/src/core/events/v3/control-witness.ts +3 -2
- package/src/core/events/v3/coordination-transaction-recovery.ts +4 -3
- package/src/core/events/v3/fingerprint-keys.ts +7 -6
- package/src/core/events/v3/generated.ts +1 -1
- package/src/core/events/v3/live-feed.ts +5 -4
- package/src/core/events/v3/producers/command-recorder.ts +8 -6
- package/src/core/events/v3/producers/coordination-recorder.ts +8 -7
- package/src/core/events/v3/producers/diagnostic-summaries.ts +5 -4
- package/src/core/events/v3/producers/hook-base.ts +11 -4
- package/src/core/events/v3/producers/hook.ts +19 -1
- package/src/core/events/v3/producers/intake.ts +18 -6
- package/src/core/events/v3/producers/recorder.ts +139 -114
- package/src/core/events/v3/projection.ts +11 -1
- package/src/core/events/v3/recovery.ts +4 -3
- package/src/core/events/v3/support-storage/inventory.ts +2 -1
- package/src/core/events/v3/support-storage/maintenance.ts +8 -3
- package/src/core/events/v3/support-storage/pack-reader.ts +4 -3
- package/src/core/events/v3/support-storage/pack-writer.ts +6 -5
- package/src/core/events/v3/tool-line-changes.ts +139 -0
- package/src/core/events/v3/writer.ts +8 -7
- package/src/core/governor/planning.ts +6 -5
- package/src/core/governor/service.ts +12 -11
- package/src/core/governor/state.ts +7 -6
- package/src/core/guard/evaluator.ts +1 -0
- package/src/core/guard/storage.ts +3 -2
- package/src/core/hooks/adapter/behaviors/claude-code.ts +33 -0
- package/src/core/hooks/adapter/behaviors/codex.ts +96 -0
- package/src/core/hooks/adapter/behaviors/cursor.ts +37 -0
- package/src/core/hooks/adapter/behaviors/index.ts +31 -0
- package/src/core/hooks/adapter/behaviors/openclaw.ts +36 -0
- package/src/core/hooks/adapter/behaviors/opencode.ts +36 -0
- package/src/core/hooks/adapter/behaviors/types.ts +110 -0
- package/src/core/hooks/adapter/detect.ts +2 -3
- package/src/core/hooks/adapter/events.ts +72 -1
- package/src/core/hooks/adapter/opencode-plugin.ts +177 -0
- package/src/core/hooks/adapter/output.ts +8 -1
- package/src/core/hooks/adapter/runtime-telemetry.ts +3 -1
- package/src/core/hooks/adapter/wiring.ts +198 -16
- package/src/core/hooks/cli.ts +776 -539
- package/src/core/hooks/effects/image-capture.ts +2 -1
- package/src/core/hooks/prompt-context/contract.ts +1 -0
- package/src/core/hooks/resolve/transcript.ts +42 -5
- package/src/core/hooks/run-markers.ts +132 -0
- package/src/core/hooks/session-name-presence.ts +10 -3
- package/src/core/hooks/shell-waiter.ts +166 -0
- package/src/core/resources/service.ts +5 -4
- package/src/core/semantic/review.ts +5 -4
- package/src/core/semantic/service.ts +15 -14
- package/src/core/semantic/storage.ts +5 -4
- package/src/core/servers/index.ts +712 -0
- package/src/core/servers/net.ts +283 -0
- package/src/core/servers/tunnels.ts +39 -0
- package/src/core/storage/atomic-json.ts +7 -3
- package/src/core/storage/builtins.ts +1 -0
- package/src/core/storage/durable-history.ts +6 -5
- package/src/core/storage/maintenance-providers.ts +2 -1
- package/src/core/storage/maintenance.ts +7 -6
- package/src/core/storage/metrics.ts +5 -1
- package/src/core/storage/modes.ts +53 -0
- package/src/core/storage/process-log.ts +3 -2
- package/src/core/storage/receipts.ts +4 -3
- package/src/core/storage/segments.ts +7 -6
- package/src/core/supervisor/hook-health.ts +4 -4
- package/src/core/supervisor/service.ts +7 -6
- package/src/core/work/state.ts +8 -7
- package/src/core/workflow/adapters.ts +15 -0
- package/src/core/workflow/approvals.ts +6 -5
- package/src/core/workflow/billing.ts +34 -4
- package/src/core/workflow/durable-record.ts +4 -3
- package/src/core/workflow/engine.ts +38 -1
- package/src/core/workflow/live-session-v3.ts +2 -3
- package/src/core/workflow/run-state.ts +3 -2
- package/src/core/workflow/sandbox-projection.ts +38 -2
- package/src/core/workflow/spawn-claude.ts +5 -1
- package/src/core/workflow/spawn-codex.ts +18 -1
- package/src/core/workflow/spawn-cursor.ts +5 -1
- package/src/core/workflow/spawn-opencode.ts +153 -0
- package/src/core/workflow/transcript.ts +3 -2
- package/src/core/workflow/types.ts +62 -2
- package/src/core/workflow/worker-access.ts +525 -0
- package/src/core/workflow/workspaces/leases.ts +5 -4
- package/src/core/workflow/workspaces/local-git.ts +3 -2
- package/src/core/workflow/workspaces/paths.ts +2 -1
- package/src/core/workflow/workspaces/state.ts +10 -9
- package/src/lazy-commands.ts +11 -1
- package/src/lib/agent-browser/client.ts +18 -0
- package/src/lib/browser/batch-steps.ts +79 -0
- package/src/lib/browser/client.ts +147 -9
- package/src/lib/browser/collect/collector.ts +306 -0
- package/src/lib/browser/collect/fields.ts +87 -0
- package/src/lib/browser/collect/index.ts +47 -0
- package/src/lib/browser/collect/page-driver.ts +327 -0
- package/src/lib/browser/collect/presets.ts +237 -0
- package/src/lib/browser/collect/scroll-plan.ts +179 -0
- package/src/lib/browser/identity.ts +64 -0
- package/src/lib/browser/index.ts +16 -1
- package/src/lib/browser/launch-args.ts +53 -1
- package/src/lib/browser/page-review-judge.ts +16 -0
- package/src/lib/browser/qa-run-contracts.ts +11 -0
- package/src/lib/browser/qa-run.ts +1 -0
- package/src/lib/browser/user-agent.ts +130 -0
- package/src/lib/browser/windows-chrome.ts +227 -0
- package/src/lib/claude-desktop-share.ts +511 -0
- package/src/lib/disk-usage.ts +347 -0
- package/src/lib/docs-lint.ts +8 -3
- package/src/lib/docs-metadata-managed.ts +21 -0
- package/src/lib/guarded-remove.ts +376 -0
- package/src/lib/identities/assume.ts +7 -7
- package/src/lib/instructions/apply.ts +2 -0
- package/src/lib/instructions/templates.ts +8 -8
- package/src/lib/pace/index.ts +14 -0
- package/src/lib/pace/pace.ts +370 -0
- package/src/lib/tunnel/error-page.ts +25 -9
- package/src/lib/tunnel/forward-headers.ts +16 -0
- package/src/lib/tunnel/gate.ts +39 -10
- package/src/lib/tunnel/path-scope.ts +73 -0
- package/src/lib/tunnel/state.ts +20 -3
- package/src/lib/webview/index.ts +13 -0
- package/src/lib/webview/webview.ts +457 -0
package/dist/core/hooks/cli.js
CHANGED
|
@@ -21,12 +21,13 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
21
21
|
import { dirname, join } from "node:path";
|
|
22
22
|
import { coordEnv } from "../../lib/env.js";
|
|
23
23
|
import { buildInstructionBundle } from "../../lib/instructions/bundle.js";
|
|
24
|
+
import { adapterFromPlatform as sharedAdapterFromPlatform } from "../adapter.js";
|
|
24
25
|
import { resolveCoordRoot } from "../agents/coord-client.js";
|
|
25
26
|
import { classifyWriteClaimFinalization, formatWriteClaimFinalizationDenial, } from "../agents/finalization.js";
|
|
26
27
|
import { recordLiveClaimChangeV3, restoreLiveCoordinationStateAfterEpochV3, } from "../agents/live-authority-v3.js";
|
|
27
28
|
import { evaluateStopHook, STOP_REMEDIATION_MARKER } from "../agents/rules/stop-hook.js";
|
|
28
|
-
import { assistantTextStartsWithSessionNameBlock, isSessionNameRemediationCommand, matchSessionNameDisplay, sessionNameDisplayAcceptedNames, sessionNameDisplayInstruction, sessionNameDisplayPending, sessionNameDisplayRecoveryInstruction, toolResponseMintedSessionName, } from "../agents/session-name-display.js";
|
|
29
|
-
import { readHeartbeat, setAssignedNameCache, stampSessionNameRequested, stampSessionNameSeen, } from "../agents/state/heartbeat-writer.js";
|
|
29
|
+
import { assistantTextStartsWithSessionNameBlock, isSessionNameRemediationCommand, matchSessionNameDisplay, sessionNameDisplayAcceptedNames, sessionNameDisplayInstruction, sessionNameDisplayPending, sessionNameDisplayRecoveryInstruction, sessionNameDisplayReminder, sessionNameDisplayReminderDue, toolResponseMintedSessionName, } from "../agents/session-name-display.js";
|
|
30
|
+
import { readHeartbeat, setAssignedNameCache, stampSessionNameReminded, stampSessionNameRequested, stampSessionNameSeen, } from "../agents/state/heartbeat-writer.js";
|
|
30
31
|
import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
|
|
31
32
|
import { ensureLiveCoordinationHeartbeat } from "../agents/state/live-coordination-writer.js";
|
|
32
33
|
import { assignName } from "../agents/state/names.js";
|
|
@@ -39,11 +40,12 @@ import { captureSpanClockV3 } from "../events/v3/span-state.js";
|
|
|
39
40
|
import { ensureRelayDaemon, fetchPresence, publishPresence } from "../presence/index.js";
|
|
40
41
|
import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.js";
|
|
41
42
|
import { stableScopeId } from "../workflow/scope-id.js";
|
|
43
|
+
import { adapterBehavior } from "./adapter/behaviors/index.js";
|
|
42
44
|
import { detectAdapter, shouldSkipHookAdapter } from "./adapter/detect.js";
|
|
45
|
+
import { ADAPTER_SPECS } from "./adapter/events.js";
|
|
43
46
|
import { extractBashCommand, extractToolDescription, normalizeEventName, parsePayload, } from "./adapter/parse.js";
|
|
44
|
-
import {
|
|
47
|
+
import { hookTimeoutFor } from "./adapter/wiring.js";
|
|
45
48
|
import { scheduleBackupSnapshot } from "./backup-schedule.js";
|
|
46
|
-
import { codexWslFileLinkTelemetry, inspectCodexWslBridge, isWslUncPath, renderCodexWslFileLinkContext, } from "./codex-wsl-bridge.js";
|
|
47
49
|
import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, recordImageArtifactsV3, resetSoundCounters, runSessionSyncExtension, soundForEvent, } from "./effects/index.js";
|
|
48
50
|
import { toolInputHash, toolTargetHash } from "./events/input-hash.js";
|
|
49
51
|
import { canonicalize } from "./guard-path.js";
|
|
@@ -51,10 +53,12 @@ import { beginHookHealth, observeHookDebug, observeHookError, writeHookHealthCom
|
|
|
51
53
|
import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.js";
|
|
52
54
|
import { extractIntentComment, resolveIntent } from "./resolve/intent.js";
|
|
53
55
|
import { resolveOwner } from "./resolve/owner.js";
|
|
54
|
-
import { inspectSessionNameDisplayImmediately, scanAssistantStatusBoxPresent,
|
|
56
|
+
import { inspectSessionNameDisplayImmediately, scanAssistantStatusBoxPresent, scanStatusBoxPresent, scanTranscriptRuntime, } from "./resolve/transcript.js";
|
|
57
|
+
import { clearHookRunMarker, writeHookRunMarker } from "./run-markers.js";
|
|
55
58
|
import { sessionNamePresence } from "./session-name-presence.js";
|
|
56
|
-
import {
|
|
59
|
+
import { shellWaiterReason } from "./shell-waiter.js";
|
|
57
60
|
let hookHealthState;
|
|
61
|
+
let hookRunMarkerPath = null;
|
|
58
62
|
function parseArgv(argv) {
|
|
59
63
|
const out = { eventName: null, extra: [] };
|
|
60
64
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -223,11 +227,9 @@ function buildEventData(eventType, ctx) {
|
|
|
223
227
|
const p = ctx.payload;
|
|
224
228
|
switch (eventType) {
|
|
225
229
|
case "session.started": {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
? "cursor"
|
|
230
|
-
: "codex";
|
|
230
|
+
// The platform label is the adapter id itself; enumerating and defaulting
|
|
231
|
+
// to codex silently mislabels any adapter not in the list.
|
|
232
|
+
const adapterPlatform = ctx.adapter;
|
|
231
233
|
// Recorded fork lineage is NOT detected here. On claude-code a fork
|
|
232
234
|
// never fires its own session.started — SessionStart fires under the
|
|
233
235
|
// PARENT's session id (source=resume) before the fork id is minted
|
|
@@ -238,7 +240,11 @@ function buildEventData(eventType, ctx) {
|
|
|
238
240
|
const forkedFrom = undefined;
|
|
239
241
|
// Assign (or recover) name + kind in-process. Idempotent: resume
|
|
240
242
|
// returns the original name; new owner consumes a counter slot.
|
|
241
|
-
|
|
243
|
+
// Workflow children stay unnamed, as on the tool path: a pool name
|
|
244
|
+
// would make them the addressee of mail held for that name.
|
|
245
|
+
const assigned = coordEnv("WORKFLOW_CHILD") === "1"
|
|
246
|
+
? undefined
|
|
247
|
+
: assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
|
|
242
248
|
// Write the adapter pid-map row so `harn agents whoami` ppid-walks find
|
|
243
249
|
// this owner. Prefer the payload pid (the actual claude binary), then the
|
|
244
250
|
// anchor walk (the `node` ancestor for Cursor, which has no payload pid),
|
|
@@ -301,7 +307,8 @@ function buildEventData(eventType, ctx) {
|
|
|
301
307
|
// SessionStart. Mint durable display identity here so a later
|
|
302
308
|
// SessionStart remains idempotent and Codec is not stuck on the
|
|
303
309
|
// 8-character native id.
|
|
304
|
-
if (
|
|
310
|
+
if (coordEnv("WORKFLOW_CHILD") !== "1" &&
|
|
311
|
+
!readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
|
|
305
312
|
assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session");
|
|
306
313
|
}
|
|
307
314
|
const prompt = p?.prompt ?? "";
|
|
@@ -310,9 +317,7 @@ function buildEventData(eventType, ctx) {
|
|
|
310
317
|
}
|
|
311
318
|
case "turn.completed": {
|
|
312
319
|
const lastAssistantMessage = p?.raw.last_assistant_message ?? "";
|
|
313
|
-
const fileLinkTelemetry = ctx.adapter
|
|
314
|
-
? codexWslFileLinkTelemetry(ctx.coordRoot, p?.cwd, lastAssistantMessage)
|
|
315
|
-
: null;
|
|
320
|
+
const fileLinkTelemetry = adapterBehavior(ctx.adapter).fileLinkTelemetry?.(ctx.coordRoot, p?.cwd, lastAssistantMessage) ?? null;
|
|
316
321
|
return {
|
|
317
322
|
// Backfill the model for adapters that omit it at session.started
|
|
318
323
|
// (Claude Code). The transcript is populated with assistant turns by
|
|
@@ -343,13 +348,12 @@ function buildEventData(eventType, ctx) {
|
|
|
343
348
|
// and which name that refers to. Reported on every stop while a name
|
|
344
349
|
// exists (the stop-hook.session_name verdict reads it per turn); the
|
|
345
350
|
// transcript scan itself stops once the name has been sighted.
|
|
346
|
-
...sessionNamePresence(ctx.coordRoot, ctx.instanceId, (name) =>
|
|
347
|
-
//
|
|
348
|
-
// session id so the naming ritual can
|
|
351
|
+
...sessionNamePresence(ctx.coordRoot, ctx.instanceId, (name) => inspectSessionNameDisplayImmediately(
|
|
352
|
+
// Some adapters omit transcript_path on stop; the behavior may
|
|
353
|
+
// discover the transcript by session id so the naming ritual can
|
|
354
|
+
// verify and stamp there too.
|
|
349
355
|
p?.transcript_path ??
|
|
350
|
-
(ctx.adapter
|
|
351
|
-
? discoverCodexSessionTranscript(p?.session_id ?? ctx.instanceId)
|
|
352
|
-
: undefined), name, assistantTextStartsWithSessionNameBlock)),
|
|
356
|
+
adapterBehavior(ctx.adapter).discoverTranscript?.(p?.session_id ?? ctx.instanceId), name, assistantTextStartsWithSessionNameBlock)),
|
|
353
357
|
...(fileLinkTelemetry ?? {}),
|
|
354
358
|
stop_hook_active: p?.stop_hook_active,
|
|
355
359
|
};
|
|
@@ -479,6 +483,24 @@ async function main() {
|
|
|
479
483
|
const hookClock = captureSpanClockV3();
|
|
480
484
|
const { eventName, extra } = parseArgv(process.argv.slice(2));
|
|
481
485
|
const adapter = detectAdapter(process.argv.slice(2));
|
|
486
|
+
const behavior = adapter ? adapterBehavior(adapter) : null;
|
|
487
|
+
// Adapters that kill a hook at its timeout get a start marker, so a killed
|
|
488
|
+
// run leaves evidence for doctor (ADR 0197). Written before stdin is read.
|
|
489
|
+
const markerSpec = adapter ? ADAPTER_SPECS[adapter] : undefined;
|
|
490
|
+
const markerTimeout = markerSpec && eventName && markerSpec.events.some((e) => e.subcommand === eventName)
|
|
491
|
+
? hookTimeoutFor(markerSpec, eventName)
|
|
492
|
+
: undefined;
|
|
493
|
+
if (markerTimeout !== undefined && adapter && eventName && coordEnv("AGENT_COORD_OFF") !== "1") {
|
|
494
|
+
const markerRoot = resolveCoordRoot(process.cwd());
|
|
495
|
+
if (markerRoot) {
|
|
496
|
+
hookRunMarkerPath = writeHookRunMarker(markerRoot, {
|
|
497
|
+
event: eventName,
|
|
498
|
+
adapter,
|
|
499
|
+
pid: process.pid,
|
|
500
|
+
timeout_sec: markerTimeout,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
482
504
|
const raw = await readStdin();
|
|
483
505
|
hookHealthState = beginHookHealth({
|
|
484
506
|
started_at_ms: hookStartedAt,
|
|
@@ -516,8 +538,8 @@ async function main() {
|
|
|
516
538
|
// Kill-switch-INDEPENDENT effects: notification sounds fire BEFORE the
|
|
517
539
|
// HARNERY_AGENT_COORD_OFF gate so audible feedback survives incident-triage
|
|
518
540
|
// bypass: sound playback happens before the kill-switch bailout.
|
|
519
|
-
//
|
|
520
|
-
if (
|
|
541
|
+
// Behavior-gated; stop-failure → error, sub-agent-start → subagent-start.
|
|
542
|
+
if (behavior?.soundEffects && eventName) {
|
|
521
543
|
const s = soundForEvent(eventName);
|
|
522
544
|
if (s) {
|
|
523
545
|
const repoRoot = resolveCoordRoot(process.cwd());
|
|
@@ -568,7 +590,7 @@ async function main() {
|
|
|
568
590
|
pid: process.pid,
|
|
569
591
|
ppid: process.ppid,
|
|
570
592
|
};
|
|
571
|
-
if (!eventName || !adapter) {
|
|
593
|
+
if (!eventName || !adapter || !behavior) {
|
|
572
594
|
appendDebug(coordRoot, { ...debugBase, skipped: "missing-event-or-adapter" });
|
|
573
595
|
return 0;
|
|
574
596
|
}
|
|
@@ -579,90 +601,17 @@ async function main() {
|
|
|
579
601
|
// assistant text. Preserve only privacy-safe ritual booleans in the V3
|
|
580
602
|
// producer's open-turn state; Stop consumes them into the authoritative
|
|
581
603
|
// turn.completed event. The response body never enters V3 or heartbeat.
|
|
582
|
-
if (
|
|
583
|
-
|
|
584
|
-
const owner = resolveOwner({ payload: payload?.raw ?? null, coordRoot });
|
|
585
|
-
if (!owner) {
|
|
586
|
-
appendDebug(coordRoot, { ...debugBase, skipped: "no-owner-resolved" });
|
|
587
|
-
return 0;
|
|
588
|
-
}
|
|
589
|
-
const row = readLiveCoordinationRow(coordRoot, owner.instance_id);
|
|
590
|
-
const name = sessionNameDisplayPending(row);
|
|
591
|
-
const text = typeof payload?.raw.text === "string" ? payload.raw.text : "";
|
|
592
|
-
const sighting = matchSessionNameDisplay(row, text);
|
|
593
|
-
if (sighting) {
|
|
594
|
-
stampSessionNameSeen(coordRoot, owner.instance_id, sighting.pending);
|
|
595
|
-
appendDebug(coordRoot, {
|
|
596
|
-
...debugBase,
|
|
597
|
-
effect: "session-name-display-stamped",
|
|
598
|
-
session_name_pending: sighting.pending,
|
|
599
|
-
...(sighting.displayed === sighting.pending
|
|
600
|
-
? {}
|
|
601
|
-
: { session_name_displayed: sighting.displayed, session_name_drift: true }),
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
else if (!name) {
|
|
605
|
-
// Nothing was owed. Distinct from a miss: conflating the two is what made
|
|
606
|
-
// a latched Cursor session undiagnosable from this log.
|
|
607
|
-
appendDebug(coordRoot, { ...debugBase, skipped: "no-pending-session-name" });
|
|
608
|
-
}
|
|
609
|
-
else {
|
|
610
|
-
appendDebug(coordRoot, {
|
|
611
|
-
...debugBase,
|
|
612
|
-
skipped: "session-name-block-absent",
|
|
613
|
-
session_name_pending: name,
|
|
614
|
-
reply_bytes: text.length,
|
|
615
|
-
reply_leads_with_fence: /^\s*`{3,}/.test(text),
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
if (ledgerRoute.state === "blocked") {
|
|
619
|
-
appendDebug(coordRoot, {
|
|
620
|
-
...debugBase,
|
|
621
|
-
skipped: "v3-control-blocked",
|
|
622
|
-
reason: ledgerRoute.reason,
|
|
623
|
-
owner_source: owner.source,
|
|
624
|
-
});
|
|
625
|
-
return 0;
|
|
626
|
-
}
|
|
627
|
-
const statusBoxPresentStrict = scanAssistantStatusBoxPresent(undefined, text);
|
|
628
|
-
// The hook parser needs the completed reply long enough to scan it, but
|
|
629
|
-
// durable hook intake must never retain that body. Keep every other native
|
|
630
|
-
// field so turn correlation remains available to the producer.
|
|
631
|
-
const { text: _completedReply, ...privacySafeRaw } = payload?.raw ?? {};
|
|
632
|
-
const privacySafePayload = payload ? { ...payload, raw: privacySafeRaw } : null;
|
|
633
|
-
const v3Result = recordLiveHookSignalV3({
|
|
604
|
+
if (behavior && eventName && eventName === behavior.completedResponseEvent) {
|
|
605
|
+
return handleCompletedResponse({
|
|
634
606
|
coordRoot,
|
|
635
|
-
route: ledgerRoute,
|
|
636
|
-
eventName,
|
|
637
|
-
payload: privacySafePayload,
|
|
638
607
|
adapter,
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
status_box_present_strict: statusBoxPresentStrict,
|
|
646
|
-
session_name_required: name !== undefined,
|
|
647
|
-
session_name_present: name ? assistantTextStartsWithSessionNameBlock(text, name) : false,
|
|
648
|
-
},
|
|
649
|
-
});
|
|
650
|
-
observeHookDebug(hookHealthState, { event_v3_state: v3Result.state });
|
|
651
|
-
appendDebug(coordRoot, {
|
|
652
|
-
...debugBase,
|
|
653
|
-
effect: "cursor-response-ritual-observed",
|
|
654
|
-
owner_source: owner.source,
|
|
655
|
-
event_v3_state: v3Result.state,
|
|
656
|
-
...(v3Result.state === "observed"
|
|
657
|
-
? {
|
|
658
|
-
generation_id: v3Result.generation_id,
|
|
659
|
-
turn_id: v3Result.turn_id,
|
|
660
|
-
response_observed_at: v3Result.observed_at,
|
|
661
|
-
status_box_present_strict: statusBoxPresentStrict,
|
|
662
|
-
}
|
|
663
|
-
: {}),
|
|
608
|
+
eventName,
|
|
609
|
+
raw,
|
|
610
|
+
debugBase,
|
|
611
|
+
ledgerRoute,
|
|
612
|
+
hookStartedAt,
|
|
613
|
+
hookClock,
|
|
664
614
|
});
|
|
665
|
-
return 0;
|
|
666
615
|
}
|
|
667
616
|
const norm = normalizeEventName(eventName);
|
|
668
617
|
if (!norm) {
|
|
@@ -670,6 +619,7 @@ async function main() {
|
|
|
670
619
|
return 0;
|
|
671
620
|
}
|
|
672
621
|
const payload = parsePayload(raw, adapter);
|
|
622
|
+
const bridge = behavior.bridgeFor(payload?.cwd);
|
|
673
623
|
const owner = resolveOwner({
|
|
674
624
|
payload: payload?.raw ?? null,
|
|
675
625
|
coordRoot,
|
|
@@ -711,8 +661,14 @@ async function main() {
|
|
|
711
661
|
status_box_present_strict: data.status_box_present_strict === true,
|
|
712
662
|
session_name_required: typeof data.session_name_present_for === "string",
|
|
713
663
|
session_name_present: data.session_name_present === true,
|
|
664
|
+
...(typeof data.session_name_unavailable_reason === "string"
|
|
665
|
+
? { session_name_unavailable_reason: data.session_name_unavailable_reason }
|
|
666
|
+
: {}),
|
|
714
667
|
}
|
|
715
668
|
: undefined;
|
|
669
|
+
if (typeof data.session_name_unavailable_reason === "string") {
|
|
670
|
+
reportSessionNameObservationUnavailable(coordRoot, owner.instance_id, adapter, eventName, data.session_name_unavailable_reason);
|
|
671
|
+
}
|
|
716
672
|
const stopRemediation = norm.event_type === "turn.started" &&
|
|
717
673
|
typeof data.prompt_text === "string" &&
|
|
718
674
|
data.prompt_text.includes(STOP_REMEDIATION_MARKER);
|
|
@@ -726,6 +682,86 @@ async function main() {
|
|
|
726
682
|
});
|
|
727
683
|
return 0;
|
|
728
684
|
}
|
|
685
|
+
const run = {
|
|
686
|
+
coordRoot,
|
|
687
|
+
adapter,
|
|
688
|
+
behavior,
|
|
689
|
+
eventName,
|
|
690
|
+
norm,
|
|
691
|
+
payload,
|
|
692
|
+
raw,
|
|
693
|
+
owner,
|
|
694
|
+
sessionId,
|
|
695
|
+
data,
|
|
696
|
+
debugBase,
|
|
697
|
+
ledgerRoute,
|
|
698
|
+
hookStartedAt,
|
|
699
|
+
hookClock,
|
|
700
|
+
priorCoordination,
|
|
701
|
+
topLevelSession,
|
|
702
|
+
stopRemediation,
|
|
703
|
+
bridge,
|
|
704
|
+
turnRitual,
|
|
705
|
+
};
|
|
706
|
+
recordSignal(run);
|
|
707
|
+
recordContextTelemetry(run);
|
|
708
|
+
// Phase 8: SessionStart post-emit: project the event so the heartbeat
|
|
709
|
+
// lands synchronously, run stale-sweep, and emit the adapter-shaped
|
|
710
|
+
// systemMessage JSON (peer table + wiring check + council invites).
|
|
711
|
+
// Adapter-agnostic since v0.5.0; replaces the previous bash UX layer
|
|
712
|
+
// and the equivalent per-adapter bash session_start handlers.
|
|
713
|
+
if (norm.event_type === "session.started")
|
|
714
|
+
await handleSessionStarted(run);
|
|
715
|
+
// Phase 8: SessionEnd cleanup: delete heartbeat + pid-map rows. Adapter-
|
|
716
|
+
// agnostic since v0.5.0.
|
|
717
|
+
if (norm.event_type === "session.ended")
|
|
718
|
+
handleSessionEnded(run);
|
|
719
|
+
// Phase 8: SubagentStart: the child generation was opened above from the
|
|
720
|
+
// native delegation identity. Refresh its disposable projection, log the
|
|
721
|
+
// lifecycle event, and emit a context message announcing the subagent.
|
|
722
|
+
if (norm.event_type === "agent.started")
|
|
723
|
+
await handleAgentStarted(run);
|
|
724
|
+
// Phase 8: SubagentStop: delete subagent heartbeat + log.
|
|
725
|
+
if (norm.event_type === "agent.completed")
|
|
726
|
+
await handleAgentCompleted(run);
|
|
727
|
+
// Phase 8: UserPromptSubmit: render dedup'd peer table + council pending
|
|
728
|
+
// and emit the adapter-shaped systemMessage JSON. Adapter-agnostic since v0.5.0.
|
|
729
|
+
if (norm.event_type === "turn.started")
|
|
730
|
+
await handleTurnStarted(run);
|
|
731
|
+
// turn.completed: telemetry, then the stop verdict. The
|
|
732
|
+
// verdict + codex-replay previously lived in the per-adapter shell adapters;
|
|
733
|
+
// agent-hook owns them now. Runs on the normal "stop" event only;
|
|
734
|
+
// "stop-failure" (API error) gets no gate, matching the previous
|
|
735
|
+
// stop vs stop-failure split.
|
|
736
|
+
if (norm.event_type === "turn.completed" && eventName === "stop") {
|
|
737
|
+
const exit = await handleStop(run);
|
|
738
|
+
if (exit !== undefined)
|
|
739
|
+
return exit;
|
|
740
|
+
}
|
|
741
|
+
// Phase 7: PreToolUse: heartbeat + pid-map self-heal on every tool call.
|
|
742
|
+
// Adapter-agnostic: both writes have the same shape regardless of who fired.
|
|
743
|
+
// Cursor/Codex bash dispatchers still fire their own G-guard logic, but the
|
|
744
|
+
// heals here keep the agent-coord layer's view of liveness fresh.
|
|
745
|
+
//
|
|
746
|
+
// The heartbeat + pid-map heals are paired by design; they were wired
|
|
747
|
+
// side-by-side in the previous pre-tool-use adapter. The Phase 4-6 refactor
|
|
748
|
+
// preserved the heartbeat half but dropped the pid-map half; the pid-map
|
|
749
|
+
// call was restored here afterward.
|
|
750
|
+
if (norm.event_type === "tool.requested") {
|
|
751
|
+
const exit = await handleToolRequested(run);
|
|
752
|
+
if (exit !== undefined)
|
|
753
|
+
return exit;
|
|
754
|
+
}
|
|
755
|
+
if (norm.event_type === "tool.completed" && eventName === "post-tool-use")
|
|
756
|
+
await handlePostToolUse(run);
|
|
757
|
+
// Phase 7: PostToolUseFailure: release claim on failed Edit (the file
|
|
758
|
+
// never landed; the claim is stale). Adapter-agnostic.
|
|
759
|
+
if (norm.event_type === "tool.completed" && eventName === "post-tool-use-failure")
|
|
760
|
+
handlePostToolUseFailure(run);
|
|
761
|
+
return 0;
|
|
762
|
+
}
|
|
763
|
+
function recordSignal(run) {
|
|
764
|
+
const { coordRoot, adapter, eventName, norm, payload, owner, sessionId, data, debugBase, ledgerRoute, hookStartedAt, hookClock, priorCoordination, stopRemediation, bridge, turnRitual, } = run;
|
|
729
765
|
let capturedImages = [];
|
|
730
766
|
if (norm.event_type === "tool.requested" ||
|
|
731
767
|
(norm.event_type === "tool.completed" && eventName === "post-tool-use")) {
|
|
@@ -753,7 +789,7 @@ async function main() {
|
|
|
753
789
|
: {}),
|
|
754
790
|
}
|
|
755
791
|
: {}),
|
|
756
|
-
...(
|
|
792
|
+
...(bridge ? { bridge } : {}),
|
|
757
793
|
hook_name: eventName,
|
|
758
794
|
hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
|
|
759
795
|
monotonic_ns: hookClock.monotonic_ns,
|
|
@@ -806,9 +842,7 @@ async function main() {
|
|
|
806
842
|
parentEvent: v3Result.event,
|
|
807
843
|
payload,
|
|
808
844
|
adapter,
|
|
809
|
-
...(
|
|
810
|
-
? { bridge: "codex-wsl" }
|
|
811
|
-
: {}),
|
|
845
|
+
...(bridge ? { bridge } : {}),
|
|
812
846
|
monotonic_ns: hookClock.monotonic_ns,
|
|
813
847
|
});
|
|
814
848
|
const nativeChild = payload.subagent_id ?? payload.agent_id;
|
|
@@ -851,6 +885,12 @@ async function main() {
|
|
|
851
885
|
...(v3Result ? { event_v3_state: v3Result.state } : {}),
|
|
852
886
|
...(v3Result && "reason" in v3Result ? { event_v3_reason: v3Result.reason } : {}),
|
|
853
887
|
});
|
|
888
|
+
run.v3Result = v3Result;
|
|
889
|
+
run.v3EventId = v3EventId;
|
|
890
|
+
run.recordedGenerationId = recordedGenerationId;
|
|
891
|
+
}
|
|
892
|
+
function recordContextTelemetry(run) {
|
|
893
|
+
const { coordRoot, adapter, norm, payload, owner, sessionId } = run;
|
|
854
894
|
// Context telemetry is opportunistic and truthful: only persist a sample
|
|
855
895
|
// when the adapter payload actually exposes usage/window data. Identical
|
|
856
896
|
// measurements are de-duplicated before they reach the canonical stream.
|
|
@@ -902,413 +942,519 @@ async function main() {
|
|
|
902
942
|
logError(coordRoot, err, { phase: "context-compaction-completed" });
|
|
903
943
|
}
|
|
904
944
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
//
|
|
909
|
-
//
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
let backupCue = "";
|
|
916
|
-
try {
|
|
917
|
-
backupCue = scheduleBackupSnapshot(coordRoot).cue ?? "";
|
|
918
|
-
}
|
|
919
|
-
catch (err) {
|
|
920
|
-
logError(coordRoot, err, { phase: "backup-schedule" });
|
|
921
|
-
}
|
|
922
|
-
// Effect (claude-code): prune stale journal archives + sweep orphans.
|
|
923
|
-
// The recovery-cue is merged into the
|
|
924
|
-
// session-start additionalContext inside emitSessionStartSystemMessage.
|
|
925
|
-
if (adapter === "claude-code")
|
|
926
|
-
journalJanitor(coordRoot);
|
|
927
|
-
imageJanitor(coordRoot);
|
|
928
|
-
// Session start is the only caller of the artifact janitor and the storage
|
|
929
|
-
// maintenance pass, so both load here instead of on every tool call.
|
|
930
|
-
const { autoCleanArtifacts } = await import("../artifacts/index.js");
|
|
931
|
-
const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.js");
|
|
932
|
-
const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import("../storage/maintenance-providers.js");
|
|
933
|
-
// Effect: throttled daily sweep of expired artifact workspaces (guarded,
|
|
934
|
-
// managed-expired only; see autoCleanArtifacts). Best-effort like every
|
|
935
|
-
// janitor here: a failure logs and never blocks session start.
|
|
936
|
-
try {
|
|
937
|
-
autoCleanArtifacts(coordRoot);
|
|
938
|
-
}
|
|
939
|
-
catch (err) {
|
|
940
|
-
logError(coordRoot, err, { phase: "artifact-auto-clean" });
|
|
941
|
-
}
|
|
942
|
-
// Effect: apply the same daily throttle to closed V3 epochs. The newest
|
|
943
|
-
// recovery epochs remain protected by the archive retention policy.
|
|
944
|
-
try {
|
|
945
|
-
autoCleanEventV3Archives(coordRoot);
|
|
946
|
-
}
|
|
947
|
-
catch (err) {
|
|
948
|
-
logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
|
|
949
|
-
}
|
|
950
|
-
// Plan one bounded, claim-first global slice from cached pressure only.
|
|
951
|
-
// Existing owner janitors above remain the only active executors until
|
|
952
|
-
// each provider receives a separate production activation.
|
|
953
|
-
try {
|
|
954
|
-
await runAutomaticMaintenancePass(createAutomaticMaintenanceComposition(coordRoot, {
|
|
955
|
-
journal: () => {
|
|
956
|
-
if (adapter === "claude-code")
|
|
957
|
-
journalJanitor(coordRoot);
|
|
958
|
-
},
|
|
959
|
-
images: () => imageJanitor(coordRoot),
|
|
960
|
-
artifacts: () => autoCleanArtifacts(coordRoot),
|
|
961
|
-
}));
|
|
962
|
-
}
|
|
963
|
-
catch (err) {
|
|
964
|
-
logError(coordRoot, err, { phase: "storage-maintenance-auto" });
|
|
965
|
-
}
|
|
966
|
-
let recovery = null;
|
|
967
|
-
if (payload?.source === "compact") {
|
|
968
|
-
try {
|
|
969
|
-
markContextCompactionCompleted(coordRoot, {
|
|
970
|
-
sessionId,
|
|
971
|
-
instanceId: owner.instance_id,
|
|
972
|
-
});
|
|
973
|
-
recovery = prepareContextRecovery(coordRoot, {
|
|
974
|
-
instanceId: owner.instance_id,
|
|
975
|
-
sessionId,
|
|
976
|
-
cwd: payload?.cwd ?? process.cwd(),
|
|
977
|
-
});
|
|
978
|
-
}
|
|
979
|
-
catch (err) {
|
|
980
|
-
logError(coordRoot, err, { phase: "context-recovery-session-start" });
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
try {
|
|
984
|
-
const injected = await emitSessionStartSystemMessage(coordRoot, owner.instance_id, sessionId, data, adapter, recovery?.briefing ?? "", backupCue);
|
|
985
|
-
if (injected && recovery) {
|
|
986
|
-
completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
catch (err) {
|
|
990
|
-
logError(coordRoot, err, { phase: "session-start-systemMessage" });
|
|
991
|
-
}
|
|
992
|
-
// Cross-machine presence is derived from the authority-safe V3 projection,
|
|
993
|
-
// not the disposable heartbeat cache. Announce this session, pull peers,
|
|
994
|
-
// and keep the optional live relay connected.
|
|
995
|
-
try {
|
|
996
|
-
publishPresence(coordRoot);
|
|
997
|
-
fetchPresence(coordRoot);
|
|
998
|
-
ensureRelayDaemon(coordRoot);
|
|
999
|
-
}
|
|
1000
|
-
catch (err) {
|
|
1001
|
-
logError(coordRoot, err, { phase: "session-start-presence" });
|
|
1002
|
-
}
|
|
945
|
+
}
|
|
946
|
+
async function handleSessionStarted(run) {
|
|
947
|
+
const { coordRoot, adapter, behavior, payload, owner, sessionId, data } = run;
|
|
948
|
+
// A configured backup runs out of process and is freshness-gated per host.
|
|
949
|
+
// Startup never waits for restic or a remote provider; the child owns no
|
|
950
|
+
// hook stdio, so the harness is not held open until it finishes. A failed
|
|
951
|
+
// previous run surfaces as one line in this session's context.
|
|
952
|
+
let backupCue = "";
|
|
953
|
+
try {
|
|
954
|
+
backupCue = scheduleBackupSnapshot(coordRoot).cue ?? "";
|
|
1003
955
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
}
|
|
956
|
+
catch (err) {
|
|
957
|
+
logError(coordRoot, err, { phase: "backup-schedule" });
|
|
958
|
+
}
|
|
959
|
+
// Effect (journal-maintaining adapters): prune stale journal archives +
|
|
960
|
+
// sweep orphans. The recovery-cue is merged into the session-start
|
|
961
|
+
// additionalContext inside emitSessionStartSystemMessage.
|
|
962
|
+
if (behavior.journalMaintenance)
|
|
963
|
+
journalJanitor(coordRoot);
|
|
964
|
+
imageJanitor(coordRoot);
|
|
965
|
+
// Session start is the only caller of the artifact janitor and the storage
|
|
966
|
+
// maintenance pass, so both load here instead of on every tool call.
|
|
967
|
+
const { autoCleanArtifacts } = await import("../artifacts/index.js");
|
|
968
|
+
const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.js");
|
|
969
|
+
const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import("../storage/maintenance-providers.js");
|
|
970
|
+
// Effect: throttled hourly artifact sweep (see autoCleanArtifacts). It
|
|
971
|
+
// deletes expired workspaces, plus unheld workspaces over the per-bundle
|
|
972
|
+
// ceiling or repository budget once they have been idle past
|
|
973
|
+
// artifacts.idle_grace_hours. Each deletion is rechecked first and recorded
|
|
974
|
+
// in .harnery/artifact-deletions.jsonl. Best-effort like every janitor
|
|
975
|
+
// here: a failure logs and never blocks session start.
|
|
976
|
+
try {
|
|
977
|
+
autoCleanArtifacts(coordRoot);
|
|
1027
978
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
979
|
+
catch (err) {
|
|
980
|
+
logError(coordRoot, err, { phase: "artifact-auto-clean" });
|
|
981
|
+
}
|
|
982
|
+
// Effect: apply the same daily throttle to closed V3 epochs. The newest
|
|
983
|
+
// recovery epochs remain protected by the archive retention policy.
|
|
984
|
+
try {
|
|
985
|
+
autoCleanEventV3Archives(coordRoot);
|
|
986
|
+
}
|
|
987
|
+
catch (err) {
|
|
988
|
+
logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
|
|
989
|
+
}
|
|
990
|
+
// Plan one bounded, claim-first global slice from cached pressure only.
|
|
991
|
+
// Existing owner janitors above remain the only active executors until
|
|
992
|
+
// each provider receives a separate production activation.
|
|
993
|
+
try {
|
|
994
|
+
await runAutomaticMaintenancePass(createAutomaticMaintenanceComposition(coordRoot, {
|
|
995
|
+
journal: () => {
|
|
996
|
+
if (behavior.journalMaintenance)
|
|
997
|
+
journalJanitor(coordRoot);
|
|
998
|
+
},
|
|
999
|
+
images: () => imageJanitor(coordRoot),
|
|
1000
|
+
artifacts: () => autoCleanArtifacts(coordRoot),
|
|
1001
|
+
}));
|
|
1002
|
+
}
|
|
1003
|
+
catch (err) {
|
|
1004
|
+
logError(coordRoot, err, { phase: "storage-maintenance-auto" });
|
|
1005
|
+
}
|
|
1006
|
+
let recovery = null;
|
|
1007
|
+
if (payload?.source === "compact") {
|
|
1032
1008
|
try {
|
|
1033
|
-
|
|
1034
|
-
|
|
1009
|
+
markContextCompactionCompleted(coordRoot, {
|
|
1010
|
+
sessionId,
|
|
1011
|
+
instanceId: owner.instance_id,
|
|
1012
|
+
});
|
|
1013
|
+
recovery = prepareContextRecovery(coordRoot, {
|
|
1014
|
+
instanceId: owner.instance_id,
|
|
1015
|
+
sessionId,
|
|
1016
|
+
cwd: payload?.cwd ?? process.cwd(),
|
|
1017
|
+
});
|
|
1035
1018
|
}
|
|
1036
1019
|
catch (err) {
|
|
1037
|
-
logError(coordRoot, err, { phase: "
|
|
1020
|
+
logError(coordRoot, err, { phase: "context-recovery-session-start" });
|
|
1038
1021
|
}
|
|
1039
1022
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
cleanupSessionEnd(coordRoot, childInstanceId);
|
|
1045
|
-
const { reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.js");
|
|
1046
|
-
reconcileSessionFinalizationV3(coordRoot, { archive_observations: [] });
|
|
1047
|
-
}
|
|
1048
|
-
catch (err) {
|
|
1049
|
-
logError(coordRoot, err, { phase: "subagent-stop-cleanup" });
|
|
1023
|
+
try {
|
|
1024
|
+
const injected = await emitSessionStartSystemMessage(coordRoot, owner.instance_id, sessionId, data, adapter, recovery?.briefing ?? "", backupCue);
|
|
1025
|
+
if (injected && recovery) {
|
|
1026
|
+
completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
|
|
1050
1027
|
}
|
|
1051
1028
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1029
|
+
catch (err) {
|
|
1030
|
+
logError(coordRoot, err, { phase: "session-start-systemMessage" });
|
|
1031
|
+
}
|
|
1032
|
+
// Cross-machine presence is derived from the authority-safe V3 projection,
|
|
1033
|
+
// not the disposable heartbeat cache. Announce this session, pull peers,
|
|
1034
|
+
// and keep the optional live relay connected.
|
|
1035
|
+
try {
|
|
1036
|
+
publishPresence(coordRoot);
|
|
1037
|
+
fetchPresence(coordRoot);
|
|
1038
|
+
ensureRelayDaemon(coordRoot);
|
|
1039
|
+
}
|
|
1040
|
+
catch (err) {
|
|
1041
|
+
logError(coordRoot, err, { phase: "session-start-presence" });
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
function handleSessionEnded(run) {
|
|
1045
|
+
const { coordRoot, behavior, owner } = run;
|
|
1046
|
+
try {
|
|
1047
|
+
cleanupSessionEnd(coordRoot, owner.instance_id);
|
|
1048
|
+
}
|
|
1049
|
+
catch (err) {
|
|
1050
|
+
logError(coordRoot, err, { phase: "session-end-cleanup" });
|
|
1051
|
+
}
|
|
1052
|
+
// Effects: archive the ending agent's journal + force a session-telemetry
|
|
1053
|
+
// sync (via HARNERY_CLAUDE_SESSIONS_FORCE=1), each gated by the behavior.
|
|
1054
|
+
if (behavior.journalMaintenance)
|
|
1055
|
+
journalArchive(coordRoot, owner.instance_id);
|
|
1056
|
+
if (behavior.sessionTelemetrySync)
|
|
1057
|
+
runSessionSyncExtension(coordRoot, true);
|
|
1058
|
+
// Publish the post-cleanup V3 projection so remote machines see this
|
|
1059
|
+
// session disappear without waiting for the stale window.
|
|
1060
|
+
try {
|
|
1061
|
+
publishPresence(coordRoot);
|
|
1062
|
+
}
|
|
1063
|
+
catch (err) {
|
|
1064
|
+
logError(coordRoot, err, { phase: "session-end-presence" });
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
async function handleAgentStarted(run) {
|
|
1068
|
+
const { coordRoot, adapter, owner, sessionId, data } = run;
|
|
1069
|
+
try {
|
|
1070
|
+
const childInstanceId = data.agent_id ?? owner.instance_id;
|
|
1071
|
+
await emitSubagentStartContext(coordRoot, childInstanceId, sessionId, data, adapter);
|
|
1072
|
+
}
|
|
1073
|
+
catch (err) {
|
|
1074
|
+
logError(coordRoot, err, { phase: "subagent-start-context" });
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
async function handleAgentCompleted(run) {
|
|
1078
|
+
const { coordRoot, payload, owner } = run;
|
|
1079
|
+
try {
|
|
1080
|
+
const childInstanceId = payload?.subagent_id ?? payload?.agent_id ?? owner.instance_id;
|
|
1081
|
+
cleanupSessionEnd(coordRoot, childInstanceId);
|
|
1082
|
+
const { reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.js");
|
|
1083
|
+
reconcileSessionFinalizationV3(coordRoot, { archive_observations: [] });
|
|
1084
|
+
}
|
|
1085
|
+
catch (err) {
|
|
1086
|
+
logError(coordRoot, err, { phase: "subagent-stop-cleanup" });
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
async function handleTurnStarted(run) {
|
|
1090
|
+
const { coordRoot, adapter, behavior, payload, owner, sessionId, debugBase, topLevelSession, stopRemediation, v3EventId, } = run;
|
|
1091
|
+
// Effects: reset per-turn sound rate-limit counters + run presence
|
|
1092
|
+
// detection on the prompt, each gated by the behavior.
|
|
1093
|
+
if (behavior.soundEffects)
|
|
1094
|
+
resetSoundCounters(sessionId);
|
|
1095
|
+
if (behavior.promptPresenceDetection) {
|
|
1096
|
+
const prompt = payload?.raw?.prompt ?? "";
|
|
1097
|
+
if (prompt)
|
|
1098
|
+
detectPresence(prompt);
|
|
1099
|
+
}
|
|
1100
|
+
// Presence: throttled pull of peer machines' presence refs (default 60s
|
|
1101
|
+
// interval; the systemMessage below reads whatever is locally known).
|
|
1102
|
+
try {
|
|
1103
|
+
fetchPresence(coordRoot);
|
|
1104
|
+
}
|
|
1105
|
+
catch (err) {
|
|
1106
|
+
logError(coordRoot, err, { phase: "user-prompt-submit-presence" });
|
|
1107
|
+
}
|
|
1108
|
+
let recovery = null;
|
|
1109
|
+
const continuityState = readContextState(coordRoot, sessionId);
|
|
1110
|
+
if (continuityState?.phase === "checkpointed" &&
|
|
1111
|
+
continuityState.compaction_completed_at !== undefined) {
|
|
1065
1112
|
try {
|
|
1066
|
-
|
|
1113
|
+
recovery = prepareContextRecovery(coordRoot, {
|
|
1114
|
+
instanceId: owner.instance_id,
|
|
1115
|
+
sessionId,
|
|
1116
|
+
cwd: payload?.cwd ?? process.cwd(),
|
|
1117
|
+
});
|
|
1067
1118
|
}
|
|
1068
1119
|
catch (err) {
|
|
1069
|
-
logError(coordRoot, err, { phase: "user-prompt
|
|
1070
|
-
}
|
|
1071
|
-
let recovery = null;
|
|
1072
|
-
const continuityState = readContextState(coordRoot, sessionId);
|
|
1073
|
-
if (continuityState?.phase === "checkpointed" &&
|
|
1074
|
-
continuityState.compaction_completed_at !== undefined) {
|
|
1075
|
-
try {
|
|
1076
|
-
recovery = prepareContextRecovery(coordRoot, {
|
|
1077
|
-
instanceId: owner.instance_id,
|
|
1078
|
-
sessionId,
|
|
1079
|
-
cwd: payload?.cwd ?? process.cwd(),
|
|
1080
|
-
});
|
|
1081
|
-
}
|
|
1082
|
-
catch (err) {
|
|
1083
|
-
logError(coordRoot, err, { phase: "context-recovery-user-prompt" });
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
let hostPromptContext = "";
|
|
1087
|
-
const hostPromptEnabled = hostPromptContextConfig(coordRoot)?.enabled === true;
|
|
1088
|
-
if (hostPromptEnabled && topLevelSession && !stopRemediation) {
|
|
1089
|
-
try {
|
|
1090
|
-
const { runPromptContext } = await import("./prompt-context/runner.js");
|
|
1091
|
-
const result = await runPromptContext({
|
|
1092
|
-
coordRoot,
|
|
1093
|
-
adapter,
|
|
1094
|
-
sessionId,
|
|
1095
|
-
turnId: payload?.turn_id ?? v3EventId ?? sessionId,
|
|
1096
|
-
cwd: payload?.cwd ?? process.cwd(),
|
|
1097
|
-
prompt: payload?.prompt ?? "",
|
|
1098
|
-
});
|
|
1099
|
-
appendDebug(coordRoot, {
|
|
1100
|
-
...debugBase,
|
|
1101
|
-
effect: "prompt-context",
|
|
1102
|
-
...result.audit,
|
|
1103
|
-
});
|
|
1104
|
-
hostPromptContext = result.context ?? "";
|
|
1105
|
-
}
|
|
1106
|
-
catch (err) {
|
|
1107
|
-
logError(coordRoot, err, { phase: "prompt-context-user-prompt" });
|
|
1108
|
-
}
|
|
1120
|
+
logError(coordRoot, err, { phase: "context-recovery-user-prompt" });
|
|
1109
1121
|
}
|
|
1122
|
+
}
|
|
1123
|
+
let hostPromptContext = "";
|
|
1124
|
+
const hostPromptEnabled = hostPromptContextConfig(coordRoot)?.enabled === true;
|
|
1125
|
+
if (hostPromptEnabled && topLevelSession && !stopRemediation) {
|
|
1110
1126
|
try {
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1127
|
+
const { runPromptContext } = await import("./prompt-context/runner.js");
|
|
1128
|
+
const result = await runPromptContext({
|
|
1129
|
+
coordRoot,
|
|
1130
|
+
adapter,
|
|
1131
|
+
sessionId,
|
|
1132
|
+
turnId: payload?.turn_id ?? v3EventId ?? sessionId,
|
|
1133
|
+
cwd: payload?.cwd ?? process.cwd(),
|
|
1134
|
+
prompt: payload?.prompt ?? "",
|
|
1135
|
+
});
|
|
1136
|
+
appendDebug(coordRoot, {
|
|
1137
|
+
...debugBase,
|
|
1138
|
+
effect: "prompt-context",
|
|
1139
|
+
...result.audit,
|
|
1140
|
+
});
|
|
1141
|
+
hostPromptContext = result.context ?? "";
|
|
1115
1142
|
}
|
|
1116
1143
|
catch (err) {
|
|
1117
|
-
logError(coordRoot, err, { phase: "user-prompt
|
|
1144
|
+
logError(coordRoot, err, { phase: "prompt-context-user-prompt" });
|
|
1118
1145
|
}
|
|
1119
1146
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
// stop vs stop-failure split.
|
|
1125
|
-
if (norm.event_type === "turn.completed" && eventName === "stop") {
|
|
1126
|
-
// Claude Code session telemetry sync remains an independent side effect.
|
|
1127
|
-
if (adapter === "claude-code") {
|
|
1128
|
-
runSessionSyncExtension(coordRoot, false);
|
|
1129
|
-
}
|
|
1130
|
-
// Publish after the canonical turn event has landed so the blob carries
|
|
1131
|
-
// current task, activity, and claim state. Re-ensure the relay here because
|
|
1132
|
-
// its daemon intentionally exits when a machine has no live sessions.
|
|
1133
|
-
try {
|
|
1134
|
-
publishPresence(coordRoot);
|
|
1135
|
-
ensureRelayDaemon(coordRoot);
|
|
1147
|
+
try {
|
|
1148
|
+
const injected = await emitUserPromptSubmitSystemMessage(coordRoot, owner.instance_id, sessionId, adapter, payload?.cwd, recovery?.briefing ?? "", hostPromptContext);
|
|
1149
|
+
if (injected && recovery) {
|
|
1150
|
+
completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
|
|
1136
1151
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1152
|
+
}
|
|
1153
|
+
catch (err) {
|
|
1154
|
+
logError(coordRoot, err, { phase: "user-prompt-submit-systemMessage" });
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
async function handleStop(run) {
|
|
1158
|
+
const { coordRoot, adapter, behavior, payload, owner, sessionId, data, debugBase } = run;
|
|
1159
|
+
// Session telemetry sync remains an independent side effect.
|
|
1160
|
+
if (behavior.sessionTelemetrySync)
|
|
1161
|
+
runSessionSyncExtension(coordRoot, false);
|
|
1162
|
+
// Publish after the canonical turn event has landed so the blob carries
|
|
1163
|
+
// current task, activity, and claim state. Re-ensure the relay here because
|
|
1164
|
+
// its daemon intentionally exits when a machine has no live sessions.
|
|
1165
|
+
try {
|
|
1166
|
+
publishPresence(coordRoot);
|
|
1167
|
+
ensureRelayDaemon(coordRoot);
|
|
1168
|
+
}
|
|
1169
|
+
catch (err) {
|
|
1170
|
+
logError(coordRoot, err, { phase: "stop-presence" });
|
|
1171
|
+
}
|
|
1172
|
+
// Stop verdict (V3 ritual + task/status gate). Direct in-process call: the
|
|
1173
|
+
// rule lives in Harnery. agent-hook already emitted this turn.completed
|
|
1174
|
+
// with privacy-safe ritual observations, so the evidence is in the stream.
|
|
1175
|
+
const verdict = evaluateStopHook(coordRoot, {
|
|
1176
|
+
rule: "stop-hook",
|
|
1177
|
+
instance_id: owner.instance_id,
|
|
1178
|
+
session_id: sessionId,
|
|
1179
|
+
adapter,
|
|
1180
|
+
stop_hook_active: payload?.stop_hook_active === true,
|
|
1181
|
+
status_box_present_strict: data.status_box_present_strict === true,
|
|
1182
|
+
session_name_observation_unavailable: typeof data.session_name_unavailable_reason === "string",
|
|
1183
|
+
bypass: coordEnv("AGENT_COORD_BYPASS_STOP") === "1",
|
|
1184
|
+
workflow_child: coordEnv("WORKFLOW_CHILD") === "1",
|
|
1185
|
+
});
|
|
1186
|
+
if (!verdict.allow) {
|
|
1187
|
+
// Backstop for sessions whose evidence can never land (e.g. a headless
|
|
1188
|
+
// child whose status command resolves to a different owner): after
|
|
1189
|
+
// `cap` consecutive blocked stops in one cycle, allow the stop instead
|
|
1190
|
+
// of bouncing the model forever. Cycle boundaries come from the
|
|
1191
|
+
// adapter's continuation flag, so adapters without one are unaffected.
|
|
1192
|
+
const { recordRemediationBlock } = await import("./remediation-cap.js");
|
|
1193
|
+
const remediation = recordRemediationBlock(sessionId, payload?.stop_hook_active === true);
|
|
1194
|
+
if (remediation.exceeded) {
|
|
1195
|
+
appendDebug(coordRoot, {
|
|
1196
|
+
...debugBase,
|
|
1197
|
+
skipped: "stop-remediation-cap-exhausted",
|
|
1198
|
+
blocked_rule: verdict.rule,
|
|
1199
|
+
blocked_count: remediation.count,
|
|
1200
|
+
session_id: sessionId,
|
|
1201
|
+
remediation_cycle_anchor: verdict.remediation_cycle_anchor,
|
|
1202
|
+
});
|
|
1178
1203
|
}
|
|
1179
1204
|
else {
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
// when such a request exists; any later real work cancels it in the
|
|
1186
|
-
// finalizer instead of being terminated underneath the agent.
|
|
1187
|
-
try {
|
|
1188
|
-
const { hasPendingExplicitSessionEndV3, reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.js");
|
|
1189
|
-
if (hasPendingExplicitSessionEndV3(coordRoot)) {
|
|
1190
|
-
reconcileSessionFinalizationV3(coordRoot);
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
catch (err) {
|
|
1194
|
-
logError(coordRoot, err, { phase: "stop-explicit-session-finalization" });
|
|
1205
|
+
// Adapter-aware enforcement channel: Claude Code honors exit-2 + stderr
|
|
1206
|
+
// as a turn block; Cursor ignores exit codes and re-prompts only via a
|
|
1207
|
+
// `followup_message` it auto-submits.
|
|
1208
|
+
const { emitStopOutcome } = await import("./adapter/output.js");
|
|
1209
|
+
return emitStopOutcome(adapter, { verdict }, coordRoot);
|
|
1195
1210
|
}
|
|
1196
|
-
if (adapter === "codex")
|
|
1197
|
-
scheduleCodexRuntimeContextRetry(coordRoot, sessionId);
|
|
1198
1211
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
//
|
|
1204
|
-
//
|
|
1205
|
-
//
|
|
1206
|
-
//
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
// never fires its own session.started (SessionStart fires under the
|
|
1212
|
-
// PARENT's session id with source=resume, before the fork id is
|
|
1213
|
-
// minted; verified 2026-08-05), so the fork's new instance first
|
|
1214
|
-
// materializes right here. Gate detection on "no heartbeat yet" so the
|
|
1215
|
-
// transcript scan runs once per instance lifetime, not per tool call.
|
|
1216
|
-
refreshPidmap(coordRoot, owner.instance_id, adapter, payload?.pid);
|
|
1217
|
-
}
|
|
1218
|
-
catch (err) {
|
|
1219
|
-
logError(coordRoot, err, { phase: "pre-tool-use-heal" });
|
|
1220
|
-
}
|
|
1221
|
-
// The suggested name is a pending display latch. The set-task call itself
|
|
1222
|
-
// runs before a name exists; every later tool waits until the exact block
|
|
1223
|
-
// is the first assistant text after the mint result. Cursor supplies
|
|
1224
|
-
// agent_message directly; Claude Code and Codex are resolved from their
|
|
1225
|
-
// JSONL transcripts. Cursor's current narration can provide positive
|
|
1226
|
-
// evidence, but cannot disprove an earlier response; afterAgentResponse
|
|
1227
|
-
// stamps that durable sighting. Later commentary must not erase an
|
|
1228
|
-
// already-correct display, and unavailable evidence must not deadlock
|
|
1229
|
-
// every tool.
|
|
1230
|
-
try {
|
|
1231
|
-
const displayAllowed = await enforcePendingSessionNameDisplay(coordRoot, owner.instance_id, adapter, payload, generationBoundHeartbeat(readHeartbeat(coordRoot, owner.instance_id), recordedGenerationId));
|
|
1232
|
-
if (!displayAllowed)
|
|
1233
|
-
return 0;
|
|
1234
|
-
}
|
|
1235
|
-
catch (err) {
|
|
1236
|
-
logError(coordRoot, err, { phase: "pre-tool-use-session-name" });
|
|
1237
|
-
}
|
|
1238
|
-
// Windows-native Codex + WSL UNC only: block the one cross-shell shape
|
|
1239
|
-
// proven to corrupt argument boundaries. Normal WSL argv calls, literal
|
|
1240
|
-
// bash -s scripts, native Linux/macOS sessions, and every other adapter
|
|
1241
|
-
// pass through. The host instructions name its concrete safe bridge.
|
|
1242
|
-
const unsafeShellReason = unsafeCrossShellReason({
|
|
1243
|
-
adapter,
|
|
1244
|
-
cwd: payload?.cwd,
|
|
1245
|
-
toolName: payload?.tool_name,
|
|
1246
|
-
toolInput: payload?.tool_input,
|
|
1247
|
-
});
|
|
1248
|
-
if (unsafeShellReason) {
|
|
1249
|
-
const { emitDeny } = await import("./adapter/output.js");
|
|
1250
|
-
emitDeny(adapter, unsafeShellReason);
|
|
1251
|
-
return 0;
|
|
1252
|
-
}
|
|
1253
|
-
// G-guard for ALL adapters. Claude Code previously ran this via a
|
|
1254
|
-
// pre-tool-use bash adapter (which called `agent-coord verdict --rule=claim`);
|
|
1255
|
-
// that adapter is now deleted, so agent-hook owns the deny for every adapter.
|
|
1256
|
-
// emitDeny() inside emits the adapter-shaped permission JSON (claude-code +
|
|
1257
|
-
// codex use hookSpecificOutput.permissionDecision; cursor uses .permission).
|
|
1258
|
-
// apply_patch (codex) parses paths from the patch body and runs verdict
|
|
1259
|
-
// per-path; Edit/Write/NotebookEdit resolve a single target. Non-write tools
|
|
1260
|
-
// (incl. Agent) yield no targets and pass through with no deny.
|
|
1261
|
-
let guardAllowed = true;
|
|
1262
|
-
try {
|
|
1263
|
-
guardAllowed = await runPreToolUseGuard(coordRoot, owner.instance_id, sessionId, data, adapter);
|
|
1264
|
-
}
|
|
1265
|
-
catch (err) {
|
|
1266
|
-
logError(coordRoot, err, { phase: "pre-tool-use-guard" });
|
|
1212
|
+
else {
|
|
1213
|
+
const { clearRemediationCount } = await import("./remediation-cap.js");
|
|
1214
|
+
clearRemediationCount(sessionId);
|
|
1215
|
+
}
|
|
1216
|
+
// An explicit end requested from inside this turn cannot be authoritative
|
|
1217
|
+
// until the adapter has committed turn.completed above. Reconcile only
|
|
1218
|
+
// when such a request exists; any later real work cancels it in the
|
|
1219
|
+
// finalizer instead of being terminated underneath the agent.
|
|
1220
|
+
try {
|
|
1221
|
+
const { hasPendingExplicitSessionEndV3, reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.js");
|
|
1222
|
+
if (hasPendingExplicitSessionEndV3(coordRoot)) {
|
|
1223
|
+
reconcileSessionFinalizationV3(coordRoot);
|
|
1267
1224
|
}
|
|
1268
|
-
|
|
1225
|
+
}
|
|
1226
|
+
catch (err) {
|
|
1227
|
+
logError(coordRoot, err, { phase: "stop-explicit-session-finalization" });
|
|
1228
|
+
}
|
|
1229
|
+
if (behavior.runtimeContextRetry)
|
|
1230
|
+
scheduleRuntimeContextRetry(coordRoot, adapter, sessionId);
|
|
1231
|
+
return undefined;
|
|
1232
|
+
}
|
|
1233
|
+
async function handleToolRequested(run) {
|
|
1234
|
+
const { coordRoot, adapter, behavior, payload, owner, sessionId, data, recordedGenerationId } = run;
|
|
1235
|
+
try {
|
|
1236
|
+
// Recorded fork lineage, heal-path flavor. A forked CC conversation
|
|
1237
|
+
// never fires its own session.started (SessionStart fires under the
|
|
1238
|
+
// PARENT's session id with source=resume, before the fork id is
|
|
1239
|
+
// minted; verified 2026-08-05), so the fork's new instance first
|
|
1240
|
+
// materializes right here. Gate detection on "no heartbeat yet" so the
|
|
1241
|
+
// transcript scan runs once per instance lifetime, not per tool call.
|
|
1242
|
+
refreshPidmap(coordRoot, owner.instance_id, adapter, payload?.pid);
|
|
1243
|
+
}
|
|
1244
|
+
catch (err) {
|
|
1245
|
+
logError(coordRoot, err, { phase: "pre-tool-use-heal" });
|
|
1246
|
+
}
|
|
1247
|
+
// The suggested name is a pending display latch. The set-task call itself
|
|
1248
|
+
// runs before a name exists; every later tool waits until the exact block
|
|
1249
|
+
// is the first assistant text after the mint result. Cursor supplies
|
|
1250
|
+
// agent_message directly; Claude Code and Codex are resolved from their
|
|
1251
|
+
// JSONL transcripts. Cursor's current narration can provide positive
|
|
1252
|
+
// evidence, but cannot disprove an earlier response; afterAgentResponse
|
|
1253
|
+
// stamps that durable sighting. Later commentary must not erase an
|
|
1254
|
+
// already-correct display, and unavailable evidence must not deadlock
|
|
1255
|
+
// every tool.
|
|
1256
|
+
try {
|
|
1257
|
+
const displayAllowed = await enforcePendingSessionNameDisplay(coordRoot, owner.instance_id, adapter, payload, generationBoundHeartbeat(readHeartbeat(coordRoot, owner.instance_id), recordedGenerationId));
|
|
1258
|
+
if (!displayAllowed)
|
|
1269
1259
|
return 0;
|
|
1270
1260
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1261
|
+
catch (err) {
|
|
1262
|
+
logError(coordRoot, err, { phase: "pre-tool-use-session-name" });
|
|
1263
|
+
}
|
|
1264
|
+
// Windows-native Codex + WSL UNC only: block the one cross-shell shape
|
|
1265
|
+
// proven to corrupt argument boundaries. Normal WSL argv calls, literal
|
|
1266
|
+
// bash -s scripts, native Linux/macOS sessions, and every other adapter
|
|
1267
|
+
// pass through. The host instructions name its concrete safe bridge.
|
|
1268
|
+
const unsafeShellReason = behavior.unsafeCrossShellReason?.({
|
|
1269
|
+
cwd: payload?.cwd,
|
|
1270
|
+
toolName: payload?.tool_name,
|
|
1271
|
+
toolInput: payload?.tool_input,
|
|
1272
|
+
}) ?? null;
|
|
1273
|
+
if (unsafeShellReason) {
|
|
1274
|
+
const { emitDeny } = await import("./adapter/output.js");
|
|
1275
|
+
emitDeny(adapter, unsafeShellReason);
|
|
1276
|
+
return 0;
|
|
1277
|
+
}
|
|
1278
|
+
const waiterReason = shellWaiterReason(payload?.tool_name, payload?.tool_input);
|
|
1279
|
+
if (waiterReason) {
|
|
1280
|
+
const { emitDeny } = await import("./adapter/output.js");
|
|
1281
|
+
emitDeny(adapter, waiterReason);
|
|
1282
|
+
return 0;
|
|
1283
|
+
}
|
|
1284
|
+
// G-guard for ALL adapters. Claude Code previously ran this via a
|
|
1285
|
+
// pre-tool-use bash adapter (which called `agent-coord verdict --rule=claim`);
|
|
1286
|
+
// that adapter is now deleted, so agent-hook owns the deny for every adapter.
|
|
1287
|
+
// emitDeny() inside emits the adapter-shaped permission JSON (claude-code +
|
|
1288
|
+
// codex use hookSpecificOutput.permissionDecision; cursor uses .permission).
|
|
1289
|
+
// apply_patch (codex) parses paths from the patch body and runs verdict
|
|
1290
|
+
// per-path; Edit/Write/NotebookEdit resolve a single target. Non-write tools
|
|
1291
|
+
// (incl. Agent) yield no targets and pass through with no deny.
|
|
1292
|
+
let guardAllowed = true;
|
|
1293
|
+
try {
|
|
1294
|
+
guardAllowed = await runPreToolUseGuard(coordRoot, owner.instance_id, sessionId, data, adapter);
|
|
1295
|
+
}
|
|
1296
|
+
catch (err) {
|
|
1297
|
+
logError(coordRoot, err, { phase: "pre-tool-use-guard" });
|
|
1298
|
+
}
|
|
1299
|
+
if (!guardAllowed)
|
|
1300
|
+
return 0;
|
|
1301
|
+
return undefined;
|
|
1302
|
+
}
|
|
1303
|
+
async function handlePostToolUse(run) {
|
|
1304
|
+
const { coordRoot, adapter, eventName, payload, owner } = run;
|
|
1305
|
+
// Inject the full instruction only at the successful tool boundary that
|
|
1306
|
+
// actually minted or retried a name: the first non-empty set-task, a
|
|
1307
|
+
// pending-name set-task retry, or the transition to lifecycle done. The
|
|
1308
|
+
// coordination row intentionally remains pending until transcript evidence
|
|
1309
|
+
// catches up, so the latch alone cannot drive injection: that would print
|
|
1310
|
+
// the same block after every later tool.
|
|
1311
|
+
//
|
|
1312
|
+
// Between those two extremes sits a bounded reminder. Since ADR 0182 the
|
|
1313
|
+
// PreToolUse gate allows a tool when transcript evidence is unavailable
|
|
1314
|
+
// and says so only on stderr, which the model never reads. A model that
|
|
1315
|
+
// skipped the block after the mint was therefore never asked again
|
|
1316
|
+
// (ADR 0183). While the requested title is still pending, ordinary tool
|
|
1317
|
+
// results carry an idempotent reminder up to a fixed count; coordination
|
|
1318
|
+
// chores (set-task, status, suggest-name) are skipped because a mint or
|
|
1319
|
+
// retry response already carries the full instruction and an end-turn
|
|
1320
|
+
// status is the wrong place to ask.
|
|
1321
|
+
try {
|
|
1322
|
+
const row = readLiveCoordinationRow(coordRoot, owner.instance_id);
|
|
1323
|
+
// A workflow child has no user-visible title to set.
|
|
1324
|
+
const name = coordEnv("WORKFLOW_CHILD") === "1" ? undefined : sessionNameDisplayPending(row);
|
|
1325
|
+
if (name && toolResponseMintedSessionName(payload?.tool_response, name)) {
|
|
1326
|
+
// Record the title before asking for it. The suggestion can still
|
|
1327
|
+
// change afterwards (an assigned-name rewrite, a lifecycle re-mint, a
|
|
1328
|
+
// rebuilt cache), and the agent must not be stranded for displaying
|
|
1329
|
+
// exactly what it was handed.
|
|
1330
|
+
stampSessionNameRequested(coordRoot, owner.instance_id, name);
|
|
1331
|
+
const { emitContext } = await import("./adapter/output.js");
|
|
1332
|
+
emitContext(adapter, "PostToolUse", sessionNameDisplayInstruction(name));
|
|
1333
|
+
}
|
|
1334
|
+
else if (name) {
|
|
1335
|
+
const due = sessionNameDisplayReminderDue(row);
|
|
1336
|
+
const command = extractBashCommand(payload?.tool_name, payload?.tool_input);
|
|
1337
|
+
if (due && !isSessionNameRemediationCommand(command, resolveBinName(coordRoot))) {
|
|
1338
|
+
stampSessionNameReminded(coordRoot, owner.instance_id, due);
|
|
1339
|
+
appendDebug(coordRoot, {
|
|
1340
|
+
ts: new Date().toISOString(),
|
|
1341
|
+
event_name: eventName,
|
|
1342
|
+
adapter,
|
|
1343
|
+
instance_id: owner.instance_id,
|
|
1344
|
+
effect: "session-name-display-reminded",
|
|
1345
|
+
session_name_pending: due,
|
|
1346
|
+
reminders: (row?.session_name_display_reminders ?? 0) + 1,
|
|
1347
|
+
});
|
|
1286
1348
|
const { emitContext } = await import("./adapter/output.js");
|
|
1287
|
-
emitContext(adapter, "PostToolUse",
|
|
1349
|
+
emitContext(adapter, "PostToolUse", sessionNameDisplayReminder(due));
|
|
1288
1350
|
}
|
|
1289
1351
|
}
|
|
1290
|
-
catch (err) {
|
|
1291
|
-
logError(coordRoot, err, { phase: "post-tool-use-session-name" });
|
|
1292
|
-
}
|
|
1293
1352
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1353
|
+
catch (err) {
|
|
1354
|
+
logError(coordRoot, err, { phase: "post-tool-use-session-name" });
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
function handlePostToolUseFailure(run) {
|
|
1358
|
+
const { coordRoot, payload, owner, data } = run;
|
|
1359
|
+
try {
|
|
1360
|
+
releaseClaimOnFailure(coordRoot, owner.instance_id, data, payload?.raw);
|
|
1361
|
+
}
|
|
1362
|
+
catch (err) {
|
|
1363
|
+
logError(coordRoot, err, { phase: "post-tool-use-failure-release" });
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
async function handleCompletedResponse(run) {
|
|
1367
|
+
const { coordRoot, adapter, eventName, raw, debugBase, ledgerRoute, hookStartedAt, hookClock } = run;
|
|
1368
|
+
const payload = parsePayload(raw, adapter);
|
|
1369
|
+
const owner = resolveOwner({ payload: payload?.raw ?? null, coordRoot });
|
|
1370
|
+
if (!owner) {
|
|
1371
|
+
appendDebug(coordRoot, { ...debugBase, skipped: "no-owner-resolved" });
|
|
1372
|
+
return 0;
|
|
1373
|
+
}
|
|
1374
|
+
const row = readLiveCoordinationRow(coordRoot, owner.instance_id);
|
|
1375
|
+
const name = sessionNameDisplayPending(row);
|
|
1376
|
+
const text = typeof payload?.raw.text === "string" ? payload.raw.text : "";
|
|
1377
|
+
const sighting = matchSessionNameDisplay(row, text);
|
|
1378
|
+
if (sighting) {
|
|
1379
|
+
stampSessionNameSeen(coordRoot, owner.instance_id, sighting.pending);
|
|
1380
|
+
appendDebug(coordRoot, {
|
|
1381
|
+
...debugBase,
|
|
1382
|
+
effect: "session-name-display-stamped",
|
|
1383
|
+
session_name_pending: sighting.pending,
|
|
1384
|
+
...(sighting.displayed === sighting.pending
|
|
1385
|
+
? {}
|
|
1386
|
+
: { session_name_displayed: sighting.displayed, session_name_drift: true }),
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
else if (!name) {
|
|
1390
|
+
// Nothing was owed. Distinct from a miss: conflating the two is what made
|
|
1391
|
+
// a latched Cursor session undiagnosable from this log.
|
|
1392
|
+
appendDebug(coordRoot, { ...debugBase, skipped: "no-pending-session-name" });
|
|
1393
|
+
}
|
|
1394
|
+
else {
|
|
1395
|
+
appendDebug(coordRoot, {
|
|
1396
|
+
...debugBase,
|
|
1397
|
+
skipped: "session-name-block-absent",
|
|
1398
|
+
session_name_pending: name,
|
|
1399
|
+
reply_bytes: text.length,
|
|
1400
|
+
reply_leads_with_fence: /^\s*`{3,}/.test(text),
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
if (ledgerRoute.state === "blocked") {
|
|
1404
|
+
appendDebug(coordRoot, {
|
|
1405
|
+
...debugBase,
|
|
1406
|
+
skipped: "v3-control-blocked",
|
|
1407
|
+
reason: ledgerRoute.reason,
|
|
1408
|
+
owner_source: owner.source,
|
|
1409
|
+
});
|
|
1410
|
+
return 0;
|
|
1303
1411
|
}
|
|
1412
|
+
const statusBoxPresentStrict = scanAssistantStatusBoxPresent(undefined, text);
|
|
1413
|
+
// The hook parser needs the completed reply long enough to scan it, but
|
|
1414
|
+
// durable hook intake must never retain that body. Keep every other native
|
|
1415
|
+
// field so turn correlation remains available to the producer.
|
|
1416
|
+
const { text: _completedReply, ...privacySafeRaw } = payload?.raw ?? {};
|
|
1417
|
+
const privacySafePayload = payload ? { ...payload, raw: privacySafeRaw } : null;
|
|
1418
|
+
const v3Result = recordLiveHookSignalV3({
|
|
1419
|
+
coordRoot,
|
|
1420
|
+
route: ledgerRoute,
|
|
1421
|
+
eventName,
|
|
1422
|
+
payload: privacySafePayload,
|
|
1423
|
+
adapter,
|
|
1424
|
+
instanceId: owner.instance_id,
|
|
1425
|
+
hook_name: eventName,
|
|
1426
|
+
hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
|
|
1427
|
+
monotonic_ns: hookClock.monotonic_ns,
|
|
1428
|
+
turn_ritual: {
|
|
1429
|
+
status_box_present: statusBoxPresentStrict,
|
|
1430
|
+
status_box_present_strict: statusBoxPresentStrict,
|
|
1431
|
+
session_name_required: name !== undefined,
|
|
1432
|
+
session_name_present: name ? assistantTextStartsWithSessionNameBlock(text, name) : false,
|
|
1433
|
+
},
|
|
1434
|
+
});
|
|
1435
|
+
observeHookDebug(hookHealthState, { event_v3_state: v3Result.state });
|
|
1436
|
+
appendDebug(coordRoot, {
|
|
1437
|
+
...debugBase,
|
|
1438
|
+
effect: "cursor-response-ritual-observed",
|
|
1439
|
+
owner_source: owner.source,
|
|
1440
|
+
event_v3_state: v3Result.state,
|
|
1441
|
+
status_box_present_strict: statusBoxPresentStrict,
|
|
1442
|
+
...(v3Result.state === "observed"
|
|
1443
|
+
? {
|
|
1444
|
+
generation_id: v3Result.generation_id,
|
|
1445
|
+
turn_id: v3Result.turn_id,
|
|
1446
|
+
response_observed_at: v3Result.observed_at,
|
|
1447
|
+
}
|
|
1448
|
+
: {}),
|
|
1449
|
+
});
|
|
1304
1450
|
return 0;
|
|
1305
1451
|
}
|
|
1306
|
-
function
|
|
1452
|
+
function scheduleRuntimeContextRetry(coordRoot, adapter, nativeSessionId) {
|
|
1307
1453
|
const modulePath = process.argv[1];
|
|
1308
1454
|
if (!modulePath)
|
|
1309
1455
|
return;
|
|
1310
1456
|
try {
|
|
1311
|
-
const child = spawn(process.execPath, [modulePath, "runtime-context-retry", "--adapter",
|
|
1457
|
+
const child = spawn(process.execPath, [modulePath, "runtime-context-retry", "--adapter", adapter, "--session-id", nativeSessionId], {
|
|
1312
1458
|
cwd: coordRoot,
|
|
1313
1459
|
detached: true,
|
|
1314
1460
|
stdio: "ignore",
|
|
@@ -1322,7 +1468,7 @@ function scheduleCodexRuntimeContextRetry(coordRoot, nativeSessionId) {
|
|
|
1322
1468
|
}
|
|
1323
1469
|
async function runRuntimeContextRetryWorker(coordRoot, adapter, extra) {
|
|
1324
1470
|
const nativeSessionId = extraArg(extra, "--session-id");
|
|
1325
|
-
if (adapter
|
|
1471
|
+
if (!adapter || !adapterBehavior(adapter).runtimeContextRetry || !nativeSessionId)
|
|
1326
1472
|
return 0;
|
|
1327
1473
|
for (const [index, delayMs] of [400, 1_000].entries()) {
|
|
1328
1474
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
@@ -1340,7 +1486,21 @@ async function runRuntimeContextRetryWorker(coordRoot, adapter, extra) {
|
|
|
1340
1486
|
}
|
|
1341
1487
|
return 0;
|
|
1342
1488
|
}
|
|
1489
|
+
function reportSessionNameObservationUnavailable(coordRoot, instanceId, adapter, eventName, reason) {
|
|
1490
|
+
appendDebug(coordRoot, {
|
|
1491
|
+
ts: new Date().toISOString(),
|
|
1492
|
+
event_name: eventName,
|
|
1493
|
+
adapter,
|
|
1494
|
+
instance_id: instanceId,
|
|
1495
|
+
effect: "session-name-observation-unavailable",
|
|
1496
|
+
reason,
|
|
1497
|
+
action: "fail-open",
|
|
1498
|
+
});
|
|
1499
|
+
process.stderr.write(`Harnery: session-name observation unavailable (${reason}); allowing this ${eventName} without display enforcement. The name remains pending.\n`);
|
|
1500
|
+
}
|
|
1343
1501
|
async function enforcePendingSessionNameDisplay(coordRoot, instanceId, adapter, payload, coordination) {
|
|
1502
|
+
if (coordEnv("WORKFLOW_CHILD") === "1")
|
|
1503
|
+
return true;
|
|
1344
1504
|
const name = sessionNameDisplayPending(coordination);
|
|
1345
1505
|
if (!name)
|
|
1346
1506
|
return true;
|
|
@@ -1353,11 +1513,7 @@ async function enforcePendingSessionNameDisplay(coordRoot, instanceId, adapter,
|
|
|
1353
1513
|
return true;
|
|
1354
1514
|
}
|
|
1355
1515
|
if (inspection.state === "unavailable") {
|
|
1356
|
-
|
|
1357
|
-
// unavailable transcript cannot become evidence, and repeating a generic
|
|
1358
|
-
// reminder on every tool creates an unbounded Codex loop. Keep the latch
|
|
1359
|
-
// honestly pending and fail open without another instruction. A later
|
|
1360
|
-
// readable transcript can still verify it.
|
|
1516
|
+
reportSessionNameObservationUnavailable(coordRoot, instanceId, adapter, "pre-tool-use", inspection.reason);
|
|
1361
1517
|
return true;
|
|
1362
1518
|
}
|
|
1363
1519
|
// Record the refusal. Without this the gate was the one enforcement path
|
|
@@ -1384,19 +1540,18 @@ async function enforcePendingSessionNameDisplay(coordRoot, instanceId, adapter,
|
|
|
1384
1540
|
* other opening text for every accepted title.
|
|
1385
1541
|
*/
|
|
1386
1542
|
function inspectDisplayEvidence(instanceId, adapter, payload, coordination) {
|
|
1387
|
-
|
|
1388
|
-
|
|
1543
|
+
const behavior = adapterBehavior(adapter);
|
|
1544
|
+
const inlineText = behavior.inlineAssistantText?.(payload);
|
|
1545
|
+
if (inlineText !== undefined) {
|
|
1546
|
+
return matchSessionNameDisplay(coordination, inlineText)
|
|
1389
1547
|
? { state: "present" }
|
|
1390
1548
|
: { state: "unavailable", reason: "transcript_not_ready" };
|
|
1391
1549
|
}
|
|
1392
|
-
//
|
|
1393
|
-
// inspection permanently unavailable and the latch never stamped.
|
|
1394
|
-
// the
|
|
1395
|
-
// the latch closes after one success.
|
|
1396
|
-
const transcriptPath = payload?.transcript_path ??
|
|
1397
|
-
(adapter === "codex"
|
|
1398
|
-
? discoverCodexSessionTranscript(payload?.session_id ?? instanceId)
|
|
1399
|
-
: undefined);
|
|
1550
|
+
// Some adapters omit transcript_path on every event, which left this
|
|
1551
|
+
// inspection permanently unavailable and the latch never stamped. The
|
|
1552
|
+
// behavior may discover the transcript by session id; the scan only runs
|
|
1553
|
+
// while a name is pending, so the latch closes after one success.
|
|
1554
|
+
const transcriptPath = payload?.transcript_path ?? behavior.discoverTranscript?.(payload?.session_id ?? instanceId);
|
|
1400
1555
|
let verdict = { state: "absent" };
|
|
1401
1556
|
for (const candidate of sessionNameDisplayAcceptedNames(coordination)) {
|
|
1402
1557
|
const inspection = inspectSessionNameDisplayImmediately(transcriptPath, candidate, assistantTextStartsWithSessionNameBlock);
|
|
@@ -1671,7 +1826,7 @@ function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
|
|
|
1671
1826
|
coordRoot,
|
|
1672
1827
|
owner: instanceId,
|
|
1673
1828
|
nativeSessionId: before?.session_id ?? instanceId,
|
|
1674
|
-
adapter:
|
|
1829
|
+
adapter: sharedAdapterFromPlatform(before?.platform, { context: "hook-claim-release" }),
|
|
1675
1830
|
operation: "released",
|
|
1676
1831
|
path: canonical,
|
|
1677
1832
|
access: "write",
|
|
@@ -1707,22 +1862,23 @@ function cleanupSessionEnd(coordRoot, instanceId) {
|
|
|
1707
1862
|
}
|
|
1708
1863
|
}
|
|
1709
1864
|
async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionId, adapter, workspaceCwd, recoveryBriefing = "", hostPromptContext = "") {
|
|
1865
|
+
// Workflow children get no prompt-time coordination context, matching
|
|
1866
|
+
// SessionStart. The turn ritual is not enforced for them, the naming nudge
|
|
1867
|
+
// would register them as named peers, and rendering drains peer mail.
|
|
1868
|
+
if (coordEnv("WORKFLOW_CHILD") === "1")
|
|
1869
|
+
return false;
|
|
1870
|
+
const behavior = adapterBehavior(adapter);
|
|
1710
1871
|
const { renderPromptContext } = await import("../agents/render/prompt-context.js");
|
|
1711
1872
|
let additionalContext = renderPromptContext({
|
|
1712
1873
|
coordRoot,
|
|
1713
1874
|
instanceId,
|
|
1714
1875
|
sessionId,
|
|
1715
1876
|
sessionNameNudge: true,
|
|
1716
|
-
|
|
1717
|
-
hostPromptReminder: adapter === "codex" || adapter === "cursor",
|
|
1718
|
-
statusFooterNudge: adapter === "codex",
|
|
1719
|
-
turnRitualNudge: adapter === "claude-code" ? adapter : undefined,
|
|
1877
|
+
...behavior.promptContextNudges,
|
|
1720
1878
|
}).trim();
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
additionalContext = [additionalContext, fileLinkContext].filter(Boolean).join("\n\n");
|
|
1725
|
-
}
|
|
1879
|
+
const fileLinkContext = behavior.fileLinkContext?.(coordRoot, workspaceCwd) ?? "";
|
|
1880
|
+
if (fileLinkContext) {
|
|
1881
|
+
additionalContext = [additionalContext, fileLinkContext].filter(Boolean).join("\n\n");
|
|
1726
1882
|
}
|
|
1727
1883
|
if (recoveryBriefing) {
|
|
1728
1884
|
additionalContext = [additionalContext, recoveryBriefing].filter(Boolean).join("\n\n");
|
|
@@ -1764,18 +1920,10 @@ async function emitSubagentStartContext(coordRoot, instanceId, sessionId, data,
|
|
|
1764
1920
|
emitContext(adapter, "SubagentStart", combined);
|
|
1765
1921
|
}
|
|
1766
1922
|
function adapterPlatform(adapter) {
|
|
1767
|
-
if (adapter === "claude-code")
|
|
1768
|
-
return "claude-code";
|
|
1769
1923
|
return adapter;
|
|
1770
1924
|
}
|
|
1771
|
-
function adapterFromPlatform(platform) {
|
|
1772
|
-
if (platform === "cursor")
|
|
1773
|
-
return "cursor";
|
|
1774
|
-
if (platform === "codex")
|
|
1775
|
-
return "codex";
|
|
1776
|
-
return "claude-code";
|
|
1777
|
-
}
|
|
1778
1925
|
async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, emittedData, adapter, recoveryBriefing = "", backupCue = "") {
|
|
1926
|
+
const behavior = adapterBehavior(adapter);
|
|
1779
1927
|
const workflowChild = coordEnv("WORKFLOW_CHILD") === "1";
|
|
1780
1928
|
let additionalContext = "";
|
|
1781
1929
|
// Opportunistic reconciliation makes normal session starts a failsafe for
|
|
@@ -1795,14 +1943,14 @@ async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, e
|
|
|
1795
1943
|
instanceId,
|
|
1796
1944
|
sessionId,
|
|
1797
1945
|
agentName: agentName || undefined,
|
|
1798
|
-
platformLabel:
|
|
1946
|
+
platformLabel: behavior.sessionStartPlatformLabel,
|
|
1799
1947
|
}).trim();
|
|
1800
1948
|
}
|
|
1801
|
-
// Effect (
|
|
1802
|
-
// context. Was a standalone additionalContext emission
|
|
1803
|
-
// journal-on-start adapter; now that agent-hook is the
|
|
1804
|
-
// entry, it folds in here.
|
|
1805
|
-
if (
|
|
1949
|
+
// Effect (journal-maintaining adapters): merge the journal recovery cue into
|
|
1950
|
+
// the session-start context. Was a standalone additionalContext emission
|
|
1951
|
+
// from the previous journal-on-start adapter; now that agent-hook is the
|
|
1952
|
+
// single SessionStart entry, it folds in here.
|
|
1953
|
+
if (behavior.journalMaintenance && !workflowChild) {
|
|
1806
1954
|
const cue = journalRecoveryCue(coordRoot);
|
|
1807
1955
|
if (cue)
|
|
1808
1956
|
additionalContext = [additionalContext, cue].filter(Boolean).join("\n\n");
|
|
@@ -1813,19 +1961,14 @@ async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, e
|
|
|
1813
1961
|
if (backupCue && !workflowChild) {
|
|
1814
1962
|
additionalContext = [additionalContext, backupCue].filter(Boolean).join("\n\n");
|
|
1815
1963
|
}
|
|
1816
|
-
if (
|
|
1817
|
-
const fileLinkContext =
|
|
1964
|
+
if (!workflowChild && behavior.bridgeFor(emittedData.cwd)) {
|
|
1965
|
+
const fileLinkContext = behavior.fileLinkContext?.(coordRoot, emittedData.cwd) ?? "";
|
|
1818
1966
|
if (fileLinkContext) {
|
|
1819
1967
|
additionalContext = [additionalContext, fileLinkContext].filter(Boolean).join("\n\n");
|
|
1820
1968
|
}
|
|
1821
|
-
const
|
|
1822
|
-
if (
|
|
1823
|
-
additionalContext = [
|
|
1824
|
-
additionalContext,
|
|
1825
|
-
`Harnery hybrid warning: ${bridge.detail}. Run \`${resolveBinName(coordRoot)} doctor\` for the repair hint.`,
|
|
1826
|
-
]
|
|
1827
|
-
.filter(Boolean)
|
|
1828
|
-
.join("\n\n");
|
|
1969
|
+
const warning = behavior.bridgeWarning?.(coordRoot, emittedData.cwd);
|
|
1970
|
+
if (warning) {
|
|
1971
|
+
additionalContext = [additionalContext, warning].filter(Boolean).join("\n\n");
|
|
1829
1972
|
}
|
|
1830
1973
|
}
|
|
1831
1974
|
const { emitContext } = await import("./adapter/output.js");
|
|
@@ -1837,6 +1980,7 @@ function completeRecoveryInjection(coordRoot, instanceId, sessionId, recovery) {
|
|
|
1837
1980
|
}
|
|
1838
1981
|
main()
|
|
1839
1982
|
.then(async (code) => {
|
|
1983
|
+
clearHookRunMarker(hookRunMarkerPath);
|
|
1840
1984
|
writeHookHealthCompletion(hookHealthState, {
|
|
1841
1985
|
finished_at_ms: performance.now(),
|
|
1842
1986
|
finished_rss_bytes: process.memoryUsage().rss,
|
|
@@ -1851,6 +1995,7 @@ main()
|
|
|
1851
1995
|
pid: process.pid,
|
|
1852
1996
|
phase: "top-level",
|
|
1853
1997
|
});
|
|
1998
|
+
clearHookRunMarker(hookRunMarkerPath);
|
|
1854
1999
|
writeHookHealthCompletion(hookHealthState, {
|
|
1855
2000
|
finished_at_ms: performance.now(),
|
|
1856
2001
|
finished_rss_bytes: process.memoryUsage().rss,
|