@iowarp/clio-coder 0.4.3 → 0.4.4
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 +63 -0
- package/README.md +1 -1
- package/dist/{acp-H2NGRPWO.js → acp-WNAYYF4F.js} +4 -5
- package/dist/{agents-TL5LLUQP.js → agents-3OKXHLOI.js} +33 -31
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-E5SW4HMS.js → auth-VKNNMGPU.js} +10 -11
- package/dist/{builtins-IA7V7FUC.js → builtins-WGALA46I.js} +4 -4
- package/dist/{chunk-F5JHEYZM.js → chunk-23L32XTI.js} +10 -7
- package/dist/{chunk-DWUOQKRU.js → chunk-25QBEXRS.js} +2 -2
- package/dist/{chunk-LLDJM5XK.js → chunk-26QSH3EJ.js} +2 -2
- package/dist/{chunk-N2Z7HLVY.js → chunk-2ASED4PZ.js} +10 -10
- package/dist/{chunk-MCEPRMZW.js → chunk-2CU2H6KE.js} +2 -2
- package/dist/chunk-2DSOYNFC.js +108 -0
- package/dist/{chunk-LDJG7DW3.js → chunk-2ZSONWVL.js} +4 -4
- package/dist/{chunk-GI7YYQ3F.js → chunk-36CT5VVL.js} +324 -41
- package/dist/{chunk-4UVU7BJ5.js → chunk-3GY4F45V.js} +2 -2
- package/dist/{chunk-PUVDKJ2Y.js → chunk-3ODX73FK.js} +3 -5
- package/dist/{chunk-IUE3Y34X.js → chunk-3UNOLWNZ.js} +2 -2
- package/dist/{chunk-6PTFB5VS.js → chunk-3UUXNFEX.js} +8 -4
- package/dist/{chunk-2APPQIER.js → chunk-4M6Z5QVF.js} +4 -4
- package/dist/{chunk-K6BSR66V.js → chunk-4NSRCOYP.js} +4 -1
- package/dist/{chunk-JGRC33J2.js → chunk-54X7T7DK.js} +12 -3
- package/dist/{chunk-YQWYVTMC.js → chunk-5636DCO5.js} +4 -4
- package/dist/{chunk-7E7I3WLS.js → chunk-57XXR6DR.js} +23 -22
- package/dist/{chunk-7ZYNNDKC.js → chunk-5TUB6SLS.js} +2 -2
- package/dist/{chunk-VW6DOEDG.js → chunk-6OSVSQL5.js} +11 -2
- package/dist/{chunk-L47TF46W.js → chunk-6PAZTBPA.js} +14 -2
- package/dist/{chunk-XIVNBFZS.js → chunk-6Q3CYFD3.js} +34 -16
- package/dist/{chunk-RSJ25QSL.js → chunk-6QOTUPRG.js} +155 -36
- package/dist/{chunk-JKKCYP3C.js → chunk-6UINWWS6.js} +10 -4
- package/dist/{chunk-WBKFA554.js → chunk-72YIHOZQ.js} +2 -2
- package/dist/{chunk-PAJQJ7BS.js → chunk-75W7L2E2.js} +246 -697
- package/dist/{chunk-RRNP2ANY.js → chunk-7UGL4MB5.js} +4 -4
- package/dist/{chunk-EKCHAPYA.js → chunk-AUPNRN7C.js} +2 -2
- package/dist/{chunk-ZDN3Y73Y.js → chunk-BJVFZO5U.js} +6 -48
- package/dist/{chunk-IWT4SF4R.js → chunk-CUSRQKPU.js} +64 -2
- package/dist/{chunk-SKHCAU7K.js → chunk-DQOVN6KV.js} +3 -2
- package/dist/{chunk-ZWPRK62N.js → chunk-DT3LWJOB.js} +4 -4
- package/dist/chunk-DXKJURES.js +671 -0
- package/dist/{chunk-BEPZRGGU.js → chunk-EL24TAU4.js} +2 -2
- package/dist/{chunk-OML5D5V5.js → chunk-ELWDPP3Y.js} +5 -5
- package/dist/{chunk-NDINPTJ4.js → chunk-ELZVTCGV.js} +5 -4
- package/dist/chunk-EXLD33WO.js +381 -0
- package/dist/{chunk-IJNZMHLA.js → chunk-FEAXX7B6.js} +2 -2
- package/dist/{chunk-TM6LQDI3.js → chunk-FFUPXJC4.js} +74 -323
- package/dist/chunk-GX5WYQO4.js +59 -0
- package/dist/chunk-I2DWJ4GM.js +390 -0
- package/dist/{chunk-TXOTCRLG.js → chunk-I5FWO7L5.js} +5 -5
- package/dist/chunk-IRXAATOX.js +539 -0
- package/dist/chunk-IXIY2H4R.js +44 -0
- package/dist/{chunk-KK4JZPBQ.js → chunk-IZXGRF7P.js} +76 -10
- package/dist/{chunk-5TSRNF4G.js → chunk-JCI2ROMZ.js} +164 -6
- package/dist/{chunk-INY6HTFL.js → chunk-JEIYHLOR.js} +2 -2
- package/dist/{chunk-MUW2BDDH.js → chunk-JQLNNIKT.js} +2 -2
- package/dist/{chunk-XPWWI35G.js → chunk-JSD46VO2.js} +19 -8
- package/dist/{chunk-VA5FNYMT.js → chunk-JT2RFCC5.js} +39 -2
- package/dist/{chunk-V2ANDPVT.js → chunk-K6T2ZAMZ.js} +168 -6
- package/dist/{chunk-B4OAX3SI.js → chunk-KFV5L5SK.js} +13 -6
- package/dist/{chunk-JEQ3XTHC.js → chunk-LLXSDWXS.js} +2 -2
- package/dist/{chunk-E3TPLWFX.js → chunk-LTIKRKFL.js} +3 -3
- package/dist/{chunk-NIQJ66N4.js → chunk-N56KALIC.js} +17 -17
- package/dist/{chunk-JDAY6FIL.js → chunk-NAI6ZFCY.js} +13 -9
- package/dist/{chunk-AF4YM7Z4.js → chunk-NRO2BJRH.js} +2490 -2182
- package/dist/{chunk-WRBAGUNF.js → chunk-NXIMQY5W.js} +2 -2
- package/dist/{chunk-I7ZPNEJM.js → chunk-NXYCB2VD.js} +5 -5
- package/dist/chunk-ODGTEFFI.js +50 -0
- package/dist/{chunk-2VIKGWFZ.js → chunk-OEJSLEPW.js} +2 -2
- package/dist/{chunk-KKOJXO6R.js → chunk-OMQNJVKW.js} +4 -2
- package/dist/{chunk-54CBCGIR.js → chunk-Q4WO54TA.js} +132 -77
- package/dist/{chunk-B4VEBZKF.js → chunk-QUFRYSWI.js} +12 -6
- package/dist/{chunk-42FMPA75.js → chunk-QZWQA4DE.js} +2 -2
- package/dist/{chunk-HEQY7ZFI.js → chunk-RAY4OVGZ.js} +2 -2
- package/dist/{chunk-G76U63X4.js → chunk-RQCKCSRL.js} +7 -7
- package/dist/{chunk-64I3JVYM.js → chunk-RXTN6AKH.js} +2 -2
- package/dist/{chunk-UPZU6GE4.js → chunk-RZDWV63N.js} +3 -3
- package/dist/{chunk-2UH2KFUP.js → chunk-S6PYF2XF.js} +2 -2
- package/dist/{chunk-JSC3U7TI.js → chunk-TOIVGRUX.js} +2 -2
- package/dist/{chunk-7DRAWPTZ.js → chunk-TQAHXW6Y.js} +1 -1
- package/dist/{chunk-JIEGK6UF.js → chunk-U6TMQNSI.js} +48 -4
- package/dist/{chunk-AX2THNSA.js → chunk-UEPWCCTY.js} +4 -4
- package/dist/{chunk-QWGDJJYJ.js → chunk-USR47QNF.js} +4 -4
- package/dist/{chunk-Y3CBHOR6.js → chunk-V6HJFQZE.js} +2 -2
- package/dist/chunk-V76WTFTW.js +318 -0
- package/dist/{chunk-NMJXSHBJ.js → chunk-W54I7H25.js} +2 -2
- package/dist/{chunk-2UG5F4C5.js → chunk-XULDXHTN.js} +24 -12
- package/dist/chunk-XXYSBZIQ.js +283 -0
- package/dist/{chunk-MWUZBSAQ.js → chunk-Y55JBDO5.js} +331 -51
- package/dist/{chunk-W6RRQCPQ.js → chunk-YD5GIKET.js} +4 -4
- package/dist/{chunk-CE5AX47J.js → chunk-YECAMM3D.js} +2 -2
- package/dist/{chunk-WCXUNS7U.js → chunk-YNFKXPEC.js} +6 -6
- package/dist/cli/index.js +36 -35
- package/dist/{clio-CMMK4KRR.js → clio-QLICPCF5.js} +2 -2
- package/dist/{code-nav-MDZNQS33.js → code-nav-IJR2DBPR.js} +6 -6
- package/dist/{components-UCUQ4QXW.js → components-2TGAI2RC.js} +3 -4
- package/dist/{config-SVM5P5YI.js → config-IUA6OYNS.js} +52 -47
- package/dist/{configure-LE3IK2TJ.js → configure-VEPX4NMX.js} +14 -15
- package/dist/{context-VNCR7KAG.js → context-2DKHWH2T.js} +23 -23
- package/dist/{context-2OHRKS42.js → context-4MPR7WKB.js} +48 -42
- package/dist/{context-E3VC7RX5.js → context-BOYF5EJM.js} +11 -11
- package/dist/{context-clear-BW4O37TG.js → context-clear-S4ZJCQUX.js} +48 -42
- package/dist/{context-working-set-VDS25HXZ.js → context-working-set-3I3FYX6Y.js} +8 -8
- package/dist/detail-A7JAVSIG.js +98 -0
- package/dist/{dispatch-runner-5AHT53RF.js → dispatch-runner-RJ5I2F2O.js} +71 -58
- package/dist/{docs-PD3EXDKU.js → docs-SPOV3BAN.js} +3 -5
- package/dist/{doctor-WNNVO6FY.js → doctor-DKICC2SN.js} +54 -31
- package/dist/{eval-7G7SGAYO.js → eval-OQOQUDHK.js} +47 -56
- package/dist/{evidence-VD6736FQ.js → evidence-4DQ25GUQ.js} +53 -150
- package/dist/evidence-4F5USFKH.js +208 -0
- package/dist/{evolve-AL3NGVRL.js → evolve-GSS52E5J.js} +44 -41
- package/dist/{extensions-MOVJ32NM.js → extensions-G7MFLYHT.js} +4 -5
- package/dist/{fleet-QZHUMAGI.js → fleet-Q37YHHAQ.js} +74 -68
- package/dist/{fleet-commands-BAYT5FJZ.js → fleet-commands-G7E4N7SM.js} +14 -11
- package/dist/{fleet-decisions-IREVMRU4.js → fleet-decisions-O7M6QBA2.js} +6 -6
- package/dist/{fleet-graph-YCTT3HTI.js → fleet-graph-TOUBW6OW.js} +13 -13
- package/dist/{fleet-inspect-QVJTDAVB.js → fleet-inspect-SW33JJNI.js} +43 -39
- package/dist/{fleet-preflight-25QAFPK4.js → fleet-preflight-CV2655TW.js} +2 -3
- package/dist/{fleet-validate-5O57AAJ7.js → fleet-validate-KESZX2YH.js} +16 -16
- package/dist/{fleet-verify-CPH2W2T6.js → fleet-verify-UQPMTVE3.js} +42 -38
- package/dist/{fleet-view-SWBR3VGQ.js → fleet-view-MN2VG4MR.js} +43 -39
- package/dist/{init-J477LKZH.js → init-PXEXQSBF.js} +60 -54
- package/dist/{interop-3FCM6XLG.js → interop-ZG5T62U3.js} +6 -7
- package/dist/inventory-C26CFDRR.js +101 -0
- package/dist/{library-QUQEIUG6.js → library-B2W4N74O.js} +16 -17
- package/dist/{memory-SGGSEP65.js → memory-YCANYS5A.js} +45 -42
- package/dist/{models-HEKUAXXK.js → models-GERTU3YI.js} +20 -22
- package/dist/{monitor-HKU57TYQ.js → monitor-CPNIUULB.js} +50 -44
- package/dist/{orchestrator-VDFAEFAI.js → orchestrator-J4BSH4WQ.js} +491 -1182
- package/dist/{panes-DN2SSFOH.js → panes-BOHAEGYC.js} +3 -3
- package/dist/{panes-TALGNPZT.js → panes-NXSLDQZ2.js} +5 -6
- package/dist/{paths-NBMFAIEZ.js → paths-VSUWNC22.js} +3 -4
- package/dist/{reset-EAJFFJVB.js → reset-TNWTB5LU.js} +5 -6
- package/dist/{resources-OVKSEFVE.js → resources-4PXNMD5G.js} +14 -14
- package/dist/{run-7DP7ZF2J.js → run-D6XJ34CN.js} +79 -83
- package/dist/{share-WML67FT3.js → share-2NWMJJEE.js} +14 -15
- package/dist/{skills-SG662R2K.js → skills-KR7WON5G.js} +18 -19
- package/dist/{skills-eval-VVZEUU46.js → skills-eval-O2ZNOLDS.js} +49 -46
- package/dist/{skills-inventory-I2E23GET.js → skills-inventory-ZZOUBK7O.js} +14 -14
- package/dist/{slash-commands-S7MBJDQK.js → slash-commands-ZXPJD64J.js} +32 -23
- package/dist/{steer-2LQOMCPB.js → steer-XA25PSCS.js} +3 -3
- package/dist/{support-CC2UJBJ6.js → support-7EMVWYG2.js} +2 -2
- package/dist/{targets-4QC3HIEW.js → targets-OMH2XCSN.js} +25 -27
- package/dist/tasks-IPAGMEIX.js +36 -0
- package/dist/{terminal-lease-TUHIJ6Y2.js → terminal-lease-C2J3JYRE.js} +4 -4
- package/dist/{tools-TFGJICCU.js → tools-EFFEAIDP.js} +5 -6
- package/dist/{uninstall-5PEVOE5B.js → uninstall-HALS6BLF.js} +7 -8
- package/dist/{upgrade-M4WXY6KN.js → upgrade-MS72RJEP.js} +14 -11
- package/dist/{usage-N7ZNVLEM.js → usage-NHG6MCJM.js} +60 -51
- package/dist/{verifiers-DJTP4XX6.js → verifiers-7AUNVXDY.js} +150 -17
- package/dist/{verify-RWE4PPEK.js → verify-FWYGPKMR.js} +12 -10
- package/dist/{web-fetch-MPARV2K7.js → web-fetch-V4FKSDAV.js} +4 -4
- package/dist/{wiki-generate-C7IQOXSP.js → wiki-generate-743CIGJW.js} +63 -56
- package/dist/{with-panes-4GCGSL7J.js → with-panes-BDQEWBRT.js} +2 -2
- package/dist/worker/entry.js +36 -33
- package/docs/README.md +3 -2
- package/docs/architecture/acp.md +17 -0
- package/docs/architecture/artifact-versions.md +1 -1
- package/docs/architecture/dispatch-typed-intent.md +1 -1
- package/docs/architecture/evidence-and-memory.md +1 -1
- package/docs/architecture/observability.md +7 -1
- package/docs/architecture/prompt-envelope-and-tools.md +15 -5
- package/docs/architecture/safety-model.md +10 -17
- package/docs/architecture/tui-design.md +1 -1
- package/docs/guide/built-in-agents.md +8 -8
- package/docs/guide/commands-and-modes.md +17 -2
- package/docs/guide/configuration-and-targets.md +3 -1
- package/docs/guide/configuration-reference.md +10 -5
- package/docs/guide/environment-variables.md +2 -2
- package/docs/guide/tool-usage.md +78 -3
- package/docs/history/config-knobs-audit.md +2 -2
- package/docs/process/development-pipeline.md +6 -1
- package/docs/process/git-commit-provenance.md +15 -0
- package/docs/process/release-cut-checklist.md +207 -0
- package/docs/process/scientific-validation.md +18 -17
- package/evals/behavioral-machinery-support.ts +1 -0
- package/evals/behavioral-machinery.yaml +1 -1
- package/package.json +1 -1
- package/skills/coding/coding-standards/SKILL.md +14 -4
- package/skills/context/context-handoff/SKILL.md +3 -3
- package/skills/context/context-prime/SKILL.md +3 -3
- package/skills/planning/architecture/SKILL.md +5 -5
- package/skills/planning/backlog/SKILL.md +4 -4
- package/skills/planning/prd/SKILL.md +4 -3
- package/skills/planning/product-intent/SKILL.md +8 -8
- package/skills/planning/tech-spec/SKILL.md +4 -4
- package/skills/registry.yaml +32 -32
- package/skills/research/arxiv-literature/SKILL.md +3 -3
- package/skills/research/experiment-protocol/SKILL.md +3 -3
- package/skills/research/scientific-debugging/SKILL.md +3 -2
- package/skills/research/scientific-modernization/SKILL.md +3 -3
- package/skills/skill-marketplace.json +16 -16
- package/skills/workflow/cut-it/SKILL.md +3 -4
- package/skills/workflow/design-council/SKILL.md +5 -10
- package/skills/workflow/grill-me/SKILL.md +14 -15
- package/skills/workflow/workflow-distiller/SKILL.md +4 -4
- package/src/cli/args.ts +0 -8
- package/src/cli/configure.ts +2 -1
- package/src/cli/doctor-state-size.ts +1 -12
- package/src/cli/doctor-validation-contract.ts +28 -0
- package/src/cli/doctor.ts +5 -0
- package/src/cli/evidence-detail.ts +1 -75
- package/src/cli/evidence-inventory.ts +1 -167
- package/src/cli/index.ts +2 -0
- package/src/cli/run.ts +0 -2
- package/src/cli/targets.ts +1 -1
- package/src/cli/tasks.ts +84 -0
- package/src/cli/upgrade.ts +10 -5
- package/src/cli/usage.ts +6 -0
- package/src/cli/verifiers.ts +147 -1
- package/src/cli/wiki-generate.ts +1 -0
- package/src/core/commit-attribution.ts +41 -1
- package/src/core/git-commit-attribution.ts +46 -3
- package/src/core/run-overrides.ts +0 -5
- package/src/core/skill-activation.ts +3 -0
- package/src/core/tool-names.ts +5 -2
- package/src/domains/agents/builtins/architect.md +1 -1
- package/src/domains/agents/builtins/coder.md +1 -1
- package/src/domains/agents/builtins/documenter.md +1 -1
- package/src/domains/agents/builtins/git-master.md +1 -1
- package/src/domains/agents/builtins/provenance.md +7 -7
- package/src/domains/agents/builtins/tester.md +1 -1
- package/src/domains/agents/builtins/verifier.md +2 -2
- package/src/domains/agents/builtins/wiki-writer.md +4 -3
- package/src/domains/context/extension.ts +31 -7
- package/src/domains/context/refresh.ts +3 -0
- package/src/domains/context/wiki/frontmatter.ts +5 -2
- package/src/domains/context/wiki/generate.ts +6 -0
- package/src/domains/context/wiki/prompts.ts +43 -0
- package/src/domains/dispatch/active-route-planner.ts +4 -0
- package/src/domains/dispatch/code-step.ts +11 -4
- package/src/domains/dispatch/contract.ts +31 -7
- package/src/domains/dispatch/execution-scheduler.ts +2 -0
- package/src/domains/dispatch/extension.ts +50 -27
- package/src/domains/dispatch/fleet-commit-attribution.ts +5 -0
- package/src/domains/dispatch/fleet-run.ts +1 -0
- package/src/domains/dispatch/host-verification.ts +114 -13
- package/src/domains/dispatch/intent.ts +28 -18
- package/src/domains/dispatch/orphan-recovery.ts +2 -0
- package/src/domains/dispatch/receipt-integrity.ts +4 -0
- package/src/domains/dispatch/reservation-store.ts +5 -3
- package/src/domains/dispatch/state.ts +15 -2
- package/src/domains/dispatch/types.ts +17 -2
- package/src/domains/eval/runners/clio-run.ts +12 -9
- package/src/domains/eval/runners/context-index.ts +2 -7
- package/src/domains/eval/runners/context-init.ts +3 -6
- package/src/domains/eval/runners/external-command.ts +29 -11
- package/src/domains/evidence/build.ts +102 -15
- package/src/domains/evidence/detail.ts +69 -0
- package/src/domains/evidence/eval.ts +13 -1
- package/src/domains/evidence/finish-contract-map.ts +5 -1
- package/src/domains/evidence/inventory.ts +167 -0
- package/src/domains/evidence/store.ts +16 -0
- package/src/domains/evidence/types.ts +12 -0
- package/src/domains/extensions/resources.ts +7 -0
- package/src/domains/middleware/marketplace-offer.ts +20 -1
- package/src/domains/middleware/runtime.ts +7 -3
- package/src/domains/mux/detect.ts +3 -6
- package/src/domains/observability/accountability.ts +15 -1
- package/src/domains/observability/contract.ts +52 -7
- package/src/domains/observability/evidence-index.ts +10 -0
- package/src/domains/observability/extension.ts +9 -5
- package/src/domains/observability/projection.ts +394 -45
- package/src/{interactive → domains/observability}/worker-progress.ts +3 -3
- package/src/domains/prompts/fragments/operating/contract.md +2 -0
- package/src/domains/prompts/fragments/wiki/page.md +8 -0
- package/src/domains/providers/model-discovery.ts +1 -4
- package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +16 -14
- package/src/domains/providers/runtimes/claude/claude-code.ts +9 -0
- package/src/domains/providers/support.ts +1 -1
- package/src/domains/providers/types/runtime-descriptor.ts +1 -1
- package/src/domains/safety/action-classifier.ts +7 -0
- package/src/domains/safety/default-path-policy.ts +2 -0
- package/src/domains/safety/finish-contract-registration.ts +29 -14
- package/src/domains/safety/finish-contract.ts +252 -40
- package/src/domains/safety/index.ts +20 -1
- package/src/domains/safety/policy-engine.ts +26 -6
- package/src/domains/safety/rigor.ts +53 -39
- package/src/domains/safety/validation-contract.ts +388 -0
- package/src/domains/session/archive-readers.ts +10 -1
- package/src/domains/session/decision-board.ts +101 -2
- package/src/domains/session/entries.ts +44 -7
- package/src/domains/session/extension.ts +4 -4
- package/src/domains/session/handoff.ts +2 -1
- package/src/domains/session/manager.ts +2 -3
- package/src/domains/session/task-board.ts +14 -1
- package/src/domains/session/tree/fork.ts +1 -2
- package/src/domains/session/tree/navigator.ts +1 -1
- package/src/domains/user-tasks/acceptance.ts +56 -0
- package/src/domains/user-tasks/active-acceptance.ts +40 -0
- package/src/domains/user-tasks/store.ts +34 -3
- package/src/engine/acp/adapter.ts +24 -6
- package/src/engine/acp/server.ts +21 -4
- package/src/engine/acp/transport.ts +53 -8
- package/src/engine/acp/types.ts +4 -0
- package/src/engine/apis/ollama-native.ts +15 -0
- package/src/engine/claude/subprocess-runtime.ts +107 -60
- package/src/engine/external-subprocess.ts +12 -4
- package/src/entry/orchestrator.ts +11 -2
- package/src/interactive/dispatch-board.ts +46 -717
- package/src/interactive/fleet-run-preview.ts +2 -1
- package/src/interactive/interactive-application.ts +3 -2
- package/src/interactive/interactive-presentation.ts +55 -12
- package/src/interactive/interactive-slash-runtime.ts +2 -1
- package/src/interactive/oracle.ts +5 -2
- package/src/interactive/overlays/fleet-run-approval.ts +3 -2
- package/src/interactive/overlays/message-picker.ts +2 -2
- package/src/interactive/overlays/settings.ts +2 -2
- package/src/interactive/overlays/tree-selector.ts +2 -2
- package/src/interactive/renderers/branch-summary.ts +1 -1
- package/src/interactive/slash-autocomplete.ts +4 -6
- package/src/interactive/slash-commands.ts +25 -39
- package/src/interactive/slash-spec.ts +28 -0
- package/src/interactive/view/artifacts.ts +2 -0
- package/src/interactive/worker-stream.ts +7 -3
- package/src/tools/bootstrap.ts +4 -0
- package/src/tools/builtin-tool-catalog.ts +31 -0
- package/src/tools/compete-worktrees.ts +7 -1
- package/src/tools/core-bootstrap.ts +16 -0
- package/src/tools/decide.ts +136 -0
- package/src/tools/dispatch-admission.ts +21 -0
- package/src/tools/dispatch-plan.ts +5 -2
- package/src/tools/dispatch-runner.ts +15 -1
- package/src/tools/dispatch-types.ts +6 -0
- package/src/tools/evidence.ts +96 -0
- package/src/tools/limitation.ts +76 -0
- package/src/tools/policy.ts +9 -0
- package/src/tools/presentation.ts +3 -0
- package/src/tools/registry.ts +1 -1
- package/src/tools/result-shaping.ts +17 -5
- package/src/tools/task-worktree.ts +13 -3
- package/src/tools/tasks.ts +10 -1
- package/src/tools/verify/authoring.ts +170 -83
- package/src/tools/verify/catalog.ts +122 -5
- package/src/tools/verify/index.ts +2 -1
- package/src/tools/verify/numeric.ts +298 -0
- package/src/tools/verify/perf.ts +143 -0
- package/src/tools/verify/scripts.ts +229 -2
- package/dist/chunk-7DICMOS6.js +0 -313
- package/dist/chunk-RVG5JXAL.js +0 -41
- package/dist/chunk-T56WDKA5.js +0 -183
- package/dist/chunk-VPKWYKEY.js +0 -169
|
@@ -5,6 +5,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
5
5
|
import { PATH_BOUNDARY_MAX_ENTRIES, pathBoundaryCovers, resolvePathBoundary } from "../../core/path-boundary.js";
|
|
6
6
|
import { withStateFileLockSync } from "../../core/state-file-lock.js";
|
|
7
7
|
import { clioStateDir } from "../../core/xdg.js";
|
|
8
|
+
import { judgeNumericTexts, judgePerfTexts } from "../../tools/verify/scripts.js";
|
|
8
9
|
import { FLEET_COMMAND_BASE_ENV, type FleetCommand } from "../agents/fleet-commands.js";
|
|
9
10
|
import { runCodeStep } from "./code-step.js";
|
|
10
11
|
import type { DispatchRequest } from "./contract.js";
|
|
@@ -12,7 +13,7 @@ import type { RunHostVerification, RunHostVerificationAttribution, RunHostVerifi
|
|
|
12
13
|
import { captureWorkspaceSnapshot } from "./write-boundary.js";
|
|
13
14
|
|
|
14
15
|
const OUTPUT_TAIL_BYTES = 2_048;
|
|
15
|
-
const MEMO_VERSION =
|
|
16
|
+
const MEMO_VERSION = 2;
|
|
16
17
|
|
|
17
18
|
type ResolvedCheck = NonNullable<DispatchRequest["resolvedVerification"]>[number];
|
|
18
19
|
|
|
@@ -37,7 +38,7 @@ interface MemoEntry {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
interface MemoFile {
|
|
40
|
-
version:
|
|
41
|
+
version: typeof MEMO_VERSION;
|
|
41
42
|
entries: MemoEntry[];
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -108,10 +109,10 @@ function storeMemoEntry(stateDir: string, entry: MemoEntry): void {
|
|
|
108
109
|
});
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
function memoKey(command: FleetCommand, fingerprint: string, env: NodeJS.ProcessEnv): string {
|
|
112
|
+
function memoKey(command: FleetCommand, judgment: string, fingerprint: string, env: NodeJS.ProcessEnv): string {
|
|
112
113
|
const names = [...FLEET_COMMAND_BASE_ENV, ...command.env];
|
|
113
114
|
const values = Object.fromEntries(names.map((name) => [name, env[name] ?? null]));
|
|
114
|
-
return sha256(JSON.stringify({ fingerprint,
|
|
115
|
+
return sha256(JSON.stringify({ fingerprint, judgment, env: values }));
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
export function hostVerificationRejection(
|
|
@@ -124,10 +125,63 @@ export function hostVerificationRejection(
|
|
|
124
125
|
detail:
|
|
125
126
|
failedCheck === undefined
|
|
126
127
|
? "host verification rejected"
|
|
127
|
-
:
|
|
128
|
+
: failedCheck.report !== undefined
|
|
129
|
+
? `host verification check '${failedCheck.check}' rejected: ${failedCheck.report.summary}`
|
|
130
|
+
: `host verification check '${failedCheck.check}' rejected with exit code ${failedCheck.exitCode}`,
|
|
128
131
|
};
|
|
129
132
|
}
|
|
130
133
|
|
|
134
|
+
function readSealedFile(absolutePath: string): string | undefined {
|
|
135
|
+
try {
|
|
136
|
+
return readFileSync(absolutePath, "utf8");
|
|
137
|
+
} catch {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The kind-specific verdict for a resolved check that already ran. A command
|
|
144
|
+
* check keeps its exit code. A numeric-compare check judges captured stdout
|
|
145
|
+
* against the sealed reference; a perf-budget check judges the measured
|
|
146
|
+
* duration against the sealed budget or baseline. Either judgement turns a
|
|
147
|
+
* clean exit into exit 1 when it fails, and a judgement that cannot be made
|
|
148
|
+
* (missing reference, unparseable output) fails with the reason in the tail.
|
|
149
|
+
*/
|
|
150
|
+
function judgeResolvedCheck(
|
|
151
|
+
resolvedCheck: ResolvedCheck,
|
|
152
|
+
exitCode: number,
|
|
153
|
+
durationMs: number,
|
|
154
|
+
stdout: string,
|
|
155
|
+
): { exitCode: number; report?: RunHostVerificationCheck["report"]; outputTail?: string } {
|
|
156
|
+
if (resolvedCheck.kind === undefined || resolvedCheck.kind === "command" || exitCode !== 0) return { exitCode };
|
|
157
|
+
if (resolvedCheck.kind === "numeric-compare" && resolvedCheck.numeric !== undefined) {
|
|
158
|
+
const referenceText = readSealedFile(resolvedCheck.numeric.reference);
|
|
159
|
+
if (referenceText === undefined) {
|
|
160
|
+
return { exitCode: 1, outputTail: `numeric-compare: reference '${resolvedCheck.numeric.reference}' cannot be read` };
|
|
161
|
+
}
|
|
162
|
+
const report = judgeNumericTexts(
|
|
163
|
+
stdout,
|
|
164
|
+
referenceText,
|
|
165
|
+
resolvedCheck.numeric.tolerance,
|
|
166
|
+
`reference '${resolvedCheck.numeric.reference}'`,
|
|
167
|
+
);
|
|
168
|
+
if (report instanceof Error) return { exitCode: 1, outputTail: `numeric-compare: ${report.message}` };
|
|
169
|
+
return { exitCode: report.passed ? 0 : 1, report, outputTail: outputTail(report.summary) };
|
|
170
|
+
}
|
|
171
|
+
if (resolvedCheck.kind === "perf-budget" && resolvedCheck.perf !== undefined) {
|
|
172
|
+
const baselinePath = resolvedCheck.perf.baseline;
|
|
173
|
+
const report = judgePerfTexts(
|
|
174
|
+
durationMs,
|
|
175
|
+
resolvedCheck.perf,
|
|
176
|
+
baselinePath === undefined ? undefined : readSealedFile(baselinePath),
|
|
177
|
+
`baseline '${baselinePath ?? ""}'`,
|
|
178
|
+
);
|
|
179
|
+
if (report instanceof Error) return { exitCode: 1, outputTail: `perf-budget: ${report.message}` };
|
|
180
|
+
return { exitCode: report.passed ? 0 : 1, report, outputTail: outputTail(report.summary) };
|
|
181
|
+
}
|
|
182
|
+
return { exitCode: 1, outputTail: `${resolvedCheck.kind}: sealed check carries no parameters` };
|
|
183
|
+
}
|
|
184
|
+
|
|
131
185
|
async function runResolvedCheck(input: {
|
|
132
186
|
runId: string;
|
|
133
187
|
index: number;
|
|
@@ -145,9 +199,9 @@ async function runResolvedCheck(input: {
|
|
|
145
199
|
env: [],
|
|
146
200
|
description: `Host verification check ${resolvedCheck.check}.`,
|
|
147
201
|
};
|
|
202
|
+
const judgmentKey = checkDedupeKey(resolvedCheck);
|
|
148
203
|
const fingerprint = workspaceFingerprint(resolvedCheck.cwd);
|
|
149
|
-
const key =
|
|
150
|
-
fingerprint === null ? null : memoKey({ ...command, cwd: resolve(resolvedCheck.cwd) }, fingerprint, input.env);
|
|
204
|
+
const key = fingerprint === null ? null : memoKey(command, judgmentKey, fingerprint, input.env);
|
|
151
205
|
const hit =
|
|
152
206
|
key === null
|
|
153
207
|
? undefined
|
|
@@ -168,17 +222,24 @@ async function runResolvedCheck(input: {
|
|
|
168
222
|
env: input.env,
|
|
169
223
|
});
|
|
170
224
|
const artifactPath = outcome.record.artifactPaths[0];
|
|
225
|
+
const judgement = judgeResolvedCheck(
|
|
226
|
+
resolvedCheck,
|
|
227
|
+
outcome.record.exitCode,
|
|
228
|
+
outcome.record.durationMs,
|
|
229
|
+
outcome.stdout,
|
|
230
|
+
);
|
|
171
231
|
const check: RunHostVerificationCheck = {
|
|
172
232
|
check: resolvedCheck.check,
|
|
173
233
|
argv: [...outcome.record.argv],
|
|
174
234
|
cwd: outcome.record.cwd,
|
|
175
|
-
exitCode:
|
|
235
|
+
exitCode: judgement.exitCode,
|
|
176
236
|
durationMs: outcome.record.durationMs,
|
|
177
237
|
memo: false,
|
|
178
|
-
outputTail: outputTail(outcome.report.outputExcerpt),
|
|
238
|
+
outputTail: judgement.outputTail ?? outputTail(outcome.report.outputExcerpt),
|
|
179
239
|
...(artifactPath !== undefined ? { artifactPath } : {}),
|
|
240
|
+
...(judgement.report !== undefined ? { report: judgement.report } : {}),
|
|
180
241
|
};
|
|
181
|
-
if (check.exitCode === 0 && key !== null) {
|
|
242
|
+
if (check.exitCode === 0 && key !== null && checkDedupeKey(resolvedCheck) === judgmentKey) {
|
|
182
243
|
try {
|
|
183
244
|
storeMemoEntry(input.stateDir, { key, runId: input.runId, check });
|
|
184
245
|
} catch (error) {
|
|
@@ -229,14 +290,45 @@ function implicatedPaths(outputExcerpt: string, checkCwd: string): string[] {
|
|
|
229
290
|
return [...paths].sort();
|
|
230
291
|
}
|
|
231
292
|
|
|
232
|
-
/** Identity
|
|
293
|
+
/** Identity shared by memo reuse and batch deduplication, including the declared judgment and its input files. */
|
|
233
294
|
function checkDedupeKey(resolvedCheck: ResolvedCheck): string {
|
|
295
|
+
const numeric = resolvedCheck.numeric;
|
|
296
|
+
const perf = resolvedCheck.perf;
|
|
234
297
|
return sha256(
|
|
235
298
|
JSON.stringify({
|
|
236
299
|
check: resolvedCheck.check,
|
|
237
300
|
argv: [...resolvedCheck.argv],
|
|
238
301
|
cwd: resolve(resolvedCheck.cwd),
|
|
239
302
|
timeoutMs: resolvedCheck.timeoutMs,
|
|
303
|
+
kind: resolvedCheck.kind ?? "command",
|
|
304
|
+
numeric:
|
|
305
|
+
numeric === undefined
|
|
306
|
+
? undefined
|
|
307
|
+
: {
|
|
308
|
+
reference: numeric.reference,
|
|
309
|
+
referenceDigest: sha256(JSON.stringify(readSealedFile(numeric.reference) ?? null)),
|
|
310
|
+
tolerance: {
|
|
311
|
+
absolute: numeric.tolerance.absolute,
|
|
312
|
+
relative: numeric.tolerance.relative,
|
|
313
|
+
ulp: numeric.tolerance.ulp,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
perf:
|
|
317
|
+
perf === undefined
|
|
318
|
+
? undefined
|
|
319
|
+
: {
|
|
320
|
+
budget:
|
|
321
|
+
perf.budget === undefined
|
|
322
|
+
? undefined
|
|
323
|
+
: {
|
|
324
|
+
wallTimeMs: perf.budget.wallTimeMs,
|
|
325
|
+
tolerance: perf.budget.tolerance,
|
|
326
|
+
},
|
|
327
|
+
baseline: perf.baseline,
|
|
328
|
+
baselineDigest:
|
|
329
|
+
perf.baseline === undefined ? undefined : sha256(JSON.stringify(readSealedFile(perf.baseline) ?? null)),
|
|
330
|
+
tolerance: perf.tolerance,
|
|
331
|
+
},
|
|
240
332
|
}),
|
|
241
333
|
);
|
|
242
334
|
}
|
|
@@ -304,6 +396,12 @@ function memberBoundary(participant: BatchVerificationParticipant): string[] {
|
|
|
304
396
|
* the tree. `basis` is the weakest evidence behind the charge set, never the
|
|
305
397
|
* strongest, so `write_roots` on a receipt means every charged run has a named
|
|
306
398
|
* path inside its own declared boundary.
|
|
399
|
+
*
|
|
400
|
+
* A `worktree: true` member's write roots resolve against its own checkout
|
|
401
|
+
* (`request.cwd`) while the shared check ran in the parent frame, so none of
|
|
402
|
+
* its roots can cover a path the check named: attribution cannot exculpate
|
|
403
|
+
* anyone on its account and every declarer is charged, the conservative
|
|
404
|
+
* fallback those receipts sealed before batch settlement existed.
|
|
307
405
|
*/
|
|
308
406
|
function attributeFailure(input: {
|
|
309
407
|
implicated: ReadonlyArray<string>;
|
|
@@ -421,9 +519,11 @@ export function createBatchVerificationGate(
|
|
|
421
519
|
string,
|
|
422
520
|
{ resolvedCheck: ResolvedCheck; owner: BatchVerificationParticipant; index: number }
|
|
423
521
|
>();
|
|
522
|
+
const checkKeys = new Map<ResolvedCheck, string>();
|
|
424
523
|
for (const member of contributors) {
|
|
425
524
|
for (const resolvedCheck of member.request.resolvedVerification ?? []) {
|
|
426
525
|
const key = checkDedupeKey(resolvedCheck);
|
|
526
|
+
checkKeys.set(resolvedCheck, key);
|
|
427
527
|
if (!distinct.has(key)) distinct.set(key, { resolvedCheck, owner: member, index: distinct.size });
|
|
428
528
|
}
|
|
429
529
|
}
|
|
@@ -448,7 +548,7 @@ export function createBatchVerificationGate(
|
|
|
448
548
|
for (const [key, entry] of ran) {
|
|
449
549
|
if (entry.check.exitCode === 0) continue;
|
|
450
550
|
const declarers = contributors.filter((member) =>
|
|
451
|
-
(member.request.resolvedVerification ?? []).some((candidate) =>
|
|
551
|
+
(member.request.resolvedVerification ?? []).some((candidate) => checkKeys.get(candidate) === key),
|
|
452
552
|
);
|
|
453
553
|
const implicated = implicatedPaths(entry.outputExcerpt, entry.check.cwd);
|
|
454
554
|
const verdict = attributeFailure({ implicated, declarers, wave: members });
|
|
@@ -478,7 +578,8 @@ export function createBatchVerificationGate(
|
|
|
478
578
|
let rejected = false;
|
|
479
579
|
let unimplicated = false;
|
|
480
580
|
for (const resolvedCheck of member.request.resolvedVerification ?? []) {
|
|
481
|
-
const key =
|
|
581
|
+
const key = checkKeys.get(resolvedCheck);
|
|
582
|
+
if (key === undefined) throw new Error("host verification: declared check has no batch identity");
|
|
482
583
|
const shared = ran.get(key);
|
|
483
584
|
if (shared === undefined) continue;
|
|
484
585
|
own.push(
|
|
@@ -271,13 +271,34 @@ export function normalizeDispatchIntent(
|
|
|
271
271
|
if (!Array.isArray(writeRoots)) return writeRoots;
|
|
272
272
|
const relevantPaths = normalizePathList(raw.relevant_paths, "intent.relevant_paths");
|
|
273
273
|
if (!Array.isArray(relevantPaths)) return relevantPaths;
|
|
274
|
-
const expectedOutputs =
|
|
275
|
-
raw.expected_outputs,
|
|
276
|
-
"intent.expected_outputs",
|
|
277
|
-
normalizeExpectedOutputEntry,
|
|
278
|
-
);
|
|
274
|
+
const expectedOutputs = normalizeIntentExpectedOutputs(raw.expected_outputs);
|
|
279
275
|
if (!Array.isArray(expectedOutputs)) return expectedOutputs;
|
|
280
|
-
const
|
|
276
|
+
const verification = normalizeIntentVerification(raw.verification, checks);
|
|
277
|
+
if (!Array.isArray(verification)) return verification;
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
ok: true,
|
|
281
|
+
intent: {
|
|
282
|
+
version: 2,
|
|
283
|
+
readRoots,
|
|
284
|
+
writeRoots,
|
|
285
|
+
relevantPaths,
|
|
286
|
+
pathProvenance: declaredIntentPathProvenance({ readRoots, writeRoots, relevantPaths }),
|
|
287
|
+
expectedOutputs,
|
|
288
|
+
verification,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function normalizeIntentExpectedOutputs(raw: unknown): string[] | DispatchIntentNormalizationResult {
|
|
294
|
+
return normalizePathList(raw, "intent.expected_outputs", normalizeExpectedOutputEntry);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function normalizeIntentVerification(
|
|
298
|
+
raw: unknown,
|
|
299
|
+
checks: ReadonlyMap<string, DispatchIntentCheckBound>,
|
|
300
|
+
): DispatchIntentVerification[] | DispatchIntentNormalizationResult {
|
|
301
|
+
const rawVerification = raw ?? [];
|
|
281
302
|
if (!Array.isArray(rawVerification)) {
|
|
282
303
|
return fail("verification_malformed", "intent.verification must be an array");
|
|
283
304
|
}
|
|
@@ -321,16 +342,5 @@ export function normalizeDispatchIntent(
|
|
|
321
342
|
timeoutMs: Math.min(declared.timeoutMs, Math.max(DISPATCH_INTENT_TIMEOUT_MIN_MS, requested)),
|
|
322
343
|
});
|
|
323
344
|
}
|
|
324
|
-
return
|
|
325
|
-
ok: true,
|
|
326
|
-
intent: {
|
|
327
|
-
version: 2,
|
|
328
|
-
readRoots,
|
|
329
|
-
writeRoots,
|
|
330
|
-
relevantPaths,
|
|
331
|
-
pathProvenance: declaredIntentPathProvenance({ readRoots, writeRoots, relevantPaths }),
|
|
332
|
-
expectedOutputs,
|
|
333
|
-
verification,
|
|
334
|
-
},
|
|
335
|
-
};
|
|
345
|
+
return verification;
|
|
336
346
|
}
|
|
@@ -49,6 +49,7 @@ function receiptsDir(): string {
|
|
|
49
49
|
*/
|
|
50
50
|
function envelopeFromReceipt(receipt: RunReceipt, status: RunStatus, receiptPath: string): RunEnvelope {
|
|
51
51
|
const envelope: RunEnvelope = {
|
|
52
|
+
version: 1,
|
|
52
53
|
id: receipt.runId,
|
|
53
54
|
agentId: receipt.agentId,
|
|
54
55
|
executionRole: receipt.executionRole,
|
|
@@ -97,6 +98,7 @@ function envelopeFromReceipt(receipt: RunReceipt, status: RunStatus, receiptPath
|
|
|
97
98
|
if (receipt.gate !== undefined) envelope.gate = receipt.gate;
|
|
98
99
|
if (receipt.plan !== undefined) envelope.plan = receipt.plan;
|
|
99
100
|
if (receipt.personaOverride !== undefined) envelope.personaOverride = receipt.personaOverride;
|
|
101
|
+
if (receipt.decisionRefs !== undefined) envelope.decisionRefs = receipt.decisionRefs;
|
|
100
102
|
return envelope;
|
|
101
103
|
}
|
|
102
104
|
|
|
@@ -99,6 +99,9 @@ export const RECEIPT_INTEGRITY_FIELD_COVERAGE = {
|
|
|
99
99
|
plan: true,
|
|
100
100
|
fleetGate: true,
|
|
101
101
|
personaOverride: true,
|
|
102
|
+
// Absent unless the parent board held decisions, so older receipts and
|
|
103
|
+
// decision-free runs digest exactly as before.
|
|
104
|
+
decisionRefs: true,
|
|
102
105
|
projectContext: true,
|
|
103
106
|
// Always set on receipts written after #104 landed; absent only on older
|
|
104
107
|
// receipts, which omit these fields from canonical serialization.
|
|
@@ -193,6 +196,7 @@ function ledgerDigestFields(envelope: RunEnvelope): Record<string, unknown> {
|
|
|
193
196
|
executionRole: envelope.executionRole,
|
|
194
197
|
task: envelope.task,
|
|
195
198
|
...(envelope.budget !== undefined ? { budget: envelope.budget } : {}),
|
|
199
|
+
...(envelope.decisionRefs !== undefined ? { decisionRefs: envelope.decisionRefs } : {}),
|
|
196
200
|
targetId: envelope.targetId,
|
|
197
201
|
wireModelId: envelope.wireModelId,
|
|
198
202
|
runtimeId: envelope.runtimeId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { withStateFileLockSync } from "../../core/state-file-lock.js";
|
|
2
|
+
import { withStateFileLock, withStateFileLockSync } from "../../core/state-file-lock.js";
|
|
3
3
|
import { endpointLabel } from "../providers/endpoint-capacity.js";
|
|
4
4
|
import {
|
|
5
5
|
acquireCapacityLease,
|
|
@@ -361,8 +361,10 @@ export function rebindDispatchReservationMember(input: {
|
|
|
361
361
|
costUpperBoundUsd: number;
|
|
362
362
|
capacity: ReservationCapacitySnapshot;
|
|
363
363
|
nowMs?: number;
|
|
364
|
-
}): DispatchReservationRecord {
|
|
365
|
-
|
|
364
|
+
}): Promise<DispatchReservationRecord> {
|
|
365
|
+
// Retry admission runs inside the async dispatch attempt, so it can yield
|
|
366
|
+
// while waiting for the cross-process lock instead of blocking the event loop.
|
|
367
|
+
return withStateFileLock(capacityStateLockPath(), () => {
|
|
366
368
|
const nowMs = input.nowMs ?? Date.now();
|
|
367
369
|
const records = expireRecords(readStore(), nowMs, false);
|
|
368
370
|
const record = records.find((entry) => entry.ownerId === input.ownerId);
|
|
@@ -52,6 +52,7 @@ export interface LedgerOptions {
|
|
|
52
52
|
|
|
53
53
|
export type CreateRunInput = Omit<
|
|
54
54
|
RunEnvelope,
|
|
55
|
+
| "version"
|
|
55
56
|
| "id"
|
|
56
57
|
| "startedAt"
|
|
57
58
|
| "endedAt"
|
|
@@ -143,6 +144,10 @@ export function listFleetRuns(limit = MAX_FLEET_RUN_SCAN): FleetRunRecord[] {
|
|
|
143
144
|
return records.slice(0, Math.max(0, limit));
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
148
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
149
|
+
}
|
|
150
|
+
|
|
146
151
|
// `RunEnvelope.heartbeatAt` deliberately persists only the absolute wall-clock
|
|
147
152
|
// anchor used for operator display and crash recovery. The monotonic heartbeat
|
|
148
153
|
// stamp belongs to the live transport in this process; serializing it would
|
|
@@ -152,9 +157,16 @@ function readRuns(): RunEnvelope[] {
|
|
|
152
157
|
if (!existsSync(path)) return [];
|
|
153
158
|
const raw = readFileSync(path, "utf8").trim();
|
|
154
159
|
if (raw.length === 0) return [];
|
|
155
|
-
const parsed = JSON.parse(raw) as
|
|
160
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
156
161
|
if (!Array.isArray(parsed)) return [];
|
|
157
|
-
|
|
162
|
+
const envelopes: RunEnvelope[] = [];
|
|
163
|
+
for (const entry of parsed) {
|
|
164
|
+
if (!isRecord(entry)) continue;
|
|
165
|
+
const version = entry.version === undefined ? 1 : entry.version;
|
|
166
|
+
if (version !== 1) continue;
|
|
167
|
+
envelopes.push({ ...(entry as unknown as RunEnvelope), version: 1 });
|
|
168
|
+
}
|
|
169
|
+
return envelopes;
|
|
158
170
|
}
|
|
159
171
|
|
|
160
172
|
function resolveMaxRuns(opt?: number | undefined): number {
|
|
@@ -241,6 +253,7 @@ export function openLedger(opts?: LedgerOptions): Ledger {
|
|
|
241
253
|
return {
|
|
242
254
|
create(input: CreateRunInput): RunEnvelope {
|
|
243
255
|
const envelope: RunEnvelope = {
|
|
256
|
+
version: 1,
|
|
244
257
|
id: input.id ?? newRunId(),
|
|
245
258
|
agentId: input.agentId,
|
|
246
259
|
executionRole: input.executionRole,
|
|
@@ -10,6 +10,7 @@ import type { GatewayRoutingObservation } from "../../core/gateway-routing.js";
|
|
|
10
10
|
import type { ResponseModelIdObservation } from "../../core/response-model-id.js";
|
|
11
11
|
import type { SkillActivation } from "../../core/skill-activation.js";
|
|
12
12
|
import type { ToolProfileName } from "../../tools/profiles.js";
|
|
13
|
+
import type { DeclaredCheckReport } from "../../tools/verify/scripts.js";
|
|
13
14
|
import type { AgentAudience } from "../agents/spec.js";
|
|
14
15
|
import type { EvidenceTag } from "../evidence/index.js";
|
|
15
16
|
import type { CostProvenance, RuntimeTargetSnapshot } from "../providers/index.js";
|
|
@@ -442,6 +443,7 @@ export interface RunReceiptFindingsSummary {
|
|
|
442
443
|
}
|
|
443
444
|
|
|
444
445
|
export interface RunEnvelope {
|
|
446
|
+
version: 1;
|
|
445
447
|
id: string;
|
|
446
448
|
agentId: string;
|
|
447
449
|
/** Semantic role this attempt's route statistics belong to. */
|
|
@@ -483,6 +485,8 @@ export interface RunEnvelope {
|
|
|
483
485
|
plan?: RunPlanProvenance;
|
|
484
486
|
/** Ad-hoc specialist provenance; present only when a persona override composed the stable prompt. */
|
|
485
487
|
personaOverride?: RunPersonaOverride;
|
|
488
|
+
/** Decision refs active on the parent decision board at dispatch; absent when the board was empty. */
|
|
489
|
+
decisionRefs?: ReadonlyArray<string>;
|
|
486
490
|
exitCode: number | null;
|
|
487
491
|
pid: number | null;
|
|
488
492
|
heartbeatAt: string | null;
|
|
@@ -710,11 +714,14 @@ export interface RunHostVerificationCheck {
|
|
|
710
714
|
check: string;
|
|
711
715
|
argv: string[];
|
|
712
716
|
cwd: string;
|
|
717
|
+
/** For a numeric-compare or perf-budget check, 1 when the judgement failed even though the command exited 0. */
|
|
713
718
|
exitCode: number;
|
|
714
719
|
durationMs: number;
|
|
715
720
|
memo: boolean;
|
|
716
721
|
outputTail: string;
|
|
717
722
|
artifactPath?: string;
|
|
723
|
+
/** Structured verdict of a numeric-compare or perf-budget check; absent for a command check. Covered by the receipt digest through `hostVerification`. */
|
|
724
|
+
report?: DeclaredCheckReport;
|
|
718
725
|
/** Run that owns the evidence when it did not come from this run: a memo hit, or the batch member the shared check ran under. */
|
|
719
726
|
evidenceRunId?: string;
|
|
720
727
|
}
|
|
@@ -811,8 +818,14 @@ export interface RunReceipt {
|
|
|
811
818
|
node?: RunNodeIdentity;
|
|
812
819
|
/**
|
|
813
820
|
* Route and node identity attested by the worker process that executed the
|
|
814
|
-
* run.
|
|
815
|
-
*
|
|
821
|
+
* run. Every native and remote worker transport attests, so a receipt from
|
|
822
|
+
* the worker path carries this field. It is legitimately absent on receipts
|
|
823
|
+
* that no worker process produced: the print-mode main-agent receipt
|
|
824
|
+
* (src/cli/modes/print.ts), which runs in the orchestrator process, and ACP
|
|
825
|
+
* delegation receipts, whose external agent never sends an announce frame.
|
|
826
|
+
* The eval process invariants (src/domains/eval/metrics/invariants.ts) rely
|
|
827
|
+
* on that absence to skip the main-agent path when counting orphaned
|
|
828
|
+
* workers. Contract tests that stub the spawn also leave it absent.
|
|
816
829
|
*/
|
|
817
830
|
attestation?: RunReceiptAttestation;
|
|
818
831
|
/** Dead-node failover hops, oldest first; absent when the run was never rerouted. */
|
|
@@ -828,6 +841,8 @@ export interface RunReceipt {
|
|
|
828
841
|
fleetGate?: { path: string; pathHash: string };
|
|
829
842
|
/** Ad-hoc specialist provenance; present only when a persona override composed the stable prompt. */
|
|
830
843
|
personaOverride?: RunPersonaOverride;
|
|
844
|
+
/** Decision refs active on the parent decision board at dispatch; absent when the board was empty. */
|
|
845
|
+
decisionRefs?: ReadonlyArray<string>;
|
|
831
846
|
/** Effective project-context tier for this run; absent on receipts written before this field landed. */
|
|
832
847
|
projectContext?: RunProjectContextProvenance;
|
|
833
848
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
2
2
|
import { normalizeClioCoderEventRecord } from "../../../core/naming-events.js";
|
|
3
|
-
import { shellQuote } from "../../../core/shell-quote.js";
|
|
4
3
|
import { clioStateDir } from "../../../core/xdg.js";
|
|
5
4
|
import {
|
|
6
5
|
evidenceMetricsFromReceipt,
|
|
@@ -22,19 +21,23 @@ export async function runClioRunRunner(
|
|
|
22
21
|
readObservation?: { allowedPaths: string[]; decoyPaths: string[] },
|
|
23
22
|
): Promise<EvalRunnerOutput> {
|
|
24
23
|
const prompt = runner.prompt ?? "";
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
// Spawned as an argv, not a shell line: the task timeout has to reach the
|
|
25
|
+
// Clio process itself so the receipt it seals and the exit code this
|
|
26
|
+
// runner reports describe the same ending. See runShellCommand.
|
|
27
|
+
const args: [string, ...string[]] = [
|
|
28
|
+
process.execPath,
|
|
29
|
+
clioEntry,
|
|
27
30
|
"run",
|
|
28
31
|
"--json",
|
|
29
|
-
...(runner.agent === undefined ? [] : ["--agent",
|
|
32
|
+
...(runner.agent === undefined ? [] : ["--agent", runner.agent]),
|
|
30
33
|
"--target",
|
|
31
|
-
|
|
32
|
-
...(target.model === undefined ? [] : ["--model",
|
|
33
|
-
...(target.thinking === undefined ? [] : ["--thinking",
|
|
34
|
+
target.id,
|
|
35
|
+
...(target.model === undefined ? [] : ["--model", target.model]),
|
|
36
|
+
...(target.thinking === undefined ? [] : ["--thinking", target.thinking]),
|
|
34
37
|
...(runner.autonomy === undefined ? [] : ["--autonomy", runner.autonomy]),
|
|
35
|
-
|
|
38
|
+
prompt,
|
|
36
39
|
];
|
|
37
|
-
const result = await runShellCommand(
|
|
40
|
+
const result = await runShellCommand(args, cwd, runner.timeoutMs ?? timeoutMs, env);
|
|
38
41
|
// Usage is folded from the live stream, not from the bounded stdout
|
|
39
42
|
// artifact: a verbose run's `message_end` events do not survive truncation.
|
|
40
43
|
const tokens = result.usage;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { shellQuote } from "../../../core/shell-quote.js";
|
|
2
1
|
import type { EvalSuiteTargetV2 } from "../schema/suite.js";
|
|
3
2
|
import { type EvalRunnerOutput, runShellCommand } from "./external-command.js";
|
|
4
3
|
|
|
@@ -9,12 +8,8 @@ export async function runContextIndexRunner(
|
|
|
9
8
|
target: EvalSuiteTargetV2,
|
|
10
9
|
env?: NodeJS.ProcessEnv,
|
|
11
10
|
): Promise<EvalRunnerOutput> {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
cwd,
|
|
15
|
-
timeoutMs,
|
|
16
|
-
env,
|
|
17
|
-
);
|
|
11
|
+
const args: [string, ...string[]] = [process.execPath, clioEntry, "context", "index", "--json"];
|
|
12
|
+
const result = await runShellCommand(args, cwd, timeoutMs, env);
|
|
18
13
|
const parsed = parseContextIndexOutput(result.stdout);
|
|
19
14
|
return {
|
|
20
15
|
assignmentId: null,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { existsSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { shellQuote } from "../../../core/shell-quote.js";
|
|
4
3
|
import type { EvalRunnerV2, EvalSuiteTargetV2 } from "../schema/suite.js";
|
|
5
4
|
import { type EvalRunnerOutput, runShellCommand } from "./external-command.js";
|
|
6
5
|
|
|
@@ -13,7 +12,7 @@ export async function runContextInitRunner(
|
|
|
13
12
|
env?: NodeJS.ProcessEnv,
|
|
14
13
|
): Promise<EvalRunnerOutput> {
|
|
15
14
|
const extraArgs = runner.args ?? [];
|
|
16
|
-
const
|
|
15
|
+
const args: [string, ...string[]] = [
|
|
17
16
|
process.execPath,
|
|
18
17
|
clioEntry,
|
|
19
18
|
"context",
|
|
@@ -25,10 +24,8 @@ export async function runContextInitRunner(
|
|
|
25
24
|
...(target.model === undefined ? [] : ["--model", target.model]),
|
|
26
25
|
...(target.thinking === undefined ? [] : ["--thinking", target.thinking]),
|
|
27
26
|
...extraArgs,
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
.join(" ");
|
|
31
|
-
const result = await runShellCommand(command, cwd, runner.timeoutMs ?? timeoutMs, env);
|
|
27
|
+
];
|
|
28
|
+
const result = await runShellCommand(args, cwd, runner.timeoutMs ?? timeoutMs, env);
|
|
32
29
|
const payload = parseInitPayload(result.stdout);
|
|
33
30
|
const candidateGeneration = recordField(payload, "generation");
|
|
34
31
|
const generation = isValidGenerationPayload(payload, candidateGeneration) ? candidateGeneration : null;
|
|
@@ -65,6 +65,11 @@ const OUTPUT_HEAD_LIMIT = 20_000;
|
|
|
65
65
|
const OUTPUT_TRUNCATION_MARKER = "\n[output middle truncated; tail preserved]\n";
|
|
66
66
|
const METRIC_JSONL_LIMIT = 256_000;
|
|
67
67
|
const METRIC_JSONL_LINE_LIMIT = 64_000;
|
|
68
|
+
// After the deadline's SIGTERM, how long a run gets to end on its own terms
|
|
69
|
+
// before SIGKILL. Clio seals the interrupted run's receipt inside its
|
|
70
|
+
// coordinated shutdown, which budgets 500ms per hook across several hooks; a
|
|
71
|
+
// one-second grace killed it mid-seal and the timed-out run left no receipt.
|
|
72
|
+
const TIMEOUT_KILL_GRACE_MS = 5_000;
|
|
68
73
|
|
|
69
74
|
export async function runExternalCommandRunner(
|
|
70
75
|
runner: EvalRunnerV2,
|
|
@@ -133,8 +138,23 @@ export async function runExternalCommandRunner(
|
|
|
133
138
|
};
|
|
134
139
|
}
|
|
135
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Run one eval subprocess to completion or to the timeout.
|
|
143
|
+
*
|
|
144
|
+
* A string runs through the shell, which is what an operator's
|
|
145
|
+
* `external-command` line and the verifier commands are. An argv array spawns
|
|
146
|
+
* the program directly, with no shell between the runner and the process
|
|
147
|
+
* whose exit code it reports. That distinction matters on the timeout path:
|
|
148
|
+
* `/bin/sh -c` on dash keeps the shell as the parent, so SIGTERM to the shell
|
|
149
|
+
* stops the shell alone while the program runs on to completion. A
|
|
150
|
+
* `clio-coder run` spawned that way finished its turn after the deadline,
|
|
151
|
+
* sealed a succeeded receipt with exit 0, and exited 0, and this runner
|
|
152
|
+
* reported the shell's signal death as exit 124 (issue #275). With an argv the
|
|
153
|
+
* signal reaches Clio itself, which seals the run as canceled with the exit
|
|
154
|
+
* status it then reports, so the receipt and the exit code agree either way.
|
|
155
|
+
*/
|
|
136
156
|
export function runShellCommand(
|
|
137
|
-
command: string,
|
|
157
|
+
command: string | readonly [string, ...string[]],
|
|
138
158
|
cwd: string,
|
|
139
159
|
timeoutMs: number,
|
|
140
160
|
env?: NodeJS.ProcessEnv,
|
|
@@ -153,14 +173,12 @@ export function runShellCommand(
|
|
|
153
173
|
const callLedgerFold = createEvalCallLedgerFold();
|
|
154
174
|
let timedOut = false;
|
|
155
175
|
let settled = false;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
env: env === undefined ? process.env : { ...process.env, ...env },
|
|
163
|
-
});
|
|
176
|
+
// The overlay is additive: an eval item pins where Clio writes its
|
|
177
|
+
// journal, and inherits everything else the operator's shell provides.
|
|
178
|
+
const spawnEnv = env === undefined ? process.env : { ...process.env, ...env };
|
|
179
|
+
const [file, args, shell] = typeof command === "string" ? [command, [], true] : [command[0], command.slice(1), false];
|
|
180
|
+
const child = spawn(file, args, { cwd, shell, stdio: ["ignore", "pipe", "pipe"], env: spawnEnv });
|
|
181
|
+
const commandLine = typeof command === "string" ? command : command.join(" ");
|
|
164
182
|
child.stdout.setEncoding("utf8");
|
|
165
183
|
child.stderr.setEncoding("utf8");
|
|
166
184
|
child.stdout.on("data", (chunk: string) => {
|
|
@@ -177,14 +195,14 @@ export function runShellCommand(
|
|
|
177
195
|
const timer = setTimeout(() => {
|
|
178
196
|
timedOut = true;
|
|
179
197
|
child.kill("SIGTERM");
|
|
180
|
-
setTimeout(() => child.kill("SIGKILL"),
|
|
198
|
+
setTimeout(() => child.kill("SIGKILL"), TIMEOUT_KILL_GRACE_MS);
|
|
181
199
|
}, timeoutMs);
|
|
182
200
|
const finish = (exitCode: number): void => {
|
|
183
201
|
if (settled) return;
|
|
184
202
|
settled = true;
|
|
185
203
|
clearTimeout(timer);
|
|
186
204
|
resolve({
|
|
187
|
-
command,
|
|
205
|
+
command: commandLine,
|
|
188
206
|
exitCode,
|
|
189
207
|
stdout,
|
|
190
208
|
metricJsonl: metricCapture.finish(),
|