harnery 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/commander.d.ts +8 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +14 -13
- package/dist/commands/adapter.d.ts +12 -0
- package/dist/commands/adapter.d.ts.map +1 -0
- package/dist/commands/{harness.js → adapter.js} +35 -35
- package/dist/commands/agents.d.ts +3 -2
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +61 -60
- package/dist/commands/artifacts.d.ts +4 -0
- package/dist/commands/artifacts.d.ts.map +1 -0
- package/dist/commands/artifacts.js +143 -0
- package/dist/commands/backup.d.ts +1 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +5 -2
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +310 -7
- package/dist/commands/checkpoint.d.ts +12 -0
- package/dist/commands/checkpoint.d.ts.map +1 -0
- package/dist/commands/checkpoint.js +162 -0
- package/dist/commands/context.d.ts +2 -2
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +3 -151
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +31 -7
- package/dist/commands/deinit.d.ts +2 -2
- package/dist/commands/deinit.js +15 -15
- package/dist/commands/doctor.js +23 -23
- package/dist/commands/governor.d.ts +4 -0
- package/dist/commands/governor.d.ts.map +1 -0
- package/dist/commands/{supervisor.js → governor.js} +133 -108
- package/dist/commands/init.d.ts +12 -12
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +44 -30
- package/dist/commands/{session.d.ts → journal.d.ts} +2 -2
- package/dist/commands/journal.d.ts.map +1 -0
- package/dist/commands/{scratch.js → journal.js} +47 -47
- package/dist/commands/policy.js +1 -1
- package/dist/commands/presence.js +2 -2
- package/dist/commands/sync.d.ts +1 -1
- package/dist/commands/sync.js +3 -3
- package/dist/commands/tunnel.d.ts +16 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +193 -23
- package/dist/commands/web-fetch.js +1 -1
- package/dist/commands/web.d.ts +19 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +51 -0
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +38 -22
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +35 -28
- package/dist/core/{harnesses → adapters}/attest-projection.d.ts +2 -2
- package/dist/core/adapters/attest-projection.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attest.d.ts +16 -16
- package/dist/core/adapters/attest.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attest.js +9 -9
- package/dist/core/{harnesses → adapters}/attestation.d.ts +16 -16
- package/dist/core/adapters/attestation.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attestation.js +20 -20
- package/dist/core/{harnesses → adapters}/bench.d.ts +12 -12
- package/dist/core/adapters/bench.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/bench.js +12 -12
- package/dist/core/adapters/index.d.ts +13 -0
- package/dist/core/adapters/index.d.ts.map +1 -0
- package/dist/core/adapters/index.js +6 -0
- package/dist/core/{harnesses → adapters}/profiles.d.ts +11 -11
- package/dist/core/adapters/profiles.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/profiles.js +11 -11
- package/dist/core/adapters/registry.d.ts +15 -0
- package/dist/core/adapters/registry.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/registry.js +11 -11
- package/dist/core/{harnesses → adapters}/types.d.ts +23 -23
- package/dist/core/adapters/types.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/types.js +1 -1
- package/dist/core/agents/canonical-emit.d.ts +4 -4
- package/dist/core/agents/canonical-emit.js +5 -5
- package/dist/core/agents/cli-emit.js +1 -1
- package/dist/core/agents/cli.js +30 -26
- package/dist/core/agents/codex-replay.d.ts +1 -1
- package/dist/core/agents/codex-replay.js +6 -6
- package/dist/core/agents/coord-client.d.ts +21 -2
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +122 -27
- package/dist/core/agents/events/consume.d.ts +1 -1
- package/dist/core/agents/events/emit.d.ts +5 -6
- package/dist/core/agents/events/emit.d.ts.map +1 -1
- package/dist/core/agents/events/emit.js +2 -2
- package/dist/core/agents/render/prompt-context.d.ts +21 -9
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +82 -30
- package/dist/core/agents/render/session-context.d.ts +1 -1
- package/dist/core/agents/render/session-context.js +5 -5
- package/dist/core/agents/rules/claim-conflict.d.ts +2 -6
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +1 -1
- package/dist/core/agents/rules/commit-conflict.js +98 -22
- package/dist/core/agents/rules/stop-hook.d.ts +9 -9
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +23 -5
- package/dist/core/agents/rules/verdict.d.ts +14 -0
- package/dist/core/agents/rules/verdict.d.ts.map +1 -0
- package/dist/core/agents/rules/verdict.js +1 -0
- package/dist/core/agents/session-events.js +3 -3
- package/dist/core/agents/state/council.d.ts +1 -1
- package/dist/core/agents/state/council.js +1 -1
- package/dist/core/agents/state/heartbeat-projector.d.ts +3 -2
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +14 -10
- package/dist/core/agents/state/heartbeat-writer.d.ts +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +12 -12
- package/dist/core/agents/state/{scratch.d.ts → journal.d.ts} +7 -7
- package/dist/core/agents/state/{scratch.d.ts.map → journal.d.ts.map} +1 -1
- package/dist/core/agents/state/{scratch.js → journal.js} +12 -12
- package/dist/core/agents/state/pidmap.d.ts +28 -4
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +130 -15
- package/dist/core/agents/state/proc-start.d.ts +95 -0
- package/dist/core/agents/state/proc-start.d.ts.map +1 -0
- package/dist/core/agents/state/proc-start.js +199 -0
- package/dist/core/agents/state/shell-mutation.d.ts +1 -1
- package/dist/core/agents/state/shell-mutation.js +1 -1
- package/dist/core/agents/state/stale-sweep.js +7 -7
- package/dist/core/artifacts/index.d.ts +86 -0
- package/dist/core/artifacts/index.d.ts.map +1 -0
- package/dist/core/artifacts/index.js +421 -0
- package/dist/core/config.d.ts +9 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +26 -2
- package/dist/core/context/index.d.ts +12 -12
- package/dist/core/context/index.js +10 -10
- package/dist/core/governor/adapter-spread.d.ts +52 -0
- package/dist/core/governor/adapter-spread.d.ts.map +1 -0
- package/dist/core/governor/adapter-spread.js +52 -0
- package/dist/core/governor/index.d.ts +7 -0
- package/dist/core/governor/index.d.ts.map +1 -0
- package/dist/core/governor/index.js +6 -0
- package/dist/core/governor/plan-api.d.ts +38 -0
- package/dist/core/governor/plan-api.d.ts.map +1 -0
- package/dist/core/governor/plan-api.js +83 -0
- package/dist/core/governor/plan-read.d.ts +5 -0
- package/dist/core/governor/plan-read.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/plan-read.js +66 -52
- package/dist/core/{supervisor → governor}/plan-types.d.ts +57 -56
- package/dist/core/governor/plan-types.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/plan-types.js +4 -4
- package/dist/core/governor/planning.d.ts +48 -0
- package/dist/core/governor/planning.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/planning.js +125 -94
- package/dist/core/governor/read.d.ts +6 -0
- package/dist/core/governor/read.d.ts.map +1 -0
- package/dist/core/governor/read.js +4 -0
- package/dist/core/governor/runner.d.ts +37 -0
- package/dist/core/governor/runner.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/runner.js +15 -15
- package/dist/core/governor/service-read.d.ts +6 -0
- package/dist/core/governor/service-read.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/service-read.js +12 -12
- package/dist/core/governor/service.d.ts +114 -0
- package/dist/core/governor/service.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/service.js +57 -57
- package/dist/core/governor/state.d.ts +116 -0
- package/dist/core/governor/state.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/state.js +228 -108
- package/dist/core/hooks/adapter/detect.d.ts +9 -0
- package/dist/core/hooks/{harness → adapter}/detect.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/detect.js +8 -8
- package/dist/core/hooks/{harness → adapter}/events.d.ts +15 -14
- package/dist/core/hooks/adapter/events.d.ts.map +1 -0
- package/dist/core/hooks/{harness → adapter}/events.js +5 -5
- package/dist/core/hooks/{harness → adapter}/output.d.ts +20 -14
- package/dist/core/hooks/adapter/output.d.ts.map +1 -0
- package/dist/core/hooks/{harness → adapter}/output.js +30 -22
- package/dist/core/hooks/{harness → adapter}/parse.d.ts +5 -5
- package/dist/core/hooks/{harness → adapter}/parse.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/parse.js +4 -4
- package/dist/core/hooks/{harness → adapter}/wiring.d.ts +15 -15
- package/dist/core/hooks/{harness → adapter}/wiring.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/wiring.js +13 -13
- package/dist/core/hooks/cli.d.ts +3 -3
- package/dist/core/hooks/cli.js +175 -151
- package/dist/core/hooks/effects/image-capture.d.ts +5 -5
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +3 -3
- package/dist/core/hooks/effects/index.d.ts +12 -12
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +17 -17
- package/dist/core/hooks/events/emit.d.ts +2 -2
- package/dist/core/hooks/events/emit.d.ts.map +1 -1
- package/dist/core/hooks/events/emit.js +1 -1
- package/dist/core/hooks/events/rotate.js +1 -1
- package/dist/core/hooks/events/schema.d.ts +13 -5
- package/dist/core/hooks/events/schema.d.ts.map +1 -1
- package/dist/core/hooks/guard-path.d.ts +2 -2
- package/dist/core/hooks/guard-path.js +2 -2
- package/dist/core/hooks/index.d.ts +1 -1
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/resolve/anchor.d.ts +14 -5
- package/dist/core/hooks/resolve/anchor.d.ts.map +1 -1
- package/dist/core/hooks/resolve/anchor.js +64 -7
- package/dist/core/hooks/resolve/coord-root.d.ts +3 -3
- package/dist/core/hooks/resolve/coord-root.js +4 -4
- package/dist/core/hooks/resolve/intent.js +1 -1
- package/dist/core/hooks/resolve/owner.d.ts +1 -1
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +11 -5
- package/dist/core/{scratch → journal}/index.d.ts +25 -25
- package/dist/core/{scratch → journal}/index.d.ts.map +1 -1
- package/dist/core/{scratch → journal}/index.js +38 -38
- package/dist/core/policy/evaluate.d.ts +1 -1
- package/dist/core/policy/evaluate.d.ts.map +1 -1
- package/dist/core/policy/evaluate.js +7 -7
- package/dist/core/policy/types.d.ts +3 -3
- package/dist/core/policy/types.d.ts.map +1 -1
- package/dist/core/work/state.d.ts +12 -4
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +50 -17
- package/dist/core/workflow/adapters.d.ts +17 -0
- package/dist/core/workflow/adapters.d.ts.map +1 -0
- package/dist/core/workflow/adapters.js +21 -0
- package/dist/core/workflow/approvals.js +6 -6
- package/dist/core/workflow/billing.d.ts +9 -9
- package/dist/core/workflow/billing.js +9 -9
- package/dist/core/workflow/child-env.d.ts +10 -2
- package/dist/core/workflow/child-env.d.ts.map +1 -1
- package/dist/core/workflow/child-env.js +12 -4
- package/dist/core/workflow/engine.d.ts +15 -5
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +138 -83
- package/dist/core/workflow/evidence-preflight.d.ts +24 -0
- package/dist/core/workflow/evidence-preflight.d.ts.map +1 -0
- package/dist/core/workflow/evidence-preflight.js +134 -0
- package/dist/core/workflow/index.d.ts +1 -1
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/proof.d.ts +11 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +45 -36
- package/dist/core/workflow/run-state.d.ts +1 -1
- package/dist/core/workflow/run-state.js +7 -7
- package/dist/core/workflow/sandbox-projection.d.ts +6 -6
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +11 -11
- package/dist/core/workflow/spawn-claude.d.ts +3 -3
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +9 -6
- package/dist/core/workflow/spawn-codex.d.ts +4 -4
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +9 -6
- package/dist/core/workflow/spawn-cursor.d.ts +4 -4
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +9 -6
- package/dist/core/workflow/spawn-failure.d.ts +1 -1
- package/dist/core/workflow/spawn-failure.js +1 -1
- package/dist/core/workflow/specialists.js +2 -2
- package/dist/core/workflow/{journal.d.ts → transcript.d.ts} +8 -8
- package/dist/core/workflow/transcript.d.ts.map +1 -0
- package/dist/core/workflow/{journal.js → transcript.js} +14 -14
- package/dist/core/workflow/types.d.ts +98 -51
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/types.js +11 -1
- package/dist/core/workflow/validate.d.ts +1 -1
- package/dist/core/workflow/validate.js +1 -1
- package/dist/core/workflow/workspaces/cancellation.js +2 -2
- package/dist/core/workflow/workspaces/cleanup.js +2 -2
- package/dist/core/workflow/workspaces/execution.js +6 -6
- package/dist/core/workflow/workspaces/inspect.js +9 -9
- package/dist/core/workflow/workspaces/integration.js +14 -14
- package/dist/core/workflow/workspaces/lifecycle.d.ts +1 -1
- package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/lifecycle.js +9 -9
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +39 -5
- package/dist/core/workflow/workspaces/recovery.js +3 -3
- package/dist/core/workflow/workspaces/state.d.ts +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +3 -3
- package/dist/core/workflow/workspaces/types.d.ts +1 -1
- package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
- package/dist/lib/browser/asserts.d.ts +28 -0
- package/dist/lib/browser/asserts.d.ts.map +1 -0
- package/dist/lib/browser/asserts.js +152 -0
- package/dist/lib/browser/client.d.ts +49 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +155 -14
- package/dist/lib/browser/content-checks.d.ts +108 -0
- package/dist/lib/browser/content-checks.d.ts.map +1 -0
- package/dist/lib/browser/content-checks.js +474 -0
- package/dist/lib/browser/critique.d.ts +91 -0
- package/dist/lib/browser/critique.d.ts.map +1 -0
- package/dist/lib/browser/critique.js +167 -0
- package/dist/lib/browser/geometry.d.ts +32 -0
- package/dist/lib/browser/geometry.d.ts.map +1 -1
- package/dist/lib/browser/geometry.js +244 -1
- package/dist/lib/browser/index.d.ts +4 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -0
- package/dist/lib/claude-desktop.d.ts.map +1 -1
- package/dist/lib/claude-desktop.js +4 -1
- package/dist/lib/council/index.d.ts +6 -6
- package/dist/lib/council/index.d.ts.map +1 -1
- package/dist/lib/council/index.js +8 -8
- package/dist/lib/identities/assume.d.ts +2 -2
- package/dist/lib/identities/assume.js +7 -7
- package/dist/lib/instructions/apply.d.ts +3 -3
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +75 -27
- package/dist/lib/instructions/host-addendum.d.ts +54 -0
- package/dist/lib/instructions/host-addendum.d.ts.map +1 -0
- package/dist/lib/instructions/host-addendum.js +107 -0
- package/dist/lib/instructions/templates.d.ts +10 -3
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +41 -12
- package/dist/lib/machine.d.ts +2 -2
- package/dist/lib/machine.js +2 -2
- package/package.json +26 -21
- package/schemas/config.schema.json +26 -1
- package/src/commander.ts +22 -13
- package/src/commands/{harness.ts → adapter.ts} +46 -46
- package/src/commands/agents.ts +71 -64
- package/src/commands/artifacts.ts +168 -0
- package/src/commands/backup.ts +47 -33
- package/src/commands/browse.ts +438 -2
- package/src/commands/checkpoint.ts +196 -0
- package/src/commands/context.ts +3 -176
- package/src/commands/decision.ts +41 -7
- package/src/commands/deinit.ts +16 -16
- package/src/commands/doctor.ts +28 -28
- package/src/commands/{supervisor.ts → governor.ts} +184 -154
- package/src/commands/init.ts +50 -34
- package/src/commands/{scratch.ts → journal.ts} +60 -60
- package/src/commands/policy.ts +1 -1
- package/src/commands/presence.ts +2 -2
- package/src/commands/sync.ts +3 -3
- package/src/commands/tunnel.ts +237 -34
- package/src/commands/web-fetch.ts +1 -1
- package/src/commands/web.ts +56 -1
- package/src/commands/work.ts +50 -35
- package/src/commands/workflow.ts +43 -34
- package/src/core/{harnesses → adapters}/attest-projection.ts +2 -2
- package/src/core/{harnesses → adapters}/attest.ts +27 -27
- package/src/core/{harnesses → adapters}/attestation.ts +41 -41
- package/src/core/{harnesses → adapters}/bench.ts +34 -43
- package/src/core/adapters/index.ts +56 -0
- package/src/core/{harnesses → adapters}/profiles.ts +16 -16
- package/src/core/{harnesses → adapters}/registry.ts +23 -23
- package/src/core/{harnesses → adapters}/types.ts +23 -23
- package/src/core/agents/canonical-emit.ts +6 -6
- package/src/core/agents/cli-emit.ts +1 -1
- package/src/core/agents/cli.ts +30 -28
- package/src/core/agents/codex-replay.ts +6 -6
- package/src/core/agents/coord-client.ts +119 -28
- package/src/core/agents/events/consume.ts +1 -1
- package/src/core/agents/events/emit.ts +7 -6
- package/src/core/agents/render/prompt-context.ts +107 -31
- package/src/core/agents/render/session-context.ts +6 -6
- package/src/core/agents/rules/claim-conflict.ts +4 -7
- package/src/core/agents/rules/commit-conflict.ts +103 -22
- package/src/core/agents/rules/stop-hook.ts +33 -17
- package/src/core/agents/rules/verdict.ts +13 -0
- package/src/core/agents/session-events.ts +4 -4
- package/src/core/agents/state/council.ts +1 -1
- package/src/core/agents/state/heartbeat-projector.ts +16 -12
- package/src/core/agents/state/heartbeat-writer.ts +12 -12
- package/src/core/agents/state/{scratch.ts → journal.ts} +12 -12
- package/src/core/agents/state/pidmap.ts +130 -15
- package/src/core/agents/state/proc-start.ts +208 -0
- package/src/core/agents/state/shell-mutation.ts +1 -1
- package/src/core/agents/state/stale-sweep.ts +7 -7
- package/src/core/artifacts/index.ts +557 -0
- package/src/core/config.ts +30 -2
- package/src/core/context/index.ts +15 -15
- package/src/core/governor/adapter-spread.ts +82 -0
- package/src/core/governor/index.ts +91 -0
- package/src/core/governor/plan-api.ts +115 -0
- package/src/core/{supervisor → governor}/plan-read.ts +112 -97
- package/src/core/{supervisor → governor}/plan-types.ts +59 -56
- package/src/core/{supervisor → governor}/planning.ts +223 -192
- package/src/core/governor/read.ts +59 -0
- package/src/core/{supervisor → governor}/runner.ts +39 -39
- package/src/core/{supervisor → governor}/service-read.ts +22 -22
- package/src/core/{supervisor → governor}/service.ts +117 -119
- package/src/core/{supervisor → governor}/state.ts +325 -167
- package/src/core/hooks/adapter/detect.ts +30 -0
- package/src/core/hooks/{harness → adapter}/events.ts +16 -14
- package/src/core/hooks/{harness → adapter}/output.ts +31 -23
- package/src/core/hooks/{harness → adapter}/parse.ts +5 -5
- package/src/core/hooks/{harness → adapter}/wiring.ts +21 -21
- package/src/core/hooks/cli.ts +185 -162
- package/src/core/hooks/effects/image-capture.ts +6 -6
- package/src/core/hooks/effects/index.ts +17 -17
- package/src/core/hooks/events/emit.ts +3 -3
- package/src/core/hooks/events/rotate.ts +1 -1
- package/src/core/hooks/events/schema.ts +15 -7
- package/src/core/hooks/guard-path.ts +2 -2
- package/src/core/hooks/index.ts +1 -1
- package/src/core/hooks/resolve/anchor.ts +63 -8
- package/src/core/hooks/resolve/coord-root.ts +4 -4
- package/src/core/hooks/resolve/intent.ts +1 -1
- package/src/core/hooks/resolve/owner.ts +11 -5
- package/src/core/{scratch → journal}/index.ts +53 -53
- package/src/core/policy/evaluate.ts +8 -8
- package/src/core/policy/types.ts +3 -3
- package/src/core/work/state.ts +65 -23
- package/src/core/workflow/adapters.ts +33 -0
- package/src/core/workflow/approvals.ts +7 -7
- package/src/core/workflow/billing.ts +12 -12
- package/src/core/workflow/child-env.ts +13 -4
- package/src/core/workflow/engine.ts +155 -90
- package/src/core/workflow/evidence-preflight.ts +165 -0
- package/src/core/workflow/index.ts +3 -3
- package/src/core/workflow/proof.ts +60 -50
- package/src/core/workflow/run-state.ts +7 -7
- package/src/core/workflow/sandbox-projection.ts +16 -16
- package/src/core/workflow/spawn-claude.ts +13 -10
- package/src/core/workflow/spawn-codex.ts +13 -10
- package/src/core/workflow/spawn-cursor.ts +13 -10
- package/src/core/workflow/spawn-failure.ts +1 -1
- package/src/core/workflow/specialists.ts +2 -2
- package/src/core/workflow/{journal.ts → transcript.ts} +16 -16
- package/src/core/workflow/types.ts +108 -51
- package/src/core/workflow/validate.ts +1 -1
- package/src/core/workflow/workspaces/cancellation.ts +2 -2
- package/src/core/workflow/workspaces/cleanup.ts +2 -2
- package/src/core/workflow/workspaces/execution.ts +6 -6
- package/src/core/workflow/workspaces/inspect.ts +14 -11
- package/src/core/workflow/workspaces/integration.ts +18 -14
- package/src/core/workflow/workspaces/lifecycle.ts +10 -10
- package/src/core/workflow/workspaces/local-git.ts +47 -5
- package/src/core/workflow/workspaces/recovery.ts +3 -3
- package/src/core/workflow/workspaces/state.ts +4 -3
- package/src/core/workflow/workspaces/types.ts +1 -1
- package/src/lib/browser/asserts.ts +177 -0
- package/src/lib/browser/client.ts +189 -14
- package/src/lib/browser/content-checks.ts +571 -0
- package/src/lib/browser/critique.ts +231 -0
- package/src/lib/browser/geometry.ts +297 -1
- package/src/lib/browser/index.ts +34 -0
- package/src/lib/claude-desktop.ts +4 -1
- package/src/lib/council/index.ts +9 -9
- package/src/lib/identities/assume.ts +7 -7
- package/src/lib/instructions/apply.ts +87 -25
- package/src/lib/instructions/host-addendum.ts +126 -0
- package/src/lib/instructions/templates.ts +44 -15
- package/src/lib/machine.ts +2 -2
- package/dist/commands/harness.d.ts +0 -12
- package/dist/commands/harness.d.ts.map +0 -1
- package/dist/commands/scratch.d.ts +0 -4
- package/dist/commands/scratch.d.ts.map +0 -1
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/session.js +0 -162
- package/dist/commands/supervisor.d.ts +0 -4
- package/dist/commands/supervisor.d.ts.map +0 -1
- package/dist/core/harnesses/attest-projection.d.ts.map +0 -1
- package/dist/core/harnesses/attest.d.ts.map +0 -1
- package/dist/core/harnesses/attestation.d.ts.map +0 -1
- package/dist/core/harnesses/bench.d.ts.map +0 -1
- package/dist/core/harnesses/index.d.ts +0 -13
- package/dist/core/harnesses/index.d.ts.map +0 -1
- package/dist/core/harnesses/index.js +0 -6
- package/dist/core/harnesses/profiles.d.ts.map +0 -1
- package/dist/core/harnesses/registry.d.ts +0 -15
- package/dist/core/harnesses/registry.d.ts.map +0 -1
- package/dist/core/harnesses/types.d.ts.map +0 -1
- package/dist/core/hooks/harness/detect.d.ts +0 -9
- package/dist/core/hooks/harness/events.d.ts.map +0 -1
- package/dist/core/hooks/harness/output.d.ts.map +0 -1
- package/dist/core/supervisor/index.d.ts +0 -7
- package/dist/core/supervisor/index.d.ts.map +0 -1
- package/dist/core/supervisor/index.js +0 -6
- package/dist/core/supervisor/plan-api.d.ts +0 -23
- package/dist/core/supervisor/plan-api.d.ts.map +0 -1
- package/dist/core/supervisor/plan-api.js +0 -50
- package/dist/core/supervisor/plan-read.d.ts +0 -5
- package/dist/core/supervisor/plan-read.d.ts.map +0 -1
- package/dist/core/supervisor/plan-types.d.ts.map +0 -1
- package/dist/core/supervisor/planning.d.ts +0 -33
- package/dist/core/supervisor/planning.d.ts.map +0 -1
- package/dist/core/supervisor/read.d.ts +0 -6
- package/dist/core/supervisor/read.d.ts.map +0 -1
- package/dist/core/supervisor/read.js +0 -4
- package/dist/core/supervisor/runner.d.ts +0 -37
- package/dist/core/supervisor/runner.d.ts.map +0 -1
- package/dist/core/supervisor/service-read.d.ts +0 -6
- package/dist/core/supervisor/service-read.d.ts.map +0 -1
- package/dist/core/supervisor/service.d.ts +0 -114
- package/dist/core/supervisor/service.d.ts.map +0 -1
- package/dist/core/supervisor/state.d.ts +0 -95
- package/dist/core/supervisor/state.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.d.ts +0 -17
- package/dist/core/workflow/harnesses.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.js +0 -21
- package/dist/core/workflow/journal.d.ts.map +0 -1
- package/src/commands/session.ts +0 -187
- package/src/core/harnesses/index.ts +0 -56
- package/src/core/hooks/harness/detect.ts +0 -30
- package/src/core/supervisor/index.ts +0 -89
- package/src/core/supervisor/plan-api.ts +0 -73
- package/src/core/supervisor/read.ts +0 -59
- package/src/core/workflow/harnesses.ts +0 -33
- /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
|
@@ -17,18 +17,18 @@ import { assertWorkId, readWorkItem, type WorkRecord } from "../work/read.ts";
|
|
|
17
17
|
import { workflowScriptDigest } from "../workflow/run-state.ts";
|
|
18
18
|
import { normalizeWorkflowSpecialists } from "../workflow/specialists.ts";
|
|
19
19
|
import type { WorkflowSpecialistProfile } from "../workflow/types.ts";
|
|
20
|
-
import {
|
|
20
|
+
import { readGovernorPlans } from "./plan-read.ts";
|
|
21
21
|
import {
|
|
22
|
-
type
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
type
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
type CreateGovernorReplanningInput,
|
|
23
|
+
type GovernorPlanHistory,
|
|
24
|
+
type GovernorPlanRecord,
|
|
25
|
+
type GovernorReplanningPolicy,
|
|
26
|
+
governorGraphFingerprint,
|
|
27
|
+
MAX_GOVERNOR_PLAN_REVIEWERS,
|
|
28
|
+
MAX_GOVERNOR_PLAN_REVISION_ROUNDS,
|
|
29
29
|
} from "./plan-types.ts";
|
|
30
30
|
|
|
31
|
-
export const
|
|
31
|
+
export const GOVERNOR_INTENT_SCHEMA_VERSION = 1 as const;
|
|
32
32
|
|
|
33
33
|
const GOAL_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
34
34
|
const MAX_TITLE = 200;
|
|
@@ -46,7 +46,7 @@ const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
|
|
|
46
46
|
* environment failure self-bounds at one because it stops immediately. */
|
|
47
47
|
const MAX_UNCHARGED_REPLANS = 3;
|
|
48
48
|
|
|
49
|
-
export interface
|
|
49
|
+
export interface GovernorLimits {
|
|
50
50
|
max_cycles: number;
|
|
51
51
|
max_runtime_ms: number;
|
|
52
52
|
max_parallel_work: number;
|
|
@@ -55,38 +55,38 @@ export interface SupervisorLimits {
|
|
|
55
55
|
agent_concurrency: number;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
export interface
|
|
58
|
+
export interface GovernorAutomationPolicy {
|
|
59
59
|
accept_passing_proof: boolean;
|
|
60
60
|
resume_approved: boolean;
|
|
61
61
|
retry_blocked: boolean;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export interface
|
|
64
|
+
export interface GovernorMission {
|
|
65
65
|
objective: string;
|
|
66
66
|
acceptance: string[];
|
|
67
67
|
max_milestones: number;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
export interface
|
|
70
|
+
export interface CreateGovernorMissionInput {
|
|
71
71
|
objective: string;
|
|
72
72
|
acceptance: readonly string[];
|
|
73
73
|
maxMilestones?: number;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
export interface
|
|
77
|
-
schema_version: typeof
|
|
76
|
+
export interface GovernorIntent {
|
|
77
|
+
schema_version: typeof GOVERNOR_INTENT_SCHEMA_VERSION;
|
|
78
78
|
id: string;
|
|
79
79
|
title: string;
|
|
80
80
|
root_work_id: string;
|
|
81
81
|
specialists: Record<string, WorkflowSpecialistProfile>;
|
|
82
|
-
limits:
|
|
83
|
-
automation:
|
|
84
|
-
mission?:
|
|
85
|
-
replanning?:
|
|
82
|
+
limits: GovernorLimits;
|
|
83
|
+
automation: GovernorAutomationPolicy;
|
|
84
|
+
mission?: GovernorMission;
|
|
85
|
+
replanning?: GovernorReplanningPolicy;
|
|
86
86
|
created_at: string;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export type
|
|
89
|
+
export type GovernorState =
|
|
90
90
|
| "ready"
|
|
91
91
|
| "running"
|
|
92
92
|
| "awaiting_attention"
|
|
@@ -94,7 +94,7 @@ export type SupervisorState =
|
|
|
94
94
|
| "budget_exhausted"
|
|
95
95
|
| "succeeded";
|
|
96
96
|
|
|
97
|
-
export type
|
|
97
|
+
export type GovernorNextAction =
|
|
98
98
|
| "run"
|
|
99
99
|
| "wait_for_run"
|
|
100
100
|
| "resolve_approval"
|
|
@@ -107,19 +107,23 @@ export type SupervisorNextAction =
|
|
|
107
107
|
| "retry_plan"
|
|
108
108
|
| "none";
|
|
109
109
|
|
|
110
|
-
export interface
|
|
110
|
+
export interface GovernorProjection {
|
|
111
111
|
id: string;
|
|
112
112
|
title: string;
|
|
113
113
|
root_work_id: string;
|
|
114
114
|
root_materialized: boolean;
|
|
115
|
-
state:
|
|
115
|
+
state: GovernorState;
|
|
116
116
|
reason: string;
|
|
117
|
-
next_action:
|
|
117
|
+
next_action: GovernorNextAction;
|
|
118
118
|
work_ids: string[];
|
|
119
119
|
ready_work: string[];
|
|
120
120
|
resumable_work: string[];
|
|
121
121
|
retryable_work: string[];
|
|
122
122
|
attention_work: string[];
|
|
123
|
+
/** Work stopped because a human must rule, with the docket id when the script
|
|
124
|
+
* named one. Empty in the ordinary case. A reader that wants to tell an
|
|
125
|
+
* operator what is actually waiting on them should start here. */
|
|
126
|
+
decision_blocked_work: Array<{ work_id: string; decision_id?: string }>;
|
|
123
127
|
attempts_used: number;
|
|
124
128
|
attempts_remaining: number;
|
|
125
129
|
specialists: string[];
|
|
@@ -130,41 +134,53 @@ export interface SupervisorProjection {
|
|
|
130
134
|
milestones_remaining: number;
|
|
131
135
|
pending_plan_id?: string;
|
|
132
136
|
attention_plan_id?: string;
|
|
133
|
-
latest_plan_status?:
|
|
137
|
+
latest_plan_status?: GovernorPlanRecord["status"];
|
|
138
|
+
/**
|
|
139
|
+
* How consumed (charged) replans that did not advance the graph broke down,
|
|
140
|
+
* present only when at least one replan was spent by a planner run that
|
|
141
|
+
* produced no proposal. `reviewer_rejection` counts replans where a proposal
|
|
142
|
+
* was produced and independent review rejected it (review-round exhaustion,
|
|
143
|
+
* reviewer attention, or review failure); `planner_no_proposal` counts replans
|
|
144
|
+
* where the planner run produced no reviewable proposal at all. Absent when a
|
|
145
|
+
* goal has no planner no-proposal history, so goals that only ever reached
|
|
146
|
+
* review project exactly as before this field existed.
|
|
147
|
+
*/
|
|
148
|
+
replan_consumption?: {
|
|
149
|
+
reviewer_rejection: number;
|
|
150
|
+
planner_no_proposal: number;
|
|
151
|
+
};
|
|
134
152
|
governed_work_ids: string[];
|
|
135
153
|
}
|
|
136
154
|
|
|
137
|
-
export interface
|
|
138
|
-
intent:
|
|
139
|
-
projection:
|
|
155
|
+
export interface GovernorRecord {
|
|
156
|
+
intent: GovernorIntent;
|
|
157
|
+
projection: GovernorProjection;
|
|
140
158
|
work: WorkRecord[];
|
|
141
|
-
plans:
|
|
159
|
+
plans: GovernorPlanRecord[];
|
|
142
160
|
}
|
|
143
161
|
|
|
144
|
-
export interface
|
|
162
|
+
export interface CreateGovernorInput {
|
|
145
163
|
coordRoot: string;
|
|
146
164
|
rootWorkId?: string;
|
|
147
165
|
specialists: Readonly<Record<string, WorkflowSpecialistProfile>>;
|
|
148
166
|
title?: string;
|
|
149
167
|
id?: string;
|
|
150
|
-
limits?: Partial<
|
|
151
|
-
automation?: Partial<
|
|
152
|
-
mission?:
|
|
153
|
-
replanning?:
|
|
168
|
+
limits?: Partial<GovernorLimits>;
|
|
169
|
+
automation?: Partial<GovernorAutomationPolicy>;
|
|
170
|
+
mission?: CreateGovernorMissionInput;
|
|
171
|
+
replanning?: CreateGovernorReplanningInput;
|
|
154
172
|
}
|
|
155
173
|
|
|
156
|
-
export function
|
|
174
|
+
export function createGovernor(input: CreateGovernorInput): GovernorRecord {
|
|
157
175
|
const coordRoot = resolve(input.coordRoot);
|
|
158
|
-
const id = input.id ??
|
|
159
|
-
|
|
176
|
+
const id = input.id ?? newGovernorId();
|
|
177
|
+
assertGovernorId(id);
|
|
160
178
|
const mission = input.mission ? normalizeMission(input.mission) : undefined;
|
|
161
179
|
if (mission && !input.replanning) {
|
|
162
|
-
throw new Error("
|
|
180
|
+
throw new Error("governor mission requires a replanning policy");
|
|
163
181
|
}
|
|
164
182
|
if (!input.rootWorkId && !mission) {
|
|
165
|
-
throw new Error(
|
|
166
|
-
"supervisor creation without root work requires a mission and replanning policy",
|
|
167
|
-
);
|
|
183
|
+
throw new Error("governor creation without root work requires a mission and replanning policy");
|
|
168
184
|
}
|
|
169
185
|
if (input.rootWorkId) assertWorkId(input.rootWorkId);
|
|
170
186
|
const root = input.rootWorkId ? readWorkItem(coordRoot, input.rootWorkId) : undefined;
|
|
@@ -182,12 +198,12 @@ export function createSupervisor(input: CreateSupervisorInput): SupervisorRecord
|
|
|
182
198
|
"objective-first mission max_replans must exceed max_milestones to reserve completion review",
|
|
183
199
|
);
|
|
184
200
|
}
|
|
185
|
-
const intent:
|
|
186
|
-
schema_version:
|
|
201
|
+
const intent: GovernorIntent = {
|
|
202
|
+
schema_version: GOVERNOR_INTENT_SCHEMA_VERSION,
|
|
187
203
|
id,
|
|
188
204
|
title: bounded(
|
|
189
205
|
input.title ?? root?.intent.title ?? missionTitle(mission!),
|
|
190
|
-
"
|
|
206
|
+
"governor title",
|
|
191
207
|
MAX_TITLE,
|
|
192
208
|
),
|
|
193
209
|
root_work_id: input.rootWorkId ?? missionRootId(id),
|
|
@@ -198,42 +214,41 @@ export function createSupervisor(input: CreateSupervisorInput): SupervisorRecord
|
|
|
198
214
|
...(replanning ? { replanning } : {}),
|
|
199
215
|
created_at: new Date().toISOString(),
|
|
200
216
|
};
|
|
201
|
-
const path =
|
|
217
|
+
const path = governorIntentPath(coordRoot, id);
|
|
202
218
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
203
219
|
chmodSync(dirname(path), 0o700);
|
|
204
220
|
writePrivateJson(path, intent);
|
|
205
|
-
return
|
|
221
|
+
return readGovernor(coordRoot, id);
|
|
206
222
|
}
|
|
207
223
|
|
|
208
|
-
export function
|
|
209
|
-
return
|
|
224
|
+
export function readGovernor(coordRoot: string, goalId: string): GovernorRecord {
|
|
225
|
+
return readGovernorInternal(coordRoot, goalId, false);
|
|
210
226
|
}
|
|
211
227
|
|
|
212
|
-
/** @internal Runner seam; ignores the lease held by this
|
|
213
|
-
export function
|
|
214
|
-
return
|
|
228
|
+
/** @internal Runner seam; ignores the lease held by this governor process. */
|
|
229
|
+
export function readGovernorIgnoringLease(coordRoot: string, goalId: string): GovernorRecord {
|
|
230
|
+
return readGovernorInternal(coordRoot, goalId, true);
|
|
215
231
|
}
|
|
216
232
|
|
|
217
|
-
export function
|
|
218
|
-
const base = join(resolve(coordRoot), ".harnery", "
|
|
233
|
+
export function listGovernors(coordRoot: string): GovernorRecord[] {
|
|
234
|
+
const base = join(resolve(coordRoot), ".harnery", "governors");
|
|
219
235
|
if (!existsSync(base)) return [];
|
|
220
|
-
const records:
|
|
236
|
+
const records: GovernorRecord[] = [];
|
|
221
237
|
for (const name of readdirSync(base)) {
|
|
222
|
-
if (!GOAL_ID.test(name) || !existsSync(
|
|
223
|
-
records.push(
|
|
238
|
+
if (!GOAL_ID.test(name) || !existsSync(governorIntentPath(coordRoot, name))) continue;
|
|
239
|
+
records.push(readGovernor(coordRoot, name));
|
|
224
240
|
}
|
|
225
241
|
return records.sort((left, right) =>
|
|
226
242
|
right.intent.created_at.localeCompare(left.intent.created_at),
|
|
227
243
|
);
|
|
228
244
|
}
|
|
229
245
|
|
|
230
|
-
export function
|
|
246
|
+
export function collectGovernorWork(coordRoot: string, rootWorkId: string): WorkRecord[] {
|
|
231
247
|
const records = new Map<string, WorkRecord>();
|
|
232
248
|
const visiting = new Set<string>();
|
|
233
249
|
const visit = (workId: string): void => {
|
|
234
250
|
if (records.has(workId)) return;
|
|
235
|
-
if (visiting.has(workId))
|
|
236
|
-
throw new Error(`supervisor work graph contains a cycle at ${workId}`);
|
|
251
|
+
if (visiting.has(workId)) throw new Error(`governor work graph contains a cycle at ${workId}`);
|
|
237
252
|
visiting.add(workId);
|
|
238
253
|
const record = readWorkItem(coordRoot, workId);
|
|
239
254
|
for (const dependency of record.intent.dependencies) visit(dependency);
|
|
@@ -244,18 +259,18 @@ export function collectSupervisorWork(coordRoot: string, rootWorkId: string): Wo
|
|
|
244
259
|
return Array.from(records.values());
|
|
245
260
|
}
|
|
246
261
|
|
|
247
|
-
export function
|
|
248
|
-
if (!GOAL_ID.test(goalId)) throw new Error(`invalid
|
|
262
|
+
export function assertGovernorId(goalId: string): void {
|
|
263
|
+
if (!GOAL_ID.test(goalId)) throw new Error(`invalid governor id ${JSON.stringify(goalId)}`);
|
|
249
264
|
}
|
|
250
265
|
|
|
251
|
-
export function
|
|
266
|
+
export function newGovernorId(): string {
|
|
252
267
|
return `goal-${new Date().toISOString().replace(/[:.]/g, "-")}-${randomBytes(3).toString("hex")}`;
|
|
253
268
|
}
|
|
254
269
|
|
|
255
|
-
/** @internal Runner seam; one exclusive crash-recoverable
|
|
256
|
-
export function
|
|
257
|
-
|
|
258
|
-
const path = join(
|
|
270
|
+
/** @internal Runner seam; one exclusive crash-recoverable governor lease. */
|
|
271
|
+
export function acquireGovernorLease(coordRoot: string, goalId: string): () => void {
|
|
272
|
+
readGovernorIntent(coordRoot, goalId);
|
|
273
|
+
const path = join(governorDir(coordRoot, goalId), "lease.json");
|
|
259
274
|
const owner = {
|
|
260
275
|
pid: process.pid,
|
|
261
276
|
host: hostname(),
|
|
@@ -281,11 +296,11 @@ export function acquireSupervisorLease(coordRoot: string, goalId: string): () =>
|
|
|
281
296
|
const existing = readLease(path);
|
|
282
297
|
if (existing && leaseIsLive(existing)) {
|
|
283
298
|
throw new Error(
|
|
284
|
-
`
|
|
299
|
+
`governor ${goalId} is already running under pid ${existing.pid} on ${existing.host}`,
|
|
285
300
|
);
|
|
286
301
|
}
|
|
287
302
|
unlinkSync(path);
|
|
288
|
-
if (!acquire()) throw new Error(`
|
|
303
|
+
if (!acquire()) throw new Error(`governor ${goalId} lease raced with another process`);
|
|
289
304
|
}
|
|
290
305
|
return () => {
|
|
291
306
|
try {
|
|
@@ -297,19 +312,19 @@ export function acquireSupervisorLease(coordRoot: string, goalId: string): () =>
|
|
|
297
312
|
};
|
|
298
313
|
}
|
|
299
314
|
|
|
300
|
-
function
|
|
315
|
+
function readGovernorInternal(
|
|
301
316
|
coordRootRaw: string,
|
|
302
317
|
goalId: string,
|
|
303
318
|
ignoreLease: boolean,
|
|
304
|
-
):
|
|
319
|
+
): GovernorRecord {
|
|
305
320
|
const coordRoot = resolve(coordRootRaw);
|
|
306
|
-
const intent =
|
|
307
|
-
const plans =
|
|
321
|
+
const intent = readGovernorIntent(coordRoot, goalId);
|
|
322
|
+
const plans = readGovernorPlans(coordRoot, intent.id, intent.root_work_id);
|
|
308
323
|
const activeRootExists = workIntentExists(coordRoot, plans.active_root_work_id);
|
|
309
324
|
if (!activeRootExists && (!intent.mission || plans.generation > 0)) {
|
|
310
|
-
throw new Error(`
|
|
325
|
+
throw new Error(`governor ${intent.id} root work is missing`);
|
|
311
326
|
}
|
|
312
|
-
const work = activeRootExists ?
|
|
327
|
+
const work = activeRootExists ? collectGovernorWork(coordRoot, plans.active_root_work_id) : [];
|
|
313
328
|
const governed = collectGovernedWork(coordRoot, intent.root_work_id, plans, work);
|
|
314
329
|
return {
|
|
315
330
|
intent,
|
|
@@ -321,12 +336,12 @@ function readSupervisorInternal(
|
|
|
321
336
|
|
|
322
337
|
function deriveProjection(
|
|
323
338
|
coordRoot: string,
|
|
324
|
-
intent:
|
|
339
|
+
intent: GovernorIntent,
|
|
325
340
|
work: WorkRecord[],
|
|
326
341
|
governed: WorkRecord[],
|
|
327
|
-
plans:
|
|
342
|
+
plans: GovernorPlanHistory,
|
|
328
343
|
ignoreLease: boolean,
|
|
329
|
-
):
|
|
344
|
+
): GovernorProjection {
|
|
330
345
|
const root = work.find((record) => record.intent.id === plans.active_root_work_id);
|
|
331
346
|
const attemptsUsed = governed.reduce((sum, record) => sum + record.projection.attempts_used, 0);
|
|
332
347
|
// The goal budget, like the per-item budget, counts only charged attempts
|
|
@@ -337,10 +352,14 @@ function deriveProjection(
|
|
|
337
352
|
// count: an uncharged planner failure (environment/upstream) does not spend
|
|
338
353
|
// max_replans. replans_used stays the raw count for display.
|
|
339
354
|
const unchargedReplans = plans.plans.filter(
|
|
340
|
-
(plan) =>
|
|
341
|
-
plan.status === "failed" && (plan.class === "environment" || plan.class === "upstream"),
|
|
355
|
+
(plan) => plan.status === "failed" && isUnchargedPlanClass(plan.class),
|
|
342
356
|
).length;
|
|
343
357
|
const chargedReplans = plans.plans.length - unchargedReplans;
|
|
358
|
+
// Attribute the consumed replans that did not advance the graph so the
|
|
359
|
+
// projection can distinguish planner no-proposal exhaustion from reviewer
|
|
360
|
+
// rejection through existing plan seams (review receipt presence), rather than
|
|
361
|
+
// reporting both as a single undifferentiated replan exhaustion.
|
|
362
|
+
const replanConsumption = classifyReplanConsumption(plans);
|
|
344
363
|
const readyWork = work
|
|
345
364
|
.filter((record) => record.projection.state === "ready")
|
|
346
365
|
.map((record) => record.intent.id);
|
|
@@ -375,6 +394,15 @@ function deriveProjection(
|
|
|
375
394
|
(record) => record.projection.state === "blocked" && record.projection.next_action === "none",
|
|
376
395
|
)
|
|
377
396
|
.map((record) => record.intent.id);
|
|
397
|
+
// Work stopped on a person, kept apart from the rest of terminalBlocked. Every
|
|
398
|
+
// other terminal block is something an agent could in principle clear; this one
|
|
399
|
+
// can only be cleared by a ruling, so it is the thing worth saying out loud.
|
|
400
|
+
const decisionBlocked = work
|
|
401
|
+
.filter((record) => record.projection.blocked_on_decision !== undefined)
|
|
402
|
+
.map((record) => ({
|
|
403
|
+
work_id: record.intent.id,
|
|
404
|
+
decision_id: record.projection.blocked_on_decision || undefined,
|
|
405
|
+
}));
|
|
378
406
|
const attentionWork = unique([
|
|
379
407
|
...pendingApproval,
|
|
380
408
|
...(intent.automation.accept_passing_proof ? [] : reviews),
|
|
@@ -390,6 +418,12 @@ function deriveProjection(
|
|
|
390
418
|
record.intent.id === intent.root_work_id && record.projection.state === "succeeded",
|
|
391
419
|
);
|
|
392
420
|
const milestonesCompleted = plans.milestones_completed + (initialRootAccepted ? 1 : 0);
|
|
421
|
+
const resumableDispatchable = intent.automation.resume_approved ? resumableWork : [];
|
|
422
|
+
const attemptDispatchable = [
|
|
423
|
+
...readyWork,
|
|
424
|
+
...(intent.automation.retry_blocked ? retryableWork : []),
|
|
425
|
+
];
|
|
426
|
+
const dispatchable = [...resumableDispatchable, ...attemptDispatchable];
|
|
393
427
|
const base = {
|
|
394
428
|
id: intent.id,
|
|
395
429
|
title: intent.title,
|
|
@@ -400,6 +434,7 @@ function deriveProjection(
|
|
|
400
434
|
resumable_work: resumableWork,
|
|
401
435
|
retryable_work: retryableWork,
|
|
402
436
|
attention_work: attentionWork,
|
|
437
|
+
decision_blocked_work: decisionBlocked,
|
|
403
438
|
attempts_used: attemptsUsed,
|
|
404
439
|
attempts_remaining: Math.max(0, intent.limits.max_total_attempts - chargedUsed),
|
|
405
440
|
specialists: Object.keys(intent.specialists),
|
|
@@ -410,13 +445,14 @@ function deriveProjection(
|
|
|
410
445
|
milestones_remaining: Math.max(0, (intent.mission?.max_milestones ?? 0) - milestonesCompleted),
|
|
411
446
|
pending_plan_id: plans.latest?.status === "proposed" ? plans.latest.request.id : undefined,
|
|
412
447
|
latest_plan_status: plans.latest?.status,
|
|
448
|
+
...(replanConsumption.planner_no_proposal > 0 ? { replan_consumption: replanConsumption } : {}),
|
|
413
449
|
governed_work_ids: governed.map((record) => record.intent.id),
|
|
414
450
|
};
|
|
415
|
-
if (!ignoreLease &&
|
|
451
|
+
if (!ignoreLease && governorLeaseIsLive(coordRoot, intent.id)) {
|
|
416
452
|
return {
|
|
417
453
|
...base,
|
|
418
454
|
state: "running",
|
|
419
|
-
reason: "the foreground
|
|
455
|
+
reason: "the foreground governor holds the goal lease",
|
|
420
456
|
next_action: "wait_for_run",
|
|
421
457
|
};
|
|
422
458
|
}
|
|
@@ -428,6 +464,19 @@ function deriveProjection(
|
|
|
428
464
|
next_action: "none",
|
|
429
465
|
};
|
|
430
466
|
}
|
|
467
|
+
// ADR 0050: a mission whose completion was reopened dispatches the reopened work
|
|
468
|
+
// before anything else. Without this the goal falls through to the milestone
|
|
469
|
+
// branch below, which still sees a succeeded root when the reopened item is one
|
|
470
|
+
// of its children, and would spend a replan reassessing a mission whose real
|
|
471
|
+
// remaining work is already sitting in ready_work.
|
|
472
|
+
if (intent.mission && plans.latest?.status === "reopened" && dispatchable.length > 0) {
|
|
473
|
+
return {
|
|
474
|
+
...base,
|
|
475
|
+
state: "ready",
|
|
476
|
+
reason: `mission completion was reopened; ${dispatchable.length} work item${dispatchable.length === 1 ? " is" : "s are"} dispatchable`,
|
|
477
|
+
next_action: "run",
|
|
478
|
+
};
|
|
479
|
+
}
|
|
431
480
|
if (plans.latest?.status === "proposed") {
|
|
432
481
|
return {
|
|
433
482
|
...base,
|
|
@@ -468,6 +517,17 @@ function deriveProjection(
|
|
|
468
517
|
next_action: "none",
|
|
469
518
|
};
|
|
470
519
|
}
|
|
520
|
+
if (plans.latest?.status === "failed" && plans.latest.class === "decision") {
|
|
521
|
+
// The planner concluded that a human must rule before this can be planned.
|
|
522
|
+
// Stop and say so. Replanning would re-ask a question the planner already
|
|
523
|
+
// answered correctly, which is how a goal spins without a person noticing.
|
|
524
|
+
return {
|
|
525
|
+
...base,
|
|
526
|
+
state: "blocked",
|
|
527
|
+
reason: `planning is waiting on a human decision: ${plans.latest.reason ?? "the planner did not say which"}`,
|
|
528
|
+
next_action: "none",
|
|
529
|
+
};
|
|
530
|
+
}
|
|
471
531
|
if (plans.latest?.status === "failed" && plans.latest.class === "upstream") {
|
|
472
532
|
// The vendor refused. Uncharged replans do not spend max_replans, so the
|
|
473
533
|
// only brake on an unending outage is this consecutive bound; at the limit
|
|
@@ -475,7 +535,7 @@ function deriveProjection(
|
|
|
475
535
|
let trailingUncharged = 0;
|
|
476
536
|
for (let index = plans.plans.length - 1; index >= 0; index--) {
|
|
477
537
|
const plan = plans.plans[index]!;
|
|
478
|
-
if (plan.status !== "failed" || (plan.class
|
|
538
|
+
if (plan.status !== "failed" || !isUnchargedPlanClass(plan.class)) {
|
|
479
539
|
break;
|
|
480
540
|
}
|
|
481
541
|
trailingUncharged++;
|
|
@@ -492,7 +552,7 @@ function deriveProjection(
|
|
|
492
552
|
// dispatchable — the vendor may have recovered. canReplan reads
|
|
493
553
|
// chargedReplans, so these uncharged attempts do not exhaust max_replans.
|
|
494
554
|
}
|
|
495
|
-
const triggerFingerprint =
|
|
555
|
+
const triggerFingerprint = governorGraphFingerprint({
|
|
496
556
|
rootWorkId: plans.active_root_work_id,
|
|
497
557
|
generation: plans.generation,
|
|
498
558
|
work,
|
|
@@ -502,13 +562,16 @@ function deriveProjection(
|
|
|
502
562
|
plans.latest.request.prior_root_work_id === plans.active_root_work_id &&
|
|
503
563
|
plans.latest.status === "attention";
|
|
504
564
|
if (latestHandledSameGraph && plans.latest) {
|
|
565
|
+
const canRetry =
|
|
566
|
+
intent.replanning !== undefined && chargedReplans < intent.replanning.max_replans;
|
|
505
567
|
return {
|
|
506
568
|
...base,
|
|
507
569
|
state: "awaiting_attention",
|
|
508
|
-
reason:
|
|
570
|
+
reason: canRetry
|
|
571
|
+
? (plans.latest.reason ?? `plan ${plans.latest.request.id} requires attention`)
|
|
572
|
+
: exhaustedAttentionReason(plans.latest, replanConsumption),
|
|
509
573
|
attention_plan_id: plans.latest.request.id,
|
|
510
|
-
next_action:
|
|
511
|
-
intent.replanning && chargedReplans < intent.replanning.max_replans ? "retry_plan" : "none",
|
|
574
|
+
next_action: canRetry ? "retry_plan" : "none",
|
|
512
575
|
};
|
|
513
576
|
}
|
|
514
577
|
if (!root && intent.mission) {
|
|
@@ -516,7 +579,10 @@ function deriveProjection(
|
|
|
516
579
|
return {
|
|
517
580
|
...base,
|
|
518
581
|
state: "budget_exhausted",
|
|
519
|
-
reason:
|
|
582
|
+
reason: replanBudgetReason(
|
|
583
|
+
"mission exhausted its planning budget before creating an initial milestone",
|
|
584
|
+
replanConsumption,
|
|
585
|
+
),
|
|
520
586
|
next_action: "none",
|
|
521
587
|
};
|
|
522
588
|
}
|
|
@@ -527,13 +593,16 @@ function deriveProjection(
|
|
|
527
593
|
next_action: "plan_initial",
|
|
528
594
|
};
|
|
529
595
|
}
|
|
530
|
-
if (!root) throw new Error(`
|
|
596
|
+
if (!root) throw new Error(`governor ${intent.id} root work is missing`);
|
|
531
597
|
if (root.projection.state === "succeeded" && intent.mission) {
|
|
532
598
|
if (!intent.replanning || chargedReplans >= intent.replanning.max_replans) {
|
|
533
599
|
return {
|
|
534
600
|
...base,
|
|
535
601
|
state: "budget_exhausted",
|
|
536
|
-
reason:
|
|
602
|
+
reason: replanBudgetReason(
|
|
603
|
+
"mission exhausted its planning budget at a milestone boundary",
|
|
604
|
+
replanConsumption,
|
|
605
|
+
),
|
|
537
606
|
next_action: "none",
|
|
538
607
|
};
|
|
539
608
|
}
|
|
@@ -552,12 +621,6 @@ function deriveProjection(
|
|
|
552
621
|
next_action: "none",
|
|
553
622
|
};
|
|
554
623
|
}
|
|
555
|
-
const resumableDispatchable = intent.automation.resume_approved ? resumableWork : [];
|
|
556
|
-
const attemptDispatchable = [
|
|
557
|
-
...readyWork,
|
|
558
|
-
...(intent.automation.retry_blocked ? retryableWork : []),
|
|
559
|
-
];
|
|
560
|
-
const dispatchable = [...resumableDispatchable, ...attemptDispatchable];
|
|
561
624
|
if (intent.automation.accept_passing_proof && reviews.length > 0) {
|
|
562
625
|
return {
|
|
563
626
|
...base,
|
|
@@ -653,48 +716,143 @@ function deriveProjection(
|
|
|
653
716
|
return {
|
|
654
717
|
...base,
|
|
655
718
|
state: "budget_exhausted",
|
|
656
|
-
reason:
|
|
719
|
+
reason: replanBudgetReason(
|
|
720
|
+
`goal exhausted its ${intent.replanning.max_replans} replans`,
|
|
721
|
+
replanConsumption,
|
|
722
|
+
),
|
|
657
723
|
next_action: "none",
|
|
658
724
|
};
|
|
659
725
|
}
|
|
660
726
|
return {
|
|
661
727
|
...base,
|
|
662
728
|
state: "blocked",
|
|
729
|
+
// Lead with the decisions when there are any. "3 work items need
|
|
730
|
+
// intervention" reads as a queue an agent will get to; "waiting on you to
|
|
731
|
+
// rule on X" is the only phrasing that reaches the person who can clear it.
|
|
663
732
|
reason:
|
|
664
|
-
|
|
665
|
-
? `${
|
|
666
|
-
:
|
|
733
|
+
decisionBlocked.length > 0
|
|
734
|
+
? `${decisionBlocked.length} work item${decisionBlocked.length === 1 ? " is" : "s are"} waiting on a human decision: ${describeDecisionBlocks(decisionBlocked)}`
|
|
735
|
+
: attentionWork.length > 0
|
|
736
|
+
? `${attentionWork.length} work item${attentionWork.length === 1 ? " needs" : "s need"} intervention`
|
|
737
|
+
: "goal graph has no legal progress action",
|
|
667
738
|
next_action: "none",
|
|
668
739
|
};
|
|
669
740
|
}
|
|
670
741
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
742
|
+
/** Bounded, operator-readable list of what is waiting on a ruling. */
|
|
743
|
+
function describeDecisionBlocks(
|
|
744
|
+
blocks: readonly { work_id: string; decision_id?: string }[],
|
|
745
|
+
): string {
|
|
746
|
+
const shown = blocks
|
|
747
|
+
.slice(0, 3)
|
|
748
|
+
.map((b) => (b.decision_id ? `${b.work_id} (${b.decision_id})` : b.work_id))
|
|
749
|
+
.join(", ");
|
|
750
|
+
return blocks.length > 3 ? `${shown}, +${blocks.length - 3} more` : shown;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
interface ReplanConsumption {
|
|
754
|
+
reviewer_rejection: number;
|
|
755
|
+
planner_no_proposal: number;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Classify each consumed replan that did not advance the graph, using existing
|
|
760
|
+
* plan seams only. A plan carries a review receipt exactly when a proposal was
|
|
761
|
+
* produced and independently reviewed, so its presence separates a reviewer
|
|
762
|
+
* rejection from a planner run that produced no reviewable proposal. Uncharged
|
|
763
|
+
* environment/upstream planner failures (ADR 0046) do not spend the replan
|
|
764
|
+
* budget and are attributed to their outside precondition, not counted here.
|
|
765
|
+
*/
|
|
766
|
+
/** Whether a failed plan told us nothing about the plan, and so must not spend
|
|
767
|
+
* a replan (ADR 0046). All three classes mean "replanning this cannot help":
|
|
768
|
+
* the binary is missing, the vendor is down, or a person has to rule. */
|
|
769
|
+
function isUnchargedPlanClass(value: GovernorPlanRecord["class"]): boolean {
|
|
770
|
+
return value === "environment" || value === "upstream" || value === "decision";
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function classifyReplanConsumption(plans: GovernorPlanHistory): ReplanConsumption {
|
|
774
|
+
let reviewerRejection = 0;
|
|
775
|
+
let plannerNoProposal = 0;
|
|
776
|
+
for (const plan of plans.plans) {
|
|
777
|
+
if (plan.status === "failed" && isUnchargedPlanClass(plan.class)) {
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
if (plan.review && plan.review.status !== "passed") {
|
|
781
|
+
reviewerRejection++;
|
|
782
|
+
} else if (
|
|
783
|
+
!plan.review &&
|
|
784
|
+
(plan.status === "attention" ||
|
|
785
|
+
plan.status === "interrupted" ||
|
|
786
|
+
plan.status === "failed" ||
|
|
787
|
+
plan.status === "retry_requested")
|
|
788
|
+
) {
|
|
789
|
+
// `retry_requested` is a planner no-proposal attention the operator asked to
|
|
790
|
+
// replan: the original run still produced no reviewable proposal and still
|
|
791
|
+
// spent a charged replan, so it belongs in this bucket even though a later
|
|
792
|
+
// plan superseded it. Omitting it would misattribute a retried no-proposal
|
|
793
|
+
// to nothing and let the reason read as pure review exhaustion.
|
|
794
|
+
plannerNoProposal++;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return { reviewer_rejection: reviewerRejection, planner_no_proposal: plannerNoProposal };
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Reason for a goal held at `awaiting_attention/none` because its latest plan is
|
|
802
|
+
* an unresolved attention and no replan slot remains. The latest plan's own
|
|
803
|
+
* per-plan reason always leads (a reviewed-and-rejected latest reports its
|
|
804
|
+
* review-round exhaustion verbatim), then `replanBudgetReason` names the planner
|
|
805
|
+
* no-proposal share of the consumed budget. This keeps the `projection.reason`
|
|
806
|
+
* field carrying the same truth the operator-visible row does: a goal whose
|
|
807
|
+
* budget was mostly spent by planner no-proposal runs is not attributed wholesale
|
|
808
|
+
* to review just because its final, reviewed plan happened to be rejected.
|
|
809
|
+
*/
|
|
810
|
+
function exhaustedAttentionReason(
|
|
811
|
+
latest: GovernorPlanRecord,
|
|
812
|
+
consumption: ReplanConsumption,
|
|
813
|
+
): string {
|
|
814
|
+
const fallback = latest.reason ?? `plan ${latest.request.id} requires attention`;
|
|
815
|
+
return replanBudgetReason(fallback, consumption);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Append planner no-proposal attribution to a replan-exhaustion reason. Returns
|
|
820
|
+
* the base reason unchanged when no consumed replan was a planner no-proposal
|
|
821
|
+
* outcome, so goals without that history project exactly as before.
|
|
822
|
+
*/
|
|
823
|
+
function replanBudgetReason(base: string, consumption: ReplanConsumption): string {
|
|
824
|
+
if (consumption.planner_no_proposal <= 0) return base;
|
|
825
|
+
const count = consumption.planner_no_proposal;
|
|
826
|
+
return `${base}; ${count} replan${count === 1 ? "" : "s"} ended with the planner producing no proposal`;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function readGovernorIntent(coordRoot: string, goalId: string): GovernorIntent {
|
|
830
|
+
assertGovernorId(goalId);
|
|
831
|
+
const path = governorIntentPath(coordRoot, goalId);
|
|
832
|
+
if (!existsSync(path)) throw new Error(`governor ${goalId} does not exist`);
|
|
675
833
|
const size = statSync(path).size;
|
|
676
834
|
if (size <= 0 || size > MAX_INTENT_BYTES) {
|
|
677
|
-
throw new Error(`
|
|
835
|
+
throw new Error(`governor intent has invalid size ${size}`);
|
|
678
836
|
}
|
|
679
|
-
let intent:
|
|
837
|
+
let intent: GovernorIntent;
|
|
680
838
|
try {
|
|
681
|
-
intent = JSON.parse(readFileSync(path, "utf8")) as
|
|
839
|
+
intent = JSON.parse(readFileSync(path, "utf8")) as GovernorIntent;
|
|
682
840
|
} catch (error) {
|
|
683
|
-
throw new Error(`cannot parse
|
|
841
|
+
throw new Error(`cannot parse governor intent ${goalId}: ${(error as Error).message}`);
|
|
684
842
|
}
|
|
685
843
|
validateIntent(intent, goalId);
|
|
686
844
|
return intent;
|
|
687
845
|
}
|
|
688
846
|
|
|
689
|
-
function validateIntent(intent:
|
|
847
|
+
function validateIntent(intent: GovernorIntent, goalId: string): void {
|
|
690
848
|
if (
|
|
691
|
-
intent.schema_version !==
|
|
849
|
+
intent.schema_version !== GOVERNOR_INTENT_SCHEMA_VERSION ||
|
|
692
850
|
intent.id !== goalId ||
|
|
693
851
|
!validTimestamp(intent.created_at)
|
|
694
852
|
) {
|
|
695
|
-
throw new Error(`
|
|
853
|
+
throw new Error(`governor intent ${goalId} has an unsupported or mismatched schema`);
|
|
696
854
|
}
|
|
697
|
-
bounded(intent.title, "
|
|
855
|
+
bounded(intent.title, "governor title", MAX_TITLE);
|
|
698
856
|
assertWorkId(intent.root_work_id);
|
|
699
857
|
if (intent.mission) {
|
|
700
858
|
const normalizedMission = normalizeMission({
|
|
@@ -703,79 +861,79 @@ function validateIntent(intent: SupervisorIntent, goalId: string): void {
|
|
|
703
861
|
maxMilestones: intent.mission.max_milestones,
|
|
704
862
|
});
|
|
705
863
|
if (JSON.stringify(normalizedMission) !== JSON.stringify(intent.mission)) {
|
|
706
|
-
throw new Error(`
|
|
864
|
+
throw new Error(`governor intent ${goalId} mission is not canonical`);
|
|
707
865
|
}
|
|
708
866
|
if (!intent.replanning) {
|
|
709
|
-
throw new Error(`
|
|
867
|
+
throw new Error(`governor intent ${goalId} mission requires replanning`);
|
|
710
868
|
}
|
|
711
869
|
}
|
|
712
870
|
const specialists = normalizeWorkflowSpecialists(intent.specialists);
|
|
713
871
|
if (JSON.stringify(specialists) !== JSON.stringify(intent.specialists)) {
|
|
714
|
-
throw new Error(`
|
|
872
|
+
throw new Error(`governor intent ${goalId} specialists are not canonical`);
|
|
715
873
|
}
|
|
716
874
|
if (JSON.stringify(normalizeLimits(intent.limits)) !== JSON.stringify(intent.limits)) {
|
|
717
|
-
throw new Error(`
|
|
875
|
+
throw new Error(`governor intent ${goalId} limits are not canonical`);
|
|
718
876
|
}
|
|
719
877
|
if (
|
|
720
878
|
JSON.stringify(normalizeAutomation(intent.automation)) !== JSON.stringify(intent.automation)
|
|
721
879
|
) {
|
|
722
|
-
throw new Error(`
|
|
880
|
+
throw new Error(`governor intent ${goalId} automation policy is not canonical`);
|
|
723
881
|
}
|
|
724
882
|
if (intent.replanning) validateReplanning(intent.replanning, specialists, intent.limits, goalId);
|
|
725
883
|
}
|
|
726
884
|
|
|
727
|
-
function normalizeMission(input:
|
|
885
|
+
function normalizeMission(input: CreateGovernorMissionInput): GovernorMission {
|
|
728
886
|
if (!Array.isArray(input.acceptance)) {
|
|
729
|
-
throw new Error("
|
|
887
|
+
throw new Error("governor mission acceptance must be an array");
|
|
730
888
|
}
|
|
731
889
|
if (input.acceptance.length < 1 || input.acceptance.length > MAX_ACCEPTANCE) {
|
|
732
|
-
throw new Error(`
|
|
890
|
+
throw new Error(`governor mission acceptance must contain 1 to ${MAX_ACCEPTANCE} criteria`);
|
|
733
891
|
}
|
|
734
892
|
const acceptance = input.acceptance.map((criterion, index) =>
|
|
735
|
-
bounded(criterion, `
|
|
893
|
+
bounded(criterion, `governor mission acceptance[${index}]`, 500),
|
|
736
894
|
);
|
|
737
895
|
if (new Set(acceptance).size !== acceptance.length) {
|
|
738
|
-
throw new Error("
|
|
896
|
+
throw new Error("governor mission acceptance criteria must be unique");
|
|
739
897
|
}
|
|
740
898
|
return {
|
|
741
|
-
objective: bounded(input.objective, "
|
|
899
|
+
objective: bounded(input.objective, "governor mission objective", MAX_OBJECTIVE),
|
|
742
900
|
acceptance,
|
|
743
|
-
max_milestones: positive(input.maxMilestones ?? 4, "
|
|
901
|
+
max_milestones: positive(input.maxMilestones ?? 4, "governor mission max_milestones", 20),
|
|
744
902
|
};
|
|
745
903
|
}
|
|
746
904
|
|
|
747
|
-
function missionTitle(mission:
|
|
905
|
+
function missionTitle(mission: GovernorMission): string {
|
|
748
906
|
return mission.objective.length <= MAX_TITLE
|
|
749
907
|
? mission.objective
|
|
750
908
|
: `${mission.objective.slice(0, MAX_TITLE - 1).trimEnd()}…`;
|
|
751
909
|
}
|
|
752
910
|
|
|
753
|
-
function normalizeLimits(input: Partial<
|
|
911
|
+
function normalizeLimits(input: Partial<GovernorLimits> | undefined): GovernorLimits {
|
|
754
912
|
return {
|
|
755
|
-
max_cycles: positive(input?.max_cycles ?? 50, "
|
|
913
|
+
max_cycles: positive(input?.max_cycles ?? 50, "governor max_cycles", 1_000),
|
|
756
914
|
max_runtime_ms: positive(
|
|
757
915
|
input?.max_runtime_ms ?? 4 * 60 * 60 * 1_000,
|
|
758
|
-
"
|
|
916
|
+
"governor max_runtime_ms",
|
|
759
917
|
7 * 24 * 60 * 60 * 1_000,
|
|
760
918
|
),
|
|
761
|
-
max_parallel_work: positive(input?.max_parallel_work ?? 1, "
|
|
919
|
+
max_parallel_work: positive(input?.max_parallel_work ?? 1, "governor max_parallel_work", 20),
|
|
762
920
|
max_total_attempts: positive(
|
|
763
921
|
input?.max_total_attempts ?? 100,
|
|
764
|
-
"
|
|
922
|
+
"governor max_total_attempts",
|
|
765
923
|
10_000,
|
|
766
924
|
),
|
|
767
925
|
max_agents_per_work: positive(
|
|
768
926
|
input?.max_agents_per_work ?? 20,
|
|
769
|
-
"
|
|
927
|
+
"governor max_agents_per_work",
|
|
770
928
|
1_000,
|
|
771
929
|
),
|
|
772
|
-
agent_concurrency: positive(input?.agent_concurrency ?? 4, "
|
|
930
|
+
agent_concurrency: positive(input?.agent_concurrency ?? 4, "governor agent_concurrency", 100),
|
|
773
931
|
};
|
|
774
932
|
}
|
|
775
933
|
|
|
776
934
|
function normalizeAutomation(
|
|
777
|
-
input: Partial<
|
|
778
|
-
):
|
|
935
|
+
input: Partial<GovernorAutomationPolicy> | undefined,
|
|
936
|
+
): GovernorAutomationPolicy {
|
|
779
937
|
return {
|
|
780
938
|
accept_passing_proof: boolean(input?.accept_passing_proof ?? false, "accept_passing_proof"),
|
|
781
939
|
resume_approved: boolean(input?.resume_approved ?? true, "resume_approved"),
|
|
@@ -784,10 +942,10 @@ function normalizeAutomation(
|
|
|
784
942
|
}
|
|
785
943
|
|
|
786
944
|
function normalizeReplanning(
|
|
787
|
-
input:
|
|
945
|
+
input: CreateGovernorReplanningInput,
|
|
788
946
|
specialistsInput: Readonly<Record<string, WorkflowSpecialistProfile>>,
|
|
789
|
-
limits:
|
|
790
|
-
):
|
|
947
|
+
limits: GovernorLimits,
|
|
948
|
+
): GovernorReplanningPolicy {
|
|
791
949
|
const specialists = normalizeWorkflowSpecialists(specialistsInput);
|
|
792
950
|
const plannerSpecialist = bounded(input.plannerSpecialist, "planner specialist", 100);
|
|
793
951
|
if (!specialists[plannerSpecialist]) {
|
|
@@ -851,17 +1009,17 @@ function normalizeReplanning(
|
|
|
851
1009
|
}
|
|
852
1010
|
|
|
853
1011
|
function normalizeReviewPolicy(
|
|
854
|
-
input: NonNullable<
|
|
1012
|
+
input: NonNullable<CreateGovernorReplanningInput["review"]>,
|
|
855
1013
|
specialists: Record<string, WorkflowSpecialistProfile>,
|
|
856
1014
|
plannerSpecialist: string,
|
|
857
|
-
limits:
|
|
858
|
-
): NonNullable<
|
|
1015
|
+
limits: GovernorLimits,
|
|
1016
|
+
): NonNullable<GovernorReplanningPolicy["review"]> {
|
|
859
1017
|
if (!Array.isArray(input.reviewerSpecialists) || input.reviewerSpecialists.length < 1) {
|
|
860
1018
|
throw new Error("replanning review requires at least one reviewer specialist");
|
|
861
1019
|
}
|
|
862
|
-
if (input.reviewerSpecialists.length >
|
|
1020
|
+
if (input.reviewerSpecialists.length > MAX_GOVERNOR_PLAN_REVIEWERS) {
|
|
863
1021
|
throw new Error(
|
|
864
|
-
`replanning review cannot exceed ${
|
|
1022
|
+
`replanning review cannot exceed ${MAX_GOVERNOR_PLAN_REVIEWERS} reviewer specialists`,
|
|
865
1023
|
);
|
|
866
1024
|
}
|
|
867
1025
|
const reviewerSpecialists = input.reviewerSpecialists.map((specialist, index) =>
|
|
@@ -881,7 +1039,7 @@ function normalizeReviewPolicy(
|
|
|
881
1039
|
const maxRevisionRounds = nonNegative(
|
|
882
1040
|
input.maxRevisionRounds,
|
|
883
1041
|
"replanning review max_revision_rounds",
|
|
884
|
-
|
|
1042
|
+
MAX_GOVERNOR_PLAN_REVISION_ROUNDS,
|
|
885
1043
|
);
|
|
886
1044
|
const worstCaseAgents =
|
|
887
1045
|
1 + reviewerSpecialists.length * (maxRevisionRounds + 1) + maxRevisionRounds;
|
|
@@ -897,24 +1055,24 @@ function normalizeReviewPolicy(
|
|
|
897
1055
|
}
|
|
898
1056
|
|
|
899
1057
|
function validateReplanning(
|
|
900
|
-
policy:
|
|
1058
|
+
policy: GovernorReplanningPolicy,
|
|
901
1059
|
specialists: Record<string, WorkflowSpecialistProfile>,
|
|
902
|
-
limits:
|
|
1060
|
+
limits: GovernorLimits,
|
|
903
1061
|
goalId: string,
|
|
904
1062
|
): void {
|
|
905
1063
|
if (!specialists[policy.planner_specialist]) {
|
|
906
|
-
throw new Error(`
|
|
1064
|
+
throw new Error(`governor ${goalId} planner specialist is not in the frozen team`);
|
|
907
1065
|
}
|
|
908
1066
|
boolean(policy.auto_apply, "replanning auto_apply");
|
|
909
1067
|
positive(policy.max_replans, "replanning max_replans", 100);
|
|
910
1068
|
positive(policy.max_work_items_per_plan, "replanning max_work_items_per_plan", 25);
|
|
911
1069
|
positive(policy.max_total_work_items, "replanning max_total_work_items", 100);
|
|
912
1070
|
if (policy.max_work_items_per_plan > policy.max_total_work_items) {
|
|
913
|
-
throw new Error(`
|
|
1071
|
+
throw new Error(`governor ${goalId} replanning work-item bounds are inconsistent`);
|
|
914
1072
|
}
|
|
915
1073
|
const entries = Object.entries(policy.templates ?? {});
|
|
916
1074
|
if (entries.length < 1 || entries.length > MAX_TEMPLATES) {
|
|
917
|
-
throw new Error(`
|
|
1075
|
+
throw new Error(`governor ${goalId} has invalid replanning templates`);
|
|
918
1076
|
}
|
|
919
1077
|
let rootCapable = false;
|
|
920
1078
|
for (const [id, template] of entries) {
|
|
@@ -927,32 +1085,32 @@ function validateReplanning(
|
|
|
927
1085
|
template.max_attempts > 100 ||
|
|
928
1086
|
typeof template.root !== "boolean"
|
|
929
1087
|
) {
|
|
930
|
-
throw new Error(`
|
|
1088
|
+
throw new Error(`governor ${goalId} replanning template ${id} is invalid`);
|
|
931
1089
|
}
|
|
932
1090
|
rootCapable ||= template.root;
|
|
933
1091
|
}
|
|
934
|
-
if (!rootCapable) throw new Error(`
|
|
1092
|
+
if (!rootCapable) throw new Error(`governor ${goalId} has no root-capable replanning template`);
|
|
935
1093
|
if (policy.review !== undefined) {
|
|
936
1094
|
const review = policy.review;
|
|
937
1095
|
if (
|
|
938
1096
|
!Array.isArray(review.reviewer_specialists) ||
|
|
939
1097
|
review.reviewer_specialists.length < 1 ||
|
|
940
|
-
review.reviewer_specialists.length >
|
|
1098
|
+
review.reviewer_specialists.length > MAX_GOVERNOR_PLAN_REVIEWERS ||
|
|
941
1099
|
!Number.isSafeInteger(review.max_revision_rounds) ||
|
|
942
1100
|
review.max_revision_rounds < 0 ||
|
|
943
|
-
review.max_revision_rounds >
|
|
1101
|
+
review.max_revision_rounds > MAX_GOVERNOR_PLAN_REVISION_ROUNDS
|
|
944
1102
|
) {
|
|
945
|
-
throw new Error(`
|
|
1103
|
+
throw new Error(`governor ${goalId} replanning review policy is invalid`);
|
|
946
1104
|
}
|
|
947
1105
|
if (new Set(review.reviewer_specialists).size !== review.reviewer_specialists.length) {
|
|
948
|
-
throw new Error(`
|
|
1106
|
+
throw new Error(`governor ${goalId} replanning reviewers are not unique`);
|
|
949
1107
|
}
|
|
950
1108
|
for (const specialist of review.reviewer_specialists) {
|
|
951
1109
|
if (!specialists[specialist]) {
|
|
952
|
-
throw new Error(`
|
|
1110
|
+
throw new Error(`governor ${goalId} reviewer specialist is not in the frozen team`);
|
|
953
1111
|
}
|
|
954
1112
|
if (specialist === policy.planner_specialist) {
|
|
955
|
-
throw new Error(`
|
|
1113
|
+
throw new Error(`governor ${goalId} reviewer specialist cannot be planner`);
|
|
956
1114
|
}
|
|
957
1115
|
}
|
|
958
1116
|
const worstCaseAgents =
|
|
@@ -960,7 +1118,7 @@ function validateReplanning(
|
|
|
960
1118
|
review.reviewer_specialists.length * (review.max_revision_rounds + 1) +
|
|
961
1119
|
review.max_revision_rounds;
|
|
962
1120
|
if (worstCaseAgents > limits.max_agents_per_work) {
|
|
963
|
-
throw new Error(`
|
|
1121
|
+
throw new Error(`governor ${goalId} review policy exceeds the agent budget`);
|
|
964
1122
|
}
|
|
965
1123
|
}
|
|
966
1124
|
}
|
|
@@ -968,12 +1126,12 @@ function validateReplanning(
|
|
|
968
1126
|
function collectGovernedWork(
|
|
969
1127
|
coordRoot: string,
|
|
970
1128
|
originalRootWorkId: string,
|
|
971
|
-
plans:
|
|
1129
|
+
plans: GovernorPlanHistory,
|
|
972
1130
|
active: WorkRecord[],
|
|
973
1131
|
): WorkRecord[] {
|
|
974
1132
|
const governed = new Map<string, WorkRecord>();
|
|
975
1133
|
if (workIntentExists(coordRoot, originalRootWorkId)) {
|
|
976
|
-
for (const record of
|
|
1134
|
+
for (const record of collectGovernorWork(coordRoot, originalRootWorkId)) {
|
|
977
1135
|
governed.set(record.intent.id, record);
|
|
978
1136
|
}
|
|
979
1137
|
}
|
|
@@ -991,17 +1149,17 @@ function missionRootId(goalId: string): string {
|
|
|
991
1149
|
return `${goalId.slice(0, 87)}-mission-root`;
|
|
992
1150
|
}
|
|
993
1151
|
|
|
994
|
-
function
|
|
995
|
-
|
|
996
|
-
return join(resolve(coordRoot), ".harnery", "
|
|
1152
|
+
function governorDir(coordRoot: string, goalId: string): string {
|
|
1153
|
+
assertGovernorId(goalId);
|
|
1154
|
+
return join(resolve(coordRoot), ".harnery", "governors", goalId);
|
|
997
1155
|
}
|
|
998
1156
|
|
|
999
|
-
function
|
|
1000
|
-
return join(
|
|
1157
|
+
function governorIntentPath(coordRoot: string, goalId: string): string {
|
|
1158
|
+
return join(governorDir(coordRoot, goalId), "intent.json");
|
|
1001
1159
|
}
|
|
1002
1160
|
|
|
1003
|
-
function
|
|
1004
|
-
const lease = readLease(join(
|
|
1161
|
+
function governorLeaseIsLive(coordRoot: string, goalId: string): boolean {
|
|
1162
|
+
const lease = readLease(join(governorDir(coordRoot, goalId), "lease.json"));
|
|
1005
1163
|
return lease ? leaseIsLive(lease) : false;
|
|
1006
1164
|
}
|
|
1007
1165
|
|
|
@@ -1043,7 +1201,7 @@ function leaseIsLive(lease: { pid: number; host: string; created_at: string }):
|
|
|
1043
1201
|
function writePrivateJson(path: string, value: unknown): void {
|
|
1044
1202
|
const body = `${JSON.stringify(value, null, 2)}\n`;
|
|
1045
1203
|
if (Buffer.byteLength(body) > MAX_INTENT_BYTES) {
|
|
1046
|
-
throw new Error(`
|
|
1204
|
+
throw new Error(`governor intent exceeds ${MAX_INTENT_BYTES} bytes`);
|
|
1047
1205
|
}
|
|
1048
1206
|
writeFileSync(path, body, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
1049
1207
|
chmodSync(path, 0o600);
|
|
@@ -1072,7 +1230,7 @@ function nonNegative(value: unknown, field: string, max: number): number {
|
|
|
1072
1230
|
}
|
|
1073
1231
|
|
|
1074
1232
|
function boolean(value: unknown, field: string): boolean {
|
|
1075
|
-
if (typeof value !== "boolean") throw new Error(`
|
|
1233
|
+
if (typeof value !== "boolean") throw new Error(`governor ${field} must be boolean`);
|
|
1076
1234
|
return value;
|
|
1077
1235
|
}
|
|
1078
1236
|
|