@iowarp/clio-coder 0.3.4 → 0.3.6
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 +37 -2
- package/CONTRIBUTING.md +6 -6
- package/README.md +2 -2
- package/dist/{acp-S5R4RR5B.js → acp-2BEHC4DL.js} +4 -4
- package/dist/{agents-P6DMMVZY.js → agents-LNNFTM53.js} +13 -11
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-2XCZLPKS.js → auth-KXXFI2VS.js} +6 -6
- package/dist/{chunk-YCWGATWI.js → chunk-24I7BN55.js} +2 -2
- package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
- package/dist/chunk-3BPUFZDL.js +37 -0
- package/dist/{chunk-WPQLXFOZ.js → chunk-43AOLP7E.js} +2 -2
- package/dist/{chunk-N4CZJQRK.js → chunk-5JGRAMKL.js} +4 -4
- package/dist/{chunk-BRXQQJFP.js → chunk-6US73PDB.js} +568 -47
- package/dist/{chunk-K6WL7QZT.js → chunk-6XXKFVSN.js} +2 -2
- package/dist/{chunk-QQK64KLB.js → chunk-CJUB2JJ2.js} +138 -20
- package/dist/{chunk-HV5X7OR2.js → chunk-CKXWIANG.js} +12 -12
- package/dist/{chunk-UZHIZC5S.js → chunk-CYQKWTG3.js} +61 -76
- package/dist/{chunk-QWU7ZBO7.js → chunk-DJVECN66.js} +204 -45
- package/dist/{chunk-ZWMF7253.js → chunk-E2ER4LJF.js} +304 -9
- package/dist/{chunk-7RXG6QRZ.js → chunk-EKY57CSP.js} +2 -75
- package/dist/{chunk-EDRHSCIE.js → chunk-EYPA3EGJ.js} +10 -2
- package/dist/{chunk-TTNYS3EA.js → chunk-G7MUEIGA.js} +1 -1
- package/dist/{chunk-BPGS2WCQ.js → chunk-GEYXPTRF.js} +2 -1
- package/dist/{chunk-BEY543CS.js → chunk-GOXNB3AO.js} +5 -2
- package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
- package/dist/chunk-HWUFFB6L.js +83 -0
- package/dist/{chunk-35MKKU5R.js → chunk-K7T3E2SR.js} +15 -8
- package/dist/{chunk-VAWWTKDP.js → chunk-KHSFENX2.js} +2 -2
- package/dist/chunk-LCGCVYZ4.js +57 -0
- package/dist/{chunk-X6COSD2O.js → chunk-LYF7OHWH.js} +41 -14
- package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
- package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
- package/dist/{chunk-5M54SPOL.js → chunk-ODFEOB4F.js} +161 -5
- package/dist/{chunk-3JLKSKD7.js → chunk-OH3TOQTB.js} +5 -1
- package/dist/{chunk-MEQ45TQ4.js → chunk-PBTHKCPN.js} +18 -4
- package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
- package/dist/{chunk-QQL5RT5M.js → chunk-QM3F2GKX.js} +94 -36
- package/dist/{chunk-A2GZF7DC.js → chunk-QNQHSOLF.js} +4 -4
- package/dist/{chunk-KRPY7NTG.js → chunk-R46L2BIR.js} +3 -3
- package/dist/{chunk-BP4OYD6A.js → chunk-RY3LY4J5.js} +20 -2
- package/dist/{chunk-34475P3I.js → chunk-TSHXZTOQ.js} +5 -4
- package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
- package/dist/{chunk-2TZWSW76.js → chunk-WHGPSPT5.js} +2 -2
- package/dist/{chunk-TW3WDMVS.js → chunk-WHJYKASB.js} +2 -2
- package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
- package/dist/{chunk-HXG4IURW.js → chunk-XE2VEJHX.js} +2 -2
- package/dist/{chunk-3HZ5RWN2.js → chunk-XF5N4U5A.js} +7 -6
- package/dist/{chunk-ZYKPLLNQ.js → chunk-XXQNGV4M.js} +590 -32
- package/dist/{chunk-4JUF2NNX.js → chunk-XYDYPRZI.js} +4 -4
- package/dist/{chunk-VMNQ6OZA.js → chunk-ZRGEBJ4T.js} +971 -794
- package/dist/{chunk-2LZI5CAG.js → chunk-ZXF4XRKW.js} +75 -33
- package/dist/{chunk-VSNATDE6.js → chunk-ZZMN5OM4.js} +2 -2
- package/dist/cli/index.js +31 -31
- package/dist/{clio-J5JIOIDS.js → clio-M2KGYUFZ.js} +2 -2
- package/dist/{code-nav-AXCXSBHX.js → code-nav-GQNL7XA6.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-KELWS457.js → components-5TTYYX6G.js} +3 -3
- package/dist/{config-OEBMIN2U.js → config-XUUYQIWO.js} +27 -25
- package/dist/{configure-PUQOSIXQ.js → configure-IHJ7YOMV.js} +7 -7
- package/dist/{context-URSXPBCK.js → context-74JLXAWD.js} +12 -12
- package/dist/{context-MGSE4Z2T.js → context-75MIWW3U.js} +24 -22
- package/dist/{context-EKDCKUUZ.js → context-ZQ7SIFJV.js} +8 -7
- package/dist/{context-clear-KDAJRNUK.js → context-clear-GYKWNUML.js} +24 -22
- package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
- package/dist/{context-working-set-SBKMPPI2.js → context-working-set-UX5KEP4J.js} +11 -10
- package/dist/{dispatch-runner-MSWN72NK.js → dispatch-runner-GIJBHNFL.js} +21 -20
- package/dist/{docs-2C2LTVT2.js → docs-6FZSCG5B.js} +3 -3
- package/dist/{doctor-7BSE27PJ.js → doctor-SVJ5BZCW.js} +4 -4
- package/dist/{eval-IZGDOO4H.js → eval-CG6LLBLD.js} +47 -232
- package/dist/{evidence-SR7WXB5B.js → evidence-ZYFIEN42.js} +19 -18
- package/dist/{evolve-K7VE2CBX.js → evolve-QGEXEMDW.js} +19 -18
- package/dist/{extensions-QVDOHDGJ.js → extensions-ADGNCJJD.js} +3 -3
- package/dist/{fleet-7XMJNQNF.js → fleet-S5R4ZOQY.js} +49 -30
- package/dist/{fleet-preflight-AQNAH644.js → fleet-preflight-BHSNPBMH.js} +2 -2
- package/dist/{init-JGNPAYXT.js → init-5DRU55YR.js} +31 -29
- package/dist/memory-7YKKR6UC.js +467 -0
- package/dist/{models-ZMMLFJNN.js → models-ZPOLRU2C.js} +10 -10
- package/dist/{monitor-2F3T5KHP.js → monitor-US5F5YGZ.js} +33 -18
- package/dist/{orchestrator-ORHT43JB.js → orchestrator-E2AL4T5N.js} +1092 -659
- package/dist/{paths-UXLN5YYZ.js → paths-E7KYAQWE.js} +3 -3
- package/dist/{reset-NXGTYNUO.js → reset-KZ652EK6.js} +3 -3
- package/dist/{run-RF4WJGMT.js → run-SRNBKDWD.js} +52 -40
- package/dist/{share-UT3W6E4M.js → share-CGZE33UP.js} +3 -3
- package/dist/{skills-PSACKC5Q.js → skills-S2X4DLY5.js} +4 -4
- package/dist/{skills-eval-WJSI55RZ.js → skills-eval-W2GGIC4R.js} +19 -18
- package/dist/{targets-PIIRAOYS.js → targets-54SWINWB.js} +14 -12
- package/dist/{terminal-lease-ULWXWNVY.js → terminal-lease-SAIF2OGY.js} +5 -4
- package/dist/{uninstall-FZCQCDKC.js → uninstall-BVLWXKBT.js} +3 -3
- package/dist/{upgrade-346TZ6AV.js → upgrade-JKAR27XC.js} +8 -8
- package/dist/{usage-6KKXR32N.js → usage-MSAWCLX4.js} +60 -27
- package/dist/{verifiers-4UUM6TEE.js → verifiers-NCBTHHN2.js} +60 -54
- package/dist/{wiki-generate-7STOCIFZ.js → wiki-generate-GUSOQ6ZP.js} +30 -28
- package/dist/worker/entry.js +69 -58
- package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
- package/docs/README.md +3 -3
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-placement.md +1 -2
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +9 -7
- package/docs/configuration-and-targets.md +12 -1
- package/docs/context-engine.md +4 -2
- package/docs/context-working-set.md +4 -4
- package/docs/development-pipeline.md +1 -1
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +2 -2
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +4 -45
- package/docs/evidence-and-memory.md +67 -7
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +2 -2
- package/docs/fleet-dispatch.md +28 -2
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +19 -2
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +3 -3
- package/docs/proactive-memory.md +26 -16
- package/docs/prompt-envelope-and-tools.md +4 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +38 -35
- package/docs/safety-model.md +29 -7
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +2 -2
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +38 -4
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +7 -4
- package/src/cli/agents.ts +2 -3
- package/src/cli/argv.ts +14 -1
- package/src/cli/fleet.ts +15 -0
- package/src/cli/index.ts +1 -1
- package/src/cli/memory.ts +272 -10
- package/src/cli/modes/json-stream.ts +2 -2
- package/src/cli/modes/print.ts +12 -1
- package/src/cli/run.ts +22 -2
- package/src/cli/targets.ts +12 -3
- package/src/cli/usage.ts +55 -7
- package/src/core/bus-events.ts +3 -0
- package/src/core/response-model-id.ts +134 -0
- package/src/core/toml.ts +62 -0
- package/src/core/workspace-files.ts +0 -1
- package/src/domains/agents/builtins/architect.md +1 -1
- package/src/domains/agents/catalog.ts +5 -4
- package/src/domains/agents/recipe.ts +54 -14
- package/src/domains/agents/result-contract.ts +7 -4
- package/src/domains/context/bootstrap.ts +36 -27
- package/src/domains/context/project-metadata.ts +19 -63
- package/src/domains/context/prompt-context.ts +8 -0
- package/src/domains/context/working-set/policies/index.ts +3 -4
- package/src/domains/dispatch/budget-envelope.ts +396 -0
- package/src/domains/dispatch/contract.ts +2 -0
- package/src/domains/dispatch/extension.ts +81 -27
- package/src/domains/dispatch/orphan-recovery.ts +1 -0
- package/src/domains/dispatch/receipt-integrity.ts +4 -0
- package/src/domains/dispatch/state.ts +1 -0
- package/src/domains/dispatch/types.ts +10 -3
- package/src/domains/dispatch/validation.ts +14 -0
- package/src/domains/dispatch/worker-spawn.ts +14 -3
- package/src/domains/eval/metrics/evidence.ts +0 -116
- package/src/domains/eval/metrics/invariants.ts +1 -1
- package/src/domains/eval/runners/clio-run.ts +1 -10
- package/src/domains/eval/runners/external-command.ts +2 -29
- package/src/domains/eval/schema/suite.ts +0 -7
- package/src/domains/eval/suites/run.ts +1 -7
- package/src/domains/memory/index.ts +22 -0
- package/src/domains/memory/operations.ts +58 -1
- package/src/domains/memory/promotion.ts +281 -0
- package/src/domains/memory/prompt-section.ts +25 -5
- package/src/domains/memory/proposal.ts +51 -7
- package/src/domains/memory/task-bank.ts +3 -2
- package/src/domains/memory/task-memory-handoff.ts +181 -24
- package/src/domains/memory/task-memory-policy.ts +3 -1
- package/src/domains/memory/types.ts +37 -0
- package/src/domains/memory/validate.ts +178 -0
- package/src/domains/middleware/memory-intervention.ts +35 -25
- package/src/domains/middleware/runtime.ts +6 -0
- package/src/domains/middleware/skills-reminder.ts +19 -4
- package/src/domains/middleware/stalled-turn.ts +43 -1
- package/src/domains/middleware/types.ts +10 -0
- package/src/domains/observability/contract.ts +6 -1
- package/src/domains/observability/cost.ts +20 -4
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/providers/index.ts +3 -0
- package/src/domains/providers/model-discovery.ts +9 -0
- package/src/domains/providers/runtime-resolution.ts +38 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
- package/src/domains/providers/types/context-window-slots.ts +18 -0
- package/src/domains/providers/types/runtime-descriptor.ts +3 -1
- package/src/domains/safety/call-target.ts +211 -14
- package/src/domains/safety/decision-presentation.ts +268 -0
- package/src/domains/safety/redaction.ts +73 -0
- package/src/domains/session/context-ledger.ts +10 -1
- package/src/domains/session/decision-board.ts +4 -0
- package/src/domains/session/entries.ts +3 -0
- package/src/domains/session/history.ts +68 -19
- package/src/domains/session/usage.ts +24 -7
- package/src/engine/acp/event-mapper.ts +7 -0
- package/src/engine/acp/server.ts +29 -2
- package/src/engine/apis/lmstudio.ts +25 -4
- package/src/engine/apis/openai-completions.ts +147 -22
- package/src/engine/claude/sdk-runtime.ts +8 -2
- package/src/engine/claude/tool-safety.ts +13 -0
- package/src/engine/loop-guard.ts +27 -3
- package/src/engine/worker-events.ts +4 -3
- package/src/engine/worker-runtime.ts +59 -54
- package/src/entry/orchestrator.ts +18 -1
- package/src/interactive/chat-loop-messages.ts +22 -0
- package/src/interactive/chat-loop.ts +13 -0
- package/src/interactive/chat-renderer.ts +19 -3
- package/src/interactive/clio-editor.ts +44 -7
- package/src/interactive/context-overlay.ts +43 -5
- package/src/interactive/cost-overlay.ts +39 -8
- package/src/interactive/dispatch-board.ts +212 -35
- package/src/interactive/footer/widgets.ts +13 -0
- package/src/interactive/interactive-application.ts +6 -1
- package/src/interactive/interactive-input-runtime.ts +11 -1
- package/src/interactive/interactive-presentation.ts +11 -1
- package/src/interactive/memory-overlay.ts +89 -4
- package/src/interactive/overlay-ask-user-lifecycle.ts +1 -1
- package/src/interactive/overlay-frame.ts +5 -2
- package/src/interactive/overlay-general-openers.ts +40 -1
- package/src/interactive/overlay-key-routing.ts +41 -1
- package/src/interactive/overlay-lifecycle.ts +11 -4
- package/src/interactive/overlay-permission-lifecycle.ts +23 -8
- package/src/interactive/overlay-transitions.ts +11 -0
- package/src/interactive/overlays/ask-user.ts +74 -30
- package/src/interactive/overlays/decisions.ts +3 -1
- package/src/interactive/permission-hint.ts +35 -0
- package/src/interactive/permission-overlay.ts +95 -45
- package/src/interactive/renderers/tool-execution.ts +19 -49
- package/src/interactive/session-last-turn.ts +8 -1
- package/src/interactive/session-usage-reseed.ts +36 -10
- package/src/interactive/slash-commands.ts +2 -2
- package/src/interactive/status/summary.ts +5 -0
- package/src/interactive/status/types.ts +5 -0
- package/src/interactive/terminal-lease.ts +1 -0
- package/src/interactive/turn-context.ts +96 -23
- package/src/interactive/turn-middleware.ts +1 -0
- package/src/interactive/turn-runtime.ts +37 -8
- package/src/interactive/turn-state.ts +3 -0
- package/src/interactive/worker-progress.ts +440 -0
- package/src/interactive/worker-stream.ts +51 -110
- package/src/tools/agent-tools.ts +28 -3
- package/src/tools/ask-user.ts +21 -1
- package/src/tools/context/index.ts +2 -2
- package/src/tools/dispatch-arguments.ts +8 -0
- package/src/tools/dispatch-event-text.ts +19 -0
- package/src/tools/dispatch.ts +24 -1
- package/src/tools/monitor.ts +15 -0
- package/src/tools/registry.ts +15 -5
- package/src/tools/result-disposition.ts +156 -0
- package/src/tools/result-shaping.ts +59 -1
- package/src/tools/verify/authoring.ts +55 -54
- package/src/tools/worker-evidence.ts +19 -0
- package/src/worker/spec-contract.ts +43 -3
- package/dist/chunk-EFADSJET.js +0 -18
- package/dist/memory-4ALKDJ4Q.js +0 -246
- package/src/domains/eval/metrics/chaos-stream.ts +0 -93
|
@@ -19,6 +19,7 @@ import { GUARDRAIL_DEFAULTS } from "../../core/guardrails.js";
|
|
|
19
19
|
import { readClioVersion, readPiMonoVersion } from "../../core/package-root.js";
|
|
20
20
|
import { canonicalizeExistingPath } from "../../core/path-canonical.js";
|
|
21
21
|
import { protectedResidencyModels } from "../../core/residency-protection.js";
|
|
22
|
+
import { responseModelIdObservationFromRecord } from "../../core/response-model-id.js";
|
|
22
23
|
import {
|
|
23
24
|
responseSchemaConflictsWithTools,
|
|
24
25
|
runtimeSpeaksResponseSchemaDialect,
|
|
@@ -134,6 +135,7 @@ import {
|
|
|
134
135
|
registerDetachedBatch,
|
|
135
136
|
} from "./batch-store.js";
|
|
136
137
|
import { type BatchState, createBatch, onRunComplete, snapshotBatch } from "./batch-tracker.js";
|
|
138
|
+
import { type RunToolBudgetEnvelope, resolveToolBudgetEnvelope } from "./budget-envelope.js";
|
|
137
139
|
import { assessCapabilityMismatch, type CapabilityMismatch } from "./capability-match.js";
|
|
138
140
|
import { capacityLeaseUsage, createNodeLeaseUsageReader } from "./capacity-lease.js";
|
|
139
141
|
import type {
|
|
@@ -307,6 +309,7 @@ interface ActiveRun {
|
|
|
307
309
|
requestOrigin?: DispatchRequestOrigin;
|
|
308
310
|
agentId: string;
|
|
309
311
|
task: string;
|
|
312
|
+
budget?: RunToolBudgetEnvelope;
|
|
310
313
|
cwd: string;
|
|
311
314
|
/** Fleet node this run was placed on; null when no placement resolved it. */
|
|
312
315
|
node: RunNodeIdentity | null;
|
|
@@ -1287,6 +1290,7 @@ interface DispatchLifecycleStage {
|
|
|
1287
1290
|
capabilityMismatch: CapabilityMismatch | null;
|
|
1288
1291
|
effectiveAutonomy: AutonomyLevel;
|
|
1289
1292
|
budget: WorkerBudget;
|
|
1293
|
+
budgetEnvelope: RunToolBudgetEnvelope;
|
|
1290
1294
|
settings?: Readonly<ReturnType<ConfigContract["get"]>>;
|
|
1291
1295
|
}
|
|
1292
1296
|
|
|
@@ -1411,7 +1415,7 @@ function assertWriteRootsEnforceable(runtime: RuntimeDescriptor, writeRoots: Rea
|
|
|
1411
1415
|
function assertWorkerBudgetEnforceable(runtime: RuntimeDescriptor, hasDeclaredBudget: boolean): void {
|
|
1412
1416
|
if (!hasDeclaredBudget || runtime.kind !== "subprocess") return;
|
|
1413
1417
|
throw new Error(
|
|
1414
|
-
`dispatch: runtime '${runtime.id}' cannot enforce an explicit
|
|
1418
|
+
`dispatch: runtime '${runtime.id}' cannot enforce an explicit dispatch budget because subprocess workers do not expose per-tool mediation; choose a native or claude-sdk worker`,
|
|
1415
1419
|
);
|
|
1416
1420
|
}
|
|
1417
1421
|
|
|
@@ -1543,26 +1547,36 @@ function assertPostRuntimeToolCompatibility(
|
|
|
1543
1547
|
);
|
|
1544
1548
|
}
|
|
1545
1549
|
|
|
1550
|
+
function workerToolCallHardCap(settings: EffectiveSettings): number {
|
|
1551
|
+
const rawEnvCap = process.env.CLIO_CODER_WORKER_TOOL_CALL_CAP?.trim();
|
|
1552
|
+
const parsedEnvCap = rawEnvCap && /^[1-9]\d*$/.test(rawEnvCap) ? Number(rawEnvCap) : Number.NaN;
|
|
1553
|
+
return Number.isSafeInteger(parsedEnvCap)
|
|
1554
|
+
? parsedEnvCap
|
|
1555
|
+
: (settings?.guardrails.workerToolCallCap ?? GUARDRAIL_DEFAULTS.workerToolCallCap);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1546
1558
|
function resolveEffectiveWorkerBudget(input: {
|
|
1559
|
+
req: DispatchRequest;
|
|
1560
|
+
recipeId: string;
|
|
1547
1561
|
declared: ReturnType<typeof normalizeAgentSpec>["budget"];
|
|
1548
1562
|
allowedTools: ReadonlyArray<ToolName>;
|
|
1549
1563
|
settings: EffectiveSettings;
|
|
1550
|
-
}):
|
|
1551
|
-
const
|
|
1552
|
-
const parsedEnvCap = rawEnvCap && /^[1-9]\d*$/.test(rawEnvCap) ? Number(rawEnvCap) : Number.NaN;
|
|
1553
|
-
const hardCap = Number.isSafeInteger(parsedEnvCap)
|
|
1554
|
-
? parsedEnvCap
|
|
1555
|
-
: (input.settings?.guardrails.workerToolCallCap ?? GUARDRAIL_DEFAULTS.workerToolCallCap);
|
|
1564
|
+
}): RunToolBudgetEnvelope {
|
|
1565
|
+
const hardCap = workerToolCallHardCap(input.settings);
|
|
1556
1566
|
const declared = input.declared ?? {
|
|
1557
1567
|
toolCalls: hardCap,
|
|
1558
1568
|
readReserve: Math.min(5, Math.max(0, hardCap - 1)),
|
|
1559
1569
|
synthesis: true,
|
|
1560
1570
|
};
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
:
|
|
1565
|
-
|
|
1571
|
+
return resolveToolBudgetEnvelope({
|
|
1572
|
+
recipeId: input.recipeId,
|
|
1573
|
+
policy: declared,
|
|
1574
|
+
...(input.req.budget === undefined ? {} : { request: input.req.budget }),
|
|
1575
|
+
hardCap,
|
|
1576
|
+
hasReadTool: input.allowedTools.includes(ToolNames.Read),
|
|
1577
|
+
retry: (input.req.lineage?.attempt ?? 0) > 0,
|
|
1578
|
+
revision: input.req.gate?.role === "builder" && input.req.gate.cycle > 1 && input.req.gate.verdict === "revise",
|
|
1579
|
+
});
|
|
1566
1580
|
}
|
|
1567
1581
|
|
|
1568
1582
|
function frozenProtectedArtifactState(state: ProtectedArtifactState | undefined): ProtectedArtifactState {
|
|
@@ -3193,7 +3207,7 @@ export function createDispatchBundle(
|
|
|
3193
3207
|
);
|
|
3194
3208
|
enforceCapabilityGate(target.target.id, target.modelCapabilities, req.requiredCapabilities);
|
|
3195
3209
|
assertRuntimeCanHonorWorkerPermissionMode(target.runtime, settings?.workers.onPermission ?? "deny");
|
|
3196
|
-
assertWorkerBudgetEnforceable(target.runtime, spec.budget !== null);
|
|
3210
|
+
assertWorkerBudgetEnforceable(target.runtime, spec.budget !== null || req.budget !== undefined);
|
|
3197
3211
|
|
|
3198
3212
|
const cwd = req.cwd ?? process.cwd();
|
|
3199
3213
|
const sessionAutonomy = settings?.autonomy ?? "auto-edit";
|
|
@@ -3231,7 +3245,13 @@ export function createDispatchBundle(
|
|
|
3231
3245
|
workingContextPaths: workerWorkingContextPaths(req),
|
|
3232
3246
|
});
|
|
3233
3247
|
const systemPrompt = compiledWorkerPrompt.systemPrompt;
|
|
3234
|
-
const
|
|
3248
|
+
const budgetEnvelope = resolveEffectiveWorkerBudget({
|
|
3249
|
+
req,
|
|
3250
|
+
recipeId: recipe.id,
|
|
3251
|
+
declared: spec.budget,
|
|
3252
|
+
allowedTools: effectiveTools,
|
|
3253
|
+
settings,
|
|
3254
|
+
});
|
|
3235
3255
|
// Fetch structured project context only for tiers that receive it, so
|
|
3236
3256
|
// read-only scouts never pay the CLIO-CODER.md read. The tier is spec policy
|
|
3237
3257
|
// (capability-class default, recipe frontmatter override).
|
|
@@ -3291,7 +3311,8 @@ export function createDispatchBundle(
|
|
|
3291
3311
|
operatorProfileApplied: compiledWorkerPrompt.operatorProfileApplied ?? false,
|
|
3292
3312
|
capabilityMismatch,
|
|
3293
3313
|
effectiveAutonomy,
|
|
3294
|
-
budget,
|
|
3314
|
+
budget: budgetEnvelope.effective,
|
|
3315
|
+
budgetEnvelope,
|
|
3295
3316
|
...(settings ? { settings } : {}),
|
|
3296
3317
|
};
|
|
3297
3318
|
}
|
|
@@ -3473,6 +3494,7 @@ export function createDispatchBundle(
|
|
|
3473
3494
|
role?: string;
|
|
3474
3495
|
usage?: unknown;
|
|
3475
3496
|
model?: unknown;
|
|
3497
|
+
responseModelIdObservation?: unknown;
|
|
3476
3498
|
responseModel?: unknown;
|
|
3477
3499
|
responseId?: unknown;
|
|
3478
3500
|
stopReason?: unknown;
|
|
@@ -3522,12 +3544,16 @@ export function createDispatchBundle(
|
|
|
3522
3544
|
tokenMeter.cacheReadTokens += typeof u.cacheRead === "number" ? u.cacheRead : 0;
|
|
3523
3545
|
tokenMeter.cacheWriteTokens += typeof u.cacheWrite === "number" ? u.cacheWrite : 0;
|
|
3524
3546
|
tokenMeter.reasoningTokens += extractReasoningTokenCount(u);
|
|
3525
|
-
const
|
|
3526
|
-
const
|
|
3527
|
-
const
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3547
|
+
const requestedModelId = readStringOrNull(event.message.model);
|
|
3548
|
+
const responseModelIdObservation = responseModelIdObservationFromRecord(event.message, "not-observed");
|
|
3549
|
+
const differingResponseModelId = readStringOrNull(event.message.responseModel);
|
|
3550
|
+
const providerResponseId = readStringOrNull(event.message.responseId);
|
|
3551
|
+
upstreamResponses.push({
|
|
3552
|
+
requestedModelId,
|
|
3553
|
+
responseModelIdObservation,
|
|
3554
|
+
differingResponseModelId,
|
|
3555
|
+
providerResponseId,
|
|
3556
|
+
});
|
|
3531
3557
|
if (event.message.stopReason === "error") {
|
|
3532
3558
|
const message = readStringOrNull(event.message.errorMessage);
|
|
3533
3559
|
if (message !== null) failureMessage = message;
|
|
@@ -4142,6 +4168,11 @@ export function createDispatchBundle(
|
|
|
4142
4168
|
return handle;
|
|
4143
4169
|
};
|
|
4144
4170
|
if (req.delegationAgentId) {
|
|
4171
|
+
if (req.budget !== undefined) {
|
|
4172
|
+
throw new Error(
|
|
4173
|
+
"dispatch: budget envelopes cannot be enforced on an ACP delegation target; dispatch to a native or claude-sdk worker",
|
|
4174
|
+
);
|
|
4175
|
+
}
|
|
4145
4176
|
assertPlannedNodeIdentity(req, { id: "local", kind: "local" });
|
|
4146
4177
|
assertProtectedArtifactsEnforceable("acp-delegation", false, protectedArtifactState);
|
|
4147
4178
|
if (req.responseSchema !== undefined) {
|
|
@@ -4372,6 +4403,7 @@ export function createDispatchBundle(
|
|
|
4372
4403
|
role?: string;
|
|
4373
4404
|
usage?: unknown;
|
|
4374
4405
|
model?: unknown;
|
|
4406
|
+
responseModelIdObservation?: unknown;
|
|
4375
4407
|
responseModel?: unknown;
|
|
4376
4408
|
responseId?: unknown;
|
|
4377
4409
|
stopReason?: unknown;
|
|
@@ -4477,12 +4509,16 @@ export function createDispatchBundle(
|
|
|
4477
4509
|
tokenMeter.cacheReadTokens += typeof u.cacheRead === "number" ? u.cacheRead : 0;
|
|
4478
4510
|
tokenMeter.cacheWriteTokens += typeof u.cacheWrite === "number" ? u.cacheWrite : 0;
|
|
4479
4511
|
tokenMeter.reasoningTokens += extractReasoningTokenCount(u);
|
|
4480
|
-
const
|
|
4481
|
-
const
|
|
4482
|
-
const
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4512
|
+
const requestedModelId = readStringOrNull(event.message.model);
|
|
4513
|
+
const responseModelIdObservation = responseModelIdObservationFromRecord(event.message, "not-observed");
|
|
4514
|
+
const differingResponseModelId = readStringOrNull(event.message.responseModel);
|
|
4515
|
+
const providerResponseId = readStringOrNull(event.message.responseId);
|
|
4516
|
+
upstreamResponses.push({
|
|
4517
|
+
requestedModelId,
|
|
4518
|
+
responseModelIdObservation,
|
|
4519
|
+
differingResponseModelId,
|
|
4520
|
+
providerResponseId,
|
|
4521
|
+
});
|
|
4486
4522
|
if (event.message.stopReason === "error") {
|
|
4487
4523
|
const message = readStringOrNull(event.message.errorMessage);
|
|
4488
4524
|
if (message !== null) failureMessage = message;
|
|
@@ -4573,6 +4609,7 @@ export function createDispatchBundle(
|
|
|
4573
4609
|
agentAudience: lifecycle.agentAudience,
|
|
4574
4610
|
requestOrigin: lifecycle.requestOrigin,
|
|
4575
4611
|
task: req.task,
|
|
4612
|
+
budget: lifecycle.budgetEnvelope,
|
|
4576
4613
|
targetId: lifecycle.target.target.id,
|
|
4577
4614
|
wireModelId: lifecycle.target.wireModelId,
|
|
4578
4615
|
runtimeId: lifecycle.target.runtime.id,
|
|
@@ -4638,6 +4675,7 @@ export function createDispatchBundle(
|
|
|
4638
4675
|
// reroute lineage depth, and the model context window for the
|
|
4639
4676
|
// per-worker meter. All optional so consumers degrade to local/plain.
|
|
4640
4677
|
const fleetIdentity = {
|
|
4678
|
+
budget: lifecycle.budgetEnvelope,
|
|
4641
4679
|
...(placement !== undefined && placement !== null ? { node: placement.node.id } : {}),
|
|
4642
4680
|
...(req.gate !== undefined ? { gate: { role: req.gate.role, cycle: req.gate.cycle } } : {}),
|
|
4643
4681
|
...(req.reroutes !== undefined && req.reroutes.length > 0 ? { rerouteCount: req.reroutes.length } : {}),
|
|
@@ -4743,6 +4781,7 @@ export function createDispatchBundle(
|
|
|
4743
4781
|
requestOrigin: lifecycle.requestOrigin,
|
|
4744
4782
|
agentId: req.agentId,
|
|
4745
4783
|
task: req.task,
|
|
4784
|
+
budget: lifecycle.budgetEnvelope,
|
|
4746
4785
|
cwd: lifecycle.cwd,
|
|
4747
4786
|
node: placement?.node ?? null,
|
|
4748
4787
|
aborted: false,
|
|
@@ -4817,6 +4856,7 @@ export function createDispatchBundle(
|
|
|
4817
4856
|
agentAudience: lifecycle.agentAudience,
|
|
4818
4857
|
requestOrigin: lifecycle.requestOrigin,
|
|
4819
4858
|
task: req.task,
|
|
4859
|
+
budget: lifecycle.budgetEnvelope,
|
|
4820
4860
|
targetId: lifecycle.target.target.id,
|
|
4821
4861
|
wireModelId: lifecycle.target.wireModelId,
|
|
4822
4862
|
runtimeId: lifecycle.target.runtime.id,
|
|
@@ -5306,6 +5346,11 @@ export function createDispatchBundle(
|
|
|
5306
5346
|
req = validation.restore(validated.spec);
|
|
5307
5347
|
|
|
5308
5348
|
if (req.delegationAgentId) {
|
|
5349
|
+
if (req.budget !== undefined) {
|
|
5350
|
+
throw new Error(
|
|
5351
|
+
"dispatch: budget envelopes cannot be enforced on an ACP delegation target; dispatch to a native or claude-sdk worker",
|
|
5352
|
+
);
|
|
5353
|
+
}
|
|
5309
5354
|
const protectedArtifactState = getProtectedArtifactState();
|
|
5310
5355
|
assertProtectedArtifactsEnforceable("acp-delegation", false, protectedArtifactState);
|
|
5311
5356
|
if (req.responseSchema !== undefined) {
|
|
@@ -5369,6 +5414,7 @@ export function createDispatchBundle(
|
|
|
5369
5414
|
);
|
|
5370
5415
|
assertPostRuntimeToolCompatibility(req.agentId, agentSpec, effectiveTools, target);
|
|
5371
5416
|
assertRuntimeCanHonorWorkerPermissionMode(target.runtime, settings?.workers.onPermission ?? "deny");
|
|
5417
|
+
assertWorkerBudgetEnforceable(target.runtime, agentSpec.budget !== null || req.budget !== undefined);
|
|
5372
5418
|
assertResponseSchemaEnforceable(target.runtime, target.modelCapabilities, req.responseSchema, effectiveTools.length);
|
|
5373
5419
|
assertWriteRootsEnforceable(target.runtime, req.writeRoots);
|
|
5374
5420
|
assertProtectedArtifactsEnforceable(
|
|
@@ -5376,6 +5422,13 @@ export function createDispatchBundle(
|
|
|
5376
5422
|
target.runtime.kind !== "subprocess",
|
|
5377
5423
|
getProtectedArtifactState(),
|
|
5378
5424
|
);
|
|
5425
|
+
resolveEffectiveWorkerBudget({
|
|
5426
|
+
req,
|
|
5427
|
+
recipeId: recipe.id,
|
|
5428
|
+
declared: agentSpec.budget,
|
|
5429
|
+
allowedTools: effectiveTools,
|
|
5430
|
+
settings,
|
|
5431
|
+
});
|
|
5379
5432
|
return {
|
|
5380
5433
|
agentId: req.agentId,
|
|
5381
5434
|
specFingerprint: agentSpecFingerprint(agentSpec),
|
|
@@ -5773,6 +5826,7 @@ export function createDispatchBundle(
|
|
|
5773
5826
|
runId: run.runId,
|
|
5774
5827
|
agentId: run.agentId,
|
|
5775
5828
|
task: run.task,
|
|
5829
|
+
...(run.budget !== undefined ? { budget: run.budget } : {}),
|
|
5776
5830
|
runtimeKind: run.runtimeKind,
|
|
5777
5831
|
outcomePhase: run.stallKilled ? "terminating" : run.aborted ? "aborting" : "running",
|
|
5778
5832
|
heartbeat,
|
|
@@ -55,6 +55,7 @@ function envelopeFromReceipt(receipt: RunReceipt, status: RunStatus, receiptPath
|
|
|
55
55
|
...(receipt.agentAudience !== undefined ? { agentAudience: receipt.agentAudience } : {}),
|
|
56
56
|
...(receipt.requestOrigin !== undefined ? { requestOrigin: receipt.requestOrigin } : {}),
|
|
57
57
|
task: receipt.task,
|
|
58
|
+
...(receipt.budget !== undefined ? { budget: receipt.budget } : {}),
|
|
58
59
|
...(receipt.briefing !== undefined ? { briefing: receipt.briefing } : {}),
|
|
59
60
|
...(receipt.steering !== undefined ? { steering: receipt.steering } : {}),
|
|
60
61
|
targetId: receipt.targetId,
|
|
@@ -76,6 +76,8 @@ export const RECEIPT_INTEGRITY_FIELD_COVERAGE = {
|
|
|
76
76
|
agentAudience: true,
|
|
77
77
|
requestOrigin: true,
|
|
78
78
|
task: true,
|
|
79
|
+
// Historical receipts omit this field, so their receipt and ledger payloads remain unchanged.
|
|
80
|
+
budget: true,
|
|
79
81
|
targetId: true,
|
|
80
82
|
wireModelId: true,
|
|
81
83
|
runtimeId: true,
|
|
@@ -179,6 +181,7 @@ function ledgerDigestFields(envelope: RunEnvelope): Record<string, unknown> {
|
|
|
179
181
|
agentId: envelope.agentId,
|
|
180
182
|
executionRole: envelope.executionRole,
|
|
181
183
|
task: envelope.task,
|
|
184
|
+
...(envelope.budget !== undefined ? { budget: envelope.budget } : {}),
|
|
182
185
|
targetId: envelope.targetId,
|
|
183
186
|
wireModelId: envelope.wireModelId,
|
|
184
187
|
runtimeId: envelope.runtimeId,
|
|
@@ -356,6 +359,7 @@ function firstLedgerMismatch(receipt: RunReceipt, envelope: RunEnvelope): string
|
|
|
356
359
|
if (!Object.is(receiptValue, ledgerValue)) return field;
|
|
357
360
|
}
|
|
358
361
|
if (canonicalJson(receipt.briefing ?? null) !== canonicalJson(envelope.briefing ?? null)) return "briefing";
|
|
362
|
+
if (canonicalJson(receipt.budget ?? null) !== canonicalJson(envelope.budget ?? null)) return "budget";
|
|
359
363
|
if (canonicalJson(receipt.steering ?? null) !== canonicalJson(envelope.steering ?? null)) return "steering";
|
|
360
364
|
return null;
|
|
361
365
|
}
|
|
@@ -175,6 +175,7 @@ export function openLedger(opts?: LedgerOptions): Ledger {
|
|
|
175
175
|
...(input.agentAudience !== undefined ? { agentAudience: input.agentAudience } : {}),
|
|
176
176
|
...(input.requestOrigin !== undefined ? { requestOrigin: input.requestOrigin } : {}),
|
|
177
177
|
task: input.task,
|
|
178
|
+
...(input.budget !== undefined ? { budget: structuredClone(input.budget) } : {}),
|
|
178
179
|
...(input.briefing !== undefined ? { briefing: structuredClone(input.briefing) } : {}),
|
|
179
180
|
...(input.steering !== undefined ? { steering: structuredClone(input.steering) } : {}),
|
|
180
181
|
targetId: input.targetId,
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
* are pure data: no class methods, no engine refs.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type { ResponseModelIdObservation } from "../../core/response-model-id.js";
|
|
9
10
|
import type { SkillActivation } from "../../core/skill-activation.js";
|
|
10
11
|
import type { ToolProfileName } from "../../tools/profiles.js";
|
|
11
12
|
import type { AgentAudience } from "../agents/spec.js";
|
|
12
13
|
import type { EvidenceTag } from "../evidence/index.js";
|
|
13
14
|
import type { CostProvenance, RuntimeTargetSnapshot } from "../providers/index.js";
|
|
15
|
+
import type { RunToolBudgetEnvelope } from "./budget-envelope.js";
|
|
14
16
|
import type { ExecutionRole, GateTopologyRole } from "./execution-role.js";
|
|
15
17
|
import type { RouteDecisionV1 } from "./route-decision.js";
|
|
16
18
|
import type { RoutingIntent } from "./routing-intent.js";
|
|
@@ -418,6 +420,8 @@ export interface RunEnvelope {
|
|
|
418
420
|
agentAudience?: AgentAudience;
|
|
419
421
|
requestOrigin?: DispatchRequestOrigin;
|
|
420
422
|
task: string;
|
|
423
|
+
/** Immutable recipe policy, invocation request, effective phase, and admission reasons. */
|
|
424
|
+
budget?: RunToolBudgetEnvelope;
|
|
421
425
|
/** Present only when a bounded parent briefing was sent as dynamic task data. */
|
|
422
426
|
briefing?: RunBriefingProvenance;
|
|
423
427
|
/** Sent steering provenance in stable per-run sequence order; prose is never persisted. */
|
|
@@ -615,9 +619,10 @@ export interface RunReceiptReproducibility {
|
|
|
615
619
|
}
|
|
616
620
|
|
|
617
621
|
export interface RunReceiptUpstreamResponse {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
622
|
+
requestedModelId: string | null;
|
|
623
|
+
responseModelIdObservation: ResponseModelIdObservation;
|
|
624
|
+
differingResponseModelId: string | null;
|
|
625
|
+
providerResponseId: string | null;
|
|
621
626
|
}
|
|
622
627
|
|
|
623
628
|
/**
|
|
@@ -677,6 +682,8 @@ export interface RunReceipt {
|
|
|
677
682
|
agentAudience?: AgentAudience;
|
|
678
683
|
requestOrigin?: DispatchRequestOrigin;
|
|
679
684
|
task: string;
|
|
685
|
+
/** Integrity-sealed recipe policy, invocation request, effective phase, and admission reasons. */
|
|
686
|
+
budget?: RunToolBudgetEnvelope;
|
|
680
687
|
/** Proof of briefing content without copying its prose into the receipt. */
|
|
681
688
|
briefing?: RunBriefingProvenance;
|
|
682
689
|
/** Sent steering provenance in stable per-run sequence order; prose is never persisted. */
|
|
@@ -11,6 +11,7 @@ import { cloneValidatedResponseSchema } from "../../core/response-schema.js";
|
|
|
11
11
|
import { isToolProfileName, type ToolProfileName } from "../../tools/profiles.js";
|
|
12
12
|
import type { AgentProduct } from "../agents/spec.js";
|
|
13
13
|
import { type AutonomyLevel, isAutonomyLevel } from "../safety/autonomy.js";
|
|
14
|
+
import { cloneDispatchBudgetRequest, type DispatchBudgetRequest } from "./budget-envelope.js";
|
|
14
15
|
import {
|
|
15
16
|
EXECUTION_HANDOFF_MAX_ITEMS,
|
|
16
17
|
EXECUTION_HANDOFF_MAX_TEXT_BYTES,
|
|
@@ -76,6 +77,8 @@ export interface JobSpec {
|
|
|
76
77
|
target?: string;
|
|
77
78
|
model?: string;
|
|
78
79
|
thinkingLevel?: JobThinkingLevel;
|
|
80
|
+
/** Invocation phase and optional preauthorized retry or revision ceiling. */
|
|
81
|
+
budget?: DispatchBudgetRequest;
|
|
79
82
|
/**
|
|
80
83
|
* What this run delivers, which decides what its reserve window admits.
|
|
81
84
|
* "orientation" adds `code_nav` to the delivery tools kept live inside the
|
|
@@ -180,6 +183,7 @@ const KNOWN_KEYS = new Set([
|
|
|
180
183
|
"target",
|
|
181
184
|
"model",
|
|
182
185
|
"thinkingLevel",
|
|
186
|
+
"budget",
|
|
183
187
|
"node",
|
|
184
188
|
"routingIntent",
|
|
185
189
|
"failover",
|
|
@@ -378,6 +382,15 @@ export function validateJobSpec(spec: unknown): Validated {
|
|
|
378
382
|
}
|
|
379
383
|
}
|
|
380
384
|
|
|
385
|
+
let budget: DispatchBudgetRequest | undefined;
|
|
386
|
+
if ("budget" in spec && spec.budget !== undefined) {
|
|
387
|
+
try {
|
|
388
|
+
budget = cloneDispatchBudgetRequest(spec.budget);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
381
394
|
if ("requiredCapabilities" in spec && spec.requiredCapabilities !== undefined) {
|
|
382
395
|
if (!Array.isArray(spec.requiredCapabilities) || spec.requiredCapabilities.some((c) => typeof c !== "string")) {
|
|
383
396
|
errors.push("requiredCapabilities must be a string[]");
|
|
@@ -530,6 +543,7 @@ export function validateJobSpec(spec: unknown): Validated {
|
|
|
530
543
|
out.reroutes = spec.reroutes.map((hop) => ({ ...hop }));
|
|
531
544
|
}
|
|
532
545
|
if (typeof spec.thinkingLevel === "string") out.thinkingLevel = spec.thinkingLevel as JobThinkingLevel;
|
|
546
|
+
if (budget !== undefined) out.budget = budget;
|
|
533
547
|
if (Array.isArray(spec.requiredCapabilities)) {
|
|
534
548
|
out.requiredCapabilities = spec.requiredCapabilities.map((c) => String(c));
|
|
535
549
|
}
|
|
@@ -133,6 +133,16 @@ export interface WorkerProcessOptions {
|
|
|
133
133
|
approvedIdentity?: ApprovedWorkerIdentity;
|
|
134
134
|
/** How long bulk output may precede the announce before the peer is refused. */
|
|
135
135
|
attestationGraceMs?: number;
|
|
136
|
+
/**
|
|
137
|
+
* Clock behind the heartbeat stamp, defaulting to `Date.now`. The stamp stays
|
|
138
|
+
* a wall-clock instant in production because the ledger, the receipt, and the
|
|
139
|
+
* evidence record all serialize it as ISO-8601 for an operator to read. That
|
|
140
|
+
* leaves a test with no honest way to assert the stamp advanced: comparing
|
|
141
|
+
* two wall-clock reads taken either side of an interval orders them backward
|
|
142
|
+
* on any host that resyncs its clock in between. A test injects the steppable
|
|
143
|
+
* clock from tests/harness/clock.ts here and asserts on that instead.
|
|
144
|
+
*/
|
|
145
|
+
now?: () => number;
|
|
136
146
|
}
|
|
137
147
|
|
|
138
148
|
/**
|
|
@@ -181,6 +191,7 @@ export function spawnWorkerProcess(
|
|
|
181
191
|
): SpawnedWorker {
|
|
182
192
|
const shutdownGraceMs = opts?.shutdownGraceMs ?? DEFAULT_SHUTDOWN_GRACE_MS;
|
|
183
193
|
const attestationGraceMs = opts?.attestationGraceMs ?? DEFAULT_ATTESTATION_GRACE_MS;
|
|
194
|
+
const now = opts?.now ?? Date.now;
|
|
184
195
|
const approved = opts?.approvedIdentity ?? approvedIdentityForSpec(spec);
|
|
185
196
|
|
|
186
197
|
const child: ChildProcess = spawn(command, [...args], {
|
|
@@ -193,7 +204,7 @@ export function spawnWorkerProcess(
|
|
|
193
204
|
});
|
|
194
205
|
const pid = child.pid ?? null;
|
|
195
206
|
|
|
196
|
-
const heartbeatAt = { current:
|
|
207
|
+
const heartbeatAt = { current: now() };
|
|
197
208
|
|
|
198
209
|
const queue = createBoundedEventQueue();
|
|
199
210
|
const waiters: Array<(r: IteratorResult<unknown>) => void> = [];
|
|
@@ -222,7 +233,7 @@ export function spawnWorkerProcess(
|
|
|
222
233
|
}
|
|
223
234
|
|
|
224
235
|
function push(value: unknown): void {
|
|
225
|
-
heartbeatAt.current =
|
|
236
|
+
heartbeatAt.current = now();
|
|
226
237
|
const w = waiters.shift();
|
|
227
238
|
if (w) {
|
|
228
239
|
w({ value, done: false });
|
|
@@ -367,7 +378,7 @@ export function spawnWorkerProcess(
|
|
|
367
378
|
if (!announceAccepted) failAttestation(`Invalid worker attestation: ${frame.reason}`);
|
|
368
379
|
return;
|
|
369
380
|
}
|
|
370
|
-
heartbeatAt.current =
|
|
381
|
+
heartbeatAt.current = now();
|
|
371
382
|
if (frame.value.kind === "announce") {
|
|
372
383
|
if (announceAccepted || announceFailed) return;
|
|
373
384
|
const verdict = verifyWorkerAttestation(frame.value.attestation, approved);
|
|
@@ -64,119 +64,3 @@ export function evidenceMetricsFromReceipt(receipt: RunReceipt): Record<string,
|
|
|
64
64
|
...(typeof receipt.costUsd === "number" && Number.isFinite(receipt.costUsd) ? { "cost.usd": receipt.costUsd } : {}),
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Count terminal dispatch tool calls in a `clio-coder run --json` stream: the
|
|
70
|
-
* decomposition checkpoint. Mirrors the dedup rules of the tool-call metric
|
|
71
|
-
* fold (canonical clio finishes win over pi execution ends when both exist).
|
|
72
|
-
* An absent or eventless stream counts zero.
|
|
73
|
-
*/
|
|
74
|
-
export function dispatchCountFromJsonl(stdout: string): number {
|
|
75
|
-
let executionEnds = 0;
|
|
76
|
-
let canonicalFinishes = 0;
|
|
77
|
-
const seenExecutionEnds = new Set<string>();
|
|
78
|
-
const seenCanonicalFinishes = new Set<string>();
|
|
79
|
-
let sawCanonicalFinishEvent = false;
|
|
80
|
-
for (const event of jsonlEvents(stdout)) {
|
|
81
|
-
if (event.type === "tool_execution_end") {
|
|
82
|
-
if (stringField(event, "toolName") !== "dispatch") continue;
|
|
83
|
-
const callId = stringField(event, "toolCallId");
|
|
84
|
-
if (callId !== undefined) {
|
|
85
|
-
if (seenExecutionEnds.has(callId)) continue;
|
|
86
|
-
seenExecutionEnds.add(callId);
|
|
87
|
-
}
|
|
88
|
-
executionEnds += 1;
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (event.type !== "clio_tool_finish" || !isRecord(event.payload)) continue;
|
|
92
|
-
sawCanonicalFinishEvent = true;
|
|
93
|
-
if (stringField(event.payload, "tool") !== "dispatch") continue;
|
|
94
|
-
const callId = stringField(event.payload, "toolCallId") ?? stringField(event, "toolCallId");
|
|
95
|
-
if (callId !== undefined) {
|
|
96
|
-
if (seenCanonicalFinishes.has(callId)) continue;
|
|
97
|
-
seenCanonicalFinishes.add(callId);
|
|
98
|
-
}
|
|
99
|
-
canonicalFinishes += 1;
|
|
100
|
-
}
|
|
101
|
-
return sawCanonicalFinishEvent ? canonicalFinishes : executionEnds;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** Count model-authored dispatch calls whose normalized task set includes Scout. */
|
|
105
|
-
export function scoutDispatchCountFromJsonl(stdout: string): number {
|
|
106
|
-
const seen = new Set<string>();
|
|
107
|
-
let count = 0;
|
|
108
|
-
for (const event of jsonlEvents(stdout)) {
|
|
109
|
-
if (event.type !== "tool_execution_start" || stringField(event, "toolName") !== "dispatch") continue;
|
|
110
|
-
if (!isRecord(event.args) || !dispatchArgsIncludeAgent(event.args, "scout")) continue;
|
|
111
|
-
const callId = stringField(event, "toolCallId");
|
|
112
|
-
if (callId !== undefined) {
|
|
113
|
-
if (seen.has(callId)) continue;
|
|
114
|
-
seen.add(callId);
|
|
115
|
-
}
|
|
116
|
-
count += 1;
|
|
117
|
-
}
|
|
118
|
-
return count;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function dispatchArgsIncludeAgent(args: Record<string, unknown>, expectedAgent: string): boolean {
|
|
122
|
-
const sharedAgent = stringField(args, "agent") ?? stringField(args, "agent_id") ?? stringField(args, "agentId");
|
|
123
|
-
let tasks: unknown = args.tasks;
|
|
124
|
-
if (typeof tasks === "string") {
|
|
125
|
-
const trimmed = tasks.trim();
|
|
126
|
-
if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
|
|
127
|
-
try {
|
|
128
|
-
tasks = JSON.parse(trimmed) as unknown;
|
|
129
|
-
} catch {
|
|
130
|
-
// A plain task string keeps using the shared agent below.
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
const taskList: unknown[] = Array.isArray(tasks) ? tasks : tasks === undefined ? [{ task: args.task }] : [tasks];
|
|
135
|
-
return taskList.some((task: unknown) => {
|
|
136
|
-
if (typeof task === "string") return sharedAgent?.toLowerCase() === expectedAgent;
|
|
137
|
-
if (!isRecord(task)) return false;
|
|
138
|
-
const agent =
|
|
139
|
-
stringField(task, "agent") ?? stringField(task, "agent_id") ?? stringField(task, "agentId") ?? sharedAgent;
|
|
140
|
-
return agent?.toLowerCase() === expectedAgent;
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Stale-wiki behavioral checkpoint: true when the stream shows a live source
|
|
146
|
-
* read (read/grep start) AFTER a `code_nav mode=wiki` start, i.e. the model
|
|
147
|
-
* did not answer from the wiki alone. Absent either half → false.
|
|
148
|
-
*/
|
|
149
|
-
export function wikiStaleAcknowledgedFromJsonl(stdout: string): boolean {
|
|
150
|
-
let sawWikiLookup = false;
|
|
151
|
-
for (const event of jsonlEvents(stdout)) {
|
|
152
|
-
if (event.type !== "tool_execution_start") continue;
|
|
153
|
-
const toolName = stringField(event, "toolName");
|
|
154
|
-
if (toolName === "code_nav" && isRecord(event.args) && event.args.mode === "wiki") {
|
|
155
|
-
sawWikiLookup = true;
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
if (sawWikiLookup && (toolName === "read" || toolName === "grep")) return true;
|
|
159
|
-
}
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function* jsonlEvents(stdout: string): Generator<Record<string, unknown>> {
|
|
164
|
-
for (const line of stdout.split(/\r?\n/)) {
|
|
165
|
-
if (line.trim().length === 0) continue;
|
|
166
|
-
try {
|
|
167
|
-
const parsed: unknown = JSON.parse(line);
|
|
168
|
-
if (isRecord(parsed)) yield parsed;
|
|
169
|
-
} catch {
|
|
170
|
-
// Non-JSON noise (including the pretty receipt tail) is not an event.
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function stringField(record: Record<string, unknown>, field: string): string | undefined {
|
|
176
|
-
const value = record[field];
|
|
177
|
-
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
181
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
182
|
-
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Invariant metrics: the promises Clio makes about its own machinery, read
|
|
3
3
|
* from the journal one eval item left behind.
|
|
4
4
|
*
|
|
5
|
-
* These invert what a
|
|
5
|
+
* These invert what a task outcome measures. A weak model that never solved the
|
|
6
6
|
* task leaves every one of them intact; a strong model that solved it leaves
|
|
7
7
|
* them broken the moment Clio failed to seal, to agree with itself, or to
|
|
8
8
|
* write a receipt its own ledger can authenticate. The task outcome and the
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { shellQuote } from "../../../core/shell-quote.js";
|
|
2
|
-
import {
|
|
3
|
-
dispatchCountFromJsonl,
|
|
4
|
-
evidenceMetricsFromReceipt,
|
|
5
|
-
receiptFromRunJsonStdout,
|
|
6
|
-
scoutDispatchCountFromJsonl,
|
|
7
|
-
wikiStaleAcknowledgedFromJsonl,
|
|
8
|
-
} from "../metrics/evidence.js";
|
|
2
|
+
import { evidenceMetricsFromReceipt, receiptFromRunJsonStdout } from "../metrics/evidence.js";
|
|
9
3
|
import { streamInvariantMetrics } from "../metrics/invariants.js";
|
|
10
4
|
import { tokenMetricEntries } from "../metrics/token-stream.js";
|
|
11
5
|
import type { EvalRunnerV2, EvalSuiteTargetV2 } from "../schema/suite.js";
|
|
@@ -58,9 +52,6 @@ export async function runClioRunRunner(
|
|
|
58
52
|
"tools.failed": tools.failed,
|
|
59
53
|
"tools.blocked": tools.blocked,
|
|
60
54
|
"verifier.exitCode": result.exitCode,
|
|
61
|
-
"dispatch.count": dispatchCountFromJsonl(toolMetricStream),
|
|
62
|
-
"dispatch.scoutCount": scoutDispatchCountFromJsonl(toolMetricStream),
|
|
63
|
-
"wiki.staleAcknowledged": wikiStaleAcknowledgedFromJsonl(toolMetricStream),
|
|
64
55
|
...(receipt === null ? {} : evidenceMetricsFromReceipt(receipt)),
|
|
65
56
|
...(receipt === null
|
|
66
57
|
? {}
|