harnery 0.30.0 → 0.31.1
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 +4 -4
- package/dist/commander.d.ts +8 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +14 -13
- package/dist/commands/adapter.d.ts +12 -0
- package/dist/commands/adapter.d.ts.map +1 -0
- package/dist/commands/{harness.js → adapter.js} +35 -35
- package/dist/commands/agents.d.ts +3 -2
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +185 -74
- package/dist/commands/artifacts.d.ts +4 -0
- package/dist/commands/artifacts.d.ts.map +1 -0
- package/dist/commands/artifacts.js +143 -0
- package/dist/commands/backup.d.ts +1 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +5 -2
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +310 -7
- package/dist/commands/checkpoint.d.ts +12 -0
- package/dist/commands/checkpoint.d.ts.map +1 -0
- package/dist/commands/checkpoint.js +162 -0
- package/dist/commands/context.d.ts +2 -2
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +3 -151
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +31 -7
- package/dist/commands/deinit.d.ts +2 -2
- package/dist/commands/deinit.js +15 -15
- package/dist/commands/doctor.js +23 -23
- package/dist/commands/governor.d.ts +4 -0
- package/dist/commands/governor.d.ts.map +1 -0
- package/dist/commands/{supervisor.js → governor.js} +133 -108
- package/dist/commands/grep.d.ts +1 -1
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +19 -2
- package/dist/commands/init.d.ts +12 -12
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +44 -30
- package/dist/commands/{session.d.ts → journal.d.ts} +2 -2
- package/dist/commands/journal.d.ts.map +1 -0
- package/dist/commands/{scratch.js → journal.js} +47 -47
- package/dist/commands/policy.js +1 -1
- package/dist/commands/presence.js +2 -2
- package/dist/commands/sync.d.ts +1 -1
- package/dist/commands/sync.js +3 -3
- package/dist/commands/tunnel.d.ts +16 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +213 -27
- package/dist/commands/web-fetch.js +1 -1
- package/dist/commands/web.d.ts +19 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +51 -0
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +38 -22
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +35 -28
- package/dist/core/{harnesses → adapters}/attest-projection.d.ts +2 -2
- package/dist/core/adapters/attest-projection.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attest.d.ts +16 -16
- package/dist/core/adapters/attest.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attest.js +9 -9
- package/dist/core/{harnesses → adapters}/attestation.d.ts +16 -16
- package/dist/core/adapters/attestation.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attestation.js +20 -20
- package/dist/core/{harnesses → adapters}/bench.d.ts +12 -12
- package/dist/core/adapters/bench.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/bench.js +12 -12
- package/dist/core/adapters/index.d.ts +13 -0
- package/dist/core/adapters/index.d.ts.map +1 -0
- package/dist/core/adapters/index.js +6 -0
- package/dist/core/{harnesses → adapters}/profiles.d.ts +11 -11
- package/dist/core/adapters/profiles.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/profiles.js +11 -11
- package/dist/core/adapters/registry.d.ts +15 -0
- package/dist/core/adapters/registry.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/registry.js +11 -11
- package/dist/core/{harnesses → adapters}/types.d.ts +23 -23
- package/dist/core/adapters/types.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/types.js +1 -1
- package/dist/core/agents/canonical-emit.d.ts +9 -14
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +15 -44
- package/dist/core/agents/cli-emit.js +1 -1
- package/dist/core/agents/cli.js +30 -26
- package/dist/core/agents/codex-replay.d.ts +1 -1
- package/dist/core/agents/codex-replay.js +6 -6
- package/dist/core/agents/coord-bin.d.ts +33 -0
- package/dist/core/agents/coord-bin.d.ts.map +1 -0
- package/dist/core/agents/coord-bin.js +89 -0
- package/dist/core/agents/coord-client.d.ts +78 -8
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +303 -75
- package/dist/core/agents/events/consume.d.ts +1 -1
- package/dist/core/agents/events/emit.d.ts +5 -6
- package/dist/core/agents/events/emit.d.ts.map +1 -1
- package/dist/core/agents/events/emit.js +2 -2
- package/dist/core/agents/paths.d.ts +5 -2
- package/dist/core/agents/paths.d.ts.map +1 -1
- package/dist/core/agents/paths.js +7 -13
- package/dist/core/agents/render/prompt-context.d.ts +21 -9
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +82 -30
- package/dist/core/agents/render/session-context.d.ts +1 -1
- package/dist/core/agents/render/session-context.js +5 -5
- package/dist/core/agents/rules/claim-conflict.d.ts +2 -6
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +1 -1
- package/dist/core/agents/rules/commit-conflict.js +98 -22
- package/dist/core/agents/rules/stop-hook.d.ts +9 -9
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +23 -5
- package/dist/core/agents/rules/verdict.d.ts +14 -0
- package/dist/core/agents/rules/verdict.d.ts.map +1 -0
- package/dist/core/agents/rules/verdict.js +1 -0
- package/dist/core/agents/session-events.js +3 -3
- package/dist/core/agents/state/council.d.ts +1 -1
- package/dist/core/agents/state/council.js +1 -1
- package/dist/core/agents/state/heartbeat-projector.d.ts +3 -2
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +14 -10
- package/dist/core/agents/state/heartbeat-writer.d.ts +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +12 -12
- package/dist/core/agents/state/{scratch.d.ts → journal.d.ts} +7 -7
- package/dist/core/agents/state/{scratch.d.ts.map → journal.d.ts.map} +1 -1
- package/dist/core/agents/state/{scratch.js → journal.js} +12 -12
- package/dist/core/agents/state/pidmap.d.ts +28 -4
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +130 -15
- package/dist/core/agents/state/proc-start.d.ts +95 -0
- package/dist/core/agents/state/proc-start.d.ts.map +1 -0
- package/dist/core/agents/state/proc-start.js +199 -0
- package/dist/core/agents/state/shell-mutation.d.ts +1 -1
- package/dist/core/agents/state/shell-mutation.js +1 -1
- package/dist/core/agents/state/stale-sweep.js +7 -7
- package/dist/core/artifacts/index.d.ts +86 -0
- package/dist/core/artifacts/index.d.ts.map +1 -0
- package/dist/core/artifacts/index.js +421 -0
- package/dist/core/config.d.ts +9 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +26 -2
- package/dist/core/context/index.d.ts +12 -12
- package/dist/core/context/index.js +10 -10
- package/dist/core/governor/adapter-spread.d.ts +52 -0
- package/dist/core/governor/adapter-spread.d.ts.map +1 -0
- package/dist/core/governor/adapter-spread.js +52 -0
- package/dist/core/governor/index.d.ts +7 -0
- package/dist/core/governor/index.d.ts.map +1 -0
- package/dist/core/governor/index.js +6 -0
- package/dist/core/governor/plan-api.d.ts +38 -0
- package/dist/core/governor/plan-api.d.ts.map +1 -0
- package/dist/core/governor/plan-api.js +83 -0
- package/dist/core/governor/plan-read.d.ts +5 -0
- package/dist/core/governor/plan-read.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/plan-read.js +66 -52
- package/dist/core/{supervisor → governor}/plan-types.d.ts +57 -56
- package/dist/core/governor/plan-types.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/plan-types.js +4 -4
- package/dist/core/governor/planning.d.ts +48 -0
- package/dist/core/governor/planning.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/planning.js +125 -94
- package/dist/core/governor/read.d.ts +6 -0
- package/dist/core/governor/read.d.ts.map +1 -0
- package/dist/core/governor/read.js +4 -0
- package/dist/core/governor/runner.d.ts +37 -0
- package/dist/core/governor/runner.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/runner.js +15 -15
- package/dist/core/governor/service-read.d.ts +6 -0
- package/dist/core/governor/service-read.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/service-read.js +12 -12
- package/dist/core/governor/service.d.ts +114 -0
- package/dist/core/governor/service.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/service.js +57 -57
- package/dist/core/governor/state.d.ts +116 -0
- package/dist/core/governor/state.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/state.js +228 -108
- package/dist/core/hooks/adapter/detect.d.ts +9 -0
- package/dist/core/hooks/{harness → adapter}/detect.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/detect.js +8 -8
- package/dist/core/hooks/{harness → adapter}/events.d.ts +15 -14
- package/dist/core/hooks/adapter/events.d.ts.map +1 -0
- package/dist/core/hooks/{harness → adapter}/events.js +5 -5
- package/dist/core/hooks/{harness → adapter}/output.d.ts +20 -14
- package/dist/core/hooks/adapter/output.d.ts.map +1 -0
- package/dist/core/hooks/{harness → adapter}/output.js +30 -22
- package/dist/core/hooks/{harness → adapter}/parse.d.ts +5 -5
- package/dist/core/hooks/{harness → adapter}/parse.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/parse.js +4 -4
- package/dist/core/hooks/{harness → adapter}/wiring.d.ts +15 -15
- package/dist/core/hooks/{harness → adapter}/wiring.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/wiring.js +13 -13
- package/dist/core/hooks/cli.d.ts +3 -3
- package/dist/core/hooks/cli.js +187 -162
- package/dist/core/hooks/effects/image-capture.d.ts +5 -5
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +3 -3
- package/dist/core/hooks/effects/index.d.ts +12 -12
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +19 -19
- package/dist/core/hooks/events/emit.d.ts +2 -2
- package/dist/core/hooks/events/emit.d.ts.map +1 -1
- package/dist/core/hooks/events/emit.js +1 -1
- package/dist/core/hooks/events/rotate.js +1 -1
- package/dist/core/hooks/events/schema.d.ts +13 -5
- package/dist/core/hooks/events/schema.d.ts.map +1 -1
- package/dist/core/hooks/guard-path.d.ts +2 -2
- package/dist/core/hooks/guard-path.js +2 -2
- package/dist/core/hooks/index.d.ts +1 -1
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/resolve/anchor.d.ts +14 -5
- package/dist/core/hooks/resolve/anchor.d.ts.map +1 -1
- package/dist/core/hooks/resolve/anchor.js +64 -7
- package/dist/core/hooks/resolve/coord-root.d.ts +16 -11
- package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -1
- package/dist/core/hooks/resolve/coord-root.js +18 -43
- package/dist/core/hooks/resolve/intent.js +1 -1
- package/dist/core/hooks/resolve/owner.d.ts +1 -1
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +11 -5
- package/dist/core/{scratch → journal}/index.d.ts +25 -25
- package/dist/core/{scratch → journal}/index.d.ts.map +1 -1
- package/dist/core/{scratch → journal}/index.js +38 -38
- package/dist/core/policy/evaluate.d.ts +1 -1
- package/dist/core/policy/evaluate.d.ts.map +1 -1
- package/dist/core/policy/evaluate.js +7 -7
- package/dist/core/policy/types.d.ts +3 -3
- package/dist/core/policy/types.d.ts.map +1 -1
- package/dist/core/work/state.d.ts +12 -4
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +50 -17
- package/dist/core/workflow/adapters.d.ts +17 -0
- package/dist/core/workflow/adapters.d.ts.map +1 -0
- package/dist/core/workflow/adapters.js +21 -0
- package/dist/core/workflow/approvals.js +6 -6
- package/dist/core/workflow/billing.d.ts +9 -9
- package/dist/core/workflow/billing.js +9 -9
- package/dist/core/workflow/child-env.d.ts +10 -2
- package/dist/core/workflow/child-env.d.ts.map +1 -1
- package/dist/core/workflow/child-env.js +12 -4
- package/dist/core/workflow/engine.d.ts +15 -5
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +138 -83
- package/dist/core/workflow/evidence-preflight.d.ts +24 -0
- package/dist/core/workflow/evidence-preflight.d.ts.map +1 -0
- package/dist/core/workflow/evidence-preflight.js +134 -0
- package/dist/core/workflow/index.d.ts +1 -1
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/proof.d.ts +11 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +45 -36
- package/dist/core/workflow/run-state.d.ts +1 -1
- package/dist/core/workflow/run-state.js +7 -7
- package/dist/core/workflow/sandbox-projection.d.ts +6 -6
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +11 -11
- package/dist/core/workflow/spawn-claude.d.ts +3 -3
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +9 -6
- package/dist/core/workflow/spawn-codex.d.ts +4 -4
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +9 -6
- package/dist/core/workflow/spawn-cursor.d.ts +4 -4
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +9 -6
- package/dist/core/workflow/spawn-failure.d.ts +1 -1
- package/dist/core/workflow/spawn-failure.js +1 -1
- package/dist/core/workflow/specialists.js +2 -2
- package/dist/core/workflow/{journal.d.ts → transcript.d.ts} +8 -8
- package/dist/core/workflow/transcript.d.ts.map +1 -0
- package/dist/core/workflow/{journal.js → transcript.js} +14 -14
- package/dist/core/workflow/types.d.ts +98 -51
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/types.js +11 -1
- package/dist/core/workflow/validate.d.ts +1 -1
- package/dist/core/workflow/validate.js +1 -1
- package/dist/core/workflow/workspaces/cancellation.js +2 -2
- package/dist/core/workflow/workspaces/cleanup.js +2 -2
- package/dist/core/workflow/workspaces/execution.js +6 -6
- package/dist/core/workflow/workspaces/inspect.js +9 -9
- package/dist/core/workflow/workspaces/integration.js +14 -14
- package/dist/core/workflow/workspaces/lifecycle.d.ts +1 -1
- package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/lifecycle.js +9 -9
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +39 -5
- package/dist/core/workflow/workspaces/recovery.js +3 -3
- package/dist/core/workflow/workspaces/state.d.ts +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +3 -3
- package/dist/core/workflow/workspaces/types.d.ts +1 -1
- package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
- package/dist/lib/browser/asserts.d.ts +28 -0
- package/dist/lib/browser/asserts.d.ts.map +1 -0
- package/dist/lib/browser/asserts.js +152 -0
- package/dist/lib/browser/client.d.ts +49 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +155 -14
- package/dist/lib/browser/content-checks.d.ts +108 -0
- package/dist/lib/browser/content-checks.d.ts.map +1 -0
- package/dist/lib/browser/content-checks.js +474 -0
- package/dist/lib/browser/critique.d.ts +91 -0
- package/dist/lib/browser/critique.d.ts.map +1 -0
- package/dist/lib/browser/critique.js +167 -0
- package/dist/lib/browser/geometry.d.ts +32 -0
- package/dist/lib/browser/geometry.d.ts.map +1 -1
- package/dist/lib/browser/geometry.js +244 -1
- package/dist/lib/browser/index.d.ts +4 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -0
- package/dist/lib/claude-desktop.d.ts.map +1 -1
- package/dist/lib/claude-desktop.js +4 -1
- package/dist/lib/context/index.js +9 -2
- package/dist/lib/council/index.d.ts +6 -6
- package/dist/lib/council/index.d.ts.map +1 -1
- package/dist/lib/council/index.js +8 -8
- package/dist/lib/identities/assume.d.ts +2 -2
- package/dist/lib/identities/assume.js +7 -7
- package/dist/lib/instructions/apply.d.ts +3 -3
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +75 -27
- package/dist/lib/instructions/host-addendum.d.ts +54 -0
- package/dist/lib/instructions/host-addendum.d.ts.map +1 -0
- package/dist/lib/instructions/host-addendum.js +107 -0
- package/dist/lib/instructions/templates.d.ts +10 -3
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +41 -12
- package/dist/lib/machine.d.ts +2 -2
- package/dist/lib/machine.js +2 -2
- package/package.json +26 -21
- package/schemas/config.schema.json +26 -1
- package/src/commander.ts +22 -13
- package/src/commands/{harness.ts → adapter.ts} +46 -46
- package/src/commands/agents.ts +200 -78
- package/src/commands/artifacts.ts +168 -0
- package/src/commands/backup.ts +47 -33
- package/src/commands/browse.ts +438 -2
- package/src/commands/checkpoint.ts +196 -0
- package/src/commands/context.ts +3 -176
- package/src/commands/decision.ts +41 -7
- package/src/commands/deinit.ts +16 -16
- package/src/commands/doctor.ts +28 -28
- package/src/commands/{supervisor.ts → governor.ts} +184 -154
- package/src/commands/grep.ts +19 -2
- package/src/commands/init.ts +50 -34
- package/src/commands/{scratch.ts → journal.ts} +60 -60
- package/src/commands/policy.ts +1 -1
- package/src/commands/presence.ts +2 -2
- package/src/commands/sync.ts +3 -3
- package/src/commands/tunnel.ts +256 -38
- package/src/commands/web-fetch.ts +1 -1
- package/src/commands/web.ts +56 -1
- package/src/commands/work.ts +50 -35
- package/src/commands/workflow.ts +43 -34
- package/src/core/{harnesses → adapters}/attest-projection.ts +2 -2
- package/src/core/{harnesses → adapters}/attest.ts +27 -27
- package/src/core/{harnesses → adapters}/attestation.ts +41 -41
- package/src/core/{harnesses → adapters}/bench.ts +34 -43
- package/src/core/adapters/index.ts +56 -0
- package/src/core/{harnesses → adapters}/profiles.ts +16 -16
- package/src/core/{harnesses → adapters}/registry.ts +23 -23
- package/src/core/{harnesses → adapters}/types.ts +23 -23
- package/src/core/agents/canonical-emit.ts +16 -43
- package/src/core/agents/cli-emit.ts +1 -1
- package/src/core/agents/cli.ts +30 -28
- package/src/core/agents/codex-replay.ts +6 -6
- package/src/core/agents/coord-bin.ts +92 -0
- package/src/core/agents/coord-client.ts +302 -78
- package/src/core/agents/events/consume.ts +1 -1
- package/src/core/agents/events/emit.ts +7 -6
- package/src/core/agents/paths.ts +7 -11
- package/src/core/agents/render/prompt-context.ts +107 -31
- package/src/core/agents/render/session-context.ts +6 -6
- package/src/core/agents/rules/claim-conflict.ts +4 -7
- package/src/core/agents/rules/commit-conflict.ts +103 -22
- package/src/core/agents/rules/stop-hook.ts +33 -17
- package/src/core/agents/rules/verdict.ts +13 -0
- package/src/core/agents/session-events.ts +4 -4
- package/src/core/agents/state/council.ts +1 -1
- package/src/core/agents/state/heartbeat-projector.ts +16 -12
- package/src/core/agents/state/heartbeat-writer.ts +12 -12
- package/src/core/agents/state/{scratch.ts → journal.ts} +12 -12
- package/src/core/agents/state/pidmap.ts +130 -15
- package/src/core/agents/state/proc-start.ts +208 -0
- package/src/core/agents/state/shell-mutation.ts +1 -1
- package/src/core/agents/state/stale-sweep.ts +7 -7
- package/src/core/artifacts/index.ts +557 -0
- package/src/core/config.ts +30 -2
- package/src/core/context/index.ts +15 -15
- package/src/core/governor/adapter-spread.ts +82 -0
- package/src/core/governor/index.ts +91 -0
- package/src/core/governor/plan-api.ts +115 -0
- package/src/core/{supervisor → governor}/plan-read.ts +112 -97
- package/src/core/{supervisor → governor}/plan-types.ts +59 -56
- package/src/core/{supervisor → governor}/planning.ts +223 -192
- package/src/core/governor/read.ts +59 -0
- package/src/core/{supervisor → governor}/runner.ts +39 -39
- package/src/core/{supervisor → governor}/service-read.ts +22 -22
- package/src/core/{supervisor → governor}/service.ts +117 -119
- package/src/core/{supervisor → governor}/state.ts +325 -167
- package/src/core/hooks/adapter/detect.ts +30 -0
- package/src/core/hooks/{harness → adapter}/events.ts +16 -14
- package/src/core/hooks/{harness → adapter}/output.ts +31 -23
- package/src/core/hooks/{harness → adapter}/parse.ts +5 -5
- package/src/core/hooks/{harness → adapter}/wiring.ts +21 -21
- package/src/core/hooks/cli.ts +197 -173
- package/src/core/hooks/effects/image-capture.ts +6 -6
- package/src/core/hooks/effects/index.ts +19 -19
- package/src/core/hooks/events/emit.ts +3 -3
- package/src/core/hooks/events/rotate.ts +1 -1
- package/src/core/hooks/events/schema.ts +15 -7
- package/src/core/hooks/guard-path.ts +2 -2
- package/src/core/hooks/index.ts +1 -1
- package/src/core/hooks/resolve/anchor.ts +63 -8
- package/src/core/hooks/resolve/coord-root.ts +18 -40
- package/src/core/hooks/resolve/intent.ts +1 -1
- package/src/core/hooks/resolve/owner.ts +11 -5
- package/src/core/{scratch → journal}/index.ts +53 -53
- package/src/core/policy/evaluate.ts +8 -8
- package/src/core/policy/types.ts +3 -3
- package/src/core/work/state.ts +65 -23
- package/src/core/workflow/adapters.ts +33 -0
- package/src/core/workflow/approvals.ts +7 -7
- package/src/core/workflow/billing.ts +12 -12
- package/src/core/workflow/child-env.ts +13 -4
- package/src/core/workflow/engine.ts +155 -90
- package/src/core/workflow/evidence-preflight.ts +165 -0
- package/src/core/workflow/index.ts +3 -3
- package/src/core/workflow/proof.ts +60 -50
- package/src/core/workflow/run-state.ts +7 -7
- package/src/core/workflow/sandbox-projection.ts +16 -16
- package/src/core/workflow/spawn-claude.ts +13 -10
- package/src/core/workflow/spawn-codex.ts +13 -10
- package/src/core/workflow/spawn-cursor.ts +13 -10
- package/src/core/workflow/spawn-failure.ts +1 -1
- package/src/core/workflow/specialists.ts +2 -2
- package/src/core/workflow/{journal.ts → transcript.ts} +16 -16
- package/src/core/workflow/types.ts +108 -51
- package/src/core/workflow/validate.ts +1 -1
- package/src/core/workflow/workspaces/cancellation.ts +2 -2
- package/src/core/workflow/workspaces/cleanup.ts +2 -2
- package/src/core/workflow/workspaces/execution.ts +6 -6
- package/src/core/workflow/workspaces/inspect.ts +14 -11
- package/src/core/workflow/workspaces/integration.ts +18 -14
- package/src/core/workflow/workspaces/lifecycle.ts +10 -10
- package/src/core/workflow/workspaces/local-git.ts +47 -5
- package/src/core/workflow/workspaces/recovery.ts +3 -3
- package/src/core/workflow/workspaces/state.ts +4 -3
- package/src/core/workflow/workspaces/types.ts +1 -1
- package/src/lib/browser/asserts.ts +177 -0
- package/src/lib/browser/client.ts +189 -14
- package/src/lib/browser/content-checks.ts +571 -0
- package/src/lib/browser/critique.ts +231 -0
- package/src/lib/browser/geometry.ts +297 -1
- package/src/lib/browser/index.ts +34 -0
- package/src/lib/claude-desktop.ts +4 -1
- package/src/lib/context/index.ts +11 -1
- package/src/lib/council/index.ts +9 -9
- package/src/lib/identities/assume.ts +7 -7
- package/src/lib/instructions/apply.ts +87 -25
- package/src/lib/instructions/host-addendum.ts +126 -0
- package/src/lib/instructions/templates.ts +44 -15
- package/src/lib/machine.ts +2 -2
- package/dist/commands/harness.d.ts +0 -12
- package/dist/commands/harness.d.ts.map +0 -1
- package/dist/commands/scratch.d.ts +0 -4
- package/dist/commands/scratch.d.ts.map +0 -1
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/session.js +0 -162
- package/dist/commands/supervisor.d.ts +0 -4
- package/dist/commands/supervisor.d.ts.map +0 -1
- package/dist/core/harnesses/attest-projection.d.ts.map +0 -1
- package/dist/core/harnesses/attest.d.ts.map +0 -1
- package/dist/core/harnesses/attestation.d.ts.map +0 -1
- package/dist/core/harnesses/bench.d.ts.map +0 -1
- package/dist/core/harnesses/index.d.ts +0 -13
- package/dist/core/harnesses/index.d.ts.map +0 -1
- package/dist/core/harnesses/index.js +0 -6
- package/dist/core/harnesses/profiles.d.ts.map +0 -1
- package/dist/core/harnesses/registry.d.ts +0 -15
- package/dist/core/harnesses/registry.d.ts.map +0 -1
- package/dist/core/harnesses/types.d.ts.map +0 -1
- package/dist/core/hooks/harness/detect.d.ts +0 -9
- package/dist/core/hooks/harness/events.d.ts.map +0 -1
- package/dist/core/hooks/harness/output.d.ts.map +0 -1
- package/dist/core/supervisor/index.d.ts +0 -7
- package/dist/core/supervisor/index.d.ts.map +0 -1
- package/dist/core/supervisor/index.js +0 -6
- package/dist/core/supervisor/plan-api.d.ts +0 -23
- package/dist/core/supervisor/plan-api.d.ts.map +0 -1
- package/dist/core/supervisor/plan-api.js +0 -50
- package/dist/core/supervisor/plan-read.d.ts +0 -5
- package/dist/core/supervisor/plan-read.d.ts.map +0 -1
- package/dist/core/supervisor/plan-types.d.ts.map +0 -1
- package/dist/core/supervisor/planning.d.ts +0 -33
- package/dist/core/supervisor/planning.d.ts.map +0 -1
- package/dist/core/supervisor/read.d.ts +0 -6
- package/dist/core/supervisor/read.d.ts.map +0 -1
- package/dist/core/supervisor/read.js +0 -4
- package/dist/core/supervisor/runner.d.ts +0 -37
- package/dist/core/supervisor/runner.d.ts.map +0 -1
- package/dist/core/supervisor/service-read.d.ts +0 -6
- package/dist/core/supervisor/service-read.d.ts.map +0 -1
- package/dist/core/supervisor/service.d.ts +0 -114
- package/dist/core/supervisor/service.d.ts.map +0 -1
- package/dist/core/supervisor/state.d.ts +0 -95
- package/dist/core/supervisor/state.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.d.ts +0 -17
- package/dist/core/workflow/harnesses.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.js +0 -21
- package/dist/core/workflow/journal.d.ts.map +0 -1
- package/src/commands/session.ts +0 -187
- package/src/core/harnesses/index.ts +0 -56
- package/src/core/hooks/harness/detect.ts +0 -30
- package/src/core/supervisor/index.ts +0 -89
- package/src/core/supervisor/plan-api.ts +0 -73
- package/src/core/supervisor/read.ts +0 -59
- package/src/core/workflow/harnesses.ts +0 -33
- /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
package/src/core/hooks/cli.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* alongside the legacy stream.
|
|
4
4
|
*
|
|
5
5
|
* Flow:
|
|
6
|
-
* 1. Parse argv → event-name +
|
|
7
|
-
* 2. Read stdin →
|
|
6
|
+
* 1. Parse argv → event-name + adapter.
|
|
7
|
+
* 2. Read stdin → adapter payload (JSON or empty).
|
|
8
8
|
* 3. Find coord root (walk up for .harnery/).
|
|
9
9
|
* 4. Resolve instance_id (env → payload → pid-map walk).
|
|
10
10
|
* 5. Map event-name → canonical event_type.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* Phase 2 ship criterion: confirms parser correctness across thousands of
|
|
16
16
|
* real events without affecting behavior. Always exits 0. Failures land in
|
|
17
17
|
* `.harnery/debug/agent-hook.errors.ndjson` for audit but never break the
|
|
18
|
-
*
|
|
18
|
+
* adapter flow.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { spawnSync } from "node:child_process";
|
|
@@ -23,9 +23,11 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
23
23
|
import { dirname, join } from "node:path";
|
|
24
24
|
import { coordEnv } from "../../lib/env.ts";
|
|
25
25
|
import { replayCodexJsonl } from "../agents/codex-replay.ts";
|
|
26
|
+
import { coordBinPath } from "../agents/coord-bin.ts";
|
|
26
27
|
import { consumeSince, writeCursor } from "../agents/events/consume.ts";
|
|
27
28
|
import { evaluateStopHook } from "../agents/rules/stop-hook.ts";
|
|
28
29
|
import { projectHeartbeats } from "../agents/state/heartbeat-projector.ts";
|
|
30
|
+
import { writePidmapRow } from "../agents/state/pidmap.ts";
|
|
29
31
|
import { shellMutationPaths } from "../agents/state/shell-mutation.ts";
|
|
30
32
|
import {
|
|
31
33
|
checkpointContext,
|
|
@@ -38,32 +40,32 @@ import {
|
|
|
38
40
|
recordContextSample,
|
|
39
41
|
} from "../context/index.ts";
|
|
40
42
|
import { ensureRelayDaemon, fetchPresence, publishPresence } from "../presence/index.ts";
|
|
43
|
+
import { detectAdapter } from "./adapter/detect.ts";
|
|
44
|
+
import {
|
|
45
|
+
extractBashCommand,
|
|
46
|
+
extractToolDescription,
|
|
47
|
+
type NormalizedEventType,
|
|
48
|
+
normalizeEventName,
|
|
49
|
+
type ParsedPayload,
|
|
50
|
+
parsePayload,
|
|
51
|
+
} from "./adapter/parse.ts";
|
|
41
52
|
import {
|
|
42
53
|
captureImages,
|
|
43
54
|
detectPresence,
|
|
44
55
|
imageJanitor,
|
|
56
|
+
journalArchive,
|
|
57
|
+
journalJanitor,
|
|
58
|
+
journalRecoveryCue,
|
|
45
59
|
playSound,
|
|
46
60
|
resetSoundCounters,
|
|
47
61
|
runSessionSyncExtension,
|
|
48
62
|
runTurnSummary,
|
|
49
|
-
scratchArchive,
|
|
50
|
-
scratchJanitor,
|
|
51
|
-
scratchRecoveryCue,
|
|
52
63
|
soundForEvent,
|
|
53
64
|
} from "./effects/index.ts";
|
|
54
65
|
import { emit } from "./events/emit.ts";
|
|
55
|
-
import type {
|
|
66
|
+
import type { Adapter } from "./events/schema.ts";
|
|
56
67
|
import { canonicalize } from "./guard-path.ts";
|
|
57
|
-
import {
|
|
58
|
-
import {
|
|
59
|
-
extractBashCommand,
|
|
60
|
-
extractToolDescription,
|
|
61
|
-
type NormalizedEventType,
|
|
62
|
-
normalizeEventName,
|
|
63
|
-
type ParsedPayload,
|
|
64
|
-
parsePayload,
|
|
65
|
-
} from "./harness/parse.ts";
|
|
66
|
-
import { parsePsChainLine, selectAnchorPid } from "./resolve/anchor.ts";
|
|
68
|
+
import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.ts";
|
|
67
69
|
import { findCoordRoot } from "./resolve/coord-root.ts";
|
|
68
70
|
import { extractIntentComment, resolveIntent } from "./resolve/intent.ts";
|
|
69
71
|
import { resolveOwner } from "./resolve/owner.ts";
|
|
@@ -78,11 +80,11 @@ function parseArgv(argv: string[]): Argv {
|
|
|
78
80
|
const out: Argv = { eventName: null, extra: [] };
|
|
79
81
|
for (let i = 0; i < argv.length; i++) {
|
|
80
82
|
const arg = argv[i]!;
|
|
81
|
-
if (arg === "--
|
|
82
|
-
i++; //
|
|
83
|
+
if (arg === "--adapter") {
|
|
84
|
+
i++; // detectAdapter will re-parse; just consume the value here.
|
|
83
85
|
continue;
|
|
84
86
|
}
|
|
85
|
-
if (arg.startsWith("--
|
|
87
|
+
if (arg.startsWith("--adapter=")) continue;
|
|
86
88
|
if (!out.eventName && !arg.startsWith("--")) {
|
|
87
89
|
out.eventName = arg;
|
|
88
90
|
} else {
|
|
@@ -108,7 +110,7 @@ async function readStdin(): Promise<string> {
|
|
|
108
110
|
/**
|
|
109
111
|
* Env for child agent-coord spawns, with the coord root pinned. The hook
|
|
110
112
|
* process may be running with the session shell's cwd (inside a submodule or
|
|
111
|
-
*
|
|
113
|
+
* journal dir), and agent-coord resolves its root from cwd unless overridden —
|
|
112
114
|
* without the pin a child could write state to a different .harnery than the
|
|
113
115
|
* one this hook resolved.
|
|
114
116
|
*/
|
|
@@ -149,7 +151,7 @@ function logError(coordRoot: string | null, err: unknown, context: Record<string
|
|
|
149
151
|
/**
|
|
150
152
|
* Spawn `agent-coord assign-name <owner> <kind>` to mint or recover the
|
|
151
153
|
* hurricane-style name for this owner. Returns null on any failure so
|
|
152
|
-
* session.start emission never breaks the
|
|
154
|
+
* session.start emission never breaks the adapter flow.
|
|
153
155
|
*
|
|
154
156
|
* Lives at agent-hooks side (not agent-coord) to keep emitter/consumer
|
|
155
157
|
* separation: we spawn rather than import.
|
|
@@ -159,7 +161,7 @@ function assignNameViaAgentCoord(
|
|
|
159
161
|
instanceId: string,
|
|
160
162
|
kind: "session" | "subagent" | "transient",
|
|
161
163
|
): { name: string; kind: string } | null {
|
|
162
|
-
const binary =
|
|
164
|
+
const binary = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
163
165
|
if (!existsSync(binary)) return null;
|
|
164
166
|
try {
|
|
165
167
|
const result = spawnSync(binary, ["assign-name", instanceId, kind], {
|
|
@@ -182,9 +184,15 @@ function assignNameViaAgentCoord(
|
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
/**
|
|
185
|
-
* Direct (in-process) pidmap write,
|
|
186
|
-
* the agent-coord CLI for every session.start / subagent.start. Pid-map
|
|
187
|
-
*
|
|
187
|
+
* Direct (in-process) pidmap write, avoiding the spawn overhead of going via
|
|
188
|
+
* the agent-coord CLI for every session.start / subagent.start. Pid-map rows
|
|
189
|
+
* are essential for `harn agents whoami` ppid resolution.
|
|
190
|
+
*
|
|
191
|
+
* This used to inline its own copy of the write to keep this module's
|
|
192
|
+
* dependencies narrow, which quietly mattered: the shared writer is where row
|
|
193
|
+
* hygiene lives, so the duplicate meant the only hot write path in the system
|
|
194
|
+
* never swept dead rows, and the map grew unbounded. It calls the shared writer
|
|
195
|
+
* now, and this module already imports from that directory anyway.
|
|
188
196
|
*/
|
|
189
197
|
function writePidmapViaAgentCoord(
|
|
190
198
|
coordRoot: string,
|
|
@@ -193,26 +201,9 @@ function writePidmapViaAgentCoord(
|
|
|
193
201
|
platform: string,
|
|
194
202
|
): void {
|
|
195
203
|
try {
|
|
196
|
-
|
|
197
|
-
// agent-coord/src/state/pidmap.ts but skips importing across module
|
|
198
|
-
// boundaries to keep agent-hooks's deps explicit.
|
|
199
|
-
const dir = join(coordRoot, ".harnery", "pid-map");
|
|
200
|
-
const path = join(dir, String(pid));
|
|
201
|
-
const row = `${instanceId}\t${platform}`;
|
|
202
|
-
if (existsSync(path)) {
|
|
203
|
-
try {
|
|
204
|
-
const current = require("node:fs").readFileSync(path, "utf8");
|
|
205
|
-
if (current === row) return;
|
|
206
|
-
} catch {
|
|
207
|
-
/* fall through */
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
mkdirSync(dir, { recursive: true });
|
|
211
|
-
const tmp = `${path}.tmp.${process.pid}`;
|
|
212
|
-
require("node:fs").writeFileSync(tmp, row, "utf8");
|
|
213
|
-
require("node:fs").renameSync(tmp, path);
|
|
204
|
+
writePidmapRow(coordRoot, pid, instanceId, platform);
|
|
214
205
|
} catch {
|
|
215
|
-
/* never break the
|
|
206
|
+
/* never break the adapter flow */
|
|
216
207
|
}
|
|
217
208
|
}
|
|
218
209
|
|
|
@@ -234,7 +225,7 @@ interface BuildContext {
|
|
|
234
225
|
coordRoot: string;
|
|
235
226
|
payload: ParsedPayload | null;
|
|
236
227
|
raw: string;
|
|
237
|
-
|
|
228
|
+
adapter: Adapter;
|
|
238
229
|
instanceId: string;
|
|
239
230
|
}
|
|
240
231
|
|
|
@@ -245,24 +236,24 @@ function buildEventData(
|
|
|
245
236
|
const p = ctx.payload;
|
|
246
237
|
switch (eventType) {
|
|
247
238
|
case "session.start": {
|
|
248
|
-
const
|
|
249
|
-
ctx.
|
|
239
|
+
const adapterPlatform =
|
|
240
|
+
ctx.adapter === "claude-code"
|
|
250
241
|
? "claude_code"
|
|
251
|
-
: ctx.
|
|
242
|
+
: ctx.adapter === "cursor"
|
|
252
243
|
? "cursor"
|
|
253
244
|
: "codex";
|
|
254
245
|
// Assign (or recover) name + kind via agent-coord. Idempotent: resume
|
|
255
246
|
// returns the original name; new owner consumes a counter slot.
|
|
256
247
|
const assigned = assignNameViaAgentCoord(ctx.coordRoot, ctx.instanceId, "session");
|
|
257
|
-
// Write the
|
|
248
|
+
// Write the adapter pid-map row so `harn agents whoami` ppid-walks find
|
|
258
249
|
// this owner. Prefer the payload pid (the actual claude binary), then the
|
|
259
250
|
// anchor walk (the `node` ancestor for Cursor, which has no payload pid),
|
|
260
251
|
// then our own process.ppid. Without the anchor, Cursor anchored on the
|
|
261
252
|
// ephemeral hook bash parent, a PID that dies before the agent's next
|
|
262
253
|
// shell tool call, so the ppid walk found nothing (no_pidmap_entry).
|
|
263
|
-
const
|
|
264
|
-
if (
|
|
265
|
-
writePidmapViaAgentCoord(ctx.coordRoot,
|
|
254
|
+
const adapterPid = p?.pid ?? findAdapterAnchorPid(ctx.adapter) ?? process.ppid;
|
|
255
|
+
if (adapterPid) {
|
|
256
|
+
writePidmapViaAgentCoord(ctx.coordRoot, adapterPid, ctx.instanceId, adapterPlatform);
|
|
266
257
|
}
|
|
267
258
|
return {
|
|
268
259
|
started_at: new Date().toISOString(),
|
|
@@ -271,17 +262,25 @@ function buildEventData(
|
|
|
271
262
|
// supply it). Fall back to the transcript, populated on `resume`, and
|
|
272
263
|
// backfilled later by `turn.stop` for a fresh `startup` session.
|
|
273
264
|
model: p?.model ?? scanTranscriptModel(p?.transcript_path),
|
|
274
|
-
pid:
|
|
265
|
+
pid: adapterPid,
|
|
275
266
|
source: p?.source,
|
|
276
|
-
platform:
|
|
267
|
+
platform: adapterPlatform,
|
|
277
268
|
name: assigned?.name,
|
|
278
269
|
kind: "session",
|
|
279
270
|
agent_id: ctx.instanceId,
|
|
280
271
|
// Workflow-child linkage: `workflow run` children carry the run id in
|
|
281
272
|
// env (spawn adapters set it via buildChildEnv), so their sessions and
|
|
282
|
-
// heartbeats join back to the run
|
|
273
|
+
// heartbeats join back to the run transcript + /workflows web view.
|
|
283
274
|
...(coordEnv("WORKFLOW_CHILD") === "1" && coordEnv("WORKFLOW_RUN_ID")
|
|
284
|
-
? {
|
|
275
|
+
? {
|
|
276
|
+
workflow_run_id: coordEnv("WORKFLOW_RUN_ID"),
|
|
277
|
+
// Which agent row of that run this session is. The orchestrator
|
|
278
|
+
// stamps it on the way in, because the adapter does not report a
|
|
279
|
+
// session id until the agent has already finished.
|
|
280
|
+
...(coordEnv("WORKFLOW_AGENT_ID")
|
|
281
|
+
? { workflow_agent_id: coordEnv("WORKFLOW_AGENT_ID") }
|
|
282
|
+
: {}),
|
|
283
|
+
}
|
|
285
284
|
: {}),
|
|
286
285
|
};
|
|
287
286
|
}
|
|
@@ -300,7 +299,7 @@ function buildEventData(
|
|
|
300
299
|
|
|
301
300
|
case "turn.stop": {
|
|
302
301
|
return {
|
|
303
|
-
// Backfill the model for
|
|
302
|
+
// Backfill the model for adapters that omit it at session.start
|
|
304
303
|
// (Claude Code). The transcript is populated with assistant turns by
|
|
305
304
|
// Stop-hook time, so this resolves even for fresh `startup` sessions.
|
|
306
305
|
model: p?.model ?? scanTranscriptModel(p?.transcript_path),
|
|
@@ -442,14 +441,14 @@ function numberField(value: unknown): number | undefined {
|
|
|
442
441
|
|
|
443
442
|
async function main(): Promise<number> {
|
|
444
443
|
const { eventName, extra } = parseArgv(process.argv.slice(2));
|
|
445
|
-
const
|
|
444
|
+
const adapter = detectAdapter(process.argv.slice(2));
|
|
446
445
|
const raw = await readStdin();
|
|
447
446
|
|
|
448
447
|
// Kill-switch-INDEPENDENT effects: notification sounds fire BEFORE the
|
|
449
448
|
// HARNERY_AGENT_COORD_OFF gate so audible feedback survives incident-triage
|
|
450
449
|
// bypass: sound playback happens before the kill-switch bailout.
|
|
451
450
|
// Claude-Code-only; stop-failure → error, sub-agent-start → subagent-start.
|
|
452
|
-
if (
|
|
451
|
+
if (adapter === "claude-code" && eventName) {
|
|
453
452
|
const s = soundForEvent(eventName);
|
|
454
453
|
if (s) {
|
|
455
454
|
const repoRoot = findCoordRoot(process.cwd());
|
|
@@ -481,7 +480,7 @@ async function main(): Promise<number> {
|
|
|
481
480
|
const debugBase = {
|
|
482
481
|
ts: new Date().toISOString(),
|
|
483
482
|
event_name: eventName,
|
|
484
|
-
|
|
483
|
+
adapter,
|
|
485
484
|
extra_argv: extra,
|
|
486
485
|
payload_bytes: raw.length,
|
|
487
486
|
cwd: process.cwd(),
|
|
@@ -489,8 +488,8 @@ async function main(): Promise<number> {
|
|
|
489
488
|
ppid: process.ppid,
|
|
490
489
|
};
|
|
491
490
|
|
|
492
|
-
if (!eventName || !
|
|
493
|
-
appendDebug(coordRoot, { ...debugBase, skipped: "missing-event-or-
|
|
491
|
+
if (!eventName || !adapter) {
|
|
492
|
+
appendDebug(coordRoot, { ...debugBase, skipped: "missing-event-or-adapter" });
|
|
494
493
|
return 0;
|
|
495
494
|
}
|
|
496
495
|
|
|
@@ -500,7 +499,7 @@ async function main(): Promise<number> {
|
|
|
500
499
|
return 0;
|
|
501
500
|
}
|
|
502
501
|
|
|
503
|
-
const payload = parsePayload(raw,
|
|
502
|
+
const payload = parsePayload(raw, adapter);
|
|
504
503
|
const owner = resolveOwner({ payload: payload?.raw ?? null, coordRoot });
|
|
505
504
|
if (!owner) {
|
|
506
505
|
appendDebug(coordRoot, {
|
|
@@ -517,7 +516,7 @@ async function main(): Promise<number> {
|
|
|
517
516
|
coordRoot,
|
|
518
517
|
payload,
|
|
519
518
|
raw,
|
|
520
|
-
|
|
519
|
+
adapter,
|
|
521
520
|
instanceId: owner.instance_id,
|
|
522
521
|
});
|
|
523
522
|
|
|
@@ -528,7 +527,7 @@ async function main(): Promise<number> {
|
|
|
528
527
|
parent_session_id: payload?.parent_session_id,
|
|
529
528
|
turn_id: payload?.turn_id,
|
|
530
529
|
parent_turn_id: payload?.parent_turn_id,
|
|
531
|
-
|
|
530
|
+
adapter,
|
|
532
531
|
data,
|
|
533
532
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
534
533
|
} as Parameters<typeof emit>[1]);
|
|
@@ -541,13 +540,13 @@ async function main(): Promise<number> {
|
|
|
541
540
|
});
|
|
542
541
|
|
|
543
542
|
// Context telemetry is opportunistic and truthful: only persist a sample
|
|
544
|
-
// when the
|
|
543
|
+
// when the adapter payload actually exposes usage/window data. Identical
|
|
545
544
|
// measurements are de-duplicated before they reach the canonical stream.
|
|
546
545
|
if (payload?.raw) {
|
|
547
546
|
try {
|
|
548
547
|
const sample = extractContextSample(payload.raw, {
|
|
549
548
|
sessionId,
|
|
550
|
-
|
|
549
|
+
adapter,
|
|
551
550
|
model: payload.model,
|
|
552
551
|
source: "hook",
|
|
553
552
|
confidence: "reported",
|
|
@@ -559,7 +558,7 @@ async function main(): Promise<number> {
|
|
|
559
558
|
event_type: "context.sampled",
|
|
560
559
|
instance_id: owner.instance_id,
|
|
561
560
|
session_id: sessionId,
|
|
562
|
-
|
|
561
|
+
adapter,
|
|
563
562
|
data: {
|
|
564
563
|
model: sample.model,
|
|
565
564
|
used_tokens: sample.used_tokens,
|
|
@@ -578,13 +577,13 @@ async function main(): Promise<number> {
|
|
|
578
577
|
|
|
579
578
|
// A native pre-compaction signal is the safest available point to capture
|
|
580
579
|
// external work state. The operation is idempotent until recovery, so a
|
|
581
|
-
//
|
|
580
|
+
// adapter retry cannot create a storm of near-identical capsules.
|
|
582
581
|
if (norm.event_type === "context.compaction.started") {
|
|
583
582
|
try {
|
|
584
583
|
const checkpoint = checkpointContext(coordRoot, {
|
|
585
584
|
sessionId,
|
|
586
585
|
instanceId: owner.instance_id,
|
|
587
|
-
|
|
586
|
+
adapter,
|
|
588
587
|
cwd: payload?.cwd ?? process.cwd(),
|
|
589
588
|
reason: "pre_compact",
|
|
590
589
|
model: payload?.model,
|
|
@@ -593,7 +592,7 @@ async function main(): Promise<number> {
|
|
|
593
592
|
event_type: "context.checkpoint.created",
|
|
594
593
|
instance_id: owner.instance_id,
|
|
595
594
|
session_id: sessionId,
|
|
596
|
-
|
|
595
|
+
adapter,
|
|
597
596
|
data: {
|
|
598
597
|
capsule_id: checkpoint.capsule.capsule_id,
|
|
599
598
|
generation: checkpoint.capsule.generation,
|
|
@@ -619,17 +618,17 @@ async function main(): Promise<number> {
|
|
|
619
618
|
}
|
|
620
619
|
|
|
621
620
|
// Phase 8: SessionStart post-emit: project the event so the heartbeat
|
|
622
|
-
// lands synchronously, run stale-sweep, and emit the
|
|
621
|
+
// lands synchronously, run stale-sweep, and emit the adapter-shaped
|
|
623
622
|
// systemMessage JSON (peer table + wiring check + council invites).
|
|
624
|
-
//
|
|
625
|
-
// and the equivalent per-
|
|
623
|
+
// Adapter-agnostic since v0.5.0; replaces the previous bash UX layer
|
|
624
|
+
// and the equivalent per-adapter bash session_start handlers.
|
|
626
625
|
if (norm.event_type === "session.start") {
|
|
627
|
-
// Effect (claude-code): prune stale
|
|
626
|
+
// Effect (claude-code): prune stale journal archives + sweep orphans.
|
|
628
627
|
// The recovery-cue is merged into the
|
|
629
628
|
// session-start additionalContext inside emitSessionStartSystemMessage.
|
|
630
|
-
if (
|
|
631
|
-
// Image-feed retention sweep (size + age cap on .harnery/images/).
|
|
632
|
-
// agnostic, cheap (one readdir), fail-soft. Paired with
|
|
629
|
+
if (adapter === "claude-code") journalJanitor(coordRoot);
|
|
630
|
+
// Image-feed retention sweep (size + age cap on .harnery/images/). Adapter-
|
|
631
|
+
// agnostic, cheap (one readdir), fail-soft. Paired with journalJanitor as a
|
|
633
632
|
// session-start "tidy the coord layer" step.
|
|
634
633
|
try {
|
|
635
634
|
imageJanitor(coordRoot);
|
|
@@ -643,7 +642,7 @@ async function main(): Promise<number> {
|
|
|
643
642
|
event_type: "context.compaction.completed",
|
|
644
643
|
instance_id: owner.instance_id,
|
|
645
644
|
session_id: sessionId,
|
|
646
|
-
|
|
645
|
+
adapter,
|
|
647
646
|
data: { trigger: "auto" },
|
|
648
647
|
});
|
|
649
648
|
markContextCompactionCompleted(coordRoot, {
|
|
@@ -665,7 +664,7 @@ async function main(): Promise<number> {
|
|
|
665
664
|
owner.instance_id,
|
|
666
665
|
sessionId,
|
|
667
666
|
data,
|
|
668
|
-
|
|
667
|
+
adapter,
|
|
669
668
|
recovery?.briefing ?? "",
|
|
670
669
|
);
|
|
671
670
|
if (injected && recovery) {
|
|
@@ -673,7 +672,7 @@ async function main(): Promise<number> {
|
|
|
673
672
|
coordRoot,
|
|
674
673
|
owner.instance_id,
|
|
675
674
|
sessionId,
|
|
676
|
-
|
|
675
|
+
adapter,
|
|
677
676
|
recovery,
|
|
678
677
|
"SessionStart",
|
|
679
678
|
);
|
|
@@ -693,7 +692,7 @@ async function main(): Promise<number> {
|
|
|
693
692
|
}
|
|
694
693
|
}
|
|
695
694
|
|
|
696
|
-
// Phase 8: SessionEnd cleanup: delete heartbeat + pid-map rows.
|
|
695
|
+
// Phase 8: SessionEnd cleanup: delete heartbeat + pid-map rows. Adapter-
|
|
697
696
|
// agnostic since v0.5.0.
|
|
698
697
|
if (norm.event_type === "session.end") {
|
|
699
698
|
try {
|
|
@@ -701,10 +700,10 @@ async function main(): Promise<number> {
|
|
|
701
700
|
} catch (err) {
|
|
702
701
|
logError(coordRoot, err, { phase: "session-end-cleanup" });
|
|
703
702
|
}
|
|
704
|
-
// Effects (claude-code): archive the ending agent's
|
|
703
|
+
// Effects (claude-code): archive the ending agent's journal + force a
|
|
705
704
|
// session-telemetry sync (via HARNERY_CLAUDE_SESSIONS_FORCE=1).
|
|
706
|
-
if (
|
|
707
|
-
|
|
705
|
+
if (adapter === "claude-code") {
|
|
706
|
+
journalArchive(coordRoot, owner.instance_id);
|
|
708
707
|
runSessionSyncExtension(coordRoot, true);
|
|
709
708
|
}
|
|
710
709
|
// Presence: publish the post-cleanup state so peers see this session gone
|
|
@@ -721,7 +720,7 @@ async function main(): Promise<number> {
|
|
|
721
720
|
// subagent (claude-code + cursor; codex doesn't fan out subagents today).
|
|
722
721
|
if (norm.event_type === "subagent.start") {
|
|
723
722
|
try {
|
|
724
|
-
const agentCoordBin =
|
|
723
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
725
724
|
if (existsSync(agentCoordBin)) {
|
|
726
725
|
spawnSync(agentCoordBin, ["project"], {
|
|
727
726
|
encoding: "utf8",
|
|
@@ -732,14 +731,14 @@ async function main(): Promise<number> {
|
|
|
732
731
|
agentCoordBin,
|
|
733
732
|
[
|
|
734
733
|
"log",
|
|
735
|
-
`SUBAGENT_START agent_type=${(data.agent_type as string) ?? "unknown"} agent_id=${owner.instance_id.slice(0, 8)} platform=${
|
|
734
|
+
`SUBAGENT_START agent_type=${(data.agent_type as string) ?? "unknown"} agent_id=${owner.instance_id.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
|
|
736
735
|
"--instance",
|
|
737
736
|
owner.instance_id,
|
|
738
737
|
],
|
|
739
738
|
{ encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) },
|
|
740
739
|
);
|
|
741
740
|
}
|
|
742
|
-
emitSubagentStartContext(coordRoot, owner.instance_id, sessionId, data,
|
|
741
|
+
emitSubagentStartContext(coordRoot, owner.instance_id, sessionId, data, adapter);
|
|
743
742
|
} catch (err) {
|
|
744
743
|
logError(coordRoot, err, { phase: "subagent-start-project" });
|
|
745
744
|
}
|
|
@@ -749,13 +748,13 @@ async function main(): Promise<number> {
|
|
|
749
748
|
if (norm.event_type === "subagent.stop") {
|
|
750
749
|
try {
|
|
751
750
|
cleanupSessionEnd(coordRoot, owner.instance_id, (data.reason as string) ?? "unknown");
|
|
752
|
-
const agentCoordBin =
|
|
751
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
753
752
|
if (existsSync(agentCoordBin)) {
|
|
754
753
|
spawnSync(
|
|
755
754
|
agentCoordBin,
|
|
756
755
|
[
|
|
757
756
|
"log",
|
|
758
|
-
`SUBAGENT_STOP agent_id=${owner.instance_id.slice(0, 8)} platform=${
|
|
757
|
+
`SUBAGENT_STOP agent_id=${owner.instance_id.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
|
|
759
758
|
"--instance",
|
|
760
759
|
owner.instance_id,
|
|
761
760
|
],
|
|
@@ -768,11 +767,11 @@ async function main(): Promise<number> {
|
|
|
768
767
|
}
|
|
769
768
|
|
|
770
769
|
// Phase 8: UserPromptSubmit: render dedup'd peer table + council pending
|
|
771
|
-
// and emit the
|
|
770
|
+
// and emit the adapter-shaped systemMessage JSON. Adapter-agnostic since v0.5.0.
|
|
772
771
|
if (norm.event_type === "user_prompt.submit") {
|
|
773
772
|
// Effects (claude-code): reset per-turn sound rate-limit counters + run
|
|
774
773
|
// presence detection on the prompt.
|
|
775
|
-
if (
|
|
774
|
+
if (adapter === "claude-code") {
|
|
776
775
|
resetSoundCounters(sessionId);
|
|
777
776
|
const prompt = (payload?.raw?.prompt as string | undefined) ?? "";
|
|
778
777
|
if (prompt) detectPresence(prompt);
|
|
@@ -805,7 +804,7 @@ async function main(): Promise<number> {
|
|
|
805
804
|
coordRoot,
|
|
806
805
|
owner.instance_id,
|
|
807
806
|
sessionId,
|
|
808
|
-
|
|
807
|
+
adapter,
|
|
809
808
|
recovery?.briefing ?? "",
|
|
810
809
|
);
|
|
811
810
|
if (injected && recovery) {
|
|
@@ -813,7 +812,7 @@ async function main(): Promise<number> {
|
|
|
813
812
|
coordRoot,
|
|
814
813
|
owner.instance_id,
|
|
815
814
|
sessionId,
|
|
816
|
-
|
|
815
|
+
adapter,
|
|
817
816
|
recovery,
|
|
818
817
|
"UserPromptSubmit",
|
|
819
818
|
);
|
|
@@ -824,7 +823,7 @@ async function main(): Promise<number> {
|
|
|
824
823
|
}
|
|
825
824
|
|
|
826
825
|
// turn.stop: telemetry + turn-summary effects, then the stop verdict. The
|
|
827
|
-
// verdict + codex-replay previously lived in the per-
|
|
826
|
+
// verdict + codex-replay previously lived in the per-adapter shell adapters;
|
|
828
827
|
// agent-hook owns them now. Runs on the normal "stop" event only;
|
|
829
828
|
// "stop-failure" (API error) gets no gate, matching the previous
|
|
830
829
|
// stop vs stop-failure split.
|
|
@@ -833,7 +832,7 @@ async function main(): Promise<number> {
|
|
|
833
832
|
// the status_checked / task_set / status_box_present evidence (codex
|
|
834
833
|
// doesn't emit those live; this re-emits turn.stop after agent-hook's own,
|
|
835
834
|
// so the verdict reads the replay's box signal as the latest).
|
|
836
|
-
if (
|
|
835
|
+
if (adapter === "codex" && payload?.transcript_path && existsSync(payload.transcript_path)) {
|
|
837
836
|
try {
|
|
838
837
|
replayCodexJsonl({
|
|
839
838
|
coordRoot,
|
|
@@ -849,7 +848,7 @@ async function main(): Promise<number> {
|
|
|
849
848
|
|
|
850
849
|
// CC effects: rate-limited session-telemetry sync + turn-summary Haiku
|
|
851
850
|
// auto-summary.
|
|
852
|
-
if (
|
|
851
|
+
if (adapter === "claude-code") {
|
|
853
852
|
runSessionSyncExtension(coordRoot, false);
|
|
854
853
|
runTurnSummary(coordRoot, owner.instance_id, sessionId, payload?.transcript_path);
|
|
855
854
|
}
|
|
@@ -857,7 +856,7 @@ async function main(): Promise<number> {
|
|
|
857
856
|
// Master-state heartbeat projection. Drains events.ndjson since the last
|
|
858
857
|
// cursor → per-owner heartbeats. Was a SECOND binary (`agent-coord project`)
|
|
859
858
|
// pinned to Claude Code Stop only; folded in here so it (a) is one
|
|
860
|
-
// entry per event like everything else and (b) fires on EVERY
|
|
859
|
+
// entry per event like everything else and (b) fires on EVERY adapter's stop,
|
|
861
860
|
// not just CC. Runs unconditionally before the verdict's possible exit-2 return
|
|
862
861
|
// (the events are real regardless of whether the agent gets nagged), and after
|
|
863
862
|
// codex-replay above so codex's replayed events are included in the drain.
|
|
@@ -888,22 +887,22 @@ async function main(): Promise<number> {
|
|
|
888
887
|
rule: "stop-hook",
|
|
889
888
|
instance_id: owner.instance_id,
|
|
890
889
|
session_id: sessionId,
|
|
891
|
-
|
|
890
|
+
adapter,
|
|
892
891
|
bypass: coordEnv("AGENT_COORD_BYPASS_STOP") === "1",
|
|
893
892
|
workflow_child: coordEnv("WORKFLOW_CHILD") === "1",
|
|
894
893
|
});
|
|
895
894
|
if (!verdict.allow) {
|
|
896
|
-
//
|
|
895
|
+
// Adapter-aware enforcement channel: Claude Code / Codex honor exit-2 +
|
|
897
896
|
// stderr as a turn block; Cursor ignores exit codes (fail-open) and
|
|
898
897
|
// re-prompts only via a `followup_message` it auto-submits. emitStopBlock
|
|
899
898
|
// writes the right shape and returns the exit code to use.
|
|
900
|
-
const { emitStopBlock } = await import("./
|
|
901
|
-
return emitStopBlock(
|
|
899
|
+
const { emitStopBlock } = await import("./adapter/output.ts");
|
|
900
|
+
return emitStopBlock(adapter, verdict);
|
|
902
901
|
}
|
|
903
902
|
}
|
|
904
903
|
|
|
905
904
|
// Phase 7: PreToolUse: heartbeat + pid-map self-heal on every tool call.
|
|
906
|
-
//
|
|
905
|
+
// Adapter-agnostic: both writes have the same shape regardless of who fired.
|
|
907
906
|
// Cursor/Codex bash dispatchers still fire their own G-guard logic, but the
|
|
908
907
|
// heals here keep the agent-coord layer's view of liveness fresh.
|
|
909
908
|
//
|
|
@@ -913,8 +912,8 @@ async function main(): Promise<number> {
|
|
|
913
912
|
// call was restored here afterward.
|
|
914
913
|
if (norm.event_type === "tool.pre_use") {
|
|
915
914
|
try {
|
|
916
|
-
healHeartbeatViaCli(coordRoot, owner.instance_id, sessionId,
|
|
917
|
-
refreshPidmap(coordRoot, owner.instance_id,
|
|
915
|
+
healHeartbeatViaCli(coordRoot, owner.instance_id, sessionId, adapter);
|
|
916
|
+
refreshPidmap(coordRoot, owner.instance_id, adapter, payload?.pid);
|
|
918
917
|
} catch (err) {
|
|
919
918
|
logError(coordRoot, err, { phase: "pre-tool-use-heal" });
|
|
920
919
|
}
|
|
@@ -928,29 +927,29 @@ async function main(): Promise<number> {
|
|
|
928
927
|
payload,
|
|
929
928
|
instanceId: owner.instance_id,
|
|
930
929
|
sessionId,
|
|
931
|
-
|
|
930
|
+
adapter,
|
|
932
931
|
});
|
|
933
932
|
} catch (err) {
|
|
934
933
|
logError(coordRoot, err, { phase: "pre-tool-use-image-capture" });
|
|
935
934
|
}
|
|
936
935
|
|
|
937
|
-
// G-guard for ALL
|
|
936
|
+
// G-guard for ALL adapters. Claude Code previously ran this via a
|
|
938
937
|
// pre-tool-use bash adapter (which called `agent-coord verdict --rule=claim`);
|
|
939
|
-
// that adapter is now deleted, so agent-hook owns the deny for every
|
|
940
|
-
// emitDeny() inside emits the
|
|
938
|
+
// that adapter is now deleted, so agent-hook owns the deny for every adapter.
|
|
939
|
+
// emitDeny() inside emits the adapter-shaped permission JSON (claude-code +
|
|
941
940
|
// codex use hookSpecificOutput.permissionDecision; cursor uses .permission).
|
|
942
941
|
// apply_patch (codex) parses paths from the patch body and runs verdict
|
|
943
942
|
// per-path; Edit/Write/NotebookEdit resolve a single target. Non-write tools
|
|
944
943
|
// (incl. Agent) yield no targets and pass through with no deny.
|
|
945
944
|
try {
|
|
946
|
-
await runPreToolUseGuard(coordRoot, owner.instance_id, sessionId, data,
|
|
945
|
+
await runPreToolUseGuard(coordRoot, owner.instance_id, sessionId, data, adapter);
|
|
947
946
|
} catch (err) {
|
|
948
947
|
logError(coordRoot, err, { phase: "pre-tool-use-guard" });
|
|
949
948
|
}
|
|
950
949
|
|
|
951
950
|
// Shell-mutation warn (warn-only, never blocks). Was the cursor
|
|
952
951
|
// beforeShellExecution + codex preToolUse-Bash shell-mutation-claim-log in
|
|
953
|
-
// the per-
|
|
952
|
+
// the per-adapter shell adapters. Cursor sends the command at payload.command;
|
|
954
953
|
// codex Bash at tool_input.command. Emits a decision.warn per candidate-mutated
|
|
955
954
|
// path so a peer sees the write in events.ndjson. (CC never did this,
|
|
956
955
|
// preserved; it emits with its own hooks-side emitter per the
|
|
@@ -958,7 +957,7 @@ async function main(): Promise<number> {
|
|
|
958
957
|
const shellCmd =
|
|
959
958
|
eventName === "before-shell-execution"
|
|
960
959
|
? ((payload?.raw.command as string | undefined) ?? "")
|
|
961
|
-
:
|
|
960
|
+
: adapter === "codex" && data.tool_name === "Bash"
|
|
962
961
|
? (((payload?.raw.tool_input as Record<string, unknown> | undefined)?.command as
|
|
963
962
|
| string
|
|
964
963
|
| undefined) ?? "")
|
|
@@ -967,13 +966,13 @@ async function main(): Promise<number> {
|
|
|
967
966
|
try {
|
|
968
967
|
const paths = shellMutationPaths(shellCmd, coordRoot);
|
|
969
968
|
const truncated = shellCmd.length > 80 ? shellCmd.slice(0, 80) : shellCmd;
|
|
970
|
-
const platform =
|
|
969
|
+
const platform = adapterPlatform(adapter);
|
|
971
970
|
for (const p of paths) {
|
|
972
971
|
emit(coordRoot, {
|
|
973
972
|
event_type: "decision.warn",
|
|
974
973
|
instance_id: owner.instance_id,
|
|
975
974
|
session_id: sessionId,
|
|
976
|
-
|
|
975
|
+
adapter,
|
|
977
976
|
data: {
|
|
978
977
|
rule: "shell_mutation_candidate",
|
|
979
978
|
reason: `path=${p} cmd=${truncated} platform=${platform}`,
|
|
@@ -988,7 +987,7 @@ async function main(): Promise<number> {
|
|
|
988
987
|
}
|
|
989
988
|
|
|
990
989
|
// Phase 7: PostToolUse: stamp last_tool + last_tool_target on heartbeat.
|
|
991
|
-
//
|
|
990
|
+
// Adapter-agnostic for the same reason as tool.pre_use above.
|
|
992
991
|
if (norm.event_type === "tool.post_use") {
|
|
993
992
|
try {
|
|
994
993
|
stampToolActivity(coordRoot, owner.instance_id, data);
|
|
@@ -1006,7 +1005,7 @@ async function main(): Promise<number> {
|
|
|
1006
1005
|
payload,
|
|
1007
1006
|
instanceId: owner.instance_id,
|
|
1008
1007
|
sessionId,
|
|
1009
|
-
|
|
1008
|
+
adapter,
|
|
1010
1009
|
});
|
|
1011
1010
|
} catch (err) {
|
|
1012
1011
|
logError(coordRoot, err, { phase: "post-tool-use-image-capture" });
|
|
@@ -1014,7 +1013,7 @@ async function main(): Promise<number> {
|
|
|
1014
1013
|
}
|
|
1015
1014
|
|
|
1016
1015
|
// Phase 7: PostToolUseFailure: release claim on failed Edit (the file
|
|
1017
|
-
// never landed; the claim is stale).
|
|
1016
|
+
// never landed; the claim is stale). Adapter-agnostic.
|
|
1018
1017
|
if (norm.event_type === "tool.post_use_failure") {
|
|
1019
1018
|
try {
|
|
1020
1019
|
releaseClaimOnFailure(coordRoot, owner.instance_id, data, payload?.raw);
|
|
@@ -1031,7 +1030,7 @@ async function runPreToolUseGuard(
|
|
|
1031
1030
|
instanceId: string,
|
|
1032
1031
|
sessionId: string,
|
|
1033
1032
|
data: Record<string, unknown>,
|
|
1034
|
-
|
|
1033
|
+
adapter: Adapter,
|
|
1035
1034
|
): Promise<void> {
|
|
1036
1035
|
const toolName = (data.tool_name as string | undefined) ?? "";
|
|
1037
1036
|
const targets = collectGuardTargets(toolName, data)
|
|
@@ -1039,7 +1038,7 @@ async function runPreToolUseGuard(
|
|
|
1039
1038
|
.filter((p): p is string => p !== null);
|
|
1040
1039
|
if (targets.length === 0) return;
|
|
1041
1040
|
|
|
1042
|
-
const agentCoordBin =
|
|
1041
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1043
1042
|
if (!existsSync(agentCoordBin)) return;
|
|
1044
1043
|
|
|
1045
1044
|
// For apply_patch (multi-file), collect siblings so the deny reason names
|
|
@@ -1075,8 +1074,8 @@ async function runPreToolUseGuard(
|
|
|
1075
1074
|
reason += ` The patch also touched: ${siblings}: pick a different file or wait.`;
|
|
1076
1075
|
}
|
|
1077
1076
|
}
|
|
1078
|
-
const { emitDeny } = await import("./
|
|
1079
|
-
emitDeny(
|
|
1077
|
+
const { emitDeny } = await import("./adapter/output.ts");
|
|
1078
|
+
emitDeny(adapter, reason);
|
|
1080
1079
|
return;
|
|
1081
1080
|
}
|
|
1082
1081
|
}
|
|
@@ -1142,15 +1141,15 @@ function healHeartbeatViaCli(
|
|
|
1142
1141
|
coordRoot: string,
|
|
1143
1142
|
instanceId: string,
|
|
1144
1143
|
sessionId: string,
|
|
1145
|
-
|
|
1144
|
+
adapter: string,
|
|
1146
1145
|
): void {
|
|
1147
|
-
const agentCoordBin =
|
|
1146
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1148
1147
|
if (!existsSync(agentCoordBin)) return;
|
|
1149
|
-
// Pass the detected
|
|
1148
|
+
// Pass the detected adapter so a pruned Cursor/Codex heartbeat is recreated
|
|
1150
1149
|
// with the correct platform; without it, healHeartbeat defaults to
|
|
1151
1150
|
// claude_code and the dashboard mislabels the agent. See
|
|
1152
1151
|
// heartbeat-writer.healHeartbeat.
|
|
1153
|
-
spawnSync(agentCoordBin, ["heal-heartbeat", instanceId, sessionId, `--
|
|
1152
|
+
spawnSync(agentCoordBin, ["heal-heartbeat", instanceId, sessionId, `--adapter=${adapter}`], {
|
|
1154
1153
|
encoding: "utf8",
|
|
1155
1154
|
timeout: 2000,
|
|
1156
1155
|
env: childEnv(coordRoot),
|
|
@@ -1158,38 +1157,59 @@ function healHeartbeatViaCli(
|
|
|
1158
1157
|
}
|
|
1159
1158
|
|
|
1160
1159
|
/**
|
|
1161
|
-
* Walk up the ppid chain on Linux/WSL looking for the
|
|
1160
|
+
* Walk up the ppid chain on Linux/WSL looking for the adapter anchor PID,
|
|
1162
1161
|
* the PID of the claude / cursor / codex binary. Finds the agent PID. Used by
|
|
1163
|
-
* `tool.pre_use`'s pid-map self-heal so a re-parented
|
|
1162
|
+
* `tool.pre_use`'s pid-map self-heal so a re-parented adapter binary (the
|
|
1164
1163
|
* VS Code 2.1.x sibling-claude spawn case) gets its pid-map row rewritten on
|
|
1165
1164
|
* the next tool call rather than going invisible until SessionStart fires
|
|
1166
1165
|
* again, which it may never do.
|
|
1167
1166
|
*
|
|
1168
1167
|
* Returns undefined only when no anchor is found; callers fall back to
|
|
1169
|
-
* `process.ppid` (the bash wrapper's parent, which is usually the
|
|
1168
|
+
* `process.ppid` (the bash wrapper's parent, which is usually the adapter binary
|
|
1170
1169
|
* itself). `HARNERY_AGENT_COORD_TEST_ANCHOR_PID` overrides everything so the
|
|
1171
1170
|
* test sandbox can pin a deterministic PID.
|
|
1172
1171
|
*/
|
|
1173
|
-
function
|
|
1172
|
+
function findAdapterAnchorPid(adapter?: Adapter): number | undefined {
|
|
1174
1173
|
const override = coordEnv("AGENT_COORD_TEST_ANCHOR_PID");
|
|
1175
1174
|
if (override) {
|
|
1176
1175
|
const n = Number(override);
|
|
1177
1176
|
if (Number.isFinite(n) && n > 0) return n;
|
|
1178
1177
|
}
|
|
1178
|
+
// A adapter that states its own pid ends the search. Nothing inferred from the
|
|
1179
|
+
// process tree beats the adapter naming itself, and the inference is the part
|
|
1180
|
+
// that has been failing: comm matching wants a binary called after its
|
|
1181
|
+
// adapter, and one Claude Code build ships its CLI under a version-numbered
|
|
1182
|
+
// filename, so the walk found nothing and the caller fell back to the hook's
|
|
1183
|
+
// own shell. That row was dead within seconds, leaving every session in this
|
|
1184
|
+
// environment unattributed.
|
|
1185
|
+
const fromEnv = adapterPidFromEnv();
|
|
1186
|
+
if (fromEnv) return fromEnv;
|
|
1179
1187
|
// Build the ppid chain (nearest → root, up to 20 hops), then hand it to the
|
|
1180
1188
|
// pure selector. Linux/WSL reads /proc; macOS/BSD (no /proc) falls back to
|
|
1181
1189
|
// `ps -o ppid=,comm=` parsed by the unit-tested `parsePsChainLine`. Splitting
|
|
1182
|
-
// the walk (untestable off a live box) from the
|
|
1183
|
-
//
|
|
1184
|
-
|
|
1190
|
+
// the walk (untestable off a live box) from the matching keeps the selector
|
|
1191
|
+
// logic verifiable.
|
|
1192
|
+
//
|
|
1193
|
+
// Only the /proc branch carries an executable path for the selector's
|
|
1194
|
+
// path-based pass. Asking `ps` for one means either a second spawn per hop or
|
|
1195
|
+
// splitting `comm` from `args` on a line where comm may itself contain spaces,
|
|
1196
|
+
// and the adapter that needs the path pass states its pid in the env anyway.
|
|
1197
|
+
const chain: Array<{ pid: number; comm: string; exe?: string }> = [];
|
|
1185
1198
|
let pid = process.pid;
|
|
1186
1199
|
for (let hops = 0; hops < 20; hops++) {
|
|
1187
|
-
let hop: { comm: string; ppid: number } | null = null;
|
|
1200
|
+
let hop: { comm: string; ppid: number; exe?: string } | null = null;
|
|
1188
1201
|
try {
|
|
1189
1202
|
const comm = readFileSync(`/proc/${pid}/comm`, "utf8").trim();
|
|
1190
1203
|
const status = readFileSync(`/proc/${pid}/status`, "utf8");
|
|
1191
1204
|
const m = status.match(/^PPid:\s+(\d+)/m);
|
|
1192
|
-
|
|
1205
|
+
// argv[0], for the path-based second pass. cmdline is NUL-separated.
|
|
1206
|
+
let exe: string | undefined;
|
|
1207
|
+
try {
|
|
1208
|
+
exe = readFileSync(`/proc/${pid}/cmdline`, "utf8").split("\0")[0] || undefined;
|
|
1209
|
+
} catch {
|
|
1210
|
+
/* exe is optional */
|
|
1211
|
+
}
|
|
1212
|
+
hop = { comm, ppid: m ? Number(m[1]) : 0, exe };
|
|
1193
1213
|
} catch {
|
|
1194
1214
|
// no /proc (macOS/BSD) — fall through to ps
|
|
1195
1215
|
}
|
|
@@ -1198,11 +1218,11 @@ function findHarnessAnchorPid(harness?: Harness): number | undefined {
|
|
|
1198
1218
|
if (out.status === 0) hop = parsePsChainLine(out.stdout);
|
|
1199
1219
|
}
|
|
1200
1220
|
if (!hop) break;
|
|
1201
|
-
chain.push({ pid, comm: hop.comm });
|
|
1221
|
+
chain.push({ pid, comm: hop.comm, exe: hop.exe });
|
|
1202
1222
|
if (!Number.isFinite(hop.ppid) || hop.ppid === 0 || hop.ppid === 1) break;
|
|
1203
1223
|
pid = hop.ppid;
|
|
1204
1224
|
}
|
|
1205
|
-
return selectAnchorPid(chain,
|
|
1225
|
+
return selectAnchorPid(chain, adapter);
|
|
1206
1226
|
}
|
|
1207
1227
|
|
|
1208
1228
|
/**
|
|
@@ -1212,7 +1232,7 @@ function findHarnessAnchorPid(harness?: Harness): number | undefined {
|
|
|
1212
1232
|
*
|
|
1213
1233
|
* The pid argument prefers the payload's `pid` (CC populates it on
|
|
1214
1234
|
* SessionStart and may also send it on PreToolUse), then
|
|
1215
|
-
* `
|
|
1235
|
+
* `findAdapterAnchorPid`, then `process.ppid`. Writes go through the same
|
|
1216
1236
|
* idempotent `writePidmapViaAgentCoord` helper that SessionStart uses: no
|
|
1217
1237
|
* disk I/O on no-op heals (when the row already points at us).
|
|
1218
1238
|
*
|
|
@@ -1223,12 +1243,12 @@ function findHarnessAnchorPid(harness?: Harness): number | undefined {
|
|
|
1223
1243
|
function refreshPidmap(
|
|
1224
1244
|
coordRoot: string,
|
|
1225
1245
|
instanceId: string,
|
|
1226
|
-
|
|
1246
|
+
adapter: Adapter,
|
|
1227
1247
|
payloadPid?: number,
|
|
1228
1248
|
): void {
|
|
1229
|
-
const pid = payloadPid ??
|
|
1249
|
+
const pid = payloadPid ?? findAdapterAnchorPid(adapter) ?? process.ppid;
|
|
1230
1250
|
if (!Number.isFinite(pid) || pid <= 0) return;
|
|
1231
|
-
writePidmapViaAgentCoord(coordRoot, pid, instanceId,
|
|
1251
|
+
writePidmapViaAgentCoord(coordRoot, pid, instanceId, adapterPlatform(adapter));
|
|
1232
1252
|
}
|
|
1233
1253
|
|
|
1234
1254
|
function stampToolActivity(
|
|
@@ -1236,7 +1256,7 @@ function stampToolActivity(
|
|
|
1236
1256
|
instanceId: string,
|
|
1237
1257
|
data: Record<string, unknown>,
|
|
1238
1258
|
): void {
|
|
1239
|
-
const agentCoordBin =
|
|
1259
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1240
1260
|
if (!existsSync(agentCoordBin)) return;
|
|
1241
1261
|
const toolName = (data.tool_name as string | undefined) ?? "";
|
|
1242
1262
|
// Extract a 1-line target from the tool_input blob (file path / command head).
|
|
@@ -1308,7 +1328,7 @@ function releaseClaimOnFailure(
|
|
|
1308
1328
|
: filePath;
|
|
1309
1329
|
}
|
|
1310
1330
|
|
|
1311
|
-
const agentCoordBin =
|
|
1331
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1312
1332
|
if (!existsSync(agentCoordBin)) return;
|
|
1313
1333
|
spawnSync(agentCoordBin, ["release-claim", instanceId, canonical], {
|
|
1314
1334
|
encoding: "utf8",
|
|
@@ -1349,7 +1369,7 @@ function cleanupSessionEnd(coordRoot: string, instanceId: string, reason: string
|
|
|
1349
1369
|
}
|
|
1350
1370
|
}
|
|
1351
1371
|
// Activity log
|
|
1352
|
-
const agentCoordBin =
|
|
1372
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1353
1373
|
if (existsSync(agentCoordBin)) {
|
|
1354
1374
|
spawnSync(
|
|
1355
1375
|
agentCoordBin,
|
|
@@ -1363,10 +1383,10 @@ async function emitUserPromptSubmitSystemMessage(
|
|
|
1363
1383
|
coordRoot: string,
|
|
1364
1384
|
instanceId: string,
|
|
1365
1385
|
sessionId: string,
|
|
1366
|
-
|
|
1386
|
+
adapter: Adapter,
|
|
1367
1387
|
recoveryBriefing = "",
|
|
1368
1388
|
): Promise<boolean> {
|
|
1369
|
-
const agentCoordBin =
|
|
1389
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1370
1390
|
let additionalContext = "";
|
|
1371
1391
|
|
|
1372
1392
|
if (existsSync(agentCoordBin)) {
|
|
@@ -1385,10 +1405,14 @@ async function emitUserPromptSubmitSystemMessage(
|
|
|
1385
1405
|
|
|
1386
1406
|
const args = ["prompt-context", "--instance", instanceId, "--session", sessionId];
|
|
1387
1407
|
if (agentName) args.push("--name", agentName);
|
|
1388
|
-
//
|
|
1389
|
-
//
|
|
1390
|
-
//
|
|
1391
|
-
|
|
1408
|
+
// Every human-facing adapter gets the first-session naming reminder from
|
|
1409
|
+
// the shared hook path. Cursor + Codex additionally get ongoing set-task
|
|
1410
|
+
// staleness nudges; Claude Code enforces those via its Stop transcript.
|
|
1411
|
+
// Codex gets a fresh status-footer reminder on every prompt. Its Stop hook
|
|
1412
|
+
// stays observe-only, so missing the footer never replaces the answer.
|
|
1413
|
+
args.push("--session-name-nudge");
|
|
1414
|
+
if (adapter === "cursor" || adapter === "codex") args.push("--task-nudge");
|
|
1415
|
+
if (adapter === "codex") args.push("--status-footer-nudge");
|
|
1392
1416
|
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1393
1417
|
if (result.status === 0 && result.stdout) additionalContext = result.stdout.trim();
|
|
1394
1418
|
}
|
|
@@ -1397,8 +1421,8 @@ async function emitUserPromptSubmitSystemMessage(
|
|
|
1397
1421
|
}
|
|
1398
1422
|
if (!additionalContext) return false;
|
|
1399
1423
|
|
|
1400
|
-
const { emitContext } = await import("./
|
|
1401
|
-
emitContext(
|
|
1424
|
+
const { emitContext } = await import("./adapter/output.ts");
|
|
1425
|
+
emitContext(adapter, "UserPromptSubmit", additionalContext);
|
|
1402
1426
|
return true;
|
|
1403
1427
|
}
|
|
1404
1428
|
|
|
@@ -1407,14 +1431,14 @@ function emitSubagentStartContext(
|
|
|
1407
1431
|
instanceId: string,
|
|
1408
1432
|
sessionId: string,
|
|
1409
1433
|
data: Record<string, unknown>,
|
|
1410
|
-
|
|
1434
|
+
adapter: Adapter,
|
|
1411
1435
|
): void {
|
|
1412
1436
|
// Look up the subagent's assigned name (just-written by agent-coord assignName
|
|
1413
1437
|
// in session.start data) + the parent's short id for the "you are a subagent
|
|
1414
1438
|
// of X" framing.
|
|
1415
1439
|
const subagentName = (data.name as string | undefined) ?? "";
|
|
1416
1440
|
if (!subagentName) return;
|
|
1417
|
-
const platformLabel =
|
|
1441
|
+
const platformLabel = adapterPlatform(adapter);
|
|
1418
1442
|
const parentShort =
|
|
1419
1443
|
sessionId && sessionId !== instanceId ? `agent-${sessionId.slice(0, 8)}` : "the parent session";
|
|
1420
1444
|
const message = `You are agent-${subagentName} (${platformLabel} subagent). You're a subagent of ${parentShort}.`;
|
|
@@ -1422,7 +1446,7 @@ function emitSubagentStartContext(
|
|
|
1422
1446
|
// Render peer table inline since the subagent might want to know who else
|
|
1423
1447
|
// is around. Reuse prompt-context (which dedups against the per-owner hash);
|
|
1424
1448
|
// first call will always emit.
|
|
1425
|
-
const agentCoordBin =
|
|
1449
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1426
1450
|
let combined = message;
|
|
1427
1451
|
if (existsSync(agentCoordBin)) {
|
|
1428
1452
|
const result = spawnSync(
|
|
@@ -1436,14 +1460,14 @@ function emitSubagentStartContext(
|
|
|
1436
1460
|
|
|
1437
1461
|
// Use SubagentStart event-name in CC's hookSpecificOutput shape; cursor's
|
|
1438
1462
|
// flat `additional_context` works the same way.
|
|
1439
|
-
void import("./
|
|
1440
|
-
emitContext(
|
|
1463
|
+
void import("./adapter/output.ts").then(({ emitContext }) => {
|
|
1464
|
+
emitContext(adapter, "SubagentStart", combined);
|
|
1441
1465
|
});
|
|
1442
1466
|
}
|
|
1443
1467
|
|
|
1444
|
-
function
|
|
1445
|
-
if (
|
|
1446
|
-
return
|
|
1468
|
+
function adapterPlatform(adapter: Adapter): string {
|
|
1469
|
+
if (adapter === "claude-code") return "claude_code";
|
|
1470
|
+
return adapter;
|
|
1447
1471
|
}
|
|
1448
1472
|
|
|
1449
1473
|
async function emitSessionStartSystemMessage(
|
|
@@ -1451,10 +1475,10 @@ async function emitSessionStartSystemMessage(
|
|
|
1451
1475
|
instanceId: string,
|
|
1452
1476
|
sessionId: string,
|
|
1453
1477
|
emittedData: Record<string, unknown>,
|
|
1454
|
-
|
|
1478
|
+
adapter: Adapter,
|
|
1455
1479
|
recoveryBriefing = "",
|
|
1456
1480
|
): Promise<boolean> {
|
|
1457
|
-
const agentCoordBin =
|
|
1481
|
+
const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
|
|
1458
1482
|
let additionalContext = "";
|
|
1459
1483
|
if (existsSync(agentCoordBin)) {
|
|
1460
1484
|
// Sync-project so the heartbeat exists for downstream readers (peer table,
|
|
@@ -1463,10 +1487,10 @@ async function emitSessionStartSystemMessage(
|
|
|
1463
1487
|
// Stale-sweep dead peers before rendering peer table.
|
|
1464
1488
|
spawnSync(agentCoordBin, ["stale-sweep"], { encoding: "utf8", timeout: 3000 });
|
|
1465
1489
|
|
|
1466
|
-
// SESSION_START activity log line, fired across all
|
|
1490
|
+
// SESSION_START activity log line, fired across all adapters.
|
|
1467
1491
|
const model = (emittedData.model as string | undefined) ?? "unknown";
|
|
1468
1492
|
const source = (emittedData.source as string | undefined) ?? "startup";
|
|
1469
|
-
const platform =
|
|
1493
|
+
const platform = adapterPlatform(adapter);
|
|
1470
1494
|
spawnSync(
|
|
1471
1495
|
agentCoordBin,
|
|
1472
1496
|
[
|
|
@@ -1485,19 +1509,19 @@ async function emitSessionStartSystemMessage(
|
|
|
1485
1509
|
// The "You are agent-X." prefix in session-context renders unqualified by
|
|
1486
1510
|
// default (claude-code-style). For cursor/codex the bash dispatchers add
|
|
1487
1511
|
// a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
|
|
1488
|
-
if (
|
|
1512
|
+
if (adapter !== "claude-code") {
|
|
1489
1513
|
args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
|
|
1490
1514
|
}
|
|
1491
1515
|
const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
|
|
1492
1516
|
if (result.status === 0 && result.stdout) additionalContext = result.stdout.trim();
|
|
1493
1517
|
}
|
|
1494
1518
|
|
|
1495
|
-
// Effect (claude-code): merge the
|
|
1519
|
+
// Effect (claude-code): merge the journal recovery cue into the session-start
|
|
1496
1520
|
// context. Was a standalone additionalContext emission from the previous
|
|
1497
|
-
//
|
|
1521
|
+
// journal-on-start adapter; now that agent-hook is the single SessionStart
|
|
1498
1522
|
// entry, it folds in here.
|
|
1499
|
-
if (
|
|
1500
|
-
const cue =
|
|
1523
|
+
if (adapter === "claude-code") {
|
|
1524
|
+
const cue = journalRecoveryCue(coordRoot);
|
|
1501
1525
|
if (cue) additionalContext = [additionalContext, cue].filter(Boolean).join("\n\n");
|
|
1502
1526
|
}
|
|
1503
1527
|
if (recoveryBriefing) {
|
|
@@ -1505,8 +1529,8 @@ async function emitSessionStartSystemMessage(
|
|
|
1505
1529
|
}
|
|
1506
1530
|
if (!additionalContext) return false;
|
|
1507
1531
|
|
|
1508
|
-
const { emitContext } = await import("./
|
|
1509
|
-
emitContext(
|
|
1532
|
+
const { emitContext } = await import("./adapter/output.ts");
|
|
1533
|
+
emitContext(adapter, "SessionStart", additionalContext);
|
|
1510
1534
|
return true;
|
|
1511
1535
|
}
|
|
1512
1536
|
|
|
@@ -1514,7 +1538,7 @@ function completeRecoveryInjection(
|
|
|
1514
1538
|
coordRoot: string,
|
|
1515
1539
|
instanceId: string,
|
|
1516
1540
|
sessionId: string,
|
|
1517
|
-
|
|
1541
|
+
adapter: Adapter,
|
|
1518
1542
|
recovery: PreparedContextRecovery,
|
|
1519
1543
|
injectionEvent: "SessionStart" | "UserPromptSubmit",
|
|
1520
1544
|
): void {
|
|
@@ -1523,7 +1547,7 @@ function completeRecoveryInjection(
|
|
|
1523
1547
|
event_type: "context.recovery.injected",
|
|
1524
1548
|
instance_id: instanceId,
|
|
1525
1549
|
session_id: sessionId,
|
|
1526
|
-
|
|
1550
|
+
adapter,
|
|
1527
1551
|
data: {
|
|
1528
1552
|
capsule_id: recovery.capsule.capsule_id,
|
|
1529
1553
|
generation: recovery.capsule.generation,
|