harnery 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/commander.d.ts +8 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +14 -13
- package/dist/commands/adapter.d.ts +12 -0
- package/dist/commands/adapter.d.ts.map +1 -0
- package/dist/commands/{harness.js → adapter.js} +35 -35
- package/dist/commands/agents.d.ts +3 -2
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +61 -60
- package/dist/commands/artifacts.d.ts +4 -0
- package/dist/commands/artifacts.d.ts.map +1 -0
- package/dist/commands/artifacts.js +143 -0
- package/dist/commands/backup.d.ts +1 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +5 -2
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +310 -7
- package/dist/commands/checkpoint.d.ts +12 -0
- package/dist/commands/checkpoint.d.ts.map +1 -0
- package/dist/commands/checkpoint.js +162 -0
- package/dist/commands/context.d.ts +2 -2
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +3 -151
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +31 -7
- package/dist/commands/deinit.d.ts +2 -2
- package/dist/commands/deinit.js +15 -15
- package/dist/commands/doctor.js +23 -23
- package/dist/commands/governor.d.ts +4 -0
- package/dist/commands/governor.d.ts.map +1 -0
- package/dist/commands/{supervisor.js → governor.js} +133 -108
- package/dist/commands/init.d.ts +12 -12
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +44 -30
- package/dist/commands/{session.d.ts → journal.d.ts} +2 -2
- package/dist/commands/journal.d.ts.map +1 -0
- package/dist/commands/{scratch.js → journal.js} +47 -47
- package/dist/commands/policy.js +1 -1
- package/dist/commands/presence.js +2 -2
- package/dist/commands/sync.d.ts +1 -1
- package/dist/commands/sync.js +3 -3
- package/dist/commands/tunnel.d.ts +16 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +193 -23
- package/dist/commands/web-fetch.js +1 -1
- package/dist/commands/web.d.ts +19 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +51 -0
- package/dist/commands/work.d.ts +2 -1
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +55 -25
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +35 -28
- package/dist/core/{harnesses → adapters}/attest-projection.d.ts +2 -2
- package/dist/core/adapters/attest-projection.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attest.d.ts +16 -16
- package/dist/core/adapters/attest.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attest.js +9 -9
- package/dist/core/{harnesses → adapters}/attestation.d.ts +16 -16
- package/dist/core/adapters/attestation.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attestation.js +20 -20
- package/dist/core/{harnesses → adapters}/bench.d.ts +12 -12
- package/dist/core/adapters/bench.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/bench.js +12 -12
- package/dist/core/adapters/index.d.ts +13 -0
- package/dist/core/adapters/index.d.ts.map +1 -0
- package/dist/core/adapters/index.js +6 -0
- package/dist/core/{harnesses → adapters}/profiles.d.ts +11 -11
- package/dist/core/adapters/profiles.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/profiles.js +11 -11
- package/dist/core/adapters/registry.d.ts +15 -0
- package/dist/core/adapters/registry.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/registry.js +11 -11
- package/dist/core/{harnesses → adapters}/types.d.ts +28 -23
- package/dist/core/adapters/types.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/types.js +1 -1
- package/dist/core/agents/canonical-emit.d.ts +4 -4
- package/dist/core/agents/canonical-emit.js +5 -5
- package/dist/core/agents/cli-emit.js +1 -1
- package/dist/core/agents/cli.js +30 -26
- package/dist/core/agents/codex-replay.d.ts +1 -1
- package/dist/core/agents/codex-replay.js +6 -6
- package/dist/core/agents/coord-client.d.ts +21 -2
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +122 -27
- package/dist/core/agents/events/consume.d.ts +1 -1
- package/dist/core/agents/events/emit.d.ts +5 -6
- package/dist/core/agents/events/emit.d.ts.map +1 -1
- package/dist/core/agents/events/emit.js +2 -2
- package/dist/core/agents/render/prompt-context.d.ts +21 -9
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +82 -30
- package/dist/core/agents/render/session-context.d.ts +1 -1
- package/dist/core/agents/render/session-context.js +5 -5
- package/dist/core/agents/rules/claim-conflict.d.ts +2 -6
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +1 -1
- package/dist/core/agents/rules/commit-conflict.js +98 -22
- package/dist/core/agents/rules/stop-hook.d.ts +9 -9
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +23 -5
- package/dist/core/agents/rules/verdict.d.ts +14 -0
- package/dist/core/agents/rules/verdict.d.ts.map +1 -0
- package/dist/core/agents/rules/verdict.js +1 -0
- package/dist/core/agents/session-events.js +3 -3
- package/dist/core/agents/state/council.d.ts +1 -1
- package/dist/core/agents/state/council.js +1 -1
- package/dist/core/agents/state/heartbeat-projector.d.ts +3 -2
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +14 -10
- package/dist/core/agents/state/heartbeat-writer.d.ts +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +12 -12
- package/dist/core/agents/state/{scratch.d.ts → journal.d.ts} +7 -7
- package/dist/core/agents/state/{scratch.d.ts.map → journal.d.ts.map} +1 -1
- package/dist/core/agents/state/{scratch.js → journal.js} +12 -12
- package/dist/core/agents/state/pidmap.d.ts +28 -4
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +130 -15
- package/dist/core/agents/state/proc-start.d.ts +95 -0
- package/dist/core/agents/state/proc-start.d.ts.map +1 -0
- package/dist/core/agents/state/proc-start.js +199 -0
- package/dist/core/agents/state/shell-mutation.d.ts +1 -1
- package/dist/core/agents/state/shell-mutation.js +1 -1
- package/dist/core/agents/state/stale-sweep.js +7 -7
- package/dist/core/artifacts/index.d.ts +86 -0
- package/dist/core/artifacts/index.d.ts.map +1 -0
- package/dist/core/artifacts/index.js +421 -0
- package/dist/core/config.d.ts +9 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +26 -2
- package/dist/core/context/index.d.ts +12 -12
- package/dist/core/context/index.js +10 -10
- package/dist/core/governor/adapter-spread.d.ts +52 -0
- package/dist/core/governor/adapter-spread.d.ts.map +1 -0
- package/dist/core/governor/adapter-spread.js +52 -0
- package/dist/core/governor/index.d.ts +7 -0
- package/dist/core/governor/index.d.ts.map +1 -0
- package/dist/core/governor/index.js +6 -0
- package/dist/core/governor/plan-api.d.ts +38 -0
- package/dist/core/governor/plan-api.d.ts.map +1 -0
- package/dist/core/governor/plan-api.js +83 -0
- package/dist/core/governor/plan-read.d.ts +5 -0
- package/dist/core/governor/plan-read.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/plan-read.js +71 -51
- package/dist/core/governor/plan-types.d.ts +181 -0
- package/dist/core/governor/plan-types.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/plan-types.js +4 -4
- package/dist/core/governor/planning.d.ts +48 -0
- package/dist/core/governor/planning.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/planning.js +143 -94
- package/dist/core/governor/read.d.ts +6 -0
- package/dist/core/governor/read.d.ts.map +1 -0
- package/dist/core/governor/read.js +4 -0
- package/dist/core/governor/runner.d.ts +37 -0
- package/dist/core/governor/runner.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/runner.js +15 -15
- package/dist/core/governor/service-read.d.ts +6 -0
- package/dist/core/governor/service-read.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/service-read.js +12 -12
- package/dist/core/governor/service.d.ts +114 -0
- package/dist/core/governor/service.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/service.js +57 -57
- package/dist/core/governor/state.d.ts +116 -0
- package/dist/core/governor/state.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/state.js +290 -116
- package/dist/core/hooks/adapter/detect.d.ts +9 -0
- package/dist/core/hooks/{harness → adapter}/detect.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/detect.js +8 -8
- package/dist/core/hooks/{harness → adapter}/events.d.ts +15 -14
- package/dist/core/hooks/adapter/events.d.ts.map +1 -0
- package/dist/core/hooks/{harness → adapter}/events.js +5 -5
- package/dist/core/hooks/{harness → adapter}/output.d.ts +20 -14
- package/dist/core/hooks/adapter/output.d.ts.map +1 -0
- package/dist/core/hooks/{harness → adapter}/output.js +30 -22
- package/dist/core/hooks/{harness → adapter}/parse.d.ts +5 -5
- package/dist/core/hooks/{harness → adapter}/parse.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/parse.js +4 -4
- package/dist/core/hooks/{harness → adapter}/wiring.d.ts +15 -15
- package/dist/core/hooks/{harness → adapter}/wiring.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/wiring.js +13 -13
- package/dist/core/hooks/cli.d.ts +3 -3
- package/dist/core/hooks/cli.js +175 -151
- package/dist/core/hooks/effects/image-capture.d.ts +5 -5
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +3 -3
- package/dist/core/hooks/effects/index.d.ts +12 -12
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +17 -17
- package/dist/core/hooks/events/emit.d.ts +2 -2
- package/dist/core/hooks/events/emit.d.ts.map +1 -1
- package/dist/core/hooks/events/emit.js +1 -1
- package/dist/core/hooks/events/rotate.js +1 -1
- package/dist/core/hooks/events/schema.d.ts +13 -5
- package/dist/core/hooks/events/schema.d.ts.map +1 -1
- package/dist/core/hooks/guard-path.d.ts +2 -2
- package/dist/core/hooks/guard-path.js +2 -2
- package/dist/core/hooks/index.d.ts +1 -1
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/resolve/anchor.d.ts +14 -5
- package/dist/core/hooks/resolve/anchor.d.ts.map +1 -1
- package/dist/core/hooks/resolve/anchor.js +64 -7
- package/dist/core/hooks/resolve/coord-root.d.ts +3 -3
- package/dist/core/hooks/resolve/coord-root.js +4 -4
- package/dist/core/hooks/resolve/intent.js +1 -1
- package/dist/core/hooks/resolve/owner.d.ts +1 -1
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +11 -5
- package/dist/core/{scratch → journal}/index.d.ts +25 -25
- package/dist/core/{scratch → journal}/index.d.ts.map +1 -1
- package/dist/core/{scratch → journal}/index.js +38 -38
- package/dist/core/policy/evaluate.d.ts +1 -1
- package/dist/core/policy/evaluate.d.ts.map +1 -1
- package/dist/core/policy/evaluate.js +7 -7
- package/dist/core/policy/types.d.ts +3 -3
- package/dist/core/policy/types.d.ts.map +1 -1
- package/dist/core/work/runner.d.ts.map +1 -1
- package/dist/core/work/runner.js +11 -2
- package/dist/core/work/state.d.ts +28 -3
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +148 -16
- package/dist/core/workflow/adapters.d.ts +17 -0
- package/dist/core/workflow/adapters.d.ts.map +1 -0
- package/dist/core/workflow/adapters.js +21 -0
- package/dist/core/workflow/approvals.js +6 -6
- package/dist/core/workflow/billing.d.ts +9 -9
- package/dist/core/workflow/billing.js +9 -9
- package/dist/core/workflow/child-env.d.ts +10 -2
- package/dist/core/workflow/child-env.d.ts.map +1 -1
- package/dist/core/workflow/child-env.js +12 -4
- package/dist/core/workflow/engine.d.ts +15 -5
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +151 -83
- package/dist/core/workflow/evidence-preflight.d.ts +24 -0
- package/dist/core/workflow/evidence-preflight.d.ts.map +1 -0
- package/dist/core/workflow/evidence-preflight.js +134 -0
- package/dist/core/workflow/index.d.ts +3 -2
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -1
- package/dist/core/workflow/proof.d.ts +28 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +77 -34
- package/dist/core/workflow/run-state.d.ts +1 -1
- package/dist/core/workflow/run-state.js +7 -7
- package/dist/core/workflow/sandbox-projection.d.ts +6 -6
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +11 -11
- package/dist/core/workflow/spawn-claude.d.ts +3 -3
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +28 -8
- package/dist/core/workflow/spawn-codex.d.ts +4 -4
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +26 -8
- package/dist/core/workflow/spawn-cursor.d.ts +4 -4
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +27 -8
- package/dist/core/workflow/spawn-failure.d.ts +15 -1
- package/dist/core/workflow/spawn-failure.d.ts.map +1 -1
- package/dist/core/workflow/spawn-failure.js +35 -1
- package/dist/core/workflow/specialists.js +2 -2
- package/dist/core/workflow/{journal.d.ts → transcript.d.ts} +8 -8
- package/dist/core/workflow/transcript.d.ts.map +1 -0
- package/dist/core/workflow/{journal.js → transcript.js} +14 -14
- package/dist/core/workflow/types.d.ts +119 -46
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/types.js +11 -1
- package/dist/core/workflow/validate.d.ts +1 -1
- package/dist/core/workflow/validate.js +1 -1
- package/dist/core/workflow/workspaces/cancellation.js +2 -2
- package/dist/core/workflow/workspaces/cleanup.js +2 -2
- package/dist/core/workflow/workspaces/execution.js +6 -6
- package/dist/core/workflow/workspaces/inspect.js +9 -9
- package/dist/core/workflow/workspaces/integration.js +14 -14
- package/dist/core/workflow/workspaces/lifecycle.d.ts +1 -1
- package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/lifecycle.js +9 -9
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +39 -5
- package/dist/core/workflow/workspaces/recovery.js +3 -3
- package/dist/core/workflow/workspaces/state.d.ts +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +3 -3
- package/dist/core/workflow/workspaces/types.d.ts +1 -1
- package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
- package/dist/lib/browser/asserts.d.ts +28 -0
- package/dist/lib/browser/asserts.d.ts.map +1 -0
- package/dist/lib/browser/asserts.js +152 -0
- package/dist/lib/browser/client.d.ts +49 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +155 -14
- package/dist/lib/browser/content-checks.d.ts +108 -0
- package/dist/lib/browser/content-checks.d.ts.map +1 -0
- package/dist/lib/browser/content-checks.js +474 -0
- package/dist/lib/browser/critique.d.ts +91 -0
- package/dist/lib/browser/critique.d.ts.map +1 -0
- package/dist/lib/browser/critique.js +167 -0
- package/dist/lib/browser/geometry.d.ts +32 -0
- package/dist/lib/browser/geometry.d.ts.map +1 -1
- package/dist/lib/browser/geometry.js +244 -1
- package/dist/lib/browser/index.d.ts +4 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -0
- package/dist/lib/claude-desktop.d.ts.map +1 -1
- package/dist/lib/claude-desktop.js +4 -1
- package/dist/lib/council/index.d.ts +6 -6
- package/dist/lib/council/index.d.ts.map +1 -1
- package/dist/lib/council/index.js +8 -8
- package/dist/lib/exec.d.ts +7 -0
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +6 -3
- package/dist/lib/identities/assume.d.ts +2 -2
- package/dist/lib/identities/assume.js +7 -7
- package/dist/lib/instructions/apply.d.ts +3 -3
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +75 -27
- package/dist/lib/instructions/host-addendum.d.ts +54 -0
- package/dist/lib/instructions/host-addendum.d.ts.map +1 -0
- package/dist/lib/instructions/host-addendum.js +107 -0
- package/dist/lib/instructions/templates.d.ts +10 -3
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +41 -12
- package/dist/lib/machine.d.ts +2 -2
- package/dist/lib/machine.js +2 -2
- package/package.json +26 -21
- package/schemas/config.schema.json +26 -1
- package/src/commander.ts +22 -13
- package/src/commands/{harness.ts → adapter.ts} +46 -46
- package/src/commands/agents.ts +71 -64
- package/src/commands/artifacts.ts +168 -0
- package/src/commands/backup.ts +47 -33
- package/src/commands/browse.ts +438 -2
- package/src/commands/checkpoint.ts +196 -0
- package/src/commands/context.ts +3 -176
- package/src/commands/decision.ts +41 -7
- package/src/commands/deinit.ts +16 -16
- package/src/commands/doctor.ts +28 -28
- package/src/commands/{supervisor.ts → governor.ts} +184 -154
- package/src/commands/init.ts +50 -34
- package/src/commands/{scratch.ts → journal.ts} +60 -60
- package/src/commands/policy.ts +1 -1
- package/src/commands/presence.ts +2 -2
- package/src/commands/sync.ts +3 -3
- package/src/commands/tunnel.ts +237 -34
- package/src/commands/web-fetch.ts +1 -1
- package/src/commands/web.ts +56 -1
- package/src/commands/work.ts +72 -38
- package/src/commands/workflow.ts +43 -34
- package/src/core/{harnesses → adapters}/attest-projection.ts +2 -2
- package/src/core/{harnesses → adapters}/attest.ts +27 -27
- package/src/core/{harnesses → adapters}/attestation.ts +41 -41
- package/src/core/{harnesses → adapters}/bench.ts +34 -43
- package/src/core/adapters/index.ts +56 -0
- package/src/core/{harnesses → adapters}/profiles.ts +16 -16
- package/src/core/{harnesses → adapters}/registry.ts +23 -23
- package/src/core/{harnesses → adapters}/types.ts +28 -23
- package/src/core/agents/canonical-emit.ts +6 -6
- package/src/core/agents/cli-emit.ts +1 -1
- package/src/core/agents/cli.ts +30 -28
- package/src/core/agents/codex-replay.ts +6 -6
- package/src/core/agents/coord-client.ts +119 -28
- package/src/core/agents/events/consume.ts +1 -1
- package/src/core/agents/events/emit.ts +7 -6
- package/src/core/agents/render/prompt-context.ts +107 -31
- package/src/core/agents/render/session-context.ts +6 -6
- package/src/core/agents/rules/claim-conflict.ts +4 -7
- package/src/core/agents/rules/commit-conflict.ts +103 -22
- package/src/core/agents/rules/stop-hook.ts +33 -17
- package/src/core/agents/rules/verdict.ts +13 -0
- package/src/core/agents/session-events.ts +4 -4
- package/src/core/agents/state/council.ts +1 -1
- package/src/core/agents/state/heartbeat-projector.ts +16 -12
- package/src/core/agents/state/heartbeat-writer.ts +12 -12
- package/src/core/agents/state/{scratch.ts → journal.ts} +12 -12
- package/src/core/agents/state/pidmap.ts +130 -15
- package/src/core/agents/state/proc-start.ts +208 -0
- package/src/core/agents/state/shell-mutation.ts +1 -1
- package/src/core/agents/state/stale-sweep.ts +7 -7
- package/src/core/artifacts/index.ts +557 -0
- package/src/core/config.ts +30 -2
- package/src/core/context/index.ts +15 -15
- package/src/core/governor/adapter-spread.ts +82 -0
- package/src/core/governor/index.ts +91 -0
- package/src/core/governor/plan-api.ts +115 -0
- package/src/core/{supervisor → governor}/plan-read.ts +120 -96
- package/src/core/{supervisor → governor}/plan-types.ts +64 -50
- package/src/core/{supervisor → governor}/planning.ts +240 -188
- package/src/core/governor/read.ts +59 -0
- package/src/core/{supervisor → governor}/runner.ts +39 -39
- package/src/core/{supervisor → governor}/service-read.ts +22 -22
- package/src/core/{supervisor → governor}/service.ts +117 -119
- package/src/core/{supervisor → governor}/state.ts +389 -174
- package/src/core/hooks/adapter/detect.ts +30 -0
- package/src/core/hooks/{harness → adapter}/events.ts +16 -14
- package/src/core/hooks/{harness → adapter}/output.ts +31 -23
- package/src/core/hooks/{harness → adapter}/parse.ts +5 -5
- package/src/core/hooks/{harness → adapter}/wiring.ts +21 -21
- package/src/core/hooks/cli.ts +185 -162
- package/src/core/hooks/effects/image-capture.ts +6 -6
- package/src/core/hooks/effects/index.ts +17 -17
- package/src/core/hooks/events/emit.ts +3 -3
- package/src/core/hooks/events/rotate.ts +1 -1
- package/src/core/hooks/events/schema.ts +15 -7
- package/src/core/hooks/guard-path.ts +2 -2
- package/src/core/hooks/index.ts +1 -1
- package/src/core/hooks/resolve/anchor.ts +63 -8
- package/src/core/hooks/resolve/coord-root.ts +4 -4
- package/src/core/hooks/resolve/intent.ts +1 -1
- package/src/core/hooks/resolve/owner.ts +11 -5
- package/src/core/{scratch → journal}/index.ts +53 -53
- package/src/core/policy/evaluate.ts +8 -8
- package/src/core/policy/types.ts +3 -3
- package/src/core/work/runner.ts +11 -2
- package/src/core/work/state.ts +185 -21
- package/src/core/workflow/adapters.ts +33 -0
- package/src/core/workflow/approvals.ts +7 -7
- package/src/core/workflow/billing.ts +12 -12
- package/src/core/workflow/child-env.ts +13 -4
- package/src/core/workflow/engine.ts +166 -90
- package/src/core/workflow/evidence-preflight.ts +165 -0
- package/src/core/workflow/index.ts +6 -3
- package/src/core/workflow/proof.ts +94 -48
- package/src/core/workflow/run-state.ts +7 -7
- package/src/core/workflow/sandbox-projection.ts +16 -16
- package/src/core/workflow/spawn-claude.ts +34 -12
- package/src/core/workflow/spawn-codex.ts +30 -12
- package/src/core/workflow/spawn-cursor.ts +31 -12
- package/src/core/workflow/spawn-failure.ts +41 -1
- package/src/core/workflow/specialists.ts +2 -2
- package/src/core/workflow/{journal.ts → transcript.ts} +16 -16
- package/src/core/workflow/types.ts +130 -46
- package/src/core/workflow/validate.ts +1 -1
- package/src/core/workflow/workspaces/cancellation.ts +2 -2
- package/src/core/workflow/workspaces/cleanup.ts +2 -2
- package/src/core/workflow/workspaces/execution.ts +6 -6
- package/src/core/workflow/workspaces/inspect.ts +14 -11
- package/src/core/workflow/workspaces/integration.ts +18 -14
- package/src/core/workflow/workspaces/lifecycle.ts +10 -10
- package/src/core/workflow/workspaces/local-git.ts +47 -5
- package/src/core/workflow/workspaces/recovery.ts +3 -3
- package/src/core/workflow/workspaces/state.ts +4 -3
- package/src/core/workflow/workspaces/types.ts +1 -1
- package/src/lib/browser/asserts.ts +177 -0
- package/src/lib/browser/client.ts +189 -14
- package/src/lib/browser/content-checks.ts +571 -0
- package/src/lib/browser/critique.ts +231 -0
- package/src/lib/browser/geometry.ts +297 -1
- package/src/lib/browser/index.ts +34 -0
- package/src/lib/claude-desktop.ts +4 -1
- package/src/lib/council/index.ts +9 -9
- package/src/lib/exec.ts +13 -3
- package/src/lib/identities/assume.ts +7 -7
- package/src/lib/instructions/apply.ts +87 -25
- package/src/lib/instructions/host-addendum.ts +126 -0
- package/src/lib/instructions/templates.ts +44 -15
- package/src/lib/machine.ts +2 -2
- package/dist/commands/harness.d.ts +0 -12
- package/dist/commands/harness.d.ts.map +0 -1
- package/dist/commands/scratch.d.ts +0 -4
- package/dist/commands/scratch.d.ts.map +0 -1
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/session.js +0 -162
- package/dist/commands/supervisor.d.ts +0 -4
- package/dist/commands/supervisor.d.ts.map +0 -1
- package/dist/core/harnesses/attest-projection.d.ts.map +0 -1
- package/dist/core/harnesses/attest.d.ts.map +0 -1
- package/dist/core/harnesses/attestation.d.ts.map +0 -1
- package/dist/core/harnesses/bench.d.ts.map +0 -1
- package/dist/core/harnesses/index.d.ts +0 -13
- package/dist/core/harnesses/index.d.ts.map +0 -1
- package/dist/core/harnesses/index.js +0 -6
- package/dist/core/harnesses/profiles.d.ts.map +0 -1
- package/dist/core/harnesses/registry.d.ts +0 -15
- package/dist/core/harnesses/registry.d.ts.map +0 -1
- package/dist/core/harnesses/types.d.ts.map +0 -1
- package/dist/core/hooks/harness/detect.d.ts +0 -9
- package/dist/core/hooks/harness/events.d.ts.map +0 -1
- package/dist/core/hooks/harness/output.d.ts.map +0 -1
- package/dist/core/supervisor/index.d.ts +0 -7
- package/dist/core/supervisor/index.d.ts.map +0 -1
- package/dist/core/supervisor/index.js +0 -6
- package/dist/core/supervisor/plan-api.d.ts +0 -23
- package/dist/core/supervisor/plan-api.d.ts.map +0 -1
- package/dist/core/supervisor/plan-api.js +0 -50
- package/dist/core/supervisor/plan-read.d.ts +0 -5
- package/dist/core/supervisor/plan-read.d.ts.map +0 -1
- package/dist/core/supervisor/plan-types.d.ts +0 -170
- package/dist/core/supervisor/plan-types.d.ts.map +0 -1
- package/dist/core/supervisor/planning.d.ts +0 -33
- package/dist/core/supervisor/planning.d.ts.map +0 -1
- package/dist/core/supervisor/read.d.ts +0 -6
- package/dist/core/supervisor/read.d.ts.map +0 -1
- package/dist/core/supervisor/read.js +0 -4
- package/dist/core/supervisor/runner.d.ts +0 -37
- package/dist/core/supervisor/runner.d.ts.map +0 -1
- package/dist/core/supervisor/service-read.d.ts +0 -6
- package/dist/core/supervisor/service-read.d.ts.map +0 -1
- package/dist/core/supervisor/service.d.ts +0 -114
- package/dist/core/supervisor/service.d.ts.map +0 -1
- package/dist/core/supervisor/state.d.ts +0 -95
- package/dist/core/supervisor/state.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.d.ts +0 -17
- package/dist/core/workflow/harnesses.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.js +0 -21
- package/dist/core/workflow/journal.d.ts.map +0 -1
- package/src/commands/session.ts +0 -187
- package/src/core/harnesses/index.ts +0 -56
- package/src/core/hooks/harness/detect.ts +0 -30
- package/src/core/supervisor/index.ts +0 -89
- package/src/core/supervisor/plan-api.ts +0 -73
- package/src/core/supervisor/read.ts +0 -59
- package/src/core/workflow/harnesses.ts +0 -33
- /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
package/src/commands/tunnel.ts
CHANGED
|
@@ -7,10 +7,11 @@ function cachePath(tool: string, filename: string): string {
|
|
|
7
7
|
return resolve(dir, filename);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
10
|
+
import { type ChildProcess, spawn, spawnSync } from "node:child_process";
|
|
11
11
|
import { existsSync, openSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { resolve } from "node:path";
|
|
13
13
|
|
|
14
|
+
import { resolveBinName } from "../core/config.ts";
|
|
14
15
|
import {
|
|
15
16
|
clearState,
|
|
16
17
|
DEFAULT_INSTANCE,
|
|
@@ -62,6 +63,11 @@ interface DownOpts {
|
|
|
62
63
|
all?: boolean;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
interface ReloadOpts {
|
|
67
|
+
name?: string;
|
|
68
|
+
all?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
interface StatusOpts {
|
|
66
72
|
name?: string;
|
|
67
73
|
}
|
|
@@ -393,6 +399,49 @@ function tunnelIsAlive(state: TunnelState): boolean {
|
|
|
393
399
|
return isProcessAlive(state.gate_pid) && providerIsAlive(state);
|
|
394
400
|
}
|
|
395
401
|
|
|
402
|
+
interface GateSpawnOpts {
|
|
403
|
+
name: string;
|
|
404
|
+
gatePort: number;
|
|
405
|
+
target: string;
|
|
406
|
+
vhost: string;
|
|
407
|
+
provider: TunnelProvider;
|
|
408
|
+
allowedIps: string[];
|
|
409
|
+
gateLogPath: string;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Spawn the detached gate worker.
|
|
414
|
+
*
|
|
415
|
+
* Shared by `up` (fresh start) and `reload` (in-place restart) so both hand the
|
|
416
|
+
* gate an identical environment and record the same kind of PID. The allowlist
|
|
417
|
+
* is passed as an env var, which the gate snapshots at module load and never
|
|
418
|
+
* re-reads — that snapshot is precisely why `reload` has to exist.
|
|
419
|
+
*/
|
|
420
|
+
function spawnGate(o: GateSpawnOpts): ChildProcess {
|
|
421
|
+
const gateFd = openSync(o.gateLogPath, "a");
|
|
422
|
+
// `--name`/`--port` on argv mirror the env vars; they're what makes the gate
|
|
423
|
+
// process distinguishable per-instance in `pgrep -f` (see sweepStrays).
|
|
424
|
+
const gateProc = spawn(
|
|
425
|
+
"bun",
|
|
426
|
+
["run", gateScriptPath(), "--name", o.name, "--port", String(o.gatePort)],
|
|
427
|
+
{
|
|
428
|
+
detached: true,
|
|
429
|
+
stdio: ["ignore", gateFd, gateFd],
|
|
430
|
+
env: {
|
|
431
|
+
...process.env,
|
|
432
|
+
HARNERY_TUNNEL_ALLOW: o.allowedIps.join(","),
|
|
433
|
+
HARNERY_TUNNEL_ACCESS:
|
|
434
|
+
o.provider === "cloudflare" ? "cloudflare-allowlist" : "trusted-local-proxy",
|
|
435
|
+
HARNERY_TUNNEL_TARGET: o.target,
|
|
436
|
+
HARNERY_TUNNEL_VHOST: o.vhost,
|
|
437
|
+
HARNERY_TUNNEL_PORT: String(o.gatePort),
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
);
|
|
441
|
+
gateProc.unref();
|
|
442
|
+
return gateProc;
|
|
443
|
+
}
|
|
444
|
+
|
|
396
445
|
async function up(opts: UpOpts): Promise<void> {
|
|
397
446
|
if (!bunAvailable()) {
|
|
398
447
|
emit.error({
|
|
@@ -463,27 +512,15 @@ async function up(opts: UpOpts): Promise<void> {
|
|
|
463
512
|
writeFileSync(gateLogPath, "");
|
|
464
513
|
writeFileSync(providerLogPath, "");
|
|
465
514
|
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
env: {
|
|
476
|
-
...process.env,
|
|
477
|
-
HARNERY_TUNNEL_ALLOW: cfg.allowed_ips.join(","),
|
|
478
|
-
HARNERY_TUNNEL_ACCESS:
|
|
479
|
-
provider === "cloudflare" ? "cloudflare-allowlist" : "trusted-local-proxy",
|
|
480
|
-
HARNERY_TUNNEL_TARGET: target,
|
|
481
|
-
HARNERY_TUNNEL_VHOST: vhost,
|
|
482
|
-
HARNERY_TUNNEL_PORT: String(gatePort),
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
);
|
|
486
|
-
gateProc.unref();
|
|
515
|
+
const gateProc = spawnGate({
|
|
516
|
+
name,
|
|
517
|
+
gatePort,
|
|
518
|
+
target,
|
|
519
|
+
vhost,
|
|
520
|
+
provider,
|
|
521
|
+
allowedIps: cfg.allowed_ips,
|
|
522
|
+
gateLogPath,
|
|
523
|
+
});
|
|
487
524
|
|
|
488
525
|
await sleep(800);
|
|
489
526
|
|
|
@@ -658,6 +695,156 @@ function down(opts: DownOpts): void {
|
|
|
658
695
|
);
|
|
659
696
|
}
|
|
660
697
|
|
|
698
|
+
/** Poll until `port` has no LISTEN socket (or the deadline passes). */
|
|
699
|
+
async function waitForPortFree(port: number, timeoutMs: number): Promise<boolean> {
|
|
700
|
+
const deadline = Date.now() + timeoutMs;
|
|
701
|
+
while (Date.now() < deadline) {
|
|
702
|
+
if (!listeningPorts().has(port)) return true;
|
|
703
|
+
await sleep(100);
|
|
704
|
+
}
|
|
705
|
+
return !listeningPorts().has(port);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** Poll until `port` has a LISTEN socket (or the deadline passes). */
|
|
709
|
+
async function waitForPortBound(port: number, timeoutMs: number): Promise<boolean> {
|
|
710
|
+
const deadline = Date.now() + timeoutMs;
|
|
711
|
+
while (Date.now() < deadline) {
|
|
712
|
+
if (listeningPorts().has(port)) return true;
|
|
713
|
+
await sleep(100);
|
|
714
|
+
}
|
|
715
|
+
return listeningPorts().has(port);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Restart ONE instance's gate in place, deliberately leaving the provider
|
|
720
|
+
* process running.
|
|
721
|
+
*
|
|
722
|
+
* That asymmetry is the whole point. A Cloudflare quick tunnel's hostname is
|
|
723
|
+
* minted by cloudflared at startup, so a full `down`/`up` hands back a NEW
|
|
724
|
+
* random *.trycloudflare.com URL and breaks every link already shared — a
|
|
725
|
+
* miserable trade for adding one IP. cloudflared only ever forwards to
|
|
726
|
+
* `localhost:<gate_port>`, so the gate beneath it can be swapped out and the
|
|
727
|
+
* edge reconnects without noticing.
|
|
728
|
+
*/
|
|
729
|
+
export async function reloadOne(state: TunnelState): Promise<{ ok: boolean; message: string }> {
|
|
730
|
+
const bin = resolveBinName();
|
|
731
|
+
const name = state.name;
|
|
732
|
+
const gatePort = state.gate_port;
|
|
733
|
+
|
|
734
|
+
if (!providerIsAlive(state)) {
|
|
735
|
+
return {
|
|
736
|
+
ok: false,
|
|
737
|
+
message:
|
|
738
|
+
`[${name}] the provider process is gone, so the public URL is already dead. ` +
|
|
739
|
+
`Reloading the gate can't bring it back — run \`${bin} tunnel up --name ${name}\`.`,
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
if (!bunAvailable()) {
|
|
743
|
+
return { ok: false, message: `[${name}] bun is not on PATH, so the gate can't be respawned.` };
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Kill ONLY the gate. sweepStrays() is deliberately avoided here: it also
|
|
747
|
+
// matches `--url http://localhost:<port>`, which is the provider process we
|
|
748
|
+
// are going out of our way to keep alive.
|
|
749
|
+
const killed = new Set<number>();
|
|
750
|
+
if (isProcessAlive(state.gate_pid)) {
|
|
751
|
+
try {
|
|
752
|
+
process.kill(state.gate_pid);
|
|
753
|
+
killed.add(state.gate_pid);
|
|
754
|
+
} catch {
|
|
755
|
+
/* race: already gone */
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
killByPattern(`gate\\.ts.*--port ${gatePort}( |$)`, killed);
|
|
759
|
+
|
|
760
|
+
if (!(await waitForPortFree(gatePort, 5_000))) {
|
|
761
|
+
return {
|
|
762
|
+
ok: false,
|
|
763
|
+
message:
|
|
764
|
+
`[${name}] port ${gatePort} never released, so the old gate looks wedged. ` +
|
|
765
|
+
`Tunnel is down; check \`${bin} tunnel status\`.`,
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const cfg = readConfig();
|
|
770
|
+
const gateProc = spawnGate({
|
|
771
|
+
name,
|
|
772
|
+
gatePort,
|
|
773
|
+
target: state.target,
|
|
774
|
+
vhost: state.vhost,
|
|
775
|
+
provider: state.provider,
|
|
776
|
+
allowedIps: cfg.allowed_ips,
|
|
777
|
+
gateLogPath: cachePath("tunnel", gateLogFile(name)),
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
await sleep(800);
|
|
781
|
+
const pid = gateProc.pid;
|
|
782
|
+
if (
|
|
783
|
+
typeof pid !== "number" ||
|
|
784
|
+
!isProcessAlive(pid) ||
|
|
785
|
+
!(await waitForPortBound(gatePort, 5_000))
|
|
786
|
+
) {
|
|
787
|
+
return {
|
|
788
|
+
ok: false,
|
|
789
|
+
message:
|
|
790
|
+
`[${name}] the gate did not come back on port ${gatePort}. ` +
|
|
791
|
+
`Tunnel is down; check \`${bin} tunnel logs --name ${name}\`.`,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
writeState({ ...state, gate_pid: pid });
|
|
796
|
+
const allowNote =
|
|
797
|
+
state.provider === "cloudflare" ? ` Allowlist: ${cfg.allowed_ips.length} IP(s).` : "";
|
|
798
|
+
return { ok: true, message: `[${name}] gate reloaded, URL unchanged: ${state.url}.${allowNote}` };
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
async function reload(opts: ReloadOpts): Promise<void> {
|
|
802
|
+
const bin = resolveBinName();
|
|
803
|
+
|
|
804
|
+
if (opts.all) {
|
|
805
|
+
const all = listStates();
|
|
806
|
+
// Only live tunnels are reloadable, and a box commonly accumulates stale
|
|
807
|
+
// state files from long-dead runs. Reporting those as failures would make
|
|
808
|
+
// the common post-`allow add` path exit non-zero for no real reason.
|
|
809
|
+
const live = all.filter((s) => providerIsAlive(s));
|
|
810
|
+
const stale = all.length - live.length;
|
|
811
|
+
if (live.length === 0) {
|
|
812
|
+
emit.text(
|
|
813
|
+
all.length === 0
|
|
814
|
+
? "No tunnels up. Nothing to reload.\n"
|
|
815
|
+
: `No live tunnels to reload (${all.length} stale). Start one with \`${bin} tunnel up\`.\n`,
|
|
816
|
+
);
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
let failed = 0;
|
|
820
|
+
for (const s of live) {
|
|
821
|
+
const r = await reloadOne(s);
|
|
822
|
+
emit.text(`${r.ok ? "ok " : "FAIL"} ${r.message}\n`);
|
|
823
|
+
if (!r.ok) failed++;
|
|
824
|
+
}
|
|
825
|
+
emit.text(
|
|
826
|
+
`Reloaded ${live.length - failed} of ${live.length} live tunnel(s)` +
|
|
827
|
+
(stale > 0 ? `; skipped ${stale} stale.\n` : ".\n"),
|
|
828
|
+
);
|
|
829
|
+
if (failed > 0) process.exitCode = 1;
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const name = resolveName(opts.name);
|
|
834
|
+
const state = readState(name);
|
|
835
|
+
if (!state) {
|
|
836
|
+
emit.error({
|
|
837
|
+
code: "tunnel_not_found",
|
|
838
|
+
message: `No tunnel state for [${name}]. Start it with \`${bin} tunnel up --name ${name}\`.`,
|
|
839
|
+
});
|
|
840
|
+
process.exit(1);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
const r = await reloadOne(state);
|
|
844
|
+
emit.text(`${r.message}\n`);
|
|
845
|
+
if (!r.ok) process.exit(1);
|
|
846
|
+
}
|
|
847
|
+
|
|
661
848
|
function instanceState(state: TunnelState): "up" | "stale" {
|
|
662
849
|
return tunnelIsAlive(state) ? "up" : "stale";
|
|
663
850
|
}
|
|
@@ -772,6 +959,23 @@ function allowList(): void {
|
|
|
772
959
|
for (const ip of cfg.allowed_ips) emit.text(`${ip}\n`);
|
|
773
960
|
}
|
|
774
961
|
|
|
962
|
+
/**
|
|
963
|
+
* Trailer for `allow add`/`allow rm`. Each gate snapshots the allowlist at
|
|
964
|
+
* spawn, so a config edit alone changes nothing until the gates restart.
|
|
965
|
+
* Only LIVE tunnels are listed — stale state files pile up on a long-running
|
|
966
|
+
* box, and naming nine dead instances made the old hint read like nine
|
|
967
|
+
* restarts were owed.
|
|
968
|
+
*/
|
|
969
|
+
function allowChangedHint(): void {
|
|
970
|
+
const live = listStates().filter((s) => s.provider === "cloudflare" && providerIsAlive(s));
|
|
971
|
+
if (live.length === 0) return;
|
|
972
|
+
emit.text(
|
|
973
|
+
`Each gate reads the allowlist once at start, so this applies on reload ` +
|
|
974
|
+
`(${live.map((s) => s.name).join(", ")}):\n` +
|
|
975
|
+
` ${resolveBinName()} tunnel reload --all\n`,
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
|
|
775
979
|
function allowAdd(ip: string): void {
|
|
776
980
|
const cfg = readConfig();
|
|
777
981
|
if (cfg.allowed_ips.includes(ip)) {
|
|
@@ -781,12 +985,7 @@ function allowAdd(ip: string): void {
|
|
|
781
985
|
cfg.allowed_ips.push(ip);
|
|
782
986
|
writeConfig(cfg);
|
|
783
987
|
emit.text(`Added ${ip}.\n`);
|
|
784
|
-
|
|
785
|
-
if (up.length > 0) {
|
|
786
|
-
emit.text(
|
|
787
|
-
`Cloudflare allowlist is shared across Cloudflare tunnels; restart each to apply (${up.map((s) => s.name).join(", ")}).\n`,
|
|
788
|
-
);
|
|
789
|
-
}
|
|
988
|
+
allowChangedHint();
|
|
790
989
|
}
|
|
791
990
|
|
|
792
991
|
function allowRm(ip: string): void {
|
|
@@ -799,12 +998,7 @@ function allowRm(ip: string): void {
|
|
|
799
998
|
cfg.allowed_ips.splice(idx, 1);
|
|
800
999
|
writeConfig(cfg);
|
|
801
1000
|
emit.text(`Removed ${ip}.\n`);
|
|
802
|
-
|
|
803
|
-
if (up.length > 0) {
|
|
804
|
-
emit.text(
|
|
805
|
-
`Cloudflare allowlist is shared across Cloudflare tunnels; restart each to apply (${up.map((s) => s.name).join(", ")}).\n`,
|
|
806
|
-
);
|
|
807
|
-
}
|
|
1001
|
+
allowChangedHint();
|
|
808
1002
|
}
|
|
809
1003
|
|
|
810
1004
|
let emit: EmitContext;
|
|
@@ -858,6 +1052,15 @@ export function registerTunnelCommand(
|
|
|
858
1052
|
.option("--all", "stop every running tunnel")
|
|
859
1053
|
.action(down);
|
|
860
1054
|
|
|
1055
|
+
cmd
|
|
1056
|
+
.command("reload")
|
|
1057
|
+
.description(
|
|
1058
|
+
"Restart an instance's gate in place to pick up allowlist changes, keeping the public URL",
|
|
1059
|
+
)
|
|
1060
|
+
.option("--name <name>", "instance to reload", DEFAULT_INSTANCE)
|
|
1061
|
+
.option("--all", "reload every live tunnel")
|
|
1062
|
+
.action(reload);
|
|
1063
|
+
|
|
861
1064
|
cmd
|
|
862
1065
|
.command("status")
|
|
863
1066
|
.description("Show tunnel state: a table of all instances, or detail for one via --name")
|
|
@@ -16,7 +16,7 @@ import type { EmitContext } from "../commander.ts";
|
|
|
16
16
|
* harnery repo at the *matching version tag* into a cache dir and install the
|
|
17
17
|
* web app's deps. Subsequent runs reuse the cache.
|
|
18
18
|
*
|
|
19
|
-
* The only thing `web/` pulls from `src/` is `harnery/core/
|
|
19
|
+
* The only thing `web/` pulls from `src/` is `harnery/core/journal` (a single,
|
|
20
20
|
* dependency-free module), so the install is `web/` deps only: no root install,
|
|
21
21
|
* no Playwright browser download.
|
|
22
22
|
*/
|
package/src/commands/web.ts
CHANGED
|
@@ -32,6 +32,41 @@ function webRoot(): string {
|
|
|
32
32
|
return path.resolve(here, "..", "..", "web");
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** Default V8 old-space ceiling (MB) for the dashboard server. */
|
|
36
|
+
const DEFAULT_MAX_OLD_SPACE_MB = 2048;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the V8 old-space ceiling for the dashboard process.
|
|
40
|
+
*
|
|
41
|
+
* Next sizes `--max-old-space-size` to roughly half of system RAM when the
|
|
42
|
+
* flag is absent, which on a large machine hands a long-lived dashboard a
|
|
43
|
+
* ceiling it will never approach — so V8 never feels enough pressure to run a
|
|
44
|
+
* major GC and the process settles at a multi-gigabyte working set made mostly
|
|
45
|
+
* of collectable garbage. Pinning a modest ceiling restores that pressure.
|
|
46
|
+
*
|
|
47
|
+
* Precedence: explicit flag → `HARNERY_WEB_MAX_OLD_SPACE` → the default.
|
|
48
|
+
* `0` (or any non-positive value) opts out and restores Next's own sizing.
|
|
49
|
+
*/
|
|
50
|
+
export function resolveMaxOldSpaceMb(flag?: string): number {
|
|
51
|
+
const raw = flag ?? process.env.HARNERY_WEB_MAX_OLD_SPACE;
|
|
52
|
+
if (raw === undefined || raw === "") return DEFAULT_MAX_OLD_SPACE_MB;
|
|
53
|
+
const n = Number(raw);
|
|
54
|
+
if (!Number.isFinite(n) || n <= 0) return 0;
|
|
55
|
+
return Math.floor(n);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Append the heap ceiling to any inherited `NODE_OPTIONS`. Next only supplies
|
|
60
|
+
* its own `--max-old-space-size` when the flag is absent, so appending here
|
|
61
|
+
* wins without clobbering flags the caller set (source maps, inspector, …).
|
|
62
|
+
*/
|
|
63
|
+
export function nodeOptionsWithHeapCap(mb: number): string | undefined {
|
|
64
|
+
const inherited = process.env.NODE_OPTIONS ?? "";
|
|
65
|
+
if (mb <= 0) return inherited || undefined;
|
|
66
|
+
if (/--max-old-space-size[=\s]/.test(inherited)) return inherited;
|
|
67
|
+
return `${inherited} --max-old-space-size=${mb}`.trim();
|
|
68
|
+
}
|
|
69
|
+
|
|
35
70
|
/**
|
|
36
71
|
* Resolve the web app root. A local `web/` (source checkout / submodule) wins.
|
|
37
72
|
* Otherwise, on an npm install, lazy-fetch it (clone + install) unless the
|
|
@@ -57,6 +92,7 @@ interface UpOpts {
|
|
|
57
92
|
coordRoot?: string;
|
|
58
93
|
prod?: boolean;
|
|
59
94
|
fetch?: boolean;
|
|
95
|
+
maxOldSpace?: string;
|
|
60
96
|
}
|
|
61
97
|
|
|
62
98
|
export function registerWebCommand(program: Command, emit: EmitContext): void {
|
|
@@ -77,6 +113,10 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
|
|
|
77
113
|
)
|
|
78
114
|
.option("--prod", "Use next start instead of next dev (requires prior build)")
|
|
79
115
|
.option("--no-fetch", "Don't auto-fetch the dashboard if it's missing (npm installs)")
|
|
116
|
+
.option(
|
|
117
|
+
"--max-old-space <mb>",
|
|
118
|
+
`V8 old-space ceiling in MB; 0 restores Next's own sizing (default ${DEFAULT_MAX_OLD_SPACE_MB}, env HARNERY_WEB_MAX_OLD_SPACE)`,
|
|
119
|
+
)
|
|
80
120
|
.action(async (opts: UpOpts) => {
|
|
81
121
|
const root = resolveWebRoot(emit, opts.fetch !== false);
|
|
82
122
|
if (!root) {
|
|
@@ -101,8 +141,11 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
|
|
|
101
141
|
}
|
|
102
142
|
}
|
|
103
143
|
|
|
144
|
+
const heapMb = resolveMaxOldSpaceMb(opts.maxOldSpace);
|
|
104
145
|
emit.log(`harn web · http://localhost:${port} (${mode})`, "info");
|
|
146
|
+
emit.log(`files origin · http://harnery-files.localhost:${port}`, "info");
|
|
105
147
|
emit.log(`reading .harnery/ from: ${coordRoot}`, "info");
|
|
148
|
+
if (heapMb > 0) emit.log(`heap ceiling · ${heapMb} MB`, "info");
|
|
106
149
|
|
|
107
150
|
const child = spawn(webRunner(), ["run", mode], {
|
|
108
151
|
cwd: root,
|
|
@@ -110,6 +153,9 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
|
|
|
110
153
|
...process.env,
|
|
111
154
|
HARNERY_COORD_ROOT: coordRoot,
|
|
112
155
|
HARNERY_WEB_PORT: port,
|
|
156
|
+
...(nodeOptionsWithHeapCap(heapMb)
|
|
157
|
+
? { NODE_OPTIONS: nodeOptionsWithHeapCap(heapMb) as string }
|
|
158
|
+
: {}),
|
|
113
159
|
},
|
|
114
160
|
stdio: "inherit",
|
|
115
161
|
});
|
|
@@ -154,7 +200,11 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
|
|
|
154
200
|
.option("-p, --port <port>", "Listen port", "9000")
|
|
155
201
|
.option("--coord-root <dir>", "Override the coord root")
|
|
156
202
|
.option("--no-fetch", "Don't auto-fetch the dashboard if it's missing (npm installs)")
|
|
157
|
-
.
|
|
203
|
+
.option(
|
|
204
|
+
"--max-old-space <mb>",
|
|
205
|
+
`V8 old-space ceiling in MB; 0 restores Next's own sizing (default ${DEFAULT_MAX_OLD_SPACE_MB}, env HARNERY_WEB_MAX_OLD_SPACE)`,
|
|
206
|
+
)
|
|
207
|
+
.action((opts: { port: string; coordRoot?: string; fetch?: boolean; maxOldSpace?: string }) => {
|
|
158
208
|
const root = resolveWebRoot(emit, opts.fetch !== false);
|
|
159
209
|
if (!root) {
|
|
160
210
|
process.exitCode = 1;
|
|
@@ -172,8 +222,10 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
|
|
|
172
222
|
}
|
|
173
223
|
const coordRoot = opts.coordRoot ?? process.cwd();
|
|
174
224
|
const port = String(opts.port);
|
|
225
|
+
const heapMb = resolveMaxOldSpaceMb(opts.maxOldSpace);
|
|
175
226
|
emit.log(`harn web · http://localhost:${port} (start)`, "info");
|
|
176
227
|
emit.log(`reading .harnery/ from: ${coordRoot}`, "info");
|
|
228
|
+
if (heapMb > 0) emit.log(`heap ceiling · ${heapMb} MB`, "info");
|
|
177
229
|
|
|
178
230
|
const child = spawn(webRunner(), ["run", "start"], {
|
|
179
231
|
cwd: root,
|
|
@@ -181,6 +233,9 @@ export function registerWebCommand(program: Command, emit: EmitContext): void {
|
|
|
181
233
|
...process.env,
|
|
182
234
|
HARNERY_COORD_ROOT: coordRoot,
|
|
183
235
|
HARNERY_WEB_PORT: port,
|
|
236
|
+
...(nodeOptionsWithHeapCap(heapMb)
|
|
237
|
+
? { NODE_OPTIONS: nodeOptionsWithHeapCap(heapMb) as string }
|
|
238
|
+
: {}),
|
|
184
239
|
},
|
|
185
240
|
stdio: "inherit",
|
|
186
241
|
});
|
package/src/commands/work.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import type { Command } from "commander";
|
|
3
3
|
import type { EmitContext } from "../commander.ts";
|
|
4
|
-
import { workflowSubscriptionOnly } from "../core/config.ts";
|
|
5
4
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
adapterProofInputs,
|
|
6
|
+
createBuiltinAdapterRegistry,
|
|
8
7
|
probeBinaryVersion,
|
|
9
|
-
} from "../core/
|
|
8
|
+
} from "../core/adapters/index.ts";
|
|
9
|
+
import { workflowSubscriptionOnly } from "../core/config.ts";
|
|
10
|
+
import { findCompletedMissionGoverning, reopenGovernorMission } from "../core/governor/index.ts";
|
|
10
11
|
import { findCoordRoot } from "../core/hooks/resolve/coord-root.ts";
|
|
11
12
|
import type { PolicyIsolation } from "../core/policy/index.ts";
|
|
12
13
|
import { loadPolicyFile } from "../core/policy/index.ts";
|
|
@@ -30,6 +31,7 @@ interface WorkCreateOpts {
|
|
|
30
31
|
accept?: string[];
|
|
31
32
|
dependsOn?: string;
|
|
32
33
|
maxAttempts?: string;
|
|
34
|
+
maxUnchargedAttempts?: string;
|
|
33
35
|
sourceKind?: "human" | "workflow" | "external";
|
|
34
36
|
sourceRef?: string;
|
|
35
37
|
actor?: string;
|
|
@@ -37,7 +39,7 @@ interface WorkCreateOpts {
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
interface WorkRunOpts {
|
|
40
|
-
|
|
42
|
+
adapter?: string;
|
|
41
43
|
maxAgents?: string;
|
|
42
44
|
concurrency?: string;
|
|
43
45
|
cwd?: string;
|
|
@@ -59,11 +61,11 @@ interface GovernanceOpts {
|
|
|
59
61
|
dispose?: string[];
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
const
|
|
64
|
+
const MAX_ATTEMPT_TRANSCRIPT_ERROR_RENDER = 240;
|
|
63
65
|
|
|
64
66
|
export function registerWorkCommand(program: Command, emit: EmitContext): void {
|
|
65
|
-
const registry =
|
|
66
|
-
const
|
|
67
|
+
const registry = createBuiltinAdapterRegistry();
|
|
68
|
+
const adapters = registry.ids();
|
|
67
69
|
const work = program
|
|
68
70
|
.command("work")
|
|
69
71
|
.description("Track durable objectives across bounded workflow attempts.");
|
|
@@ -75,7 +77,11 @@ export function registerWorkCommand(program: Command, emit: EmitContext): void {
|
|
|
75
77
|
.option("--id <work-id>", "Stable work id (generated by default)")
|
|
76
78
|
.option("--accept <text>", "Acceptance statement; repeat for more than one", collect, [])
|
|
77
79
|
.option("--depends-on <ids>", "Comma-separated prerequisite work ids")
|
|
78
|
-
.option("--max-attempts <n>", "
|
|
80
|
+
.option("--max-attempts <n>", "Charged-attempt ceiling (default 3)")
|
|
81
|
+
.option(
|
|
82
|
+
"--max-uncharged-attempts <n>",
|
|
83
|
+
"Ceiling on consecutive uncharged upstream attempts (default 3)",
|
|
84
|
+
)
|
|
79
85
|
.option("--source-kind <kind>", "human | workflow | external", "human")
|
|
80
86
|
.option("--source-ref <ref>", "Bounded source reference")
|
|
81
87
|
.option("--actor <name>", "Actor recorded in the creation receipt")
|
|
@@ -91,6 +97,9 @@ export function registerWorkCommand(program: Command, emit: EmitContext): void {
|
|
|
91
97
|
acceptance: opts.accept,
|
|
92
98
|
dependencies: splitIds(opts.dependsOn),
|
|
93
99
|
maxAttempts: opts.maxAttempts ? Number.parseInt(opts.maxAttempts, 10) : undefined,
|
|
100
|
+
maxUnchargedAttempts: opts.maxUnchargedAttempts
|
|
101
|
+
? Number.parseInt(opts.maxUnchargedAttempts, 10)
|
|
102
|
+
: undefined,
|
|
94
103
|
source: opts.sourceKind ? { kind: opts.sourceKind, ref: opts.sourceRef } : undefined,
|
|
95
104
|
actor: opts.actor,
|
|
96
105
|
});
|
|
@@ -150,8 +159,8 @@ export function registerWorkCommand(program: Command, emit: EmitContext): void {
|
|
|
150
159
|
});
|
|
151
160
|
});
|
|
152
161
|
|
|
153
|
-
registerRunCommand(work, "run", false, registry,
|
|
154
|
-
registerRunCommand(work, "retry", true, registry,
|
|
162
|
+
registerRunCommand(work, "run", false, registry, adapters, emit);
|
|
163
|
+
registerRunCommand(work, "retry", true, registry, adapters, emit);
|
|
155
164
|
registerGovernanceCommand(work, "accept", emit);
|
|
156
165
|
registerGovernanceCommand(work, "cancel", emit);
|
|
157
166
|
registerGovernanceCommand(work, "reopen", emit);
|
|
@@ -161,8 +170,8 @@ function registerRunCommand(
|
|
|
161
170
|
work: Command,
|
|
162
171
|
name: "run" | "retry",
|
|
163
172
|
retry: boolean,
|
|
164
|
-
registry: ReturnType<typeof
|
|
165
|
-
|
|
173
|
+
registry: ReturnType<typeof createBuiltinAdapterRegistry>,
|
|
174
|
+
adapters: string[],
|
|
166
175
|
emit: EmitContext,
|
|
167
176
|
): void {
|
|
168
177
|
work
|
|
@@ -175,8 +184,8 @@ function registerRunCommand(
|
|
|
175
184
|
.option("--max-agents <n>", "Total-agent ceiling for the workflow")
|
|
176
185
|
.option("--concurrency <n>", "Concurrent-subagent cap")
|
|
177
186
|
.option("--cwd <dir>", "Working directory children spawn in")
|
|
178
|
-
.option("--
|
|
179
|
-
.option("--subscription-only", "Require stored
|
|
187
|
+
.option("--adapter <name>", `Default adapter: ${adapters.join(" | ")}`)
|
|
188
|
+
.option("--subscription-only", "Require stored adapter-login billing")
|
|
180
189
|
.option("--allow-api-billing", "Permit API-key override billing")
|
|
181
190
|
.option("--policy <file>", "Host policy JSON/JSONC")
|
|
182
191
|
.option("--isolation <mode>", "shared | worktree | sandbox | remote")
|
|
@@ -186,8 +195,8 @@ function registerRunCommand(
|
|
|
186
195
|
.option("--json", "Emit the workflow report or parked result as JSON")
|
|
187
196
|
.action(async (workId: string, opts: WorkRunOpts) => {
|
|
188
197
|
await withWorkRootAsync(emit, async (coordRoot) => {
|
|
189
|
-
if (opts.
|
|
190
|
-
throw new Error(`unknown
|
|
198
|
+
if (opts.adapter && !registry.get(opts.adapter)) {
|
|
199
|
+
throw new Error(`unknown adapter ${JSON.stringify(opts.adapter)}`);
|
|
191
200
|
}
|
|
192
201
|
if (opts.workspaceRoot && opts.isolation !== "worktree") {
|
|
193
202
|
throw new Error("--workspace-root requires --isolation worktree");
|
|
@@ -211,14 +220,14 @@ function registerRunCommand(
|
|
|
211
220
|
actor: opts.actor,
|
|
212
221
|
engine: {
|
|
213
222
|
spawners: registry.spawners(),
|
|
214
|
-
|
|
223
|
+
defaultAdapter: opts.adapter,
|
|
215
224
|
maxAgents: opts.maxAgents ? Number.parseInt(opts.maxAgents, 10) : undefined,
|
|
216
225
|
concurrency: opts.concurrency ? Number.parseInt(opts.concurrency, 10) : undefined,
|
|
217
226
|
cwd: opts.cwd,
|
|
218
227
|
subscriptionOnly:
|
|
219
228
|
opts.subscriptionOnly === true ? true : workflowSubscriptionOnly(coordRoot),
|
|
220
229
|
allowApiBilling: opts.allowApiBilling,
|
|
221
|
-
...
|
|
230
|
+
...adapterProofInputs(
|
|
222
231
|
registry.list().map((adapter) => adapter.profile),
|
|
223
232
|
{ versionProbe: probeBinaryVersion },
|
|
224
233
|
),
|
|
@@ -258,7 +267,7 @@ function registerRunCommand(
|
|
|
258
267
|
workId,
|
|
259
268
|
runId: error.runId,
|
|
260
269
|
approvalId: error.approvalId,
|
|
261
|
-
|
|
270
|
+
transcriptPath: error.transcriptPath,
|
|
262
271
|
});
|
|
263
272
|
} else {
|
|
264
273
|
emit.text(
|
|
@@ -312,16 +321,30 @@ function registerGovernanceCommand(
|
|
|
312
321
|
withWorkRoot(emit, (coordRoot) => {
|
|
313
322
|
const fn =
|
|
314
323
|
name === "accept" ? acceptWorkItem : name === "cancel" ? cancelWorkItem : reopenWorkItem;
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
324
|
+
// ADR 0050: a reopen under a mission that already succeeded has to reopen the
|
|
325
|
+
// mission too, or the item lands in ready_work that the governor will never
|
|
326
|
+
// dispatch. Resolve the goal BEFORE touching the work item so a refusal leaves
|
|
327
|
+
// nothing half-done.
|
|
328
|
+
const goalId =
|
|
329
|
+
name === "reopen" ? findCompletedMissionGoverning(coordRoot, workId) : undefined;
|
|
330
|
+
const record = fn(coordRoot, workId, {
|
|
331
|
+
...opts,
|
|
332
|
+
...(opts.finding?.length ? { findings: opts.finding } : {}),
|
|
333
|
+
...(opts.dispose?.length ? { dispositions: opts.dispose.map(parseDisposition) } : {}),
|
|
334
|
+
});
|
|
335
|
+
if (goalId) {
|
|
336
|
+
reopenGovernorMission({
|
|
337
|
+
coordRoot,
|
|
338
|
+
goalId,
|
|
339
|
+
actor: opts.actor,
|
|
340
|
+
reason: opts.reason?.trim() || `work ${workId} was reopened after mission completion`,
|
|
341
|
+
});
|
|
342
|
+
emit.log(
|
|
343
|
+
`mission ${goalId} had completed; its completion was reopened so ${workId} can be dispatched`,
|
|
344
|
+
"warn",
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
emitWork(record, opts.json, emit, true);
|
|
325
348
|
});
|
|
326
349
|
});
|
|
327
350
|
}
|
|
@@ -374,7 +397,7 @@ function emitWork(
|
|
|
374
397
|
`objective: ${record.intent.objective}`,
|
|
375
398
|
`reason: ${projection.reason}`,
|
|
376
399
|
`next: ${projection.next_action}`,
|
|
377
|
-
`attempts: ${
|
|
400
|
+
`attempts: ${renderAttemptBudget(record)}`,
|
|
378
401
|
];
|
|
379
402
|
if (record.intent.dependencies.length)
|
|
380
403
|
lines.push(`depends on: ${record.intent.dependencies.join(", ")}`);
|
|
@@ -391,22 +414,33 @@ function emitWork(
|
|
|
391
414
|
|
|
392
415
|
export function renderAttemptRow(attempt: WorkAttempt): string {
|
|
393
416
|
const statusDetail =
|
|
394
|
-
attempt.status === "
|
|
395
|
-
? `: ${
|
|
417
|
+
attempt.status === "transcript_unreadable"
|
|
418
|
+
? `: ${renderAttemptTranscriptError(attempt.transcript_error)}`
|
|
396
419
|
: "";
|
|
397
420
|
return ` ${attempt.number}. ${attempt.run_id} ${attempt.status}${statusDetail}`;
|
|
398
421
|
}
|
|
399
422
|
|
|
400
|
-
function
|
|
423
|
+
function renderAttemptTranscriptError(error: string | undefined): string {
|
|
401
424
|
const normalized = (error ?? "").replace(/\s+/g, " ").trim();
|
|
402
|
-
const value = normalized || "unknown
|
|
403
|
-
if (value.length <=
|
|
404
|
-
return `${value.slice(0,
|
|
425
|
+
const value = normalized || "unknown transcript read error";
|
|
426
|
+
if (value.length <= MAX_ATTEMPT_TRANSCRIPT_ERROR_RENDER) return value;
|
|
427
|
+
return `${value.slice(0, MAX_ATTEMPT_TRANSCRIPT_ERROR_RENDER - 3).trimEnd()}...`;
|
|
405
428
|
}
|
|
406
429
|
|
|
407
430
|
function renderWorkRow(record: WorkRecord): string {
|
|
408
431
|
const projection = record.projection;
|
|
409
|
-
return `${record.intent.id} ${projection.state.padEnd(17)} ${
|
|
432
|
+
return `${record.intent.id} ${projection.state.padEnd(17)} ${renderAttemptBudget(record).padEnd(18)} ${projection.next_action.padEnd(21)} ${record.intent.title}`;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// `max_attempts` budgets CHARGED attempts (ADR 0046), so the budget is
|
|
436
|
+
// charged/max, not the raw attempt count. When uncharged environment/upstream
|
|
437
|
+
// attempts happened, surface them as a suffix rather than folding them into the
|
|
438
|
+
// numerator, where they would read as spent budget the item still has.
|
|
439
|
+
export function renderAttemptBudget(record: WorkRecord): string {
|
|
440
|
+
const { charged_attempts, attempts_used } = record.projection;
|
|
441
|
+
const uncharged = attempts_used - charged_attempts;
|
|
442
|
+
const base = `${charged_attempts}/${record.intent.max_attempts}`;
|
|
443
|
+
return uncharged > 0 ? `${base} (+${uncharged} uncharged)` : base;
|
|
410
444
|
}
|
|
411
445
|
|
|
412
446
|
function withWorkRoot(emit: EmitContext, fn: (coordRoot: string) => void): void {
|