harnery 0.29.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 +2 -1
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +55 -25
- 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 +28 -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 +71 -51
- package/dist/core/governor/plan-types.d.ts +181 -0
- 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 +143 -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 +290 -116
- 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/runner.d.ts.map +1 -1
- package/dist/core/work/runner.js +11 -2
- package/dist/core/work/state.d.ts +28 -3
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +148 -16
- 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 +151 -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 +3 -2
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -1
- package/dist/core/workflow/proof.d.ts +28 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +77 -34
- 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 +28 -8
- 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 +26 -8
- 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 +27 -8
- package/dist/core/workflow/spawn-failure.d.ts +15 -1
- package/dist/core/workflow/spawn-failure.d.ts.map +1 -1
- package/dist/core/workflow/spawn-failure.js +35 -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 +119 -46
- 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/exec.d.ts +7 -0
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +6 -3
- 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 +72 -38
- 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 +28 -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 +120 -96
- package/src/core/{supervisor → governor}/plan-types.ts +64 -50
- package/src/core/{supervisor → governor}/planning.ts +240 -188
- 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 +389 -174
- 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/runner.ts +11 -2
- package/src/core/work/state.ts +185 -21
- 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 +166 -90
- package/src/core/workflow/evidence-preflight.ts +165 -0
- package/src/core/workflow/index.ts +6 -3
- package/src/core/workflow/proof.ts +94 -48
- 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 +34 -12
- package/src/core/workflow/spawn-codex.ts +30 -12
- package/src/core/workflow/spawn-cursor.ts +31 -12
- package/src/core/workflow/spawn-failure.ts +41 -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 +130 -46
- 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/exec.ts +13 -3
- 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 +0 -170
- package/dist/core/supervisor/plan-types.d.ts.map +0 -1
- package/dist/core/supervisor/planning.d.ts +0 -33
- package/dist/core/supervisor/planning.d.ts.map +0 -1
- package/dist/core/supervisor/read.d.ts +0 -6
- package/dist/core/supervisor/read.d.ts.map +0 -1
- package/dist/core/supervisor/read.js +0 -4
- package/dist/core/supervisor/runner.d.ts +0 -37
- package/dist/core/supervisor/runner.d.ts.map +0 -1
- package/dist/core/supervisor/service-read.d.ts +0 -6
- package/dist/core/supervisor/service-read.d.ts.map +0 -1
- package/dist/core/supervisor/service.d.ts +0 -114
- package/dist/core/supervisor/service.d.ts.map +0 -1
- package/dist/core/supervisor/state.d.ts +0 -95
- package/dist/core/supervisor/state.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.d.ts +0 -17
- package/dist/core/workflow/harnesses.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.js +0 -21
- package/dist/core/workflow/journal.d.ts.map +0 -1
- package/src/commands/session.ts +0 -187
- package/src/core/harnesses/index.ts +0 -56
- package/src/core/hooks/harness/detect.ts +0 -30
- package/src/core/supervisor/index.ts +0 -89
- package/src/core/supervisor/plan-api.ts +0 -73
- package/src/core/supervisor/read.ts +0 -59
- package/src/core/workflow/harnesses.ts +0 -33
- /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
package/src/core/work/state.ts
CHANGED
|
@@ -16,9 +16,9 @@ import { hostname } from "node:os";
|
|
|
16
16
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
17
17
|
import { readWorkflowApproval } from "../workflow/approvals.ts";
|
|
18
18
|
import { stableDigest } from "../workflow/durable-record.ts";
|
|
19
|
-
import { WORKFLOW_JOURNAL_EVENT_BYTES, workflowJournalPath } from "../workflow/journal.ts";
|
|
20
19
|
import { readWorkflowProof } from "../workflow/proof.ts";
|
|
21
20
|
import { readWorkflowRunManifest, workflowScriptDigest } from "../workflow/run-state.ts";
|
|
21
|
+
import { WORKFLOW_TRANSCRIPT_EVENT_BYTES, workflowTranscriptPath } from "../workflow/transcript.ts";
|
|
22
22
|
import { isWorkspaceBoundExecutionEvidence } from "../workflow/workspaces/validate.ts";
|
|
23
23
|
|
|
24
24
|
export const WORK_INTENT_SCHEMA_VERSION = 1 as const;
|
|
@@ -39,6 +39,12 @@ const MAX_EVENTS_BYTES = 4 * 1024 * 1024;
|
|
|
39
39
|
const MAX_EVENT_BYTES = 16 * 1024;
|
|
40
40
|
const MAX_EVENTS = 1_000;
|
|
41
41
|
const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
|
|
42
|
+
/** Default ceiling on consecutive uncharged attempts (ADR 0046). Kept low: each
|
|
43
|
+
* one is a real vendor round-trip, so a handful gives a transient outage room
|
|
44
|
+
* to recover before the item stops and names the outside service. There is no
|
|
45
|
+
* backoff between them beyond the cadence of the governor and the vendor's own
|
|
46
|
+
* responses, which is why the bound stays small. */
|
|
47
|
+
const DEFAULT_MAX_UNCHARGED_ATTEMPTS = 3;
|
|
42
48
|
|
|
43
49
|
export type WorkState =
|
|
44
50
|
| "waiting"
|
|
@@ -71,6 +77,12 @@ export interface WorkIntent {
|
|
|
71
77
|
dependencies: string[];
|
|
72
78
|
workflow: { path: string; sha256: string };
|
|
73
79
|
max_attempts: number;
|
|
80
|
+
/** Ceiling on CONSECUTIVE uncharged attempts (ADR 0046), separate from
|
|
81
|
+
* max_attempts. An upstream outage produces uncharged attempt after uncharged
|
|
82
|
+
* attempt; without a bound it would retry forever. At the bound the item stops
|
|
83
|
+
* and reports it is blocked on an outside service. Optional for back-compat:
|
|
84
|
+
* an intent written before ADR 0046 has none and falls back to the default. */
|
|
85
|
+
max_uncharged_attempts?: number;
|
|
74
86
|
source?: { kind: "human" | "workflow" | "external"; ref?: string };
|
|
75
87
|
created_at: string;
|
|
76
88
|
}
|
|
@@ -124,10 +136,17 @@ export interface WorkAttempt {
|
|
|
124
136
|
run_id: string;
|
|
125
137
|
started_at: string;
|
|
126
138
|
trigger?: WorkAttemptTrigger;
|
|
127
|
-
status: "running" | "parked" | "succeeded" | "failed" | "lost" | "
|
|
139
|
+
status: "running" | "parked" | "succeeded" | "failed" | "lost" | "transcript_unreadable";
|
|
128
140
|
approval_id?: string;
|
|
129
141
|
proof_path?: string;
|
|
130
|
-
|
|
142
|
+
transcript_error?: string;
|
|
143
|
+
/** Why this failed attempt was uninformative about the work (ADR 0046), read
|
|
144
|
+
* from the proof's run.class. Absent ⇒ the attempt is charged, exactly as
|
|
145
|
+
* before ADR 0046 (which is also how a proof without a class reads). */
|
|
146
|
+
uncharged?: "environment" | "upstream" | "decision";
|
|
147
|
+
/** Docket id this attempt stopped on, when uncharged is "decision" and the
|
|
148
|
+
* script named one. Lets the projection point at the question itself. */
|
|
149
|
+
blocked_on?: string;
|
|
131
150
|
}
|
|
132
151
|
|
|
133
152
|
export interface WorkProjection {
|
|
@@ -138,11 +157,22 @@ export interface WorkProjection {
|
|
|
138
157
|
next_action: WorkNextAction;
|
|
139
158
|
unresolved_dependencies: string[];
|
|
140
159
|
attempts: WorkAttempt[];
|
|
160
|
+
/** Every attempt started, charged or not. Drives the next attempt number and
|
|
161
|
+
* history ordering, so it counts uncharged attempts too. */
|
|
141
162
|
attempts_used: number;
|
|
163
|
+
/** Attempts that were informative about the work (ADR 0046). This — not
|
|
164
|
+
* attempts_used — is what max_attempts budgets, so an uncharged environment or
|
|
165
|
+
* upstream attempt does not consume the retry budget. */
|
|
166
|
+
charged_attempts: number;
|
|
142
167
|
attempts_remaining: number;
|
|
143
168
|
latest_run_id?: string;
|
|
144
169
|
approval_id?: string;
|
|
145
170
|
proof_path?: string;
|
|
171
|
+
/** Set when this item stopped because a human must rule. The value is the
|
|
172
|
+
* docket id when the script named one, else the empty string — present either
|
|
173
|
+
* way, so a reader can distinguish "waiting on a person" from every other
|
|
174
|
+
* terminal block without parsing the reason prose. */
|
|
175
|
+
blocked_on_decision?: string;
|
|
146
176
|
updated_at: string;
|
|
147
177
|
}
|
|
148
178
|
|
|
@@ -160,6 +190,7 @@ export interface CreateWorkItemInput {
|
|
|
160
190
|
acceptance?: string[];
|
|
161
191
|
dependencies?: string[];
|
|
162
192
|
maxAttempts?: number;
|
|
193
|
+
maxUnchargedAttempts?: number;
|
|
163
194
|
source?: WorkIntent["source"];
|
|
164
195
|
id?: string;
|
|
165
196
|
actor?: string;
|
|
@@ -188,6 +219,14 @@ export function createWorkItem(input: CreateWorkItemInput): WorkRecord {
|
|
|
188
219
|
if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 100) {
|
|
189
220
|
throw new Error("work maxAttempts must be an integer from 1 to 100");
|
|
190
221
|
}
|
|
222
|
+
const maxUnchargedAttempts = input.maxUnchargedAttempts ?? DEFAULT_MAX_UNCHARGED_ATTEMPTS;
|
|
223
|
+
if (
|
|
224
|
+
!Number.isSafeInteger(maxUnchargedAttempts) ||
|
|
225
|
+
maxUnchargedAttempts < 1 ||
|
|
226
|
+
maxUnchargedAttempts > 100
|
|
227
|
+
) {
|
|
228
|
+
throw new Error("work maxUnchargedAttempts must be an integer from 1 to 100");
|
|
229
|
+
}
|
|
191
230
|
const acceptance = (input.acceptance ?? []).map((value, index) =>
|
|
192
231
|
boundedString(value, `work acceptance[${index}]`, MAX_ACCEPTANCE_ITEM),
|
|
193
232
|
);
|
|
@@ -204,6 +243,7 @@ export function createWorkItem(input: CreateWorkItemInput): WorkRecord {
|
|
|
204
243
|
dependencies,
|
|
205
244
|
workflow: { path: workflowPath, sha256: workflowScriptDigest(workflowPath) },
|
|
206
245
|
max_attempts: maxAttempts,
|
|
246
|
+
max_uncharged_attempts: maxUnchargedAttempts,
|
|
207
247
|
source,
|
|
208
248
|
created_at: new Date().toISOString(),
|
|
209
249
|
};
|
|
@@ -375,13 +415,18 @@ function deriveWorkProjection(
|
|
|
375
415
|
const attempts = attemptEvents.map((event, index) =>
|
|
376
416
|
inspectAttempt(coordRoot, event, intent, attemptEvents[index - 1]?.run_id),
|
|
377
417
|
);
|
|
418
|
+
// Charged attempts — not the raw count — are what max_attempts budgets
|
|
419
|
+
// (ADR 0046). An uncharged environment/upstream attempt still increments
|
|
420
|
+
// attempts_used (ordering + next number) but not the budget.
|
|
421
|
+
const chargedAttempts = attempts.filter((attempt) => attempt.uncharged === undefined).length;
|
|
378
422
|
const base = {
|
|
379
423
|
id: intent.id,
|
|
380
424
|
title: intent.title,
|
|
381
425
|
unresolved_dependencies: [] as string[],
|
|
382
426
|
attempts,
|
|
383
427
|
attempts_used: attempts.length,
|
|
384
|
-
|
|
428
|
+
charged_attempts: chargedAttempts,
|
|
429
|
+
attempts_remaining: Math.max(0, intent.max_attempts - chargedAttempts),
|
|
385
430
|
latest_run_id: attempts.at(-1)?.run_id,
|
|
386
431
|
approval_id: attempts.at(-1)?.approval_id,
|
|
387
432
|
proof_path: attempts.at(-1)?.proof_path,
|
|
@@ -462,17 +507,74 @@ function deriveWorkProjection(
|
|
|
462
507
|
next_action: "review",
|
|
463
508
|
};
|
|
464
509
|
}
|
|
465
|
-
const attemptsRemaining = intent.max_attempts -
|
|
466
|
-
if (latest.status === "
|
|
510
|
+
const attemptsRemaining = intent.max_attempts - chargedAttempts;
|
|
511
|
+
if (latest.status === "transcript_unreadable") {
|
|
467
512
|
return {
|
|
468
513
|
...base,
|
|
469
514
|
state: "blocked",
|
|
470
|
-
reason: `workflow attempt ${latest.number}
|
|
471
|
-
latest.
|
|
515
|
+
reason: `workflow attempt ${latest.number} transcript is unreadable: ${
|
|
516
|
+
latest.transcript_error ?? "unknown transcript read error"
|
|
472
517
|
}`,
|
|
473
518
|
next_action: attemptsRemaining > 0 ? "retry" : "none",
|
|
474
519
|
};
|
|
475
520
|
}
|
|
521
|
+
// Uncharged failures (ADR 0046) are handled before the ordinary work-failure
|
|
522
|
+
// path: they did not touch the work, so they neither spent the budget nor
|
|
523
|
+
// signal that retrying the work would help.
|
|
524
|
+
if (latest.status === "failed" && latest.uncharged === "environment") {
|
|
525
|
+
// A missing precondition. The operator chose to STOP the item immediately
|
|
526
|
+
// rather than retry an unchanged environment (ADR 0046); next_action "none"
|
|
527
|
+
// stops the governor. A human who fixes the environment can still force a
|
|
528
|
+
// retry — the attempt was uncharged, so the budget is intact.
|
|
529
|
+
return {
|
|
530
|
+
...base,
|
|
531
|
+
state: "blocked",
|
|
532
|
+
reason: environmentBlockedReason(latest),
|
|
533
|
+
next_action: "none",
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
if (latest.status === "failed" && latest.uncharged === "decision") {
|
|
537
|
+
// The script did its job and the answer was "a person has to settle this".
|
|
538
|
+
// Terminal for the same reason "environment" is terminal — retrying cannot
|
|
539
|
+
// change the blocker — but the fix is a ruling, not a repair, so the reason
|
|
540
|
+
// names the decision instead of a precondition. Uncharged, so once the
|
|
541
|
+
// decision lands the item can be retried with its budget intact.
|
|
542
|
+
return {
|
|
543
|
+
...base,
|
|
544
|
+
state: "blocked",
|
|
545
|
+
reason: decisionBlockedReason(latest),
|
|
546
|
+
next_action: "none",
|
|
547
|
+
blocked_on_decision: latest.blocked_on ?? "",
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
if (latest.status === "failed" && latest.uncharged === "upstream") {
|
|
551
|
+
// Count trailing consecutive uncharged attempts in the current window. The
|
|
552
|
+
// bound is the only brake on an outage that never ends, so at the limit the
|
|
553
|
+
// item stops and names the outside service — distinct from work-blocked.
|
|
554
|
+
const maxUncharged = intent.max_uncharged_attempts ?? DEFAULT_MAX_UNCHARGED_ATTEMPTS;
|
|
555
|
+
let trailingUncharged = 0;
|
|
556
|
+
for (let index = currentAttempts.length - 1; index >= 0; index--) {
|
|
557
|
+
if (currentAttempts[index]!.uncharged === undefined) break;
|
|
558
|
+
trailingUncharged++;
|
|
559
|
+
}
|
|
560
|
+
if (trailingUncharged >= maxUncharged || attemptsRemaining <= 0) {
|
|
561
|
+
return {
|
|
562
|
+
...base,
|
|
563
|
+
state: "blocked",
|
|
564
|
+
reason:
|
|
565
|
+
trailingUncharged >= maxUncharged
|
|
566
|
+
? `blocked waiting on an outside service after ${trailingUncharged} consecutive uncharged attempt(s): ${upstreamReason(latest)}`
|
|
567
|
+
: `workflow attempt ${latest.number} was uncharged (${upstreamReason(latest)}) but the work attempt budget is exhausted`,
|
|
568
|
+
next_action: "none",
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
...base,
|
|
573
|
+
state: "blocked",
|
|
574
|
+
reason: `workflow attempt ${latest.number} did not touch the work; an outside service refused: ${upstreamReason(latest)}`,
|
|
575
|
+
next_action: "retry",
|
|
576
|
+
};
|
|
577
|
+
}
|
|
476
578
|
return {
|
|
477
579
|
...base,
|
|
478
580
|
state: "blocked",
|
|
@@ -484,6 +586,48 @@ function deriveWorkProjection(
|
|
|
484
586
|
};
|
|
485
587
|
}
|
|
486
588
|
|
|
589
|
+
/** The failure reason for an uncharged attempt, read from the proof when
|
|
590
|
+
* present. Bounded so a verbose vendor transcript cannot bloat the projection
|
|
591
|
+
* reason (which is persisted verbatim into the reconciliation event). */
|
|
592
|
+
function unchargedProofError(attempt: WorkAttempt): string | undefined {
|
|
593
|
+
if (!attempt.proof_path) return undefined;
|
|
594
|
+
try {
|
|
595
|
+
const proof = parseObject(readFileSync(attempt.proof_path, "utf8"), "workflow proof");
|
|
596
|
+
const run = proof.run;
|
|
597
|
+
const error =
|
|
598
|
+
run && typeof run === "object" ? (run as Record<string, unknown>).error : undefined;
|
|
599
|
+
return typeof error === "string" && error.trim() ? boundedTranscriptError(error) : undefined;
|
|
600
|
+
} catch {
|
|
601
|
+
return undefined;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function environmentBlockedReason(attempt: WorkAttempt): string {
|
|
606
|
+
const detail = unchargedProofError(attempt);
|
|
607
|
+
return detail
|
|
608
|
+
? `workflow attempt ${attempt.number} could not start; a required precondition is missing: ${detail}`
|
|
609
|
+
: `workflow attempt ${attempt.number} could not start; a required precondition is missing`;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function upstreamReason(attempt: WorkAttempt): string {
|
|
613
|
+
return unchargedProofError(attempt) ?? "the vendor was reached and refused";
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** Operator-facing reason for a run that stopped on a human. Reads the script's
|
|
617
|
+
* own sentence out of the proof and drops the WorkflowBlockedError prefix the
|
|
618
|
+
* engine added, so the projection says the thing once rather than twice. */
|
|
619
|
+
function decisionBlockedReason(attempt: WorkAttempt): string {
|
|
620
|
+
const raw = unchargedProofError(attempt);
|
|
621
|
+
const detail = raw
|
|
622
|
+
?.replace(/^blocked on decision \S+: /, "")
|
|
623
|
+
.replace(/^blocked pending a human decision: /, "")
|
|
624
|
+
.trim();
|
|
625
|
+
const named = attempt.blocked_on ? ` (decision ${attempt.blocked_on})` : "";
|
|
626
|
+
return detail
|
|
627
|
+
? `a human must rule before this can proceed: ${detail}${named}`
|
|
628
|
+
: `a human must rule before this can proceed${named}`;
|
|
629
|
+
}
|
|
630
|
+
|
|
487
631
|
function inspectAttempt(
|
|
488
632
|
coordRoot: string,
|
|
489
633
|
event: WorkEvent,
|
|
@@ -547,20 +691,34 @@ function inspectAttempt(
|
|
|
547
691
|
proof.acceptance.summary.unknown === 0
|
|
548
692
|
? "succeeded"
|
|
549
693
|
: "failed";
|
|
694
|
+
// A failed attempt the run classified as uninformative about the work is
|
|
695
|
+
// uncharged (ADR 0046). Absent class ⇒ charged, as before. Read only for a
|
|
696
|
+
// failed attempt: a succeeded run never carries a class.
|
|
697
|
+
if (
|
|
698
|
+
attempt.status === "failed" &&
|
|
699
|
+
(proof.run.class === "environment" ||
|
|
700
|
+
proof.run.class === "upstream" ||
|
|
701
|
+
proof.run.class === "decision")
|
|
702
|
+
) {
|
|
703
|
+
attempt.uncharged = proof.run.class;
|
|
704
|
+
if (proof.run.class === "decision" && typeof proof.run.decision_id === "string") {
|
|
705
|
+
attempt.blocked_on = proof.run.decision_id;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
550
708
|
return attempt;
|
|
551
709
|
}
|
|
552
|
-
const
|
|
553
|
-
if (existsSync(
|
|
710
|
+
const transcriptPath = workflowTranscriptPath(coordRoot, runId);
|
|
711
|
+
if (existsSync(transcriptPath)) {
|
|
554
712
|
let parked: string | undefined;
|
|
555
713
|
let resumed = false;
|
|
556
714
|
try {
|
|
557
715
|
for (const line of readBoundedLines(
|
|
558
|
-
|
|
716
|
+
transcriptPath,
|
|
559
717
|
MAX_EVENTS_BYTES,
|
|
560
|
-
|
|
561
|
-
"workflow
|
|
718
|
+
WORKFLOW_TRANSCRIPT_EVENT_BYTES,
|
|
719
|
+
"workflow transcript",
|
|
562
720
|
)) {
|
|
563
|
-
const value = parseObject(line, `workflow run ${runId}
|
|
721
|
+
const value = parseObject(line, `workflow run ${runId} transcript`);
|
|
564
722
|
if (value.event === "run.parked" && typeof value.approval_id === "string") {
|
|
565
723
|
parked = value.approval_id;
|
|
566
724
|
resumed = false;
|
|
@@ -576,8 +734,8 @@ function inspectAttempt(
|
|
|
576
734
|
if (workflowResumeLeaseIsLive(coordRoot, runId)) {
|
|
577
735
|
attempt.status = "running";
|
|
578
736
|
} else {
|
|
579
|
-
attempt.status = "
|
|
580
|
-
attempt.
|
|
737
|
+
attempt.status = "transcript_unreadable";
|
|
738
|
+
attempt.transcript_error = boundedTranscriptError((error as Error).message);
|
|
581
739
|
}
|
|
582
740
|
return attempt;
|
|
583
741
|
}
|
|
@@ -882,7 +1040,13 @@ function validateWorkIntent(intent: WorkIntent, workId: string): void {
|
|
|
882
1040
|
!/^[a-f0-9]{64}$/.test(intent.workflow.sha256) ||
|
|
883
1041
|
!Number.isSafeInteger(intent.max_attempts) ||
|
|
884
1042
|
intent.max_attempts < 1 ||
|
|
885
|
-
intent.max_attempts > 100
|
|
1043
|
+
intent.max_attempts > 100 ||
|
|
1044
|
+
// Optional for back-compat: absent on pre-ADR-0046 intents. When present it
|
|
1045
|
+
// must be a valid bound.
|
|
1046
|
+
(intent.max_uncharged_attempts !== undefined &&
|
|
1047
|
+
(!Number.isSafeInteger(intent.max_uncharged_attempts) ||
|
|
1048
|
+
intent.max_uncharged_attempts < 1 ||
|
|
1049
|
+
intent.max_uncharged_attempts > 100))
|
|
886
1050
|
) {
|
|
887
1051
|
throw new Error(`work intent ${workId} has an unsupported or mismatched schema`);
|
|
888
1052
|
}
|
|
@@ -1049,13 +1213,13 @@ export function acquireWorkLease(coordRoot: string, workId: string): () => void
|
|
|
1049
1213
|
return acquireWorkFileLease(coordRoot, workId, "lease.json", "is already active");
|
|
1050
1214
|
}
|
|
1051
1215
|
|
|
1052
|
-
/** @internal Work
|
|
1216
|
+
/** @internal Work transcript mutation seam. */
|
|
1053
1217
|
export function acquireWorkEventLease(coordRoot: string, workId: string): () => void {
|
|
1054
1218
|
return acquireWorkFileLease(
|
|
1055
1219
|
coordRoot,
|
|
1056
1220
|
workId,
|
|
1057
1221
|
"events.lease.json",
|
|
1058
|
-
"event
|
|
1222
|
+
"event transcript is already active",
|
|
1059
1223
|
);
|
|
1060
1224
|
}
|
|
1061
1225
|
|
|
@@ -1186,8 +1350,8 @@ function readBoundedLines(
|
|
|
1186
1350
|
return lines;
|
|
1187
1351
|
}
|
|
1188
1352
|
|
|
1189
|
-
function
|
|
1190
|
-
const normalized = message.trim() || "unknown
|
|
1353
|
+
function boundedTranscriptError(message: string): string {
|
|
1354
|
+
const normalized = message.trim() || "unknown transcript read error";
|
|
1191
1355
|
return normalized.length > MAX_REASON ? normalized.slice(0, MAX_REASON) : normalized;
|
|
1192
1356
|
}
|
|
1193
1357
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-adapter CLI metadata: binary names plus install/login hints, shared by
|
|
3
|
+
* the spawn adapters (a not-found error should say how to fix it, not just
|
|
4
|
+
* that it happened) and `harn doctor`'s workflow-adapter checks.
|
|
5
|
+
*
|
|
6
|
+
* Install commands are the vendors' official one-liners; they drift rarely
|
|
7
|
+
* but they do drift — keep this module the single place they live.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BUILTIN_ADAPTER_PROFILES } from "../adapters/profiles.ts";
|
|
11
|
+
import type { AdapterName } from "./types.ts";
|
|
12
|
+
|
|
13
|
+
export const ADAPTER_BINARIES: Record<string, string> = Object.fromEntries(
|
|
14
|
+
Object.values(BUILTIN_ADAPTER_PROFILES).map((profile) => [profile.id, profile.binary]),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export const ADAPTER_INSTALL_HINTS: Record<string, string> = Object.fromEntries(
|
|
18
|
+
Object.values(BUILTIN_ADAPTER_PROFILES).map((profile) => [profile.id, profile.installHint]),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
/** How to authenticate each CLI with a subscription login (the billing
|
|
22
|
+
* default — see billing.ts). */
|
|
23
|
+
export const ADAPTER_LOGIN_HINTS: Record<string, string> = Object.fromEntries(
|
|
24
|
+
Object.values(BUILTIN_ADAPTER_PROFILES).map((profile) => [profile.id, profile.loginHint]),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
/** One-line "it's missing, here's the fix" string for spawn adapters. */
|
|
28
|
+
export function notFoundError(adapter: AdapterName): string {
|
|
29
|
+
const binary = ADAPTER_BINARIES[adapter] ?? adapter;
|
|
30
|
+
const install = ADAPTER_INSTALL_HINTS[adapter] ?? "install the adapter CLI";
|
|
31
|
+
const login = ADAPTER_LOGIN_HINTS[adapter] ?? "authenticate the adapter CLI";
|
|
32
|
+
return `${binary} CLI not found on PATH; ` + `install: ${install} then authenticate: ${login}`;
|
|
33
|
+
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "node:fs";
|
|
13
13
|
import { dirname, join } from "node:path";
|
|
14
14
|
import type { PolicyEvaluation, PolicyRequestSummary, PolicyVerdict } from "../policy/index.ts";
|
|
15
|
-
import {
|
|
15
|
+
import { appendWorkflowTranscriptEvent } from "./transcript.ts";
|
|
16
16
|
|
|
17
17
|
export const WORKFLOW_APPROVAL_SCHEMA_VERSION = 1 as const;
|
|
18
18
|
|
|
@@ -148,7 +148,7 @@ export function resolveWorkflowApproval(input: ResolveWorkflowApprovalInput): {
|
|
|
148
148
|
`approval ${input.approvalId} is already ${resolved.status}; conflicting ${input.verdict === "allow" ? "approval" : "denial"} refused`,
|
|
149
149
|
);
|
|
150
150
|
}
|
|
151
|
-
if (applied)
|
|
151
|
+
if (applied) appendResolutionTranscript(input.coordRoot, resolved);
|
|
152
152
|
return { approval: resolved, applied };
|
|
153
153
|
}
|
|
154
154
|
|
|
@@ -214,16 +214,16 @@ export function assertWorkflowRunId(runId: string): void {
|
|
|
214
214
|
assertRunId(runId);
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
function
|
|
218
|
-
const
|
|
217
|
+
function appendResolutionTranscript(coordRoot: string, approval: WorkflowApproval): void {
|
|
218
|
+
const transcriptPath = join(
|
|
219
219
|
coordRoot,
|
|
220
220
|
".harnery",
|
|
221
221
|
"workflows",
|
|
222
222
|
approval.request.run_id,
|
|
223
|
-
"
|
|
223
|
+
"transcript.jsonl",
|
|
224
224
|
);
|
|
225
|
-
if (!existsSync(
|
|
226
|
-
|
|
225
|
+
if (!existsSync(transcriptPath) || !approval.decision) return;
|
|
226
|
+
appendWorkflowTranscriptEvent(coordRoot, approval.request.run_id, "approval.resolved", {
|
|
227
227
|
ts: approval.decision.decided_at,
|
|
228
228
|
stage: "",
|
|
229
229
|
approval_id: approval.request.id,
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Billing-mode probe: which auth a headless
|
|
2
|
+
* Billing-mode probe: which auth a headless adapter child will actually use.
|
|
3
3
|
*
|
|
4
|
-
* The engine never handles credentials — children are plain
|
|
4
|
+
* The engine never handles credentials — children are plain adapter CLIs and
|
|
5
5
|
* authenticate however that CLI does. But the CLIs prefer an exported API key
|
|
6
6
|
* over a stored (subscription) login when both are present, and that override
|
|
7
7
|
* is almost always an accident: a sourced .env or a leftover export silently
|
|
8
8
|
* moves the run from subsidized subscription billing to per-token API billing.
|
|
9
|
-
* This probe classifies the state per
|
|
9
|
+
* This probe classifies the state per adapter so the engine can refuse the
|
|
10
10
|
* silent-override case (see decision 0015 addendum) while leaving deliberate
|
|
11
11
|
* key-only hosts (CI boxes with no login) working.
|
|
12
12
|
*
|
|
13
13
|
* Detection is a heuristic over well-known credential locations; when a
|
|
14
14
|
* location can't prove presence or absence (e.g. Claude Code stores its OAuth
|
|
15
15
|
* token in the macOS keychain, not a file), the state is "unknown" and the
|
|
16
|
-
* engine never hard-fails on it — the
|
|
16
|
+
* engine never hard-fails on it — the adapter CLI itself is the final
|
|
17
17
|
* authority and will error loudly if truly unauthenticated.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { existsSync, readFileSync } from "node:fs";
|
|
21
21
|
import { homedir } from "node:os";
|
|
22
22
|
import { join } from "node:path";
|
|
23
|
-
import type {
|
|
23
|
+
import type { AdapterName } from "./types.ts";
|
|
24
24
|
|
|
25
|
-
/** The env var each
|
|
25
|
+
/** The env var each adapter CLI reads as an API key (preferred over a stored
|
|
26
26
|
* login when set). Shared with the child-env builder's subscription-only
|
|
27
27
|
* scrub. */
|
|
28
28
|
export const API_KEY_VARS: Record<string, string> = {
|
|
@@ -43,7 +43,7 @@ export type BillingMode =
|
|
|
43
43
|
| "api-key-override";
|
|
44
44
|
|
|
45
45
|
export interface BillingProbe {
|
|
46
|
-
|
|
46
|
+
adapter: AdapterName;
|
|
47
47
|
/** The env var checked (from API_KEY_VARS), or a note when the key was
|
|
48
48
|
* found in a credential file instead of the environment. */
|
|
49
49
|
apiKeySource: string | null;
|
|
@@ -57,19 +57,19 @@ export interface ProbeIo {
|
|
|
57
57
|
home?: string;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export type BillingProber = (
|
|
60
|
+
export type BillingProber = (adapter: AdapterName) => BillingProbe;
|
|
61
61
|
|
|
62
|
-
export function probeBilling(
|
|
62
|
+
export function probeBilling(adapter: AdapterName, io: ProbeIo = {}): BillingProbe {
|
|
63
63
|
const env = io.env ?? process.env;
|
|
64
64
|
const home = io.home ?? homedir();
|
|
65
|
-
const keyVar = API_KEY_VARS[
|
|
65
|
+
const keyVar = API_KEY_VARS[adapter];
|
|
66
66
|
const envKey = keyVar ? Boolean(env[keyVar]?.trim()) : false;
|
|
67
67
|
|
|
68
68
|
let login: LoginState;
|
|
69
69
|
let apiKeyPresent = envKey;
|
|
70
70
|
let apiKeySource: string | null = envKey ? keyVar : null;
|
|
71
71
|
|
|
72
|
-
switch (
|
|
72
|
+
switch (adapter) {
|
|
73
73
|
case "claude-code":
|
|
74
74
|
login = probeClaudeLogin(home);
|
|
75
75
|
break;
|
|
@@ -105,7 +105,7 @@ export function probeBilling(harness: HarnessName, io: ProbeIo = {}): BillingPro
|
|
|
105
105
|
? "api-key"
|
|
106
106
|
: "subscription";
|
|
107
107
|
|
|
108
|
-
return {
|
|
108
|
+
return { adapter, apiKeySource, apiKeyPresent, login, mode };
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/** Claude Code: OAuth login lives at ~/.claude/.credentials.json on Linux;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Child-process environment builder shared by every spawn adapter.
|
|
3
3
|
*
|
|
4
4
|
* Rules (the first is a hard-won Phase 1 spike finding):
|
|
5
|
-
* 1. **Delete inherited
|
|
6
|
-
*
|
|
5
|
+
* 1. **Delete inherited adapter-session vars, never blank them.** A nested
|
|
6
|
+
* adapter CLI under a live session inherits vars that make it exit 1 with
|
|
7
7
|
* empty output; an empty-string var still reads as set. Scrub all three
|
|
8
8
|
* families (CLAUDE*, CODEX*, CURSOR*) regardless of which adapter spawns —
|
|
9
9
|
* a codex child launched from inside a Claude Code session must not inherit
|
|
@@ -25,16 +25,24 @@
|
|
|
25
25
|
* hooks stay on, keeping heartbeat + event capture.
|
|
26
26
|
* 5. **Stamp the run id** (HARNERY_WORKFLOW_RUN_ID) so the coord layer and
|
|
27
27
|
* web UI can associate child sessions with their workflow run.
|
|
28
|
+
* 6. **Stamp the agent id** (HARNERY_WORKFLOW_AGENT_ID) so that association is
|
|
29
|
+
* per-agent rather than only per-run. The orchestrator cannot use the child's
|
|
30
|
+
* session id for this: the adapter mints that id and reports it back only in
|
|
31
|
+
* the result envelope, which is to say only once the work being watched is
|
|
32
|
+
* over. Passing in the id the orchestrator already owns is the only way to
|
|
33
|
+
* attribute in-flight activity to a particular agent.
|
|
28
34
|
*/
|
|
29
35
|
|
|
30
36
|
import { API_KEY_VARS } from "./billing.ts";
|
|
31
37
|
|
|
32
38
|
const SCRUB_PREFIXES = ["CLAUDE", "CODEX", "CURSOR"];
|
|
33
|
-
const
|
|
39
|
+
const PRESERVED_ADAPTER_VARS = new Set(["CODEX_HOME"]);
|
|
34
40
|
|
|
35
41
|
export interface ChildEnvOpts {
|
|
36
42
|
/** Delete all API-key vars so children can only use stored logins. */
|
|
37
43
|
subscriptionOnly?: boolean;
|
|
44
|
+
/** Transcript agent id (`a1`, `a2`, …) this child is running. */
|
|
45
|
+
agentId?: string;
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
export function buildChildEnv(runId?: string, opts: ChildEnvOpts = {}): Record<string, string> {
|
|
@@ -46,7 +54,7 @@ export function buildChildEnv(runId?: string, opts: ChildEnvOpts = {}): Record<s
|
|
|
46
54
|
if (
|
|
47
55
|
SCRUB_PREFIXES.some((p) => k.startsWith(p)) &&
|
|
48
56
|
!keyVars.has(k) &&
|
|
49
|
-
!
|
|
57
|
+
!PRESERVED_ADAPTER_VARS.has(k)
|
|
50
58
|
) {
|
|
51
59
|
continue;
|
|
52
60
|
}
|
|
@@ -54,5 +62,6 @@ export function buildChildEnv(runId?: string, opts: ChildEnvOpts = {}): Record<s
|
|
|
54
62
|
}
|
|
55
63
|
env.HARNERY_WORKFLOW_CHILD = "1";
|
|
56
64
|
if (runId) env.HARNERY_WORKFLOW_RUN_ID = runId;
|
|
65
|
+
if (opts.agentId) env.HARNERY_WORKFLOW_AGENT_ID = opts.agentId;
|
|
57
66
|
return env;
|
|
58
67
|
}
|