@iowarp/clio-coder 0.3.6 → 0.3.7
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/CHANGELOG.md +28 -0
- package/README.md +16 -5
- package/dist/{acp-2BEHC4DL.js → acp-SK4MD6MM.js} +10 -10
- package/dist/{agents-LNNFTM53.js → agents-2FN2K6ME.js} +30 -25
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-KXXFI2VS.js → auth-QIYZWM5I.js} +13 -13
- package/dist/{chunk-KHSFENX2.js → chunk-3HAPLH5M.js} +10 -10
- package/dist/{chunk-24I7BN55.js → chunk-465YSENW.js} +2 -2
- package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
- package/dist/{chunk-CYQKWTG3.js → chunk-4DWFMQDR.js} +4 -4
- package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
- package/dist/{chunk-22NAGB7X.js → chunk-5C77SEEY.js} +5 -94
- package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
- package/dist/{chunk-K7T3E2SR.js → chunk-5UJ6ECTS.js} +10 -9
- package/dist/{chunk-6US73PDB.js → chunk-6M7VS3J3.js} +5 -5
- package/dist/{chunk-CJUB2JJ2.js → chunk-6TUKSZVF.js} +5 -5
- package/dist/{chunk-5JGRAMKL.js → chunk-AB4XIIVB.js} +8 -6
- package/dist/{chunk-R46L2BIR.js → chunk-BMWK7ZIZ.js} +14 -20
- package/dist/{chunk-4BPJXDWC.js → chunk-C4JBQ5SR.js} +30 -14
- package/dist/{chunk-XXQNGV4M.js → chunk-CEYBNUGC.js} +243 -63
- package/dist/{chunk-VEZEGCGW.js → chunk-D4MDIG46.js} +20 -18
- package/dist/chunk-DJNLUABN.js +843 -0
- package/dist/{chunk-RY3LY4J5.js → chunk-DMD2AGVS.js} +5 -4
- package/dist/{chunk-KOHPCX4K.js → chunk-DOOEX22V.js} +2 -2
- package/dist/chunk-DQA7QLMD.js +123 -0
- package/dist/chunk-DR52UMZW.js +21 -0
- package/dist/{chunk-XF5N4U5A.js → chunk-EBEFWSGL.js} +6 -5
- package/dist/{chunk-EYPA3EGJ.js → chunk-EELBMBT6.js} +120 -13
- package/dist/{chunk-CKXWIANG.js → chunk-EOOQZZDE.js} +16 -14
- package/dist/{chunk-WR67VIZY.js → chunk-FOT2FX5J.js} +63 -5
- package/dist/{chunk-FYYLNIL5.js → chunk-GH5622CP.js} +2 -2
- package/dist/chunk-GWS3VEIW.js +195 -0
- package/dist/{chunk-LYF7OHWH.js → chunk-J7PIKKWC.js} +8 -463
- package/dist/{chunk-NILBFAPG.js → chunk-JNXPYBB4.js} +2 -2
- package/dist/{chunk-4VP4KH3K.js → chunk-JRIO5UD2.js} +4 -4
- package/dist/{chunk-6XXKFVSN.js → chunk-JTSEDYVQ.js} +7 -7
- package/dist/{chunk-QKMUKYO7.js → chunk-KCMKRQX4.js} +236 -84
- package/dist/chunk-KZ2H5X4G.js +1026 -0
- package/dist/{chunk-QNQHSOLF.js → chunk-LADCF22A.js} +12 -12
- package/dist/chunk-M4AKACEO.js +382 -0
- package/dist/{chunk-XYDYPRZI.js → chunk-MXI6J5JF.js} +7 -7
- package/dist/{chunk-G7MUEIGA.js → chunk-OB5HIGJY.js} +1 -1
- package/dist/{chunk-EKY57CSP.js → chunk-OBMAI2DP.js} +61 -767
- package/dist/chunk-PD3MESLB.js +242 -0
- package/dist/{chunk-ZRGEBJ4T.js → chunk-QCTRSGHQ.js} +21 -21
- package/dist/chunk-RVG5JXAL.js +41 -0
- package/dist/{chunk-RD5U66HV.js → chunk-SROCI7ZU.js} +7 -7
- package/dist/{chunk-MFFY33HR.js → chunk-THKY7CD7.js} +466 -205
- package/dist/{chunk-PCZJO5TI.js → chunk-UFQ3F4FW.js} +13 -178
- package/dist/{chunk-AD2SYQYC.js → chunk-UHXRNZ2J.js} +121 -3
- package/dist/chunk-UND3GU2L.js +103 -0
- package/dist/{chunk-QM3F2GKX.js → chunk-UUANF5CR.js} +2247 -2096
- package/dist/chunk-UVDSQ6LW.js +472 -0
- package/dist/{chunk-DJVECN66.js → chunk-VQNODYQ4.js} +14 -14
- package/dist/{chunk-3BPUFZDL.js → chunk-VREKEFLL.js} +3 -3
- package/dist/{chunk-PBTHKCPN.js → chunk-WJHBC77E.js} +6 -6
- package/dist/{chunk-XE2VEJHX.js → chunk-X2KV5FXT.js} +2 -2
- package/dist/{chunk-ZXF4XRKW.js → chunk-XEGB6BCN.js} +157 -7
- package/dist/{chunk-E25LMLRW.js → chunk-YD734TPH.js} +2 -2
- package/dist/{verifiers-NCBTHHN2.js → chunk-YTYFXUI3.js} +65 -322
- package/dist/{chunk-OH3TOQTB.js → chunk-ZGH7FGS5.js} +13 -7
- package/dist/cli/index.js +32 -30
- package/dist/{clio-M2KGYUFZ.js → clio-WBVQEBKO.js} +7 -7
- package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +3 -3
- package/dist/{components-5TTYYX6G.js → components-F7OEATSO.js} +4 -4
- package/dist/{config-XUUYQIWO.js → config-TRBL3RCF.js} +34 -29
- package/dist/{configure-IHJ7YOMV.js → configure-OLCVPHNM.js} +15 -15
- package/dist/{context-74JLXAWD.js → context-MJIJ6GOX.js} +11 -11
- package/dist/{context-ZQ7SIFJV.js → context-WFPKQSM6.js} +19 -3
- package/dist/{context-75MIWW3U.js → context-XEWE3MOJ.js} +31 -26
- package/dist/{context-clear-GYKWNUML.js → context-clear-KNOS2JPB.js} +31 -26
- package/dist/{context-working-set-UX5KEP4J.js → context-working-set-EUXAZI6N.js} +8 -8
- package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-B7MTOVKL.js} +313 -53
- package/dist/{docs-6FZSCG5B.js → docs-FLJTIDSE.js} +4 -4
- package/dist/{doctor-SVJ5BZCW.js → doctor-RN4YKO2X.js} +14 -14
- package/dist/{eval-CG6LLBLD.js → eval-RUBJVSNQ.js} +8 -7
- package/dist/{evidence-ZYFIEN42.js → evidence-JZNBUOQZ.js} +30 -25
- package/dist/{evolve-QGEXEMDW.js → evolve-FJVC4KKI.js} +30 -25
- package/dist/{extensions-ADGNCJJD.js → extensions-IQL36S7K.js} +4 -4
- package/dist/{fleet-S5R4ZOQY.js → fleet-BDKYJFCP.js} +214 -360
- package/dist/fleet-commands-ZFIWZSB3.js +70 -0
- package/dist/fleet-graph-Y6HPXIVF.js +125 -0
- package/dist/fleet-new-RDVJLHHH.js +48 -0
- package/dist/fleet-validate-BIYREGIK.js +79 -0
- package/dist/{init-5DRU55YR.js → init-LQUB5COQ.js} +44 -37
- package/dist/library-NJAHIGG4.js +217 -0
- package/dist/{memory-7YKKR6UC.js → memory-OG6HOYKM.js} +31 -26
- package/dist/{models-ZPOLRU2C.js → models-5ZG5XY7J.js} +21 -20
- package/dist/{monitor-US5F5YGZ.js → monitor-TJ7AMTGB.js} +49 -30
- package/dist/{orchestrator-E2AL4T5N.js → orchestrator-WZYB54DM.js} +3827 -581
- package/dist/{paths-E7KYAQWE.js → paths-XUC7GS6E.js} +4 -4
- package/dist/{reset-KZ652EK6.js → reset-PXQT45IY.js} +7 -7
- package/dist/{run-SRNBKDWD.js → run-FQ74YF62.js} +53 -45
- package/dist/{share-CGZE33UP.js → share-FW7SVCL3.js} +33 -9
- package/dist/{skills-S2X4DLY5.js → skills-7E7IRB3R.js} +24 -8
- package/dist/{skills-eval-W2GGIC4R.js → skills-eval-LI75W6OK.js} +34 -27
- package/dist/{targets-54SWINWB.js → targets-4CIFKCTW.js} +23 -22
- package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WUZY7ZV5.js} +4 -4
- package/dist/{uninstall-BVLWXKBT.js → uninstall-7FV7IP4E.js} +4 -4
- package/dist/{upgrade-JKAR27XC.js → upgrade-K2HVIVMQ.js} +20 -19
- package/dist/{usage-MSAWCLX4.js → usage-GTZELZQX.js} +116 -49
- package/dist/verifiers-RLAHT27O.js +336 -0
- package/dist/{verify-X5HDROLA.js → verify-BX3BRKH5.js} +7 -6
- package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-ASIFASCN.js} +45 -37
- package/dist/worker/entry.js +38 -35
- package/docs/README.md +3 -2
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +10 -6
- package/docs/built-in-agents.md +26 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +82 -2
- package/docs/configuration-and-targets.md +79 -2
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +1 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +3 -3
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +5 -5
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +4 -1
- package/docs/extensions-and-sharing.md +6 -2
- package/docs/fleet-demo-runbook.md +2 -2
- package/docs/fleet-dispatch.md +197 -10
- package/docs/git-commit-provenance.md +2 -2
- package/docs/glossary.md +1 -1
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +2 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +55 -8
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +1 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +79 -64
- package/docs/resource-library.md +59 -0
- package/docs/safety-model.md +2 -2
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +37 -1
- package/docs/skills-marketplace.md +16 -3
- package/docs/tool-usage.md +14 -7
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +1 -1
- package/src/cli/fleet-commands.ts +37 -0
- package/src/cli/fleet-graph.ts +102 -0
- package/src/cli/fleet-new.ts +36 -0
- package/src/cli/fleet-preflight.ts +121 -0
- package/src/cli/fleet-validate.ts +30 -0
- package/src/cli/fleet.ts +173 -335
- package/src/cli/index.ts +3 -1
- package/src/cli/library.ts +190 -0
- package/src/cli/share.ts +13 -1
- package/src/cli/usage.ts +111 -19
- package/src/core/bus-events.ts +4 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +130 -0
- package/src/core/defaults.ts +81 -0
- package/src/domains/agents/builtins/architect.md +1 -0
- package/src/domains/agents/builtins/oracle.md +33 -0
- package/src/domains/agents/catalog.ts +13 -1
- package/src/domains/agents/fleet-contract.ts +278 -16
- package/src/domains/agents/index.ts +14 -0
- package/src/domains/agents/result-contract.ts +235 -1
- package/src/domains/config/classify.ts +4 -0
- package/src/domains/dispatch/active-route-planner.ts +14 -0
- package/src/domains/dispatch/backoff.ts +2 -1
- package/src/domains/dispatch/capability-match.ts +1 -0
- package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
- package/src/domains/dispatch/contract.ts +34 -0
- package/src/domains/dispatch/delegation-plan.ts +167 -0
- package/src/domains/dispatch/execution-plan.ts +76 -5
- package/src/domains/dispatch/execution-role.ts +3 -1
- package/src/domains/dispatch/execution-scheduler.ts +183 -67
- package/src/domains/dispatch/extension.ts +258 -9
- package/src/domains/dispatch/fleet-gate.ts +14 -0
- package/src/domains/dispatch/fleet-plan.ts +63 -3
- package/src/domains/dispatch/fleet-run.ts +737 -0
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/host-verification.ts +178 -0
- package/src/domains/dispatch/index.ts +38 -0
- package/src/domains/dispatch/intent.ts +159 -0
- package/src/domains/dispatch/receipt-integrity.ts +8 -4
- package/src/domains/dispatch/state.ts +36 -3
- package/src/domains/dispatch/types.ts +51 -6
- package/src/domains/dispatch/validation.ts +66 -6
- package/src/domains/evidence/trust-status.ts +10 -1
- package/src/domains/middleware/index.ts +15 -0
- package/src/domains/middleware/watchdog.ts +281 -0
- package/src/domains/observability/contract.ts +3 -1
- package/src/domains/observability/cost.ts +12 -1
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/observability/index.ts +10 -0
- package/src/domains/observability/out-of-turn-usage.ts +223 -0
- package/src/domains/resources/index.ts +20 -0
- package/src/domains/resources/library.ts +326 -0
- package/src/domains/resources/skills/marketplace.ts +37 -12
- package/src/domains/session/handoff.ts +629 -0
- package/src/domains/share/archive.ts +67 -2
- package/src/entry/orchestrator.ts +37 -0
- package/src/interactive/bus-notices.ts +26 -0
- package/src/interactive/chat-loop.ts +235 -1
- package/src/interactive/chat-renderer.ts +22 -0
- package/src/interactive/cost-overlay.ts +31 -3
- package/src/interactive/council-dispatch.ts +30 -0
- package/src/interactive/council-grid.ts +213 -0
- package/src/interactive/council.ts +99 -0
- package/src/interactive/dispatch-board.ts +260 -16
- package/src/interactive/fleet-run-preview.ts +307 -0
- package/src/interactive/footer/notifications.ts +219 -0
- package/src/interactive/handoff-round.ts +56 -0
- package/src/interactive/interactive-application.ts +43 -1
- package/src/interactive/interactive-event-projection.ts +9 -1
- package/src/interactive/interactive-slash-runtime.ts +52 -2
- package/src/interactive/interactive-subscriptions.ts +14 -2
- package/src/interactive/oracle.ts +179 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +6 -0
- package/src/interactive/overlay-general-openers.ts +190 -1
- package/src/interactive/overlay-key-routing.ts +17 -1
- package/src/interactive/overlay-lifecycle.ts +41 -1
- package/src/interactive/overlay-permission-lifecycle.ts +10 -0
- package/src/interactive/overlay-resource-openers.ts +11 -3
- package/src/interactive/overlay-session-lifecycle.ts +234 -2
- package/src/interactive/overlays/fleet-run-approval.ts +208 -0
- package/src/interactive/overlays/handoff-review.ts +185 -0
- package/src/interactive/overlays/library-install-confirm.ts +151 -0
- package/src/interactive/overlays/list-overlay.ts +168 -2
- package/src/interactive/overlays/settings.ts +101 -4
- package/src/interactive/overlays/side-question.ts +139 -0
- package/src/interactive/overlays/skills-hub.ts +401 -15
- package/src/interactive/side-question.ts +171 -0
- package/src/interactive/slash-commands.ts +432 -5
- package/src/interactive/slash-spec.ts +19 -6
- package/src/interactive/theme/tokens.ts +30 -0
- package/src/interactive/turn-middleware.ts +15 -1
- package/src/interactive/watchdog-run.ts +75 -0
- package/src/interactive/worker-share.ts +56 -1
- package/src/interactive/worker-stream.ts +7 -0
- package/src/tools/bootstrap.ts +3 -0
- package/src/tools/compete-worktrees.ts +13 -79
- package/src/tools/dispatch-admission.ts +242 -8
- package/src/tools/dispatch-arguments.ts +57 -1
- package/src/tools/dispatch-plan.ts +136 -6
- package/src/tools/dispatch-runner.ts +319 -13
- package/src/tools/dispatch-types.ts +20 -1
- package/src/tools/dispatch.ts +72 -2
- package/src/tools/monitor.ts +16 -0
- package/src/tools/profiles.ts +18 -4
- package/src/tools/task-worktree.ts +238 -0
- package/src/tools/verify/authoring.ts +61 -1
- package/src/tools/verify/scripts.ts +62 -0
- package/src/tools/worker-evidence.ts +2 -1
- package/src/worker/spec-contract.ts +1 -0
- package/dist/chunk-HC4CLZ2Y.js +0 -68
package/src/cli/fleet.ts
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* `clio-coder fleet` operator surface.
|
|
3
3
|
*
|
|
4
4
|
* clio-coder fleet list enumerate .clio-coder/fleets/*.md with validity
|
|
5
|
+
* clio-coder fleet new <name> --from <name> copy a shipped contract into the project
|
|
6
|
+
* clio-coder fleet validate|graph <name> inspect a contract without executing it
|
|
7
|
+
* clio-coder fleet commands init draft a repository command registry
|
|
5
8
|
* clio-coder fleet run <name> --var k=v ... preflight + execute a fleet contract
|
|
6
9
|
* clio-coder fleet status [--json] runtime snapshot from the durable ledger
|
|
7
10
|
* clio-coder fleet drain|resume [--json] close or reopen durable dispatch admission
|
|
@@ -15,7 +18,7 @@
|
|
|
15
18
|
import { randomBytes } from "node:crypto";
|
|
16
19
|
import { readFileSync } from "node:fs";
|
|
17
20
|
import { join } from "node:path";
|
|
18
|
-
import {
|
|
21
|
+
import { type ClioSettings, readSettings } from "../core/config.js";
|
|
19
22
|
import { loadDomains } from "../core/domain-loader.js";
|
|
20
23
|
import { clioStateDir } from "../core/xdg.js";
|
|
21
24
|
import type { AgentsContract } from "../domains/agents/contract.js";
|
|
@@ -32,7 +35,6 @@ import {
|
|
|
32
35
|
loadFleetCommands,
|
|
33
36
|
loadFleetContract,
|
|
34
37
|
renderFleetPrompt,
|
|
35
|
-
resultContractAuthorship,
|
|
36
38
|
} from "../domains/agents/index.js";
|
|
37
39
|
import type { ConfigContract } from "../domains/config/contract.js";
|
|
38
40
|
import { ConfigDomainModule } from "../domains/config/index.js";
|
|
@@ -45,35 +47,22 @@ import {
|
|
|
45
47
|
type RunToolBudgetEnvelope,
|
|
46
48
|
} from "../domains/dispatch/budget-envelope.js";
|
|
47
49
|
import { type CapacityDrain, capacityDrain, setCapacityDraining } from "../domains/dispatch/capacity-lease.js";
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import
|
|
51
|
-
import {
|
|
52
|
-
type ExecutionPlan,
|
|
53
|
-
type ExecutionPlanCodeStep,
|
|
54
|
-
executionPlanAncestors,
|
|
55
|
-
} from "../domains/dispatch/execution-plan.js";
|
|
56
|
-
import {
|
|
57
|
-
agentRoleFactsResolver,
|
|
58
|
-
gateRouteCorrelation,
|
|
59
|
-
requestExecutionRole,
|
|
60
|
-
withAttemptRole,
|
|
61
|
-
} from "../domains/dispatch/execution-role.js";
|
|
62
|
-
import { type ExecutionPlanResult, executePlan } from "../domains/dispatch/execution-scheduler.js";
|
|
63
|
-
import { deriveFleetCommitAttribution } from "../domains/dispatch/fleet-commit-attribution.js";
|
|
50
|
+
import type { DispatchContract } from "../domains/dispatch/contract.js";
|
|
51
|
+
import type { ExecutionPlan } from "../domains/dispatch/execution-plan.js";
|
|
52
|
+
import { agentRoleFactsResolver, requestExecutionRole, withAttemptRole } from "../domains/dispatch/execution-role.js";
|
|
64
53
|
import { compileFleetExecutionPlan } from "../domains/dispatch/fleet-plan.js";
|
|
65
54
|
import {
|
|
66
|
-
|
|
67
|
-
type
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
55
|
+
DispatchDomainModule,
|
|
56
|
+
type ExecuteFleetRunInput,
|
|
57
|
+
executeFleetRun,
|
|
58
|
+
type FleetRunOutcome,
|
|
59
|
+
planFleetResume,
|
|
60
|
+
readFleetRun,
|
|
61
|
+
} from "../domains/dispatch/index.js";
|
|
73
62
|
import { openLedger } from "../domains/dispatch/state.js";
|
|
74
63
|
import type { RunEnvelope, RunReceipt } from "../domains/dispatch/types.js";
|
|
75
64
|
import { WRITE_BOUNDARY_VIOLATION_REASON } from "../domains/dispatch/write-boundary.js";
|
|
76
|
-
import {
|
|
65
|
+
import { preflightWriteBoundaries } from "../domains/dispatch/write-boundary-enforcer.js";
|
|
77
66
|
import { ensureClioState, LifecycleDomainModule } from "../domains/lifecycle/index.js";
|
|
78
67
|
import { MiddlewareDomainModule } from "../domains/middleware/index.js";
|
|
79
68
|
import { ObservabilityDomainModule } from "../domains/observability/index.js";
|
|
@@ -92,7 +81,12 @@ Repo-owned fleet contracts and the dispatch status surface.
|
|
|
92
81
|
|
|
93
82
|
Subcommands:
|
|
94
83
|
list list .clio-coder/fleets/*.md contracts with validation status
|
|
84
|
+
new <name> --from <builtin> copy build-review, build-test, or sdlc into this repository
|
|
85
|
+
validate <name> [--json] run the fleet execution preflight without side effects
|
|
86
|
+
graph <name> [--json] print compiled waves, loops, scopes, and write boundaries
|
|
87
|
+
commands init draft a commented command registry from declared project entries
|
|
95
88
|
run <name> [--var k=v ...] preflight and execute a fleet contract
|
|
89
|
+
[--resume <runId>] replay a completed prefix from a prior run of the same plan
|
|
96
90
|
[--json] emit step receipts as JSON
|
|
97
91
|
status [--json] show running, retrying, and total dispatch state
|
|
98
92
|
drain [--json] deny new execution starts for up to one hour
|
|
@@ -111,6 +105,11 @@ function fail(message: string): number {
|
|
|
111
105
|
return 2;
|
|
112
106
|
}
|
|
113
107
|
|
|
108
|
+
function refuse(message: string): number {
|
|
109
|
+
process.stderr.write(`clio-coder fleet: ${message}\n`);
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
|
|
114
113
|
function parseVars(args: ReadonlyArray<string>): { vars: Record<string, string>; rest: string[]; error?: string } {
|
|
115
114
|
const vars: Record<string, string> = {};
|
|
116
115
|
const rest: string[] = [];
|
|
@@ -134,25 +133,6 @@ function parseVars(args: ReadonlyArray<string>): { vars: Record<string, string>;
|
|
|
134
133
|
return { vars, rest };
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
/** Sealed independence facts between the work under review and its reviewer. */
|
|
138
|
-
function fleetGateCorrelation(subject: RunReceipt, decider: RunReceipt): GateDecisionCorrelation {
|
|
139
|
-
const facts = (receipt: RunReceipt) => ({
|
|
140
|
-
agentId: receipt.agentId,
|
|
141
|
-
targetId: receipt.targetId,
|
|
142
|
-
wireModelId: receipt.wireModelId,
|
|
143
|
-
runtimeId: receipt.runtimeId,
|
|
144
|
-
nodeId: receipt.node?.id ?? "local",
|
|
145
|
-
});
|
|
146
|
-
const { agent, target, modelFamily, runtime, node, independent } = gateRouteCorrelation(
|
|
147
|
-
facts(subject),
|
|
148
|
-
facts(decider),
|
|
149
|
-
);
|
|
150
|
-
return { agent, target, modelFamily, runtime, node, independent };
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/** Owner id for a plan with no model runs; it holds nothing to release. */
|
|
154
|
-
const NO_WORKER_RESERVATION = "fleet-no-worker-reservation";
|
|
155
|
-
|
|
156
136
|
function newFleetRootId(): string {
|
|
157
137
|
return `fleet-${randomBytes(6).toString("hex")}`;
|
|
158
138
|
}
|
|
@@ -164,7 +144,14 @@ function newFleetRootId(): string {
|
|
|
164
144
|
function renderStep(step: FleetContractStep): string {
|
|
165
145
|
if (step.kind === "code") return `code:${step.command}[${step.scope}]`;
|
|
166
146
|
if (step.kind === "agent") return `${step.agent}[${step.scope}]`;
|
|
167
|
-
|
|
147
|
+
if (step.kind === "gate") return `gate:${step.path}[${step.run}]`;
|
|
148
|
+
if (step.kind === "plan") return `plan:${step.agent}[${step.roster.join(",")}; max ${step.maxTasks}]`;
|
|
149
|
+
const check =
|
|
150
|
+
step.check.kind === "code"
|
|
151
|
+
? `code:${step.check.command}`
|
|
152
|
+
: step.check.kind === "gate"
|
|
153
|
+
? `gate:${step.check.gate}`
|
|
154
|
+
: step.check.agent;
|
|
168
155
|
return `loop:${step.id}(${check} -> ${step.repair.agent} x${step.maxAttempts})`;
|
|
169
156
|
}
|
|
170
157
|
|
|
@@ -216,6 +203,7 @@ interface FleetPreflightDeps {
|
|
|
216
203
|
agents: AgentsContract;
|
|
217
204
|
safety: SafetyContract;
|
|
218
205
|
scheduling: SchedulingContract | undefined;
|
|
206
|
+
settings: Readonly<ClioSettings>;
|
|
219
207
|
}
|
|
220
208
|
|
|
221
209
|
/** Every agent a contract dispatches, including both halves of every loop. */
|
|
@@ -225,7 +213,11 @@ function contractAgents(
|
|
|
225
213
|
const agents: Array<{ id: string; agent: string; scope: "readonly" | "workspace" }> = [];
|
|
226
214
|
for (const step of contract.steps) {
|
|
227
215
|
if (step.kind === "agent") agents.push({ id: step.id, agent: step.agent, scope: step.scope });
|
|
228
|
-
else if (step.kind === "
|
|
216
|
+
else if (step.kind === "gate") agents.push({ id: step.id, agent: step.agent, scope: "workspace" });
|
|
217
|
+
else if (step.kind === "plan") {
|
|
218
|
+
agents.push({ id: step.id, agent: step.agent, scope: step.scope });
|
|
219
|
+
for (const agent of step.roster) agents.push({ id: `${step.id}.roster`, agent, scope: "readonly" });
|
|
220
|
+
} else if (step.kind === "loop") {
|
|
229
221
|
if (step.check.kind === "agent") {
|
|
230
222
|
agents.push({ id: `${step.id}.check`, agent: step.check.agent, scope: step.check.scope });
|
|
231
223
|
}
|
|
@@ -236,6 +228,28 @@ function contractAgents(
|
|
|
236
228
|
}
|
|
237
229
|
|
|
238
230
|
function preflightFleet(contract: FleetContract, deps: FleetPreflightDeps): string | null {
|
|
231
|
+
const targets = new Set(deps.settings.targets.map((target) => target.id));
|
|
232
|
+
const profiles = new Set(Object.keys(deps.settings.workers?.profiles ?? {}));
|
|
233
|
+
const checkRoute = (id: string, route: { target?: string; profile?: string }): string | null => {
|
|
234
|
+
if (route.target !== undefined && !targets.has(route.target))
|
|
235
|
+
return `unknown target '${route.target}' at step '${id}'`;
|
|
236
|
+
if (route.profile !== undefined && !profiles.has(route.profile))
|
|
237
|
+
return `unknown profile '${route.profile}' at step '${id}'`;
|
|
238
|
+
return null;
|
|
239
|
+
};
|
|
240
|
+
for (const step of contract.steps) {
|
|
241
|
+
if (step.kind === "agent" || step.kind === "gate" || step.kind === "plan") {
|
|
242
|
+
const error = checkRoute(step.id, step);
|
|
243
|
+
if (error !== null) return error;
|
|
244
|
+
}
|
|
245
|
+
if (step.kind !== "loop") continue;
|
|
246
|
+
if (step.check.kind === "agent") {
|
|
247
|
+
const error = checkRoute(`${step.id}.check`, step.check);
|
|
248
|
+
if (error !== null) return error;
|
|
249
|
+
}
|
|
250
|
+
const error = checkRoute(`${step.id}.repair`, step.repair);
|
|
251
|
+
if (error !== null) return error;
|
|
252
|
+
}
|
|
239
253
|
for (const step of contractAgents(contract)) {
|
|
240
254
|
if (!deps.agents.get(step.agent)) {
|
|
241
255
|
return `unknown agent '${step.agent}' (step '${step.id}' must name a recipe from 'clio-coder agents')`;
|
|
@@ -264,23 +278,21 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
|
|
|
264
278
|
const { vars, rest, error } = parseVars(args);
|
|
265
279
|
if (error !== undefined) return fail(error);
|
|
266
280
|
const json = rest.includes("--json");
|
|
281
|
+
const resumeIndex = rest.indexOf("--resume");
|
|
282
|
+
const resumeId = resumeIndex === -1 ? undefined : rest[resumeIndex + 1];
|
|
283
|
+
if (resumeIndex !== -1 && (resumeId === undefined || resumeId.startsWith("-")))
|
|
284
|
+
return fail("--resume requires a run id");
|
|
267
285
|
const name = rest.find((arg) => !arg.startsWith("-"));
|
|
268
|
-
if (!name) return fail("usage: clio-coder fleet run <name> [--var key=value ...] [--json]");
|
|
286
|
+
if (!name) return fail("usage: clio-coder fleet run <name> [--var key=value ...] [--resume <runId>] [--json]");
|
|
269
287
|
|
|
270
|
-
// Phase 1: zero-side-effect validation. Parse and render strictly before
|
|
271
|
-
// any domain boots or any process spawns.
|
|
272
288
|
let contract: FleetContract;
|
|
273
289
|
let prompt: string;
|
|
274
290
|
let commands: FleetCommandRegistry | null;
|
|
275
291
|
try {
|
|
276
292
|
contract = loadFleetContract(process.cwd(), name);
|
|
277
293
|
prompt = renderFleetPrompt(contract.body, vars);
|
|
278
|
-
// loadFleetContract already refused any unregistered command id; this
|
|
279
|
-
// read is the binding the runner executes.
|
|
280
294
|
commands = loadFleetCommands(process.cwd());
|
|
281
295
|
} catch (err) {
|
|
282
|
-
// The listing calls this state `setup` and says how to leave it; the run
|
|
283
|
-
// path is where a user meets it with intent, so it says the same thing.
|
|
284
296
|
if (err instanceof FleetCommandRegistryMissingError) {
|
|
285
297
|
process.stderr.write(`clio-coder fleet: ${err.message}\n ${FLEET_COMMANDS_REMEDY}\n`);
|
|
286
298
|
return 2;
|
|
@@ -313,16 +325,19 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
|
|
|
313
325
|
await loaded.stop();
|
|
314
326
|
return fail("required domains unavailable (dispatch/agents/safety)");
|
|
315
327
|
}
|
|
316
|
-
|
|
317
328
|
const roleFacts = agentRoleFactsResolver((id) => agents.getSpec(id));
|
|
318
|
-
const preflightError = preflightFleet(contract, {
|
|
329
|
+
const preflightError = preflightFleet(contract, {
|
|
330
|
+
agents,
|
|
331
|
+
safety,
|
|
332
|
+
scheduling,
|
|
333
|
+
settings: config?.get() ?? readSettings(),
|
|
334
|
+
});
|
|
319
335
|
if (preflightError !== null) {
|
|
320
336
|
await loaded.stop();
|
|
321
337
|
return fail(`preflight failed: ${preflightError}`);
|
|
322
338
|
}
|
|
323
339
|
|
|
324
340
|
const fleetRootId = newFleetRootId();
|
|
325
|
-
let planOutcome: ExecutionPlanResult;
|
|
326
341
|
let plan: ExecutionPlan;
|
|
327
342
|
try {
|
|
328
343
|
plan = compileFleetExecutionPlan({
|
|
@@ -341,9 +356,14 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
|
|
|
341
356
|
return {
|
|
342
357
|
requestedAuthority: spec.capabilityClass,
|
|
343
358
|
approvedAuthority: spec.capabilityClass,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
359
|
+
expectedResultContract:
|
|
360
|
+
context.planRole === true
|
|
361
|
+
? "delegation-plan"
|
|
362
|
+
: context.gateAuthorRole === true
|
|
363
|
+
? "artifact-report"
|
|
364
|
+
: context.gateRole === "reviewer"
|
|
365
|
+
? "verifier-report"
|
|
366
|
+
: spec.resultContract.kind,
|
|
347
367
|
executionRole: withAttemptRole(requestRole, context.attempt),
|
|
348
368
|
};
|
|
349
369
|
},
|
|
@@ -352,268 +372,91 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
|
|
|
352
372
|
await loaded.stop();
|
|
353
373
|
return fail(err instanceof Error ? err.message : String(err));
|
|
354
374
|
}
|
|
355
|
-
// A declared boundary is verified against the checkout, so the checkout has
|
|
356
|
-
// to be one this can read. Refused here, before any spawn, rather than
|
|
357
|
-
// discovered as an unenforceable claim halfway through the run.
|
|
358
375
|
try {
|
|
359
376
|
preflightWriteBoundaries(plan, process.cwd());
|
|
360
377
|
} catch (err) {
|
|
361
378
|
await loaded.stop();
|
|
362
379
|
return fail(`preflight failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
363
380
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
381
|
+
|
|
382
|
+
let resume: ExecuteFleetRunInput["resume"];
|
|
383
|
+
if (resumeId !== undefined) {
|
|
384
|
+
const record = readFleetRun(resumeId);
|
|
385
|
+
if (record === null) {
|
|
386
|
+
await loaded.stop();
|
|
387
|
+
return refuse(`resume run '${resumeId}' was not found in the durable fleet ledger`);
|
|
388
|
+
}
|
|
389
|
+
const planned = planFleetResume(record, plan, contract, vars);
|
|
390
|
+
if (!planned.ok) {
|
|
391
|
+
if (planned.reason === "fleet-name") {
|
|
392
|
+
await loaded.stop();
|
|
393
|
+
return refuse(`resume run '${resumeId}' belongs to fleet '${planned.priorFleet}', not '${planned.currentFleet}'`);
|
|
394
|
+
}
|
|
395
|
+
if (planned.reason === "vars") {
|
|
396
|
+
await loaded.stop();
|
|
397
|
+
return refuse(`resume run '${resumeId}' used different --var values`);
|
|
398
|
+
}
|
|
399
|
+
process.stderr.write(
|
|
400
|
+
`clio-coder fleet: resume plan hash differs: prior=${planned.priorHash} current=${planned.currentHash}\n`,
|
|
401
|
+
);
|
|
402
|
+
for (const entry of planned.diff) {
|
|
403
|
+
process.stderr.write(` step ${entry.index + 1}: ${entry.priorId} -> ${entry.currentId}\n`);
|
|
404
|
+
process.stderr.write(` prior: ${JSON.stringify(entry.prior)}\n`);
|
|
405
|
+
process.stderr.write(` current: ${JSON.stringify(entry.current)}\n`);
|
|
406
|
+
}
|
|
407
|
+
await loaded.stop();
|
|
408
|
+
return 1;
|
|
409
|
+
}
|
|
410
|
+
resume = { record, replayed: planned.replayed };
|
|
411
|
+
}
|
|
412
|
+
|
|
374
413
|
process.stderr.write(
|
|
375
414
|
`fleet ${contract.name}: root=${fleetRootId} plan=${plan.hash} steps=${plan.steps.length} loops=${plan.loops.length}\n`,
|
|
376
415
|
);
|
|
377
|
-
|
|
378
|
-
const receiptsByStep = new Map<string, RunReceipt>();
|
|
379
|
-
const planStep = (stepId: string) => plan.steps.find((entry) => entry.id === stepId);
|
|
380
|
-
const attributionEnabled = config?.get().attribution.gitCommits ?? true;
|
|
381
|
-
// Freshness is coordinator-owned execution state. Worker prose cannot set
|
|
382
|
-
// either flag: a successful deterministic verification sets the first, and
|
|
383
|
-
// an integrity-valid independent gate decision sets the second. Any later
|
|
384
|
-
// workspace agent invalidates both before a commit may read them.
|
|
385
|
-
let validationFresh = false;
|
|
386
|
-
let independentReviewFresh = false;
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* The commit message is the words of the agent that produced the work, and
|
|
390
|
-
* only that agent's. Candidates are consulted newest first; when none of
|
|
391
|
-
* them authored one, code writes a deterministic line rather than inventing
|
|
392
|
-
* a sentence or reusing somebody else's.
|
|
393
|
-
*/
|
|
394
|
-
const commitMessageFor = (
|
|
395
|
-
step: ExecutionPlanCodeStep,
|
|
396
|
-
priorResults: ReadonlyMap<string, { output: string }>,
|
|
397
|
-
): string => {
|
|
398
|
-
for (const candidate of step.commitFrom ?? []) {
|
|
399
|
-
const source = planStep(candidate);
|
|
400
|
-
const result = priorResults.get(candidate);
|
|
401
|
-
if (source === undefined || source.kind !== "agent" || result === undefined) continue;
|
|
402
|
-
const resultContract = agents.getSpec(source.agentId)?.resultContract ?? null;
|
|
403
|
-
if (resultContract === null) continue;
|
|
404
|
-
const authored = resultContractAuthorship(resultContract, result.output);
|
|
405
|
-
if (authored.commitMessage !== null) return authored.commitMessage;
|
|
406
|
-
if (authored.summary !== null) return `clio(${fleetRootId}): ${authored.summary}`;
|
|
407
|
-
}
|
|
408
|
-
return `clio(${fleetRootId}): ${contract.name} ${step.id}`;
|
|
409
|
-
};
|
|
416
|
+
let outcome: FleetRunOutcome;
|
|
410
417
|
try {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
memberId: admission.step.id,
|
|
434
|
-
wave: plan.waves.findIndex((wave) => wave.includes(admission.step.id)),
|
|
435
|
-
resolution: dispatch.preview?.({
|
|
436
|
-
agentId: admission.step.agentId,
|
|
437
|
-
executionRole: admission.step.executionRole,
|
|
438
|
-
task: admission.step.task,
|
|
439
|
-
}) as NonNullable<ReturnType<NonNullable<DispatchContract["preview"]>>>,
|
|
440
|
-
})),
|
|
441
|
-
});
|
|
442
|
-
if (!reservation) throw new Error("fleet whole-plan reservation is unavailable");
|
|
443
|
-
return { ownerId: reservation.ownerId };
|
|
444
|
-
},
|
|
445
|
-
async run(step, handoffs, reservation, ledger) {
|
|
446
|
-
// A loop repair is attempt n of the same logical work, so it enters
|
|
447
|
-
// the run ledger as one: recovery evidence, not a fresh observation.
|
|
448
|
-
const attempt = step.loop?.role === "repair" ? step.loop.attempt : 0;
|
|
449
|
-
const request: DispatchRequest = {
|
|
450
|
-
agentId: step.agentId,
|
|
451
|
-
executionRole: step.executionRole,
|
|
452
|
-
task: step.task,
|
|
453
|
-
predecessorHandoffs: handoffs,
|
|
454
|
-
requestOrigin: "user",
|
|
455
|
-
lineage: { parentRunId: fleetRootId, rootRunId: fleetRootId, attempt, depth: 1 },
|
|
456
|
-
reservation,
|
|
457
|
-
...(ledger !== undefined ? { ledger } : {}),
|
|
458
|
-
...(step.loop?.role === "check"
|
|
459
|
-
? {
|
|
460
|
-
gate: {
|
|
461
|
-
role: "reviewer" as const,
|
|
462
|
-
group: `${fleetRootId}:${step.loop.loopId}`,
|
|
463
|
-
cycle: step.loop.attempt,
|
|
464
|
-
subjects: handoffs.map((handoff) => ({
|
|
465
|
-
runId: handoff.terminalRunId,
|
|
466
|
-
digest: handoff.receiptDigest,
|
|
467
|
-
})),
|
|
468
|
-
},
|
|
469
|
-
}
|
|
470
|
-
: {}),
|
|
471
|
-
...(step.scope === "readonly" ? { autonomy: "read-only" as const } : {}),
|
|
472
|
-
};
|
|
473
|
-
const handle = await dispatch.dispatch(request);
|
|
474
|
-
void (async () => {
|
|
475
|
-
for await (const _event of handle.events) {
|
|
476
|
-
/* background display drain */
|
|
477
|
-
}
|
|
478
|
-
})().catch(() => {});
|
|
479
|
-
return {
|
|
480
|
-
assignmentId: handle.runId,
|
|
481
|
-
result: handle.finalPromise.then((receipt) => {
|
|
482
|
-
if (step.scope === "workspace") {
|
|
483
|
-
validationFresh = false;
|
|
484
|
-
independentReviewFresh = false;
|
|
485
|
-
}
|
|
486
|
-
receipts.push(receipt);
|
|
487
|
-
receiptsByStep.set(step.id, receipt);
|
|
488
|
-
const envelope = dispatch.getRun(receipt.runId);
|
|
489
|
-
const integrityValid = envelope !== null && verifyReceiptIntegrity(receipt, envelope).ok;
|
|
490
|
-
const succeeded = receipt.exitCode === 0 && (receipt.outcome === undefined || receipt.outcome === "succeeded");
|
|
491
|
-
if (json) process.stdout.write(`${JSON.stringify(receipt)}\n`);
|
|
492
|
-
else
|
|
493
|
-
process.stdout.write(
|
|
494
|
-
`step ${step.id} ${step.agentId}: ${succeeded ? "succeeded" : "failed"} assignment=${handle.runId} terminal-run=${receipt.runId} cost=$${receipt.costUsd.toFixed(4)}\n`,
|
|
495
|
-
);
|
|
496
|
-
return {
|
|
497
|
-
stepId: step.id,
|
|
498
|
-
assignmentId: handle.runId,
|
|
499
|
-
terminalRunId: receipt.runId,
|
|
500
|
-
receiptDigest: receipt.integrity.digest,
|
|
501
|
-
output: receipt.output?.state === "final" ? receipt.output.text : "",
|
|
502
|
-
succeeded,
|
|
503
|
-
integrityValid,
|
|
504
|
-
};
|
|
505
|
-
}),
|
|
506
|
-
};
|
|
507
|
-
},
|
|
508
|
-
async runCode(step, _handoffs, signal, priorResults) {
|
|
509
|
-
const command = commands?.commands.get(step.commandId);
|
|
510
|
-
if (command === undefined) throw new Error(`fleet code step '${step.id}' has no registered command`);
|
|
511
|
-
const isCommit = step.commitFrom !== undefined;
|
|
512
|
-
const evidence = isCommit
|
|
513
|
-
? deriveFleetCommitAttribution({
|
|
514
|
-
plan,
|
|
515
|
-
step,
|
|
516
|
-
priorResults,
|
|
517
|
-
validationFresh,
|
|
518
|
-
independentReviewFresh,
|
|
519
|
-
})
|
|
520
|
-
: null;
|
|
521
|
-
if (!isCommit && step.scope === "workspace" && step.verification !== true) {
|
|
522
|
-
validationFresh = false;
|
|
523
|
-
independentReviewFresh = false;
|
|
418
|
+
outcome = await executeFleetRun({
|
|
419
|
+
plan,
|
|
420
|
+
contractName: contract.name,
|
|
421
|
+
commands,
|
|
422
|
+
workspaceRoot: process.cwd(),
|
|
423
|
+
fleetRootId,
|
|
424
|
+
dispatch,
|
|
425
|
+
agents: { getSpec: (agentId) => agents.getSpec(agentId) },
|
|
426
|
+
attributionEnabled: config?.get().attribution.gitCommits ?? true,
|
|
427
|
+
vars,
|
|
428
|
+
...(resume !== undefined ? { resume } : {}),
|
|
429
|
+
onStepSettled(step) {
|
|
430
|
+
if (step.replayed === true && step.result !== undefined) {
|
|
431
|
+
if (json)
|
|
432
|
+
process.stdout.write(
|
|
433
|
+
`${JSON.stringify({ stepId: step.stepId, status: "replayed", receipt: { runId: step.result.terminalRunId, digest: step.result.receiptDigest } })}\n`,
|
|
434
|
+
);
|
|
435
|
+
else
|
|
436
|
+
process.stdout.write(
|
|
437
|
+
`step ${step.stepId}: replayed terminal-run=${step.result.terminalRunId} receipt=${step.result.receiptDigest}\n`,
|
|
438
|
+
);
|
|
439
|
+
return;
|
|
524
440
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
...(isCommit && evidence !== null && originalMessage !== null
|
|
533
|
-
? {
|
|
534
|
-
substitutions: {
|
|
535
|
-
commitMessage: attributeCommitMessage(originalMessage, evidence, attributionEnabled),
|
|
536
|
-
},
|
|
537
|
-
requireWorkspaceChanges: true,
|
|
538
|
-
commitAttribution: { enabled: attributionEnabled, evidence },
|
|
539
|
-
}
|
|
540
|
-
: {}),
|
|
541
|
-
});
|
|
542
|
-
if (step.verification === true) validationFresh = outcome.report.passed;
|
|
543
|
-
const recordPath = await writeCodeStepRecord(fleetRootId, outcome.record);
|
|
544
|
-
if (json) process.stdout.write(`${JSON.stringify({ codeStep: outcome.record })}\n`);
|
|
545
|
-
else
|
|
546
|
-
process.stdout.write(
|
|
547
|
-
`step ${step.id} code:${command.id}: ${outcome.report.passed ? "passed" : "failed"} exit=${outcome.report.exitCode} ${outcome.record.durationMs}ms record=${recordPath}\n`,
|
|
548
|
-
);
|
|
549
|
-
return {
|
|
550
|
-
stepId: step.id,
|
|
551
|
-
assignmentId: outcome.record.runId,
|
|
552
|
-
terminalRunId: outcome.record.runId,
|
|
553
|
-
receiptDigest: outcome.record.reportDigest,
|
|
554
|
-
output: outcome.output,
|
|
555
|
-
succeeded: outcome.report.passed,
|
|
556
|
-
// The runner authored this report itself, so its provenance is
|
|
557
|
-
// valid by construction; only the command's verdict can be red.
|
|
558
|
-
integrityValid: true,
|
|
559
|
-
};
|
|
560
|
-
},
|
|
561
|
-
async decideLoop({ loop, step, attempt, terminalAttempt, result }) {
|
|
562
|
-
// Reuse the coordinator's existing review policy verbatim: pass ends
|
|
563
|
-
// the loop, a failure with attempts left earns one repair, and the
|
|
564
|
-
// same failure at the bound is simply the terminal answer. `revise`
|
|
565
|
-
// is never a verdict the reviewed model authors.
|
|
566
|
-
const decider = receiptsByStep.get(step.id);
|
|
567
|
-
// The subject is the work under review: the most recent agent run
|
|
568
|
-
// upstream of this verification. A direct dependency is not enough,
|
|
569
|
-
// because a loop's declared dependency is another loop's
|
|
570
|
-
// deterministic check, which produced no receipt at all.
|
|
571
|
-
const ancestors = executionPlanAncestors(plan, step.id);
|
|
572
|
-
let subject: RunReceipt | undefined;
|
|
573
|
-
for (const [stepId, receipt] of receiptsByStep) {
|
|
574
|
-
if (ancestors.has(stepId)) subject = receipt;
|
|
575
|
-
}
|
|
576
|
-
if (decider === undefined || subject === undefined) {
|
|
577
|
-
return {
|
|
578
|
-
resolved: false,
|
|
579
|
-
findings: null,
|
|
580
|
-
needsDecision: `loop '${loop.id}' cycle ${attempt} has no sealed reviewer or subject receipt`,
|
|
581
|
-
};
|
|
441
|
+
if (step.receipt !== undefined) {
|
|
442
|
+
if (json) process.stdout.write(`${JSON.stringify(step.receipt)}\n`);
|
|
443
|
+
else
|
|
444
|
+
process.stdout.write(
|
|
445
|
+
`step ${step.stepId} ${step.agentId}: ${step.succeeded ? "succeeded" : "failed"}${step.failureReason !== undefined ? ` reason=${step.failureReason}` : ""} assignment=${step.assignmentId} terminal-run=${step.terminalRunId} cost=$${step.costUsd.toFixed(4)}\n`,
|
|
446
|
+
);
|
|
447
|
+
return;
|
|
582
448
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
decider: { runId: decider.runId, digest: decider.integrity.digest },
|
|
590
|
-
correlation,
|
|
591
|
-
output: result.output,
|
|
592
|
-
});
|
|
593
|
-
independentReviewFresh = decided.verdict === "pass" && correlation.independent;
|
|
594
|
-
const staged = stagePendingGateDecision(decided.draft);
|
|
595
|
-
const { path: decisionPath } = materializePendingGateDecision(staged);
|
|
596
|
-
if (!json) {
|
|
597
|
-
process.stdout.write(
|
|
598
|
-
`loop ${loop.id} cycle ${attempt}: ${decided.draft.outcome}${decided.draft.detail === undefined ? "" : ` (${decided.draft.detail})`} decision=${decisionPath}\n`,
|
|
599
|
-
);
|
|
449
|
+
if (step.codeStep !== undefined) {
|
|
450
|
+
if (json) process.stdout.write(`${JSON.stringify({ codeStep: step.codeStep })}\n`);
|
|
451
|
+
else
|
|
452
|
+
process.stdout.write(
|
|
453
|
+
`step ${step.stepId} code:${step.commandId}: ${step.succeeded ? "passed" : "failed"} exit=${step.codeStep.exitCode} ${step.codeStep.durationMs}ms record=${step.recordPath}\n`,
|
|
454
|
+
);
|
|
600
455
|
}
|
|
601
|
-
return {
|
|
602
|
-
resolved: decided.verdict === "pass",
|
|
603
|
-
findings: decided.findings,
|
|
604
|
-
needsDecision: decided.needsDecision,
|
|
605
|
-
};
|
|
606
|
-
},
|
|
607
|
-
beginWriteBoundary: (window, stepIds) => boundaryEnforcer.begin(window, stepIds),
|
|
608
|
-
verifyWriteBoundary: (window, stepIds) => boundaryEnforcer.verify(window, stepIds),
|
|
609
|
-
cancel: (assignmentId) => dispatch.abort(assignmentId),
|
|
610
|
-
release: (ownerId) => {
|
|
611
|
-
if (ownerId === NO_WORKER_RESERVATION) return;
|
|
612
|
-
dispatch.reservations?.rollbackUnconsumed(ownerId);
|
|
613
456
|
},
|
|
614
|
-
|
|
615
|
-
if (
|
|
616
|
-
|
|
457
|
+
onNotice(text, kind) {
|
|
458
|
+
if (kind === "write-boundary") process.stderr.write(`${text}\n`);
|
|
459
|
+
else if (kind === "gate" && !json) process.stdout.write(`${text}\n`);
|
|
617
460
|
},
|
|
618
461
|
});
|
|
619
462
|
} catch (err) {
|
|
@@ -623,53 +466,40 @@ async function runFleet(args: ReadonlyArray<string>): Promise<number> {
|
|
|
623
466
|
}
|
|
624
467
|
await dispatch.drain();
|
|
625
468
|
await loaded.stop();
|
|
626
|
-
|
|
627
|
-
// verification that a later attempt fixed is the loop working as declared,
|
|
628
|
-
// and a node a resolved loop made unnecessary never had to run at all.
|
|
629
|
-
const required = plan.steps.filter((step) => step.loop === undefined && !planOutcome.unneeded.includes(step.id));
|
|
630
|
-
const failedLoops = planOutcome.loops.filter((loop) => !loop.resolved);
|
|
631
|
-
const succeededSteps = required.filter((step) => planOutcome.results.get(step.id)?.succeeded === true).length;
|
|
632
|
-
const resolvedLoops = planOutcome.loops.length - failedLoops.length;
|
|
469
|
+
|
|
633
470
|
if (json) {
|
|
634
471
|
process.stdout.write(
|
|
635
472
|
`${JSON.stringify({
|
|
636
473
|
fleet: contract.name,
|
|
637
474
|
rootId: fleetRootId,
|
|
638
475
|
planHash: plan.hash,
|
|
639
|
-
loops:
|
|
640
|
-
revalidated:
|
|
641
|
-
unneeded:
|
|
642
|
-
skipped:
|
|
643
|
-
needsDecision:
|
|
644
|
-
writeBoundaries:
|
|
476
|
+
loops: outcome.result.loops,
|
|
477
|
+
revalidated: outcome.result.revalidated,
|
|
478
|
+
unneeded: outcome.result.unneeded,
|
|
479
|
+
skipped: outcome.result.skipped,
|
|
480
|
+
needsDecision: outcome.result.needsDecision,
|
|
481
|
+
writeBoundaries: outcome.result.writeBoundaries,
|
|
645
482
|
})}\n`,
|
|
646
483
|
);
|
|
647
484
|
} else {
|
|
648
|
-
const spentUsd = receipts.reduce((sum, receipt) => sum + receipt.costUsd, 0);
|
|
649
485
|
process.stdout.write(
|
|
650
|
-
`fleet ${contract.name}: ${
|
|
486
|
+
`fleet ${contract.name}: ${outcome.succeededStepCount}/${outcome.requiredStepCount} steps succeeded, ${outcome.resolvedLoopCount}/${outcome.result.loops.length} loops resolved, total cost $${outcome.totalCostUsd.toFixed(4)}\n`,
|
|
651
487
|
);
|
|
652
|
-
for (const loop of
|
|
488
|
+
for (const loop of outcome.result.loops)
|
|
653
489
|
process.stdout.write(
|
|
654
490
|
` loop ${loop.loopId}: ${loop.reason} after ${loop.attempts} verification(s) and ${loop.repairs} repair(s)\n`,
|
|
655
491
|
);
|
|
656
|
-
|
|
657
|
-
if (planOutcome.revalidated.length > 0) {
|
|
492
|
+
if (outcome.result.revalidated.length > 0)
|
|
658
493
|
process.stdout.write(
|
|
659
|
-
` staleness: re-ran ${
|
|
494
|
+
` staleness: re-ran ${outcome.result.revalidated.join(", ")} because a workspace step landed after the last green\n`,
|
|
660
495
|
);
|
|
661
|
-
}
|
|
662
|
-
for (const
|
|
663
|
-
for (const boundary of planOutcome.writeBoundaries) {
|
|
496
|
+
for (const message of outcome.result.needsDecision) process.stdout.write(` needs operator decision: ${message}\n`);
|
|
497
|
+
for (const boundary of outcome.result.writeBoundaries) {
|
|
664
498
|
if (!boundary.violated) continue;
|
|
665
499
|
process.stdout.write(` write boundary ${boundary.window}: ${boundary.detail ?? WRITE_BOUNDARY_VIOLATION_REASON}\n`);
|
|
666
500
|
}
|
|
667
501
|
}
|
|
668
|
-
|
|
669
|
-
failedLoops.length === 0 &&
|
|
670
|
-
planOutcome.skipped.length === 0 &&
|
|
671
|
-
required.every((step) => planOutcome.results.get(step.id)?.succeeded === true);
|
|
672
|
-
return cleanRun ? 0 : 1;
|
|
502
|
+
return outcome.cleanRun ? 0 : 1;
|
|
673
503
|
}
|
|
674
504
|
|
|
675
505
|
// ---------------------------------------------------------------------------
|
|
@@ -867,6 +697,14 @@ export async function runFleetCommand(args: ReadonlyArray<string>): Promise<numb
|
|
|
867
697
|
switch (sub) {
|
|
868
698
|
case "list":
|
|
869
699
|
return runList(args.slice(1));
|
|
700
|
+
case "new":
|
|
701
|
+
return (await import("./fleet-new.js")).runFleetNew(args.slice(1));
|
|
702
|
+
case "validate":
|
|
703
|
+
return (await import("./fleet-validate.js")).runFleetValidate(args.slice(1));
|
|
704
|
+
case "graph":
|
|
705
|
+
return (await import("./fleet-graph.js")).runFleetGraph(args.slice(1));
|
|
706
|
+
case "commands":
|
|
707
|
+
return (await import("./fleet-commands.js")).runFleetCommands(args.slice(1));
|
|
870
708
|
case "run":
|
|
871
709
|
return runFleet(args.slice(1));
|
|
872
710
|
case "status":
|