@iowarp/clio-coder 0.3.6 → 0.3.8
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 +59 -0
- package/README.md +22 -6
- package/dist/{acp-2BEHC4DL.js → acp-U67UHUK2.js} +14 -14
- package/dist/{agents-LNNFTM53.js → agents-YU6SGALZ.js} +42 -35
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-KXXFI2VS.js → auth-5ZPJOIVG.js} +32 -24
- package/dist/builtins-C6JMZVV6.js +17 -0
- package/dist/{chunk-E25LMLRW.js → chunk-26LEYJZH.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-2HEJ2F35.js} +5 -5
- package/dist/{chunk-PBTHKCPN.js → chunk-2HFZQUHL.js} +7 -7
- package/dist/{chunk-FO5ZOVUY.js → chunk-3BINW3FP.js} +5 -5
- package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
- package/dist/{chunk-CKXWIANG.js → chunk-4SPRNWDE.js} +18 -16
- package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
- package/dist/{chunk-XF5N4U5A.js → chunk-5DHKRSMQ.js} +9 -8
- package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
- package/dist/{chunk-EKY57CSP.js → chunk-5H3GB5BO.js} +68 -772
- package/dist/{chunk-6XXKFVSN.js → chunk-5Q2VVUKB.js} +4 -4
- package/dist/{chunk-DJVECN66.js → chunk-7RFXX52T.js} +295 -3705
- package/dist/{chunk-ZXF4XRKW.js → chunk-7RGZWPB6.js} +158 -8
- package/dist/{chunk-FYYLNIL5.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-XXQNGV4M.js → chunk-A3WNZD3P.js} +548 -72
- package/dist/{chunk-VEZEGCGW.js → chunk-B5CSFE7B.js} +23 -21
- package/dist/{chunk-2SFS6XQE.js → chunk-DGSYXYMX.js} +3 -2
- package/dist/chunk-DR52UMZW.js +21 -0
- package/dist/{chunk-ZWLZP4ZT.js → chunk-DYIM5TJT.js} +98 -12
- package/dist/{chunk-QKMUKYO7.js → chunk-E77JEWSD.js} +270 -125
- package/dist/{chunk-LYF7OHWH.js → chunk-EMYUUSFG.js} +12 -467
- package/dist/{chunk-4VP4KH3K.js → chunk-EQ63NRB7.js} +8 -8
- package/dist/chunk-FBVTI2TJ.js +518 -0
- package/dist/{chunk-R46L2BIR.js → chunk-FHJEP5SW.js} +19 -25
- package/dist/{chunk-WR67VIZY.js → chunk-GN57SG4G.js} +66 -8
- package/dist/{chunk-RD5U66HV.js → chunk-GPIEI3LY.js} +9 -9
- package/dist/{chunk-PCZJO5TI.js → chunk-GU2UIAFZ.js} +13 -178
- package/dist/chunk-GWS3VEIW.js +195 -0
- package/dist/chunk-H7IXIC72.js +103 -0
- package/dist/{chunk-3BPUFZDL.js → chunk-HLE42MG7.js} +3 -3
- package/dist/{chunk-24I7BN55.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-QNQHSOLF.js → chunk-IGWKHNIQ.js} +114 -55
- package/dist/chunk-IIZWH4XA.js +172 -0
- package/dist/{chunk-AD2SYQYC.js → chunk-IJ7RPIYJ.js} +124 -6
- package/dist/chunk-J3YUBZWY.js +382 -0
- package/dist/{chunk-5JGRAMKL.js → chunk-JOZYP4GM.js} +8 -6
- package/dist/{chunk-OH3TOQTB.js → chunk-K4XHGFR5.js} +751 -15
- package/dist/{chunk-EYPA3EGJ.js → chunk-KTYTFRMB.js} +178 -14
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/chunk-ME6CCNFO.js +108 -0
- package/dist/chunk-MXKJU4JB.js +1100 -0
- package/dist/{chunk-RY3LY4J5.js → chunk-N22QMJKY.js} +21 -18
- package/dist/chunk-NMPKI6XL.js +3006 -0
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-22NAGB7X.js → chunk-P43ETTHK.js} +5 -94
- package/dist/{chunk-NILBFAPG.js → chunk-PMDBGQSJ.js} +2 -2
- package/dist/chunk-PT7HYKEM.js +165 -0
- package/dist/chunk-RVG5JXAL.js +41 -0
- package/dist/{chunk-GEYXPTRF.js → chunk-RWSI4YD7.js} +2 -2
- package/dist/{chunk-4BPJXDWC.js → chunk-TANS5ZJS.js} +35 -19
- package/dist/{verifiers-NCBTHHN2.js → chunk-TB5666IT.js} +67 -324
- package/dist/{chunk-QM3F2GKX.js → chunk-TLQJPP24.js} +7927 -8088
- package/dist/{chunk-G7MUEIGA.js → chunk-TT36MB5S.js} +2 -1
- package/dist/chunk-TTHACPOM.js +961 -0
- package/dist/{chunk-6US73PDB.js → chunk-TYPGUK6W.js} +7 -7
- package/dist/{chunk-MFFY33HR.js → chunk-U6MBIEMB.js} +554 -209
- package/dist/chunk-VAWNZU7Z.js +242 -0
- package/dist/{chunk-IR4CFBFN.js → chunk-VCBR6CU7.js} +12 -12
- package/dist/{chunk-WHJYKASB.js → chunk-VHN4MY6O.js} +2 -2
- package/dist/{chunk-XYDYPRZI.js → chunk-VWZOAB7K.js} +10 -10
- package/dist/{chunk-ZRGEBJ4T.js → chunk-WLFILSD5.js} +48 -48
- package/dist/{chunk-K7T3E2SR.js → chunk-WNIJTQQK.js} +12 -11
- package/dist/{chunk-CYQKWTG3.js → chunk-WSB3FPX7.js} +68 -20
- package/dist/{chunk-KHSFENX2.js → chunk-WWCZ5F23.js} +116 -14
- package/dist/{chunk-XE2VEJHX.js → chunk-WXY7KU3G.js} +2 -2
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-KOHPCX4K.js → chunk-XWSF374K.js} +5 -5
- package/dist/{chunk-CJUB2JJ2.js → chunk-YS5VLNH5.js} +10 -10
- package/dist/{chunk-ZZMN5OM4.js → chunk-ZNLWCMVZ.js} +2 -2
- package/dist/{chunk-WHGPSPT5.js → chunk-ZVJ5BLO2.js} +2 -2
- package/dist/cli/index.js +33 -31
- package/dist/{clio-M2KGYUFZ.js → clio-QVTYJ57A.js} +10 -10
- package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-5TTYYX6G.js → components-ZFA3SAER.js} +9 -9
- package/dist/{config-XUUYQIWO.js → config-LW5IJFQN.js} +65 -55
- package/dist/{configure-IHJ7YOMV.js → configure-7XIZCOU4.js} +29 -24
- package/dist/{context-75MIWW3U.js → context-L3WL3X7K.js} +54 -43
- package/dist/{context-ZQ7SIFJV.js → context-N52ZA626.js} +30 -14
- package/dist/{context-74JLXAWD.js → context-Y6Y7QPR6.js} +12 -12
- package/dist/{context-clear-GYKWNUML.js → context-clear-MBQRLSDQ.js} +54 -43
- package/dist/{context-index-SSR5ECNE.js → context-index-HVMFQHK3.js} +5 -5
- package/dist/{context-working-set-UX5KEP4J.js → context-working-set-GS6DSO7F.js} +18 -18
- package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-22ZCNOM3.js} +367 -79
- package/dist/{docs-6FZSCG5B.js → docs-7LQ23DLM.js} +9 -9
- package/dist/{doctor-SVJ5BZCW.js → doctor-M7YEDGAE.js} +27 -23
- package/dist/{eval-CG6LLBLD.js → eval-BEC2WHDA.js} +73 -20
- package/dist/{evidence-ZYFIEN42.js → evidence-REJUMSKM.js} +70 -59
- package/dist/{evolve-QGEXEMDW.js → evolve-PY5ZBA5K.js} +50 -39
- package/dist/{extensions-ADGNCJJD.js → extensions-HVKU65YU.js} +7 -7
- package/dist/{fleet-S5R4ZOQY.js → fleet-7WZEWRFA.js} +236 -377
- package/dist/fleet-commands-UVHWM76J.js +70 -0
- package/dist/fleet-graph-6ULH7PES.js +125 -0
- package/dist/fleet-new-RDVJLHHH.js +48 -0
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-J53T6CCE.js} +5 -5
- package/dist/fleet-validate-72PC4SLA.js +79 -0
- package/dist/{init-5DRU55YR.js → init-OG3TPGQG.js} +69 -57
- package/dist/library-CNTMPLRF.js +217 -0
- package/dist/{memory-7YKKR6UC.js → memory-6IS7F275.js} +52 -41
- package/dist/{models-ZPOLRU2C.js → models-ENRJDA5W.js} +37 -31
- package/dist/{monitor-US5F5YGZ.js → monitor-XLDVO7TN.js} +79 -42
- package/dist/{orchestrator-E2AL4T5N.js → orchestrator-6KSPYRHA.js} +4126 -762
- package/dist/{paths-E7KYAQWE.js → paths-DBXMZMDU.js} +6 -6
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-KZ652EK6.js → reset-RZ4ER727.js} +12 -12
- package/dist/{run-SRNBKDWD.js → run-Y2CNK5RU.js} +89 -76
- package/dist/{share-CGZE33UP.js → share-A55GYP6Z.js} +37 -13
- package/dist/{skills-S2X4DLY5.js → skills-ALC5J6AT.js} +29 -13
- package/dist/{skills-eval-W2GGIC4R.js → skills-eval-JPBEBYQU.js} +60 -48
- package/dist/support-MIETYA5E.js +38 -0
- package/dist/{targets-54SWINWB.js → targets-VGNXIR3S.js} +46 -36
- package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WOBR64YA.js} +4 -4
- package/dist/{uninstall-BVLWXKBT.js → uninstall-ZJF5H5ZN.js} +9 -9
- package/dist/{upgrade-JKAR27XC.js → upgrade-FUSUAGHR.js} +31 -28
- package/dist/{usage-MSAWCLX4.js → usage-N4MKVHKD.js} +140 -68
- package/dist/verifiers-YAWOJ3H2.js +336 -0
- package/dist/{verify-X5HDROLA.js → verify-LTDHYBGY.js} +10 -9
- package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-6M7GHTBJ.js} +75 -62
- package/dist/worker/entry.js +67 -57
- 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 +11 -6
- package/docs/built-in-agents.md +26 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +83 -3
- package/docs/configuration-and-targets.md +86 -4
- 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 +74 -10
- 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 +228 -16
- package/docs/git-commit-provenance.md +2 -2
- package/docs/glossary.md +22 -2
- 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 +56 -9
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +4 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +83 -65
- 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 -2
- package/src/cli/argv.ts +5 -0
- package/src/cli/configure.ts +107 -23
- package/src/cli/doctor.ts +5 -1
- package/src/cli/evidence.ts +30 -25
- 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 +111 -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/shared.ts +1 -0
- package/src/cli/targets.ts +4 -1
- package/src/cli/usage.ts +111 -19
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +29 -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/core/path-boundary.ts +100 -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/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +304 -24
- package/src/domains/agents/index.ts +14 -0
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +360 -15
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/config/classify.ts +4 -0
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/active-route-planner.ts +14 -0
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- 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 +48 -0
- package/src/domains/dispatch/delegation-plan.ts +164 -0
- package/src/domains/dispatch/execution-plan.ts +76 -5
- package/src/domains/dispatch/execution-role.ts +12 -2
- package/src/domains/dispatch/execution-scheduler.ts +183 -67
- package/src/domains/dispatch/extension.ts +412 -74
- 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 +791 -0
- package/src/domains/dispatch/gate-role-prompts.ts +47 -0
- package/src/domains/dispatch/host-verification.ts +178 -0
- package/src/domains/dispatch/index.ts +41 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +235 -0
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +9 -4
- package/src/domains/dispatch/state.ts +36 -3
- package/src/domains/dispatch/types.ts +64 -12
- package/src/domains/dispatch/validation.ts +69 -6
- package/src/domains/dispatch/write-boundary-enforcer.ts +45 -0
- package/src/domains/dispatch/write-boundary.ts +201 -22
- package/src/domains/eval/metrics/evidence.ts +79 -2
- package/src/domains/eval/runners/clio-run.ts +12 -2
- package/src/domains/evidence/build.ts +69 -11
- package/src/domains/evidence/index.ts +21 -0
- package/src/domains/evidence/provenance.ts +46 -11
- package/src/domains/evidence/trust-projection.ts +274 -0
- package/src/domains/evidence/trust-status.ts +155 -18
- package/src/domains/evidence/types.ts +4 -0
- package/src/domains/extensions/discovery.ts +88 -1
- package/src/domains/extensions/resources.ts +20 -8
- package/src/domains/extensions/state.ts +6 -2
- package/src/domains/extensions/types.ts +4 -1
- package/src/domains/lifecycle/doctor.ts +140 -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/prompts/contract.ts +3 -5
- package/src/domains/providers/extension.ts +30 -2
- package/src/domains/resources/common-loader.ts +3 -0
- package/src/domains/resources/index.ts +20 -0
- package/src/domains/resources/library.ts +326 -0
- package/src/domains/resources/prompts/loader.ts +119 -14
- package/src/domains/resources/skills/marketplace.ts +37 -12
- package/src/domains/safety/policy-engine.ts +5 -5
- package/src/domains/safety/run-effects.ts +64 -1
- package/src/domains/safety/scope.ts +7 -12
- package/src/domains/session/handoff.ts +629 -0
- package/src/domains/share/archive.ts +67 -2
- package/src/engine/acp/server.ts +4 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/worker-runtime.ts +6 -3
- 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 +311 -17
- 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 +23 -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 +221 -30
- 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 +439 -7
- 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/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +15 -3
- package/src/interactive/watchdog-run.ts +75 -0
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-share.ts +56 -1
- package/src/interactive/worker-stream.ts +15 -0
- package/src/tools/bootstrap.ts +3 -0
- package/src/tools/compete-worktrees.ts +13 -79
- package/src/tools/dispatch-admission.ts +251 -18
- package/src/tools/dispatch-arguments.ts +84 -1
- package/src/tools/dispatch-plan.ts +165 -6
- package/src/tools/dispatch-runner.ts +364 -23
- package/src/tools/dispatch-types.ts +20 -1
- package/src/tools/dispatch.ts +72 -2
- package/src/tools/monitor.ts +29 -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 +21 -14
- package/src/worker/spec-contract.ts +3 -1
- package/dist/chunk-HC4CLZ2Y.js +0 -68
package/src/cli/configure.ts
CHANGED
|
@@ -27,7 +27,12 @@ import { fingerprintNativeRuntime } from "../domains/providers/probe/fingerprint
|
|
|
27
27
|
import { getRuntimeRegistry } from "../domains/providers/registry.js";
|
|
28
28
|
import { registerBuiltinRuntimes } from "../domains/providers/runtimes/builtins.js";
|
|
29
29
|
import { greetLmStudio } from "../domains/providers/runtimes/common/lmstudio-http.js";
|
|
30
|
-
import type {
|
|
30
|
+
import type {
|
|
31
|
+
ProbeContext,
|
|
32
|
+
ProbeModelStatus,
|
|
33
|
+
ProbeResult,
|
|
34
|
+
RuntimeDescriptor,
|
|
35
|
+
} from "../domains/providers/types/runtime-descriptor.js";
|
|
31
36
|
import type { TargetDescriptor } from "../domains/providers/types/target-descriptor.js";
|
|
32
37
|
import { registerClioOAuthProviders } from "../engine/oauth.js";
|
|
33
38
|
import { ask, askYesNo } from "./ask.js";
|
|
@@ -44,7 +49,7 @@ import { createDelayedManualCodeInput } from "./oauth-manual-input.js";
|
|
|
44
49
|
import { promptOAuthSelection } from "./oauth-select.js";
|
|
45
50
|
import { credentialWriteFailed, printError, printOk, printPlaintextCredentialWarning } from "./shared.js";
|
|
46
51
|
import { terminalColumns, wrapPlain } from "./text-layout.js";
|
|
47
|
-
import { validateModelChoice } from "./validate-model.js";
|
|
52
|
+
import { type LiveModelInventory, validateModelChoice } from "./validate-model.js";
|
|
48
53
|
|
|
49
54
|
const HELP = `clio-coder configure
|
|
50
55
|
|
|
@@ -70,7 +75,8 @@ Non-interactive flags:
|
|
|
70
75
|
--agent-profile-model <id> model to use for --agent-profile
|
|
71
76
|
--api-key-env <VAR> read API key from this env var at call time
|
|
72
77
|
--api-key <literal> store API key in credentials.yaml
|
|
73
|
-
--force
|
|
78
|
+
--force save a model outside the runtime catalog, or one the
|
|
79
|
+
target does not advertise, without refusing
|
|
74
80
|
--gateway mark the target as a gateway
|
|
75
81
|
--lifecycle <user-managed|clio-managed>
|
|
76
82
|
resident model lifecycle policy
|
|
@@ -378,19 +384,35 @@ function catalogContextWindowFor(runtime: RuntimeDescriptor, modelId: string): n
|
|
|
378
384
|
return null;
|
|
379
385
|
}
|
|
380
386
|
|
|
381
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Check the resolved model against the catalog where there is one and against
|
|
389
|
+
* the target's own list where there is not. A runtime with no catalog used to
|
|
390
|
+
* pass any string here while the same command wrote the live list that ruled
|
|
391
|
+
* it out into `wireModels`, so the target saved as `ok` and failed on its first
|
|
392
|
+
* turn. Without a catalog or a live list there is nothing to check against, and
|
|
393
|
+
* the note says so rather than implying the id was verified.
|
|
394
|
+
*/
|
|
395
|
+
function validateResolvedModel(
|
|
396
|
+
runtime: RuntimeDescriptor,
|
|
397
|
+
target: Pick<TargetDescriptor, "id" | "url">,
|
|
398
|
+
modelId: string | undefined,
|
|
399
|
+
force: boolean,
|
|
400
|
+
inventory: WireModelInventory,
|
|
401
|
+
): boolean {
|
|
382
402
|
if (!modelId) return true;
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
knownModels: runtimeKnownModelsFor(runtimeId),
|
|
387
|
-
force,
|
|
388
|
-
});
|
|
403
|
+
const knownModels = runtimeKnownModelsFor(runtime.id);
|
|
404
|
+
const live = inventory.source === "probe" ? liveInventoryFor(target, inventory) : undefined;
|
|
405
|
+
const validation = validateModelChoice({ runtimeId: runtime.id, modelId, knownModels, live, force });
|
|
389
406
|
if (!validation.ok) {
|
|
390
407
|
process.stderr.write(`error: ${validation.reason}\n`);
|
|
391
408
|
return false;
|
|
392
409
|
}
|
|
393
410
|
if (validation.warning) process.stderr.write(`warning: ${validation.warning}\n`);
|
|
411
|
+
if (knownModels.length === 0 && live === undefined && runtime.kind === "http") {
|
|
412
|
+
process.stderr.write(
|
|
413
|
+
`warning: could not verify model '${modelId}': ${target.url ?? `target '${target.id}'`} returned no model list; it is saved as written\n`,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
394
416
|
return true;
|
|
395
417
|
}
|
|
396
418
|
|
|
@@ -412,10 +434,15 @@ function validateContextWindowOverride(
|
|
|
412
434
|
return true;
|
|
413
435
|
}
|
|
414
436
|
|
|
415
|
-
async function runtimeProbe(
|
|
437
|
+
async function runtimeProbe(
|
|
438
|
+
runtime: RuntimeDescriptor,
|
|
439
|
+
target: TargetDescriptor,
|
|
440
|
+
authToken?: string,
|
|
441
|
+
): Promise<ProbeResult | null> {
|
|
416
442
|
if (typeof runtime.probe !== "function") return null;
|
|
417
443
|
try {
|
|
418
|
-
|
|
444
|
+
const context = await buildProbeContext(runtime, target);
|
|
445
|
+
return await runtime.probe(target, authToken === undefined ? context : { ...context, authToken });
|
|
419
446
|
} catch (err) {
|
|
420
447
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
421
448
|
}
|
|
@@ -679,17 +706,69 @@ async function loginOAuthRuntime(rl: ReturnType<typeof createInterface>, runtime
|
|
|
679
706
|
}
|
|
680
707
|
}
|
|
681
708
|
|
|
709
|
+
/**
|
|
710
|
+
* The model ids a target can be configured with, and where they came from.
|
|
711
|
+
* `probe` is the only source that says anything about the server in front of
|
|
712
|
+
* us right now; `catalog` is static provider knowledge, and `existing` is the
|
|
713
|
+
* list a previous configure recorded, which the server may no longer match.
|
|
714
|
+
*/
|
|
715
|
+
interface WireModelInventory {
|
|
716
|
+
models: string[];
|
|
717
|
+
source: "catalog" | "probe" | "existing" | "none";
|
|
718
|
+
/** Per-model load state when the probe reported it. */
|
|
719
|
+
modelStates?: Readonly<Record<string, ProbeModelStatus>> | undefined;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Every id the runtime would resolve. LM Studio lists a loaded model under its
|
|
724
|
+
* instance id and keeps the model key only in the state map, and the request
|
|
725
|
+
* path accepts either, so both count as advertised.
|
|
726
|
+
*/
|
|
727
|
+
function advertisedModelIds(inventory: Pick<WireModelInventory, "models" | "modelStates">): string[] {
|
|
728
|
+
const ids = [...inventory.models];
|
|
729
|
+
for (const id of Object.keys(inventory.modelStates ?? {})) if (!ids.includes(id)) ids.push(id);
|
|
730
|
+
return ids;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function residentModelIds(modelStates: WireModelInventory["modelStates"]): string[] {
|
|
734
|
+
return Object.entries(modelStates ?? {})
|
|
735
|
+
.filter(([, status]) => status.state === "loaded" || status.state === "loading")
|
|
736
|
+
.map(([id]) => id);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function liveInventoryFor(
|
|
740
|
+
target: Pick<TargetDescriptor, "id" | "url">,
|
|
741
|
+
inventory: WireModelInventory,
|
|
742
|
+
): LiveModelInventory {
|
|
743
|
+
return {
|
|
744
|
+
targetId: target.id,
|
|
745
|
+
url: target.url,
|
|
746
|
+
models: advertisedModelIds(inventory),
|
|
747
|
+
resident: residentModelIds(inventory.modelStates),
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
|
|
682
751
|
async function resolveSupportedWireModels(
|
|
683
752
|
runtime: RuntimeDescriptor,
|
|
684
753
|
target: TargetDescriptor,
|
|
685
754
|
existing?: TargetDescriptor,
|
|
686
755
|
authToken?: string,
|
|
687
|
-
): Promise<
|
|
756
|
+
): Promise<WireModelInventory> {
|
|
688
757
|
const known = listKnownModelsForRuntime(runtime.id);
|
|
689
|
-
if (known.length > 0) return known;
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
758
|
+
if (known.length > 0) return { models: known, source: "catalog" };
|
|
759
|
+
if (runtime.kind === "http") {
|
|
760
|
+
// The full probe carries load state alongside the ids; a runtime that
|
|
761
|
+
// lists models only through probeModels still gets its ids checked.
|
|
762
|
+
const probe = await runtimeProbe(runtime, target, authToken);
|
|
763
|
+
if (probe?.ok && probe.models && probe.models.length > 0) {
|
|
764
|
+
return { models: [...probe.models], source: "probe", modelStates: probe.modelStates };
|
|
765
|
+
}
|
|
766
|
+
const discovered = await runtimeProbeModels(runtime, target, authToken);
|
|
767
|
+
if (discovered.length > 0) return { models: discovered, source: "probe" };
|
|
768
|
+
}
|
|
769
|
+
if (existing?.wireModels && existing.wireModels.length > 0)
|
|
770
|
+
return { models: [...existing.wireModels], source: "existing" };
|
|
771
|
+
return { models: [], source: "none" };
|
|
693
772
|
}
|
|
694
773
|
|
|
695
774
|
function resolveModelChoice(
|
|
@@ -810,7 +889,8 @@ async function runNonInteractive(runtime: RuntimeDescriptor, args: ParsedArgs):
|
|
|
810
889
|
...(args.reasoning !== undefined ? { reasoning: args.reasoning } : {}),
|
|
811
890
|
...(existing?.lmstudio ? { lmstudio: existing.lmstudio } : {}),
|
|
812
891
|
});
|
|
813
|
-
const
|
|
892
|
+
const inventory = await resolveSupportedWireModels(runtime, seed, existing, args.apiKey);
|
|
893
|
+
const wireModels = inventory.models;
|
|
814
894
|
const model =
|
|
815
895
|
args.model ??
|
|
816
896
|
existing?.defaultModel ??
|
|
@@ -820,7 +900,7 @@ async function runNonInteractive(runtime: RuntimeDescriptor, args: ParsedArgs):
|
|
|
820
900
|
refuseCatalogSeededModel(runtime, support);
|
|
821
901
|
return 2;
|
|
822
902
|
}
|
|
823
|
-
if (!validateResolvedModel(runtime
|
|
903
|
+
if (!validateResolvedModel(runtime, seed, model, args.force, inventory)) return 2;
|
|
824
904
|
if (!validateContextWindowOverride(runtime, model, args.contextWindow, args.force)) return 2;
|
|
825
905
|
const descriptor = buildDescriptor(runtime, args.id, {
|
|
826
906
|
...(url !== undefined ? { url } : {}),
|
|
@@ -1192,7 +1272,10 @@ async function runInteractive(
|
|
|
1192
1272
|
}
|
|
1193
1273
|
|
|
1194
1274
|
let model: string | undefined = defaults.model;
|
|
1195
|
-
let
|
|
1275
|
+
let inventory: WireModelInventory =
|
|
1276
|
+
existing?.wireModels && existing.wireModels.length > 0
|
|
1277
|
+
? { models: [...existing.wireModels], source: "existing" }
|
|
1278
|
+
: { models: [], source: "none" };
|
|
1196
1279
|
const tentative = buildDescriptor(runtime, targetId, {
|
|
1197
1280
|
...(url !== undefined ? { url } : {}),
|
|
1198
1281
|
...(apiKeyEnv !== undefined ? { apiKeyEnv } : {}),
|
|
@@ -1211,8 +1294,9 @@ async function runInteractive(
|
|
|
1211
1294
|
});
|
|
1212
1295
|
|
|
1213
1296
|
if (runtime.kind === "http") {
|
|
1214
|
-
|
|
1297
|
+
inventory = await resolveSupportedWireModels(runtime, tentative, existing ?? undefined);
|
|
1215
1298
|
}
|
|
1299
|
+
const wireModels = inventory.models;
|
|
1216
1300
|
// The wizard shows the whole list, so a catalog-ordered runtime does not need
|
|
1217
1301
|
// --model here the way the non-interactive path does. It does need to stop
|
|
1218
1302
|
// offering the alphabetically first id as though it were the recommended one.
|
|
@@ -1234,11 +1318,11 @@ async function runInteractive(
|
|
|
1234
1318
|
process.stdout.write(" a model is required: enter a number from the list or a model id.\n");
|
|
1235
1319
|
continue;
|
|
1236
1320
|
}
|
|
1237
|
-
if (validateResolvedModel(runtime
|
|
1321
|
+
if (validateResolvedModel(runtime, tentative, model, defaults.force, inventory)) break;
|
|
1238
1322
|
}
|
|
1239
1323
|
} else {
|
|
1240
1324
|
for (;;) {
|
|
1241
|
-
if (model && validateResolvedModel(runtime
|
|
1325
|
+
if (model && validateResolvedModel(runtime, tentative, model, defaults.force, inventory)) break;
|
|
1242
1326
|
const manual = await ask(rl, "Default model id (blank to leave empty)", model ?? "");
|
|
1243
1327
|
if (manual === null) return 0;
|
|
1244
1328
|
model = manual.length > 0 ? manual : undefined;
|
package/src/cli/doctor.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
runDoctor,
|
|
5
5
|
runDoctorFleetChecks,
|
|
6
6
|
runDoctorInteropChecks,
|
|
7
|
+
runDoctorModelChecks,
|
|
7
8
|
runDoctorRuntimeChecks,
|
|
8
9
|
} from "../domains/lifecycle/doctor.js";
|
|
9
10
|
import { printError } from "./shared.js";
|
|
@@ -35,6 +36,9 @@ export async function runDoctorCommand(args: ReadonlyArray<string> = []): Promis
|
|
|
35
36
|
const untouched = !fix && isUninitializedHome();
|
|
36
37
|
const findings = runDoctor({ fix });
|
|
37
38
|
const runtimeChecks = await runDoctorRuntimeChecks();
|
|
39
|
+
// Every model pointer is checked against what its target advertises, so a
|
|
40
|
+
// placeholder id saved by configure is reported here and not on the first turn.
|
|
41
|
+
const modelChecks = await runDoctorModelChecks();
|
|
38
42
|
// The interop and fleet sweeps read the state and config roots through the
|
|
39
43
|
// ensuring accessors, which create them, and there is no fleet or interop
|
|
40
44
|
// state to inspect before Clio has ever written anything. On a home Clio has
|
|
@@ -43,7 +47,7 @@ export async function runDoctorCommand(args: ReadonlyArray<string> = []): Promis
|
|
|
43
47
|
// Fleet preflight probes each configured node over SSH and persists the
|
|
44
48
|
// per-node eligibility verdicts dispatch placement enforces.
|
|
45
49
|
const fleetChecks = untouched ? [] : await runDoctorFleetChecks();
|
|
46
|
-
const all = [...findings, ...runtimeChecks, ...interopChecks, ...fleetChecks];
|
|
50
|
+
const all = [...findings, ...runtimeChecks, ...modelChecks, ...interopChecks, ...fleetChecks];
|
|
47
51
|
const ok = all.every((f) => f.ok);
|
|
48
52
|
if (json) {
|
|
49
53
|
process.stdout.write(`${JSON.stringify({ ok, fix, findings: all }, null, 2)}\n`);
|
package/src/cli/evidence.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { InvalidIdError } from "../core/safe-id.js";
|
|
2
2
|
import { clioDataDir, clioStateDir } from "../core/xdg.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { EvidenceOverview, EvidenceRunProvenance } from "../domains/evidence/index.js";
|
|
4
4
|
import {
|
|
5
5
|
buildEvalEvidence,
|
|
6
6
|
buildEvidence,
|
|
7
7
|
EvidenceNotFoundError,
|
|
8
|
+
formatTrustAxes,
|
|
9
|
+
formatTrustSummary,
|
|
8
10
|
inspectEvidence,
|
|
9
11
|
listEvidenceOverviews,
|
|
10
12
|
loadEvidenceRunProvenance,
|
|
11
13
|
provenanceTranscriptLines,
|
|
12
14
|
} from "../domains/evidence/index.js";
|
|
13
|
-
import { printError, printOk } from "./shared.js";
|
|
15
|
+
import { printError, printNote, printOk } from "./shared.js";
|
|
14
16
|
|
|
15
17
|
const HELP = `clio-coder evidence build --run <runId>
|
|
16
18
|
clio-coder evidence build --session <sessionId>
|
|
@@ -137,15 +139,19 @@ export async function runEvidenceCommand(args: ReadonlyArray<string>): Promise<n
|
|
|
137
139
|
// but the verdict line and exit code must not say ok over a receipt
|
|
138
140
|
// that failed integrity verification.
|
|
139
141
|
const integrityFailures = result.findings.filter((entry) => entry.tag === "receipt-integrity");
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
// A retired seal is the expected state of every run that predates an
|
|
143
|
+
// integrity bump: the receipt is set aside unread, as a missing one
|
|
144
|
+
// is, so it is a note beside the verdict and leaves the exit code alone.
|
|
145
|
+
const retiredSeals = result.findings.filter((entry) => entry.tag === "receipt-retired");
|
|
146
|
+
if (integrityFailures.length > 0) process.stdout.write(`wrote ${result.evidenceId} ${result.directory}\n`);
|
|
147
|
+
else printOk(`wrote ${result.evidenceId} ${result.directory}`);
|
|
148
|
+
for (const entry of retiredSeals) {
|
|
149
|
+
printNote(`${entry.runId !== null ? `run ${entry.runId}: ` : ""}${entry.message}`);
|
|
146
150
|
}
|
|
147
|
-
|
|
148
|
-
|
|
151
|
+
for (const failure of integrityFailures) {
|
|
152
|
+
printError(`${failure.runId !== null ? `run ${failure.runId}: ` : ""}${failure.message}`);
|
|
153
|
+
}
|
|
154
|
+
return integrityFailures.length > 0 ? 1 : 0;
|
|
149
155
|
}
|
|
150
156
|
if (parsed.command === "inspect") {
|
|
151
157
|
const evidenceId = parsed.evidenceId;
|
|
@@ -191,30 +197,29 @@ function renderEvidence(
|
|
|
191
197
|
if (trustStatus.projection === "historical_format") {
|
|
192
198
|
process.stdout.write("trust status: unavailable (historical bundle without canonical projection)\n");
|
|
193
199
|
} else {
|
|
200
|
+
// The compact projection answers the trust questions; the canonical axis
|
|
201
|
+
// line beneath it is the drill-down, in the same form the dispatch tool
|
|
202
|
+
// and monitor print.
|
|
194
203
|
for (const run of trustStatus.runs) {
|
|
195
|
-
process.stdout.write(`trust ${run.runId}: ${
|
|
204
|
+
process.stdout.write(`trust ${run.runId}: ${formatTrustSummary(run.status)}\n`);
|
|
205
|
+
process.stdout.write(` ${formatTrustAxes(run.status)}\n`);
|
|
196
206
|
}
|
|
197
207
|
}
|
|
198
|
-
// Provenance is
|
|
199
|
-
//
|
|
208
|
+
// Provenance is the detail behind the projection, never a second reading
|
|
209
|
+
// of it: each run's block is gated on that run's canonical status, so a
|
|
210
|
+
// receipt the projection withheld (a broken or retired seal) prints no
|
|
211
|
+
// autonomy value here, and a run whose block would be empty prints no
|
|
212
|
+
// header. A historical bundle has no projection and admits no axis detail.
|
|
213
|
+
const statusByRun = new Map(trustStatus.runs.map((entry) => [entry.runId, entry.status]));
|
|
200
214
|
for (const { runId, view } of provenance) {
|
|
215
|
+
const lines = provenanceTranscriptLines(view, statusByRun.get(runId));
|
|
216
|
+
if (lines.length === 0) continue;
|
|
201
217
|
process.stdout.write(`provenance ${runId}:\n`);
|
|
202
|
-
for (const line of
|
|
218
|
+
for (const line of lines) process.stdout.write(` ${line}\n`);
|
|
203
219
|
}
|
|
204
220
|
process.stdout.write(`files: ${formatList(overview.files)}\n`);
|
|
205
221
|
}
|
|
206
222
|
|
|
207
|
-
function formatTrustStatus(status: CanonicalTrustStatus): string {
|
|
208
|
-
return [
|
|
209
|
-
`artifactIntegrity=${status.artifactIntegrity.state}`,
|
|
210
|
-
`validationGrounding=${status.validationGrounding.state}`,
|
|
211
|
-
`independentReview=${status.independentReview.state}`,
|
|
212
|
-
`contextProvenance=${status.contextProvenance.state}`,
|
|
213
|
-
`autonomyEnforcement=${status.autonomyEnforcement.state}`,
|
|
214
|
-
`completionEvidence=${status.completionEvidence.state}`,
|
|
215
|
-
].join(" ");
|
|
216
|
-
}
|
|
217
|
-
|
|
218
223
|
function renderEvidenceList(overviews: ReadonlyArray<EvidenceOverview>): void {
|
|
219
224
|
process.stdout.write(`${overviews.length} evidence artifacts\n`);
|
|
220
225
|
if (overviews.length === 0) return;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { discoverDeclaredProjectCommands } from "../tools/verify/authoring.js";
|
|
4
|
+
|
|
5
|
+
function quoted(value: string): string {
|
|
6
|
+
return JSON.stringify(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function runFleetCommands(args: ReadonlyArray<string>): number {
|
|
10
|
+
if (args.length !== 1 || args[0] !== "init") {
|
|
11
|
+
process.stderr.write("clio-coder fleet: commands: usage: clio-coder fleet commands init\n");
|
|
12
|
+
return 2;
|
|
13
|
+
}
|
|
14
|
+
const destination = join(process.cwd(), ".clio-coder", "fleets", "commands.yaml");
|
|
15
|
+
if (existsSync(destination)) {
|
|
16
|
+
process.stderr.write(`clio-coder fleet: commands init: destination already exists: ${destination}\n`);
|
|
17
|
+
return 2;
|
|
18
|
+
}
|
|
19
|
+
const entries = discoverDeclaredProjectCommands(process.cwd());
|
|
20
|
+
const lines = [
|
|
21
|
+
"# Draft fleet command registry.",
|
|
22
|
+
"# Every entry was discovered from a project declaration. Uncommenting an entry confirms its exact invocation.",
|
|
23
|
+
"# version: 1",
|
|
24
|
+
"# commands:",
|
|
25
|
+
];
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
lines.push(
|
|
28
|
+
`# ${entry.id}:`,
|
|
29
|
+
`# argv: [${entry.command.map(quoted).join(", ")}]`,
|
|
30
|
+
`# description: ${quoted(`Discovered from ${entry.provenance.path}: ${entry.provenance.detail}`)}`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
34
|
+
writeFileSync(destination, `${lines.join("\n")}\n`, { flag: "wx" });
|
|
35
|
+
process.stdout.write(`${destination}\n`);
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { ExecutionPlanStep } from "../domains/dispatch/execution-plan.js";
|
|
2
|
+
import { inspectFleet } from "./fleet-preflight.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The contract-level kind of a compiled step. The execution plan knows only
|
|
6
|
+
* `agent` and `code`; a version 5 `gate` or `plan` step compiles to an agent
|
|
7
|
+
* step carrying `gate` or `plan` facts, and the graph names it as the author
|
|
8
|
+
* wrote it.
|
|
9
|
+
*/
|
|
10
|
+
function contractKind(step: ExecutionPlanStep): "agent" | "code" | "gate" | "plan" {
|
|
11
|
+
if (step.kind === "code") return "code";
|
|
12
|
+
if (step.gate !== undefined) return "gate";
|
|
13
|
+
if (step.plan !== undefined) return "plan";
|
|
14
|
+
return "agent";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function projectedStep(step: ExecutionPlanStep): Record<string, unknown> {
|
|
18
|
+
return {
|
|
19
|
+
id: step.id,
|
|
20
|
+
kind: contractKind(step),
|
|
21
|
+
...(step.kind === "agent" ? { agent: step.agentId } : { command: step.commandId }),
|
|
22
|
+
...(step.kind === "agent" && step.gate !== undefined
|
|
23
|
+
? { gate: { path: step.gate.path, command: step.gate.commandId } }
|
|
24
|
+
: {}),
|
|
25
|
+
...(step.kind === "agent" && step.plan !== undefined
|
|
26
|
+
? { plan: { roster: [...step.plan.roster], maxTasks: step.plan.maxTasks, proposals: step.plan.proposals } }
|
|
27
|
+
: {}),
|
|
28
|
+
...(step.kind === "agent" && step.target !== undefined ? { target: step.target } : {}),
|
|
29
|
+
...(step.kind === "agent" && step.profile !== undefined ? { profile: step.profile } : {}),
|
|
30
|
+
scope: step.scope,
|
|
31
|
+
writes: [...(step.writes ?? [])],
|
|
32
|
+
dependencies: [...step.dependencies],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function runFleetGraph(args: ReadonlyArray<string>): number {
|
|
37
|
+
const json = args.includes("--json");
|
|
38
|
+
const name = args.find((arg) => !arg.startsWith("-"));
|
|
39
|
+
const unknown = args.find((arg, index) => (arg.startsWith("-") && arg !== "--json") || index > 1);
|
|
40
|
+
if (name === undefined || unknown !== undefined) {
|
|
41
|
+
process.stderr.write("clio-coder fleet: graph: usage: clio-coder fleet graph <name> [--json]\n");
|
|
42
|
+
return 2;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const { plan } = inspectFleet(name);
|
|
46
|
+
const byId = new Map(plan.steps.map((step) => [step.id, step]));
|
|
47
|
+
const waves = plan.waves.map((ids, index) => ({
|
|
48
|
+
wave: index + 1,
|
|
49
|
+
steps: ids.flatMap((id) => {
|
|
50
|
+
const step = byId.get(id);
|
|
51
|
+
return step === undefined ? [] : [projectedStep(step)];
|
|
52
|
+
}),
|
|
53
|
+
}));
|
|
54
|
+
const loops = plan.loops.map((loop) => ({
|
|
55
|
+
id: loop.id,
|
|
56
|
+
check: loop.checkStepIds.map((id) => projectedStep(byId.get(id) as ExecutionPlanStep)),
|
|
57
|
+
repair: loop.repairStepIds.map((id) => projectedStep(byId.get(id) as ExecutionPlanStep)),
|
|
58
|
+
}));
|
|
59
|
+
if (json) {
|
|
60
|
+
process.stdout.write(`${JSON.stringify({ fleet: name, planHash: plan.hash, waves, loops }, null, 2)}\n`);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
for (const wave of waves) {
|
|
64
|
+
process.stdout.write(`wave ${wave.wave}\n`);
|
|
65
|
+
for (const step of wave.steps) {
|
|
66
|
+
const subject = step.kind === "code" ? `command=${step.command}` : `agent=${step.agent}`;
|
|
67
|
+
const gate = step.gate as { path: string; command: string } | undefined;
|
|
68
|
+
const plan = step.plan as { roster: string[]; maxTasks: number; proposals: boolean } | undefined;
|
|
69
|
+
const detail =
|
|
70
|
+
gate !== undefined
|
|
71
|
+
? ` gate=${gate.path} run=${gate.command}`
|
|
72
|
+
: plan !== undefined
|
|
73
|
+
? ` roster=${JSON.stringify(plan.roster)} maxTasks=${plan.maxTasks}${plan.proposals ? " proposals" : ""}`
|
|
74
|
+
: "";
|
|
75
|
+
const route =
|
|
76
|
+
step.target !== undefined
|
|
77
|
+
? ` target=${step.target}`
|
|
78
|
+
: step.profile !== undefined
|
|
79
|
+
? ` profile=${step.profile}`
|
|
80
|
+
: "";
|
|
81
|
+
process.stdout.write(
|
|
82
|
+
` ${step.id} kind=${step.kind} ${subject}${route} scope=${step.scope} writes=${JSON.stringify(step.writes)}${detail}\n`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const loop of loops) {
|
|
87
|
+
process.stdout.write(`loop ${loop.id}\n`);
|
|
88
|
+
for (const step of loop.check)
|
|
89
|
+
process.stdout.write(
|
|
90
|
+
` check ${step.id} kind=${step.kind} scope=${step.scope} writes=${JSON.stringify(step.writes)}\n`,
|
|
91
|
+
);
|
|
92
|
+
for (const step of loop.repair)
|
|
93
|
+
process.stdout.write(
|
|
94
|
+
` repair ${step.id} kind=${step.kind} scope=${step.scope} writes=${JSON.stringify(step.writes)}\n`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return 0;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
process.stderr.write(`clio-coder fleet: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { resolvePackageRoot } from "../core/package-root.js";
|
|
4
|
+
|
|
5
|
+
const BUILTINS = ["build-review", "build-test", "sdlc"] as const;
|
|
6
|
+
const SAFE_STEM = /^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/u;
|
|
7
|
+
|
|
8
|
+
function usage(message: string): number {
|
|
9
|
+
process.stderr.write(`clio-coder fleet: new: ${message}\n`);
|
|
10
|
+
return 2;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function runFleetNew(args: ReadonlyArray<string>): number {
|
|
14
|
+
const name = args[0];
|
|
15
|
+
const fromIndex = args.indexOf("--from");
|
|
16
|
+
const builtin = fromIndex === -1 ? undefined : args[fromIndex + 1];
|
|
17
|
+
const known = BUILTINS.join(", ");
|
|
18
|
+
if (name === undefined) return usage("usage: clio-coder fleet new <name> --from <builtin>");
|
|
19
|
+
if (!SAFE_STEM.test(name)) return usage(`'${name}' is not a safe file stem`);
|
|
20
|
+
if (fromIndex === -1 || builtin === undefined) return usage("--from <builtin> is required");
|
|
21
|
+
const expected = new Set([name, "--from", builtin]);
|
|
22
|
+
const unknown = args.find((arg) => !expected.has(arg));
|
|
23
|
+
if (unknown !== undefined) return usage(`unknown argument: ${unknown}`);
|
|
24
|
+
if (!(BUILTINS as ReadonlyArray<string>).includes(builtin)) {
|
|
25
|
+
return usage(`unknown builtin '${builtin}'. Known builtins: ${known}`);
|
|
26
|
+
}
|
|
27
|
+
const destination = join(process.cwd(), ".clio-coder", "fleets", `${name}.md`);
|
|
28
|
+
if (existsSync(destination)) return usage(`destination already exists: ${destination}`);
|
|
29
|
+
const source = join(resolvePackageRoot(), "src", "domains", "agents", "fleets", `${builtin}.md`);
|
|
30
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
31
|
+
const sourceText = readFileSync(source, "utf8");
|
|
32
|
+
const renamed = sourceText.replace(/^name:\s*[^\n]+$/mu, `name: ${name}`);
|
|
33
|
+
writeFileSync(destination, renamed, { flag: "wx" });
|
|
34
|
+
process.stdout.write(`${destination}\n`);
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { readSettings } from "../core/config.js";
|
|
2
|
+
import {
|
|
3
|
+
type FleetCommandRegistry,
|
|
4
|
+
type FleetContract,
|
|
5
|
+
loadFleetCommands,
|
|
6
|
+
loadFleetContract,
|
|
7
|
+
renderFleetPrompt,
|
|
8
|
+
} from "../domains/agents/index.js";
|
|
9
|
+
import { discoverAgentRecipes } from "../domains/agents/registry.js";
|
|
10
|
+
import { type AgentSpec, normalizeAgentSpec } from "../domains/agents/spec.js";
|
|
11
|
+
import type { ExecutionPlan } from "../domains/dispatch/execution-plan.js";
|
|
12
|
+
import { requestExecutionRole, withAttemptRole } from "../domains/dispatch/execution-role.js";
|
|
13
|
+
import { compileFleetExecutionPlan } from "../domains/dispatch/fleet-plan.js";
|
|
14
|
+
|
|
15
|
+
export interface FleetPreflightResult {
|
|
16
|
+
contract: FleetContract;
|
|
17
|
+
commands: FleetCommandRegistry | null;
|
|
18
|
+
plan: ExecutionPlan;
|
|
19
|
+
checks: ReadonlyArray<{ check: "parse" | "graph" | "commands" | "agents" | "plan"; summary: string }>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function discoverSpecs(cwd: string): AgentSpec[] {
|
|
23
|
+
return discoverAgentRecipes(cwd).map(normalizeAgentSpec);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function inspectFleet(name: string, vars?: Readonly<Record<string, string>>): FleetPreflightResult {
|
|
27
|
+
const cwd = process.cwd();
|
|
28
|
+
const contract = loadFleetContract(cwd, name);
|
|
29
|
+
const commands = loadFleetCommands(cwd);
|
|
30
|
+
const prompt = vars === undefined ? contract.body : renderFleetPrompt(contract.body, vars);
|
|
31
|
+
const specs = discoverSpecs(cwd);
|
|
32
|
+
const byId = new Map(specs.map((spec) => [spec.id, spec]));
|
|
33
|
+
const resolved = new Set<string>();
|
|
34
|
+
const settings = readSettings();
|
|
35
|
+
const targetIds = new Set(settings.targets.map((target) => target.id));
|
|
36
|
+
const profileIds = new Set(Object.keys(settings.workers?.profiles ?? {}));
|
|
37
|
+
for (const step of contract.steps) {
|
|
38
|
+
const positions =
|
|
39
|
+
step.kind === "loop"
|
|
40
|
+
? [
|
|
41
|
+
...(step.check.kind === "agent" ? [{ id: `${step.id}.check`, route: step.check }] : []),
|
|
42
|
+
{ id: `${step.id}.repair`, route: step.repair },
|
|
43
|
+
]
|
|
44
|
+
: step.kind === "agent" || step.kind === "gate" || step.kind === "plan"
|
|
45
|
+
? [{ id: step.id, route: step }]
|
|
46
|
+
: [];
|
|
47
|
+
for (const position of positions) {
|
|
48
|
+
if (position.route.target !== undefined && !targetIds.has(position.route.target)) {
|
|
49
|
+
throw new Error(`unknown target '${position.route.target}' at step '${position.id}'`);
|
|
50
|
+
}
|
|
51
|
+
if (position.route.profile !== undefined && !profileIds.has(position.route.profile)) {
|
|
52
|
+
throw new Error(`unknown profile '${position.route.profile}' at step '${position.id}'`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const plan = compileFleetExecutionPlan({
|
|
57
|
+
contract,
|
|
58
|
+
task: prompt,
|
|
59
|
+
resolveAgent(context) {
|
|
60
|
+
const spec = byId.get(context.agentId);
|
|
61
|
+
if (spec === undefined) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`unknown agent '${context.agentId}' (step '${context.stepId}' must name a recipe from 'clio-coder agents')`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (spec.capabilityClass === "orchestration" || spec.capabilityClass === "internal") {
|
|
67
|
+
throw new Error(`fleet step '${context.stepId}' has no automatable agent authority`);
|
|
68
|
+
}
|
|
69
|
+
resolved.add(spec.id);
|
|
70
|
+
const role = requestExecutionRole({
|
|
71
|
+
agentId: spec.id,
|
|
72
|
+
resolveFacts: (id) => {
|
|
73
|
+
const found = byId.get(id);
|
|
74
|
+
return found === undefined
|
|
75
|
+
? null
|
|
76
|
+
: {
|
|
77
|
+
category: found.category,
|
|
78
|
+
capabilityClass: found.capabilityClass,
|
|
79
|
+
resultContractKind: found.resultContract.kind,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
...(context.gateRole === undefined ? {} : { gateRole: context.gateRole }),
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
requestedAuthority: spec.capabilityClass,
|
|
86
|
+
approvedAuthority: spec.capabilityClass,
|
|
87
|
+
expectedResultContract:
|
|
88
|
+
context.planRole === true
|
|
89
|
+
? "delegation-plan"
|
|
90
|
+
: context.gateAuthorRole === true
|
|
91
|
+
? "artifact-report"
|
|
92
|
+
: context.gateRole === "reviewer"
|
|
93
|
+
? "verifier-report"
|
|
94
|
+
: spec.resultContract.kind,
|
|
95
|
+
executionRole: withAttemptRole(role, context.attempt),
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
contract,
|
|
101
|
+
commands,
|
|
102
|
+
plan,
|
|
103
|
+
checks: [
|
|
104
|
+
{ check: "parse", summary: `parsed contract version ${contract.version}` },
|
|
105
|
+
{ check: "graph", summary: `validated ${contract.steps.length} declared steps` },
|
|
106
|
+
{ check: "commands", summary: `validated ${plan.steps.filter((step) => step.kind === "code").length} code steps` },
|
|
107
|
+
{ check: "agents", summary: `resolved ${resolved.size} agents` },
|
|
108
|
+
{ check: "plan", summary: `compiled ${plan.steps.length} plan steps with hash ${plan.hash}` },
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { FLEET_COMMANDS_REMEDY, FleetCommandRegistryMissingError } from "../domains/agents/index.js";
|
|
2
|
+
import { preflightWriteBoundaries } from "../domains/dispatch/write-boundary-enforcer.js";
|
|
3
|
+
import { inspectFleet } from "./fleet-preflight.js";
|
|
4
|
+
|
|
5
|
+
export function runFleetValidate(args: ReadonlyArray<string>): number {
|
|
6
|
+
const json = args.includes("--json");
|
|
7
|
+
const unknown = args.find((arg, index) => (arg.startsWith("-") && arg !== "--json") || index > 1);
|
|
8
|
+
const name = args.find((arg) => !arg.startsWith("-"));
|
|
9
|
+
if (name === undefined || unknown !== undefined) {
|
|
10
|
+
process.stderr.write("clio-coder fleet: validate: usage: clio-coder fleet validate <name> [--json]\n");
|
|
11
|
+
return 2;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const result = inspectFleet(name);
|
|
15
|
+
preflightWriteBoundaries(result.plan, process.cwd());
|
|
16
|
+
if (json)
|
|
17
|
+
process.stdout.write(
|
|
18
|
+
`${JSON.stringify({ valid: true, fleet: name, checks: result.checks, planHash: result.plan.hash }, null, 2)}\n`,
|
|
19
|
+
);
|
|
20
|
+
else for (const check of result.checks) process.stdout.write(`${check.check}: ${check.summary}\n`);
|
|
21
|
+
return 0;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
24
|
+
const message = raw.startsWith("unknown agent '") ? `preflight failed: ${raw}` : raw;
|
|
25
|
+
const diagnostics = [message, ...(error instanceof FleetCommandRegistryMissingError ? [FLEET_COMMANDS_REMEDY] : [])];
|
|
26
|
+
if (json) process.stdout.write(`${JSON.stringify({ valid: false, fleet: name, diagnostics }, null, 2)}\n`);
|
|
27
|
+
else process.stderr.write(`clio-coder fleet: ${diagnostics.join("\n ")}\n`);
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
}
|