harnery 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 +61 -60
- 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/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 +193 -23
- 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 +4 -4
- package/dist/core/agents/canonical-emit.js +5 -5
- 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-client.d.ts +21 -2
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +122 -27
- 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/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 +175 -151
- 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 +17 -17
- 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 +3 -3
- package/dist/core/hooks/resolve/coord-root.js +4 -4
- 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/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 +71 -64
- 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/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 +237 -34
- 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 +6 -6
- 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-client.ts +119 -28
- package/src/core/agents/events/consume.ts +1 -1
- package/src/core/agents/events/emit.ts +7 -6
- 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 +185 -162
- package/src/core/hooks/effects/image-capture.ts +6 -6
- package/src/core/hooks/effects/index.ts +17 -17
- 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 +4 -4
- 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/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
|
@@ -67,7 +67,7 @@ function seed(ev, coordRoot) {
|
|
|
67
67
|
const hb = {
|
|
68
68
|
instance_id: ev.instance_id,
|
|
69
69
|
session_id: ev.session_id,
|
|
70
|
-
|
|
70
|
+
adapter: ev.adapter,
|
|
71
71
|
last_heartbeat: ev.ts,
|
|
72
72
|
last_event_id: ev.event_id,
|
|
73
73
|
events_applied: 0,
|
|
@@ -112,12 +112,12 @@ function apply(hb, ev, coordRoot) {
|
|
|
112
112
|
switch (ev.event_type) {
|
|
113
113
|
case "session.start":
|
|
114
114
|
hb.started_at = pickStr(d, "started_at") ?? ev.ts;
|
|
115
|
-
hb.
|
|
115
|
+
hb.adapter = ev.adapter;
|
|
116
116
|
{
|
|
117
117
|
const model = pickStr(d, "model");
|
|
118
118
|
if (model)
|
|
119
119
|
hb.model = model;
|
|
120
|
-
const platform = pickStr(d, "platform") ??
|
|
120
|
+
const platform = pickStr(d, "platform") ?? adapterToPlatform(ev.adapter);
|
|
121
121
|
hb.platform = platform;
|
|
122
122
|
const name = pickStr(d, "name");
|
|
123
123
|
if (name)
|
|
@@ -141,6 +141,9 @@ function apply(hb, ev, coordRoot) {
|
|
|
141
141
|
const workflowRunId = pickStr(d, "workflow_run_id");
|
|
142
142
|
if (workflowRunId)
|
|
143
143
|
hb.workflow_run_id = workflowRunId;
|
|
144
|
+
const workflowAgentId = pickStr(d, "workflow_agent_id");
|
|
145
|
+
if (workflowAgentId)
|
|
146
|
+
hb.workflow_agent_id = workflowAgentId;
|
|
144
147
|
if (!hb.files_touched)
|
|
145
148
|
hb.files_touched = [];
|
|
146
149
|
}
|
|
@@ -164,7 +167,7 @@ function apply(hb, ev, coordRoot) {
|
|
|
164
167
|
hb.started_at = ev.ts;
|
|
165
168
|
if (!hb.files_touched)
|
|
166
169
|
hb.files_touched = [];
|
|
167
|
-
hb.platform =
|
|
170
|
+
hb.platform = adapterToPlatform(ev.adapter);
|
|
168
171
|
break;
|
|
169
172
|
}
|
|
170
173
|
case "subagent.stop":
|
|
@@ -183,7 +186,7 @@ function apply(hb, ev, coordRoot) {
|
|
|
183
186
|
hb.turn_summary = summary;
|
|
184
187
|
hb.turn_summary_updated_at = ev.ts;
|
|
185
188
|
}
|
|
186
|
-
// Backfill model for
|
|
189
|
+
// Backfill model for adapters that omit it at session.start (Claude
|
|
187
190
|
// Code). The Stop hook resolves it from the transcript by this point;
|
|
188
191
|
// only set when present so we never clobber a known model.
|
|
189
192
|
const model = pickStr(d, "model");
|
|
@@ -270,14 +273,14 @@ function apply(hb, ev, coordRoot) {
|
|
|
270
273
|
}
|
|
271
274
|
}
|
|
272
275
|
}
|
|
273
|
-
function
|
|
274
|
-
if (
|
|
276
|
+
function adapterToPlatform(adapter) {
|
|
277
|
+
if (adapter === "claude-code")
|
|
275
278
|
return "claude_code";
|
|
276
|
-
if (
|
|
279
|
+
if (adapter === "cursor")
|
|
277
280
|
return "cursor";
|
|
278
|
-
if (
|
|
281
|
+
if (adapter === "codex")
|
|
279
282
|
return "codex";
|
|
280
|
-
return
|
|
283
|
+
return adapter;
|
|
281
284
|
}
|
|
282
285
|
function extractFilePath(data) {
|
|
283
286
|
const raw = data.tool_input;
|
|
@@ -411,6 +414,7 @@ function writeHeartbeat(coordRoot, instanceId, hb) {
|
|
|
411
414
|
setIfDefined(merged, "agent_id", hb.agent_id);
|
|
412
415
|
setIfDefined(merged, "subagent_call_id", hb.subagent_call_id);
|
|
413
416
|
setIfDefined(merged, "workflow_run_id", hb.workflow_run_id);
|
|
417
|
+
setIfDefined(merged, "workflow_agent_id", hb.workflow_agent_id);
|
|
414
418
|
setIfDefined(merged, "model", hb.model);
|
|
415
419
|
setIfDefined(merged, "platform", hb.platform);
|
|
416
420
|
setIfDefined(merged, "started_at", hb.started_at);
|
|
@@ -69,7 +69,7 @@ export interface GroupUnclaimHit {
|
|
|
69
69
|
export declare function groupUnclaim(coordRoot: string, groupId: string, path: string): GroupUnclaimHit[];
|
|
70
70
|
export declare function killHeartbeat(coordRoot: string, instanceId: string): boolean;
|
|
71
71
|
export declare function healPidmap(coordRoot: string, instanceId: string, pid: number): void;
|
|
72
|
-
export declare function healHeartbeat(coordRoot: string, instanceId: string, sessionId?: string, model?: string,
|
|
72
|
+
export declare function healHeartbeat(coordRoot: string, instanceId: string, sessionId?: string, model?: string, adapter?: string): Heartbeat | null;
|
|
73
73
|
/**
|
|
74
74
|
* Stamp the heartbeat with the most-recent tool name + target. Written from
|
|
75
75
|
* the post-tool-use hook.
|
|
@@ -15,23 +15,23 @@
|
|
|
15
15
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
17
|
import { emit } from "../events/emit.js";
|
|
18
|
-
/** Inline
|
|
18
|
+
/** Inline adapter normalizer (mirrors canonical-emit.normalizeAdapter), kept
|
|
19
19
|
* local so the writer's only cross-module dep is the in-process emit(). */
|
|
20
|
-
function
|
|
20
|
+
function adapterOf(platform) {
|
|
21
21
|
if (platform === "cursor")
|
|
22
22
|
return "cursor";
|
|
23
23
|
if (platform === "codex")
|
|
24
24
|
return "codex";
|
|
25
25
|
return "claude-code";
|
|
26
26
|
}
|
|
27
|
-
/** Inverse of
|
|
27
|
+
/** Inverse of adapterOf: maps the canonical adapter ("claude-code"/"cursor"/
|
|
28
28
|
* "codex") to the legacy underscore platform label stored on the heartbeat.
|
|
29
|
-
* Mirrors heartbeat-projector.
|
|
29
|
+
* Mirrors heartbeat-projector.adapterToPlatform so a healed heartbeat carries
|
|
30
30
|
* the same platform value projection would have written. */
|
|
31
|
-
function
|
|
32
|
-
if (
|
|
31
|
+
function adapterToPlatform(adapter) {
|
|
32
|
+
if (adapter === "cursor")
|
|
33
33
|
return "cursor";
|
|
34
|
-
if (
|
|
34
|
+
if (adapter === "codex")
|
|
35
35
|
return "codex";
|
|
36
36
|
return "claude_code";
|
|
37
37
|
}
|
|
@@ -49,7 +49,7 @@ function emitHealthHeal(coordRoot, type, instanceId, hb, data) {
|
|
|
49
49
|
event_type: type,
|
|
50
50
|
instance_id: instanceId,
|
|
51
51
|
session_id: hb?.session_id ?? instanceId,
|
|
52
|
-
|
|
52
|
+
adapter: adapterOf(hb?.platform),
|
|
53
53
|
source: "agent-coord",
|
|
54
54
|
data,
|
|
55
55
|
});
|
|
@@ -226,7 +226,7 @@ export function healPidmap(coordRoot, instanceId, pid) {
|
|
|
226
226
|
...(existingOwner ? { prior: existingOwner.slice(0, 8) } : {}),
|
|
227
227
|
});
|
|
228
228
|
}
|
|
229
|
-
export function healHeartbeat(coordRoot, instanceId, sessionId, model,
|
|
229
|
+
export function healHeartbeat(coordRoot, instanceId, sessionId, model, adapter) {
|
|
230
230
|
const path = heartbeatPath(coordRoot, instanceId);
|
|
231
231
|
if (existsSync(path)) {
|
|
232
232
|
// Already alive, return it.
|
|
@@ -264,11 +264,11 @@ export function healHeartbeat(coordRoot, instanceId, sessionId, model, harness)
|
|
|
264
264
|
started_at: now,
|
|
265
265
|
last_heartbeat: now,
|
|
266
266
|
files_touched: [],
|
|
267
|
-
// Default to claude_code only when the caller can't tell us the
|
|
267
|
+
// Default to claude_code only when the caller can't tell us the adapter
|
|
268
268
|
// (e.g. manual `harn agents heal`). The live tool.pre_use heal threads the
|
|
269
|
-
// detected
|
|
269
|
+
// detected adapter so a pruned Cursor/Codex heartbeat is recreated with
|
|
270
270
|
// the correct platform instead of being mislabeled claude_code.
|
|
271
|
-
platform:
|
|
271
|
+
platform: adapterToPlatform(adapter),
|
|
272
272
|
};
|
|
273
273
|
atomicWrite(path, JSON.stringify(hb, null, 2));
|
|
274
274
|
// Write-only telemetry: only the actual-recreate branch reaches here (the
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `.harnery/
|
|
2
|
+
* Journal mutations: append-only timestamped transcript at
|
|
3
|
+
* `.harnery/journal/<instance_id>.md`.
|
|
4
4
|
*
|
|
5
5
|
* Used by the agents-coord web UI route handlers for both operator-nudge
|
|
6
6
|
* appends and inline edits.
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
* Append a timestamped entry to the owner's
|
|
9
|
+
* Append a timestamped entry to the owner's journal.
|
|
10
10
|
* Returns true on success, false if validation failed.
|
|
11
11
|
*/
|
|
12
|
-
export declare function
|
|
12
|
+
export declare function appendJournal(coordRoot: string, instanceId: string, category: string, body: string): {
|
|
13
13
|
ok: boolean;
|
|
14
14
|
reason?: string;
|
|
15
15
|
path?: string;
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
|
-
* Replace the
|
|
18
|
+
* Replace the journal with new body, archiving prior contents and
|
|
19
19
|
* appending an "(edited via UI by the operator)" audit-marker note.
|
|
20
20
|
*/
|
|
21
|
-
export declare function
|
|
21
|
+
export declare function editJournal(coordRoot: string, instanceId: string, newBody: string, summary: string): {
|
|
22
22
|
ok: boolean;
|
|
23
23
|
reason?: string;
|
|
24
24
|
archivePath?: string;
|
|
25
25
|
path?: string;
|
|
26
26
|
};
|
|
27
|
-
//# sourceMappingURL=
|
|
27
|
+
//# sourceMappingURL=journal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/journal.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2CH;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAqBjD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CA8BvE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `.harnery/
|
|
2
|
+
* Journal mutations: append-only timestamped transcript at
|
|
3
|
+
* `.harnery/journal/<instance_id>.md`.
|
|
4
4
|
*
|
|
5
5
|
* Used by the agents-coord web UI route handlers for both operator-nudge
|
|
6
6
|
* appends and inline edits.
|
|
@@ -20,11 +20,11 @@ const APPEND_BODY_CAP = 4096;
|
|
|
20
20
|
function nowIsoSeconds() {
|
|
21
21
|
return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
22
22
|
}
|
|
23
|
-
function
|
|
24
|
-
return join(coordRoot, ".harnery", "
|
|
23
|
+
function journalPath(coordRoot, instanceId) {
|
|
24
|
+
return join(coordRoot, ".harnery", "journal", `${instanceId}.md`);
|
|
25
25
|
}
|
|
26
26
|
function archivePath(coordRoot, instanceId, suffix) {
|
|
27
|
-
return join(coordRoot, ".harnery", "
|
|
27
|
+
return join(coordRoot, ".harnery", "journal", "archived", `${instanceId}-${suffix}.md`);
|
|
28
28
|
}
|
|
29
29
|
function atomicWriteText(path, content) {
|
|
30
30
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -33,10 +33,10 @@ function atomicWriteText(path, content) {
|
|
|
33
33
|
renameSync(tmp, path);
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
* Append a timestamped entry to the owner's
|
|
36
|
+
* Append a timestamped entry to the owner's journal.
|
|
37
37
|
* Returns true on success, false if validation failed.
|
|
38
38
|
*/
|
|
39
|
-
export function
|
|
39
|
+
export function appendJournal(coordRoot, instanceId, category, body) {
|
|
40
40
|
if (!ALLOWED_CATEGORIES.has(category)) {
|
|
41
41
|
return { ok: false, reason: `invalid category "${category}"` };
|
|
42
42
|
}
|
|
@@ -44,7 +44,7 @@ export function appendScratch(coordRoot, instanceId, category, body) {
|
|
|
44
44
|
return { ok: false, reason: "body required" };
|
|
45
45
|
}
|
|
46
46
|
const trimmedBody = body.length > APPEND_BODY_CAP ? `${body.slice(0, APPEND_BODY_CAP - 3)}...` : body;
|
|
47
|
-
const target =
|
|
47
|
+
const target = journalPath(coordRoot, instanceId);
|
|
48
48
|
const ts = nowIsoSeconds();
|
|
49
49
|
let prior = "";
|
|
50
50
|
if (existsSync(target)) {
|
|
@@ -57,14 +57,14 @@ export function appendScratch(coordRoot, instanceId, category, body) {
|
|
|
57
57
|
return { ok: true, path: target };
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
* Replace the
|
|
60
|
+
* Replace the journal with new body, archiving prior contents and
|
|
61
61
|
* appending an "(edited via UI by the operator)" audit-marker note.
|
|
62
62
|
*/
|
|
63
|
-
export function
|
|
63
|
+
export function editJournal(coordRoot, instanceId, newBody, summary) {
|
|
64
64
|
if (typeof newBody !== "string") {
|
|
65
65
|
return { ok: false, reason: "newBody required" };
|
|
66
66
|
}
|
|
67
|
-
const target =
|
|
67
|
+
const target = journalPath(coordRoot, instanceId);
|
|
68
68
|
const ts = nowIsoSeconds();
|
|
69
69
|
const archiveSuffix = `pre-ui-${ts.replace(/:/g, "-")}`;
|
|
70
70
|
const archive = archivePath(coordRoot, instanceId, archiveSuffix);
|
|
@@ -84,7 +84,7 @@ export function editScratchpad(coordRoot, instanceId, newBody, summary) {
|
|
|
84
84
|
const summaryText = summary && summary.length > 0 ? summary : "(no summary)";
|
|
85
85
|
const auditMarker = `## [${ts}] note (edited via UI by the operator)\n` +
|
|
86
86
|
`${summaryText}\n` +
|
|
87
|
-
`Pre-edit archived at .harnery/
|
|
87
|
+
`Pre-edit archived at .harnery/journal/archived/${instanceId}-${archiveSuffix}.md\n\n`;
|
|
88
88
|
atomicWriteText(target, prior + auditMarker + newBody + (newBody.endsWith("\n") ? "" : "\n"));
|
|
89
89
|
return { ok: true, archivePath: archive, path: target };
|
|
90
90
|
}
|
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pid-map writer + liveness helpers.
|
|
3
3
|
*
|
|
4
|
-
* Per-
|
|
5
|
-
* `<instance_id>\t<platform>`. `harn agents whoami` walks ppid up
|
|
6
|
-
* for a matching entry (preferring the
|
|
4
|
+
* Per-adapter pid-map at `.harnery/pid-map/<pid>` containing
|
|
5
|
+
* `<instance_id>\t<platform>\t<start_token>`. `harn agents whoami` walks ppid up
|
|
6
|
+
* 20 hops looking for a matching entry (preferring the adapter, falling back to
|
|
7
|
+
* any platform).
|
|
8
|
+
*
|
|
9
|
+
* The start token pins each row to one *run* of that pid, because a pid on its
|
|
10
|
+
* own is a number the OS re-issues — see `proc-start.ts` for how fast. Rows
|
|
11
|
+
* written before tokens existed carry two fields and read as unverified, which
|
|
12
|
+
* behaves exactly as they always did.
|
|
7
13
|
*
|
|
8
14
|
* Atomic temp+rename. Idempotent: re-writing the same row is a no-op.
|
|
9
15
|
*/
|
|
16
|
+
/** Split a row into its fields, tolerating the pre-token two-field shape. */
|
|
17
|
+
export declare function parsePidmapRow(row: string): {
|
|
18
|
+
instanceId: string;
|
|
19
|
+
platform: string;
|
|
20
|
+
startToken: string | undefined;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Delete every pid-map row that no longer names its process — exited, or its
|
|
24
|
+
* pid re-issued to something else. Returns the number removed.
|
|
25
|
+
*/
|
|
26
|
+
export declare function prunePidmapDeadRows(coordRoot: string): number;
|
|
10
27
|
export declare function writePidmapRow(coordRoot: string, pid: number, instanceId: string, platform: string): void;
|
|
11
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* True when any pid-map row for `instanceId` still belongs to a live process.
|
|
30
|
+
*
|
|
31
|
+
* Callers spend this answer on consequential things — whether a commit guard
|
|
32
|
+
* treats a heartbeat as a live peer, whether a name can be reclaimed from an
|
|
33
|
+
* abandoned session — so a re-issued pid must not read as that agent still
|
|
34
|
+
* running.
|
|
35
|
+
*/
|
|
12
36
|
export declare function instanceHasLivePid(coordRoot: string, instanceId: string): boolean;
|
|
13
37
|
/** Drop every pid-map row owned by `instanceId`. Returns how many files were removed. */
|
|
14
38
|
export declare function removePidmapRowsForInstance(coordRoot: string, instanceId: string): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pidmap.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/pidmap.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pidmap.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/pidmap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAyBH,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAGA;AAwCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA8B7D;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,IAAI,CA6BN;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAmBjF;AAED,yFAAyF;AACzF,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAoBzF"}
|
|
@@ -1,23 +1,124 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pid-map writer + liveness helpers.
|
|
3
3
|
*
|
|
4
|
-
* Per-
|
|
5
|
-
* `<instance_id>\t<platform>`. `harn agents whoami` walks ppid up
|
|
6
|
-
* for a matching entry (preferring the
|
|
4
|
+
* Per-adapter pid-map at `.harnery/pid-map/<pid>` containing
|
|
5
|
+
* `<instance_id>\t<platform>\t<start_token>`. `harn agents whoami` walks ppid up
|
|
6
|
+
* 20 hops looking for a matching entry (preferring the adapter, falling back to
|
|
7
|
+
* any platform).
|
|
8
|
+
*
|
|
9
|
+
* The start token pins each row to one *run* of that pid, because a pid on its
|
|
10
|
+
* own is a number the OS re-issues — see `proc-start.ts` for how fast. Rows
|
|
11
|
+
* written before tokens existed carry two fields and read as unverified, which
|
|
12
|
+
* behaves exactly as they always did.
|
|
7
13
|
*
|
|
8
14
|
* Atomic temp+rename. Idempotent: re-writing the same row is a no-op.
|
|
9
15
|
*/
|
|
10
16
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
11
17
|
import { dirname, join } from "node:path";
|
|
18
|
+
import { checkPidToken, processStartToken } from "./proc-start.js";
|
|
19
|
+
/**
|
|
20
|
+
* Row count that triggers a dead-row sweep on the next write.
|
|
21
|
+
*
|
|
22
|
+
* Rows are written per hook shell, and those shells exit immediately, so an
|
|
23
|
+
* unattended map only grows. One repo was observed holding 512 rows of which 510
|
|
24
|
+
* were dead. That matters beyond disk: pids get recycled, so a stale row whose
|
|
25
|
+
* number is later reused makes an identity walk resolve to a long-gone agent,
|
|
26
|
+
* and `agents whoami` starts reporting somebody else's session.
|
|
27
|
+
*/
|
|
28
|
+
const PRUNE_AT_ROWS = 200;
|
|
29
|
+
/** Split a row into its fields, tolerating the pre-token two-field shape. */
|
|
30
|
+
export function parsePidmapRow(row) {
|
|
31
|
+
const [instanceId = "", platform = "", startToken = ""] = row.trim().split("\t");
|
|
32
|
+
return { instanceId, platform, startToken: startToken || undefined };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Is this row still about the process it was written for?
|
|
36
|
+
*
|
|
37
|
+
* Two ways to fail. The pid may have exited, which a liveness probe catches.
|
|
38
|
+
* Or the pid may have been re-issued to an unrelated process, which a liveness
|
|
39
|
+
* probe cannot catch — that pid is alive — and which the start token does.
|
|
40
|
+
* Getting the second one wrong resolves an identity walk to a long-gone agent
|
|
41
|
+
* and keeps a dead agent's heartbeat looking live, so the check belongs
|
|
42
|
+
* everywhere a row is believed, not only where it is swept.
|
|
43
|
+
*/
|
|
44
|
+
function rowStillNamesItsProcess(pid, startToken) {
|
|
45
|
+
if (!pidIsAlive(pid))
|
|
46
|
+
return false;
|
|
47
|
+
return checkPidToken(pid, startToken) !== "mismatch";
|
|
48
|
+
}
|
|
12
49
|
function atomicWrite(path, content) {
|
|
13
50
|
mkdirSync(dirname(path), { recursive: true });
|
|
14
51
|
const tmp = `${path}.tmp.${process.pid}`;
|
|
15
52
|
writeFileSync(tmp, content, "utf8");
|
|
16
53
|
renameSync(tmp, path);
|
|
17
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Is this pid a running process?
|
|
57
|
+
*
|
|
58
|
+
* `EPERM` means the process exists but belongs to another user, which is still
|
|
59
|
+
* alive. Treating every failure as "dead" would drop a live foreign row and
|
|
60
|
+
* weaken the checks that depend on knowing a claim is anchored.
|
|
61
|
+
*/
|
|
62
|
+
function pidIsAlive(pid) {
|
|
63
|
+
try {
|
|
64
|
+
process.kill(pid, 0); // signal 0 = liveness probe
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
return error.code === "EPERM";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Delete every pid-map row that no longer names its process — exited, or its
|
|
73
|
+
* pid re-issued to something else. Returns the number removed.
|
|
74
|
+
*/
|
|
75
|
+
export function prunePidmapDeadRows(coordRoot) {
|
|
76
|
+
const dir = join(coordRoot, ".harnery", "pid-map");
|
|
77
|
+
if (!existsSync(dir))
|
|
78
|
+
return 0;
|
|
79
|
+
let removed = 0;
|
|
80
|
+
let names;
|
|
81
|
+
try {
|
|
82
|
+
names = readdirSync(dir);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
for (const f of names) {
|
|
88
|
+
const pid = Number.parseInt(f, 10);
|
|
89
|
+
// Only touch files whose whole name is a pid, so a stray note or a
|
|
90
|
+
// half-written `.tmp.<pid>` file is left where it is.
|
|
91
|
+
if (!Number.isFinite(pid) || String(pid) !== f)
|
|
92
|
+
continue;
|
|
93
|
+
let token;
|
|
94
|
+
try {
|
|
95
|
+
token = parsePidmapRow(readFileSync(join(dir, f), "utf8")).startToken;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* unreadable row: fall back to the liveness check alone */
|
|
99
|
+
}
|
|
100
|
+
if (rowStillNamesItsProcess(pid, token))
|
|
101
|
+
continue;
|
|
102
|
+
try {
|
|
103
|
+
unlinkSync(join(dir, f));
|
|
104
|
+
removed += 1;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
/* best-effort cleanup */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return removed;
|
|
111
|
+
}
|
|
18
112
|
export function writePidmapRow(coordRoot, pid, instanceId, platform) {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
113
|
+
const dir = join(coordRoot, ".harnery", "pid-map");
|
|
114
|
+
const path = join(dir, String(pid));
|
|
115
|
+
// Stamped at write time, when the pid provably belongs to the process we
|
|
116
|
+
// mean. A platform that will not say gets no third field rather than an empty
|
|
117
|
+
// one, so an unverifiable row stays byte-identical to what came before.
|
|
118
|
+
const startToken = processStartToken(pid);
|
|
119
|
+
const row = startToken
|
|
120
|
+
? `${instanceId}\t${platform}\t${startToken}`
|
|
121
|
+
: `${instanceId}\t${platform}`;
|
|
21
122
|
// Read-then-write idempotency: skip the rename churn when already current.
|
|
22
123
|
if (existsSync(path)) {
|
|
23
124
|
try {
|
|
@@ -28,9 +129,27 @@ export function writePidmapRow(coordRoot, pid, instanceId, platform) {
|
|
|
28
129
|
/* fall through to write */
|
|
29
130
|
}
|
|
30
131
|
}
|
|
132
|
+
// Sweep on the way past the threshold. Writes are the only event guaranteed
|
|
133
|
+
// to happen while agents are active, so hanging the sweep here keeps the map
|
|
134
|
+
// bounded without a scheduled job, and the common write stays a single stat.
|
|
135
|
+
try {
|
|
136
|
+
if (existsSync(dir) && readdirSync(dir).length > PRUNE_AT_ROWS) {
|
|
137
|
+
prunePidmapDeadRows(coordRoot);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
/* pruning is best-effort; never fail a write over it */
|
|
142
|
+
}
|
|
31
143
|
atomicWrite(path, row);
|
|
32
144
|
}
|
|
33
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* True when any pid-map row for `instanceId` still belongs to a live process.
|
|
147
|
+
*
|
|
148
|
+
* Callers spend this answer on consequential things — whether a commit guard
|
|
149
|
+
* treats a heartbeat as a live peer, whether a name can be reclaimed from an
|
|
150
|
+
* abandoned session — so a re-issued pid must not read as that agent still
|
|
151
|
+
* running.
|
|
152
|
+
*/
|
|
34
153
|
export function instanceHasLivePid(coordRoot, instanceId) {
|
|
35
154
|
const dir = join(coordRoot, ".harnery", "pid-map");
|
|
36
155
|
if (!existsSync(dir))
|
|
@@ -43,19 +162,16 @@ export function instanceHasLivePid(coordRoot, instanceId) {
|
|
|
43
162
|
catch {
|
|
44
163
|
continue;
|
|
45
164
|
}
|
|
46
|
-
const owner = row
|
|
165
|
+
const { instanceId: owner, startToken } = parsePidmapRow(row);
|
|
47
166
|
if (owner !== instanceId)
|
|
48
167
|
continue;
|
|
49
168
|
const pid = Number.parseInt(f, 10);
|
|
50
169
|
if (!Number.isFinite(pid))
|
|
51
170
|
continue;
|
|
52
|
-
|
|
53
|
-
|
|
171
|
+
// Rows that no longer name their process keep the scan going; one that does
|
|
172
|
+
// settles it.
|
|
173
|
+
if (rowStillNamesItsProcess(pid, startToken))
|
|
54
174
|
return true;
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// ESRCH (no such process): pid-map entry is stale, keep scanning
|
|
58
|
-
}
|
|
59
175
|
}
|
|
60
176
|
return false;
|
|
61
177
|
}
|
|
@@ -73,8 +189,7 @@ export function removePidmapRowsForInstance(coordRoot, instanceId) {
|
|
|
73
189
|
catch {
|
|
74
190
|
continue;
|
|
75
191
|
}
|
|
76
|
-
|
|
77
|
-
if (owner !== instanceId)
|
|
192
|
+
if (parsePidmapRow(row).instanceId !== instanceId)
|
|
78
193
|
continue;
|
|
79
194
|
try {
|
|
80
195
|
unlinkSync(join(dir, f));
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process start-token probe: the "is this still the same process?" half of pid
|
|
3
|
+
* identity.
|
|
4
|
+
*
|
|
5
|
+
* A pid alone does not identify a process. Operating systems hand numbers back
|
|
6
|
+
* out, and faster than intuition suggests: one development machine runs a
|
|
7
|
+
* `pid_max` of 99999 against roughly 100 new processes a second, turning the
|
|
8
|
+
* whole space over about every quarter hour. Anything that remembers a pid for
|
|
9
|
+
* longer than that is remembering a number, not a process.
|
|
10
|
+
*
|
|
11
|
+
* A start token pins the pair down. Two processes can share a pid but never a
|
|
12
|
+
* pid *and* a start instant, so a recorded `(pid, token)` either still names the
|
|
13
|
+
* process that was recorded or plainly does not.
|
|
14
|
+
*
|
|
15
|
+
* The token is opaque on purpose. Callers compare it for equality and nothing
|
|
16
|
+
* else, which is why no clock arithmetic appears here: Linux reports ticks since
|
|
17
|
+
* boot and BSD reports a formatted date, and neither needs converting to be
|
|
18
|
+
* compared with itself. A platform that will not answer returns null, meaning
|
|
19
|
+
* "unverifiable" — callers fall back to a plain liveness check, which is where
|
|
20
|
+
* they were before this existed.
|
|
21
|
+
*
|
|
22
|
+
* Two rules keep comparison honest, and both exist because getting them wrong
|
|
23
|
+
* reports a *false* mismatch, which prunes a live row and hands the identity
|
|
24
|
+
* walk the same wrong answer this module was written to stop:
|
|
25
|
+
*
|
|
26
|
+
* 1. One machine uses one probe. The probe is chosen by capability and locked
|
|
27
|
+
* in, never fallen back from, so a transient read failure cannot answer in
|
|
28
|
+
* the other probe's dialect and read as a recycled pid.
|
|
29
|
+
* 2. A probe's output must not depend on who asked. The `ps` text is formatted
|
|
30
|
+
* through the caller's locale and timezone, so both are pinned; without that,
|
|
31
|
+
* two callers reading the same live process disagree.
|
|
32
|
+
*/
|
|
33
|
+
/** Which probe answers "when did this process start?" on this machine. */
|
|
34
|
+
export type StartTokenProbe = "procfs" | "ps";
|
|
35
|
+
/**
|
|
36
|
+
* Which probe this machine uses, decided once.
|
|
37
|
+
*
|
|
38
|
+
* `HARNERY_PID_PROBE` forces the choice. That exists so the `ps` path can be
|
|
39
|
+
* exercised on a procfs machine — it is the same code on either OS, and running
|
|
40
|
+
* it only on hardware we happen to own is how a branch rots.
|
|
41
|
+
*/
|
|
42
|
+
export declare function startTokenProbe(): StartTokenProbe;
|
|
43
|
+
/** Forget the cached probe and boot id. Tests only. */
|
|
44
|
+
export declare function resetStartTokenCaches(): void;
|
|
45
|
+
/**
|
|
46
|
+
* `l[<boot>.]<ticks since boot>` from `/proc/<pid>/stat`, with no subprocess.
|
|
47
|
+
*
|
|
48
|
+
* The tick count is field 22, counted from the end of the comm field rather
|
|
49
|
+
* than by splitting the whole line, because comm is the executable name in
|
|
50
|
+
* parentheses and may itself contain spaces and parens. The boot segment is
|
|
51
|
+
* dropped rather than faked when the kernel will not name its boot, which
|
|
52
|
+
* yields the pre-boot-id shape that `tokensAgree` still accepts.
|
|
53
|
+
*/
|
|
54
|
+
export declare function procfsStartToken(pid: number): string | null;
|
|
55
|
+
/**
|
|
56
|
+
* `p<start date>` from one `ps -o lstart=`, for machines without procfs.
|
|
57
|
+
*
|
|
58
|
+
* Second resolution is ample: telling two runs of one pid apart would need the
|
|
59
|
+
* entire pid space to recycle inside a second. The runner is injectable so the
|
|
60
|
+
* parsing and the env pinning can be tested without a process to look at.
|
|
61
|
+
*/
|
|
62
|
+
export declare function psStartToken(pid: number, run?: (pid: number) => {
|
|
63
|
+
status: number | null;
|
|
64
|
+
stdout: string;
|
|
65
|
+
}): string | null;
|
|
66
|
+
/**
|
|
67
|
+
* An opaque token identifying *this run* of `pid`, or null when the platform
|
|
68
|
+
* will not say.
|
|
69
|
+
*/
|
|
70
|
+
export declare function processStartToken(pid: number): string | null;
|
|
71
|
+
/**
|
|
72
|
+
* Do two tokens describe the same run?
|
|
73
|
+
*
|
|
74
|
+
* Equality, plus one concession: a row written before tokens carried a boot
|
|
75
|
+
* segment recorded the tick count alone, so it is compared on the part it
|
|
76
|
+
* actually recorded. Refusing that would call every pre-upgrade row a recycled
|
|
77
|
+
* pid and prune a working machine's live rows the first time the new code ran.
|
|
78
|
+
* Such a row loses its reboot protection until the next write rewrites it,
|
|
79
|
+
* which is a strictly better position than the one it came from.
|
|
80
|
+
*/
|
|
81
|
+
export declare function tokensAgree(recorded: string, current: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Does `pid` still name the process a row recorded?
|
|
84
|
+
*
|
|
85
|
+
* Three answers, and the difference between the last two matters:
|
|
86
|
+
*
|
|
87
|
+
* - `"match"` — recorded token, current token, same run. The row is trustworthy.
|
|
88
|
+
* - `"mismatch"` — both present and different. The pid was recycled. This is
|
|
89
|
+
* the case a liveness check cannot see, because a recycled pid is alive.
|
|
90
|
+
* - `"unverified"` — no recorded token (a row written before tokens existed) or
|
|
91
|
+
* no current one (an unsupported platform). Callers treat this the way they
|
|
92
|
+
* treated every row previously: trust it if the pid is alive.
|
|
93
|
+
*/
|
|
94
|
+
export declare function checkPidToken(pid: number, recorded: string | undefined): "match" | "mismatch" | "unverified";
|
|
95
|
+
//# sourceMappingURL=proc-start.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proc-start.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/proc-start.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,IAAI,CAAC;AAc9C;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAOjD;AAyBD,uDAAuD;AACvD,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc3D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAiB,GAC7E,MAAM,GAAG,IAAI,CAUf;AAWD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQtE;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,GAAG,UAAU,GAAG,YAAY,CAKrC"}
|