harnery 0.32.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/README.md +2 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +7 -1
- 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 +1408 -565
- 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 +2 -2
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +2 -2
- 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.d.ts +4 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +55 -7
- 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 +10 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +233 -51
- package/dist/commands/instructions.d.ts +4 -0
- package/dist/commands/instructions.d.ts.map +1 -0
- package/dist/commands/instructions.js +100 -0
- 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 +15 -6
- 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 +27 -25
- package/dist/core/agents/cli.js +212 -235
- 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 -70
- 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 +5 -5
- package/dist/core/agents/index.d.ts.map +1 -1
- package/dist/core/agents/index.js +5 -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 +10 -4
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +61 -55
- 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 +15 -33
- 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 +29 -46
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +59 -199
- 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 +45 -0
- package/dist/core/agents/state/session-state.d.ts.map +1 -0
- package/dist/core/agents/state/session-state.js +90 -0
- 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 +13 -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 +64 -24
- 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 +345 -400
- package/dist/core/hooks/codex-authorization.d.ts +30 -0
- package/dist/core/hooks/codex-authorization.d.ts.map +1 -0
- package/dist/core/hooks/codex-authorization.js +199 -0
- 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 +8 -3
- package/dist/core/presence/blob.d.ts.map +1 -1
- package/dist/core/presence/blob.js +54 -41
- 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/index.js +15 -1
- 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/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 +33 -0
- package/dist/lib/instructions/bundle.d.ts.map +1 -0
- package/dist/lib/instructions/bundle.js +149 -0
- 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 +27 -6
- 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 +7 -1
- package/src/commands/adapter.ts +2 -2
- package/src/commands/agents.ts +1737 -570
- 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 +7 -5
- 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 +73 -8
- package/src/commands/events.ts +256 -0
- package/src/commands/governor.ts +18 -4
- package/src/commands/init.ts +264 -57
- package/src/commands/instructions.ts +129 -0
- package/src/commands/journal.ts +4 -13
- package/src/commands/ledger-v3.ts +111 -0
- package/src/commands/presence.ts +18 -11
- 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 +39 -28
- package/src/core/agents/cli.ts +242 -263
- package/src/core/agents/codex-archive-v3.ts +138 -0
- package/src/core/agents/coord-client.ts +75 -177
- package/src/core/agents/finalization.ts +120 -38
- package/src/core/agents/index.ts +5 -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 +89 -55
- package/src/core/agents/render/session-context.ts +23 -32
- 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 +98 -242
- package/src/core/agents/state/live-coordination-view.ts +290 -0
- package/src/core/agents/state/session-state.ts +143 -0
- 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 +15 -6
- package/src/core/hooks/adapter/output.ts +7 -2
- package/src/core/hooks/adapter/parse.ts +81 -36
- package/src/core/hooks/adapter/wiring.ts +119 -13
- package/src/core/hooks/cli.ts +398 -422
- package/src/core/hooks/codex-authorization.ts +253 -0
- 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 +63 -38
- package/src/core/presence/codec-digest.ts +215 -0
- package/src/core/presence/index.ts +18 -1
- 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/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 +199 -0
- 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 -65
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +0 -1
- package/dist/core/agents/state/heartbeat-projector.js +0 -527
- 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 -312
- 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 -582
- 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 -538
- package/src/core/hooks/events/ulid.ts +0 -51
- package/src/lib/docs-frontmatter-migrate.ts +0 -427
package/dist/core/hooks/cli.js
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
|
|
@@ -20,28 +20,32 @@ import { spawnSync } from "node:child_process";
|
|
|
20
20
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
21
21
|
import { dirname, join } from "node:path";
|
|
22
22
|
import { coordEnv } from "../../lib/env.js";
|
|
23
|
-
import {
|
|
23
|
+
import { buildInstructionBundle } from "../../lib/instructions/bundle.js";
|
|
24
24
|
import { coordBinPath } from "../agents/coord-bin.js";
|
|
25
|
-
import { consumeSince, writeCursor } from "../agents/events/consume.js";
|
|
26
25
|
import { classifyWriteClaimFinalization, formatWriteClaimFinalizationDenial, } from "../agents/finalization.js";
|
|
27
|
-
import { evaluateStopHook } from "../agents/rules/stop-hook.js";
|
|
28
|
-
import {
|
|
26
|
+
import { evaluateStopHook, STOP_REMEDIATION_MARKER } from "../agents/rules/stop-hook.js";
|
|
27
|
+
import { assistantTextStartsWithSessionNameBlock, isSessionNameRemediationCommand, sessionNameDisplayInstruction, sessionNameDisplayPending, toolResponseMintedSessionName, } from "../agents/session-name-display.js";
|
|
28
|
+
import { stampSessionNameSeen } from "../agents/state/heartbeat-writer.js";
|
|
29
|
+
import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
|
|
29
30
|
import { writePidmapRow } from "../agents/state/pidmap.js";
|
|
30
|
-
import { shellMutationPaths } from "../agents/state/shell-mutation.js";
|
|
31
31
|
import { agentsRequireGitFinalization, resolveBinName } from "../config.js";
|
|
32
32
|
import { checkpointContext, completeContextRecovery, extractContextSample, markContextCompactionCompleted, prepareContextRecovery, readContextState, recordContextSample, } from "../context/index.js";
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
33
|
+
import { tryWriteLiveDisplayV3 } from "../events/v3/live-feed.js";
|
|
34
|
+
import { recordLiveHookSignalV3, resolveLiveEventLedgerRouteV3, } from "../events/v3/live-routing.js";
|
|
35
|
+
import { captureSpanClockV3 } from "../events/v3/span-state.js";
|
|
36
|
+
import { fetchPresence } from "../presence/index.js";
|
|
37
|
+
import { stableScopeId } from "../workflow/scope-id.js";
|
|
38
|
+
import { detectAdapter, shouldSkipHookAdapter } from "./adapter/detect.js";
|
|
35
39
|
import { extractBashCommand, extractToolDescription, normalizeEventName, parsePayload, } from "./adapter/parse.js";
|
|
36
40
|
import { codexWslFileLinkTelemetry, inspectCodexWslBridge, isWslUncPath, renderCodexWslFileLinkContext, } from "./codex-wsl-bridge.js";
|
|
37
|
-
import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, resetSoundCounters, runSessionSyncExtension,
|
|
38
|
-
import {
|
|
41
|
+
import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, recordImageArtifactsV3, resetSoundCounters, runSessionSyncExtension, soundForEvent, } from "./effects/index.js";
|
|
42
|
+
import { toolInputHash, toolTargetHash } from "./events/input-hash.js";
|
|
39
43
|
import { canonicalize } from "./guard-path.js";
|
|
40
44
|
import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.js";
|
|
41
45
|
import { findCoordRoot } from "./resolve/coord-root.js";
|
|
42
46
|
import { extractIntentComment, resolveIntent } from "./resolve/intent.js";
|
|
43
47
|
import { resolveOwner } from "./resolve/owner.js";
|
|
44
|
-
import {
|
|
48
|
+
import { inspectSessionNameDisplayImmediately, scanAssistantTextIncludes, scanSessionNameDisplayedImmediately, scanStatusBoxPresent, scanTranscriptModel, } from "./resolve/transcript.js";
|
|
45
49
|
import { sessionNamePresence } from "./session-name-presence.js";
|
|
46
50
|
import { unsafeCrossShellReason } from "./unsafe-cross-shell.js";
|
|
47
51
|
function parseArgv(argv) {
|
|
@@ -117,7 +121,7 @@ function logError(coordRoot, err, context) {
|
|
|
117
121
|
/**
|
|
118
122
|
* Spawn `agent-coord assign-name <owner> <kind>` to mint or recover the
|
|
119
123
|
* hurricane-style name for this owner. Returns null on any failure so
|
|
120
|
-
* session.
|
|
124
|
+
* session.started emission never breaks the adapter flow.
|
|
121
125
|
*
|
|
122
126
|
* Lives at agent-hooks side (not agent-coord) to keep emitter/consumer
|
|
123
127
|
* separation: we spawn rather than import.
|
|
@@ -149,7 +153,7 @@ function assignNameViaAgentCoord(coordRoot, instanceId, kind, forkedFrom) {
|
|
|
149
153
|
}
|
|
150
154
|
/**
|
|
151
155
|
* Direct (in-process) pidmap write, avoiding the spawn overhead of going via
|
|
152
|
-
* the agent-coord CLI for every session.
|
|
156
|
+
* the agent-coord CLI for every session.started signal. Pid-map rows
|
|
153
157
|
* are essential for `harn agents whoami` ppid resolution.
|
|
154
158
|
*
|
|
155
159
|
* This used to inline its own copy of the write to keep this module's
|
|
@@ -183,18 +187,18 @@ function summarizeOutput(value, headTail = 500) {
|
|
|
183
187
|
function buildEventData(eventType, ctx) {
|
|
184
188
|
const p = ctx.payload;
|
|
185
189
|
switch (eventType) {
|
|
186
|
-
case "session.
|
|
190
|
+
case "session.started": {
|
|
187
191
|
const adapterPlatform = ctx.adapter === "claude-code"
|
|
188
192
|
? "claude-code"
|
|
189
193
|
: ctx.adapter === "cursor"
|
|
190
194
|
? "cursor"
|
|
191
195
|
: "codex";
|
|
192
196
|
// Recorded fork lineage is NOT detected here. On claude-code a fork
|
|
193
|
-
// never fires its own session.
|
|
197
|
+
// never fires its own session.started — SessionStart fires under the
|
|
194
198
|
// PARENT's session id (source=resume) before the fork id is minted
|
|
195
199
|
// (verified 2026-08-05) — so detection at this point can only mislabel
|
|
196
200
|
// the resumed parent. The fork's new instance is caught by the
|
|
197
|
-
// tool.
|
|
201
|
+
// tool.requested heal path instead. The forkedFrom plumbing below stays
|
|
198
202
|
// for adapters that DO report a parent at session start.
|
|
199
203
|
const forkedFrom = undefined;
|
|
200
204
|
// Assign (or recover) name + kind via agent-coord. Idempotent: resume
|
|
@@ -210,12 +214,13 @@ function buildEventData(eventType, ctx) {
|
|
|
210
214
|
if (adapterPid) {
|
|
211
215
|
writePidmapViaAgentCoord(ctx.coordRoot, adapterPid, ctx.instanceId, adapterPlatform);
|
|
212
216
|
}
|
|
217
|
+
const bundle = safeInstructionBundle(ctx.coordRoot, p?.cwd ?? process.cwd(), ctx.adapter);
|
|
213
218
|
return {
|
|
214
219
|
started_at: new Date().toISOString(),
|
|
215
220
|
cwd: p?.cwd ?? process.cwd(),
|
|
216
221
|
// Claude Code's SessionStart payload omits `model` (Codex + Cursor
|
|
217
222
|
// supply it). Fall back to the transcript, populated on `resume`, and
|
|
218
|
-
// backfilled later by `turn.
|
|
223
|
+
// backfilled later by `turn.completed` for a fresh startup session.
|
|
219
224
|
model: p?.model ?? scanTranscriptModel(p?.transcript_path),
|
|
220
225
|
pid: adapterPid,
|
|
221
226
|
source: p?.source,
|
|
@@ -223,6 +228,14 @@ function buildEventData(eventType, ctx) {
|
|
|
223
228
|
name: assigned?.name,
|
|
224
229
|
kind: "session",
|
|
225
230
|
agent_id: ctx.instanceId,
|
|
231
|
+
...(bundle
|
|
232
|
+
? {
|
|
233
|
+
instruction_bundle_id: bundle.instruction_bundle_id,
|
|
234
|
+
instruction_source_id: bundle.canonical_source_id,
|
|
235
|
+
instruction_profile_root: bundle.profile_root,
|
|
236
|
+
instruction_component_count: bundle.components.length,
|
|
237
|
+
}
|
|
238
|
+
: {}),
|
|
226
239
|
// Recorded fork lineage rides the canonical event too, so full replay
|
|
227
240
|
// and derived readers converge on the same parent linkage that
|
|
228
241
|
// .name-history carries (the ADR 0017 dual-write pattern).
|
|
@@ -243,23 +256,30 @@ function buildEventData(eventType, ctx) {
|
|
|
243
256
|
: {}),
|
|
244
257
|
};
|
|
245
258
|
}
|
|
246
|
-
case "session.
|
|
259
|
+
case "session.ended":
|
|
247
260
|
return {
|
|
248
261
|
ended_at: new Date().toISOString(),
|
|
249
262
|
clean_exit: p?.clean_exit ?? true,
|
|
250
263
|
};
|
|
251
|
-
case "
|
|
264
|
+
case "turn.started": {
|
|
265
|
+
// Cursor can deliver beforeSubmitPrompt before sessionStart. Ensure that
|
|
266
|
+
// high-confidence prompt bootstrap also mints durable display identity;
|
|
267
|
+
// a later SessionStart remains idempotent.
|
|
268
|
+
if (ctx.adapter === "cursor" &&
|
|
269
|
+
!readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
|
|
270
|
+
assignNameViaAgentCoord(ctx.coordRoot, ctx.instanceId, "session");
|
|
271
|
+
}
|
|
252
272
|
const prompt = p?.prompt ?? "";
|
|
253
273
|
const { value, truncated } = clampString(prompt, 4000);
|
|
254
274
|
return { prompt_text: value, ...(truncated ? { truncated: true } : {}) };
|
|
255
275
|
}
|
|
256
|
-
case "turn.
|
|
276
|
+
case "turn.completed": {
|
|
257
277
|
const lastAssistantMessage = p?.raw.last_assistant_message ?? "";
|
|
258
278
|
const fileLinkTelemetry = ctx.adapter === "codex"
|
|
259
279
|
? codexWslFileLinkTelemetry(ctx.coordRoot, p?.cwd, lastAssistantMessage)
|
|
260
280
|
: null;
|
|
261
281
|
return {
|
|
262
|
-
// Backfill the model for adapters that omit it at session.
|
|
282
|
+
// Backfill the model for adapters that omit it at session.started
|
|
263
283
|
// (Claude Code). The transcript is populated with assistant turns by
|
|
264
284
|
// Stop-hook time, so this resolves even for fresh `startup` sessions.
|
|
265
285
|
model: p?.model ?? scanTranscriptModel(p?.transcript_path),
|
|
@@ -273,7 +293,7 @@ function buildEventData(eventType, ctx) {
|
|
|
273
293
|
// Box present if the transcript scan finds it OR the final assistant
|
|
274
294
|
// message carries the `┌─ agent-` prefix. The latter covers codex's
|
|
275
295
|
// text-only stop (box in last_assistant_message, no transcript), which
|
|
276
|
-
// the verdict now sees because agent-hook emits this turn.
|
|
296
|
+
// the verdict now sees because agent-hook emits this turn.completed itself
|
|
277
297
|
// (the previous path passed those via the no-history fail-open).
|
|
278
298
|
status_box_present: scanStatusBoxPresent(p?.transcript_path) || lastAssistantMessage.includes("┌─ agent-"),
|
|
279
299
|
// Shadow measurement for the rule-2/3 detector: the loose scan above
|
|
@@ -291,12 +311,12 @@ function buildEventData(eventType, ctx) {
|
|
|
291
311
|
// and which name that refers to. Reported on every stop while a name
|
|
292
312
|
// exists (the stop-hook.session_name verdict reads it per turn); the
|
|
293
313
|
// transcript scan itself stops once the name has been sighted.
|
|
294
|
-
...sessionNamePresence(ctx.coordRoot, ctx.instanceId,
|
|
314
|
+
...sessionNamePresence(ctx.coordRoot, ctx.instanceId, (name) => scanSessionNameDisplayedImmediately(p?.transcript_path, name, assistantTextStartsWithSessionNameBlock)),
|
|
295
315
|
...(fileLinkTelemetry ?? {}),
|
|
296
316
|
stop_hook_active: p?.stop_hook_active,
|
|
297
317
|
};
|
|
298
318
|
}
|
|
299
|
-
case "
|
|
319
|
+
case "agent.started": {
|
|
300
320
|
const subagentCallId = p?.raw.subagent_id ?? p?.raw.agent_id;
|
|
301
321
|
// Subagents inherit parent's name via the resolve-name session_id path
|
|
302
322
|
// (agent-coord/state/names.ts → kind=transient). Use the call ID as the
|
|
@@ -314,12 +334,12 @@ function buildEventData(eventType, ctx) {
|
|
|
314
334
|
parent_session_id: p?.parent_session_id,
|
|
315
335
|
};
|
|
316
336
|
}
|
|
317
|
-
case "
|
|
337
|
+
case "agent.completed": {
|
|
318
338
|
const status = p?.exit_status;
|
|
319
339
|
const normalized = status === "error" || status === "interrupted" ? status : "ok";
|
|
320
340
|
return { exit_status: normalized, reason: p?.reason };
|
|
321
341
|
}
|
|
322
|
-
case "tool.
|
|
342
|
+
case "tool.requested": {
|
|
323
343
|
const toolName = p?.tool_name ?? "unknown";
|
|
324
344
|
const command = extractBashCommand(toolName, p?.tool_input);
|
|
325
345
|
const description = extractToolDescription(p?.tool_input);
|
|
@@ -334,36 +354,44 @@ function buildEventData(eventType, ctx) {
|
|
|
334
354
|
return {
|
|
335
355
|
tool_name: toolName,
|
|
336
356
|
tool_input: clamped.value,
|
|
357
|
+
input_hash: toolInputHash(toolName, p?.tool_input ?? null),
|
|
358
|
+
target_hash: toolTargetHash(toolName, p?.tool_input ?? null),
|
|
337
359
|
intent,
|
|
338
360
|
intent_source: source,
|
|
339
361
|
tool_use_id: p?.tool_use_id,
|
|
340
362
|
...(clamped.truncated ? { truncated: true } : {}),
|
|
341
363
|
};
|
|
342
364
|
}
|
|
343
|
-
case "
|
|
344
|
-
const
|
|
345
|
-
const
|
|
365
|
+
case "wait.started": {
|
|
366
|
+
const description = extractToolDescription(p?.tool_input);
|
|
367
|
+
const reason = description ? clampString(description, 500).value : undefined;
|
|
346
368
|
return {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
duration_ms: 0, // Phase 3 pairs pre/post via tool_use_id
|
|
351
|
-
tool_use_id: p?.tool_use_id,
|
|
352
|
-
...(summary.truncated ? { truncated: true } : {}),
|
|
369
|
+
request_kind: "permission",
|
|
370
|
+
tool_name: p?.tool_name,
|
|
371
|
+
...(reason ? { reason } : {}),
|
|
353
372
|
};
|
|
354
373
|
}
|
|
355
|
-
case "tool.
|
|
374
|
+
case "tool.completed": {
|
|
356
375
|
const toolName = p?.tool_name ?? "unknown";
|
|
357
376
|
const summary = summarizeOutput(p?.tool_response);
|
|
358
|
-
return
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
377
|
+
return ctx.eventName === "post-tool-use-failure"
|
|
378
|
+
? {
|
|
379
|
+
tool_name: toolName,
|
|
380
|
+
error: summary.summary,
|
|
381
|
+
duration_ms: 0,
|
|
382
|
+
tool_use_id: p?.tool_use_id,
|
|
383
|
+
...(summary.truncated ? { truncated: true } : {}),
|
|
384
|
+
}
|
|
385
|
+
: {
|
|
386
|
+
tool_name: toolName,
|
|
387
|
+
output_summary: summary.summary,
|
|
388
|
+
exit_status: "ok",
|
|
389
|
+
duration_ms: 0,
|
|
390
|
+
tool_use_id: p?.tool_use_id,
|
|
391
|
+
...(summary.truncated ? { truncated: true } : {}),
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
case "context.compaction_started": {
|
|
367
395
|
const metadata = objectRecord(p?.raw.compact_metadata);
|
|
368
396
|
return {
|
|
369
397
|
trigger: stringField(p?.raw.trigger) ?? stringField(metadata?.trigger),
|
|
@@ -372,7 +400,7 @@ function buildEventData(eventType, ctx) {
|
|
|
372
400
|
numberField(metadata?.pre_compact_tokens),
|
|
373
401
|
};
|
|
374
402
|
}
|
|
375
|
-
case "context.
|
|
403
|
+
case "context.compaction_completed": {
|
|
376
404
|
const metadata = objectRecord(p?.raw.compact_metadata);
|
|
377
405
|
return {
|
|
378
406
|
trigger: stringField(p?.raw.trigger) ?? stringField(metadata?.trigger),
|
|
@@ -386,6 +414,15 @@ function buildEventData(eventType, ctx) {
|
|
|
386
414
|
}
|
|
387
415
|
}
|
|
388
416
|
}
|
|
417
|
+
function safeInstructionBundle(coordRoot, cwd, adapter) {
|
|
418
|
+
try {
|
|
419
|
+
return buildInstructionBundle({ coordRoot, cwd, adapter });
|
|
420
|
+
}
|
|
421
|
+
catch (err) {
|
|
422
|
+
logError(coordRoot, err, { phase: "instruction-bundle-identity" });
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
389
426
|
function objectRecord(value) {
|
|
390
427
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
391
428
|
? value
|
|
@@ -398,9 +435,36 @@ function numberField(value) {
|
|
|
398
435
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
399
436
|
}
|
|
400
437
|
async function main() {
|
|
438
|
+
const hookStartedAt = performance.now();
|
|
439
|
+
const hookClock = captureSpanClockV3();
|
|
401
440
|
const { eventName, extra } = parseArgv(process.argv.slice(2));
|
|
402
441
|
const adapter = detectAdapter(process.argv.slice(2));
|
|
403
442
|
const raw = await readStdin();
|
|
443
|
+
// Cursor executes the Claude Code project hooks too on hosts wired for both
|
|
444
|
+
// adapters, piping them its own payload (identifiable by the top-level
|
|
445
|
+
// cursor_version envelope field). That stray `--adapter claude-code`
|
|
446
|
+
// dispatch must not record (twin generation) or play Claude-Code-only
|
|
447
|
+
// sounds, so it bails here, before every effect. Cursor's own
|
|
448
|
+
// `--adapter cursor` dispatch of the same payload carries the session.
|
|
449
|
+
if (shouldSkipHookAdapter(adapter, raw)) {
|
|
450
|
+
if (coordEnv("AGENT_COORD_OFF") !== "1") {
|
|
451
|
+
const coordRoot = findCoordRoot(process.cwd());
|
|
452
|
+
if (coordRoot) {
|
|
453
|
+
appendDebug(coordRoot, {
|
|
454
|
+
ts: new Date().toISOString(),
|
|
455
|
+
event_name: eventName,
|
|
456
|
+
adapter,
|
|
457
|
+
extra_argv: extra,
|
|
458
|
+
payload_bytes: raw.length,
|
|
459
|
+
cwd: process.cwd(),
|
|
460
|
+
pid: process.pid,
|
|
461
|
+
ppid: process.ppid,
|
|
462
|
+
skipped: "cursor-payload-claude-adapter",
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return 0;
|
|
467
|
+
}
|
|
404
468
|
// Kill-switch-INDEPENDENT effects: notification sounds fire BEFORE the
|
|
405
469
|
// HARNERY_AGENT_COORD_OFF gate so audible feedback survives incident-triage
|
|
406
470
|
// bypass: sound playback happens before the kill-switch bailout.
|
|
@@ -431,6 +495,7 @@ async function main() {
|
|
|
431
495
|
const coordRoot = findCoordRoot(process.cwd());
|
|
432
496
|
if (!coordRoot)
|
|
433
497
|
return 0;
|
|
498
|
+
const ledgerRoute = resolveLiveEventLedgerRouteV3(coordRoot);
|
|
434
499
|
// Always log a breadcrumb, useful when an event_type maps to null or owner
|
|
435
500
|
// resolution fails. Stays cheap (one append) and self-prunes via repo log
|
|
436
501
|
// rotation policy.
|
|
@@ -454,7 +519,10 @@ async function main() {
|
|
|
454
519
|
return 0;
|
|
455
520
|
}
|
|
456
521
|
const payload = parsePayload(raw, adapter);
|
|
457
|
-
const owner = resolveOwner({
|
|
522
|
+
const owner = resolveOwner({
|
|
523
|
+
payload: payload?.raw ?? null,
|
|
524
|
+
coordRoot,
|
|
525
|
+
});
|
|
458
526
|
if (!owner) {
|
|
459
527
|
appendDebug(coordRoot, {
|
|
460
528
|
...debugBase,
|
|
@@ -470,23 +538,97 @@ async function main() {
|
|
|
470
538
|
raw,
|
|
471
539
|
adapter,
|
|
472
540
|
instanceId: owner.instance_id,
|
|
541
|
+
eventName,
|
|
473
542
|
});
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
543
|
+
const turnRitual = norm.event_type === "turn.completed"
|
|
544
|
+
? {
|
|
545
|
+
status_box_present: data.status_box_present === true,
|
|
546
|
+
status_box_present_strict: data.status_box_present_strict === true,
|
|
547
|
+
session_name_required: typeof data.session_name_present_for === "string",
|
|
548
|
+
session_name_present: data.session_name_present === true,
|
|
549
|
+
}
|
|
550
|
+
: undefined;
|
|
551
|
+
const stopRemediation = norm.event_type === "turn.started" &&
|
|
552
|
+
typeof data.prompt_text === "string" &&
|
|
553
|
+
data.prompt_text.includes(STOP_REMEDIATION_MARKER);
|
|
554
|
+
if (ledgerRoute.state === "blocked") {
|
|
555
|
+
appendDebug(coordRoot, {
|
|
556
|
+
...debugBase,
|
|
557
|
+
skipped: "v3-control-blocked",
|
|
558
|
+
reason: ledgerRoute.reason,
|
|
559
|
+
event_type: norm.event_type,
|
|
560
|
+
owner_source: owner.source,
|
|
561
|
+
});
|
|
562
|
+
return 0;
|
|
563
|
+
}
|
|
564
|
+
let capturedImages = [];
|
|
565
|
+
if (norm.event_type === "tool.requested" ||
|
|
566
|
+
(norm.event_type === "tool.completed" && eventName === "post-tool-use")) {
|
|
567
|
+
try {
|
|
568
|
+
capturedImages = captureImages(coordRoot, norm.event_type, payload);
|
|
569
|
+
}
|
|
570
|
+
catch (err) {
|
|
571
|
+
logError(coordRoot, err, { phase: "image-capture" });
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const v3Result = recordLiveHookSignalV3({
|
|
575
|
+
coordRoot,
|
|
576
|
+
route: ledgerRoute,
|
|
577
|
+
eventName,
|
|
578
|
+
payload,
|
|
481
579
|
adapter,
|
|
482
|
-
|
|
483
|
-
|
|
580
|
+
instanceId: owner.instance_id,
|
|
581
|
+
...(coordEnv("WORKFLOW_CHILD") === "1" && coordEnv("WORKFLOW_RUN_ID")
|
|
582
|
+
? {
|
|
583
|
+
run_id: stableScopeId("run", coordEnv("WORKFLOW_RUN_ID")),
|
|
584
|
+
workflow_id: stableScopeId("wf", coordEnv("WORKFLOW_RUN_ID")),
|
|
585
|
+
...(coordEnv("WORKFLOW_AGENT_ID")
|
|
586
|
+
? { workflow_agent_id: coordEnv("WORKFLOW_AGENT_ID") }
|
|
587
|
+
: {}),
|
|
588
|
+
}
|
|
589
|
+
: {}),
|
|
590
|
+
...(adapter === "codex" && isWslUncPath(payload?.cwd) ? { bridge: "codex-wsl" } : {}),
|
|
591
|
+
hook_name: eventName,
|
|
592
|
+
hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
|
|
593
|
+
monotonic_ns: hookClock.monotonic_ns,
|
|
594
|
+
...(stopRemediation ? { stop_remediation: true } : {}),
|
|
595
|
+
...(turnRitual ? { turn_ritual: turnRitual } : {}),
|
|
596
|
+
...(coordEnv("EXPERIMENT_DEFER_V3_DRAIN") === "1" ? { defer_drain: true } : {}),
|
|
484
597
|
});
|
|
598
|
+
const v3EventId = v3Result && "event" in v3Result
|
|
599
|
+
? v3Result.event.event_id
|
|
600
|
+
: v3Result && "event_id" in v3Result
|
|
601
|
+
? v3Result.event_id
|
|
602
|
+
: undefined;
|
|
603
|
+
if (v3Result?.state === "recorded" && capturedImages.length > 0) {
|
|
604
|
+
try {
|
|
605
|
+
recordImageArtifactsV3(coordRoot, v3Result.event, capturedImages);
|
|
606
|
+
}
|
|
607
|
+
catch (err) {
|
|
608
|
+
logError(coordRoot, err, { phase: "image-artifact-observation" });
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (norm.event_type === "tool.requested" &&
|
|
612
|
+
v3Result &&
|
|
613
|
+
v3Result.state === "recorded" &&
|
|
614
|
+
"generation_id" in v3Result.event.scope) {
|
|
615
|
+
const intent = typeof data.intent === "string" ? data.intent : undefined;
|
|
616
|
+
if (intent && intent !== "(no intent)") {
|
|
617
|
+
tryWriteLiveDisplayV3(coordRoot, {
|
|
618
|
+
generation_id: v3Result.event.scope.generation_id,
|
|
619
|
+
event_id: v3Result.event.event_id,
|
|
620
|
+
...(typeof data.tool_name === "string" ? { executable: data.tool_name } : {}),
|
|
621
|
+
intent_display: intent,
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
}
|
|
485
625
|
appendDebug(coordRoot, {
|
|
486
626
|
...debugBase,
|
|
487
627
|
event_type: norm.event_type,
|
|
488
628
|
owner_source: owner.source,
|
|
489
|
-
|
|
629
|
+
...(v3EventId ? { event_v3_id: v3EventId } : {}),
|
|
630
|
+
...(v3Result ? { event_v3_state: v3Result.state } : {}),
|
|
631
|
+
...(v3Result && "reason" in v3Result ? { event_v3_reason: v3Result.reason } : {}),
|
|
490
632
|
});
|
|
491
633
|
// Context telemetry is opportunistic and truthful: only persist a sample
|
|
492
634
|
// when the adapter payload actually exposes usage/window data. Identical
|
|
@@ -502,22 +644,7 @@ async function main() {
|
|
|
502
644
|
});
|
|
503
645
|
if (sample) {
|
|
504
646
|
const recorded = recordContextSample(coordRoot, owner.instance_id, sample);
|
|
505
|
-
|
|
506
|
-
emit(coordRoot, {
|
|
507
|
-
event_type: "context.sampled",
|
|
508
|
-
instance_id: owner.instance_id,
|
|
509
|
-
session_id: sessionId,
|
|
510
|
-
adapter,
|
|
511
|
-
data: {
|
|
512
|
-
model: sample.model,
|
|
513
|
-
used_tokens: sample.used_tokens,
|
|
514
|
-
window_tokens: sample.window_tokens,
|
|
515
|
-
used_percent: sample.used_percent,
|
|
516
|
-
telemetry_source: sample.source,
|
|
517
|
-
confidence: sample.confidence,
|
|
518
|
-
},
|
|
519
|
-
});
|
|
520
|
-
}
|
|
647
|
+
void recorded;
|
|
521
648
|
}
|
|
522
649
|
}
|
|
523
650
|
catch (err) {
|
|
@@ -527,7 +654,7 @@ async function main() {
|
|
|
527
654
|
// A native pre-compaction signal is the safest available point to capture
|
|
528
655
|
// external work state. The operation is idempotent until recovery, so a
|
|
529
656
|
// adapter retry cannot create a storm of near-identical capsules.
|
|
530
|
-
if (norm.event_type === "context.
|
|
657
|
+
if (norm.event_type === "context.compaction_started") {
|
|
531
658
|
try {
|
|
532
659
|
const checkpoint = checkpointContext(coordRoot, {
|
|
533
660
|
sessionId,
|
|
@@ -537,25 +664,13 @@ async function main() {
|
|
|
537
664
|
reason: "pre_compact",
|
|
538
665
|
model: payload?.model,
|
|
539
666
|
});
|
|
540
|
-
|
|
541
|
-
event_type: "context.checkpoint.created",
|
|
542
|
-
instance_id: owner.instance_id,
|
|
543
|
-
session_id: sessionId,
|
|
544
|
-
adapter,
|
|
545
|
-
data: {
|
|
546
|
-
capsule_id: checkpoint.capsule.capsule_id,
|
|
547
|
-
generation: checkpoint.capsule.generation,
|
|
548
|
-
path: checkpoint.state.latest_capsule ?? "",
|
|
549
|
-
reason: checkpoint.capsule.reason,
|
|
550
|
-
reused: checkpoint.reused,
|
|
551
|
-
},
|
|
552
|
-
});
|
|
667
|
+
void checkpoint;
|
|
553
668
|
}
|
|
554
669
|
catch (err) {
|
|
555
670
|
logError(coordRoot, err, { phase: "context-checkpoint" });
|
|
556
671
|
}
|
|
557
672
|
}
|
|
558
|
-
if (norm.event_type === "context.
|
|
673
|
+
if (norm.event_type === "context.compaction_completed") {
|
|
559
674
|
try {
|
|
560
675
|
markContextCompactionCompleted(coordRoot, {
|
|
561
676
|
sessionId,
|
|
@@ -571,31 +686,16 @@ async function main() {
|
|
|
571
686
|
// systemMessage JSON (peer table + wiring check + council invites).
|
|
572
687
|
// Adapter-agnostic since v0.5.0; replaces the previous bash UX layer
|
|
573
688
|
// and the equivalent per-adapter bash session_start handlers.
|
|
574
|
-
if (norm.event_type === "session.
|
|
689
|
+
if (norm.event_type === "session.started") {
|
|
575
690
|
// Effect (claude-code): prune stale journal archives + sweep orphans.
|
|
576
691
|
// The recovery-cue is merged into the
|
|
577
692
|
// session-start additionalContext inside emitSessionStartSystemMessage.
|
|
578
693
|
if (adapter === "claude-code")
|
|
579
694
|
journalJanitor(coordRoot);
|
|
580
|
-
|
|
581
|
-
// agnostic, cheap (one readdir), fail-soft. Paired with journalJanitor as a
|
|
582
|
-
// session-start "tidy the coord layer" step.
|
|
583
|
-
try {
|
|
584
|
-
imageJanitor(coordRoot);
|
|
585
|
-
}
|
|
586
|
-
catch (err) {
|
|
587
|
-
logError(coordRoot, err, { phase: "session-start-image-janitor" });
|
|
588
|
-
}
|
|
695
|
+
imageJanitor(coordRoot);
|
|
589
696
|
let recovery = null;
|
|
590
697
|
if (payload?.source === "compact") {
|
|
591
698
|
try {
|
|
592
|
-
emit(coordRoot, {
|
|
593
|
-
event_type: "context.compaction.completed",
|
|
594
|
-
instance_id: owner.instance_id,
|
|
595
|
-
session_id: sessionId,
|
|
596
|
-
adapter,
|
|
597
|
-
data: { trigger: "auto" },
|
|
598
|
-
});
|
|
599
699
|
markContextCompactionCompleted(coordRoot, {
|
|
600
700
|
sessionId,
|
|
601
701
|
instanceId: owner.instance_id,
|
|
@@ -613,19 +713,16 @@ async function main() {
|
|
|
613
713
|
try {
|
|
614
714
|
const injected = await emitSessionStartSystemMessage(coordRoot, owner.instance_id, sessionId, data, adapter, recovery?.briefing ?? "");
|
|
615
715
|
if (injected && recovery) {
|
|
616
|
-
completeRecoveryInjection(coordRoot, owner.instance_id, sessionId,
|
|
716
|
+
completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
|
|
617
717
|
}
|
|
618
718
|
}
|
|
619
719
|
catch (err) {
|
|
620
720
|
logError(coordRoot, err, { phase: "session-start-systemMessage" });
|
|
621
721
|
}
|
|
622
|
-
//
|
|
623
|
-
//
|
|
624
|
-
// When a relay is configured, also make sure the live-socket daemon runs.
|
|
722
|
+
// Pull advisory cross-machine presence. V3 never publishes from the
|
|
723
|
+
// disposable heartbeat cache.
|
|
625
724
|
try {
|
|
626
|
-
publishPresence(coordRoot);
|
|
627
725
|
fetchPresence(coordRoot);
|
|
628
|
-
ensureRelayDaemon(coordRoot);
|
|
629
726
|
}
|
|
630
727
|
catch (err) {
|
|
631
728
|
logError(coordRoot, err, { phase: "session-start-presence" });
|
|
@@ -633,7 +730,7 @@ async function main() {
|
|
|
633
730
|
}
|
|
634
731
|
// Phase 8: SessionEnd cleanup: delete heartbeat + pid-map rows. Adapter-
|
|
635
732
|
// agnostic since v0.5.0.
|
|
636
|
-
if (norm.event_type === "session.
|
|
733
|
+
if (norm.event_type === "session.ended") {
|
|
637
734
|
try {
|
|
638
735
|
cleanupSessionEnd(coordRoot, owner.instance_id, data.reason ?? "unknown");
|
|
639
736
|
}
|
|
@@ -646,19 +743,11 @@ async function main() {
|
|
|
646
743
|
journalArchive(coordRoot, owner.instance_id);
|
|
647
744
|
runSessionSyncExtension(coordRoot, true);
|
|
648
745
|
}
|
|
649
|
-
// Presence: publish the post-cleanup state so peers see this session gone
|
|
650
|
-
// promptly rather than waiting out the remote-stale window.
|
|
651
|
-
try {
|
|
652
|
-
publishPresence(coordRoot);
|
|
653
|
-
}
|
|
654
|
-
catch (err) {
|
|
655
|
-
logError(coordRoot, err, { phase: "session-end-presence" });
|
|
656
|
-
}
|
|
657
746
|
}
|
|
658
747
|
// Phase 8: SubagentStart: sync-project to create the subagent heartbeat,
|
|
659
748
|
// log the lifecycle event, and emit a context message announcing the
|
|
660
749
|
// subagent (claude-code + cursor; codex doesn't fan out subagents today).
|
|
661
|
-
if (norm.event_type === "
|
|
750
|
+
if (norm.event_type === "agent.started") {
|
|
662
751
|
try {
|
|
663
752
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
664
753
|
if (existsSync(agentCoordBin)) {
|
|
@@ -681,7 +770,7 @@ async function main() {
|
|
|
681
770
|
}
|
|
682
771
|
}
|
|
683
772
|
// Phase 8: SubagentStop: delete subagent heartbeat + log.
|
|
684
|
-
if (norm.event_type === "
|
|
773
|
+
if (norm.event_type === "agent.completed") {
|
|
685
774
|
try {
|
|
686
775
|
cleanupSessionEnd(coordRoot, owner.instance_id, data.reason ?? "unknown");
|
|
687
776
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
@@ -700,7 +789,7 @@ async function main() {
|
|
|
700
789
|
}
|
|
701
790
|
// Phase 8: UserPromptSubmit: render dedup'd peer table + council pending
|
|
702
791
|
// and emit the adapter-shaped systemMessage JSON. Adapter-agnostic since v0.5.0.
|
|
703
|
-
if (norm.event_type === "
|
|
792
|
+
if (norm.event_type === "turn.started") {
|
|
704
793
|
// Effects (claude-code): reset per-turn sound rate-limit counters + run
|
|
705
794
|
// presence detection on the prompt.
|
|
706
795
|
if (adapter === "claude-code") {
|
|
@@ -735,74 +824,26 @@ async function main() {
|
|
|
735
824
|
try {
|
|
736
825
|
const injected = await emitUserPromptSubmitSystemMessage(coordRoot, owner.instance_id, sessionId, adapter, payload?.cwd, recovery?.briefing ?? "");
|
|
737
826
|
if (injected && recovery) {
|
|
738
|
-
completeRecoveryInjection(coordRoot, owner.instance_id, sessionId,
|
|
827
|
+
completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
|
|
739
828
|
}
|
|
740
829
|
}
|
|
741
830
|
catch (err) {
|
|
742
831
|
logError(coordRoot, err, { phase: "user-prompt-submit-systemMessage" });
|
|
743
832
|
}
|
|
744
833
|
}
|
|
745
|
-
// turn.
|
|
834
|
+
// turn.completed: telemetry, then the stop verdict. The
|
|
746
835
|
// verdict + codex-replay previously lived in the per-adapter shell adapters;
|
|
747
836
|
// agent-hook owns them now. Runs on the normal "stop" event only;
|
|
748
837
|
// "stop-failure" (API error) gets no gate, matching the previous
|
|
749
838
|
// stop vs stop-failure split.
|
|
750
|
-
if (norm.event_type === "turn.
|
|
751
|
-
//
|
|
752
|
-
// the status_checked / task_set / status_box_present evidence (codex
|
|
753
|
-
// doesn't emit those live; this re-emits turn.stop after agent-hook's own,
|
|
754
|
-
// so the verdict reads the replay's box signal as the latest).
|
|
755
|
-
if (adapter === "codex" && payload?.transcript_path && existsSync(payload.transcript_path)) {
|
|
756
|
-
try {
|
|
757
|
-
replayCodexJsonl({
|
|
758
|
-
coordRoot,
|
|
759
|
-
jsonlPath: payload.transcript_path,
|
|
760
|
-
sessionId,
|
|
761
|
-
instanceId: owner.instance_id,
|
|
762
|
-
lastAssistantMessage: payload.raw.last_assistant_message ?? "",
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
catch (err) {
|
|
766
|
-
logError(coordRoot, err, { phase: "codex-replay" });
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
// CC effects: rate-limited session-telemetry sync + turn-summary Haiku
|
|
770
|
-
// auto-summary.
|
|
839
|
+
if (norm.event_type === "turn.completed" && eventName === "stop") {
|
|
840
|
+
// Claude Code session telemetry sync remains an independent side effect.
|
|
771
841
|
if (adapter === "claude-code") {
|
|
772
842
|
runSessionSyncExtension(coordRoot, false);
|
|
773
|
-
runTurnSummary(coordRoot, owner.instance_id, sessionId, payload?.transcript_path);
|
|
774
|
-
}
|
|
775
|
-
// Master-state heartbeat projection. Drains events.ndjson since the last
|
|
776
|
-
// cursor → per-owner heartbeats. Was a SECOND binary (`agent-coord project`)
|
|
777
|
-
// pinned to Claude Code Stop only; folded in here so it (a) is one
|
|
778
|
-
// entry per event like everything else and (b) fires on EVERY adapter's stop,
|
|
779
|
-
// not just CC. Runs unconditionally before the verdict's possible exit-2 return
|
|
780
|
-
// (the events are real regardless of whether the agent gets nagged), and after
|
|
781
|
-
// codex-replay above so codex's replayed events are included in the drain.
|
|
782
|
-
// Not an emitter (consumes + writes heartbeats), so no emitter/consumer conflict.
|
|
783
|
-
try {
|
|
784
|
-
const result = consumeSince(coordRoot);
|
|
785
|
-
projectHeartbeats(coordRoot, result.events);
|
|
786
|
-
if (result.lastEventId)
|
|
787
|
-
writeCursor(coordRoot, result.lastEventId);
|
|
788
843
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
// Presence: publish AFTER the projection above so the blob carries this
|
|
793
|
-
// turn's fresh task/turn_summary/files. Change-batched + keepalive'd;
|
|
794
|
-
// the push itself runs detached. The relay daemon (when configured) is
|
|
795
|
-
// re-ensured here too — it self-exits on idle, and the next turn revives it.
|
|
796
|
-
try {
|
|
797
|
-
publishPresence(coordRoot);
|
|
798
|
-
ensureRelayDaemon(coordRoot);
|
|
799
|
-
}
|
|
800
|
-
catch (err) {
|
|
801
|
-
logError(coordRoot, err, { phase: "stop-presence" });
|
|
802
|
-
}
|
|
803
|
-
// Stop verdict (status-box + set-task gate). Direct in-process call: the
|
|
804
|
-
// rule lives in harnery. agent-hook already emitted this turn.stop (with
|
|
805
|
-
// status_box_present) above, so the evidence is in the stream.
|
|
844
|
+
// Stop verdict (V3 ritual + task/status gate). Direct in-process call: the
|
|
845
|
+
// rule lives in Harnery. agent-hook already emitted this turn.completed
|
|
846
|
+
// with privacy-safe ritual observations, so the evidence is in the stream.
|
|
806
847
|
const verdict = evaluateStopHook(coordRoot, {
|
|
807
848
|
rule: "stop-hook",
|
|
808
849
|
instance_id: owner.instance_id,
|
|
@@ -812,13 +853,26 @@ async function main() {
|
|
|
812
853
|
workflow_child: coordEnv("WORKFLOW_CHILD") === "1",
|
|
813
854
|
});
|
|
814
855
|
if (!verdict.allow) {
|
|
815
|
-
// Adapter-aware enforcement channel: Claude Code
|
|
816
|
-
//
|
|
817
|
-
//
|
|
818
|
-
// writes the right shape and returns the exit code to use.
|
|
856
|
+
// Adapter-aware enforcement channel: Claude Code honors exit-2 + stderr
|
|
857
|
+
// as a turn block; Cursor ignores exit codes and re-prompts only via a
|
|
858
|
+
// `followup_message` it auto-submits. Codex returned observe-only above.
|
|
859
|
+
// emitStopBlock writes the right shape and returns the exit code to use.
|
|
819
860
|
const { emitStopBlock } = await import("./adapter/output.js");
|
|
820
861
|
return emitStopBlock(adapter, verdict, coordRoot);
|
|
821
862
|
}
|
|
863
|
+
// An explicit end requested from inside this turn cannot be authoritative
|
|
864
|
+
// until the adapter has committed turn.completed above. Reconcile only
|
|
865
|
+
// when such a request exists; any later real work cancels it in the
|
|
866
|
+
// finalizer instead of being terminated underneath the agent.
|
|
867
|
+
try {
|
|
868
|
+
const { hasPendingExplicitSessionEndV3, reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.js");
|
|
869
|
+
if (hasPendingExplicitSessionEndV3(coordRoot)) {
|
|
870
|
+
reconcileSessionFinalizationV3(coordRoot);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
catch (err) {
|
|
874
|
+
logError(coordRoot, err, { phase: "stop-explicit-session-finalization" });
|
|
875
|
+
}
|
|
822
876
|
}
|
|
823
877
|
// Phase 7: PreToolUse: heartbeat + pid-map self-heal on every tool call.
|
|
824
878
|
// Adapter-agnostic: both writes have the same shape regardless of who fired.
|
|
@@ -829,40 +883,32 @@ async function main() {
|
|
|
829
883
|
// side-by-side in the previous pre-tool-use adapter. The Phase 4-6 refactor
|
|
830
884
|
// preserved the heartbeat half but dropped the pid-map half; the pid-map
|
|
831
885
|
// call was restored here afterward.
|
|
832
|
-
if (norm.event_type === "tool.
|
|
886
|
+
if (norm.event_type === "tool.requested") {
|
|
833
887
|
try {
|
|
834
888
|
// Recorded fork lineage, heal-path flavor. A forked CC conversation
|
|
835
|
-
// never fires its own session.
|
|
889
|
+
// never fires its own session.started (SessionStart fires under the
|
|
836
890
|
// PARENT's session id with source=resume, before the fork id is
|
|
837
891
|
// minted; verified 2026-08-05), so the fork's new instance first
|
|
838
892
|
// materializes right here. Gate detection on "no heartbeat yet" so the
|
|
839
893
|
// transcript scan runs once per instance lifetime, not per tool call.
|
|
840
|
-
let forkedFrom;
|
|
841
|
-
if (adapter === "claude-code" &&
|
|
842
|
-
payload?.transcript_path &&
|
|
843
|
-
!existsSync(join(coordRoot, ".harnery", "active", `${owner.instance_id}.json`))) {
|
|
844
|
-
forkedFrom = detectForkParent(payload.transcript_path, owner.instance_id);
|
|
845
|
-
}
|
|
846
|
-
healHeartbeatViaCli(coordRoot, owner.instance_id, sessionId, adapter, forkedFrom);
|
|
847
894
|
refreshPidmap(coordRoot, owner.instance_id, adapter, payload?.pid);
|
|
848
895
|
}
|
|
849
896
|
catch (err) {
|
|
850
897
|
logError(coordRoot, err, { phase: "pre-tool-use-heal" });
|
|
851
898
|
}
|
|
852
|
-
//
|
|
853
|
-
//
|
|
899
|
+
// The suggested name is a pending display latch. The set-task call itself
|
|
900
|
+
// runs before a name exists; every later tool waits until the exact block
|
|
901
|
+
// is the first assistant text after the mint result. Cursor supplies
|
|
902
|
+
// agent_message directly; Claude Code and Codex are resolved from their
|
|
903
|
+
// JSONL transcripts. Later commentary must not erase an already-correct
|
|
904
|
+
// display, and an unreadable transcript must not deadlock every tool.
|
|
854
905
|
try {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
payload,
|
|
859
|
-
instanceId: owner.instance_id,
|
|
860
|
-
sessionId,
|
|
861
|
-
adapter,
|
|
862
|
-
});
|
|
906
|
+
const displayAllowed = await enforcePendingSessionNameDisplay(coordRoot, owner.instance_id, adapter, payload);
|
|
907
|
+
if (!displayAllowed)
|
|
908
|
+
return 0;
|
|
863
909
|
}
|
|
864
910
|
catch (err) {
|
|
865
|
-
logError(coordRoot, err, { phase: "pre-tool-use-
|
|
911
|
+
logError(coordRoot, err, { phase: "pre-tool-use-session-name" });
|
|
866
912
|
}
|
|
867
913
|
// Windows-native Codex + WSL UNC only: block the one cross-shell shape
|
|
868
914
|
// proven to corrupt argument boundaries. Normal WSL argv calls, literal
|
|
@@ -875,13 +921,6 @@ async function main() {
|
|
|
875
921
|
toolInput: payload?.tool_input,
|
|
876
922
|
});
|
|
877
923
|
if (unsafeShellReason) {
|
|
878
|
-
emit(coordRoot, {
|
|
879
|
-
event_type: "decision.block",
|
|
880
|
-
instance_id: owner.instance_id,
|
|
881
|
-
session_id: sessionId,
|
|
882
|
-
adapter,
|
|
883
|
-
data: { rule: "unsafe_cross_shell", reason: unsafeShellReason },
|
|
884
|
-
});
|
|
885
924
|
const { emitDeny } = await import("./adapter/output.js");
|
|
886
925
|
emitDeny(adapter, unsafeShellReason);
|
|
887
926
|
return 0;
|
|
@@ -894,77 +933,36 @@ async function main() {
|
|
|
894
933
|
// apply_patch (codex) parses paths from the patch body and runs verdict
|
|
895
934
|
// per-path; Edit/Write/NotebookEdit resolve a single target. Non-write tools
|
|
896
935
|
// (incl. Agent) yield no targets and pass through with no deny.
|
|
936
|
+
let guardAllowed = true;
|
|
897
937
|
try {
|
|
898
|
-
await runPreToolUseGuard(coordRoot, owner.instance_id, sessionId, data, adapter);
|
|
938
|
+
guardAllowed = await runPreToolUseGuard(coordRoot, owner.instance_id, sessionId, data, adapter);
|
|
899
939
|
}
|
|
900
940
|
catch (err) {
|
|
901
941
|
logError(coordRoot, err, { phase: "pre-tool-use-guard" });
|
|
902
942
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
// the per-adapter shell adapters. Cursor sends the command at payload.command;
|
|
906
|
-
// codex Bash at tool_input.command. Emits a decision.warn per candidate-mutated
|
|
907
|
-
// path so a peer sees the write in events.ndjson. (CC never did this,
|
|
908
|
-
// preserved; it emits with its own hooks-side emitter per the
|
|
909
|
-
// independent-emitter rule.)
|
|
910
|
-
const shellCmd = eventName === "before-shell-execution"
|
|
911
|
-
? (payload?.raw.command ?? "")
|
|
912
|
-
: adapter === "codex" && data.tool_name === "Bash"
|
|
913
|
-
? (payload?.raw.tool_input?.command ?? "")
|
|
914
|
-
: "";
|
|
915
|
-
if (shellCmd) {
|
|
916
|
-
try {
|
|
917
|
-
const paths = shellMutationPaths(shellCmd, coordRoot);
|
|
918
|
-
const truncated = shellCmd.length > 80 ? shellCmd.slice(0, 80) : shellCmd;
|
|
919
|
-
const platform = adapterPlatform(adapter);
|
|
920
|
-
for (const p of paths) {
|
|
921
|
-
emit(coordRoot, {
|
|
922
|
-
event_type: "decision.warn",
|
|
923
|
-
instance_id: owner.instance_id,
|
|
924
|
-
session_id: sessionId,
|
|
925
|
-
adapter,
|
|
926
|
-
data: {
|
|
927
|
-
rule: "shell_mutation_candidate",
|
|
928
|
-
reason: `path=${p} cmd=${truncated} platform=${platform}`,
|
|
929
|
-
},
|
|
930
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
catch (err) {
|
|
935
|
-
logError(coordRoot, err, { phase: "shell-mutation-warn" });
|
|
936
|
-
}
|
|
937
|
-
}
|
|
943
|
+
if (!guardAllowed)
|
|
944
|
+
return 0;
|
|
938
945
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
946
|
+
if (norm.event_type === "tool.completed" && eventName === "post-tool-use") {
|
|
947
|
+
// Inject only at the successful tool boundary that actually minted a name:
|
|
948
|
+
// the first non-empty set-task or the transition to lifecycle done. The
|
|
949
|
+
// coordination row intentionally remains pending until transcript evidence
|
|
950
|
+
// catches up; reading that latch alone here would re-inject after every
|
|
951
|
+
// later tool and make the agent print the same block repeatedly.
|
|
942
952
|
try {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
// Image feed: a Bash command that wrote an image (harn browse, harn image,
|
|
949
|
-
// --diff, …) is the "agent produced this" signal. Scan the command + its
|
|
950
|
-
// output for freshly-written image paths and capture them.
|
|
951
|
-
try {
|
|
952
|
-
captureImages(coordRoot, {
|
|
953
|
-
eventType: "tool.post_use",
|
|
954
|
-
data,
|
|
955
|
-
payload,
|
|
956
|
-
instanceId: owner.instance_id,
|
|
957
|
-
sessionId,
|
|
958
|
-
adapter,
|
|
959
|
-
});
|
|
953
|
+
const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, owner.instance_id));
|
|
954
|
+
if (name && toolResponseMintedSessionName(payload?.tool_response, name)) {
|
|
955
|
+
const { emitContext } = await import("./adapter/output.js");
|
|
956
|
+
emitContext(adapter, "PostToolUse", sessionNameDisplayInstruction(name));
|
|
957
|
+
}
|
|
960
958
|
}
|
|
961
959
|
catch (err) {
|
|
962
|
-
logError(coordRoot, err, { phase: "post-tool-use-
|
|
960
|
+
logError(coordRoot, err, { phase: "post-tool-use-session-name" });
|
|
963
961
|
}
|
|
964
962
|
}
|
|
965
963
|
// Phase 7: PostToolUseFailure: release claim on failed Edit (the file
|
|
966
964
|
// never landed; the claim is stale). Adapter-agnostic.
|
|
967
|
-
if (norm.event_type === "tool.
|
|
965
|
+
if (norm.event_type === "tool.completed" && eventName === "post-tool-use-failure") {
|
|
968
966
|
try {
|
|
969
967
|
releaseClaimOnFailure(coordRoot, owner.instance_id, data, payload?.raw);
|
|
970
968
|
}
|
|
@@ -974,11 +972,38 @@ async function main() {
|
|
|
974
972
|
}
|
|
975
973
|
return 0;
|
|
976
974
|
}
|
|
975
|
+
async function enforcePendingSessionNameDisplay(coordRoot, instanceId, adapter, payload) {
|
|
976
|
+
const name = sessionNameDisplayPending(readLiveCoordinationRow(coordRoot, instanceId));
|
|
977
|
+
if (!name)
|
|
978
|
+
return true;
|
|
979
|
+
const command = extractBashCommand(payload?.tool_name, payload?.tool_input);
|
|
980
|
+
if (isSessionNameRemediationCommand(command, resolveBinName(coordRoot)))
|
|
981
|
+
return true;
|
|
982
|
+
const inspection = payload?.agent_message !== undefined
|
|
983
|
+
? {
|
|
984
|
+
state: assistantTextStartsWithSessionNameBlock(payload.agent_message, name)
|
|
985
|
+
? "present"
|
|
986
|
+
: "absent",
|
|
987
|
+
}
|
|
988
|
+
: inspectSessionNameDisplayImmediately(payload?.transcript_path, name, assistantTextStartsWithSessionNameBlock);
|
|
989
|
+
if (inspection.state === "present") {
|
|
990
|
+
stampSessionNameSeen(coordRoot, instanceId, name);
|
|
991
|
+
return true;
|
|
992
|
+
}
|
|
993
|
+
if (inspection.state === "unavailable") {
|
|
994
|
+
const { emitContext } = await import("./adapter/output.js");
|
|
995
|
+
emitContext(adapter, "PreToolUse", "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.");
|
|
996
|
+
return true;
|
|
997
|
+
}
|
|
998
|
+
const { emitDeny } = await import("./adapter/output.js");
|
|
999
|
+
emitDeny(adapter, sessionNameDisplayInstruction(name));
|
|
1000
|
+
return false;
|
|
1001
|
+
}
|
|
977
1002
|
async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapter) {
|
|
978
1003
|
const toolName = data.tool_name ?? "";
|
|
979
1004
|
const rawTargets = collectGuardTargets(toolName, data);
|
|
980
1005
|
if (rawTargets.length === 0)
|
|
981
|
-
return;
|
|
1006
|
+
return true;
|
|
982
1007
|
let targets;
|
|
983
1008
|
if (agentsRequireGitFinalization(coordRoot)) {
|
|
984
1009
|
const decisions = rawTargets.map((path) => classifyWriteClaimFinalization(coordRoot, path));
|
|
@@ -986,7 +1011,7 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
|
|
|
986
1011
|
if (denied && !denied.allow) {
|
|
987
1012
|
const { emitDeny } = await import("./adapter/output.js");
|
|
988
1013
|
emitDeny(adapter, formatWriteClaimFinalizationDenial(denied, resolveBinName(coordRoot)));
|
|
989
|
-
return;
|
|
1014
|
+
return false;
|
|
990
1015
|
}
|
|
991
1016
|
const allowed = decisions.filter((decision) => decision.allow);
|
|
992
1017
|
targets = allowed.map((decision) => ({
|
|
@@ -1001,14 +1026,15 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
|
|
|
1001
1026
|
.map((path) => ({ path }));
|
|
1002
1027
|
}
|
|
1003
1028
|
if (targets.length === 0)
|
|
1004
|
-
return;
|
|
1029
|
+
return true;
|
|
1005
1030
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1006
1031
|
if (!existsSync(agentCoordBin)) {
|
|
1007
1032
|
if (agentsRequireGitFinalization(coordRoot)) {
|
|
1008
1033
|
const { emitDeny } = await import("./adapter/output.js");
|
|
1009
1034
|
emitDeny(adapter, "Harnery denied the write before mutation because the claim coordinator is unavailable; repair the project's Harnery installation and retry.");
|
|
1035
|
+
return false;
|
|
1010
1036
|
}
|
|
1011
|
-
return;
|
|
1037
|
+
return true;
|
|
1012
1038
|
}
|
|
1013
1039
|
// For apply_patch (multi-file), collect siblings so the deny reason names
|
|
1014
1040
|
// them. For single-file tools the array has one entry.
|
|
@@ -1048,26 +1074,10 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
|
|
|
1048
1074
|
}
|
|
1049
1075
|
const { emitDeny } = await import("./adapter/output.js");
|
|
1050
1076
|
emitDeny(adapter, reason);
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
// The verdict mutates the live heartbeat immediately, but that file is a
|
|
1054
|
-
// projection and can be rebuilt at any later hook. Persist the acquisition
|
|
1055
|
-
// in the canonical stream too. This matters most for Codex apply_patch:
|
|
1056
|
-
// one tool call can claim several files, and tool.pre_use's clamped payload
|
|
1057
|
-
// is not a durable ownership representation for those targets.
|
|
1058
|
-
emit(coordRoot, {
|
|
1059
|
-
event_type: "claim.acquire",
|
|
1060
|
-
instance_id: instanceId,
|
|
1061
|
-
session_id: sessionId,
|
|
1062
|
-
adapter,
|
|
1063
|
-
source: "agent-hooks",
|
|
1064
|
-
data: {
|
|
1065
|
-
path: target.path,
|
|
1066
|
-
mode: "write",
|
|
1067
|
-
...(target.finalization ? { finalization: target.finalization } : {}),
|
|
1068
|
-
},
|
|
1069
|
-
});
|
|
1077
|
+
return false;
|
|
1078
|
+
}
|
|
1070
1079
|
}
|
|
1080
|
+
return true;
|
|
1071
1081
|
}
|
|
1072
1082
|
/** Canonicalize a path to monorepo-relative form. Absolute paths under
|
|
1073
1083
|
* coordRoot get the prefix stripped; relative paths pass through (assumed
|
|
@@ -1125,28 +1135,10 @@ function parseApplyPatchPaths(data) {
|
|
|
1125
1135
|
}
|
|
1126
1136
|
return out;
|
|
1127
1137
|
}
|
|
1128
|
-
function healHeartbeatViaCli(coordRoot, instanceId, sessionId, adapter, forkedFrom) {
|
|
1129
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1130
|
-
if (!existsSync(agentCoordBin))
|
|
1131
|
-
return;
|
|
1132
|
-
// Pass the detected adapter so a pruned Cursor/Codex heartbeat is recreated
|
|
1133
|
-
// with the correct platform; without it, healHeartbeat defaults to
|
|
1134
|
-
// claude-code and the dashboard mislabels the agent. See
|
|
1135
|
-
// heartbeat-writer.healHeartbeat. --forked-from carries detected fork
|
|
1136
|
-
// lineage for an instance that never session.started (the CC fork flow).
|
|
1137
|
-
const args = ["heal-heartbeat", instanceId, sessionId, `--adapter=${adapter}`];
|
|
1138
|
-
if (forkedFrom)
|
|
1139
|
-
args.push(`--forked-from=${forkedFrom}`);
|
|
1140
|
-
spawnSync(agentCoordBin, args, {
|
|
1141
|
-
encoding: "utf8",
|
|
1142
|
-
timeout: 2000,
|
|
1143
|
-
env: childEnv(coordRoot),
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
1138
|
/**
|
|
1147
1139
|
* Walk up the ppid chain on Linux/WSL looking for the adapter anchor PID,
|
|
1148
1140
|
* the PID of the claude / cursor / codex binary. Finds the agent PID. Used by
|
|
1149
|
-
* `tool.
|
|
1141
|
+
* `tool.requested` pid-map self-heal so a re-parented adapter binary (the
|
|
1150
1142
|
* VS Code 2.1.x sibling-claude spawn case) gets its pid-map row rewritten on
|
|
1151
1143
|
* the next tool call rather than going invisible until SessionStart fires
|
|
1152
1144
|
* again, which it may never do.
|
|
@@ -1219,9 +1211,7 @@ function findAdapterAnchorPid(adapter) {
|
|
|
1219
1211
|
return selectAnchorPid(chain, adapter);
|
|
1220
1212
|
}
|
|
1221
1213
|
/**
|
|
1222
|
-
* Pid-map self-heal for `tool.
|
|
1223
|
-
* `healHeartbeatViaCli`; the two were paired before the Phase 6 refactor split
|
|
1224
|
-
* them apart, then restored together afterward.
|
|
1214
|
+
* Pid-map self-heal for `tool.requested`.
|
|
1225
1215
|
*
|
|
1226
1216
|
* The pid argument prefers the payload's `pid` (CC populates it on
|
|
1227
1217
|
* SessionStart and may also send it on PreToolUse), then
|
|
@@ -1239,38 +1229,6 @@ function refreshPidmap(coordRoot, instanceId, adapter, payloadPid) {
|
|
|
1239
1229
|
return;
|
|
1240
1230
|
writePidmapViaAgentCoord(coordRoot, pid, instanceId, adapterPlatform(adapter));
|
|
1241
1231
|
}
|
|
1242
|
-
function stampToolActivity(coordRoot, instanceId, data) {
|
|
1243
|
-
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1244
|
-
if (!existsSync(agentCoordBin))
|
|
1245
|
-
return;
|
|
1246
|
-
const toolName = data.tool_name ?? "";
|
|
1247
|
-
// Extract a 1-line target from the tool_input blob (file path / command head).
|
|
1248
|
-
const toolInputRaw = data.tool_input;
|
|
1249
|
-
let target = "";
|
|
1250
|
-
if (typeof toolInputRaw === "string") {
|
|
1251
|
-
try {
|
|
1252
|
-
const parsed = JSON.parse(toolInputRaw);
|
|
1253
|
-
target =
|
|
1254
|
-
parsed.file_path ??
|
|
1255
|
-
parsed.path ??
|
|
1256
|
-
parsed.notebook_path ??
|
|
1257
|
-
parsed.command ??
|
|
1258
|
-
parsed.url ??
|
|
1259
|
-
parsed.pattern ??
|
|
1260
|
-
"";
|
|
1261
|
-
}
|
|
1262
|
-
catch {
|
|
1263
|
-
/* skip */
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
if (target.length > 200)
|
|
1267
|
-
target = target.slice(0, 200);
|
|
1268
|
-
spawnSync(agentCoordBin, ["stamp-tool-activity", instanceId, toolName, target], {
|
|
1269
|
-
encoding: "utf8",
|
|
1270
|
-
timeout: 2000,
|
|
1271
|
-
env: childEnv(coordRoot),
|
|
1272
|
-
});
|
|
1273
|
-
}
|
|
1274
1232
|
function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
|
|
1275
1233
|
const toolName = data.tool_name ?? "";
|
|
1276
1234
|
if (toolName !== "Edit" && toolName !== "Write" && toolName !== "NotebookEdit")
|
|
@@ -1310,6 +1268,12 @@ function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
|
|
|
1310
1268
|
? filePath.slice(coordRoot.length + 1)
|
|
1311
1269
|
: filePath;
|
|
1312
1270
|
}
|
|
1271
|
+
// In V3 the release is an authority event, not a heartbeat mutation. Avoid
|
|
1272
|
+
// creating a disposable cache for a failure that never acquired a claim,
|
|
1273
|
+
// but release the exact path when the validated projection says it is held.
|
|
1274
|
+
const row = readLiveCoordinationRow(coordRoot, instanceId);
|
|
1275
|
+
if (!row?.files_touched?.includes(canonical))
|
|
1276
|
+
return;
|
|
1313
1277
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1314
1278
|
if (!existsSync(agentCoordBin))
|
|
1315
1279
|
return;
|
|
@@ -1320,16 +1284,6 @@ function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
|
|
|
1320
1284
|
});
|
|
1321
1285
|
}
|
|
1322
1286
|
function cleanupSessionEnd(coordRoot, instanceId, reason) {
|
|
1323
|
-
// Remove heartbeat from the canonical .harnery/active/ dir.
|
|
1324
|
-
const path = join(coordRoot, ".harnery", "active", `${instanceId}.json`);
|
|
1325
|
-
try {
|
|
1326
|
-
if (existsSync(path)) {
|
|
1327
|
-
require("node:fs").unlinkSync(path);
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
catch {
|
|
1331
|
-
/* swallow */
|
|
1332
|
-
}
|
|
1333
1287
|
// Sweep pid-map entries pointing to this instance
|
|
1334
1288
|
const pidmapDir = join(coordRoot, ".harnery", "pid-map");
|
|
1335
1289
|
if (existsSync(pidmapDir)) {
|
|
@@ -1363,32 +1317,22 @@ async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionI
|
|
|
1363
1317
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1364
1318
|
let additionalContext = "";
|
|
1365
1319
|
if (existsSync(agentCoordBin)) {
|
|
1366
|
-
// Look up the agent's name from its heartbeat (for council pending rendering).
|
|
1367
|
-
let agentName = "";
|
|
1368
|
-
try {
|
|
1369
|
-
const fs = require("node:fs");
|
|
1370
|
-
const hbPath = join(coordRoot, ".harnery", "active", `${instanceId}.json`);
|
|
1371
|
-
if (fs.existsSync(hbPath)) {
|
|
1372
|
-
const hb = JSON.parse(fs.readFileSync(hbPath, "utf8"));
|
|
1373
|
-
agentName = hb.name ?? "";
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
catch {
|
|
1377
|
-
/* fall through with empty name; peer table still renders */
|
|
1378
|
-
}
|
|
1379
1320
|
const args = ["prompt-context", "--instance", instanceId, "--session", sessionId];
|
|
1380
|
-
if (agentName)
|
|
1381
|
-
args.push("--name", agentName);
|
|
1382
1321
|
// Every human-facing adapter gets the first-session naming reminder from
|
|
1383
1322
|
// the shared hook path. Cursor + Codex additionally get ongoing set-task
|
|
1384
1323
|
// staleness nudges; Claude Code enforces those via its Stop transcript.
|
|
1385
1324
|
// Codex gets a fresh status-footer reminder on every prompt. Its Stop hook
|
|
1386
1325
|
// stays observe-only, so missing the footer never replaces the answer.
|
|
1326
|
+
// Claude Code + Cursor (the Stop-ENFORCING adapters) get a fresh per-turn
|
|
1327
|
+
// ritual reminder: satisfying the contract up front is far cheaper than
|
|
1328
|
+
// the bounce-and-retry the Stop hook otherwise forces.
|
|
1387
1329
|
args.push("--session-name-nudge");
|
|
1388
1330
|
if (adapter === "cursor" || adapter === "codex")
|
|
1389
1331
|
args.push("--task-nudge");
|
|
1390
1332
|
if (adapter === "codex")
|
|
1391
1333
|
args.push("--status-footer-nudge");
|
|
1334
|
+
if (adapter === "claude-code" || adapter === "cursor")
|
|
1335
|
+
args.push("--turn-ritual-nudge", adapter);
|
|
1392
1336
|
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1393
1337
|
if (result.status === 0 && result.stdout)
|
|
1394
1338
|
additionalContext = result.stdout.trim();
|
|
@@ -1410,7 +1354,7 @@ async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionI
|
|
|
1410
1354
|
}
|
|
1411
1355
|
function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapter) {
|
|
1412
1356
|
// Look up the subagent's assigned name (just-written by agent-coord assignName
|
|
1413
|
-
// in session.
|
|
1357
|
+
// in session.started data) + the parent's short id for the "you are a subagent
|
|
1414
1358
|
// of X" framing.
|
|
1415
1359
|
const subagentName = data.name ?? "";
|
|
1416
1360
|
if (!subagentName)
|
|
@@ -1442,13 +1386,19 @@ function adapterPlatform(adapter) {
|
|
|
1442
1386
|
}
|
|
1443
1387
|
async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, emittedData, adapter, recoveryBriefing = "") {
|
|
1444
1388
|
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1389
|
+
const workflowChild = coordEnv("WORKFLOW_CHILD") === "1";
|
|
1445
1390
|
let additionalContext = "";
|
|
1446
1391
|
if (existsSync(agentCoordBin)) {
|
|
1447
1392
|
// Sync-project so the heartbeat exists for downstream readers (peer table,
|
|
1448
1393
|
// wiring check, council invites).
|
|
1449
1394
|
spawnSync(agentCoordBin, ["project"], { encoding: "utf8", timeout: 3000 });
|
|
1450
|
-
//
|
|
1451
|
-
|
|
1395
|
+
// Opportunistic reconciliation makes normal session starts a failsafe for
|
|
1396
|
+
// archive, idle, cascade, and host lifecycle observations.
|
|
1397
|
+
spawnSync(agentCoordBin, ["reconcile-finalization"], {
|
|
1398
|
+
encoding: "utf8",
|
|
1399
|
+
timeout: 5000,
|
|
1400
|
+
env: { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot },
|
|
1401
|
+
});
|
|
1452
1402
|
// SESSION_START activity log line, fired across all adapters.
|
|
1453
1403
|
const model = emittedData.model ?? "unknown";
|
|
1454
1404
|
const source = emittedData.source ?? "startup";
|
|
@@ -1459,34 +1409,39 @@ async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, e
|
|
|
1459
1409
|
"--instance",
|
|
1460
1410
|
instanceId,
|
|
1461
1411
|
], { encoding: "utf8", timeout: 2000 });
|
|
1462
|
-
//
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1412
|
+
// Workflow children retain lifecycle/event capture but do not receive
|
|
1413
|
+
// operator-facing peer, council, or init-remediation context. Injecting
|
|
1414
|
+
// that context can make a bounded child follow housekeeping instructions
|
|
1415
|
+
// instead of its assigned prompt.
|
|
1416
|
+
if (!workflowChild) {
|
|
1417
|
+
const agentName = emittedData.name ?? "";
|
|
1418
|
+
const args = ["session-context", "--instance", instanceId, "--session", sessionId];
|
|
1419
|
+
if (agentName)
|
|
1420
|
+
args.push("--name", agentName);
|
|
1421
|
+
// The "You are agent-X." prefix in session-context renders unqualified by
|
|
1422
|
+
// default (claude-code-style). For cursor/codex the bash dispatchers add
|
|
1423
|
+
// a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
|
|
1424
|
+
if (adapter !== "claude-code") {
|
|
1425
|
+
args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
|
|
1426
|
+
}
|
|
1427
|
+
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1428
|
+
if (result.status === 0 && result.stdout)
|
|
1429
|
+
additionalContext = result.stdout.trim();
|
|
1472
1430
|
}
|
|
1473
|
-
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1474
|
-
if (result.status === 0 && result.stdout)
|
|
1475
|
-
additionalContext = result.stdout.trim();
|
|
1476
1431
|
}
|
|
1477
1432
|
// Effect (claude-code): merge the journal recovery cue into the session-start
|
|
1478
1433
|
// context. Was a standalone additionalContext emission from the previous
|
|
1479
1434
|
// journal-on-start adapter; now that agent-hook is the single SessionStart
|
|
1480
1435
|
// entry, it folds in here.
|
|
1481
|
-
if (adapter === "claude-code") {
|
|
1436
|
+
if (adapter === "claude-code" && !workflowChild) {
|
|
1482
1437
|
const cue = journalRecoveryCue(coordRoot);
|
|
1483
1438
|
if (cue)
|
|
1484
1439
|
additionalContext = [additionalContext, cue].filter(Boolean).join("\n\n");
|
|
1485
1440
|
}
|
|
1486
|
-
if (recoveryBriefing) {
|
|
1441
|
+
if (recoveryBriefing && !workflowChild) {
|
|
1487
1442
|
additionalContext = [additionalContext, recoveryBriefing].filter(Boolean).join("\n\n");
|
|
1488
1443
|
}
|
|
1489
|
-
if (adapter === "codex" && isWslUncPath(emittedData.cwd)) {
|
|
1444
|
+
if (adapter === "codex" && !workflowChild && isWslUncPath(emittedData.cwd)) {
|
|
1490
1445
|
const fileLinkContext = renderCodexWslFileLinkContext(coordRoot, emittedData.cwd);
|
|
1491
1446
|
if (fileLinkContext) {
|
|
1492
1447
|
additionalContext = [additionalContext, fileLinkContext].filter(Boolean).join("\n\n");
|
|
@@ -1507,19 +1462,9 @@ async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, e
|
|
|
1507
1462
|
emitContext(adapter, "SessionStart", additionalContext);
|
|
1508
1463
|
return true;
|
|
1509
1464
|
}
|
|
1510
|
-
function completeRecoveryInjection(coordRoot, instanceId, sessionId,
|
|
1465
|
+
function completeRecoveryInjection(coordRoot, instanceId, sessionId, recovery) {
|
|
1511
1466
|
completeContextRecovery(coordRoot, { sessionId, instanceId });
|
|
1512
|
-
|
|
1513
|
-
event_type: "context.recovery.injected",
|
|
1514
|
-
instance_id: instanceId,
|
|
1515
|
-
session_id: sessionId,
|
|
1516
|
-
adapter,
|
|
1517
|
-
data: {
|
|
1518
|
-
capsule_id: recovery.capsule.capsule_id,
|
|
1519
|
-
generation: recovery.capsule.generation,
|
|
1520
|
-
injection_event: injectionEvent,
|
|
1521
|
-
},
|
|
1522
|
-
});
|
|
1467
|
+
void recovery;
|
|
1523
1468
|
}
|
|
1524
1469
|
main()
|
|
1525
1470
|
.then((code) => process.exit(code))
|