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
|
@@ -5,9 +5,9 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
|
5
5
|
import { assertWorkId, readWorkItem } from "../work/read.js";
|
|
6
6
|
import { workflowScriptDigest } from "../workflow/run-state.js";
|
|
7
7
|
import { normalizeWorkflowSpecialists } from "../workflow/specialists.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
export const
|
|
8
|
+
import { readGovernorPlans } from "./plan-read.js";
|
|
9
|
+
import { governorGraphFingerprint, MAX_GOVERNOR_PLAN_REVIEWERS, MAX_GOVERNOR_PLAN_REVISION_ROUNDS, } from "./plan-types.js";
|
|
10
|
+
export const GOVERNOR_INTENT_SCHEMA_VERSION = 1;
|
|
11
11
|
const GOAL_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
12
12
|
const MAX_TITLE = 200;
|
|
13
13
|
const MAX_OBJECTIVE = 4_000;
|
|
@@ -16,16 +16,23 @@ const MAX_INTENT_BYTES = 256 * 1024;
|
|
|
16
16
|
const MAX_TEMPLATES = 20;
|
|
17
17
|
const TEMPLATE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
18
18
|
const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
|
|
19
|
-
|
|
19
|
+
/** Ceiling on CONSECUTIVE uncharged (upstream) replans (ADR 0046). Because an
|
|
20
|
+
* uncharged replan does not spend `max_replans`, an unending vendor outage would
|
|
21
|
+
* otherwise replan forever; this bound stops it and names the outside service.
|
|
22
|
+
* Kept low — each replan is a real planner round-trip — and a module constant
|
|
23
|
+
* rather than a policy field to keep the frozen replanning schema unchanged; an
|
|
24
|
+
* environment failure self-bounds at one because it stops immediately. */
|
|
25
|
+
const MAX_UNCHARGED_REPLANS = 3;
|
|
26
|
+
export function createGovernor(input) {
|
|
20
27
|
const coordRoot = resolve(input.coordRoot);
|
|
21
|
-
const id = input.id ??
|
|
22
|
-
|
|
28
|
+
const id = input.id ?? newGovernorId();
|
|
29
|
+
assertGovernorId(id);
|
|
23
30
|
const mission = input.mission ? normalizeMission(input.mission) : undefined;
|
|
24
31
|
if (mission && !input.replanning) {
|
|
25
|
-
throw new Error("
|
|
32
|
+
throw new Error("governor mission requires a replanning policy");
|
|
26
33
|
}
|
|
27
34
|
if (!input.rootWorkId && !mission) {
|
|
28
|
-
throw new Error("
|
|
35
|
+
throw new Error("governor creation without root work requires a mission and replanning policy");
|
|
29
36
|
}
|
|
30
37
|
if (input.rootWorkId)
|
|
31
38
|
assertWorkId(input.rootWorkId);
|
|
@@ -41,9 +48,9 @@ export function createSupervisor(input) {
|
|
|
41
48
|
throw new Error("objective-first mission max_replans must exceed max_milestones to reserve completion review");
|
|
42
49
|
}
|
|
43
50
|
const intent = {
|
|
44
|
-
schema_version:
|
|
51
|
+
schema_version: GOVERNOR_INTENT_SCHEMA_VERSION,
|
|
45
52
|
id,
|
|
46
|
-
title: bounded(input.title ?? root?.intent.title ?? missionTitle(mission), "
|
|
53
|
+
title: bounded(input.title ?? root?.intent.title ?? missionTitle(mission), "governor title", MAX_TITLE),
|
|
47
54
|
root_work_id: input.rootWorkId ?? missionRootId(id),
|
|
48
55
|
specialists: normalizeWorkflowSpecialists(input.specialists),
|
|
49
56
|
limits,
|
|
@@ -52,39 +59,39 @@ export function createSupervisor(input) {
|
|
|
52
59
|
...(replanning ? { replanning } : {}),
|
|
53
60
|
created_at: new Date().toISOString(),
|
|
54
61
|
};
|
|
55
|
-
const path =
|
|
62
|
+
const path = governorIntentPath(coordRoot, id);
|
|
56
63
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
57
64
|
chmodSync(dirname(path), 0o700);
|
|
58
65
|
writePrivateJson(path, intent);
|
|
59
|
-
return
|
|
66
|
+
return readGovernor(coordRoot, id);
|
|
60
67
|
}
|
|
61
|
-
export function
|
|
62
|
-
return
|
|
68
|
+
export function readGovernor(coordRoot, goalId) {
|
|
69
|
+
return readGovernorInternal(coordRoot, goalId, false);
|
|
63
70
|
}
|
|
64
|
-
/** @internal Runner seam; ignores the lease held by this
|
|
65
|
-
export function
|
|
66
|
-
return
|
|
71
|
+
/** @internal Runner seam; ignores the lease held by this governor process. */
|
|
72
|
+
export function readGovernorIgnoringLease(coordRoot, goalId) {
|
|
73
|
+
return readGovernorInternal(coordRoot, goalId, true);
|
|
67
74
|
}
|
|
68
|
-
export function
|
|
69
|
-
const base = join(resolve(coordRoot), ".harnery", "
|
|
75
|
+
export function listGovernors(coordRoot) {
|
|
76
|
+
const base = join(resolve(coordRoot), ".harnery", "governors");
|
|
70
77
|
if (!existsSync(base))
|
|
71
78
|
return [];
|
|
72
79
|
const records = [];
|
|
73
80
|
for (const name of readdirSync(base)) {
|
|
74
|
-
if (!GOAL_ID.test(name) || !existsSync(
|
|
81
|
+
if (!GOAL_ID.test(name) || !existsSync(governorIntentPath(coordRoot, name)))
|
|
75
82
|
continue;
|
|
76
|
-
records.push(
|
|
83
|
+
records.push(readGovernor(coordRoot, name));
|
|
77
84
|
}
|
|
78
85
|
return records.sort((left, right) => right.intent.created_at.localeCompare(left.intent.created_at));
|
|
79
86
|
}
|
|
80
|
-
export function
|
|
87
|
+
export function collectGovernorWork(coordRoot, rootWorkId) {
|
|
81
88
|
const records = new Map();
|
|
82
89
|
const visiting = new Set();
|
|
83
90
|
const visit = (workId) => {
|
|
84
91
|
if (records.has(workId))
|
|
85
92
|
return;
|
|
86
93
|
if (visiting.has(workId))
|
|
87
|
-
throw new Error(`
|
|
94
|
+
throw new Error(`governor work graph contains a cycle at ${workId}`);
|
|
88
95
|
visiting.add(workId);
|
|
89
96
|
const record = readWorkItem(coordRoot, workId);
|
|
90
97
|
for (const dependency of record.intent.dependencies)
|
|
@@ -95,17 +102,17 @@ export function collectSupervisorWork(coordRoot, rootWorkId) {
|
|
|
95
102
|
visit(rootWorkId);
|
|
96
103
|
return Array.from(records.values());
|
|
97
104
|
}
|
|
98
|
-
export function
|
|
105
|
+
export function assertGovernorId(goalId) {
|
|
99
106
|
if (!GOAL_ID.test(goalId))
|
|
100
|
-
throw new Error(`invalid
|
|
107
|
+
throw new Error(`invalid governor id ${JSON.stringify(goalId)}`);
|
|
101
108
|
}
|
|
102
|
-
export function
|
|
109
|
+
export function newGovernorId() {
|
|
103
110
|
return `goal-${new Date().toISOString().replace(/[:.]/g, "-")}-${randomBytes(3).toString("hex")}`;
|
|
104
111
|
}
|
|
105
|
-
/** @internal Runner seam; one exclusive crash-recoverable
|
|
106
|
-
export function
|
|
107
|
-
|
|
108
|
-
const path = join(
|
|
112
|
+
/** @internal Runner seam; one exclusive crash-recoverable governor lease. */
|
|
113
|
+
export function acquireGovernorLease(coordRoot, goalId) {
|
|
114
|
+
readGovernorIntent(coordRoot, goalId);
|
|
115
|
+
const path = join(governorDir(coordRoot, goalId), "lease.json");
|
|
109
116
|
const owner = {
|
|
110
117
|
pid: process.pid,
|
|
111
118
|
host: hostname(),
|
|
@@ -133,11 +140,11 @@ export function acquireSupervisorLease(coordRoot, goalId) {
|
|
|
133
140
|
if (!acquire()) {
|
|
134
141
|
const existing = readLease(path);
|
|
135
142
|
if (existing && leaseIsLive(existing)) {
|
|
136
|
-
throw new Error(`
|
|
143
|
+
throw new Error(`governor ${goalId} is already running under pid ${existing.pid} on ${existing.host}`);
|
|
137
144
|
}
|
|
138
145
|
unlinkSync(path);
|
|
139
146
|
if (!acquire())
|
|
140
|
-
throw new Error(`
|
|
147
|
+
throw new Error(`governor ${goalId} lease raced with another process`);
|
|
141
148
|
}
|
|
142
149
|
return () => {
|
|
143
150
|
try {
|
|
@@ -150,15 +157,15 @@ export function acquireSupervisorLease(coordRoot, goalId) {
|
|
|
150
157
|
}
|
|
151
158
|
};
|
|
152
159
|
}
|
|
153
|
-
function
|
|
160
|
+
function readGovernorInternal(coordRootRaw, goalId, ignoreLease) {
|
|
154
161
|
const coordRoot = resolve(coordRootRaw);
|
|
155
|
-
const intent =
|
|
156
|
-
const plans =
|
|
162
|
+
const intent = readGovernorIntent(coordRoot, goalId);
|
|
163
|
+
const plans = readGovernorPlans(coordRoot, intent.id, intent.root_work_id);
|
|
157
164
|
const activeRootExists = workIntentExists(coordRoot, plans.active_root_work_id);
|
|
158
165
|
if (!activeRootExists && (!intent.mission || plans.generation > 0)) {
|
|
159
|
-
throw new Error(`
|
|
166
|
+
throw new Error(`governor ${intent.id} root work is missing`);
|
|
160
167
|
}
|
|
161
|
-
const work = activeRootExists ?
|
|
168
|
+
const work = activeRootExists ? collectGovernorWork(coordRoot, plans.active_root_work_id) : [];
|
|
162
169
|
const governed = collectGovernedWork(coordRoot, intent.root_work_id, plans, work);
|
|
163
170
|
return {
|
|
164
171
|
intent,
|
|
@@ -170,6 +177,20 @@ function readSupervisorInternal(coordRootRaw, goalId, ignoreLease) {
|
|
|
170
177
|
function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease) {
|
|
171
178
|
const root = work.find((record) => record.intent.id === plans.active_root_work_id);
|
|
172
179
|
const attemptsUsed = governed.reduce((sum, record) => sum + record.projection.attempts_used, 0);
|
|
180
|
+
// The goal budget, like the per-item budget, counts only charged attempts
|
|
181
|
+
// (ADR 0046): an uncharged environment/upstream attempt on any child does not
|
|
182
|
+
// spend the goal's total. attempts_used stays the raw count for display.
|
|
183
|
+
const chargedUsed = governed.reduce((sum, record) => sum + record.projection.charged_attempts, 0);
|
|
184
|
+
// Replans, like per-item attempts (ADR 0046), are budgeted by the CHARGED
|
|
185
|
+
// count: an uncharged planner failure (environment/upstream) does not spend
|
|
186
|
+
// max_replans. replans_used stays the raw count for display.
|
|
187
|
+
const unchargedReplans = plans.plans.filter((plan) => plan.status === "failed" && isUnchargedPlanClass(plan.class)).length;
|
|
188
|
+
const chargedReplans = plans.plans.length - unchargedReplans;
|
|
189
|
+
// Attribute the consumed replans that did not advance the graph so the
|
|
190
|
+
// projection can distinguish planner no-proposal exhaustion from reviewer
|
|
191
|
+
// rejection through existing plan seams (review receipt presence), rather than
|
|
192
|
+
// reporting both as a single undifferentiated replan exhaustion.
|
|
193
|
+
const replanConsumption = classifyReplanConsumption(plans);
|
|
173
194
|
const readyWork = work
|
|
174
195
|
.filter((record) => record.projection.state === "ready")
|
|
175
196
|
.map((record) => record.intent.id);
|
|
@@ -193,6 +214,15 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
193
214
|
const terminalBlocked = work
|
|
194
215
|
.filter((record) => record.projection.state === "blocked" && record.projection.next_action === "none")
|
|
195
216
|
.map((record) => record.intent.id);
|
|
217
|
+
// Work stopped on a person, kept apart from the rest of terminalBlocked. Every
|
|
218
|
+
// other terminal block is something an agent could in principle clear; this one
|
|
219
|
+
// can only be cleared by a ruling, so it is the thing worth saying out loud.
|
|
220
|
+
const decisionBlocked = work
|
|
221
|
+
.filter((record) => record.projection.blocked_on_decision !== undefined)
|
|
222
|
+
.map((record) => ({
|
|
223
|
+
work_id: record.intent.id,
|
|
224
|
+
decision_id: record.projection.blocked_on_decision || undefined,
|
|
225
|
+
}));
|
|
196
226
|
const attentionWork = unique([
|
|
197
227
|
...pendingApproval,
|
|
198
228
|
...(intent.automation.accept_passing_proof ? [] : reviews),
|
|
@@ -204,6 +234,12 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
204
234
|
const initialRootAccepted = intent.mission !== undefined &&
|
|
205
235
|
governed.some((record) => record.intent.id === intent.root_work_id && record.projection.state === "succeeded");
|
|
206
236
|
const milestonesCompleted = plans.milestones_completed + (initialRootAccepted ? 1 : 0);
|
|
237
|
+
const resumableDispatchable = intent.automation.resume_approved ? resumableWork : [];
|
|
238
|
+
const attemptDispatchable = [
|
|
239
|
+
...readyWork,
|
|
240
|
+
...(intent.automation.retry_blocked ? retryableWork : []),
|
|
241
|
+
];
|
|
242
|
+
const dispatchable = [...resumableDispatchable, ...attemptDispatchable];
|
|
207
243
|
const base = {
|
|
208
244
|
id: intent.id,
|
|
209
245
|
title: intent.title,
|
|
@@ -214,23 +250,25 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
214
250
|
resumable_work: resumableWork,
|
|
215
251
|
retryable_work: retryableWork,
|
|
216
252
|
attention_work: attentionWork,
|
|
253
|
+
decision_blocked_work: decisionBlocked,
|
|
217
254
|
attempts_used: attemptsUsed,
|
|
218
|
-
attempts_remaining: Math.max(0, intent.limits.max_total_attempts -
|
|
255
|
+
attempts_remaining: Math.max(0, intent.limits.max_total_attempts - chargedUsed),
|
|
219
256
|
specialists: Object.keys(intent.specialists),
|
|
220
257
|
plan_generation: plans.generation,
|
|
221
258
|
replans_used: plans.plans.length,
|
|
222
|
-
replans_remaining: Math.max(0, (intent.replanning?.max_replans ?? 0) -
|
|
259
|
+
replans_remaining: Math.max(0, (intent.replanning?.max_replans ?? 0) - chargedReplans),
|
|
223
260
|
milestones_completed: milestonesCompleted,
|
|
224
261
|
milestones_remaining: Math.max(0, (intent.mission?.max_milestones ?? 0) - milestonesCompleted),
|
|
225
262
|
pending_plan_id: plans.latest?.status === "proposed" ? plans.latest.request.id : undefined,
|
|
226
263
|
latest_plan_status: plans.latest?.status,
|
|
264
|
+
...(replanConsumption.planner_no_proposal > 0 ? { replan_consumption: replanConsumption } : {}),
|
|
227
265
|
governed_work_ids: governed.map((record) => record.intent.id),
|
|
228
266
|
};
|
|
229
|
-
if (!ignoreLease &&
|
|
267
|
+
if (!ignoreLease && governorLeaseIsLive(coordRoot, intent.id)) {
|
|
230
268
|
return {
|
|
231
269
|
...base,
|
|
232
270
|
state: "running",
|
|
233
|
-
reason: "the foreground
|
|
271
|
+
reason: "the foreground governor holds the goal lease",
|
|
234
272
|
next_action: "wait_for_run",
|
|
235
273
|
};
|
|
236
274
|
}
|
|
@@ -242,6 +280,19 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
242
280
|
next_action: "none",
|
|
243
281
|
};
|
|
244
282
|
}
|
|
283
|
+
// ADR 0050: a mission whose completion was reopened dispatches the reopened work
|
|
284
|
+
// before anything else. Without this the goal falls through to the milestone
|
|
285
|
+
// branch below, which still sees a succeeded root when the reopened item is one
|
|
286
|
+
// of its children, and would spend a replan reassessing a mission whose real
|
|
287
|
+
// remaining work is already sitting in ready_work.
|
|
288
|
+
if (intent.mission && plans.latest?.status === "reopened" && dispatchable.length > 0) {
|
|
289
|
+
return {
|
|
290
|
+
...base,
|
|
291
|
+
state: "ready",
|
|
292
|
+
reason: `mission completion was reopened; ${dispatchable.length} work item${dispatchable.length === 1 ? " is" : "s are"} dispatchable`,
|
|
293
|
+
next_action: "run",
|
|
294
|
+
};
|
|
295
|
+
}
|
|
245
296
|
if (plans.latest?.status === "proposed") {
|
|
246
297
|
return {
|
|
247
298
|
...base,
|
|
@@ -266,7 +317,58 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
266
317
|
next_action: "replan",
|
|
267
318
|
};
|
|
268
319
|
}
|
|
269
|
-
|
|
320
|
+
// ADR 0046: a planner failure that never touched the plan is handled before
|
|
321
|
+
// the replan path. Otherwise a "failed" plan falls through to canReplan and
|
|
322
|
+
// the runner replans an unchanged environment on every tick — the failure mode
|
|
323
|
+
// that burned the measured 19 "codex not found" replans.
|
|
324
|
+
if (plans.latest?.status === "failed" && plans.latest.class === "environment") {
|
|
325
|
+
// A missing precondition (the planner binary was absent). Retrying an
|
|
326
|
+
// unchanged environment cannot help, so the goal STOPS and names it. A human
|
|
327
|
+
// who installs the binary can re-run; the failure was uncharged, so the
|
|
328
|
+
// replan budget is intact.
|
|
329
|
+
return {
|
|
330
|
+
...base,
|
|
331
|
+
state: "blocked",
|
|
332
|
+
reason: `planning could not start; a required precondition is missing: ${plans.latest.reason ?? "the planner binary was absent"}`,
|
|
333
|
+
next_action: "none",
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
if (plans.latest?.status === "failed" && plans.latest.class === "decision") {
|
|
337
|
+
// The planner concluded that a human must rule before this can be planned.
|
|
338
|
+
// Stop and say so. Replanning would re-ask a question the planner already
|
|
339
|
+
// answered correctly, which is how a goal spins without a person noticing.
|
|
340
|
+
return {
|
|
341
|
+
...base,
|
|
342
|
+
state: "blocked",
|
|
343
|
+
reason: `planning is waiting on a human decision: ${plans.latest.reason ?? "the planner did not say which"}`,
|
|
344
|
+
next_action: "none",
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
if (plans.latest?.status === "failed" && plans.latest.class === "upstream") {
|
|
348
|
+
// The vendor refused. Uncharged replans do not spend max_replans, so the
|
|
349
|
+
// only brake on an unending outage is this consecutive bound; at the limit
|
|
350
|
+
// the goal stops and names the outside service, distinct from work-blocked.
|
|
351
|
+
let trailingUncharged = 0;
|
|
352
|
+
for (let index = plans.plans.length - 1; index >= 0; index--) {
|
|
353
|
+
const plan = plans.plans[index];
|
|
354
|
+
if (plan.status !== "failed" || !isUnchargedPlanClass(plan.class)) {
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
trailingUncharged++;
|
|
358
|
+
}
|
|
359
|
+
if (trailingUncharged >= MAX_UNCHARGED_REPLANS) {
|
|
360
|
+
return {
|
|
361
|
+
...base,
|
|
362
|
+
state: "blocked",
|
|
363
|
+
reason: `planning is blocked waiting on an outside service after ${trailingUncharged} consecutive uncharged attempt(s): ${plans.latest.reason ?? "the vendor refused"}`,
|
|
364
|
+
next_action: "none",
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
// Under the bound: fall through so the goal replans when nothing else is
|
|
368
|
+
// dispatchable — the vendor may have recovered. canReplan reads
|
|
369
|
+
// chargedReplans, so these uncharged attempts do not exhaust max_replans.
|
|
370
|
+
}
|
|
371
|
+
const triggerFingerprint = governorGraphFingerprint({
|
|
270
372
|
rootWorkId: plans.active_root_work_id,
|
|
271
373
|
generation: plans.generation,
|
|
272
374
|
work,
|
|
@@ -275,22 +377,23 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
275
377
|
plans.latest.request.prior_root_work_id === plans.active_root_work_id &&
|
|
276
378
|
plans.latest.status === "attention";
|
|
277
379
|
if (latestHandledSameGraph && plans.latest) {
|
|
380
|
+
const canRetry = intent.replanning !== undefined && chargedReplans < intent.replanning.max_replans;
|
|
278
381
|
return {
|
|
279
382
|
...base,
|
|
280
383
|
state: "awaiting_attention",
|
|
281
|
-
reason:
|
|
384
|
+
reason: canRetry
|
|
385
|
+
? (plans.latest.reason ?? `plan ${plans.latest.request.id} requires attention`)
|
|
386
|
+
: exhaustedAttentionReason(plans.latest, replanConsumption),
|
|
282
387
|
attention_plan_id: plans.latest.request.id,
|
|
283
|
-
next_action:
|
|
284
|
-
? "retry_plan"
|
|
285
|
-
: "none",
|
|
388
|
+
next_action: canRetry ? "retry_plan" : "none",
|
|
286
389
|
};
|
|
287
390
|
}
|
|
288
391
|
if (!root && intent.mission) {
|
|
289
|
-
if (!intent.replanning ||
|
|
392
|
+
if (!intent.replanning || chargedReplans >= intent.replanning.max_replans) {
|
|
290
393
|
return {
|
|
291
394
|
...base,
|
|
292
395
|
state: "budget_exhausted",
|
|
293
|
-
reason: "mission exhausted its planning budget before creating an initial milestone",
|
|
396
|
+
reason: replanBudgetReason("mission exhausted its planning budget before creating an initial milestone", replanConsumption),
|
|
294
397
|
next_action: "none",
|
|
295
398
|
};
|
|
296
399
|
}
|
|
@@ -302,13 +405,13 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
302
405
|
};
|
|
303
406
|
}
|
|
304
407
|
if (!root)
|
|
305
|
-
throw new Error(`
|
|
408
|
+
throw new Error(`governor ${intent.id} root work is missing`);
|
|
306
409
|
if (root.projection.state === "succeeded" && intent.mission) {
|
|
307
|
-
if (!intent.replanning ||
|
|
410
|
+
if (!intent.replanning || chargedReplans >= intent.replanning.max_replans) {
|
|
308
411
|
return {
|
|
309
412
|
...base,
|
|
310
413
|
state: "budget_exhausted",
|
|
311
|
-
reason: "mission exhausted its planning budget at a milestone boundary",
|
|
414
|
+
reason: replanBudgetReason("mission exhausted its planning budget at a milestone boundary", replanConsumption),
|
|
312
415
|
next_action: "none",
|
|
313
416
|
};
|
|
314
417
|
}
|
|
@@ -327,12 +430,6 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
327
430
|
next_action: "none",
|
|
328
431
|
};
|
|
329
432
|
}
|
|
330
|
-
const resumableDispatchable = intent.automation.resume_approved ? resumableWork : [];
|
|
331
|
-
const attemptDispatchable = [
|
|
332
|
-
...readyWork,
|
|
333
|
-
...(intent.automation.retry_blocked ? retryableWork : []),
|
|
334
|
-
];
|
|
335
|
-
const dispatchable = [...resumableDispatchable, ...attemptDispatchable];
|
|
336
433
|
if (intent.automation.accept_passing_proof && reviews.length > 0) {
|
|
337
434
|
return {
|
|
338
435
|
...base,
|
|
@@ -349,7 +446,7 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
349
446
|
next_action: "run",
|
|
350
447
|
};
|
|
351
448
|
}
|
|
352
|
-
if (
|
|
449
|
+
if (chargedUsed >= intent.limits.max_total_attempts && attemptDispatchable.length > 0) {
|
|
353
450
|
return {
|
|
354
451
|
...base,
|
|
355
452
|
state: "budget_exhausted",
|
|
@@ -397,7 +494,7 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
397
494
|
next_action: "retry",
|
|
398
495
|
};
|
|
399
496
|
}
|
|
400
|
-
if (
|
|
497
|
+
if (chargedUsed >= intent.limits.max_total_attempts && cancelled.length === 0) {
|
|
401
498
|
return {
|
|
402
499
|
...base,
|
|
403
500
|
state: "budget_exhausted",
|
|
@@ -406,7 +503,7 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
406
503
|
};
|
|
407
504
|
}
|
|
408
505
|
const canReplan = intent.replanning !== undefined &&
|
|
409
|
-
|
|
506
|
+
chargedReplans < intent.replanning.max_replans &&
|
|
410
507
|
cancelled.length === 0 &&
|
|
411
508
|
!latestHandledSameGraph;
|
|
412
509
|
if (canReplan) {
|
|
@@ -420,50 +517,127 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
420
517
|
};
|
|
421
518
|
}
|
|
422
519
|
if (intent.replanning &&
|
|
423
|
-
|
|
520
|
+
chargedReplans >= intent.replanning.max_replans &&
|
|
424
521
|
cancelled.length === 0) {
|
|
425
522
|
return {
|
|
426
523
|
...base,
|
|
427
524
|
state: "budget_exhausted",
|
|
428
|
-
reason: `goal exhausted its ${intent.replanning.max_replans} replans`,
|
|
525
|
+
reason: replanBudgetReason(`goal exhausted its ${intent.replanning.max_replans} replans`, replanConsumption),
|
|
429
526
|
next_action: "none",
|
|
430
527
|
};
|
|
431
528
|
}
|
|
432
529
|
return {
|
|
433
530
|
...base,
|
|
434
531
|
state: "blocked",
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
532
|
+
// Lead with the decisions when there are any. "3 work items need
|
|
533
|
+
// intervention" reads as a queue an agent will get to; "waiting on you to
|
|
534
|
+
// rule on X" is the only phrasing that reaches the person who can clear it.
|
|
535
|
+
reason: decisionBlocked.length > 0
|
|
536
|
+
? `${decisionBlocked.length} work item${decisionBlocked.length === 1 ? " is" : "s are"} waiting on a human decision: ${describeDecisionBlocks(decisionBlocked)}`
|
|
537
|
+
: attentionWork.length > 0
|
|
538
|
+
? `${attentionWork.length} work item${attentionWork.length === 1 ? " needs" : "s need"} intervention`
|
|
539
|
+
: "goal graph has no legal progress action",
|
|
438
540
|
next_action: "none",
|
|
439
541
|
};
|
|
440
542
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
const
|
|
543
|
+
/** Bounded, operator-readable list of what is waiting on a ruling. */
|
|
544
|
+
function describeDecisionBlocks(blocks) {
|
|
545
|
+
const shown = blocks
|
|
546
|
+
.slice(0, 3)
|
|
547
|
+
.map((b) => (b.decision_id ? `${b.work_id} (${b.decision_id})` : b.work_id))
|
|
548
|
+
.join(", ");
|
|
549
|
+
return blocks.length > 3 ? `${shown}, +${blocks.length - 3} more` : shown;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Classify each consumed replan that did not advance the graph, using existing
|
|
553
|
+
* plan seams only. A plan carries a review receipt exactly when a proposal was
|
|
554
|
+
* produced and independently reviewed, so its presence separates a reviewer
|
|
555
|
+
* rejection from a planner run that produced no reviewable proposal. Uncharged
|
|
556
|
+
* environment/upstream planner failures (ADR 0046) do not spend the replan
|
|
557
|
+
* budget and are attributed to their outside precondition, not counted here.
|
|
558
|
+
*/
|
|
559
|
+
/** Whether a failed plan told us nothing about the plan, and so must not spend
|
|
560
|
+
* a replan (ADR 0046). All three classes mean "replanning this cannot help":
|
|
561
|
+
* the binary is missing, the vendor is down, or a person has to rule. */
|
|
562
|
+
function isUnchargedPlanClass(value) {
|
|
563
|
+
return value === "environment" || value === "upstream" || value === "decision";
|
|
564
|
+
}
|
|
565
|
+
function classifyReplanConsumption(plans) {
|
|
566
|
+
let reviewerRejection = 0;
|
|
567
|
+
let plannerNoProposal = 0;
|
|
568
|
+
for (const plan of plans.plans) {
|
|
569
|
+
if (plan.status === "failed" && isUnchargedPlanClass(plan.class)) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (plan.review && plan.review.status !== "passed") {
|
|
573
|
+
reviewerRejection++;
|
|
574
|
+
}
|
|
575
|
+
else if (!plan.review &&
|
|
576
|
+
(plan.status === "attention" ||
|
|
577
|
+
plan.status === "interrupted" ||
|
|
578
|
+
plan.status === "failed" ||
|
|
579
|
+
plan.status === "retry_requested")) {
|
|
580
|
+
// `retry_requested` is a planner no-proposal attention the operator asked to
|
|
581
|
+
// replan: the original run still produced no reviewable proposal and still
|
|
582
|
+
// spent a charged replan, so it belongs in this bucket even though a later
|
|
583
|
+
// plan superseded it. Omitting it would misattribute a retried no-proposal
|
|
584
|
+
// to nothing and let the reason read as pure review exhaustion.
|
|
585
|
+
plannerNoProposal++;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return { reviewer_rejection: reviewerRejection, planner_no_proposal: plannerNoProposal };
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Reason for a goal held at `awaiting_attention/none` because its latest plan is
|
|
592
|
+
* an unresolved attention and no replan slot remains. The latest plan's own
|
|
593
|
+
* per-plan reason always leads (a reviewed-and-rejected latest reports its
|
|
594
|
+
* review-round exhaustion verbatim), then `replanBudgetReason` names the planner
|
|
595
|
+
* no-proposal share of the consumed budget. This keeps the `projection.reason`
|
|
596
|
+
* field carrying the same truth the operator-visible row does: a goal whose
|
|
597
|
+
* budget was mostly spent by planner no-proposal runs is not attributed wholesale
|
|
598
|
+
* to review just because its final, reviewed plan happened to be rejected.
|
|
599
|
+
*/
|
|
600
|
+
function exhaustedAttentionReason(latest, consumption) {
|
|
601
|
+
const fallback = latest.reason ?? `plan ${latest.request.id} requires attention`;
|
|
602
|
+
return replanBudgetReason(fallback, consumption);
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Append planner no-proposal attribution to a replan-exhaustion reason. Returns
|
|
606
|
+
* the base reason unchanged when no consumed replan was a planner no-proposal
|
|
607
|
+
* outcome, so goals without that history project exactly as before.
|
|
608
|
+
*/
|
|
609
|
+
function replanBudgetReason(base, consumption) {
|
|
610
|
+
if (consumption.planner_no_proposal <= 0)
|
|
611
|
+
return base;
|
|
612
|
+
const count = consumption.planner_no_proposal;
|
|
613
|
+
return `${base}; ${count} replan${count === 1 ? "" : "s"} ended with the planner producing no proposal`;
|
|
614
|
+
}
|
|
615
|
+
function readGovernorIntent(coordRoot, goalId) {
|
|
616
|
+
assertGovernorId(goalId);
|
|
617
|
+
const path = governorIntentPath(coordRoot, goalId);
|
|
444
618
|
if (!existsSync(path))
|
|
445
|
-
throw new Error(`
|
|
619
|
+
throw new Error(`governor ${goalId} does not exist`);
|
|
446
620
|
const size = statSync(path).size;
|
|
447
621
|
if (size <= 0 || size > MAX_INTENT_BYTES) {
|
|
448
|
-
throw new Error(`
|
|
622
|
+
throw new Error(`governor intent has invalid size ${size}`);
|
|
449
623
|
}
|
|
450
624
|
let intent;
|
|
451
625
|
try {
|
|
452
626
|
intent = JSON.parse(readFileSync(path, "utf8"));
|
|
453
627
|
}
|
|
454
628
|
catch (error) {
|
|
455
|
-
throw new Error(`cannot parse
|
|
629
|
+
throw new Error(`cannot parse governor intent ${goalId}: ${error.message}`);
|
|
456
630
|
}
|
|
457
631
|
validateIntent(intent, goalId);
|
|
458
632
|
return intent;
|
|
459
633
|
}
|
|
460
634
|
function validateIntent(intent, goalId) {
|
|
461
|
-
if (intent.schema_version !==
|
|
635
|
+
if (intent.schema_version !== GOVERNOR_INTENT_SCHEMA_VERSION ||
|
|
462
636
|
intent.id !== goalId ||
|
|
463
637
|
!validTimestamp(intent.created_at)) {
|
|
464
|
-
throw new Error(`
|
|
638
|
+
throw new Error(`governor intent ${goalId} has an unsupported or mismatched schema`);
|
|
465
639
|
}
|
|
466
|
-
bounded(intent.title, "
|
|
640
|
+
bounded(intent.title, "governor title", MAX_TITLE);
|
|
467
641
|
assertWorkId(intent.root_work_id);
|
|
468
642
|
if (intent.mission) {
|
|
469
643
|
const normalizedMission = normalizeMission({
|
|
@@ -472,40 +646,40 @@ function validateIntent(intent, goalId) {
|
|
|
472
646
|
maxMilestones: intent.mission.max_milestones,
|
|
473
647
|
});
|
|
474
648
|
if (JSON.stringify(normalizedMission) !== JSON.stringify(intent.mission)) {
|
|
475
|
-
throw new Error(`
|
|
649
|
+
throw new Error(`governor intent ${goalId} mission is not canonical`);
|
|
476
650
|
}
|
|
477
651
|
if (!intent.replanning) {
|
|
478
|
-
throw new Error(`
|
|
652
|
+
throw new Error(`governor intent ${goalId} mission requires replanning`);
|
|
479
653
|
}
|
|
480
654
|
}
|
|
481
655
|
const specialists = normalizeWorkflowSpecialists(intent.specialists);
|
|
482
656
|
if (JSON.stringify(specialists) !== JSON.stringify(intent.specialists)) {
|
|
483
|
-
throw new Error(`
|
|
657
|
+
throw new Error(`governor intent ${goalId} specialists are not canonical`);
|
|
484
658
|
}
|
|
485
659
|
if (JSON.stringify(normalizeLimits(intent.limits)) !== JSON.stringify(intent.limits)) {
|
|
486
|
-
throw new Error(`
|
|
660
|
+
throw new Error(`governor intent ${goalId} limits are not canonical`);
|
|
487
661
|
}
|
|
488
662
|
if (JSON.stringify(normalizeAutomation(intent.automation)) !== JSON.stringify(intent.automation)) {
|
|
489
|
-
throw new Error(`
|
|
663
|
+
throw new Error(`governor intent ${goalId} automation policy is not canonical`);
|
|
490
664
|
}
|
|
491
665
|
if (intent.replanning)
|
|
492
666
|
validateReplanning(intent.replanning, specialists, intent.limits, goalId);
|
|
493
667
|
}
|
|
494
668
|
function normalizeMission(input) {
|
|
495
669
|
if (!Array.isArray(input.acceptance)) {
|
|
496
|
-
throw new Error("
|
|
670
|
+
throw new Error("governor mission acceptance must be an array");
|
|
497
671
|
}
|
|
498
672
|
if (input.acceptance.length < 1 || input.acceptance.length > MAX_ACCEPTANCE) {
|
|
499
|
-
throw new Error(`
|
|
673
|
+
throw new Error(`governor mission acceptance must contain 1 to ${MAX_ACCEPTANCE} criteria`);
|
|
500
674
|
}
|
|
501
|
-
const acceptance = input.acceptance.map((criterion, index) => bounded(criterion, `
|
|
675
|
+
const acceptance = input.acceptance.map((criterion, index) => bounded(criterion, `governor mission acceptance[${index}]`, 500));
|
|
502
676
|
if (new Set(acceptance).size !== acceptance.length) {
|
|
503
|
-
throw new Error("
|
|
677
|
+
throw new Error("governor mission acceptance criteria must be unique");
|
|
504
678
|
}
|
|
505
679
|
return {
|
|
506
|
-
objective: bounded(input.objective, "
|
|
680
|
+
objective: bounded(input.objective, "governor mission objective", MAX_OBJECTIVE),
|
|
507
681
|
acceptance,
|
|
508
|
-
max_milestones: positive(input.maxMilestones ?? 4, "
|
|
682
|
+
max_milestones: positive(input.maxMilestones ?? 4, "governor mission max_milestones", 20),
|
|
509
683
|
};
|
|
510
684
|
}
|
|
511
685
|
function missionTitle(mission) {
|
|
@@ -515,12 +689,12 @@ function missionTitle(mission) {
|
|
|
515
689
|
}
|
|
516
690
|
function normalizeLimits(input) {
|
|
517
691
|
return {
|
|
518
|
-
max_cycles: positive(input?.max_cycles ?? 50, "
|
|
519
|
-
max_runtime_ms: positive(input?.max_runtime_ms ?? 4 * 60 * 60 * 1_000, "
|
|
520
|
-
max_parallel_work: positive(input?.max_parallel_work ?? 1, "
|
|
521
|
-
max_total_attempts: positive(input?.max_total_attempts ?? 100, "
|
|
522
|
-
max_agents_per_work: positive(input?.max_agents_per_work ?? 20, "
|
|
523
|
-
agent_concurrency: positive(input?.agent_concurrency ?? 4, "
|
|
692
|
+
max_cycles: positive(input?.max_cycles ?? 50, "governor max_cycles", 1_000),
|
|
693
|
+
max_runtime_ms: positive(input?.max_runtime_ms ?? 4 * 60 * 60 * 1_000, "governor max_runtime_ms", 7 * 24 * 60 * 60 * 1_000),
|
|
694
|
+
max_parallel_work: positive(input?.max_parallel_work ?? 1, "governor max_parallel_work", 20),
|
|
695
|
+
max_total_attempts: positive(input?.max_total_attempts ?? 100, "governor max_total_attempts", 10_000),
|
|
696
|
+
max_agents_per_work: positive(input?.max_agents_per_work ?? 20, "governor max_agents_per_work", 1_000),
|
|
697
|
+
agent_concurrency: positive(input?.agent_concurrency ?? 4, "governor agent_concurrency", 100),
|
|
524
698
|
};
|
|
525
699
|
}
|
|
526
700
|
function normalizeAutomation(input) {
|
|
@@ -583,8 +757,8 @@ function normalizeReviewPolicy(input, specialists, plannerSpecialist, limits) {
|
|
|
583
757
|
if (!Array.isArray(input.reviewerSpecialists) || input.reviewerSpecialists.length < 1) {
|
|
584
758
|
throw new Error("replanning review requires at least one reviewer specialist");
|
|
585
759
|
}
|
|
586
|
-
if (input.reviewerSpecialists.length >
|
|
587
|
-
throw new Error(`replanning review cannot exceed ${
|
|
760
|
+
if (input.reviewerSpecialists.length > MAX_GOVERNOR_PLAN_REVIEWERS) {
|
|
761
|
+
throw new Error(`replanning review cannot exceed ${MAX_GOVERNOR_PLAN_REVIEWERS} reviewer specialists`);
|
|
588
762
|
}
|
|
589
763
|
const reviewerSpecialists = input.reviewerSpecialists.map((specialist, index) => bounded(specialist, `reviewer specialist[${index}]`, 100));
|
|
590
764
|
if (new Set(reviewerSpecialists).size !== reviewerSpecialists.length) {
|
|
@@ -598,7 +772,7 @@ function normalizeReviewPolicy(input, specialists, plannerSpecialist, limits) {
|
|
|
598
772
|
throw new Error("reviewer specialist cannot be the planner specialist");
|
|
599
773
|
}
|
|
600
774
|
}
|
|
601
|
-
const maxRevisionRounds = nonNegative(input.maxRevisionRounds, "replanning review max_revision_rounds",
|
|
775
|
+
const maxRevisionRounds = nonNegative(input.maxRevisionRounds, "replanning review max_revision_rounds", MAX_GOVERNOR_PLAN_REVISION_ROUNDS);
|
|
602
776
|
const worstCaseAgents = 1 + reviewerSpecialists.length * (maxRevisionRounds + 1) + maxRevisionRounds;
|
|
603
777
|
if (worstCaseAgents > limits.max_agents_per_work) {
|
|
604
778
|
throw new Error(`replanning review worst-case ${worstCaseAgents} agents exceeds max_agents_per_work ${limits.max_agents_per_work}`);
|
|
@@ -610,18 +784,18 @@ function normalizeReviewPolicy(input, specialists, plannerSpecialist, limits) {
|
|
|
610
784
|
}
|
|
611
785
|
function validateReplanning(policy, specialists, limits, goalId) {
|
|
612
786
|
if (!specialists[policy.planner_specialist]) {
|
|
613
|
-
throw new Error(`
|
|
787
|
+
throw new Error(`governor ${goalId} planner specialist is not in the frozen team`);
|
|
614
788
|
}
|
|
615
789
|
boolean(policy.auto_apply, "replanning auto_apply");
|
|
616
790
|
positive(policy.max_replans, "replanning max_replans", 100);
|
|
617
791
|
positive(policy.max_work_items_per_plan, "replanning max_work_items_per_plan", 25);
|
|
618
792
|
positive(policy.max_total_work_items, "replanning max_total_work_items", 100);
|
|
619
793
|
if (policy.max_work_items_per_plan > policy.max_total_work_items) {
|
|
620
|
-
throw new Error(`
|
|
794
|
+
throw new Error(`governor ${goalId} replanning work-item bounds are inconsistent`);
|
|
621
795
|
}
|
|
622
796
|
const entries = Object.entries(policy.templates ?? {});
|
|
623
797
|
if (entries.length < 1 || entries.length > MAX_TEMPLATES) {
|
|
624
|
-
throw new Error(`
|
|
798
|
+
throw new Error(`governor ${goalId} has invalid replanning templates`);
|
|
625
799
|
}
|
|
626
800
|
let rootCapable = false;
|
|
627
801
|
for (const [id, template] of entries) {
|
|
@@ -632,45 +806,45 @@ function validateReplanning(policy, specialists, limits, goalId) {
|
|
|
632
806
|
template.max_attempts < 1 ||
|
|
633
807
|
template.max_attempts > 100 ||
|
|
634
808
|
typeof template.root !== "boolean") {
|
|
635
|
-
throw new Error(`
|
|
809
|
+
throw new Error(`governor ${goalId} replanning template ${id} is invalid`);
|
|
636
810
|
}
|
|
637
811
|
rootCapable ||= template.root;
|
|
638
812
|
}
|
|
639
813
|
if (!rootCapable)
|
|
640
|
-
throw new Error(`
|
|
814
|
+
throw new Error(`governor ${goalId} has no root-capable replanning template`);
|
|
641
815
|
if (policy.review !== undefined) {
|
|
642
816
|
const review = policy.review;
|
|
643
817
|
if (!Array.isArray(review.reviewer_specialists) ||
|
|
644
818
|
review.reviewer_specialists.length < 1 ||
|
|
645
|
-
review.reviewer_specialists.length >
|
|
819
|
+
review.reviewer_specialists.length > MAX_GOVERNOR_PLAN_REVIEWERS ||
|
|
646
820
|
!Number.isSafeInteger(review.max_revision_rounds) ||
|
|
647
821
|
review.max_revision_rounds < 0 ||
|
|
648
|
-
review.max_revision_rounds >
|
|
649
|
-
throw new Error(`
|
|
822
|
+
review.max_revision_rounds > MAX_GOVERNOR_PLAN_REVISION_ROUNDS) {
|
|
823
|
+
throw new Error(`governor ${goalId} replanning review policy is invalid`);
|
|
650
824
|
}
|
|
651
825
|
if (new Set(review.reviewer_specialists).size !== review.reviewer_specialists.length) {
|
|
652
|
-
throw new Error(`
|
|
826
|
+
throw new Error(`governor ${goalId} replanning reviewers are not unique`);
|
|
653
827
|
}
|
|
654
828
|
for (const specialist of review.reviewer_specialists) {
|
|
655
829
|
if (!specialists[specialist]) {
|
|
656
|
-
throw new Error(`
|
|
830
|
+
throw new Error(`governor ${goalId} reviewer specialist is not in the frozen team`);
|
|
657
831
|
}
|
|
658
832
|
if (specialist === policy.planner_specialist) {
|
|
659
|
-
throw new Error(`
|
|
833
|
+
throw new Error(`governor ${goalId} reviewer specialist cannot be planner`);
|
|
660
834
|
}
|
|
661
835
|
}
|
|
662
836
|
const worstCaseAgents = 1 +
|
|
663
837
|
review.reviewer_specialists.length * (review.max_revision_rounds + 1) +
|
|
664
838
|
review.max_revision_rounds;
|
|
665
839
|
if (worstCaseAgents > limits.max_agents_per_work) {
|
|
666
|
-
throw new Error(`
|
|
840
|
+
throw new Error(`governor ${goalId} review policy exceeds the agent budget`);
|
|
667
841
|
}
|
|
668
842
|
}
|
|
669
843
|
}
|
|
670
844
|
function collectGovernedWork(coordRoot, originalRootWorkId, plans, active) {
|
|
671
845
|
const governed = new Map();
|
|
672
846
|
if (workIntentExists(coordRoot, originalRootWorkId)) {
|
|
673
|
-
for (const record of
|
|
847
|
+
for (const record of collectGovernorWork(coordRoot, originalRootWorkId)) {
|
|
674
848
|
governed.set(record.intent.id, record);
|
|
675
849
|
}
|
|
676
850
|
}
|
|
@@ -686,15 +860,15 @@ function workIntentExists(coordRoot, workId) {
|
|
|
686
860
|
function missionRootId(goalId) {
|
|
687
861
|
return `${goalId.slice(0, 87)}-mission-root`;
|
|
688
862
|
}
|
|
689
|
-
function
|
|
690
|
-
|
|
691
|
-
return join(resolve(coordRoot), ".harnery", "
|
|
863
|
+
function governorDir(coordRoot, goalId) {
|
|
864
|
+
assertGovernorId(goalId);
|
|
865
|
+
return join(resolve(coordRoot), ".harnery", "governors", goalId);
|
|
692
866
|
}
|
|
693
|
-
function
|
|
694
|
-
return join(
|
|
867
|
+
function governorIntentPath(coordRoot, goalId) {
|
|
868
|
+
return join(governorDir(coordRoot, goalId), "intent.json");
|
|
695
869
|
}
|
|
696
|
-
function
|
|
697
|
-
const lease = readLease(join(
|
|
870
|
+
function governorLeaseIsLive(coordRoot, goalId) {
|
|
871
|
+
const lease = readLease(join(governorDir(coordRoot, goalId), "lease.json"));
|
|
698
872
|
return lease ? leaseIsLive(lease) : false;
|
|
699
873
|
}
|
|
700
874
|
function readLease(path) {
|
|
@@ -733,7 +907,7 @@ function leaseIsLive(lease) {
|
|
|
733
907
|
function writePrivateJson(path, value) {
|
|
734
908
|
const body = `${JSON.stringify(value, null, 2)}\n`;
|
|
735
909
|
if (Buffer.byteLength(body) > MAX_INTENT_BYTES) {
|
|
736
|
-
throw new Error(`
|
|
910
|
+
throw new Error(`governor intent exceeds ${MAX_INTENT_BYTES} bytes`);
|
|
737
911
|
}
|
|
738
912
|
writeFileSync(path, body, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
739
913
|
chmodSync(path, 0o600);
|
|
@@ -762,7 +936,7 @@ function nonNegative(value, field, max) {
|
|
|
762
936
|
}
|
|
763
937
|
function boolean(value, field) {
|
|
764
938
|
if (typeof value !== "boolean")
|
|
765
|
-
throw new Error(`
|
|
939
|
+
throw new Error(`governor ${field} must be boolean`);
|
|
766
940
|
return value;
|
|
767
941
|
}
|
|
768
942
|
function validTimestamp(value) {
|