@iowarp/clio-coder 0.3.7 → 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 +31 -0
- package/README.md +7 -2
- package/dist/{acp-SK4MD6MM.js → acp-U67UHUK2.js} +13 -13
- package/dist/{agents-2FN2K6ME.js → agents-YU6SGALZ.js} +35 -33
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-QIYZWM5I.js → auth-5ZPJOIVG.js} +32 -24
- package/dist/builtins-C6JMZVV6.js +17 -0
- package/dist/{chunk-YD734TPH.js → chunk-26LEYJZH.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-2HEJ2F35.js} +5 -5
- package/dist/{chunk-WJHBC77E.js → chunk-2HFZQUHL.js} +5 -5
- package/dist/{chunk-FO5ZOVUY.js → chunk-3BINW3FP.js} +5 -5
- package/dist/{chunk-EOOQZZDE.js → chunk-4SPRNWDE.js} +18 -18
- package/dist/{chunk-EBEFWSGL.js → chunk-5DHKRSMQ.js} +7 -7
- package/dist/{chunk-OBMAI2DP.js → chunk-5H3GB5BO.js} +11 -9
- package/dist/{chunk-JTSEDYVQ.js → chunk-5Q2VVUKB.js} +7 -7
- package/dist/{chunk-VQNODYQ4.js → chunk-7RFXX52T.js} +294 -3704
- package/dist/{chunk-XEGB6BCN.js → chunk-7RGZWPB6.js} +5 -5
- package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-CEYBNUGC.js → chunk-A3WNZD3P.js} +323 -27
- package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
- package/dist/{chunk-2SFS6XQE.js → chunk-DGSYXYMX.js} +3 -2
- package/dist/{chunk-ZWLZP4ZT.js → chunk-DYIM5TJT.js} +98 -12
- package/dist/{chunk-KCMKRQX4.js → chunk-E77JEWSD.js} +42 -49
- package/dist/{chunk-J7PIKKWC.js → chunk-EMYUUSFG.js} +7 -7
- package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
- package/dist/{chunk-UVDSQ6LW.js → chunk-FBVTI2TJ.js} +74 -28
- package/dist/{chunk-BMWK7ZIZ.js → chunk-FHJEP5SW.js} +13 -13
- package/dist/{chunk-FOT2FX5J.js → chunk-GN57SG4G.js} +7 -7
- package/dist/{chunk-SROCI7ZU.js → chunk-GPIEI3LY.js} +5 -5
- package/dist/{chunk-UFQ3F4FW.js → chunk-GU2UIAFZ.js} +4 -4
- package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
- package/dist/{chunk-VREKEFLL.js → chunk-HLE42MG7.js} +3 -3
- package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-LADCF22A.js → chunk-IGWKHNIQ.js} +113 -54
- package/dist/chunk-IIZWH4XA.js +172 -0
- package/dist/{chunk-UHXRNZ2J.js → chunk-IJ7RPIYJ.js} +4 -4
- package/dist/{chunk-M4AKACEO.js → chunk-J3YUBZWY.js} +2 -2
- package/dist/{chunk-AB4XIIVB.js → chunk-JOZYP4GM.js} +6 -6
- package/dist/{chunk-ZGH7FGS5.js → chunk-K4XHGFR5.js} +742 -12
- package/dist/{chunk-EELBMBT6.js → chunk-KTYTFRMB.js} +62 -5
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/{chunk-DQA7QLMD.js → chunk-ME6CCNFO.js} +9 -24
- package/dist/{chunk-KZ2H5X4G.js → chunk-MXKJU4JB.js} +88 -14
- package/dist/{chunk-DMD2AGVS.js → chunk-N22QMJKY.js} +20 -18
- package/dist/chunk-NMPKI6XL.js +3006 -0
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-5C77SEEY.js → chunk-P43ETTHK.js} +2 -2
- package/dist/{chunk-JNXPYBB4.js → chunk-PMDBGQSJ.js} +2 -2
- package/dist/chunk-PT7HYKEM.js +165 -0
- package/dist/{chunk-GEYXPTRF.js → chunk-RWSI4YD7.js} +2 -2
- package/dist/{chunk-C4JBQ5SR.js → chunk-TANS5ZJS.js} +6 -6
- package/dist/{chunk-YTYFXUI3.js → chunk-TB5666IT.js} +9 -9
- package/dist/{chunk-UUANF5CR.js → chunk-TLQJPP24.js} +375 -687
- package/dist/{chunk-OB5HIGJY.js → chunk-TT36MB5S.js} +2 -1
- package/dist/{chunk-DJNLUABN.js → chunk-TTHACPOM.js} +135 -17
- package/dist/{chunk-6M7VS3J3.js → chunk-TYPGUK6W.js} +4 -4
- package/dist/{chunk-THKY7CD7.js → chunk-U6MBIEMB.js} +108 -24
- package/dist/{chunk-PD3MESLB.js → chunk-VAWNZU7Z.js} +4 -4
- package/dist/{chunk-IR4CFBFN.js → chunk-VCBR6CU7.js} +12 -12
- package/dist/{chunk-WHJYKASB.js → chunk-VHN4MY6O.js} +2 -2
- package/dist/{chunk-MXI6J5JF.js → chunk-VWZOAB7K.js} +10 -10
- package/dist/{chunk-QCTRSGHQ.js → chunk-WLFILSD5.js} +36 -36
- package/dist/{chunk-5UJ6ECTS.js → chunk-WNIJTQQK.js} +6 -6
- package/dist/{chunk-4DWFMQDR.js → chunk-WSB3FPX7.js} +68 -20
- package/dist/{chunk-3HAPLH5M.js → chunk-WWCZ5F23.js} +112 -10
- package/dist/{chunk-X2KV5FXT.js → chunk-WXY7KU3G.js} +2 -2
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-DOOEX22V.js → chunk-XWSF374K.js} +4 -4
- package/dist/{chunk-6TUKSZVF.js → chunk-YS5VLNH5.js} +8 -8
- 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 +31 -31
- package/dist/{clio-WBVQEBKO.js → clio-QVTYJ57A.js} +8 -8
- package/dist/code-nav-FGGFIE7L.js +2 -2
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
- package/dist/{config-TRBL3RCF.js → config-LW5IJFQN.js} +58 -53
- package/dist/{configure-OLCVPHNM.js → configure-7XIZCOU4.js} +26 -21
- package/dist/{context-XEWE3MOJ.js → context-L3WL3X7K.js} +47 -41
- package/dist/{context-WFPKQSM6.js → context-N52ZA626.js} +22 -22
- package/dist/{context-MJIJ6GOX.js → context-Y6Y7QPR6.js} +12 -12
- package/dist/{context-clear-KNOS2JPB.js → context-clear-MBQRLSDQ.js} +47 -41
- package/dist/{context-index-SSR5ECNE.js → context-index-HVMFQHK3.js} +5 -5
- package/dist/{context-working-set-EUXAZI6N.js → context-working-set-GS6DSO7F.js} +14 -14
- package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-22ZCNOM3.js} +83 -55
- package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
- package/dist/{doctor-RN4YKO2X.js → doctor-M7YEDGAE.js} +24 -20
- package/dist/{eval-RUBJVSNQ.js → eval-BEC2WHDA.js} +71 -19
- package/dist/{evidence-JZNBUOQZ.js → evidence-REJUMSKM.js} +62 -56
- package/dist/{evolve-FJVC4KKI.js → evolve-PY5ZBA5K.js} +41 -35
- package/dist/{extensions-IQL36S7K.js → extensions-HVKU65YU.js} +6 -6
- package/dist/{fleet-BDKYJFCP.js → fleet-7WZEWRFA.js} +72 -67
- package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-UVHWM76J.js} +10 -10
- package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-6ULH7PES.js} +13 -13
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-J53T6CCE.js} +5 -5
- package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-72PC4SLA.js} +17 -17
- package/dist/{init-LQUB5COQ.js → init-OG3TPGQG.js} +61 -56
- package/dist/{library-NJAHIGG4.js → library-CNTMPLRF.js} +18 -18
- package/dist/{memory-OG6HOYKM.js → memory-6IS7F275.js} +43 -37
- package/dist/{models-5ZG5XY7J.js → models-ENRJDA5W.js} +33 -28
- package/dist/{monitor-TJ7AMTGB.js → monitor-XLDVO7TN.js} +56 -38
- package/dist/{orchestrator-WZYB54DM.js → orchestrator-6KSPYRHA.js} +421 -303
- package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-PXQT45IY.js → reset-RZ4ER727.js} +10 -10
- package/dist/{run-FQ74YF62.js → run-Y2CNK5RU.js} +79 -74
- package/dist/{share-FW7SVCL3.js → share-A55GYP6Z.js} +16 -16
- package/dist/{skills-7E7IRB3R.js → skills-ALC5J6AT.js} +19 -19
- package/dist/{skills-eval-LI75W6OK.js → skills-eval-JPBEBYQU.js} +52 -47
- package/dist/support-MIETYA5E.js +38 -0
- package/dist/{targets-4CIFKCTW.js → targets-VGNXIR3S.js} +42 -33
- package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-WOBR64YA.js} +3 -3
- package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
- package/dist/{upgrade-K2HVIVMQ.js → upgrade-FUSUAGHR.js} +27 -25
- package/dist/{usage-GTZELZQX.js → usage-N4MKVHKD.js} +51 -46
- package/dist/{verifiers-RLAHT27O.js → verifiers-YAWOJ3H2.js} +13 -13
- package/dist/{verify-BX3BRKH5.js → verify-LTDHYBGY.js} +9 -9
- package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-6M7GHTBJ.js} +66 -61
- package/dist/worker/entry.js +68 -61
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +6 -5
- package/docs/built-in-agents.md +1 -1
- package/docs/commands-and-modes.md +2 -2
- package/docs/configuration-and-targets.md +8 -3
- package/docs/context-engine.md +1 -1
- package/docs/documentation-guide.md +1 -1
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +70 -6
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +1 -1
- package/docs/fleet-dispatch.md +34 -9
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +1 -1
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +2 -2
- 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 +41 -38
- package/docs/safety-model.md +1 -1
- package/docs/scientific-validation.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +1 -1
- package/docs/trace-store.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- 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-preflight.ts +2 -12
- package/src/cli/shared.ts +1 -0
- package/src/cli/targets.ts +4 -1
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +25 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/path-boundary.ts +100 -0
- package/src/domains/agents/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +30 -12
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +128 -17
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- package/src/domains/dispatch/contract.ts +15 -1
- package/src/domains/dispatch/delegation-plan.ts +2 -5
- package/src/domains/dispatch/execution-role.ts +9 -1
- package/src/domains/dispatch/extension.ts +156 -67
- package/src/domains/dispatch/fleet-run.ts +57 -3
- package/src/domains/dispatch/gate-role-prompts.ts +38 -0
- package/src/domains/dispatch/index.ts +3 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +84 -8
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +2 -1
- package/src/domains/dispatch/types.ts +14 -7
- package/src/domains/dispatch/validation.ts +6 -3
- 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 +145 -17
- 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/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/prompts/loader.ts +119 -14
- 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/engine/acp/server.ts +4 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/worker-runtime.ts +6 -3
- package/src/interactive/dispatch-board.ts +53 -3
- package/src/interactive/interactive-event-projection.ts +14 -0
- package/src/interactive/overlays/settings.ts +141 -47
- package/src/interactive/slash-commands.ts +7 -2
- package/src/interactive/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +15 -3
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-stream.ts +8 -0
- package/src/tools/dispatch-admission.ts +12 -13
- package/src/tools/dispatch-arguments.ts +27 -0
- package/src/tools/dispatch-plan.ts +29 -0
- package/src/tools/dispatch-runner.ts +48 -13
- package/src/tools/monitor.ts +13 -0
- package/src/tools/worker-evidence.ts +19 -13
- package/src/worker/spec-contract.ts +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Worker Dispatch Mechanics
|
|
2
2
|
|
|
3
3
|
> [!TIP]
|
|
4
|
-
> **Interactive Spec Available:** An interactive NDJSON protocol timeline stream and heartbeat watchdog simulator is located at [docs/html/worker_dispatch_blueprint.html](html/worker_dispatch_blueprint.html) (Version: 0.3.
|
|
4
|
+
> **Interactive Spec Available:** An interactive NDJSON protocol timeline stream and heartbeat watchdog simulator is located at [docs/html/worker_dispatch_blueprint.html](html/worker_dispatch_blueprint.html) (Version: 0.3.8).
|
|
5
5
|
|
|
6
6
|
This document describes the design and lifecycle of Clio Coder dispatched workers, focusing on the spawning sequence, execution isolation, the standard input/output NDJSON communication loop, and permission escalation routing.
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iowarp/clio-coder",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "Coding agent for HPC and scientific-software developers, part of IOWarp's CLIO ecosystem of agentic science.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -107,7 +107,6 @@
|
|
|
107
107
|
},
|
|
108
108
|
"overrides": {
|
|
109
109
|
"@anthropic-ai/sdk": "0.105.0",
|
|
110
|
-
"ip-address": "10.2.0",
|
|
111
110
|
"esbuild": "0.28.1"
|
|
112
111
|
},
|
|
113
112
|
"devDependencies": {
|
package/src/cli/argv.ts
CHANGED
|
@@ -15,6 +15,11 @@ export function printError(message: string, detail?: string): void {
|
|
|
15
15
|
if (detail) process.stderr.write(` ${detail}\n`);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/** An expected condition worth a line on stderr that is neither an error nor a success. */
|
|
19
|
+
export function printNote(message: string): void {
|
|
20
|
+
process.stderr.write(`${chalk.yellow("note:")} ${message}\n`);
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
export function printOk(message: string): void {
|
|
19
24
|
process.stdout.write(`${chalk.green("ok:")} ${message}\n`);
|
|
20
25
|
}
|
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;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
1
|
import { readSettings } from "../core/config.js";
|
|
3
|
-
import { resolvePackageRoot } from "../core/package-root.js";
|
|
4
|
-
import { clioConfigDir } from "../core/xdg.js";
|
|
5
2
|
import {
|
|
6
3
|
type FleetCommandRegistry,
|
|
7
4
|
type FleetContract,
|
|
@@ -9,7 +6,7 @@ import {
|
|
|
9
6
|
loadFleetContract,
|
|
10
7
|
renderFleetPrompt,
|
|
11
8
|
} from "../domains/agents/index.js";
|
|
12
|
-
import {
|
|
9
|
+
import { discoverAgentRecipes } from "../domains/agents/registry.js";
|
|
13
10
|
import { type AgentSpec, normalizeAgentSpec } from "../domains/agents/spec.js";
|
|
14
11
|
import type { ExecutionPlan } from "../domains/dispatch/execution-plan.js";
|
|
15
12
|
import { requestExecutionRole, withAttemptRole } from "../domains/dispatch/execution-role.js";
|
|
@@ -23,14 +20,7 @@ export interface FleetPreflightResult {
|
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
function discoverSpecs(cwd: string): AgentSpec[] {
|
|
26
|
-
|
|
27
|
-
const builtin = loadRecipesFromDir(
|
|
28
|
-
{ dir: join(resolvePackageRoot(), "src", "domains", "agents", "builtins"), source: "builtin" },
|
|
29
|
-
diagnostics,
|
|
30
|
-
);
|
|
31
|
-
const user = loadRecipesFromDir({ dir: join(clioConfigDir(), "agents"), source: "user" }, diagnostics);
|
|
32
|
-
const project = loadRecipesFromDir({ dir: join(cwd, ".clio-coder", "agents"), source: "project" }, diagnostics);
|
|
33
|
-
return mergeRecipes(builtin, user, project).map(normalizeAgentSpec);
|
|
23
|
+
return discoverAgentRecipes(cwd).map(normalizeAgentSpec);
|
|
34
24
|
}
|
|
35
25
|
|
|
36
26
|
export function inspectFleet(name: string, vars?: Readonly<Record<string, string>>): FleetPreflightResult {
|
package/src/cli/shared.ts
CHANGED
package/src/cli/targets.ts
CHANGED
|
@@ -953,7 +953,7 @@ function renderTable(providers: ProvidersContract, entries: ReadonlyArray<Target
|
|
|
953
953
|
}
|
|
954
954
|
}
|
|
955
955
|
|
|
956
|
-
function targetTableRow(providers: ProvidersContract, status: TargetStatus): TargetTableRow {
|
|
956
|
+
export function targetTableRow(providers: ProvidersContract, status: TargetStatus): TargetTableRow {
|
|
957
957
|
return {
|
|
958
958
|
id: status.target.id,
|
|
959
959
|
tier: statusTier(status),
|
|
@@ -1051,6 +1051,9 @@ function formatNotes(status: TargetStatus): string {
|
|
|
1051
1051
|
if (status.runtime?.auth === "claude-cli") parts.push("claude-cli");
|
|
1052
1052
|
if (status.capabilities.contextWindow > 0) parts.push(formatContextWindow(status));
|
|
1053
1053
|
if (!status.available && status.reason) parts.push(status.reason);
|
|
1054
|
+
// A reachable target that cannot serve its own default is degraded, and the
|
|
1055
|
+
// row has to say why or the model column reads as a model that works.
|
|
1056
|
+
if (status.health.status === "degraded" && status.health.lastError) parts.push(status.health.lastError);
|
|
1054
1057
|
const residency = residentModelsSummary(status.discoveredModelStates);
|
|
1055
1058
|
if (residency) parts.push(residency);
|
|
1056
1059
|
if (status.probeNotes && status.probeNotes.length > 0) parts.push(`note: ${status.probeNotes.join("; ")}`);
|
|
@@ -1,7 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a target advertised when it was asked, for runtimes with no static
|
|
3
|
+
* catalog. `models` is the id list the probe returned and `resident` the
|
|
4
|
+
* subset the server reports as loaded, when it reports load state at all.
|
|
5
|
+
*/
|
|
6
|
+
export interface LiveModelInventory {
|
|
7
|
+
targetId: string;
|
|
8
|
+
url?: string | undefined;
|
|
9
|
+
models: ReadonlyArray<string>;
|
|
10
|
+
resident: ReadonlyArray<string>;
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
export interface ValidateModelInput {
|
|
2
14
|
runtimeId: string;
|
|
3
15
|
modelId: string;
|
|
4
16
|
knownModels: ReadonlyArray<string>;
|
|
17
|
+
/**
|
|
18
|
+
* The live list, when one was fetched. It is consulted only when the static
|
|
19
|
+
* catalog is empty, so the catalog stays the authority for cloud runtimes
|
|
20
|
+
* and the probe becomes the authority where there is nothing else.
|
|
21
|
+
*/
|
|
22
|
+
live?: LiveModelInventory | undefined;
|
|
5
23
|
force: boolean;
|
|
6
24
|
}
|
|
7
25
|
|
|
@@ -9,9 +27,30 @@ export type ValidateModelResult =
|
|
|
9
27
|
| { ok: true; warning?: string }
|
|
10
28
|
| { ok: false; reason: string; knownModels: ReadonlyArray<string> };
|
|
11
29
|
|
|
30
|
+
const SAMPLE_SIZE = 10;
|
|
31
|
+
|
|
32
|
+
function sample(models: ReadonlyArray<string>): string {
|
|
33
|
+
const shown = models.slice(0, SAMPLE_SIZE).join(", ");
|
|
34
|
+
return models.length > SAMPLE_SIZE ? `${shown}, …` : shown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function describeLiveTarget(live: LiveModelInventory): string {
|
|
38
|
+
return live.url ? `target '${live.targetId}' at ${live.url}` : `target '${live.targetId}'`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The refusal `configure` prints for a model the live target does not advertise. */
|
|
42
|
+
export function formatUnadvertisedModelReason(modelId: string, live: LiveModelInventory): string {
|
|
43
|
+
const resident = live.resident.length > 0 ? live.resident.join(", ") : "none";
|
|
44
|
+
return (
|
|
45
|
+
`${describeLiveTarget(live)} does not advertise model '${modelId}'. ` +
|
|
46
|
+
`Advertised (${live.models.length}): ${sample(live.models)}. Resident instances: ${resident}. ` +
|
|
47
|
+
"Pass one of the advertised ids with --model, or --force to save a model the target cannot serve."
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
12
51
|
export function validateModelChoice(input: ValidateModelInput): ValidateModelResult {
|
|
13
|
-
const { runtimeId, modelId, knownModels, force } = input;
|
|
14
|
-
if (knownModels.length === 0) return
|
|
52
|
+
const { runtimeId, modelId, knownModels, live, force } = input;
|
|
53
|
+
if (knownModels.length === 0) return validateAgainstLive(modelId, live, force);
|
|
15
54
|
if (knownModels.includes(modelId)) return { ok: true };
|
|
16
55
|
if (force) {
|
|
17
56
|
return {
|
|
@@ -19,10 +58,26 @@ export function validateModelChoice(input: ValidateModelInput): ValidateModelRes
|
|
|
19
58
|
warning: `model '${modelId}' not in ${runtimeId} catalog (use without --force to reject)`,
|
|
20
59
|
};
|
|
21
60
|
}
|
|
22
|
-
const sample = knownModels.slice(0, 10).join(", ");
|
|
23
61
|
return {
|
|
24
62
|
ok: false,
|
|
25
|
-
reason: `model '${modelId}' not in ${runtimeId} catalog. Known: ${sample}. Use --force to skip validation.`,
|
|
26
|
-
knownModels: knownModels.slice(0,
|
|
63
|
+
reason: `model '${modelId}' not in ${runtimeId} catalog. Known: ${sample(knownModels)}. Use --force to skip validation.`,
|
|
64
|
+
knownModels: knownModels.slice(0, SAMPLE_SIZE),
|
|
27
65
|
};
|
|
28
66
|
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* With no catalog, the live list is the only evidence there is. An empty
|
|
70
|
+
* catalog used to be an early `ok` on its own, which is how a placeholder id
|
|
71
|
+
* was written next to the very list that ruled it out.
|
|
72
|
+
*/
|
|
73
|
+
function validateAgainstLive(
|
|
74
|
+
modelId: string,
|
|
75
|
+
live: LiveModelInventory | undefined,
|
|
76
|
+
force: boolean,
|
|
77
|
+
): ValidateModelResult {
|
|
78
|
+
if (!live || live.models.length === 0) return { ok: true };
|
|
79
|
+
if (live.models.includes(modelId)) return { ok: true };
|
|
80
|
+
const reason = formatUnadvertisedModelReason(modelId, live);
|
|
81
|
+
if (force) return { ok: true, warning: `${reason.replace(/ Pass one of.*$/u, "")} Saved anyway because of --force.` };
|
|
82
|
+
return { ok: false, reason, knownModels: live.models.slice(0, SAMPLE_SIZE) };
|
|
83
|
+
}
|
package/src/core/bus-events.ts
CHANGED
|
@@ -51,6 +51,7 @@ export const BusChannels = {
|
|
|
51
51
|
ToolBudgetExceeded: "safety.toolBudgetExceeded",
|
|
52
52
|
ProviderHealth: "provider.health",
|
|
53
53
|
RuntimeNotice: "runtime.notice",
|
|
54
|
+
DispatchScopeNotice: "dispatch.scopeNotice",
|
|
54
55
|
DispatchEnqueued: "dispatch.enqueued",
|
|
55
56
|
DispatchStarted: "dispatch.started",
|
|
56
57
|
DispatchProgress: "dispatch.progress",
|
|
@@ -108,6 +109,29 @@ export interface ContextWarningPayload {
|
|
|
108
109
|
warning: string | null;
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
/** A dispatch path-scope decision that must remain visible in the operator transcript. */
|
|
113
|
+
export type DispatchScopeNoticePayload =
|
|
114
|
+
| {
|
|
115
|
+
code: "typed_scope_replaced_inferred_paths";
|
|
116
|
+
level: "warning";
|
|
117
|
+
agentId: string;
|
|
118
|
+
omittedPaths: ReadonlyArray<string>;
|
|
119
|
+
message: string;
|
|
120
|
+
}
|
|
121
|
+
| {
|
|
122
|
+
code: "legacy_scope_inferred" | "legacy_scope_empty";
|
|
123
|
+
level: "warning";
|
|
124
|
+
agentId: string;
|
|
125
|
+
paths: ReadonlyArray<{
|
|
126
|
+
path: string;
|
|
127
|
+
policy: "working-context" | "write-boundary";
|
|
128
|
+
provenance: "declared" | "derived" | "inferred";
|
|
129
|
+
source: "intent.read_roots" | "intent.write_roots" | "intent.relevant_paths" | "writeRoots" | "task" | "briefing";
|
|
130
|
+
confidence: "certain" | "high" | "medium" | "low";
|
|
131
|
+
}>;
|
|
132
|
+
message: string;
|
|
133
|
+
};
|
|
134
|
+
|
|
111
135
|
export type ContextActivityKind = "context-init" | "context-clear" | "context-refresh" | "context-wiki" | "compaction";
|
|
112
136
|
export type ContextActivityPhase = "scan" | "codewiki" | "generate" | "clio-md" | "state" | "done";
|
|
113
137
|
export type ContextActivityStatus = "started" | "running" | "completed" | "failed";
|
|
@@ -737,6 +761,7 @@ export type BusPayloadMap = {
|
|
|
737
761
|
[BusChannels.ToolBudgetExceeded]: ToolBudgetExceededPayload;
|
|
738
762
|
[BusChannels.ProviderHealth]: ProviderHealthPayload;
|
|
739
763
|
[BusChannels.RuntimeNotice]: RuntimeNoticePayload;
|
|
764
|
+
[BusChannels.DispatchScopeNotice]: DispatchScopeNoticePayload;
|
|
740
765
|
[BusChannels.DispatchEnqueued]: DispatchEnqueuedPayload;
|
|
741
766
|
[BusChannels.DispatchStarted]: DispatchStartedPayload;
|
|
742
767
|
[BusChannels.DispatchProgress]: DispatchProgressPayload;
|
|
@@ -16,7 +16,7 @@ export const CLIO_COMMIT_TRAILERS = {
|
|
|
16
16
|
} as const;
|
|
17
17
|
|
|
18
18
|
export interface CommitReceiptEvidence {
|
|
19
|
-
version:
|
|
19
|
+
version: 20;
|
|
20
20
|
algorithm: "sha256";
|
|
21
21
|
digest: string;
|
|
22
22
|
/** The receipt was checked against its run-ledger envelope. */
|
|
@@ -58,7 +58,7 @@ const KNOWN_CLIO_TRAILERS = new Set(
|
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
function isClioTrailer(key: string): boolean {
|
|
61
|
-
return KNOWN_CLIO_TRAILERS.has(key) || key.startsWith("clio-evidence:receipt-
|
|
61
|
+
return KNOWN_CLIO_TRAILERS.has(key) || key.startsWith("clio-evidence:receipt-v20/sha256:");
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -87,7 +87,7 @@ function trailerBlockStart(lines: ReadonlyArray<string>): number | null {
|
|
|
87
87
|
|
|
88
88
|
function receiptTrailer(receipt: CommitReceiptEvidence | undefined): string | null {
|
|
89
89
|
if (
|
|
90
|
-
receipt?.version !==
|
|
90
|
+
receipt?.version !== 20 ||
|
|
91
91
|
receipt.algorithm !== "sha256" ||
|
|
92
92
|
receipt.integrityValid !== true ||
|
|
93
93
|
receipt.directlyRelevant !== true ||
|
|
@@ -95,7 +95,7 @@ function receiptTrailer(receipt: CommitReceiptEvidence | undefined): string | nu
|
|
|
95
95
|
) {
|
|
96
96
|
return null;
|
|
97
97
|
}
|
|
98
|
-
return `Clio-Evidence: receipt-
|
|
98
|
+
return `Clio-Evidence: receipt-v20/sha256:${receipt.digest}`;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|