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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { spawnSync } from "node:child_process";
|
|
11
11
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
12
|
-
import { dirname, resolve, sep } from "node:path";
|
|
12
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
13
13
|
|
|
14
14
|
// NOTE: kept dependency-free (node builtins only); this file is vendored verbatim into
|
|
15
15
|
// a downstream consumer, so it cannot import the coordEnv helper.
|
|
@@ -73,7 +73,7 @@ export interface Heartbeat {
|
|
|
73
73
|
last_tool_target?: string;
|
|
74
74
|
/** Free-form task/intent string set via `harn agents set-task`. Phase 2. */
|
|
75
75
|
task?: string;
|
|
76
|
-
/** UTC ISO-8601 timestamp when task was last set/cleared. Used by
|
|
76
|
+
/** UTC ISO-8601 timestamp when task was last set/cleared. Used by adapters without Stop enforcement to compute staleness. 2026-05-24. */
|
|
77
77
|
task_updated_at?: string | null;
|
|
78
78
|
/** Auto-generated per-turn summary written by the Stop hook via Haiku. 2026-05-23. */
|
|
79
79
|
turn_summary?: string | null;
|
|
@@ -86,57 +86,173 @@ export interface Heartbeat {
|
|
|
86
86
|
/**
|
|
87
87
|
* Resolve the monorepo root for coord-state purposes.
|
|
88
88
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* `<superproject>/.git/modules/<name>/` to recover the superproject for
|
|
92
|
-
* submodule worktrees. Without that fallback, a TS coord caller running
|
|
93
|
-
* inside a `harn worktree add-submodule` checkout would resolve a different
|
|
94
|
-
* `.harnery/` than the bash hooks and the two layers would silently diverge.
|
|
89
|
+
* Thin alias for `resolveCoordRoot()`; kept because it is the name the CLI
|
|
90
|
+
* command modules and the vendored downstream consumer already import.
|
|
95
91
|
*/
|
|
96
92
|
export function monorepoRoot(): string | null {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
return resolveCoordRoot();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* THE coordination-root resolution. Every surface — the hooks, the CLI's reads,
|
|
98
|
+
* and the CLI's canonical emits — resolves through this one function, because a
|
|
99
|
+
* root the two layers disagree about is a root that silently breaks the
|
|
100
|
+
* end-of-turn rules: the hook evaluates `state.status_checked` from the stream
|
|
101
|
+
* it reads, so an emit into a different `.harnery/events.ndjson` is invisible
|
|
102
|
+
* and rule 1/3 blocks a turn that did run `agents status`, with no sequence of
|
|
103
|
+
* CLI commands able to satisfy it.
|
|
104
|
+
*
|
|
105
|
+
* Precedence:
|
|
106
|
+
* 1. `HARNERY_COORD_ROOT_OVERRIDE` — explicit pin (tests, git hooks, and the
|
|
107
|
+
* root every coord-helper spawn pins for its child).
|
|
108
|
+
* 2. `CLAUDE_PROJECT_DIR` — the adapter stating which project it opened. Hook
|
|
109
|
+
* processes inherit the session's *shell* cwd, which follows `cd` into a
|
|
110
|
+
* subdirectory or submodule that may carry a `.harnery/` of its own (or
|
|
111
|
+
* none at all), so the adapter's own statement outranks the cwd walk.
|
|
112
|
+
* 3. The candidate root that already holds THIS session's heartbeat.
|
|
113
|
+
* 4. The nearest enclosing `.harnery/`, then a git-derived root.
|
|
114
|
+
*
|
|
115
|
+
* Step 3 is what makes CLI/hook disagreement structurally impossible rather
|
|
116
|
+
* than a coin flip. Choosing either root unconditionally is wrong in one
|
|
117
|
+
* direction each: preferring the git superproject strands a session whose
|
|
118
|
+
* adapter opened the submodule itself (its heartbeat lives in the submodule,
|
|
119
|
+
* so status/set-task wrote events the hook never read), while walking up from
|
|
120
|
+
* cwd alone strands the opposite case, a session opened on the superproject
|
|
121
|
+
* whose shell has cd'd into a submodule that carries its own `.harnery/`
|
|
122
|
+
* (regression-tested in tests/unit/coord-helper-root-pin.test.ts). The session's
|
|
123
|
+
* own heartbeat settles it: whichever root the hook registered this session in
|
|
124
|
+
* is the root the CLI must use, and the adapter-exported session id needed to
|
|
125
|
+
* recognize it is available to a plain tool-call subprocess even though
|
|
126
|
+
* `CLAUDE_PROJECT_DIR` is not.
|
|
127
|
+
*/
|
|
128
|
+
export function resolveCoordRoot(start: string = process.cwd()): string | null {
|
|
100
129
|
const rootOverride = process.env.HARNERY_COORD_ROOT_OVERRIDE;
|
|
101
|
-
if (rootOverride)
|
|
102
|
-
|
|
130
|
+
if (rootOverride) return rootOverride;
|
|
131
|
+
|
|
132
|
+
const projectDir = process.env.CLAUDE_PROJECT_DIR;
|
|
133
|
+
if (projectDir) {
|
|
134
|
+
const fromProject = nearestCoordRoot(projectDir);
|
|
135
|
+
if (fromProject) return fromProject;
|
|
103
136
|
}
|
|
104
137
|
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return sup.stdout.trim();
|
|
138
|
+
// Nearest-first, so the fallback at the end keeps the historical cwd-walk
|
|
139
|
+
// behavior for a shell that has no registered session at all.
|
|
140
|
+
const ancestors = ancestorCoordRoots(start);
|
|
141
|
+
for (const candidate of ancestors) {
|
|
142
|
+
if (rootKnowsSession(candidate)) return candidate;
|
|
111
143
|
}
|
|
112
144
|
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
145
|
+
// Only reached when no enclosing root knows this session: a submodule
|
|
146
|
+
// worktree (`harn worktree add-submodule`) has no ancestor relationship with
|
|
147
|
+
// the superproject that registered the session, so ask git for it.
|
|
148
|
+
const gitRoots = gitCoordRoots(start).filter((r) => !ancestors.includes(r));
|
|
149
|
+
for (const candidate of gitRoots) {
|
|
150
|
+
if (rootKnowsSession(candidate)) return candidate;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (ancestors.length > 0) return ancestors[0] as string;
|
|
154
|
+
if (gitRoots.length > 0) return gitRoots[0] as string;
|
|
155
|
+
// Nothing carries `.harnery/` yet. Hand back the enclosing checkout so a
|
|
156
|
+
// first run (`harn init`, agent-hook's session.start) has somewhere to
|
|
157
|
+
// create it.
|
|
158
|
+
return gitToplevel(start);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Nearest enclosing directory carrying `.harnery/`, or null. */
|
|
162
|
+
export function nearestCoordRoot(start: string): string | null {
|
|
163
|
+
return ancestorCoordRoots(start)[0] ?? null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Every enclosing directory carrying `.harnery/`, nearest first. */
|
|
167
|
+
function ancestorCoordRoots(start: string): string[] {
|
|
168
|
+
const found: string[] = [];
|
|
169
|
+
let dir = resolve(start);
|
|
170
|
+
while (true) {
|
|
171
|
+
if (existsSync(join(dir, ".harnery"))) found.push(dir);
|
|
172
|
+
const parent = dirname(dir);
|
|
173
|
+
if (parent === dir) break;
|
|
174
|
+
dir = parent;
|
|
127
175
|
}
|
|
176
|
+
return found;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Does this root's `.harnery/` already know the process asking?
|
|
181
|
+
*
|
|
182
|
+
* Two discriminators, both genuinely about *this* session: the adapter-exported
|
|
183
|
+
* session id matching a live heartbeat, and a pid-map row on our own ppid
|
|
184
|
+
* chain. Deliberately NOT the single-live-agent fallback that owner resolution
|
|
185
|
+
* ends with — a lone stranger in the wrong root is exactly how `whoami` came to
|
|
186
|
+
* report another agent's name and task as its own.
|
|
187
|
+
*/
|
|
188
|
+
export function rootKnowsSession(root: string): boolean {
|
|
189
|
+
if (resolveOwnerBySessionEnv(root)) return true;
|
|
190
|
+
return resolveOwnerByPidmap(root).owner !== null;
|
|
191
|
+
}
|
|
128
192
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Git-derived candidate roots for `start`, in preference order, filtered to
|
|
195
|
+
* those that actually carry `.harnery/`.
|
|
196
|
+
*
|
|
197
|
+
* Spawns run with `cwd: start` rather than the process cwd so an injected
|
|
198
|
+
* start dir resolves its own repository — without that, a call about some
|
|
199
|
+
* unrelated directory inherits this process's repo and can resolve a root that
|
|
200
|
+
* has nothing to do with the question asked.
|
|
201
|
+
*/
|
|
202
|
+
function gitCoordRoots(start: string): string[] {
|
|
203
|
+
const roots: string[] = [];
|
|
204
|
+
const push = (value: string | null) => {
|
|
205
|
+
if (value && existsSync(join(value, ".harnery")) && !roots.includes(value)) roots.push(value);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Superproject working tree (running from inside a submodule).
|
|
209
|
+
push(gitRevParse(start, "--show-superproject-working-tree"));
|
|
210
|
+
|
|
211
|
+
// `--git-common-dir` fallback for submodule worktrees: `git worktree add`
|
|
212
|
+
// inside a submodule produces a worktree whose superproject working tree is
|
|
213
|
+
// empty (the worktree has no submodule relationship of its own), but the
|
|
214
|
+
// common dir points at `<superproject>/.git/modules/<name>/`, so the
|
|
215
|
+
// superproject is recoverable by stripping that suffix.
|
|
216
|
+
const common = gitRevParse(start, "--git-common-dir");
|
|
217
|
+
if (common) {
|
|
218
|
+
const idx = common.indexOf("/.git/modules/");
|
|
219
|
+
if (idx !== -1) push(common.substring(0, idx));
|
|
135
220
|
}
|
|
136
221
|
|
|
137
|
-
|
|
222
|
+
// Top-level (regular checkout).
|
|
223
|
+
push(gitRevParse(start, "--show-toplevel"));
|
|
224
|
+
return roots;
|
|
138
225
|
}
|
|
139
226
|
|
|
227
|
+
function gitToplevel(start: string): string | null {
|
|
228
|
+
return gitRevParse(start, "--show-toplevel");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function gitRevParse(cwd: string, flag: string): string | null {
|
|
232
|
+
const key = `${cwd}${flag}`;
|
|
233
|
+
const cached = gitRevParseCache.get(key);
|
|
234
|
+
if (cached !== undefined) return cached;
|
|
235
|
+
let value: string | null = null;
|
|
236
|
+
try {
|
|
237
|
+
// `cwd` must exist or the spawn itself fails; callers pass paths that may
|
|
238
|
+
// not (a project dir from a stale env var), so treat any failure as "no
|
|
239
|
+
// answer" rather than letting it throw.
|
|
240
|
+
const r = spawnSync("git", ["rev-parse", flag], { encoding: "utf8", cwd });
|
|
241
|
+
if (r.status === 0) value = r.stdout.trim() || null;
|
|
242
|
+
} catch {
|
|
243
|
+
value = null;
|
|
244
|
+
}
|
|
245
|
+
gitRevParseCache.set(key, value);
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Memoized because git spawns are the expensive part of resolution and every
|
|
251
|
+
* CLI command resolves the root many times per invocation. Keyed by (cwd, flag);
|
|
252
|
+
* a repository's identity does not change under a running process.
|
|
253
|
+
*/
|
|
254
|
+
const gitRevParseCache = new Map<string, string | null>();
|
|
255
|
+
|
|
140
256
|
/** Parse owner from a pid-map row (`owner` or `owner\tplatform`). */
|
|
141
257
|
export function parsePidmapRowOwner(row: string): string {
|
|
142
258
|
const trimmed = row.trim();
|
|
@@ -146,9 +262,93 @@ export function parsePidmapRowOwner(row: string): string {
|
|
|
146
262
|
|
|
147
263
|
/** Parse platform from a pid-map row; legacy rows default to `claude_code`. */
|
|
148
264
|
export function parsePidmapRowPlatform(row: string): string {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
265
|
+
const platform = row.trim().split("\t")[1]?.trim();
|
|
266
|
+
return platform || "claude_code";
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Parse the start token from a pid-map row; rows written before it carry none. */
|
|
270
|
+
export function parsePidmapRowStartToken(row: string): string | undefined {
|
|
271
|
+
return row.trim().split("\t")[2]?.trim() || undefined;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Is the process now holding `pid` the one this row was written for?
|
|
276
|
+
*
|
|
277
|
+
* A pid is a number the OS re-issues, and quickly: a `pid_max` of 99999 against
|
|
278
|
+
* ~100 new processes a second recycles the whole space about every quarter
|
|
279
|
+
* hour. Believing a row past that point resolves this session to whichever
|
|
280
|
+
* agent last held the number — which is what made `whoami` report a stranger's
|
|
281
|
+
* name and files. The start token settles it, since two processes may share a
|
|
282
|
+
* pid but never a pid and a start instant.
|
|
283
|
+
*
|
|
284
|
+
* Deliberately inlined rather than imported from `state/proc-start.ts`: this
|
|
285
|
+
* file is vendored verbatim into a downstream consumer and stays on node
|
|
286
|
+
* builtins only. The token is a wire format shared with that module and with
|
|
287
|
+
* the host's commit guard, so the copies must agree byte for byte; exported so
|
|
288
|
+
* a test can hold this one against `processStartToken` and fail on drift.
|
|
289
|
+
*/
|
|
290
|
+
export function pidStartToken(pid: number): string | null {
|
|
291
|
+
const forced = process.env.HARNERY_PID_PROBE;
|
|
292
|
+
const useProcfs = forced === "procfs" || (forced !== "ps" && existsSync("/proc/self/stat"));
|
|
293
|
+
// One machine, one probe. Falling back to the other on a read failure would
|
|
294
|
+
// answer in the wrong dialect and read as a recycled pid.
|
|
295
|
+
if (useProcfs) {
|
|
296
|
+
try {
|
|
297
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
298
|
+
const afterComm = stat.slice(stat.lastIndexOf(") ") + 2);
|
|
299
|
+
// Fields after comm, 0-based: 0 is state (field 3), starttime (22) is 19.
|
|
300
|
+
const ticks = afterComm.split(" ")[19];
|
|
301
|
+
if (!ticks || !/^\d+$/.test(ticks)) return null;
|
|
302
|
+
// Ticks count from boot, so they repeat across reboots; the boot id scopes
|
|
303
|
+
// them. Rows written before it carry ticks alone and still compare.
|
|
304
|
+
let boot = "";
|
|
305
|
+
try {
|
|
306
|
+
const raw = readFileSync("/proc/sys/kernel/random/boot_id", "utf8")
|
|
307
|
+
.trim()
|
|
308
|
+
.replace(/-/g, "");
|
|
309
|
+
if (/^[0-9a-f]{8,}$/.test(raw)) boot = `${raw.slice(0, 8)}.`;
|
|
310
|
+
} catch {
|
|
311
|
+
/* unnamed boot: fall back to the tick-only shape */
|
|
312
|
+
}
|
|
313
|
+
return `l${boot}${ticks}`;
|
|
314
|
+
} catch {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
// TZ and locale are pinned because `ps` renders the date through them, and
|
|
320
|
+
// two callers with different environments must not disagree about one
|
|
321
|
+
// process.
|
|
322
|
+
const out = spawnSync("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
323
|
+
encoding: "utf8",
|
|
324
|
+
timeout: 2000,
|
|
325
|
+
env: { ...process.env, TZ: "UTC", LC_ALL: "C" },
|
|
326
|
+
});
|
|
327
|
+
if (out.status !== 0) return null;
|
|
328
|
+
const lstart = (out.stdout ?? "").split("\n")[0]?.trim().replace(/\s+/g, " ");
|
|
329
|
+
return lstart ? `p${lstart}` : null;
|
|
330
|
+
} catch {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function pidWasRecycled(pid: number, row: string): boolean {
|
|
336
|
+
const recorded = parsePidmapRowStartToken(row);
|
|
337
|
+
if (!recorded) return false; // pre-token row: unverifiable, behave as before
|
|
338
|
+
const current = pidStartToken(pid);
|
|
339
|
+
if (!current) return false;
|
|
340
|
+
if (current === recorded) return false;
|
|
341
|
+
// A row predating the boot segment recorded ticks alone; compare it on what
|
|
342
|
+
// it recorded rather than pruning every live row on the first upgraded run.
|
|
343
|
+
if (
|
|
344
|
+
recorded[0] === "l" &&
|
|
345
|
+
current[0] === "l" &&
|
|
346
|
+
recorded.includes(".") !== current.includes(".")
|
|
347
|
+
) {
|
|
348
|
+
const ticks = (t: string) => (t.includes(".") ? t.slice(t.indexOf(".") + 1) : t.slice(1));
|
|
349
|
+
return ticks(recorded) !== ticks(current);
|
|
350
|
+
}
|
|
351
|
+
return true;
|
|
152
352
|
}
|
|
153
353
|
|
|
154
354
|
function readPidmapRow(pidmapDir: string, pid: number): string | null {
|
|
@@ -197,9 +397,14 @@ export function resolveOwnerWithSource(): {
|
|
|
197
397
|
const root = monorepoRoot();
|
|
198
398
|
if (!root) return { owner: null, source: "none" };
|
|
199
399
|
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
400
|
+
// Every supported adapter exports its session id into the env of the
|
|
401
|
+
// subprocess it spawns for a tool call, and each heartbeat records the
|
|
402
|
+
// session id it was minted under. Matching the two names us outright, even
|
|
403
|
+
// with many live agents, so it goes ahead of the ppid walk rather than
|
|
404
|
+
// catching what the walk drops. Cursor needed this first because its
|
|
405
|
+
// Glass/Agents UI runs several chats under one node ancestor, making that row
|
|
406
|
+
// last-writer-wins; pid recycling generalises the same hazard to every
|
|
407
|
+
// adapter.
|
|
203
408
|
if (shouldPreferSessionEnv()) {
|
|
204
409
|
const bySession = resolveOwnerBySessionEnv(root);
|
|
205
410
|
if (bySession) {
|
|
@@ -207,6 +412,34 @@ export function resolveOwnerWithSource(): {
|
|
|
207
412
|
}
|
|
208
413
|
}
|
|
209
414
|
|
|
415
|
+
if (!existsSync(resolve(root, ".harnery", "pid-map"))) return { owner: null, source: "none" };
|
|
416
|
+
|
|
417
|
+
const byPidmap = resolveOwnerByPidmap(root);
|
|
418
|
+
if (byPidmap.owner) return byPidmap;
|
|
419
|
+
|
|
420
|
+
// Last resort: if exactly one agent is live in this coord root, it's
|
|
421
|
+
// unambiguously us — resolve to it. This is what lets the bare `agents
|
|
422
|
+
// status` / `set-task` the stop hook recommends work without a `--session-id`
|
|
423
|
+
// flag in the common single-agent case. With 0 or 2+ live agents it would be
|
|
424
|
+
// a guess, so we stay null and require the explicit flag.
|
|
425
|
+
const singleton = resolveSingleActiveOwner(root);
|
|
426
|
+
if (singleton) {
|
|
427
|
+
return { owner: singleton, source: "active_singleton" };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return { owner: null, source: "none" };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Walk our own ppid chain for a pid-map row in ONE given root.
|
|
435
|
+
*
|
|
436
|
+
* Root-parameterized (rather than resolving the root itself) so root resolution
|
|
437
|
+
* can use it as a discriminator without recursing back into itself.
|
|
438
|
+
*/
|
|
439
|
+
export function resolveOwnerByPidmap(root: string): {
|
|
440
|
+
owner: string | null;
|
|
441
|
+
source: "pidmap" | "pidmap_fallback" | "none";
|
|
442
|
+
} {
|
|
210
443
|
const pidmapDir = resolve(root, ".harnery", "pid-map");
|
|
211
444
|
if (!existsSync(pidmapDir)) return { owner: null, source: "none" };
|
|
212
445
|
|
|
@@ -217,7 +450,7 @@ export function resolveOwnerWithSource(): {
|
|
|
217
450
|
for (let hop = 0; hop < 20; hop++) {
|
|
218
451
|
if (pid === null) break;
|
|
219
452
|
const row = readPidmapRow(pidmapDir, pid);
|
|
220
|
-
if (row) {
|
|
453
|
+
if (row && !pidWasRecycled(pid, row)) {
|
|
221
454
|
const rowOwner = parsePidmapRowOwner(row);
|
|
222
455
|
const rowPlat = parsePidmapRowPlatform(row);
|
|
223
456
|
if (rowPlat === prefer) {
|
|
@@ -230,35 +463,12 @@ export function resolveOwnerWithSource(): {
|
|
|
230
463
|
if (fallbackOwner) {
|
|
231
464
|
return { owner: fallbackOwner, source: "pidmap_fallback" };
|
|
232
465
|
}
|
|
233
|
-
|
|
234
|
-
// The ppid walk found nothing (e.g. a Bash-tool subshell whose process tree
|
|
235
|
-
// doesn't climb back to the harness anchor). Before guessing, try the
|
|
236
|
-
// harness-provided session id from the environment: every supported harness
|
|
237
|
-
// exports its session id into the tool subprocess env, and each heartbeat
|
|
238
|
-
// records the `session_id` it was minted under. Matching the two resolves us
|
|
239
|
-
// unambiguously even with multiple live agents, which is the case the
|
|
240
|
-
// singleton fallback below cannot handle.
|
|
241
|
-
const bySession = resolveOwnerBySessionEnv(root);
|
|
242
|
-
if (bySession) {
|
|
243
|
-
return { owner: bySession, source: "session_env" };
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Last resort: if exactly one agent is live in this coord root, it's
|
|
247
|
-
// unambiguously us — resolve to it. This is what lets the bare `agents
|
|
248
|
-
// status` / `set-task` the stop hook recommends work without a `--session-id`
|
|
249
|
-
// flag in the common single-agent case. With 0 or 2+ live agents it would be
|
|
250
|
-
// a guess, so we stay null and require the explicit flag.
|
|
251
|
-
const singleton = resolveSingleActiveOwner(root);
|
|
252
|
-
if (singleton) {
|
|
253
|
-
return { owner: singleton, source: "active_singleton" };
|
|
254
|
-
}
|
|
255
|
-
|
|
256
466
|
return { owner: null, source: "none" };
|
|
257
467
|
}
|
|
258
468
|
|
|
259
469
|
/**
|
|
260
|
-
*
|
|
261
|
-
* supported
|
|
470
|
+
* Adapter-exported session-id environment variables, in precedence order. Each
|
|
471
|
+
* supported adapter propagates its session id into the env of the subprocess it
|
|
262
472
|
* spawns for a tool call (Claude Code's Bash tool, Cursor's terminal, Codex's
|
|
263
473
|
* shell). A coord CLI invoked as such a tool can therefore recover its own
|
|
264
474
|
* identity from the env even when the ppid walk misses.
|
|
@@ -274,7 +484,7 @@ const SESSION_ID_ENV_VARS = [
|
|
|
274
484
|
"CODEX_SESSION_ID",
|
|
275
485
|
] as const;
|
|
276
486
|
|
|
277
|
-
/** Read normalized candidates from the first non-empty
|
|
487
|
+
/** Read normalized candidates from the first non-empty adapter session-id env var. */
|
|
278
488
|
function sessionIdsFromEnv(): string[] {
|
|
279
489
|
for (const key of SESSION_ID_ENV_VARS) {
|
|
280
490
|
const v = process.env[key]?.trim();
|
|
@@ -287,19 +497,33 @@ function sessionIdsFromEnv(): string[] {
|
|
|
287
497
|
return [];
|
|
288
498
|
}
|
|
289
499
|
|
|
290
|
-
/** Read the first non-empty
|
|
500
|
+
/** Read the first non-empty adapter session-id env var, or null. */
|
|
291
501
|
function sessionIdFromEnv(): string | null {
|
|
292
502
|
return sessionIdsFromEnv()[0] ?? null;
|
|
293
503
|
}
|
|
294
504
|
|
|
505
|
+
/**
|
|
506
|
+
* Should the adapter-exported session id be consulted before the ppid walk?
|
|
507
|
+
*
|
|
508
|
+
* Yes, whenever one is exported. The env var is the adapter stating its own
|
|
509
|
+
* identity; the walk is a guess over a namespace the OS recycles. On a box with
|
|
510
|
+
* `pid_max` of 99999 and ~100 pids allocated per second the whole pid space
|
|
511
|
+
* turns over about every quarter hour, so a row written before that can name a
|
|
512
|
+
* pid some unrelated process now holds. Pruning cannot save the walk here:
|
|
513
|
+
* it removes rows whose pid is dead, and a recycled pid is alive. Letting a
|
|
514
|
+
* guess outrank a statement of fact is what made `agents whoami` report another
|
|
515
|
+
* agent's name and file list.
|
|
516
|
+
*
|
|
517
|
+
* This only reorders the two. Session-env resolution still requires a live
|
|
518
|
+
* heartbeat carrying that session id, so when it does not match, the walk runs
|
|
519
|
+
* exactly as before.
|
|
520
|
+
*/
|
|
295
521
|
function shouldPreferSessionEnv(): boolean {
|
|
296
|
-
|
|
297
|
-
const platform = process.env.HARNERY_AGENT_COORD_PLATFORM?.trim();
|
|
298
|
-
return process.env.CURSOR_AGENT === "1" || platform === "cursor";
|
|
522
|
+
return sessionIdFromEnv() !== null;
|
|
299
523
|
}
|
|
300
524
|
|
|
301
525
|
/**
|
|
302
|
-
* Resolve the owner by matching the
|
|
526
|
+
* Resolve the owner by matching the adapter session-id env var against the
|
|
303
527
|
* `session_id` of a live heartbeat in this coord root. Returns the matching
|
|
304
528
|
* `instance_id`, or null if there's no session-id env var or no live heartbeat
|
|
305
529
|
* carries it. "Live" reuses the same 10-minute freshness window the singleton
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* event/schema types).
|
|
6
6
|
*
|
|
7
7
|
* Phase 4: agent-coord uses this from CLI handlers (state.task_set,
|
|
8
|
-
* state.status_checked, state.
|
|
8
|
+
* state.status_checked, state.journal_append, council.*, decision.*,
|
|
9
9
|
* presence.*).
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -19,8 +19,9 @@ const STREAM_FILE = ".harnery/events.ndjson";
|
|
|
19
19
|
const LOCK_FILE = ".harnery/events.ndjson.lock";
|
|
20
20
|
const MAX_LINE_BYTES = 64 * 1024;
|
|
21
21
|
|
|
22
|
-
export type
|
|
23
|
-
|
|
22
|
+
export type { Adapter, Source } from "../../hooks/events/schema.ts";
|
|
23
|
+
|
|
24
|
+
import type { Adapter, Source } from "../../hooks/events/schema.ts";
|
|
24
25
|
|
|
25
26
|
export interface Envelope {
|
|
26
27
|
schema_version: typeof SCHEMA_VERSION;
|
|
@@ -32,7 +33,7 @@ export interface Envelope {
|
|
|
32
33
|
parent_session_id?: string;
|
|
33
34
|
turn_id?: string;
|
|
34
35
|
parent_turn_id?: string;
|
|
35
|
-
|
|
36
|
+
adapter: Adapter;
|
|
36
37
|
source: Source;
|
|
37
38
|
data: Record<string, unknown>;
|
|
38
39
|
}
|
|
@@ -41,7 +42,7 @@ export interface EmitInput {
|
|
|
41
42
|
event_type: string;
|
|
42
43
|
instance_id: string;
|
|
43
44
|
session_id: string;
|
|
44
|
-
|
|
45
|
+
adapter: Adapter;
|
|
45
46
|
source?: Source;
|
|
46
47
|
parent_session_id?: string;
|
|
47
48
|
turn_id?: string;
|
|
@@ -61,7 +62,7 @@ export function buildEnvelope(input: EmitInput): Envelope {
|
|
|
61
62
|
parent_session_id: input.parent_session_id,
|
|
62
63
|
turn_id: input.turn_id,
|
|
63
64
|
parent_turn_id: input.parent_turn_id,
|
|
64
|
-
|
|
65
|
+
adapter: input.adapter,
|
|
65
66
|
source: input.source ?? "agent-coord",
|
|
66
67
|
data: input.data,
|
|
67
68
|
};
|
package/src/core/agents/paths.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dirname, join, resolve } from "node:path";
|
|
1
|
+
import { resolveCoordRoot } from "./coord-client.ts";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Resolve the coordination root.
|
|
5
|
+
*
|
|
6
|
+
* Delegates to `resolveCoordRoot()`. This was a third, independent copy of the
|
|
7
|
+
* walk-up — the kind of duplication that let the hooks and the CLI drift onto
|
|
8
|
+
* different roots in the first place; keep resolution in one place.
|
|
7
9
|
*/
|
|
8
10
|
export function findCoordRoot(start: string = process.cwd()): string | null {
|
|
9
|
-
|
|
10
|
-
while (true) {
|
|
11
|
-
if (existsSync(join(dir, ".harnery"))) return dir;
|
|
12
|
-
const parent = dirname(dir);
|
|
13
|
-
if (parent === dir) return null;
|
|
14
|
-
dir = parent;
|
|
15
|
-
}
|
|
11
|
+
return resolveCoordRoot(start);
|
|
16
12
|
}
|