harnery 0.33.0 → 0.34.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/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +4 -0
- package/dist/commands/adapter.js +2 -2
- package/dist/commands/agents.d.ts +144 -1
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +1220 -588
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +7 -6
- package/dist/commands/backup.d.ts +1 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +29 -16
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +234 -12
- package/dist/commands/checkpoint.d.ts.map +1 -1
- package/dist/commands/checkpoint.js +3 -2
- package/dist/commands/claude-desktop.d.ts +1 -1
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +3 -74
- package/dist/commands/context.js +1 -6
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +22 -38
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +91 -28
- package/dist/commands/doctor.js +10 -1
- package/dist/commands/events.d.ts +5 -0
- package/dist/commands/events.d.ts.map +1 -0
- package/dist/commands/events.js +189 -0
- package/dist/commands/governor.d.ts.map +1 -1
- package/dist/commands/governor.js +13 -4
- package/dist/commands/init.d.ts +9 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +223 -51
- package/dist/commands/journal.d.ts.map +1 -1
- package/dist/commands/journal.js +4 -13
- package/dist/commands/ledger-v3.d.ts +5 -0
- package/dist/commands/ledger-v3.d.ts.map +1 -0
- package/dist/commands/ledger-v3.js +91 -0
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +11 -5
- package/dist/commands/sync.d.ts +1 -1
- package/dist/commands/sync.js +1 -1
- package/dist/commands/web.d.ts +6 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +67 -7
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +22 -5
- package/dist/core/adapter.d.ts +5 -0
- package/dist/core/adapter.d.ts.map +1 -0
- package/dist/core/adapter.js +1 -0
- package/dist/core/adapters/attest.d.ts +5 -2
- package/dist/core/adapters/attest.d.ts.map +1 -1
- package/dist/core/adapters/attest.js +8 -3
- package/dist/core/adapters/profiles.d.ts +6 -6
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +4 -4
- package/dist/core/agents/canonical-emit.d.ts +17 -15
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +22 -22
- package/dist/core/agents/cli.js +207 -234
- package/dist/core/agents/codex-archive-v3.d.ts +14 -0
- package/dist/core/agents/codex-archive-v3.d.ts.map +1 -0
- package/dist/core/agents/codex-archive-v3.js +110 -0
- package/dist/core/agents/coord-client.d.ts +19 -81
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +65 -155
- package/dist/core/agents/finalization.d.ts +4 -1
- package/dist/core/agents/finalization.d.ts.map +1 -1
- package/dist/core/agents/finalization.js +89 -32
- package/dist/core/agents/index.d.ts +4 -5
- package/dist/core/agents/index.d.ts.map +1 -1
- package/dist/core/agents/index.js +4 -5
- package/dist/core/agents/live-authority-v3.d.ts +59 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -0
- package/dist/core/agents/live-authority-v3.js +233 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts +20 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -0
- package/dist/core/agents/live-lifecycle-v3.js +91 -0
- package/dist/core/agents/live-observation-v3.d.ts +25 -0
- package/dist/core/agents/live-observation-v3.d.ts.map +1 -0
- package/dist/core/agents/live-observation-v3.js +157 -0
- package/dist/core/agents/render/prompt-context.d.ts +4 -4
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +23 -58
- package/dist/core/agents/render/session-context.d.ts +1 -1
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +8 -32
- package/dist/core/agents/rules/claim-conflict.d.ts +3 -3
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +79 -127
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +3 -18
- package/dist/core/agents/rules/stop-hook.d.ts +9 -50
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +193 -236
- package/dist/core/agents/session-events.d.ts +5 -7
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +127 -95
- package/dist/core/agents/session-finalization-state-v3.d.ts +30 -0
- package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -0
- package/dist/core/agents/session-finalization-state-v3.js +40 -0
- package/dist/core/agents/session-finalizer-v3.d.ts +107 -0
- package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -0
- package/dist/core/agents/session-finalizer-v3.js +734 -0
- package/dist/core/agents/session-name-display.d.ts +40 -0
- package/dist/core/agents/session-name-display.d.ts.map +1 -0
- package/dist/core/agents/session-name-display.js +119 -0
- package/dist/core/agents/state/activity-log.d.ts +3 -3
- package/dist/core/agents/state/activity-log.d.ts.map +1 -1
- package/dist/core/agents/state/activity-log.js +7 -16
- package/dist/core/agents/state/heartbeat-writer.d.ts +23 -55
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +56 -228
- package/dist/core/agents/state/live-coordination-view.d.ts +31 -0
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -0
- package/dist/core/agents/state/live-coordination-view.js +209 -0
- package/dist/core/agents/state/session-state.d.ts +4 -3
- package/dist/core/agents/state/session-state.d.ts.map +1 -1
- package/dist/core/agents/state/session-state.js +18 -9
- package/dist/core/agents/state/stale-sweep.d.ts +1 -1
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +26 -15
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +5 -7
- package/dist/core/config.d.ts +33 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +98 -2
- package/dist/core/context/index.d.ts +1 -4
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +9 -9
- package/dist/core/events/span-parent.d.ts +15 -0
- package/dist/core/events/span-parent.d.ts.map +1 -0
- package/dist/core/events/span-parent.js +34 -0
- package/dist/core/events/v3/advance.d.ts +14 -0
- package/dist/core/events/v3/advance.d.ts.map +1 -0
- package/dist/core/events/v3/advance.js +134 -0
- package/dist/core/events/v3/authority-outbox.d.ts +78 -0
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -0
- package/dist/core/events/v3/authority-outbox.js +399 -0
- package/dist/core/events/v3/base-builder.d.ts +20 -0
- package/dist/core/events/v3/base-builder.d.ts.map +1 -0
- package/dist/core/events/v3/base-builder.js +29 -0
- package/dist/core/events/v3/base-contract.d.ts +7576 -0
- package/dist/core/events/v3/base-contract.d.ts.map +1 -0
- package/dist/core/events/v3/base-contract.js +647 -0
- package/dist/core/events/v3/bootstrap.d.ts +34 -0
- package/dist/core/events/v3/bootstrap.d.ts.map +1 -0
- package/dist/core/events/v3/bootstrap.js +175 -0
- package/dist/core/events/v3/builder.d.ts +20 -0
- package/dist/core/events/v3/builder.d.ts.map +1 -0
- package/dist/core/events/v3/builder.js +28 -0
- package/dist/core/events/v3/canonical.d.ts +31 -0
- package/dist/core/events/v3/canonical.d.ts.map +1 -0
- package/dist/core/events/v3/canonical.js +100 -0
- package/dist/core/events/v3/capabilities.d.ts +31 -0
- package/dist/core/events/v3/capabilities.d.ts.map +1 -0
- package/dist/core/events/v3/capabilities.js +107 -0
- package/dist/core/events/v3/capability-drift.d.ts +22 -0
- package/dist/core/events/v3/capability-drift.d.ts.map +1 -0
- package/dist/core/events/v3/capability-drift.js +134 -0
- package/dist/core/events/v3/catalog.d.ts +38 -0
- package/dist/core/events/v3/catalog.d.ts.map +1 -0
- package/dist/core/events/v3/catalog.js +135 -0
- package/dist/core/events/v3/clock-order.d.ts +34 -0
- package/dist/core/events/v3/clock-order.d.ts.map +1 -0
- package/dist/core/events/v3/clock-order.js +35 -0
- package/dist/core/events/v3/contract.d.ts +171 -0
- package/dist/core/events/v3/contract.d.ts.map +1 -0
- package/dist/core/events/v3/contract.js +180 -0
- package/dist/core/events/v3/control.d.ts +111 -0
- package/dist/core/events/v3/control.d.ts.map +1 -0
- package/dist/core/events/v3/control.js +419 -0
- package/dist/core/events/v3/coordination-view.d.ts +69 -0
- package/dist/core/events/v3/coordination-view.d.ts.map +1 -0
- package/dist/core/events/v3/coordination-view.js +119 -0
- package/dist/core/events/v3/economics.d.ts +77 -0
- package/dist/core/events/v3/economics.d.ts.map +1 -0
- package/dist/core/events/v3/economics.js +274 -0
- package/dist/core/events/v3/finalization-view.d.ts +26 -0
- package/dist/core/events/v3/finalization-view.d.ts.map +1 -0
- package/dist/core/events/v3/finalization-view.js +37 -0
- package/dist/core/events/v3/fingerprint-keys.d.ts +25 -0
- package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -0
- package/dist/core/events/v3/fingerprint-keys.js +217 -0
- package/dist/core/events/v3/generated.d.ts +4 -0
- package/dist/core/events/v3/generated.d.ts.map +1 -0
- package/dist/core/events/v3/generated.js +47 -0
- package/dist/core/events/v3/ids.d.ts +9 -0
- package/dist/core/events/v3/ids.d.ts.map +1 -0
- package/dist/core/events/v3/ids.js +25 -0
- package/dist/core/events/v3/index.d.ts +35 -0
- package/dist/core/events/v3/index.d.ts.map +1 -0
- package/dist/core/events/v3/index.js +33 -0
- package/dist/core/events/v3/latency.d.ts +63 -0
- package/dist/core/events/v3/latency.d.ts.map +1 -0
- package/dist/core/events/v3/latency.js +412 -0
- package/dist/core/events/v3/live-feed.d.ts +45 -0
- package/dist/core/events/v3/live-feed.d.ts.map +1 -0
- package/dist/core/events/v3/live-feed.js +246 -0
- package/dist/core/events/v3/live-observation.d.ts +35 -0
- package/dist/core/events/v3/live-observation.d.ts.map +1 -0
- package/dist/core/events/v3/live-observation.js +1 -0
- package/dist/core/events/v3/live-routing.d.ts +57 -0
- package/dist/core/events/v3/live-routing.d.ts.map +1 -0
- package/dist/core/events/v3/live-routing.js +107 -0
- package/dist/core/events/v3/producers/command-recorder.d.ts +67 -0
- package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -0
- package/dist/core/events/v3/producers/command-recorder.js +477 -0
- package/dist/core/events/v3/producers/command.d.ts +50 -0
- package/dist/core/events/v3/producers/command.d.ts.map +1 -0
- package/dist/core/events/v3/producers/command.js +135 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +53 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -0
- package/dist/core/events/v3/producers/coordination-recorder.js +376 -0
- package/dist/core/events/v3/producers/coordination.d.ts +90 -0
- package/dist/core/events/v3/producers/coordination.d.ts.map +1 -0
- package/dist/core/events/v3/producers/coordination.js +229 -0
- package/dist/core/events/v3/producers/hook-base.d.ts +54 -0
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -0
- package/dist/core/events/v3/producers/hook-base.js +424 -0
- package/dist/core/events/v3/producers/hook.d.ts +29 -0
- package/dist/core/events/v3/producers/hook.d.ts.map +1 -0
- package/dist/core/events/v3/producers/hook.js +71 -0
- package/dist/core/events/v3/producers/intake.d.ts +65 -0
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -0
- package/dist/core/events/v3/producers/intake.js +244 -0
- package/dist/core/events/v3/producers/recorder.d.ts +240 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -0
- package/dist/core/events/v3/producers/recorder.js +2176 -0
- package/dist/core/events/v3/projection.d.ts +101 -0
- package/dist/core/events/v3/projection.d.ts.map +1 -0
- package/dist/core/events/v3/projection.js +479 -0
- package/dist/core/events/v3/reader.d.ts +75 -0
- package/dist/core/events/v3/reader.d.ts.map +1 -0
- package/dist/core/events/v3/reader.js +778 -0
- package/dist/core/events/v3/recovery-record.d.ts +47 -0
- package/dist/core/events/v3/recovery-record.d.ts.map +1 -0
- package/dist/core/events/v3/recovery-record.js +167 -0
- package/dist/core/events/v3/recovery.d.ts +22 -0
- package/dist/core/events/v3/recovery.d.ts.map +1 -0
- package/dist/core/events/v3/recovery.js +227 -0
- package/dist/core/events/v3/span-state.d.ts +52 -0
- package/dist/core/events/v3/span-state.d.ts.map +1 -0
- package/dist/core/events/v3/span-state.js +142 -0
- package/dist/core/events/v3/targets.d.ts +23 -0
- package/dist/core/events/v3/targets.d.ts.map +1 -0
- package/dist/core/events/v3/targets.js +134 -0
- package/dist/core/events/v3/turn-telemetry.d.ts +41 -0
- package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -0
- package/dist/core/events/v3/turn-telemetry.js +168 -0
- package/dist/core/events/v3/validate.d.ts +9 -0
- package/dist/core/events/v3/validate.d.ts.map +1 -0
- package/dist/core/events/v3/validate.js +281 -0
- package/dist/core/events/v3/writer.d.ts +56 -0
- package/dist/core/events/v3/writer.d.ts.map +1 -0
- package/dist/core/events/v3/writer.js +335 -0
- package/dist/core/governor/index.d.ts +1 -1
- package/dist/core/governor/index.d.ts.map +1 -1
- package/dist/core/governor/index.js +1 -1
- package/dist/core/governor/plan-api.d.ts +2 -1
- package/dist/core/governor/plan-api.d.ts.map +1 -1
- package/dist/core/governor/plan-api.js +6 -3
- package/dist/core/governor/read.d.ts +1 -1
- package/dist/core/governor/read.d.ts.map +1 -1
- package/dist/core/governor/read.js +1 -1
- package/dist/core/governor/state.d.ts +9 -0
- package/dist/core/governor/state.d.ts.map +1 -1
- package/dist/core/governor/state.js +40 -3
- package/dist/core/guard/config.d.ts +5 -0
- package/dist/core/guard/config.d.ts.map +1 -0
- package/dist/core/guard/config.js +157 -0
- package/dist/core/guard/coordinator.d.ts +14 -0
- package/dist/core/guard/coordinator.d.ts.map +1 -0
- package/dist/core/guard/coordinator.js +173 -0
- package/dist/core/guard/evaluator.d.ts +4 -0
- package/dist/core/guard/evaluator.d.ts.map +1 -0
- package/dist/core/guard/evaluator.js +242 -0
- package/dist/core/guard/evidence-v3.d.ts +15 -0
- package/dist/core/guard/evidence-v3.d.ts.map +1 -0
- package/dist/core/guard/evidence-v3.js +171 -0
- package/dist/core/guard/index.d.ts +10 -0
- package/dist/core/guard/index.d.ts.map +1 -0
- package/dist/core/guard/index.js +6 -0
- package/dist/core/guard/live-source-v3.d.ts +40 -0
- package/dist/core/guard/live-source-v3.d.ts.map +1 -0
- package/dist/core/guard/live-source-v3.js +128 -0
- package/dist/core/guard/storage.d.ts +27 -0
- package/dist/core/guard/storage.d.ts.map +1 -0
- package/dist/core/guard/storage.js +140 -0
- package/dist/core/guard/types.d.ts +119 -0
- package/dist/core/guard/types.d.ts.map +1 -0
- package/dist/core/guard/types.js +1 -0
- package/dist/core/hooks/adapter/detect.d.ts +18 -1
- package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
- package/dist/core/hooks/adapter/detect.js +29 -0
- package/dist/core/hooks/adapter/events.d.ts +7 -4
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +12 -4
- package/dist/core/hooks/adapter/output.d.ts +2 -2
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/parse.d.ts +7 -4
- package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
- package/dist/core/hooks/adapter/parse.js +63 -25
- package/dist/core/hooks/adapter/wiring.d.ts +30 -4
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +89 -11
- package/dist/core/hooks/cli.d.ts +1 -1
- package/dist/core/hooks/cli.js +305 -444
- package/dist/core/hooks/effects/image-capture.d.ts +20 -34
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +212 -221
- package/dist/core/hooks/effects/index.d.ts +1 -9
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +1 -24
- package/dist/core/hooks/events/input-hash.d.ts +7 -0
- package/dist/core/hooks/events/input-hash.d.ts.map +1 -0
- package/dist/core/hooks/events/input-hash.js +43 -0
- package/dist/core/hooks/index.d.ts +4 -3
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/index.js +2 -3
- package/dist/core/hooks/resolve/coord-root.d.ts +1 -1
- package/dist/core/hooks/resolve/coord-root.js +1 -1
- package/dist/core/hooks/resolve/owner.d.ts +16 -15
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +51 -42
- package/dist/core/hooks/resolve/transcript.d.ts +33 -3
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +155 -14
- package/dist/core/hooks/session-name-presence.d.ts +9 -10
- package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
- package/dist/core/hooks/session-name-presence.js +15 -15
- package/dist/core/hooks/unsafe-cross-shell.d.ts +1 -1
- package/dist/core/hooks/unsafe-cross-shell.d.ts.map +1 -1
- package/dist/core/journal/index.d.ts +2 -2
- package/dist/core/journal/index.d.ts.map +1 -1
- package/dist/core/journal/index.js +9 -22
- package/dist/core/presence/blob.d.ts +4 -3
- package/dist/core/presence/blob.d.ts.map +1 -1
- package/dist/core/presence/blob.js +45 -44
- package/dist/core/presence/codec-digest.d.ts +29 -0
- package/dist/core/presence/codec-digest.d.ts.map +1 -0
- package/dist/core/presence/codec-digest.js +170 -0
- package/dist/core/presence/relay-client.d.ts +1 -1
- package/dist/core/presence/relay-client.d.ts.map +1 -1
- package/dist/core/presence/relay-client.js +5 -5
- package/dist/core/work/index.d.ts +1 -1
- package/dist/core/work/index.d.ts.map +1 -1
- package/dist/core/work/index.js +1 -1
- package/dist/core/work/read.d.ts +1 -1
- package/dist/core/work/read.d.ts.map +1 -1
- package/dist/core/work/read.js +1 -1
- package/dist/core/work/runner.d.ts.map +1 -1
- package/dist/core/work/runner.js +6 -0
- package/dist/core/work/state.d.ts +9 -0
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +17 -3
- package/dist/core/workflow/engine.js +24 -25
- package/dist/core/workflow/live-session-v3.d.ts +19 -0
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -0
- package/dist/core/workflow/live-session-v3.js +86 -0
- package/dist/core/workflow/scope-id.d.ts +3 -0
- package/dist/core/workflow/scope-id.d.ts.map +1 -0
- package/dist/core/workflow/scope-id.js +5 -0
- package/dist/core/workflow/workspaces/leases.d.ts +4 -0
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +53 -19
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/browser/client.d.ts +41 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +203 -0
- package/dist/lib/browser/critique.d.ts +28 -2
- package/dist/lib/browser/critique.d.ts.map +1 -1
- package/dist/lib/browser/critique.js +74 -18
- package/dist/lib/browser/geometry.d.ts +1 -1
- package/dist/lib/browser/geometry.d.ts.map +1 -1
- package/dist/lib/browser/geometry.js +130 -6
- package/dist/lib/browser/index.d.ts +3 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +3 -0
- package/dist/lib/browser/qa-plan.d.ts +174 -0
- package/dist/lib/browser/qa-plan.d.ts.map +1 -0
- package/dist/lib/browser/qa-plan.js +321 -0
- package/dist/lib/browser/qa-reuse.d.ts +99 -0
- package/dist/lib/browser/qa-reuse.d.ts.map +1 -0
- package/dist/lib/browser/qa-reuse.js +222 -0
- package/dist/lib/browser/qa-snapshot.d.ts +64 -0
- package/dist/lib/browser/qa-snapshot.d.ts.map +1 -0
- package/dist/lib/browser/qa-snapshot.js +171 -0
- package/dist/lib/browser/runts.d.ts.map +1 -1
- package/dist/lib/browser/runts.js +22 -1
- package/dist/lib/browser/tiling.d.ts +71 -0
- package/dist/lib/browser/tiling.d.ts.map +1 -0
- package/dist/lib/browser/tiling.js +118 -0
- package/dist/lib/claude-desktop.d.ts +0 -47
- package/dist/lib/claude-desktop.d.ts.map +1 -1
- package/dist/lib/claude-desktop.js +1 -169
- package/dist/lib/context/index.d.ts +0 -3
- package/dist/lib/context/index.d.ts.map +1 -1
- package/dist/lib/context/index.js +20 -42
- package/dist/lib/council/index.d.ts +2 -2
- package/dist/lib/council/index.d.ts.map +1 -1
- package/dist/lib/council/index.js +9 -20
- package/dist/lib/docs-frontmatter.d.ts +1 -6
- package/dist/lib/docs-frontmatter.d.ts.map +1 -1
- package/dist/lib/docs-frontmatter.js +12 -78
- package/dist/lib/docs-index.d.ts +9 -0
- package/dist/lib/docs-index.d.ts.map +1 -1
- package/dist/lib/docs-index.js +32 -3
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +14 -30
- package/dist/lib/docs-metadata-audit.d.ts +19 -0
- package/dist/lib/docs-metadata-audit.d.ts.map +1 -0
- package/dist/lib/docs-metadata-audit.js +132 -0
- package/dist/lib/docs-metadata-managed.d.ts +23 -0
- package/dist/lib/docs-metadata-managed.d.ts.map +1 -0
- package/dist/lib/docs-metadata-managed.js +78 -0
- package/dist/lib/docs-metadata-sync.d.ts +20 -0
- package/dist/lib/docs-metadata-sync.d.ts.map +1 -0
- package/dist/lib/docs-metadata-sync.js +222 -0
- package/dist/lib/docs-metadata-v2-schema.d.ts +273 -0
- package/dist/lib/docs-metadata-v2-schema.d.ts.map +1 -0
- package/dist/lib/docs-metadata-v2-schema.js +134 -0
- package/dist/lib/docs-metadata-v2.d.ts +21 -0
- package/dist/lib/docs-metadata-v2.d.ts.map +1 -0
- package/dist/lib/docs-metadata-v2.js +424 -0
- package/dist/lib/docs-new.d.ts +11 -0
- package/dist/lib/docs-new.d.ts.map +1 -0
- package/dist/lib/docs-new.js +52 -0
- package/dist/lib/docs-sweep.d.ts.map +1 -1
- package/dist/lib/docs-sweep.js +45 -29
- package/dist/lib/headless/index.d.ts +98 -0
- package/dist/lib/headless/index.d.ts.map +1 -0
- package/dist/lib/headless/index.js +211 -0
- package/dist/lib/identities/assume.d.ts +4 -4
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +52 -68
- package/dist/lib/instructions/apply.d.ts +5 -4
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +32 -20
- package/dist/lib/instructions/bundle.d.ts +1 -1
- package/dist/lib/instructions/bundle.d.ts.map +1 -1
- package/dist/lib/instructions/bundle.js +1 -1
- package/dist/lib/instructions/splice.d.ts +0 -1
- package/dist/lib/instructions/splice.d.ts.map +1 -1
- package/dist/lib/instructions/splice.js +22 -6
- package/dist/lib/instructions/templates.d.ts +7 -5
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +79 -9
- package/package.json +26 -5
- package/schemas/config.schema.json +120 -2
- package/schemas/docs-metadata-v2.schema.json +441 -0
- package/schemas/event-v3.schema.json +24472 -0
- package/src/commander.ts +4 -0
- package/src/commands/adapter.ts +2 -2
- package/src/commands/agents.ts +1512 -592
- package/src/commands/artifacts.ts +7 -6
- package/src/commands/backup.ts +29 -21
- package/src/commands/browse.ts +349 -8
- package/src/commands/checkpoint.ts +4 -3
- package/src/commands/claude-desktop.ts +2 -101
- package/src/commands/context.ts +1 -6
- package/src/commands/decision.ts +27 -44
- package/src/commands/docs.ts +100 -33
- package/src/commands/doctor.ts +14 -1
- package/src/commands/events.ts +256 -0
- package/src/commands/governor.ts +18 -4
- package/src/commands/init.ts +253 -57
- package/src/commands/instructions.ts +1 -1
- package/src/commands/journal.ts +4 -13
- package/src/commands/ledger-v3.ts +111 -0
- package/src/commands/presence.ts +11 -10
- package/src/commands/sync.ts +1 -1
- package/src/commands/web.ts +67 -8
- package/src/commands/work.ts +28 -5
- package/src/core/adapter.ts +5 -0
- package/src/core/adapters/attest.ts +8 -3
- package/src/core/adapters/profiles.ts +4 -6
- package/src/core/agents/canonical-emit.ts +34 -25
- package/src/core/agents/cli.ts +236 -262
- package/src/core/agents/codex-archive-v3.ts +138 -0
- package/src/core/agents/coord-client.ts +75 -188
- package/src/core/agents/finalization.ts +120 -38
- package/src/core/agents/index.ts +4 -5
- package/src/core/agents/live-authority-v3.ts +375 -0
- package/src/core/agents/live-lifecycle-v3.ts +140 -0
- package/src/core/agents/live-observation-v3.ts +216 -0
- package/src/core/agents/render/prompt-context.ts +29 -60
- package/src/core/agents/render/session-context.ts +10 -31
- package/src/core/agents/rules/claim-conflict.ts +85 -126
- package/src/core/agents/rules/commit-conflict.ts +3 -14
- package/src/core/agents/rules/stop-hook.ts +239 -293
- package/src/core/agents/session-events.ts +137 -104
- package/src/core/agents/session-finalization-state-v3.ts +90 -0
- package/src/core/agents/session-finalizer-v3.ts +953 -0
- package/src/core/agents/session-name-display.ts +149 -0
- package/src/core/agents/state/activity-log.ts +6 -14
- package/src/core/agents/state/heartbeat-writer.ts +82 -277
- package/src/core/agents/state/live-coordination-view.ts +290 -0
- package/src/core/agents/state/session-state.ts +24 -12
- package/src/core/agents/state/stale-sweep.ts +40 -17
- package/src/core/artifacts/index.ts +4 -5
- package/src/core/config.ts +148 -3
- package/src/core/context/index.ts +9 -17
- package/src/core/events/span-parent.ts +46 -0
- package/src/core/events/v3/advance.ts +168 -0
- package/src/core/events/v3/authority-outbox.ts +548 -0
- package/src/core/events/v3/base-builder.ts +60 -0
- package/src/core/events/v3/base-contract.ts +882 -0
- package/src/core/events/v3/bootstrap.ts +226 -0
- package/src/core/events/v3/builder.ts +59 -0
- package/src/core/events/v3/canonical.ts +153 -0
- package/src/core/events/v3/capabilities.ts +171 -0
- package/src/core/events/v3/capability-drift.ts +177 -0
- package/src/core/events/v3/catalog.ts +202 -0
- package/src/core/events/v3/clock-order.ts +41 -0
- package/src/core/events/v3/contract.ts +365 -0
- package/src/core/events/v3/control.ts +576 -0
- package/src/core/events/v3/coordination-view.ts +193 -0
- package/src/core/events/v3/economics.ts +384 -0
- package/src/core/events/v3/finalization-view.ts +67 -0
- package/src/core/events/v3/fingerprint-keys.ts +260 -0
- package/src/core/events/v3/generated.ts +48 -0
- package/src/core/events/v3/ids.ts +33 -0
- package/src/core/events/v3/index.ts +322 -0
- package/src/core/events/v3/latency.ts +551 -0
- package/src/core/events/v3/live-feed.ts +319 -0
- package/src/core/events/v3/live-observation.ts +39 -0
- package/src/core/events/v3/live-routing.ts +135 -0
- package/src/core/events/v3/producers/command-recorder.ts +631 -0
- package/src/core/events/v3/producers/command.ts +201 -0
- package/src/core/events/v3/producers/coordination-recorder.ts +538 -0
- package/src/core/events/v3/producers/coordination.ts +355 -0
- package/src/core/events/v3/producers/hook-base.ts +578 -0
- package/src/core/events/v3/producers/hook.ts +119 -0
- package/src/core/events/v3/producers/intake.ts +319 -0
- package/src/core/events/v3/producers/recorder.ts +2805 -0
- package/src/core/events/v3/projection.ts +653 -0
- package/src/core/events/v3/reader.ts +1127 -0
- package/src/core/events/v3/recovery-record.ts +244 -0
- package/src/core/events/v3/recovery.ts +271 -0
- package/src/core/events/v3/span-state.ts +187 -0
- package/src/core/events/v3/targets.ts +182 -0
- package/src/core/events/v3/turn-telemetry.ts +259 -0
- package/src/core/events/v3/validate.ts +361 -0
- package/src/core/events/v3/writer.ts +419 -0
- package/src/core/governor/index.ts +3 -0
- package/src/core/governor/plan-api.ts +10 -2
- package/src/core/governor/read.ts +3 -0
- package/src/core/governor/state.ts +52 -5
- package/src/core/guard/config.ts +206 -0
- package/src/core/guard/coordinator.ts +227 -0
- package/src/core/guard/evaluator.ts +343 -0
- package/src/core/guard/evidence-v3.ts +227 -0
- package/src/core/guard/index.ts +35 -0
- package/src/core/guard/live-source-v3.ts +204 -0
- package/src/core/guard/storage.ts +188 -0
- package/src/core/guard/types.ts +152 -0
- package/src/core/hooks/adapter/detect.ts +28 -1
- package/src/core/hooks/adapter/events.ts +14 -6
- package/src/core/hooks/adapter/output.ts +7 -2
- package/src/core/hooks/adapter/parse.ts +80 -38
- package/src/core/hooks/adapter/wiring.ts +119 -13
- package/src/core/hooks/cli.ts +351 -466
- package/src/core/hooks/effects/image-capture.ts +228 -238
- package/src/core/hooks/effects/index.ts +1 -36
- package/src/core/hooks/events/input-hash.ts +47 -0
- package/src/core/hooks/index.ts +4 -11
- package/src/core/hooks/resolve/coord-root.ts +1 -1
- package/src/core/hooks/resolve/owner.ts +52 -45
- package/src/core/hooks/resolve/transcript.ts +165 -14
- package/src/core/hooks/session-name-presence.ts +14 -15
- package/src/core/hooks/unsafe-cross-shell.ts +1 -1
- package/src/core/journal/index.ts +15 -22
- package/src/core/presence/blob.ts +48 -41
- package/src/core/presence/codec-digest.ts +215 -0
- package/src/core/presence/relay-client.ts +5 -5
- package/src/core/work/index.ts +3 -0
- package/src/core/work/read.ts +3 -0
- package/src/core/work/runner.ts +6 -0
- package/src/core/work/state.ts +27 -3
- package/src/core/workflow/engine.ts +24 -24
- package/src/core/workflow/live-session-v3.ts +126 -0
- package/src/core/workflow/scope-id.ts +6 -0
- package/src/core/workflow/workspaces/leases.ts +56 -19
- package/src/index.ts +1 -1
- package/src/lib/browser/client.ts +230 -0
- package/src/lib/browser/critique.ts +104 -27
- package/src/lib/browser/geometry.ts +143 -14
- package/src/lib/browser/index.ts +34 -0
- package/src/lib/browser/qa-plan.ts +501 -0
- package/src/lib/browser/qa-reuse.ts +306 -0
- package/src/lib/browser/qa-snapshot.ts +223 -0
- package/src/lib/browser/runts.ts +27 -2
- package/src/lib/browser/tiling.ts +164 -0
- package/src/lib/claude-desktop.ts +1 -232
- package/src/lib/context/index.ts +22 -46
- package/src/lib/council/index.ts +9 -21
- package/src/lib/docs-frontmatter.ts +8 -79
- package/src/lib/docs-index.ts +33 -3
- package/src/lib/docs-lint.ts +15 -29
- package/src/lib/docs-metadata-audit.ts +166 -0
- package/src/lib/docs-metadata-managed.ts +83 -0
- package/src/lib/docs-metadata-sync.ts +262 -0
- package/src/lib/docs-metadata-v2-schema.ts +138 -0
- package/src/lib/docs-metadata-v2.ts +646 -0
- package/src/lib/docs-new.ts +63 -0
- package/src/lib/docs-sweep.ts +42 -28
- package/src/lib/headless/index.ts +287 -0
- package/src/lib/identities/assume.ts +67 -86
- package/src/lib/instructions/apply.ts +33 -20
- package/src/lib/instructions/bundle.ts +2 -2
- package/src/lib/instructions/splice.ts +21 -7
- package/src/lib/instructions/templates.ts +85 -13
- package/dist/core/agents/cli-emit.d.ts +0 -27
- package/dist/core/agents/cli-emit.d.ts.map +0 -1
- package/dist/core/agents/cli-emit.js +0 -57
- package/dist/core/agents/codex-replay.d.ts +0 -29
- package/dist/core/agents/codex-replay.d.ts.map +0 -1
- package/dist/core/agents/codex-replay.js +0 -138
- package/dist/core/agents/events/consume.d.ts +0 -82
- package/dist/core/agents/events/consume.d.ts.map +0 -1
- package/dist/core/agents/events/consume.js +0 -195
- package/dist/core/agents/events/emit.d.ts +0 -42
- package/dist/core/agents/events/emit.d.ts.map +0 -1
- package/dist/core/agents/events/emit.js +0 -75
- package/dist/core/agents/events/ulid.d.ts +0 -11
- package/dist/core/agents/events/ulid.d.ts.map +0 -1
- package/dist/core/agents/events/ulid.js +0 -47
- package/dist/core/agents/state/heartbeat-projector.d.ts +0 -76
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +0 -1
- package/dist/core/agents/state/heartbeat-projector.js +0 -581
- package/dist/core/hooks/events/emit.d.ts +0 -31
- package/dist/core/hooks/events/emit.d.ts.map +0 -1
- package/dist/core/hooks/events/emit.js +0 -93
- package/dist/core/hooks/events/rotate.d.ts +0 -43
- package/dist/core/hooks/events/rotate.d.ts.map +0 -1
- package/dist/core/hooks/events/rotate.js +0 -142
- package/dist/core/hooks/events/schema.d.ts +0 -341
- package/dist/core/hooks/events/schema.d.ts.map +0 -1
- package/dist/core/hooks/events/schema.js +0 -12
- package/dist/core/hooks/events/ulid.d.ts +0 -10
- package/dist/core/hooks/events/ulid.d.ts.map +0 -1
- package/dist/core/hooks/events/ulid.js +0 -47
- package/dist/lib/docs-frontmatter-migrate.d.ts +0 -33
- package/dist/lib/docs-frontmatter-migrate.d.ts.map +0 -1
- package/dist/lib/docs-frontmatter-migrate.js +0 -364
- package/src/core/agents/cli-emit.ts +0 -64
- package/src/core/agents/codex-replay.ts +0 -163
- package/src/core/agents/events/consume.ts +0 -254
- package/src/core/agents/events/emit.ts +0 -115
- package/src/core/agents/events/ulid.ts +0 -51
- package/src/core/agents/state/heartbeat-projector.ts +0 -645
- package/src/core/hooks/events/emit.ts +0 -125
- package/src/core/hooks/events/rotate.ts +0 -151
- package/src/core/hooks/events/schema.ts +0 -582
- package/src/core/hooks/events/ulid.ts +0 -51
- package/src/lib/docs-frontmatter-migrate.ts +0 -427
package/src/core/hooks/cli.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 4. Resolve instance_id (env → payload → pid-map walk).
|
|
9
9
|
* 5. Map event-name → canonical event_type.
|
|
10
10
|
* 6. Build event data from payload + resolvers (intent, transcript scan).
|
|
11
|
-
* 7.
|
|
11
|
+
* 7. Record the normalized observation in the canonical V3 ledger.
|
|
12
12
|
* 8. (Still also writes a debug breadcrumb to .harnery/debug/ for visibility.)
|
|
13
13
|
*
|
|
14
14
|
* Phase 2 ship criterion: confirms parser correctness across thousands of
|
|
@@ -22,20 +22,25 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
22
22
|
import { dirname, join } from "node:path";
|
|
23
23
|
import { coordEnv } from "../../lib/env.ts";
|
|
24
24
|
import { buildInstructionBundle } from "../../lib/instructions/bundle.ts";
|
|
25
|
-
import {
|
|
25
|
+
import type { Adapter } from "../adapter.ts";
|
|
26
26
|
import { coordBinPath } from "../agents/coord-bin.ts";
|
|
27
|
-
import { consumeSince, writeCursor } from "../agents/events/consume.ts";
|
|
28
27
|
import {
|
|
29
28
|
type ClaimFinalizationDecision,
|
|
30
29
|
type ClaimFinalizationDescriptor,
|
|
31
30
|
classifyWriteClaimFinalization,
|
|
32
31
|
formatWriteClaimFinalizationDenial,
|
|
33
32
|
} from "../agents/finalization.ts";
|
|
34
|
-
import { evaluateStopHook } from "../agents/rules/stop-hook.ts";
|
|
35
|
-
import {
|
|
36
|
-
|
|
33
|
+
import { evaluateStopHook, STOP_REMEDIATION_MARKER } from "../agents/rules/stop-hook.ts";
|
|
34
|
+
import {
|
|
35
|
+
assistantTextStartsWithSessionNameBlock,
|
|
36
|
+
isSessionNameRemediationCommand,
|
|
37
|
+
sessionNameDisplayInstruction,
|
|
38
|
+
sessionNameDisplayPending,
|
|
39
|
+
toolResponseMintedSessionName,
|
|
40
|
+
} from "../agents/session-name-display.ts";
|
|
41
|
+
import { stampSessionNameSeen } from "../agents/state/heartbeat-writer.ts";
|
|
42
|
+
import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.ts";
|
|
37
43
|
import { writePidmapRow } from "../agents/state/pidmap.ts";
|
|
38
|
-
import { shellMutationPaths } from "../agents/state/shell-mutation.ts";
|
|
39
44
|
import { agentsRequireGitFinalization, resolveBinName } from "../config.ts";
|
|
40
45
|
import {
|
|
41
46
|
checkpointContext,
|
|
@@ -47,8 +52,15 @@ import {
|
|
|
47
52
|
readContextState,
|
|
48
53
|
recordContextSample,
|
|
49
54
|
} from "../context/index.ts";
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
55
|
+
import { tryWriteLiveDisplayV3 } from "../events/v3/live-feed.ts";
|
|
56
|
+
import {
|
|
57
|
+
recordLiveHookSignalV3,
|
|
58
|
+
resolveLiveEventLedgerRouteV3,
|
|
59
|
+
} from "../events/v3/live-routing.ts";
|
|
60
|
+
import { captureSpanClockV3 } from "../events/v3/span-state.ts";
|
|
61
|
+
import { fetchPresence } from "../presence/index.ts";
|
|
62
|
+
import { stableScopeId } from "../workflow/scope-id.ts";
|
|
63
|
+
import { detectAdapter, shouldSkipHookAdapter } from "./adapter/detect.ts";
|
|
52
64
|
import {
|
|
53
65
|
extractBashCommand,
|
|
54
66
|
extractToolDescription,
|
|
@@ -71,21 +83,21 @@ import {
|
|
|
71
83
|
journalJanitor,
|
|
72
84
|
journalRecoveryCue,
|
|
73
85
|
playSound,
|
|
86
|
+
recordImageArtifactsV3,
|
|
74
87
|
resetSoundCounters,
|
|
75
88
|
runSessionSyncExtension,
|
|
76
|
-
runTurnSummary,
|
|
77
89
|
soundForEvent,
|
|
78
90
|
} from "./effects/index.ts";
|
|
79
|
-
import {
|
|
80
|
-
import type { Adapter } from "./events/schema.ts";
|
|
91
|
+
import { toolInputHash, toolTargetHash } from "./events/input-hash.ts";
|
|
81
92
|
import { canonicalize } from "./guard-path.ts";
|
|
82
93
|
import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.ts";
|
|
83
94
|
import { findCoordRoot } from "./resolve/coord-root.ts";
|
|
84
95
|
import { extractIntentComment, resolveIntent } from "./resolve/intent.ts";
|
|
85
96
|
import { resolveOwner } from "./resolve/owner.ts";
|
|
86
97
|
import {
|
|
87
|
-
|
|
98
|
+
inspectSessionNameDisplayImmediately,
|
|
88
99
|
scanAssistantTextIncludes,
|
|
100
|
+
scanSessionNameDisplayedImmediately,
|
|
89
101
|
scanStatusBoxPresent,
|
|
90
102
|
scanTranscriptModel,
|
|
91
103
|
} from "./resolve/transcript.ts";
|
|
@@ -172,7 +184,7 @@ function logError(coordRoot: string | null, err: unknown, context: Record<string
|
|
|
172
184
|
/**
|
|
173
185
|
* Spawn `agent-coord assign-name <owner> <kind>` to mint or recover the
|
|
174
186
|
* hurricane-style name for this owner. Returns null on any failure so
|
|
175
|
-
* session.
|
|
187
|
+
* session.started emission never breaks the adapter flow.
|
|
176
188
|
*
|
|
177
189
|
* Lives at agent-hooks side (not agent-coord) to keep emitter/consumer
|
|
178
190
|
* separation: we spawn rather than import.
|
|
@@ -209,7 +221,7 @@ function assignNameViaAgentCoord(
|
|
|
209
221
|
|
|
210
222
|
/**
|
|
211
223
|
* Direct (in-process) pidmap write, avoiding the spawn overhead of going via
|
|
212
|
-
* the agent-coord CLI for every session.
|
|
224
|
+
* the agent-coord CLI for every session.started signal. Pid-map rows
|
|
213
225
|
* are essential for `harn agents whoami` ppid resolution.
|
|
214
226
|
*
|
|
215
227
|
* This used to inline its own copy of the write to keep this module's
|
|
@@ -251,6 +263,7 @@ interface BuildContext {
|
|
|
251
263
|
raw: string;
|
|
252
264
|
adapter: Adapter;
|
|
253
265
|
instanceId: string;
|
|
266
|
+
eventName: string;
|
|
254
267
|
}
|
|
255
268
|
|
|
256
269
|
function buildEventData(
|
|
@@ -259,7 +272,7 @@ function buildEventData(
|
|
|
259
272
|
): Record<string, unknown> {
|
|
260
273
|
const p = ctx.payload;
|
|
261
274
|
switch (eventType) {
|
|
262
|
-
case "session.
|
|
275
|
+
case "session.started": {
|
|
263
276
|
const adapterPlatform =
|
|
264
277
|
ctx.adapter === "claude-code"
|
|
265
278
|
? "claude-code"
|
|
@@ -267,11 +280,11 @@ function buildEventData(
|
|
|
267
280
|
? "cursor"
|
|
268
281
|
: "codex";
|
|
269
282
|
// Recorded fork lineage is NOT detected here. On claude-code a fork
|
|
270
|
-
// never fires its own session.
|
|
283
|
+
// never fires its own session.started — SessionStart fires under the
|
|
271
284
|
// PARENT's session id (source=resume) before the fork id is minted
|
|
272
285
|
// (verified 2026-08-05) — so detection at this point can only mislabel
|
|
273
286
|
// the resumed parent. The fork's new instance is caught by the
|
|
274
|
-
// tool.
|
|
287
|
+
// tool.requested heal path instead. The forkedFrom plumbing below stays
|
|
275
288
|
// for adapters that DO report a parent at session start.
|
|
276
289
|
const forkedFrom: string | undefined = undefined;
|
|
277
290
|
// Assign (or recover) name + kind via agent-coord. Idempotent: resume
|
|
@@ -298,7 +311,7 @@ function buildEventData(
|
|
|
298
311
|
cwd: p?.cwd ?? process.cwd(),
|
|
299
312
|
// Claude Code's SessionStart payload omits `model` (Codex + Cursor
|
|
300
313
|
// supply it). Fall back to the transcript, populated on `resume`, and
|
|
301
|
-
// backfilled later by `turn.
|
|
314
|
+
// backfilled later by `turn.completed` for a fresh startup session.
|
|
302
315
|
model: p?.model ?? scanTranscriptModel(p?.transcript_path),
|
|
303
316
|
pid: adapterPid,
|
|
304
317
|
source: p?.source,
|
|
@@ -335,26 +348,35 @@ function buildEventData(
|
|
|
335
348
|
};
|
|
336
349
|
}
|
|
337
350
|
|
|
338
|
-
case "session.
|
|
351
|
+
case "session.ended":
|
|
339
352
|
return {
|
|
340
353
|
ended_at: new Date().toISOString(),
|
|
341
354
|
clean_exit: p?.clean_exit ?? true,
|
|
342
355
|
};
|
|
343
356
|
|
|
344
|
-
case "
|
|
357
|
+
case "turn.started": {
|
|
358
|
+
// Cursor can deliver beforeSubmitPrompt before sessionStart. Ensure that
|
|
359
|
+
// high-confidence prompt bootstrap also mints durable display identity;
|
|
360
|
+
// a later SessionStart remains idempotent.
|
|
361
|
+
if (
|
|
362
|
+
ctx.adapter === "cursor" &&
|
|
363
|
+
!readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name
|
|
364
|
+
) {
|
|
365
|
+
assignNameViaAgentCoord(ctx.coordRoot, ctx.instanceId, "session");
|
|
366
|
+
}
|
|
345
367
|
const prompt = p?.prompt ?? "";
|
|
346
368
|
const { value, truncated } = clampString(prompt, 4000);
|
|
347
369
|
return { prompt_text: value, ...(truncated ? { truncated: true } : {}) };
|
|
348
370
|
}
|
|
349
371
|
|
|
350
|
-
case "turn.
|
|
372
|
+
case "turn.completed": {
|
|
351
373
|
const lastAssistantMessage = (p?.raw.last_assistant_message as string | undefined) ?? "";
|
|
352
374
|
const fileLinkTelemetry =
|
|
353
375
|
ctx.adapter === "codex"
|
|
354
376
|
? codexWslFileLinkTelemetry(ctx.coordRoot, p?.cwd, lastAssistantMessage)
|
|
355
377
|
: null;
|
|
356
378
|
return {
|
|
357
|
-
// Backfill the model for adapters that omit it at session.
|
|
379
|
+
// Backfill the model for adapters that omit it at session.started
|
|
358
380
|
// (Claude Code). The transcript is populated with assistant turns by
|
|
359
381
|
// Stop-hook time, so this resolves even for fresh `startup` sessions.
|
|
360
382
|
model: p?.model ?? scanTranscriptModel(p?.transcript_path),
|
|
@@ -368,7 +390,7 @@ function buildEventData(
|
|
|
368
390
|
// Box present if the transcript scan finds it OR the final assistant
|
|
369
391
|
// message carries the `┌─ agent-` prefix. The latter covers codex's
|
|
370
392
|
// text-only stop (box in last_assistant_message, no transcript), which
|
|
371
|
-
// the verdict now sees because agent-hook emits this turn.
|
|
393
|
+
// the verdict now sees because agent-hook emits this turn.completed itself
|
|
372
394
|
// (the previous path passed those via the no-history fail-open).
|
|
373
395
|
status_box_present:
|
|
374
396
|
scanStatusBoxPresent(p?.transcript_path) || lastAssistantMessage.includes("┌─ agent-"),
|
|
@@ -388,15 +410,19 @@ function buildEventData(
|
|
|
388
410
|
// and which name that refers to. Reported on every stop while a name
|
|
389
411
|
// exists (the stop-hook.session_name verdict reads it per turn); the
|
|
390
412
|
// transcript scan itself stops once the name has been sighted.
|
|
391
|
-
...sessionNamePresence(ctx.coordRoot, ctx.instanceId,
|
|
392
|
-
|
|
413
|
+
...sessionNamePresence(ctx.coordRoot, ctx.instanceId, (name) =>
|
|
414
|
+
scanSessionNameDisplayedImmediately(
|
|
415
|
+
p?.transcript_path,
|
|
416
|
+
name,
|
|
417
|
+
assistantTextStartsWithSessionNameBlock,
|
|
418
|
+
),
|
|
393
419
|
),
|
|
394
420
|
...(fileLinkTelemetry ?? {}),
|
|
395
421
|
stop_hook_active: p?.stop_hook_active,
|
|
396
422
|
};
|
|
397
423
|
}
|
|
398
424
|
|
|
399
|
-
case "
|
|
425
|
+
case "agent.started": {
|
|
400
426
|
const subagentCallId =
|
|
401
427
|
(p?.raw.subagent_id as string | undefined) ?? (p?.raw.agent_id as string | undefined);
|
|
402
428
|
// Subagents inherit parent's name via the resolve-name session_id path
|
|
@@ -417,14 +443,14 @@ function buildEventData(
|
|
|
417
443
|
};
|
|
418
444
|
}
|
|
419
445
|
|
|
420
|
-
case "
|
|
446
|
+
case "agent.completed": {
|
|
421
447
|
const status = p?.exit_status;
|
|
422
448
|
const normalized: "ok" | "error" | "interrupted" =
|
|
423
449
|
status === "error" || status === "interrupted" ? status : "ok";
|
|
424
450
|
return { exit_status: normalized, reason: p?.reason };
|
|
425
451
|
}
|
|
426
452
|
|
|
427
|
-
case "tool.
|
|
453
|
+
case "tool.requested": {
|
|
428
454
|
const toolName = p?.tool_name ?? "unknown";
|
|
429
455
|
const command = extractBashCommand(toolName, p?.tool_input);
|
|
430
456
|
const description = extractToolDescription(p?.tool_input);
|
|
@@ -439,6 +465,8 @@ function buildEventData(
|
|
|
439
465
|
return {
|
|
440
466
|
tool_name: toolName,
|
|
441
467
|
tool_input: clamped.value,
|
|
468
|
+
input_hash: toolInputHash(toolName, p?.tool_input ?? null),
|
|
469
|
+
target_hash: toolTargetHash(toolName, p?.tool_input ?? null),
|
|
442
470
|
intent,
|
|
443
471
|
intent_source: source,
|
|
444
472
|
tool_use_id: p?.tool_use_id,
|
|
@@ -446,7 +474,7 @@ function buildEventData(
|
|
|
446
474
|
};
|
|
447
475
|
}
|
|
448
476
|
|
|
449
|
-
case "
|
|
477
|
+
case "wait.started": {
|
|
450
478
|
const description = extractToolDescription(p?.tool_input);
|
|
451
479
|
const reason = description ? clampString(description, 500).value : undefined;
|
|
452
480
|
return {
|
|
@@ -456,32 +484,28 @@ function buildEventData(
|
|
|
456
484
|
};
|
|
457
485
|
}
|
|
458
486
|
|
|
459
|
-
case "tool.
|
|
460
|
-
const toolName = p?.tool_name ?? "unknown";
|
|
461
|
-
const summary = summarizeOutput(p?.tool_response);
|
|
462
|
-
return {
|
|
463
|
-
tool_name: toolName,
|
|
464
|
-
output_summary: summary.summary,
|
|
465
|
-
exit_status: "ok" as const,
|
|
466
|
-
duration_ms: 0, // Phase 3 pairs pre/post via tool_use_id
|
|
467
|
-
tool_use_id: p?.tool_use_id,
|
|
468
|
-
...(summary.truncated ? { truncated: true } : {}),
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
case "tool.post_use_failure": {
|
|
487
|
+
case "tool.completed": {
|
|
473
488
|
const toolName = p?.tool_name ?? "unknown";
|
|
474
489
|
const summary = summarizeOutput(p?.tool_response);
|
|
475
|
-
return
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
490
|
+
return ctx.eventName === "post-tool-use-failure"
|
|
491
|
+
? {
|
|
492
|
+
tool_name: toolName,
|
|
493
|
+
error: summary.summary,
|
|
494
|
+
duration_ms: 0,
|
|
495
|
+
tool_use_id: p?.tool_use_id,
|
|
496
|
+
...(summary.truncated ? { truncated: true } : {}),
|
|
497
|
+
}
|
|
498
|
+
: {
|
|
499
|
+
tool_name: toolName,
|
|
500
|
+
output_summary: summary.summary,
|
|
501
|
+
exit_status: "ok" as const,
|
|
502
|
+
duration_ms: 0,
|
|
503
|
+
tool_use_id: p?.tool_use_id,
|
|
504
|
+
...(summary.truncated ? { truncated: true } : {}),
|
|
505
|
+
};
|
|
482
506
|
}
|
|
483
507
|
|
|
484
|
-
case "context.
|
|
508
|
+
case "context.compaction_started": {
|
|
485
509
|
const metadata = objectRecord(p?.raw.compact_metadata);
|
|
486
510
|
return {
|
|
487
511
|
trigger: stringField(p?.raw.trigger) ?? stringField(metadata?.trigger),
|
|
@@ -492,7 +516,7 @@ function buildEventData(
|
|
|
492
516
|
};
|
|
493
517
|
}
|
|
494
518
|
|
|
495
|
-
case "context.
|
|
519
|
+
case "context.compaction_completed": {
|
|
496
520
|
const metadata = objectRecord(p?.raw.compact_metadata);
|
|
497
521
|
return {
|
|
498
522
|
trigger: stringField(p?.raw.trigger) ?? stringField(metadata?.trigger),
|
|
@@ -533,10 +557,38 @@ function numberField(value: unknown): number | undefined {
|
|
|
533
557
|
}
|
|
534
558
|
|
|
535
559
|
async function main(): Promise<number> {
|
|
560
|
+
const hookStartedAt = performance.now();
|
|
561
|
+
const hookClock = captureSpanClockV3();
|
|
536
562
|
const { eventName, extra } = parseArgv(process.argv.slice(2));
|
|
537
563
|
const adapter = detectAdapter(process.argv.slice(2));
|
|
538
564
|
const raw = await readStdin();
|
|
539
565
|
|
|
566
|
+
// Cursor executes the Claude Code project hooks too on hosts wired for both
|
|
567
|
+
// adapters, piping them its own payload (identifiable by the top-level
|
|
568
|
+
// cursor_version envelope field). That stray `--adapter claude-code`
|
|
569
|
+
// dispatch must not record (twin generation) or play Claude-Code-only
|
|
570
|
+
// sounds, so it bails here, before every effect. Cursor's own
|
|
571
|
+
// `--adapter cursor` dispatch of the same payload carries the session.
|
|
572
|
+
if (shouldSkipHookAdapter(adapter, raw)) {
|
|
573
|
+
if (coordEnv("AGENT_COORD_OFF") !== "1") {
|
|
574
|
+
const coordRoot = findCoordRoot(process.cwd());
|
|
575
|
+
if (coordRoot) {
|
|
576
|
+
appendDebug(coordRoot, {
|
|
577
|
+
ts: new Date().toISOString(),
|
|
578
|
+
event_name: eventName,
|
|
579
|
+
adapter,
|
|
580
|
+
extra_argv: extra,
|
|
581
|
+
payload_bytes: raw.length,
|
|
582
|
+
cwd: process.cwd(),
|
|
583
|
+
pid: process.pid,
|
|
584
|
+
ppid: process.ppid,
|
|
585
|
+
skipped: "cursor-payload-claude-adapter",
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return 0;
|
|
590
|
+
}
|
|
591
|
+
|
|
540
592
|
// Kill-switch-INDEPENDENT effects: notification sounds fire BEFORE the
|
|
541
593
|
// HARNERY_AGENT_COORD_OFF gate so audible feedback survives incident-triage
|
|
542
594
|
// bypass: sound playback happens before the kill-switch bailout.
|
|
@@ -566,6 +618,7 @@ async function main(): Promise<number> {
|
|
|
566
618
|
|
|
567
619
|
const coordRoot = findCoordRoot(process.cwd());
|
|
568
620
|
if (!coordRoot) return 0;
|
|
621
|
+
const ledgerRoute = resolveLiveEventLedgerRouteV3(coordRoot);
|
|
569
622
|
|
|
570
623
|
// Always log a breadcrumb, useful when an event_type maps to null or owner
|
|
571
624
|
// resolution fails. Stays cheap (one append) and self-prunes via repo log
|
|
@@ -593,7 +646,10 @@ async function main(): Promise<number> {
|
|
|
593
646
|
}
|
|
594
647
|
|
|
595
648
|
const payload = parsePayload(raw, adapter);
|
|
596
|
-
const owner = resolveOwner({
|
|
649
|
+
const owner = resolveOwner({
|
|
650
|
+
payload: payload?.raw ?? null,
|
|
651
|
+
coordRoot,
|
|
652
|
+
});
|
|
597
653
|
if (!owner) {
|
|
598
654
|
appendDebug(coordRoot, {
|
|
599
655
|
...debugBase,
|
|
@@ -611,42 +667,110 @@ async function main(): Promise<number> {
|
|
|
611
667
|
raw,
|
|
612
668
|
adapter,
|
|
613
669
|
instanceId: owner.instance_id,
|
|
670
|
+
eventName,
|
|
614
671
|
});
|
|
672
|
+
const turnRitual =
|
|
673
|
+
norm.event_type === "turn.completed"
|
|
674
|
+
? {
|
|
675
|
+
status_box_present: data.status_box_present === true,
|
|
676
|
+
status_box_present_strict: data.status_box_present_strict === true,
|
|
677
|
+
session_name_required: typeof data.session_name_present_for === "string",
|
|
678
|
+
session_name_present: data.session_name_present === true,
|
|
679
|
+
}
|
|
680
|
+
: undefined;
|
|
681
|
+
const stopRemediation =
|
|
682
|
+
norm.event_type === "turn.started" &&
|
|
683
|
+
typeof data.prompt_text === "string" &&
|
|
684
|
+
data.prompt_text.includes(STOP_REMEDIATION_MARKER);
|
|
615
685
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
} as Parameters<typeof emit>[1]);
|
|
627
|
-
|
|
628
|
-
appendDebug(coordRoot, {
|
|
629
|
-
...debugBase,
|
|
630
|
-
event_type: norm.event_type,
|
|
631
|
-
owner_source: owner.source,
|
|
632
|
-
event_id: envelope.event_id,
|
|
633
|
-
});
|
|
686
|
+
if (ledgerRoute.state === "blocked") {
|
|
687
|
+
appendDebug(coordRoot, {
|
|
688
|
+
...debugBase,
|
|
689
|
+
skipped: "v3-control-blocked",
|
|
690
|
+
reason: ledgerRoute.reason,
|
|
691
|
+
event_type: norm.event_type,
|
|
692
|
+
owner_source: owner.source,
|
|
693
|
+
});
|
|
694
|
+
return 0;
|
|
695
|
+
}
|
|
634
696
|
|
|
635
|
-
|
|
636
|
-
// evidence directly from this envelope rather than draining the global
|
|
637
|
-
// projector: a drain here could apply a tool event before its authorization
|
|
638
|
-
// verdict. Tool progress is stamped later, after the guard allows it.
|
|
697
|
+
let capturedImages: ReturnType<typeof captureImages> = [];
|
|
639
698
|
if (
|
|
640
|
-
norm.event_type === "
|
|
641
|
-
norm.event_type === "
|
|
699
|
+
norm.event_type === "tool.requested" ||
|
|
700
|
+
(norm.event_type === "tool.completed" && eventName === "post-tool-use")
|
|
642
701
|
) {
|
|
643
702
|
try {
|
|
644
|
-
|
|
703
|
+
capturedImages = captureImages(coordRoot, norm.event_type, payload);
|
|
645
704
|
} catch (err) {
|
|
646
|
-
logError(coordRoot, err, { phase: "
|
|
705
|
+
logError(coordRoot, err, { phase: "image-capture" });
|
|
647
706
|
}
|
|
648
707
|
}
|
|
649
708
|
|
|
709
|
+
const v3Result = recordLiveHookSignalV3({
|
|
710
|
+
coordRoot,
|
|
711
|
+
route: ledgerRoute,
|
|
712
|
+
eventName,
|
|
713
|
+
payload,
|
|
714
|
+
adapter,
|
|
715
|
+
instanceId: owner.instance_id,
|
|
716
|
+
...(coordEnv("WORKFLOW_CHILD") === "1" && coordEnv("WORKFLOW_RUN_ID")
|
|
717
|
+
? {
|
|
718
|
+
run_id: stableScopeId("run", coordEnv("WORKFLOW_RUN_ID")!),
|
|
719
|
+
workflow_id: stableScopeId("wf", coordEnv("WORKFLOW_RUN_ID")!),
|
|
720
|
+
...(coordEnv("WORKFLOW_AGENT_ID")
|
|
721
|
+
? { workflow_agent_id: coordEnv("WORKFLOW_AGENT_ID") }
|
|
722
|
+
: {}),
|
|
723
|
+
}
|
|
724
|
+
: {}),
|
|
725
|
+
...(adapter === "codex" && isWslUncPath(payload?.cwd) ? { bridge: "codex-wsl" as const } : {}),
|
|
726
|
+
hook_name: eventName,
|
|
727
|
+
hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
|
|
728
|
+
monotonic_ns: hookClock.monotonic_ns,
|
|
729
|
+
...(stopRemediation ? { stop_remediation: true } : {}),
|
|
730
|
+
...(turnRitual ? { turn_ritual: turnRitual } : {}),
|
|
731
|
+
...(coordEnv("EXPERIMENT_DEFER_V3_DRAIN") === "1" ? { defer_drain: true } : {}),
|
|
732
|
+
});
|
|
733
|
+
const v3EventId =
|
|
734
|
+
v3Result && "event" in v3Result
|
|
735
|
+
? v3Result.event.event_id
|
|
736
|
+
: v3Result && "event_id" in v3Result
|
|
737
|
+
? v3Result.event_id
|
|
738
|
+
: undefined;
|
|
739
|
+
|
|
740
|
+
if (v3Result?.state === "recorded" && capturedImages.length > 0) {
|
|
741
|
+
try {
|
|
742
|
+
recordImageArtifactsV3(coordRoot, v3Result.event, capturedImages);
|
|
743
|
+
} catch (err) {
|
|
744
|
+
logError(coordRoot, err, { phase: "image-artifact-observation" });
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (
|
|
749
|
+
norm.event_type === "tool.requested" &&
|
|
750
|
+
v3Result &&
|
|
751
|
+
v3Result.state === "recorded" &&
|
|
752
|
+
"generation_id" in v3Result.event.scope
|
|
753
|
+
) {
|
|
754
|
+
const intent = typeof data.intent === "string" ? data.intent : undefined;
|
|
755
|
+
if (intent && intent !== "(no intent)") {
|
|
756
|
+
tryWriteLiveDisplayV3(coordRoot, {
|
|
757
|
+
generation_id: v3Result.event.scope.generation_id,
|
|
758
|
+
event_id: v3Result.event.event_id,
|
|
759
|
+
...(typeof data.tool_name === "string" ? { executable: data.tool_name } : {}),
|
|
760
|
+
intent_display: intent,
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
appendDebug(coordRoot, {
|
|
766
|
+
...debugBase,
|
|
767
|
+
event_type: norm.event_type,
|
|
768
|
+
owner_source: owner.source,
|
|
769
|
+
...(v3EventId ? { event_v3_id: v3EventId } : {}),
|
|
770
|
+
...(v3Result ? { event_v3_state: v3Result.state } : {}),
|
|
771
|
+
...(v3Result && "reason" in v3Result ? { event_v3_reason: v3Result.reason } : {}),
|
|
772
|
+
});
|
|
773
|
+
|
|
650
774
|
// Context telemetry is opportunistic and truthful: only persist a sample
|
|
651
775
|
// when the adapter payload actually exposes usage/window data. Identical
|
|
652
776
|
// measurements are de-duplicated before they reach the canonical stream.
|
|
@@ -661,22 +785,7 @@ async function main(): Promise<number> {
|
|
|
661
785
|
});
|
|
662
786
|
if (sample) {
|
|
663
787
|
const recorded = recordContextSample(coordRoot, owner.instance_id, sample);
|
|
664
|
-
|
|
665
|
-
emit(coordRoot, {
|
|
666
|
-
event_type: "context.sampled",
|
|
667
|
-
instance_id: owner.instance_id,
|
|
668
|
-
session_id: sessionId,
|
|
669
|
-
adapter,
|
|
670
|
-
data: {
|
|
671
|
-
model: sample.model,
|
|
672
|
-
used_tokens: sample.used_tokens,
|
|
673
|
-
window_tokens: sample.window_tokens,
|
|
674
|
-
used_percent: sample.used_percent,
|
|
675
|
-
telemetry_source: sample.source,
|
|
676
|
-
confidence: sample.confidence,
|
|
677
|
-
},
|
|
678
|
-
});
|
|
679
|
-
}
|
|
788
|
+
void recorded;
|
|
680
789
|
}
|
|
681
790
|
} catch (err) {
|
|
682
791
|
logError(coordRoot, err, { phase: "context-sample" });
|
|
@@ -686,7 +795,7 @@ async function main(): Promise<number> {
|
|
|
686
795
|
// A native pre-compaction signal is the safest available point to capture
|
|
687
796
|
// external work state. The operation is idempotent until recovery, so a
|
|
688
797
|
// adapter retry cannot create a storm of near-identical capsules.
|
|
689
|
-
if (norm.event_type === "context.
|
|
798
|
+
if (norm.event_type === "context.compaction_started") {
|
|
690
799
|
try {
|
|
691
800
|
const checkpoint = checkpointContext(coordRoot, {
|
|
692
801
|
sessionId,
|
|
@@ -696,25 +805,13 @@ async function main(): Promise<number> {
|
|
|
696
805
|
reason: "pre_compact",
|
|
697
806
|
model: payload?.model,
|
|
698
807
|
});
|
|
699
|
-
|
|
700
|
-
event_type: "context.checkpoint.created",
|
|
701
|
-
instance_id: owner.instance_id,
|
|
702
|
-
session_id: sessionId,
|
|
703
|
-
adapter,
|
|
704
|
-
data: {
|
|
705
|
-
capsule_id: checkpoint.capsule.capsule_id,
|
|
706
|
-
generation: checkpoint.capsule.generation,
|
|
707
|
-
path: checkpoint.state.latest_capsule ?? "",
|
|
708
|
-
reason: checkpoint.capsule.reason,
|
|
709
|
-
reused: checkpoint.reused,
|
|
710
|
-
},
|
|
711
|
-
});
|
|
808
|
+
void checkpoint;
|
|
712
809
|
} catch (err) {
|
|
713
810
|
logError(coordRoot, err, { phase: "context-checkpoint" });
|
|
714
811
|
}
|
|
715
812
|
}
|
|
716
813
|
|
|
717
|
-
if (norm.event_type === "context.
|
|
814
|
+
if (norm.event_type === "context.compaction_completed") {
|
|
718
815
|
try {
|
|
719
816
|
markContextCompactionCompleted(coordRoot, {
|
|
720
817
|
sessionId,
|
|
@@ -730,29 +827,15 @@ async function main(): Promise<number> {
|
|
|
730
827
|
// systemMessage JSON (peer table + wiring check + council invites).
|
|
731
828
|
// Adapter-agnostic since v0.5.0; replaces the previous bash UX layer
|
|
732
829
|
// and the equivalent per-adapter bash session_start handlers.
|
|
733
|
-
if (norm.event_type === "session.
|
|
830
|
+
if (norm.event_type === "session.started") {
|
|
734
831
|
// Effect (claude-code): prune stale journal archives + sweep orphans.
|
|
735
832
|
// The recovery-cue is merged into the
|
|
736
833
|
// session-start additionalContext inside emitSessionStartSystemMessage.
|
|
737
834
|
if (adapter === "claude-code") journalJanitor(coordRoot);
|
|
738
|
-
|
|
739
|
-
// agnostic, cheap (one readdir), fail-soft. Paired with journalJanitor as a
|
|
740
|
-
// session-start "tidy the coord layer" step.
|
|
741
|
-
try {
|
|
742
|
-
imageJanitor(coordRoot);
|
|
743
|
-
} catch (err) {
|
|
744
|
-
logError(coordRoot, err, { phase: "session-start-image-janitor" });
|
|
745
|
-
}
|
|
835
|
+
imageJanitor(coordRoot);
|
|
746
836
|
let recovery: PreparedContextRecovery | null = null;
|
|
747
837
|
if (payload?.source === "compact") {
|
|
748
838
|
try {
|
|
749
|
-
emit(coordRoot, {
|
|
750
|
-
event_type: "context.compaction.completed",
|
|
751
|
-
instance_id: owner.instance_id,
|
|
752
|
-
session_id: sessionId,
|
|
753
|
-
adapter,
|
|
754
|
-
data: { trigger: "auto" },
|
|
755
|
-
});
|
|
756
839
|
markContextCompactionCompleted(coordRoot, {
|
|
757
840
|
sessionId,
|
|
758
841
|
instanceId: owner.instance_id,
|
|
@@ -776,25 +859,15 @@ async function main(): Promise<number> {
|
|
|
776
859
|
recovery?.briefing ?? "",
|
|
777
860
|
);
|
|
778
861
|
if (injected && recovery) {
|
|
779
|
-
completeRecoveryInjection(
|
|
780
|
-
coordRoot,
|
|
781
|
-
owner.instance_id,
|
|
782
|
-
sessionId,
|
|
783
|
-
adapter,
|
|
784
|
-
recovery,
|
|
785
|
-
"SessionStart",
|
|
786
|
-
);
|
|
862
|
+
completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
|
|
787
863
|
}
|
|
788
864
|
} catch (err) {
|
|
789
865
|
logError(coordRoot, err, { phase: "session-start-systemMessage" });
|
|
790
866
|
}
|
|
791
|
-
//
|
|
792
|
-
//
|
|
793
|
-
// When a relay is configured, also make sure the live-socket daemon runs.
|
|
867
|
+
// Pull advisory cross-machine presence. V3 never publishes from the
|
|
868
|
+
// disposable heartbeat cache.
|
|
794
869
|
try {
|
|
795
|
-
publishPresence(coordRoot);
|
|
796
870
|
fetchPresence(coordRoot);
|
|
797
|
-
ensureRelayDaemon(coordRoot);
|
|
798
871
|
} catch (err) {
|
|
799
872
|
logError(coordRoot, err, { phase: "session-start-presence" });
|
|
800
873
|
}
|
|
@@ -802,7 +875,7 @@ async function main(): Promise<number> {
|
|
|
802
875
|
|
|
803
876
|
// Phase 8: SessionEnd cleanup: delete heartbeat + pid-map rows. Adapter-
|
|
804
877
|
// agnostic since v0.5.0.
|
|
805
|
-
if (norm.event_type === "session.
|
|
878
|
+
if (norm.event_type === "session.ended") {
|
|
806
879
|
try {
|
|
807
880
|
cleanupSessionEnd(coordRoot, owner.instance_id, (data.reason as string) ?? "unknown");
|
|
808
881
|
} catch (err) {
|
|
@@ -814,19 +887,12 @@ async function main(): Promise<number> {
|
|
|
814
887
|
journalArchive(coordRoot, owner.instance_id);
|
|
815
888
|
runSessionSyncExtension(coordRoot, true);
|
|
816
889
|
}
|
|
817
|
-
// Presence: publish the post-cleanup state so peers see this session gone
|
|
818
|
-
// promptly rather than waiting out the remote-stale window.
|
|
819
|
-
try {
|
|
820
|
-
publishPresence(coordRoot);
|
|
821
|
-
} catch (err) {
|
|
822
|
-
logError(coordRoot, err, { phase: "session-end-presence" });
|
|
823
|
-
}
|
|
824
890
|
}
|
|
825
891
|
|
|
826
892
|
// Phase 8: SubagentStart: sync-project to create the subagent heartbeat,
|
|
827
893
|
// log the lifecycle event, and emit a context message announcing the
|
|
828
894
|
// subagent (claude-code + cursor; codex doesn't fan out subagents today).
|
|
829
|
-
if (norm.event_type === "
|
|
895
|
+
if (norm.event_type === "agent.started") {
|
|
830
896
|
try {
|
|
831
897
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
832
898
|
if (existsSync(agentCoordBin)) {
|
|
@@ -853,7 +919,7 @@ async function main(): Promise<number> {
|
|
|
853
919
|
}
|
|
854
920
|
|
|
855
921
|
// Phase 8: SubagentStop: delete subagent heartbeat + log.
|
|
856
|
-
if (norm.event_type === "
|
|
922
|
+
if (norm.event_type === "agent.completed") {
|
|
857
923
|
try {
|
|
858
924
|
cleanupSessionEnd(coordRoot, owner.instance_id, (data.reason as string) ?? "unknown");
|
|
859
925
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
@@ -876,7 +942,7 @@ async function main(): Promise<number> {
|
|
|
876
942
|
|
|
877
943
|
// Phase 8: UserPromptSubmit: render dedup'd peer table + council pending
|
|
878
944
|
// and emit the adapter-shaped systemMessage JSON. Adapter-agnostic since v0.5.0.
|
|
879
|
-
if (norm.event_type === "
|
|
945
|
+
if (norm.event_type === "turn.started") {
|
|
880
946
|
// Effects (claude-code): reset per-turn sound rate-limit counters + run
|
|
881
947
|
// presence detection on the prompt.
|
|
882
948
|
if (adapter === "claude-code") {
|
|
@@ -917,81 +983,27 @@ async function main(): Promise<number> {
|
|
|
917
983
|
recovery?.briefing ?? "",
|
|
918
984
|
);
|
|
919
985
|
if (injected && recovery) {
|
|
920
|
-
completeRecoveryInjection(
|
|
921
|
-
coordRoot,
|
|
922
|
-
owner.instance_id,
|
|
923
|
-
sessionId,
|
|
924
|
-
adapter,
|
|
925
|
-
recovery,
|
|
926
|
-
"UserPromptSubmit",
|
|
927
|
-
);
|
|
986
|
+
completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
|
|
928
987
|
}
|
|
929
988
|
} catch (err) {
|
|
930
989
|
logError(coordRoot, err, { phase: "user-prompt-submit-systemMessage" });
|
|
931
990
|
}
|
|
932
991
|
}
|
|
933
992
|
|
|
934
|
-
// turn.
|
|
993
|
+
// turn.completed: telemetry, then the stop verdict. The
|
|
935
994
|
// verdict + codex-replay previously lived in the per-adapter shell adapters;
|
|
936
995
|
// agent-hook owns them now. Runs on the normal "stop" event only;
|
|
937
996
|
// "stop-failure" (API error) gets no gate, matching the previous
|
|
938
997
|
// stop vs stop-failure split.
|
|
939
|
-
if (norm.event_type === "turn.
|
|
940
|
-
//
|
|
941
|
-
// the status_checked / task_set / status_box_present evidence (codex
|
|
942
|
-
// doesn't emit those live; this re-emits turn.stop after agent-hook's own,
|
|
943
|
-
// so the verdict reads the replay's box signal as the latest).
|
|
944
|
-
if (adapter === "codex" && payload?.transcript_path && existsSync(payload.transcript_path)) {
|
|
945
|
-
try {
|
|
946
|
-
replayCodexJsonl({
|
|
947
|
-
coordRoot,
|
|
948
|
-
jsonlPath: payload.transcript_path,
|
|
949
|
-
sessionId,
|
|
950
|
-
instanceId: owner.instance_id,
|
|
951
|
-
lastAssistantMessage: (payload.raw.last_assistant_message as string | undefined) ?? "",
|
|
952
|
-
});
|
|
953
|
-
} catch (err) {
|
|
954
|
-
logError(coordRoot, err, { phase: "codex-replay" });
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
// CC effects: rate-limited session-telemetry sync + turn-summary Haiku
|
|
959
|
-
// auto-summary.
|
|
998
|
+
if (norm.event_type === "turn.completed" && eventName === "stop") {
|
|
999
|
+
// Claude Code session telemetry sync remains an independent side effect.
|
|
960
1000
|
if (adapter === "claude-code") {
|
|
961
1001
|
runSessionSyncExtension(coordRoot, false);
|
|
962
|
-
runTurnSummary(coordRoot, owner.instance_id, sessionId, payload?.transcript_path);
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
// Master-state heartbeat projection. Drains events.ndjson since the last
|
|
966
|
-
// cursor → per-owner heartbeats. Was a SECOND binary (`agent-coord project`)
|
|
967
|
-
// pinned to Claude Code Stop only; folded in here so it (a) is one
|
|
968
|
-
// entry per event like everything else and (b) fires on EVERY adapter's stop,
|
|
969
|
-
// not just CC. Runs unconditionally before the verdict's possible exit-2 return
|
|
970
|
-
// (the events are real regardless of whether the agent gets nagged), and after
|
|
971
|
-
// codex-replay above so codex's replayed events are included in the drain.
|
|
972
|
-
// Not an emitter (consumes + writes heartbeats), so no emitter/consumer conflict.
|
|
973
|
-
try {
|
|
974
|
-
const result = consumeSince(coordRoot);
|
|
975
|
-
projectHeartbeats(coordRoot, result.events);
|
|
976
|
-
if (result.lastEventId) writeCursor(coordRoot, result.lastEventId);
|
|
977
|
-
} catch (err) {
|
|
978
|
-
logError(coordRoot, err, { phase: "stop-projection" });
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// Presence: publish AFTER the projection above so the blob carries this
|
|
982
|
-
// turn's fresh task/turn_summary/files. Change-batched + keepalive'd;
|
|
983
|
-
// the push itself runs detached. The relay daemon (when configured) is
|
|
984
|
-
// re-ensured here too — it self-exits on idle, and the next turn revives it.
|
|
985
|
-
try {
|
|
986
|
-
publishPresence(coordRoot);
|
|
987
|
-
ensureRelayDaemon(coordRoot);
|
|
988
|
-
} catch (err) {
|
|
989
|
-
logError(coordRoot, err, { phase: "stop-presence" });
|
|
990
1002
|
}
|
|
991
1003
|
|
|
992
|
-
// Stop verdict (
|
|
993
|
-
// rule lives in
|
|
994
|
-
//
|
|
1004
|
+
// Stop verdict (V3 ritual + task/status gate). Direct in-process call: the
|
|
1005
|
+
// rule lives in Harnery. agent-hook already emitted this turn.completed
|
|
1006
|
+
// with privacy-safe ritual observations, so the evidence is in the stream.
|
|
995
1007
|
const verdict = evaluateStopHook(coordRoot, {
|
|
996
1008
|
rule: "stop-hook",
|
|
997
1009
|
instance_id: owner.instance_id,
|
|
@@ -1000,31 +1012,29 @@ async function main(): Promise<number> {
|
|
|
1000
1012
|
bypass: coordEnv("AGENT_COORD_BYPASS_STOP") === "1",
|
|
1001
1013
|
workflow_child: coordEnv("WORKFLOW_CHILD") === "1",
|
|
1002
1014
|
});
|
|
1003
|
-
const enforcementMode = stopEnforcementMode(verdict.rule);
|
|
1004
|
-
emit(coordRoot, {
|
|
1005
|
-
event_type: "stop.verdict",
|
|
1006
|
-
instance_id: owner.instance_id,
|
|
1007
|
-
session_id: sessionId,
|
|
1008
|
-
turn_id: payload?.turn_id,
|
|
1009
|
-
parent_turn_id: payload?.parent_turn_id,
|
|
1010
|
-
adapter,
|
|
1011
|
-
data: {
|
|
1012
|
-
allow: verdict.allow,
|
|
1013
|
-
rule: verdict.rule,
|
|
1014
|
-
...(verdict.reason ? { reason: verdict.reason } : {}),
|
|
1015
|
-
enforcement_mode: enforcementMode,
|
|
1016
|
-
eligible: enforcementMode === "enforced",
|
|
1017
|
-
nag_delivered: enforcementMode === "enforced" && !verdict.allow,
|
|
1018
|
-
},
|
|
1019
|
-
});
|
|
1020
1015
|
if (!verdict.allow) {
|
|
1021
|
-
// Adapter-aware enforcement channel: Claude Code
|
|
1022
|
-
//
|
|
1023
|
-
//
|
|
1024
|
-
// writes the right shape and returns the exit code to use.
|
|
1016
|
+
// Adapter-aware enforcement channel: Claude Code honors exit-2 + stderr
|
|
1017
|
+
// as a turn block; Cursor ignores exit codes and re-prompts only via a
|
|
1018
|
+
// `followup_message` it auto-submits. Codex returned observe-only above.
|
|
1019
|
+
// emitStopBlock writes the right shape and returns the exit code to use.
|
|
1025
1020
|
const { emitStopBlock } = await import("./adapter/output.ts");
|
|
1026
1021
|
return emitStopBlock(adapter, verdict, coordRoot);
|
|
1027
1022
|
}
|
|
1023
|
+
|
|
1024
|
+
// An explicit end requested from inside this turn cannot be authoritative
|
|
1025
|
+
// until the adapter has committed turn.completed above. Reconcile only
|
|
1026
|
+
// when such a request exists; any later real work cancels it in the
|
|
1027
|
+
// finalizer instead of being terminated underneath the agent.
|
|
1028
|
+
try {
|
|
1029
|
+
const { hasPendingExplicitSessionEndV3, reconcileSessionFinalizationV3 } = await import(
|
|
1030
|
+
"../agents/session-finalizer-v3.ts"
|
|
1031
|
+
);
|
|
1032
|
+
if (hasPendingExplicitSessionEndV3(coordRoot)) {
|
|
1033
|
+
reconcileSessionFinalizationV3(coordRoot);
|
|
1034
|
+
}
|
|
1035
|
+
} catch (err) {
|
|
1036
|
+
logError(coordRoot, err, { phase: "stop-explicit-session-finalization" });
|
|
1037
|
+
}
|
|
1028
1038
|
}
|
|
1029
1039
|
|
|
1030
1040
|
// Phase 7: PreToolUse: heartbeat + pid-map self-heal on every tool call.
|
|
@@ -1036,41 +1046,35 @@ async function main(): Promise<number> {
|
|
|
1036
1046
|
// side-by-side in the previous pre-tool-use adapter. The Phase 4-6 refactor
|
|
1037
1047
|
// preserved the heartbeat half but dropped the pid-map half; the pid-map
|
|
1038
1048
|
// call was restored here afterward.
|
|
1039
|
-
if (norm.event_type === "tool.
|
|
1049
|
+
if (norm.event_type === "tool.requested") {
|
|
1040
1050
|
try {
|
|
1041
1051
|
// Recorded fork lineage, heal-path flavor. A forked CC conversation
|
|
1042
|
-
// never fires its own session.
|
|
1052
|
+
// never fires its own session.started (SessionStart fires under the
|
|
1043
1053
|
// PARENT's session id with source=resume, before the fork id is
|
|
1044
1054
|
// minted; verified 2026-08-05), so the fork's new instance first
|
|
1045
1055
|
// materializes right here. Gate detection on "no heartbeat yet" so the
|
|
1046
1056
|
// transcript scan runs once per instance lifetime, not per tool call.
|
|
1047
|
-
let forkedFrom: string | undefined;
|
|
1048
|
-
if (
|
|
1049
|
-
adapter === "claude-code" &&
|
|
1050
|
-
payload?.transcript_path &&
|
|
1051
|
-
!existsSync(join(coordRoot, ".harnery", "active", `${owner.instance_id}.json`))
|
|
1052
|
-
) {
|
|
1053
|
-
forkedFrom = detectForkParent(payload.transcript_path, owner.instance_id);
|
|
1054
|
-
}
|
|
1055
|
-
healHeartbeatViaCli(coordRoot, owner.instance_id, sessionId, adapter, forkedFrom);
|
|
1056
1057
|
refreshPidmap(coordRoot, owner.instance_id, adapter, payload?.pid);
|
|
1057
1058
|
} catch (err) {
|
|
1058
1059
|
logError(coordRoot, err, { phase: "pre-tool-use-heal" });
|
|
1059
1060
|
}
|
|
1060
1061
|
|
|
1061
|
-
//
|
|
1062
|
-
//
|
|
1062
|
+
// The suggested name is a pending display latch. The set-task call itself
|
|
1063
|
+
// runs before a name exists; every later tool waits until the exact block
|
|
1064
|
+
// is the first assistant text after the mint result. Cursor supplies
|
|
1065
|
+
// agent_message directly; Claude Code and Codex are resolved from their
|
|
1066
|
+
// JSONL transcripts. Later commentary must not erase an already-correct
|
|
1067
|
+
// display, and an unreadable transcript must not deadlock every tool.
|
|
1063
1068
|
try {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
payload,
|
|
1068
|
-
instanceId: owner.instance_id,
|
|
1069
|
-
sessionId,
|
|
1069
|
+
const displayAllowed = await enforcePendingSessionNameDisplay(
|
|
1070
|
+
coordRoot,
|
|
1071
|
+
owner.instance_id,
|
|
1070
1072
|
adapter,
|
|
1071
|
-
|
|
1073
|
+
payload,
|
|
1074
|
+
);
|
|
1075
|
+
if (!displayAllowed) return 0;
|
|
1072
1076
|
} catch (err) {
|
|
1073
|
-
logError(coordRoot, err, { phase: "pre-tool-use-
|
|
1077
|
+
logError(coordRoot, err, { phase: "pre-tool-use-session-name" });
|
|
1074
1078
|
}
|
|
1075
1079
|
|
|
1076
1080
|
// Windows-native Codex + WSL UNC only: block the one cross-shell shape
|
|
@@ -1084,13 +1088,6 @@ async function main(): Promise<number> {
|
|
|
1084
1088
|
toolInput: payload?.tool_input,
|
|
1085
1089
|
});
|
|
1086
1090
|
if (unsafeShellReason) {
|
|
1087
|
-
emit(coordRoot, {
|
|
1088
|
-
event_type: "decision.block",
|
|
1089
|
-
instance_id: owner.instance_id,
|
|
1090
|
-
session_id: sessionId,
|
|
1091
|
-
adapter,
|
|
1092
|
-
data: { rule: "unsafe_cross_shell", reason: unsafeShellReason },
|
|
1093
|
-
});
|
|
1094
1091
|
const { emitDeny } = await import("./adapter/output.ts");
|
|
1095
1092
|
emitDeny(adapter, unsafeShellReason);
|
|
1096
1093
|
return 0;
|
|
@@ -1116,83 +1113,29 @@ async function main(): Promise<number> {
|
|
|
1116
1113
|
} catch (err) {
|
|
1117
1114
|
logError(coordRoot, err, { phase: "pre-tool-use-guard" });
|
|
1118
1115
|
}
|
|
1119
|
-
|
|
1120
|
-
if (guardAllowed) {
|
|
1121
|
-
try {
|
|
1122
|
-
stampSessionStateEvent(coordRoot, owner.instance_id, envelope);
|
|
1123
|
-
} catch (err) {
|
|
1124
|
-
logError(coordRoot, err, { phase: "activity-projection" });
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
// Shell-mutation warn (warn-only, never blocks). Was the cursor
|
|
1129
|
-
// beforeShellExecution + codex preToolUse-Bash shell-mutation-claim-log in
|
|
1130
|
-
// the per-adapter shell adapters. Cursor sends the command at payload.command;
|
|
1131
|
-
// codex Bash at tool_input.command. Emits a decision.warn per candidate-mutated
|
|
1132
|
-
// path so a peer sees the write in events.ndjson. (CC never did this,
|
|
1133
|
-
// preserved; it emits with its own hooks-side emitter per the
|
|
1134
|
-
// independent-emitter rule.)
|
|
1135
|
-
const shellCmd =
|
|
1136
|
-
eventName === "before-shell-execution"
|
|
1137
|
-
? ((payload?.raw.command as string | undefined) ?? "")
|
|
1138
|
-
: adapter === "codex" && data.tool_name === "Bash"
|
|
1139
|
-
? (((payload?.raw.tool_input as Record<string, unknown> | undefined)?.command as
|
|
1140
|
-
| string
|
|
1141
|
-
| undefined) ?? "")
|
|
1142
|
-
: "";
|
|
1143
|
-
if (shellCmd) {
|
|
1144
|
-
try {
|
|
1145
|
-
const paths = shellMutationPaths(shellCmd, coordRoot);
|
|
1146
|
-
const truncated = shellCmd.length > 80 ? shellCmd.slice(0, 80) : shellCmd;
|
|
1147
|
-
const platform = adapterPlatform(adapter);
|
|
1148
|
-
for (const p of paths) {
|
|
1149
|
-
emit(coordRoot, {
|
|
1150
|
-
event_type: "decision.warn",
|
|
1151
|
-
instance_id: owner.instance_id,
|
|
1152
|
-
session_id: sessionId,
|
|
1153
|
-
adapter,
|
|
1154
|
-
data: {
|
|
1155
|
-
rule: "shell_mutation_candidate",
|
|
1156
|
-
reason: `path=${p} cmd=${truncated} platform=${platform}`,
|
|
1157
|
-
},
|
|
1158
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
1159
|
-
} as Parameters<typeof emit>[1]);
|
|
1160
|
-
}
|
|
1161
|
-
} catch (err) {
|
|
1162
|
-
logError(coordRoot, err, { phase: "shell-mutation-warn" });
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1116
|
+
if (!guardAllowed) return 0;
|
|
1165
1117
|
}
|
|
1166
1118
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1119
|
+
if (norm.event_type === "tool.completed" && eventName === "post-tool-use") {
|
|
1120
|
+
// Inject only at the successful tool boundary that actually minted a name:
|
|
1121
|
+
// the first non-empty set-task or the transition to lifecycle done. The
|
|
1122
|
+
// coordination row intentionally remains pending until transcript evidence
|
|
1123
|
+
// catches up; reading that latch alone here would re-inject after every
|
|
1124
|
+
// later tool and make the agent print the same block repeatedly.
|
|
1170
1125
|
try {
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
// Image feed: a Bash command that wrote an image (harn browse, harn image,
|
|
1177
|
-
// --diff, …) is the "agent produced this" signal. Scan the command + its
|
|
1178
|
-
// output for freshly-written image paths and capture them.
|
|
1179
|
-
try {
|
|
1180
|
-
captureImages(coordRoot, {
|
|
1181
|
-
eventType: "tool.post_use",
|
|
1182
|
-
data,
|
|
1183
|
-
payload,
|
|
1184
|
-
instanceId: owner.instance_id,
|
|
1185
|
-
sessionId,
|
|
1186
|
-
adapter,
|
|
1187
|
-
});
|
|
1126
|
+
const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, owner.instance_id));
|
|
1127
|
+
if (name && toolResponseMintedSessionName(payload?.tool_response, name)) {
|
|
1128
|
+
const { emitContext } = await import("./adapter/output.ts");
|
|
1129
|
+
emitContext(adapter, "PostToolUse", sessionNameDisplayInstruction(name));
|
|
1130
|
+
}
|
|
1188
1131
|
} catch (err) {
|
|
1189
|
-
logError(coordRoot, err, { phase: "post-tool-use-
|
|
1132
|
+
logError(coordRoot, err, { phase: "post-tool-use-session-name" });
|
|
1190
1133
|
}
|
|
1191
1134
|
}
|
|
1192
1135
|
|
|
1193
1136
|
// Phase 7: PostToolUseFailure: release claim on failed Edit (the file
|
|
1194
1137
|
// never landed; the claim is stale). Adapter-agnostic.
|
|
1195
|
-
if (norm.event_type === "tool.
|
|
1138
|
+
if (norm.event_type === "tool.completed" && eventName === "post-tool-use-failure") {
|
|
1196
1139
|
try {
|
|
1197
1140
|
releaseClaimOnFailure(coordRoot, owner.instance_id, data, payload?.raw);
|
|
1198
1141
|
} catch (err) {
|
|
@@ -1203,11 +1146,48 @@ async function main(): Promise<number> {
|
|
|
1203
1146
|
return 0;
|
|
1204
1147
|
}
|
|
1205
1148
|
|
|
1206
|
-
function
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1149
|
+
async function enforcePendingSessionNameDisplay(
|
|
1150
|
+
coordRoot: string,
|
|
1151
|
+
instanceId: string,
|
|
1152
|
+
adapter: Adapter,
|
|
1153
|
+
payload: ParsedPayload | null,
|
|
1154
|
+
): Promise<boolean> {
|
|
1155
|
+
const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, instanceId));
|
|
1156
|
+
if (!name) return true;
|
|
1157
|
+
|
|
1158
|
+
const command = extractBashCommand(payload?.tool_name, payload?.tool_input);
|
|
1159
|
+
if (isSessionNameRemediationCommand(command, resolveBinName(coordRoot))) return true;
|
|
1160
|
+
|
|
1161
|
+
const inspection =
|
|
1162
|
+
payload?.agent_message !== undefined
|
|
1163
|
+
? {
|
|
1164
|
+
state: assistantTextStartsWithSessionNameBlock(payload.agent_message, name)
|
|
1165
|
+
? ("present" as const)
|
|
1166
|
+
: ("absent" as const),
|
|
1167
|
+
}
|
|
1168
|
+
: inspectSessionNameDisplayImmediately(
|
|
1169
|
+
payload?.transcript_path,
|
|
1170
|
+
name,
|
|
1171
|
+
assistantTextStartsWithSessionNameBlock,
|
|
1172
|
+
);
|
|
1173
|
+
if (inspection.state === "present") {
|
|
1174
|
+
stampSessionNameSeen(coordRoot, instanceId, name);
|
|
1175
|
+
return true;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
if (inspection.state === "unavailable") {
|
|
1179
|
+
const { emitContext } = await import("./adapter/output.ts");
|
|
1180
|
+
emitContext(
|
|
1181
|
+
adapter,
|
|
1182
|
+
"PreToolUse",
|
|
1183
|
+
"Harnery could not verify the pending session-name display because the adapter transcript is unavailable or not flushed yet. This tool is allowed so the session cannot deadlock; display the requested block before later work. Verification remains pending.",
|
|
1184
|
+
);
|
|
1185
|
+
return true;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
const { emitDeny } = await import("./adapter/output.ts");
|
|
1189
|
+
emitDeny(adapter, sessionNameDisplayInstruction(name));
|
|
1190
|
+
return false;
|
|
1211
1191
|
}
|
|
1212
1192
|
|
|
1213
1193
|
async function runPreToolUseGuard(
|
|
@@ -1297,24 +1277,6 @@ async function runPreToolUseGuard(
|
|
|
1297
1277
|
emitDeny(adapter, reason);
|
|
1298
1278
|
return false;
|
|
1299
1279
|
}
|
|
1300
|
-
|
|
1301
|
-
// The verdict mutates the live heartbeat immediately, but that file is a
|
|
1302
|
-
// projection and can be rebuilt at any later hook. Persist the acquisition
|
|
1303
|
-
// in the canonical stream too. This matters most for Codex apply_patch:
|
|
1304
|
-
// one tool call can claim several files, and tool.pre_use's clamped payload
|
|
1305
|
-
// is not a durable ownership representation for those targets.
|
|
1306
|
-
emit(coordRoot, {
|
|
1307
|
-
event_type: "claim.acquire",
|
|
1308
|
-
instance_id: instanceId,
|
|
1309
|
-
session_id: sessionId,
|
|
1310
|
-
adapter,
|
|
1311
|
-
source: "agent-hooks",
|
|
1312
|
-
data: {
|
|
1313
|
-
path: target.path,
|
|
1314
|
-
mode: "write",
|
|
1315
|
-
...(target.finalization ? { finalization: target.finalization } : {}),
|
|
1316
|
-
},
|
|
1317
|
-
});
|
|
1318
1280
|
}
|
|
1319
1281
|
return true;
|
|
1320
1282
|
}
|
|
@@ -1375,33 +1337,10 @@ function parseApplyPatchPaths(data: Record<string, unknown>): string[] {
|
|
|
1375
1337
|
return out;
|
|
1376
1338
|
}
|
|
1377
1339
|
|
|
1378
|
-
function healHeartbeatViaCli(
|
|
1379
|
-
coordRoot: string,
|
|
1380
|
-
instanceId: string,
|
|
1381
|
-
sessionId: string,
|
|
1382
|
-
adapter: string,
|
|
1383
|
-
forkedFrom?: string,
|
|
1384
|
-
): void {
|
|
1385
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1386
|
-
if (!existsSync(agentCoordBin)) return;
|
|
1387
|
-
// Pass the detected adapter so a pruned Cursor/Codex heartbeat is recreated
|
|
1388
|
-
// with the correct platform; without it, healHeartbeat defaults to
|
|
1389
|
-
// claude-code and the dashboard mislabels the agent. See
|
|
1390
|
-
// heartbeat-writer.healHeartbeat. --forked-from carries detected fork
|
|
1391
|
-
// lineage for an instance that never session.started (the CC fork flow).
|
|
1392
|
-
const args = ["heal-heartbeat", instanceId, sessionId, `--adapter=${adapter}`];
|
|
1393
|
-
if (forkedFrom) args.push(`--forked-from=${forkedFrom}`);
|
|
1394
|
-
spawnSync(agentCoordBin, args, {
|
|
1395
|
-
encoding: "utf8",
|
|
1396
|
-
timeout: 2000,
|
|
1397
|
-
env: childEnv(coordRoot),
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
1340
|
/**
|
|
1402
1341
|
* Walk up the ppid chain on Linux/WSL looking for the adapter anchor PID,
|
|
1403
1342
|
* the PID of the claude / cursor / codex binary. Finds the agent PID. Used by
|
|
1404
|
-
* `tool.
|
|
1343
|
+
* `tool.requested` pid-map self-heal so a re-parented adapter binary (the
|
|
1405
1344
|
* VS Code 2.1.x sibling-claude spawn case) gets its pid-map row rewritten on
|
|
1406
1345
|
* the next tool call rather than going invisible until SessionStart fires
|
|
1407
1346
|
* again, which it may never do.
|
|
@@ -1468,9 +1407,7 @@ function findAdapterAnchorPid(adapter?: Adapter): number | undefined {
|
|
|
1468
1407
|
}
|
|
1469
1408
|
|
|
1470
1409
|
/**
|
|
1471
|
-
* Pid-map self-heal for `tool.
|
|
1472
|
-
* `healHeartbeatViaCli`; the two were paired before the Phase 6 refactor split
|
|
1473
|
-
* them apart, then restored together afterward.
|
|
1410
|
+
* Pid-map self-heal for `tool.requested`.
|
|
1474
1411
|
*
|
|
1475
1412
|
* The pid argument prefers the payload's `pid` (CC populates it on
|
|
1476
1413
|
* SessionStart and may also send it on PreToolUse), then
|
|
@@ -1493,40 +1430,6 @@ function refreshPidmap(
|
|
|
1493
1430
|
writePidmapViaAgentCoord(coordRoot, pid, instanceId, adapterPlatform(adapter));
|
|
1494
1431
|
}
|
|
1495
1432
|
|
|
1496
|
-
function stampToolActivity(
|
|
1497
|
-
coordRoot: string,
|
|
1498
|
-
instanceId: string,
|
|
1499
|
-
data: Record<string, unknown>,
|
|
1500
|
-
): void {
|
|
1501
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1502
|
-
if (!existsSync(agentCoordBin)) return;
|
|
1503
|
-
const toolName = (data.tool_name as string | undefined) ?? "";
|
|
1504
|
-
// Extract a 1-line target from the tool_input blob (file path / command head).
|
|
1505
|
-
const toolInputRaw = data.tool_input;
|
|
1506
|
-
let target = "";
|
|
1507
|
-
if (typeof toolInputRaw === "string") {
|
|
1508
|
-
try {
|
|
1509
|
-
const parsed = JSON.parse(toolInputRaw) as Record<string, unknown>;
|
|
1510
|
-
target =
|
|
1511
|
-
(parsed.file_path as string | undefined) ??
|
|
1512
|
-
(parsed.path as string | undefined) ??
|
|
1513
|
-
(parsed.notebook_path as string | undefined) ??
|
|
1514
|
-
(parsed.command as string | undefined) ??
|
|
1515
|
-
(parsed.url as string | undefined) ??
|
|
1516
|
-
(parsed.pattern as string | undefined) ??
|
|
1517
|
-
"";
|
|
1518
|
-
} catch {
|
|
1519
|
-
/* skip */
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
if (target.length > 200) target = target.slice(0, 200);
|
|
1523
|
-
spawnSync(agentCoordBin, ["stamp-tool-activity", instanceId, toolName, target], {
|
|
1524
|
-
encoding: "utf8",
|
|
1525
|
-
timeout: 2000,
|
|
1526
|
-
env: childEnv(coordRoot),
|
|
1527
|
-
});
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
1433
|
function releaseClaimOnFailure(
|
|
1531
1434
|
coordRoot: string,
|
|
1532
1435
|
instanceId: string,
|
|
@@ -1570,6 +1473,12 @@ function releaseClaimOnFailure(
|
|
|
1570
1473
|
: filePath;
|
|
1571
1474
|
}
|
|
1572
1475
|
|
|
1476
|
+
// In V3 the release is an authority event, not a heartbeat mutation. Avoid
|
|
1477
|
+
// creating a disposable cache for a failure that never acquired a claim,
|
|
1478
|
+
// but release the exact path when the validated projection says it is held.
|
|
1479
|
+
const row = readLiveCoordinationRow(coordRoot, instanceId);
|
|
1480
|
+
if (!row?.files_touched?.includes(canonical)) return;
|
|
1481
|
+
|
|
1573
1482
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1574
1483
|
if (!existsSync(agentCoordBin)) return;
|
|
1575
1484
|
spawnSync(agentCoordBin, ["release-claim", instanceId, canonical], {
|
|
@@ -1580,15 +1489,6 @@ function releaseClaimOnFailure(
|
|
|
1580
1489
|
}
|
|
1581
1490
|
|
|
1582
1491
|
function cleanupSessionEnd(coordRoot: string, instanceId: string, reason: string): void {
|
|
1583
|
-
// Remove heartbeat from the canonical .harnery/active/ dir.
|
|
1584
|
-
const path = join(coordRoot, ".harnery", "active", `${instanceId}.json`);
|
|
1585
|
-
try {
|
|
1586
|
-
if (existsSync(path)) {
|
|
1587
|
-
require("node:fs").unlinkSync(path);
|
|
1588
|
-
}
|
|
1589
|
-
} catch {
|
|
1590
|
-
/* swallow */
|
|
1591
|
-
}
|
|
1592
1492
|
// Sweep pid-map entries pointing to this instance
|
|
1593
1493
|
const pidmapDir = join(coordRoot, ".harnery", "pid-map");
|
|
1594
1494
|
if (existsSync(pidmapDir)) {
|
|
@@ -1633,21 +1533,7 @@ async function emitUserPromptSubmitSystemMessage(
|
|
|
1633
1533
|
let additionalContext = "";
|
|
1634
1534
|
|
|
1635
1535
|
if (existsSync(agentCoordBin)) {
|
|
1636
|
-
// Look up the agent's name from its heartbeat (for council pending rendering).
|
|
1637
|
-
let agentName = "";
|
|
1638
|
-
try {
|
|
1639
|
-
const fs = require("node:fs") as typeof import("node:fs");
|
|
1640
|
-
const hbPath = join(coordRoot, ".harnery", "active", `${instanceId}.json`);
|
|
1641
|
-
if (fs.existsSync(hbPath)) {
|
|
1642
|
-
const hb = JSON.parse(fs.readFileSync(hbPath, "utf8")) as { name?: string };
|
|
1643
|
-
agentName = hb.name ?? "";
|
|
1644
|
-
}
|
|
1645
|
-
} catch {
|
|
1646
|
-
/* fall through with empty name; peer table still renders */
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
1536
|
const args = ["prompt-context", "--instance", instanceId, "--session", sessionId];
|
|
1650
|
-
if (agentName) args.push("--name", agentName);
|
|
1651
1537
|
// Every human-facing adapter gets the first-session naming reminder from
|
|
1652
1538
|
// the shared hook path. Cursor + Codex additionally get ongoing set-task
|
|
1653
1539
|
// staleness nudges; Claude Code enforces those via its Stop transcript.
|
|
@@ -1688,7 +1574,7 @@ function emitSubagentStartContext(
|
|
|
1688
1574
|
adapter: Adapter,
|
|
1689
1575
|
): void {
|
|
1690
1576
|
// Look up the subagent's assigned name (just-written by agent-coord assignName
|
|
1691
|
-
// in session.
|
|
1577
|
+
// in session.started data) + the parent's short id for the "you are a subagent
|
|
1692
1578
|
// of X" framing.
|
|
1693
1579
|
const subagentName = (data.name as string | undefined) ?? "";
|
|
1694
1580
|
if (!subagentName) return;
|
|
@@ -1733,13 +1619,19 @@ async function emitSessionStartSystemMessage(
|
|
|
1733
1619
|
recoveryBriefing = "",
|
|
1734
1620
|
): Promise<boolean> {
|
|
1735
1621
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1622
|
+
const workflowChild = coordEnv("WORKFLOW_CHILD") === "1";
|
|
1736
1623
|
let additionalContext = "";
|
|
1737
1624
|
if (existsSync(agentCoordBin)) {
|
|
1738
1625
|
// Sync-project so the heartbeat exists for downstream readers (peer table,
|
|
1739
1626
|
// wiring check, council invites).
|
|
1740
1627
|
spawnSync(agentCoordBin, ["project"], { encoding: "utf8", timeout: 3000 });
|
|
1741
|
-
//
|
|
1742
|
-
|
|
1628
|
+
// Opportunistic reconciliation makes normal session starts a failsafe for
|
|
1629
|
+
// archive, idle, cascade, and host lifecycle observations.
|
|
1630
|
+
spawnSync(agentCoordBin, ["reconcile-finalization"], {
|
|
1631
|
+
encoding: "utf8",
|
|
1632
|
+
timeout: 5000,
|
|
1633
|
+
env: { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot },
|
|
1634
|
+
});
|
|
1743
1635
|
|
|
1744
1636
|
// SESSION_START activity log line, fired across all adapters.
|
|
1745
1637
|
const model = (emittedData.model as string | undefined) ?? "unknown";
|
|
@@ -1756,32 +1648,37 @@ async function emitSessionStartSystemMessage(
|
|
|
1756
1648
|
{ encoding: "utf8", timeout: 2000 },
|
|
1757
1649
|
);
|
|
1758
1650
|
|
|
1759
|
-
//
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1651
|
+
// Workflow children retain lifecycle/event capture but do not receive
|
|
1652
|
+
// operator-facing peer, council, or init-remediation context. Injecting
|
|
1653
|
+
// that context can make a bounded child follow housekeeping instructions
|
|
1654
|
+
// instead of its assigned prompt.
|
|
1655
|
+
if (!workflowChild) {
|
|
1656
|
+
const agentName = (emittedData.name as string | undefined) ?? "";
|
|
1657
|
+
const args = ["session-context", "--instance", instanceId, "--session", sessionId];
|
|
1658
|
+
if (agentName) args.push("--name", agentName);
|
|
1659
|
+
// The "You are agent-X." prefix in session-context renders unqualified by
|
|
1660
|
+
// default (claude-code-style). For cursor/codex the bash dispatchers add
|
|
1661
|
+
// a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
|
|
1662
|
+
if (adapter !== "claude-code") {
|
|
1663
|
+
args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
|
|
1664
|
+
}
|
|
1665
|
+
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1666
|
+
if (result.status === 0 && result.stdout) additionalContext = result.stdout.trim();
|
|
1768
1667
|
}
|
|
1769
|
-
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1770
|
-
if (result.status === 0 && result.stdout) additionalContext = result.stdout.trim();
|
|
1771
1668
|
}
|
|
1772
1669
|
|
|
1773
1670
|
// Effect (claude-code): merge the journal recovery cue into the session-start
|
|
1774
1671
|
// context. Was a standalone additionalContext emission from the previous
|
|
1775
1672
|
// journal-on-start adapter; now that agent-hook is the single SessionStart
|
|
1776
1673
|
// entry, it folds in here.
|
|
1777
|
-
if (adapter === "claude-code") {
|
|
1674
|
+
if (adapter === "claude-code" && !workflowChild) {
|
|
1778
1675
|
const cue = journalRecoveryCue(coordRoot);
|
|
1779
1676
|
if (cue) additionalContext = [additionalContext, cue].filter(Boolean).join("\n\n");
|
|
1780
1677
|
}
|
|
1781
|
-
if (recoveryBriefing) {
|
|
1678
|
+
if (recoveryBriefing && !workflowChild) {
|
|
1782
1679
|
additionalContext = [additionalContext, recoveryBriefing].filter(Boolean).join("\n\n");
|
|
1783
1680
|
}
|
|
1784
|
-
if (adapter === "codex" && isWslUncPath(emittedData.cwd)) {
|
|
1681
|
+
if (adapter === "codex" && !workflowChild && isWslUncPath(emittedData.cwd)) {
|
|
1785
1682
|
const fileLinkContext = renderCodexWslFileLinkContext(coordRoot, emittedData.cwd);
|
|
1786
1683
|
if (fileLinkContext) {
|
|
1787
1684
|
additionalContext = [additionalContext, fileLinkContext].filter(Boolean).join("\n\n");
|
|
@@ -1807,22 +1704,10 @@ function completeRecoveryInjection(
|
|
|
1807
1704
|
coordRoot: string,
|
|
1808
1705
|
instanceId: string,
|
|
1809
1706
|
sessionId: string,
|
|
1810
|
-
adapter: Adapter,
|
|
1811
1707
|
recovery: PreparedContextRecovery,
|
|
1812
|
-
injectionEvent: "SessionStart" | "UserPromptSubmit",
|
|
1813
1708
|
): void {
|
|
1814
1709
|
completeContextRecovery(coordRoot, { sessionId, instanceId });
|
|
1815
|
-
|
|
1816
|
-
event_type: "context.recovery.injected",
|
|
1817
|
-
instance_id: instanceId,
|
|
1818
|
-
session_id: sessionId,
|
|
1819
|
-
adapter,
|
|
1820
|
-
data: {
|
|
1821
|
-
capsule_id: recovery.capsule.capsule_id,
|
|
1822
|
-
generation: recovery.capsule.generation,
|
|
1823
|
-
injection_event: injectionEvent,
|
|
1824
|
-
},
|
|
1825
|
-
});
|
|
1710
|
+
void recovery;
|
|
1826
1711
|
}
|
|
1827
1712
|
|
|
1828
1713
|
main()
|