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
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
sameFilesystemIdentity,
|
|
11
11
|
validateConfiguredRoot,
|
|
12
12
|
} from "./paths.ts";
|
|
13
|
-
import {
|
|
13
|
+
import { appendWorkflowTranscriptEvent, stableDigest, writeWorkspaceRequest } from "./state.ts";
|
|
14
14
|
import type {
|
|
15
15
|
WorkspaceAttestation,
|
|
16
16
|
WorkspaceBinding,
|
|
@@ -72,7 +72,7 @@ export async function resolveWorkspaceBinding(input: {
|
|
|
72
72
|
unknowns: [],
|
|
73
73
|
receipts: {},
|
|
74
74
|
};
|
|
75
|
-
|
|
75
|
+
appendWorkflowTranscriptEvent(input.opts.coordRoot, input.runId, "workspace.compatibility", {
|
|
76
76
|
requested_isolation: fallback.requested_isolation,
|
|
77
77
|
effective_isolation: fallback.effective_isolation,
|
|
78
78
|
selection_reason: fallback.selection_reason,
|
|
@@ -114,7 +114,7 @@ export async function resolveWorkspaceBinding(input: {
|
|
|
114
114
|
}
|
|
115
115
|
throw new Error(`workflow run ${input.runId} could not reattach its frozen workspace`);
|
|
116
116
|
}
|
|
117
|
-
|
|
117
|
+
appendWorkflowTranscriptEvent(input.opts.coordRoot, input.runId, "workspace.reattach", {
|
|
118
118
|
binding_id: frozenBinding.binding_id,
|
|
119
119
|
provider_id: frozenBinding.provider.id,
|
|
120
120
|
attestation_sha256: stableDigest(reattached),
|
|
@@ -180,7 +180,7 @@ export async function resolveWorkspaceBinding(input: {
|
|
|
180
180
|
unknowns: [...probe.unknowns],
|
|
181
181
|
receipts: {},
|
|
182
182
|
};
|
|
183
|
-
|
|
183
|
+
appendWorkflowTranscriptEvent(input.opts.coordRoot, input.runId, "workspace.fallback", {
|
|
184
184
|
requested_isolation: fallback.requested_isolation,
|
|
185
185
|
effective_isolation: fallback.effective_isolation,
|
|
186
186
|
selection_reason: fallback.selection_reason,
|
|
@@ -226,7 +226,7 @@ export async function resolveWorkspaceBinding(input: {
|
|
|
226
226
|
idempotency_key: stableDigest(requestWithoutKey),
|
|
227
227
|
};
|
|
228
228
|
writeWorkspaceRequest(input.opts.coordRoot, request);
|
|
229
|
-
|
|
229
|
+
appendWorkflowTranscriptEvent(input.opts.coordRoot, input.runId, "execution.allocate.start", {
|
|
230
230
|
provider_id: probe.capabilities.provider_id,
|
|
231
231
|
request_sha256: stableDigest(request),
|
|
232
232
|
});
|
|
@@ -261,7 +261,7 @@ export async function resolveWorkspaceBinding(input: {
|
|
|
261
261
|
}
|
|
262
262
|
throw new Error("workspace provider returned a binding that failed initial attestation");
|
|
263
263
|
}
|
|
264
|
-
|
|
264
|
+
appendWorkflowTranscriptEvent(input.opts.coordRoot, input.runId, "execution.allocate.end", {
|
|
265
265
|
binding_id: binding.binding_id,
|
|
266
266
|
provider_id: binding.provider.id,
|
|
267
267
|
active_root: binding.active_root,
|
|
@@ -97,7 +97,7 @@ export type WorkflowWorkspaceInspection =
|
|
|
97
97
|
| { ok: true; value: WorkflowWorkspaceStatus }
|
|
98
98
|
| { ok: false; run_id: string; error: string };
|
|
99
99
|
|
|
100
|
-
interface
|
|
100
|
+
interface WorkflowTranscriptProjectionEvent {
|
|
101
101
|
event: string;
|
|
102
102
|
ok?: boolean;
|
|
103
103
|
status?: "blocked" | "lost" | "cancelled" | "unsupported" | "preserved_dirty";
|
|
@@ -119,7 +119,7 @@ export function readWorkflowWorkspaceStatus(
|
|
|
119
119
|
const root = resolve(coordRoot);
|
|
120
120
|
const runDir = workflowRunDir(root, runId);
|
|
121
121
|
const manifest = readWorkflowRunManifest(root, runId);
|
|
122
|
-
const
|
|
122
|
+
const transcript = readWorkspaceTranscript(runDir, runId);
|
|
123
123
|
const proofPath = join(runDir, "proof.json");
|
|
124
124
|
const proof = existsSync(proofPath) ? readWorkflowProof(root, runId) : undefined;
|
|
125
125
|
const proofSha256 = proof ? fileSha256(proofPath) : undefined;
|
|
@@ -129,7 +129,7 @@ export function readWorkflowWorkspaceStatus(
|
|
|
129
129
|
if (!binding) {
|
|
130
130
|
const lifecycle = deriveWorkspaceLifecycle({
|
|
131
131
|
manifest,
|
|
132
|
-
|
|
132
|
+
workflow_transcript: transcript,
|
|
133
133
|
proof,
|
|
134
134
|
proof_sha256: proofSha256,
|
|
135
135
|
allocation_unsupported: fallback !== undefined,
|
|
@@ -212,7 +212,7 @@ export function readWorkflowWorkspaceStatus(
|
|
|
212
212
|
binding,
|
|
213
213
|
provider_events: providerEvents,
|
|
214
214
|
manifest,
|
|
215
|
-
|
|
215
|
+
workflow_transcript: transcript,
|
|
216
216
|
proof,
|
|
217
217
|
proof_sha256: proofSha256,
|
|
218
218
|
integration_plan: integrationPlan,
|
|
@@ -355,8 +355,11 @@ export function renderWorkflowWorkspaceStatus(status: WorkflowWorkspaceStatus):
|
|
|
355
355
|
|
|
356
356
|
const MAX_LISTED_DIRTY_PATHS = 12;
|
|
357
357
|
|
|
358
|
-
function
|
|
359
|
-
|
|
358
|
+
function readWorkspaceTranscript(
|
|
359
|
+
runDir: string,
|
|
360
|
+
runId: string,
|
|
361
|
+
): WorkflowTranscriptProjectionEvent[] {
|
|
362
|
+
const path = join(runDir, "transcript.jsonl");
|
|
360
363
|
if (!existsSync(path)) return [];
|
|
361
364
|
return readFileSync(path, "utf8")
|
|
362
365
|
.split("\n")
|
|
@@ -367,7 +370,7 @@ function readWorkspaceJournal(runDir: string, runId: string): WorkflowJournalPro
|
|
|
367
370
|
record = JSON.parse(line) as Record<string, unknown>;
|
|
368
371
|
} catch (error) {
|
|
369
372
|
throw new Error(
|
|
370
|
-
`cannot parse workflow
|
|
373
|
+
`cannot parse workflow transcript event ${index + 1}: ${
|
|
371
374
|
error instanceof Error ? error.message : String(error)
|
|
372
375
|
}`,
|
|
373
376
|
);
|
|
@@ -376,12 +379,12 @@ function readWorkspaceJournal(runDir: string, runId: string): WorkflowJournalPro
|
|
|
376
379
|
typeof record.event !== "string" ||
|
|
377
380
|
(record.run_id !== undefined && record.run_id !== runId)
|
|
378
381
|
) {
|
|
379
|
-
throw new Error(`workflow
|
|
382
|
+
throw new Error(`workflow transcript event ${index + 1} is foreign or unsupported`);
|
|
380
383
|
}
|
|
381
384
|
return {
|
|
382
385
|
event: record.event,
|
|
383
386
|
ok: typeof record.ok === "boolean" ? record.ok : undefined,
|
|
384
|
-
status:
|
|
387
|
+
status: transcriptStatus(record.status),
|
|
385
388
|
work_event_seq:
|
|
386
389
|
typeof record.work_event_seq === "number" ? record.work_event_seq : undefined,
|
|
387
390
|
work_event_sha256:
|
|
@@ -424,10 +427,10 @@ function cleanupState(
|
|
|
424
427
|
return intent || attemptStatus ? "pending" : "not_requested";
|
|
425
428
|
}
|
|
426
429
|
|
|
427
|
-
function
|
|
430
|
+
function transcriptStatus(value: unknown): WorkflowTranscriptProjectionEvent["status"] {
|
|
428
431
|
return typeof value === "string" &&
|
|
429
432
|
["blocked", "lost", "cancelled", "unsupported", "preserved_dirty"].includes(value)
|
|
430
|
-
? (value as
|
|
433
|
+
? (value as WorkflowTranscriptProjectionEvent["status"])
|
|
431
434
|
: undefined;
|
|
432
435
|
}
|
|
433
436
|
|
|
@@ -16,7 +16,7 @@ import { readWorkflowRunManifest } from "../run-state.ts";
|
|
|
16
16
|
import { acquireNoClobberLease } from "./leases.ts";
|
|
17
17
|
import {
|
|
18
18
|
appendIntegrationAttempt,
|
|
19
|
-
|
|
19
|
+
appendWorkflowTranscriptEvent,
|
|
20
20
|
fileSha256,
|
|
21
21
|
readIntegrationAttempts,
|
|
22
22
|
readWorkflowSupplement,
|
|
@@ -177,7 +177,7 @@ export async function prepareIntegration(input: PrepareIntegrationInput): Promis
|
|
|
177
177
|
}
|
|
178
178
|
if (!prior) {
|
|
179
179
|
writeWorkflowSupplement(input.coordRoot, input.runId, "integration/plan.json", plan);
|
|
180
|
-
|
|
180
|
+
appendWorkflowTranscriptEvent(input.coordRoot, input.runId, "integration.plan", {
|
|
181
181
|
plan_id: plan.plan_id,
|
|
182
182
|
plan_sha256: stableDigest(plan),
|
|
183
183
|
});
|
|
@@ -236,12 +236,12 @@ export async function applyIntegration(
|
|
|
236
236
|
}
|
|
237
237
|
if (
|
|
238
238
|
authorization.decision_sha256 !== stableDigest(authorization.decision) ||
|
|
239
|
-
authorization.
|
|
240
|
-
authorization.
|
|
239
|
+
authorization.transcript_anchor.event !== "integration.plan" ||
|
|
240
|
+
authorization.transcript_anchor.plan_sha256 !== planSha256
|
|
241
241
|
) {
|
|
242
|
-
throw new Error("integration authorization digest or
|
|
242
|
+
throw new Error("integration authorization digest or transcript anchor is corrupt");
|
|
243
243
|
}
|
|
244
|
-
|
|
244
|
+
assertIntegrationPlanTranscript(input.coordRoot, input.runId, planSha256);
|
|
245
245
|
if (authorization.approval_id) {
|
|
246
246
|
const approval = readWorkflowApproval(input.coordRoot, authorization.approval_id);
|
|
247
247
|
if (
|
|
@@ -397,7 +397,7 @@ export async function applyIntegration(
|
|
|
397
397
|
applied_at: providerResult.applied_at,
|
|
398
398
|
};
|
|
399
399
|
writeWorkflowSupplement(input.coordRoot, input.runId, "integration/receipt.json", receipt);
|
|
400
|
-
|
|
400
|
+
appendWorkflowTranscriptEvent(input.coordRoot, input.runId, "integration.apply", {
|
|
401
401
|
receipt_id: receipt.receipt_id,
|
|
402
402
|
status: receipt.status,
|
|
403
403
|
target_commit: receipt.target_commit,
|
|
@@ -721,7 +721,7 @@ function authorizePlan(input: PrepareIntegrationInput, plan: IntegrationPlan): v
|
|
|
721
721
|
approval_id: approvalId,
|
|
722
722
|
approval_actor: approvalActor,
|
|
723
723
|
approval_sha256: approvalSha256,
|
|
724
|
-
|
|
724
|
+
transcript_anchor: {
|
|
725
725
|
event: "integration.plan",
|
|
726
726
|
plan_sha256: stableDigest(plan),
|
|
727
727
|
},
|
|
@@ -740,7 +740,7 @@ function authorizePlan(input: PrepareIntegrationInput, plan: IntegrationPlan): v
|
|
|
740
740
|
prior.decision_sha256 !== stableDigest(prior.decision) ||
|
|
741
741
|
prior.approval_id !== authorization.approval_id ||
|
|
742
742
|
prior.approval_sha256 !== authorization.approval_sha256 ||
|
|
743
|
-
stableDigest(prior.
|
|
743
|
+
stableDigest(prior.transcript_anchor) !== stableDigest(authorization.transcript_anchor)
|
|
744
744
|
) {
|
|
745
745
|
throw new Error("existing integration authorization does not match the exact plan");
|
|
746
746
|
}
|
|
@@ -752,7 +752,7 @@ function authorizePlan(input: PrepareIntegrationInput, plan: IntegrationPlan): v
|
|
|
752
752
|
"integration/authorization.json",
|
|
753
753
|
authorization,
|
|
754
754
|
);
|
|
755
|
-
|
|
755
|
+
appendWorkflowTranscriptEvent(input.coordRoot, input.runId, "integration.authorized", {
|
|
756
756
|
plan_id: plan.plan_id,
|
|
757
757
|
authorization_sha256: stableDigest(authorization),
|
|
758
758
|
policy_sha256: authorization.policy_sha256,
|
|
@@ -787,8 +787,12 @@ function requiredIntegrationRoot(binding: WorkspaceBinding): string {
|
|
|
787
787
|
return binding.integration_root;
|
|
788
788
|
}
|
|
789
789
|
|
|
790
|
-
function
|
|
791
|
-
|
|
790
|
+
function assertIntegrationPlanTranscript(
|
|
791
|
+
coordRoot: string,
|
|
792
|
+
runId: string,
|
|
793
|
+
planSha256: string,
|
|
794
|
+
): void {
|
|
795
|
+
const path = join(workflowRunDir(coordRoot, runId), "transcript.jsonl");
|
|
792
796
|
let records: Array<Record<string, unknown>>;
|
|
793
797
|
try {
|
|
794
798
|
records = readFileSync(path, "utf8")
|
|
@@ -796,7 +800,7 @@ function assertIntegrationPlanJournal(coordRoot: string, runId: string, planSha2
|
|
|
796
800
|
.filter(Boolean)
|
|
797
801
|
.map((line) => JSON.parse(line) as Record<string, unknown>);
|
|
798
802
|
} catch (error) {
|
|
799
|
-
throw new Error(`workflow
|
|
803
|
+
throw new Error(`workflow transcript is corrupt: ${(error as Error).message}`);
|
|
800
804
|
}
|
|
801
805
|
if (
|
|
802
806
|
records.some((record) => record.run_id !== runId || typeof record.event !== "string") ||
|
|
@@ -804,7 +808,7 @@ function assertIntegrationPlanJournal(coordRoot: string, runId: string, planSha2
|
|
|
804
808
|
(record) => record.event === "integration.plan" && record.plan_sha256 === planSha256,
|
|
805
809
|
)
|
|
806
810
|
) {
|
|
807
|
-
throw new Error("integration plan
|
|
811
|
+
throw new Error("integration plan transcript anchor is missing or mismatched");
|
|
808
812
|
}
|
|
809
813
|
}
|
|
810
814
|
|
|
@@ -135,7 +135,7 @@ export interface WorkspaceLifecycleProjectionInput {
|
|
|
135
135
|
binding?: WorkspaceBinding;
|
|
136
136
|
provider_events?: readonly WorkspaceProviderEvent[];
|
|
137
137
|
manifest?: WorkflowRunManifest;
|
|
138
|
-
|
|
138
|
+
workflow_transcript?: readonly {
|
|
139
139
|
event: string;
|
|
140
140
|
ok?: boolean;
|
|
141
141
|
status?: "blocked" | "lost" | WorkspaceCancellationResult["status"];
|
|
@@ -207,22 +207,22 @@ export function deriveWorkspaceLifecycle(
|
|
|
207
207
|
transition(next);
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
const
|
|
211
|
-
for (const event of
|
|
210
|
+
const transcript = input.workflow_transcript ?? [];
|
|
211
|
+
for (const event of transcript) {
|
|
212
212
|
if (input.binding && (event.event === "run.start" || event.event === "run.resume")) {
|
|
213
213
|
transition("running");
|
|
214
214
|
}
|
|
215
215
|
if (event.event === "run.parked") transition("parked");
|
|
216
216
|
}
|
|
217
|
-
const failedReattachment = [...
|
|
217
|
+
const failedReattachment = [...transcript]
|
|
218
218
|
.reverse()
|
|
219
219
|
.find((event) => event.event === "workspace.reattach.failed");
|
|
220
220
|
if (failedReattachment) transition(failedReattachment.status === "blocked" ? "blocked" : "lost");
|
|
221
221
|
let workflowOutcome = proofOutcome(input.proof, input.binding);
|
|
222
|
-
const
|
|
222
|
+
const terminalTranscript = [...transcript].reverse().find((event) => event.event === "run.end");
|
|
223
223
|
if (input.proof && !failedReattachment) transition(workflowOutcome!);
|
|
224
|
-
if (
|
|
225
|
-
workflowOutcome =
|
|
224
|
+
if (terminalTranscript && !input.proof && !failedReattachment) {
|
|
225
|
+
workflowOutcome = terminalTranscript.ok === false ? "failed_retained" : null;
|
|
226
226
|
transition("blocked");
|
|
227
227
|
}
|
|
228
228
|
if (projectedIntegrationState !== "none") transition("integration_requested");
|
|
@@ -269,7 +269,7 @@ export function deriveWorkspaceLifecycle(
|
|
|
269
269
|
((input.cancellation_receipt?.status === "cancelled" &&
|
|
270
270
|
input.cancellation_receipt.work_event_seq === latestGovernance.seq &&
|
|
271
271
|
input.cancellation_receipt.work_event_sha256 === workEventSha256) ||
|
|
272
|
-
|
|
272
|
+
transcript.some(
|
|
273
273
|
(event) =>
|
|
274
274
|
event.event === "workspace.cancel" &&
|
|
275
275
|
event.status === "cancelled" &&
|
|
@@ -459,8 +459,8 @@ function validateIntegrationAuthorization(
|
|
|
459
459
|
!digest(authorization.policy_sha256) ||
|
|
460
460
|
authorization.decision?.verdict !== "allow" ||
|
|
461
461
|
authorization.decision_sha256 !== stableDigest(authorization.decision) ||
|
|
462
|
-
authorization.
|
|
463
|
-
authorization.
|
|
462
|
+
authorization.transcript_anchor?.event !== "integration.plan" ||
|
|
463
|
+
authorization.transcript_anchor.plan_sha256 !== planSha256 ||
|
|
464
464
|
!validTimestamp(authorization.authorized_at) ||
|
|
465
465
|
(authorization.approval_id !== undefined &&
|
|
466
466
|
(typeof authorization.approval_actor !== "string" || !digest(authorization.approval_sha256)))
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
lstatSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
realpathSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from "node:fs";
|
|
10
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
11
|
import { WorkspaceAttestationError } from "./attestation-error.ts";
|
|
5
12
|
import {
|
|
6
13
|
git,
|
|
@@ -68,6 +75,8 @@ import { isWorkspaceAttestation } from "./validate.ts";
|
|
|
68
75
|
|
|
69
76
|
const PROVIDER_ID = "local-git-worktree";
|
|
70
77
|
const PROVIDER_VERSION = "1";
|
|
78
|
+
const WORKSPACE_PARENT_SEGMENT = ".harnery-workspaces";
|
|
79
|
+
const LEGACY_WORKSPACE_PARENT_SEGMENT = "harnery-workspaces";
|
|
71
80
|
const LOCK_STALE_MS = 5 * 60 * 1_000;
|
|
72
81
|
const OBJECT_FORMAT = /^[a-f0-9]{40,64}$/;
|
|
73
82
|
const REF_FORMAT = /^refs\/heads\/[A-Za-z0-9][A-Za-z0-9._/-]{0,180}$/;
|
|
@@ -221,7 +230,11 @@ async function allocate(
|
|
|
221
230
|
);
|
|
222
231
|
const authorityOutside = describeAuthorityOutsideRoot(repo, root.realpath);
|
|
223
232
|
if (authorityOutside) throw new Error(authorityOutside);
|
|
224
|
-
|
|
233
|
+
// Claims are immutable, so a retry must reproduce the path the first attempt
|
|
234
|
+
// froze. Only a genuinely new allocation picks the current parent.
|
|
235
|
+
const priorClaim = readWorkspaceClaim(coordRoot, PROVIDER_ID, bindingId);
|
|
236
|
+
const parentSegment = priorClaim ? claimedParentSegment(priorClaim) : WORKSPACE_PARENT_SEGMENT;
|
|
237
|
+
const workspaceRoot = candidateUnderRoot(root, [parentSegment, bindingId]);
|
|
225
238
|
const activeRoot = resolve(workspaceRoot, repo.requestedRelativePath);
|
|
226
239
|
if (!containsPath(workspaceRoot, activeRoot)) {
|
|
227
240
|
throw new Error("requested working directory escapes the isolated workspace");
|
|
@@ -287,6 +300,33 @@ async function allocate(
|
|
|
287
300
|
}
|
|
288
301
|
}
|
|
289
302
|
|
|
303
|
+
/**
|
|
304
|
+
* A claim freezes its workspace path at allocation time, so reconciliation must
|
|
305
|
+
* follow the parent recorded there rather than the parent this version would
|
|
306
|
+
* choose. Only the names this provider has ever allocated under are accepted, so
|
|
307
|
+
* a tampered claim cannot redirect creation into an unrelated directory.
|
|
308
|
+
*/
|
|
309
|
+
function claimedParentSegment(claim: WorkspaceClaim): string {
|
|
310
|
+
const segment = basename(dirname(claim.workspace_root));
|
|
311
|
+
if (segment !== WORKSPACE_PARENT_SEGMENT && segment !== LEGACY_WORKSPACE_PARENT_SEGMENT) {
|
|
312
|
+
throw new Error("claimed workspace path is not under a provider-owned workspace parent");
|
|
313
|
+
}
|
|
314
|
+
return segment;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* The parent holds real checkouts that would otherwise surface as untracked
|
|
319
|
+
* content in the surrounding repository. A directory whose every entry is
|
|
320
|
+
* ignored, including the ignore file itself, does not appear in `git status` at
|
|
321
|
+
* all, which keeps the guarantee with the provider instead of asking each
|
|
322
|
+
* consumer to maintain a rule.
|
|
323
|
+
*/
|
|
324
|
+
function hideWorkspaceParentFromGit(parent: string): void {
|
|
325
|
+
const ignoreFile = join(parent, ".gitignore");
|
|
326
|
+
if (existsSync(ignoreFile)) return;
|
|
327
|
+
writeFileSync(ignoreFile, "*\n", { mode: 0o600 });
|
|
328
|
+
}
|
|
329
|
+
|
|
290
330
|
function reconcileAllocation(
|
|
291
331
|
coordRoot: string,
|
|
292
332
|
claim: WorkspaceClaim,
|
|
@@ -328,7 +368,9 @@ function reconcileAllocation(
|
|
|
328
368
|
throw new Error("provider-owned workspace branch no longer matches its frozen base");
|
|
329
369
|
}
|
|
330
370
|
|
|
331
|
-
const
|
|
371
|
+
const parentSegment = claimedParentSegment(claim);
|
|
372
|
+
const workspaceParent = createContainedDirectories(root, [parentSegment]);
|
|
373
|
+
hideWorkspaceParentFromGit(workspaceParent);
|
|
332
374
|
const workspaceParentIdentity = filesystemIdentity(workspaceParent);
|
|
333
375
|
const registered = worktreeInventory(claim.repository.source_root.realpath);
|
|
334
376
|
const exact = registered.find((item) => resolve(item.path) === claim.workspace_root);
|
|
@@ -385,7 +427,7 @@ function reconcileAllocation(
|
|
|
385
427
|
parent_identity: workspaceParentIdentity,
|
|
386
428
|
});
|
|
387
429
|
}
|
|
388
|
-
const openedWorkspace = openContainedDirectory(root, [
|
|
430
|
+
const openedWorkspace = openContainedDirectory(root, [parentSegment, claim.binding_id]);
|
|
389
431
|
if (openedWorkspace.path !== claim.workspace_root) {
|
|
390
432
|
openedWorkspace.close();
|
|
391
433
|
throw new Error("descriptor-backed workspace directory differs from the claim");
|
|
@@ -16,11 +16,11 @@ export function readWorkflowLiveness(coordRoot: string, runId: string): Workflow
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
if (!existsSync(
|
|
19
|
+
const transcriptPath = join(root, ".harnery", "workflows", runId, "transcript.jsonl");
|
|
20
|
+
if (!existsSync(transcriptPath)) return "unknown";
|
|
21
21
|
let events: Array<{ event?: string; run_id?: string; ok?: unknown; status?: unknown }>;
|
|
22
22
|
try {
|
|
23
|
-
events = readFileSync(
|
|
23
|
+
events = readFileSync(transcriptPath, "utf8")
|
|
24
24
|
.split("\n")
|
|
25
25
|
.filter(Boolean)
|
|
26
26
|
.map(
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
writeImmutableJson,
|
|
19
19
|
} from "../durable-record.ts";
|
|
20
20
|
|
|
21
|
-
export {
|
|
21
|
+
export { appendWorkflowTranscriptEvent } from "../transcript.ts";
|
|
22
22
|
|
|
23
23
|
import type {
|
|
24
24
|
GitRepositoryBinding,
|
|
@@ -232,7 +232,8 @@ export function readWorkspaceEvents(
|
|
|
232
232
|
): WorkspaceProviderEvent[] {
|
|
233
233
|
const path = workspaceEventsPath(coordRoot, providerId, bindingId);
|
|
234
234
|
if (!existsSync(path)) return [];
|
|
235
|
-
if (statSync(path).size > JSON_LIMIT * 4)
|
|
235
|
+
if (statSync(path).size > JSON_LIMIT * 4)
|
|
236
|
+
throw new Error("workspace event transcript is too large");
|
|
236
237
|
const claim = readWorkspaceClaim(coordRoot, providerId, bindingId);
|
|
237
238
|
if (!claim) throw new Error("workspace event authority claim is missing");
|
|
238
239
|
const records = readFileSync(path, "utf8")
|
|
@@ -391,7 +392,7 @@ function readChainedRecords<
|
|
|
391
392
|
>(path: string, label: string): T[] {
|
|
392
393
|
if (!existsSync(path)) return [];
|
|
393
394
|
if (statSync(path).size > JSON_LIMIT * 4) {
|
|
394
|
-
throw new Error(`${label}
|
|
395
|
+
throw new Error(`${label} transcript is too large`);
|
|
395
396
|
}
|
|
396
397
|
const records = readFileSync(path, "utf8")
|
|
397
398
|
.split("\n")
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Value assertions for `harn browse --assert`.
|
|
2
|
+
//
|
|
3
|
+
// The layout + content checks answer "does the page look right"; asserts answer
|
|
4
|
+
// "does the page SAY the right thing" — the h1 text, a price, how many cards
|
|
5
|
+
// rendered, whether an error banner is absent. It lets an agent confirm the
|
|
6
|
+
// values it expects without a human reading the page back.
|
|
7
|
+
//
|
|
8
|
+
// One repeatable flag with a small grammar:
|
|
9
|
+
// --assert 'text h1 => Welcome' first match's trimmed text equals
|
|
10
|
+
// --assert 'contains .total => $49' first match's text contains
|
|
11
|
+
// --assert 'matches .sku => ^BN-\d+$' first match's text matches a regex
|
|
12
|
+
// --assert 'count .card => >=3' match count vs a number or comparator
|
|
13
|
+
// --assert 'exists .cta' at least one match
|
|
14
|
+
// --assert 'absent .error' zero matches
|
|
15
|
+
//
|
|
16
|
+
// `op selector => expected`; `exists`/`absent` take no `=> expected`.
|
|
17
|
+
|
|
18
|
+
export type AssertOp = "text" | "contains" | "matches" | "count" | "exists" | "absent";
|
|
19
|
+
|
|
20
|
+
export interface AssertSpec {
|
|
21
|
+
raw: string;
|
|
22
|
+
op: AssertOp;
|
|
23
|
+
selector: string;
|
|
24
|
+
expected: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AssertResult {
|
|
28
|
+
raw: string;
|
|
29
|
+
op: AssertOp;
|
|
30
|
+
selector: string;
|
|
31
|
+
expected: string;
|
|
32
|
+
/** Observed value: the text, or the match count as a string. */
|
|
33
|
+
actual: string;
|
|
34
|
+
found: boolean;
|
|
35
|
+
outcome: "pass" | "fail";
|
|
36
|
+
/** Populated when the spec itself was malformed (e.g. bad regex, bad count). */
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const OPS: readonly AssertOp[] = ["text", "contains", "matches", "count", "exists", "absent"];
|
|
41
|
+
|
|
42
|
+
/** Parse one `--assert` expression. Throws with a clear message on malformed input. */
|
|
43
|
+
export function parseAssertSpec(raw: string): AssertSpec {
|
|
44
|
+
const trimmed = raw.trim();
|
|
45
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
46
|
+
if (spaceIdx < 0) {
|
|
47
|
+
throw new Error(`--assert needs '<op> <selector> [=> expected]' (got: '${raw}')`);
|
|
48
|
+
}
|
|
49
|
+
const op = trimmed.slice(0, spaceIdx).toLowerCase() as AssertOp;
|
|
50
|
+
if (!OPS.includes(op)) {
|
|
51
|
+
throw new Error(`--assert op must be one of ${OPS.join(", ")} (got: '${op}')`);
|
|
52
|
+
}
|
|
53
|
+
const rest = trimmed.slice(spaceIdx + 1).trim();
|
|
54
|
+
const sep = rest.indexOf("=>");
|
|
55
|
+
if (op === "exists" || op === "absent") {
|
|
56
|
+
return { raw, op, selector: (sep < 0 ? rest : rest.slice(0, sep)).trim(), expected: "" };
|
|
57
|
+
}
|
|
58
|
+
if (sep < 0) {
|
|
59
|
+
throw new Error(`--assert '${op}' needs 'selector => expected' (got: '${raw}')`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
raw,
|
|
63
|
+
op,
|
|
64
|
+
selector: rest.slice(0, sep).trim(),
|
|
65
|
+
expected: rest.slice(sep + 2).trim(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Build the page evaluator. Serialized into the page by Playwright, so helpers
|
|
71
|
+
* are nested. Comparison logic that does not touch the DOM (count comparators,
|
|
72
|
+
* regex) also runs in-page to keep it one round trip.
|
|
73
|
+
*/
|
|
74
|
+
export function buildAssertCheck(): (specs: AssertSpec[]) => AssertResult[] {
|
|
75
|
+
return (specs) => {
|
|
76
|
+
const textOf = (el: Element): string => (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
77
|
+
|
|
78
|
+
const compareCount = (count: number, expected: string): { ok: boolean; error?: string } => {
|
|
79
|
+
const m = /^(>=|<=|>|<|=)?\s*(\d+)$/.exec(expected.trim());
|
|
80
|
+
if (!m) return { ok: false, error: `bad count expected '${expected}'` };
|
|
81
|
+
const op = m[1] || "=";
|
|
82
|
+
const n = Number.parseInt(m[2]!, 10);
|
|
83
|
+
switch (op) {
|
|
84
|
+
case ">=":
|
|
85
|
+
return { ok: count >= n };
|
|
86
|
+
case "<=":
|
|
87
|
+
return { ok: count <= n };
|
|
88
|
+
case ">":
|
|
89
|
+
return { ok: count > n };
|
|
90
|
+
case "<":
|
|
91
|
+
return { ok: count < n };
|
|
92
|
+
default:
|
|
93
|
+
return { ok: count === n };
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return specs.map((spec): AssertResult => {
|
|
98
|
+
const base = {
|
|
99
|
+
raw: spec.raw,
|
|
100
|
+
op: spec.op,
|
|
101
|
+
selector: spec.selector,
|
|
102
|
+
expected: spec.expected,
|
|
103
|
+
};
|
|
104
|
+
let nodes: Element[];
|
|
105
|
+
try {
|
|
106
|
+
nodes = Array.from(document.querySelectorAll(spec.selector));
|
|
107
|
+
} catch {
|
|
108
|
+
return { ...base, actual: "", found: false, outcome: "fail", error: "invalid selector" };
|
|
109
|
+
}
|
|
110
|
+
const first = nodes[0] ?? null;
|
|
111
|
+
const firstText = first ? textOf(first) : "";
|
|
112
|
+
|
|
113
|
+
switch (spec.op) {
|
|
114
|
+
case "exists":
|
|
115
|
+
return {
|
|
116
|
+
...base,
|
|
117
|
+
actual: String(nodes.length),
|
|
118
|
+
found: nodes.length > 0,
|
|
119
|
+
outcome: nodes.length > 0 ? "pass" : "fail",
|
|
120
|
+
};
|
|
121
|
+
case "absent":
|
|
122
|
+
return {
|
|
123
|
+
...base,
|
|
124
|
+
actual: String(nodes.length),
|
|
125
|
+
found: nodes.length > 0,
|
|
126
|
+
outcome: nodes.length === 0 ? "pass" : "fail",
|
|
127
|
+
};
|
|
128
|
+
case "count": {
|
|
129
|
+
const { ok, error } = compareCount(nodes.length, spec.expected);
|
|
130
|
+
return {
|
|
131
|
+
...base,
|
|
132
|
+
actual: String(nodes.length),
|
|
133
|
+
found: true,
|
|
134
|
+
outcome: ok ? "pass" : "fail",
|
|
135
|
+
...(error ? { error } : {}),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
case "text":
|
|
139
|
+
return {
|
|
140
|
+
...base,
|
|
141
|
+
actual: firstText,
|
|
142
|
+
found: !!first,
|
|
143
|
+
outcome: first && firstText === spec.expected ? "pass" : "fail",
|
|
144
|
+
};
|
|
145
|
+
case "contains":
|
|
146
|
+
return {
|
|
147
|
+
...base,
|
|
148
|
+
actual: firstText,
|
|
149
|
+
found: !!first,
|
|
150
|
+
outcome: first && firstText.includes(spec.expected) ? "pass" : "fail",
|
|
151
|
+
};
|
|
152
|
+
case "matches": {
|
|
153
|
+
let re: RegExp;
|
|
154
|
+
try {
|
|
155
|
+
re = new RegExp(spec.expected);
|
|
156
|
+
} catch {
|
|
157
|
+
return {
|
|
158
|
+
...base,
|
|
159
|
+
actual: firstText,
|
|
160
|
+
found: !!first,
|
|
161
|
+
outcome: "fail",
|
|
162
|
+
error: `invalid regex '${spec.expected}'`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
...base,
|
|
167
|
+
actual: firstText,
|
|
168
|
+
found: !!first,
|
|
169
|
+
outcome: first && re.test(firstText) ? "pass" : "fail",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
default:
|
|
173
|
+
return { ...base, actual: "", found: false, outcome: "fail", error: "unknown op" };
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
}
|