@iowarp/clio-coder 0.3.9 → 0.4.0
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 +53 -0
- package/NOTICE +33 -0
- package/README.md +7 -2
- package/dist/{acp-7LOELQFP.js → acp-G5WJBNCT.js} +13 -12
- package/dist/{agents-FIBG2SHA.js → agents-FMV2Q5G4.js} +39 -36
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-OI4LIH2I.js → auth-3IDSJEIK.js} +16 -15
- package/dist/{builtins-AD25UL3C.js → builtins-XCZWXSC7.js} +3 -3
- package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
- package/dist/{chunk-56KB5IJP.js → chunk-2OQE55CK.js} +3 -3
- package/dist/{chunk-N3PBVRTZ.js → chunk-32KWKNSF.js} +9 -7
- package/dist/{chunk-VKBMFOYV.js → chunk-36EJLSQQ.js} +23 -27
- package/dist/{chunk-465CC7FK.js → chunk-3BT2XMV4.js} +16 -13
- package/dist/{chunk-NQ6UCCOD.js → chunk-3URVFKWK.js} +7 -7
- package/dist/{chunk-WIE7ZOSW.js → chunk-3XML7CDN.js} +2 -2
- package/dist/chunk-42FMPA75.js +101 -0
- package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
- package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
- package/dist/{chunk-JEQQR47K.js → chunk-6DB53AJS.js} +168 -9
- package/dist/{chunk-KV2AOLDF.js → chunk-76ONBSIA.js} +5 -25
- package/dist/{chunk-FALJGAWU.js → chunk-7MCTRUCE.js} +2 -2
- package/dist/{chunk-5T7RBWN2.js → chunk-AB44T6BB.js} +5 -5
- package/dist/chunk-B7OBL7PK.js +1405 -0
- package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
- package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
- package/dist/{chunk-O6I4CIEU.js → chunk-BKFJHQCA.js} +6 -6
- package/dist/chunk-BKFM6EJV.js +462 -0
- package/dist/chunk-BMS5RKQY.js +27 -0
- package/dist/{chunk-MV2VUEJC.js → chunk-BPKCPIL7.js} +2 -2
- package/dist/{chunk-TEO2TLVN.js → chunk-BUMFYQFY.js} +964 -1120
- package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
- package/dist/chunk-C2LTL2W6.js +2447 -0
- package/dist/{chunk-EBOC7MT3.js → chunk-CODPRO7Q.js} +8 -8
- package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
- package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
- package/dist/{chunk-MIX5N5AC.js → chunk-DQITNCXG.js} +394 -154
- package/dist/{chunk-S4COXYBG.js → chunk-DYJP44XW.js} +129 -106
- package/dist/{chunk-N5XKWMDW.js → chunk-F4CKPOEQ.js} +6 -6
- package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
- package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
- package/dist/{chunk-7C6RYZGQ.js → chunk-GKF55TAZ.js} +3 -3
- package/dist/{chunk-B5XRQOLB.js → chunk-GR5G2PVF.js} +9 -8
- package/dist/{chunk-XDOQXGFO.js → chunk-GXNLGKAB.js} +64 -8
- package/dist/chunk-HHV2GANA.js +88 -0
- package/dist/{chunk-774ILSRL.js → chunk-HI63TFOG.js} +4 -3
- package/dist/{chunk-R7LNVMCS.js → chunk-HJJTYUHX.js} +64 -72
- package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
- package/dist/{chunk-CA42X6KT.js → chunk-HWHKMHUA.js} +6 -6
- package/dist/chunk-HZHHCK24.js +1631 -0
- package/dist/{chunk-HJB5IUKP.js → chunk-I5VEOC6I.js} +12 -12
- package/dist/chunk-IBEBSCYA.js +564 -0
- package/dist/chunk-IQ7KR472.js +362 -0
- package/dist/{chunk-4H6ULJ3H.js → chunk-J4W7KFM7.js} +921 -990
- package/dist/{chunk-4LJX2PUC.js → chunk-JDG2WCRO.js} +5 -5
- package/dist/{chunk-NZU6YDNV.js → chunk-K5C3NCBD.js} +4 -4
- package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
- package/dist/{chunk-NNNWO6F2.js → chunk-KFZI4NIL.js} +93 -3
- package/dist/chunk-KMVISBZR.js +132 -0
- package/dist/{chunk-BVDVID7E.js → chunk-LDQ2ZF2M.js} +2 -2
- package/dist/{chunk-OEDBCISO.js → chunk-LQ3DZAMX.js} +2 -2
- package/dist/{chunk-PZ4I4JE2.js → chunk-LY4S7GJC.js} +124 -116
- package/dist/{chunk-3DUR4WUA.js → chunk-MLKNTWH2.js} +19 -19
- package/dist/chunk-NEKRRTYW.js +56 -0
- package/dist/{chunk-5DQRIYDZ.js → chunk-NHCZP4K7.js} +2 -2
- package/dist/chunk-NHLBIGRH.js +1506 -0
- package/dist/chunk-NQQH3YT7.js +302 -0
- package/dist/chunk-NYS75XW5.js +15 -0
- package/dist/{chunk-TD7UE2L5.js → chunk-O4XIVISU.js} +7 -7
- package/dist/{chunk-WXCJ7VME.js → chunk-O6TL7WWY.js} +6 -6
- package/dist/chunk-OQBA45DZ.js +97 -0
- package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
- package/dist/chunk-PMZCIOCJ.js +25 -0
- package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
- package/dist/{chunk-HKO36JWF.js → chunk-QBJA7R7N.js} +31 -3
- package/dist/chunk-QDC3K2U3.js +262 -0
- package/dist/{chunk-5HFBWUMU.js → chunk-QLFS5GO2.js} +4 -4
- package/dist/{chunk-GAYUJ7LE.js → chunk-QLL7ILRG.js} +41 -32
- package/dist/{chunk-LXPJXFM5.js → chunk-QREDIESB.js} +7 -7
- package/dist/chunk-QSNYB6ZV.js +195 -0
- package/dist/chunk-R346GLFC.js +31 -0
- package/dist/{chunk-VPTUJU4P.js → chunk-SJ5ZKQ4S.js} +2 -2
- package/dist/{chunk-QQ7EKM72.js → chunk-SP2RXXYO.js} +6 -54
- package/dist/chunk-SUCTJL45.js +45 -0
- package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
- package/dist/chunk-T56WDKA5.js +183 -0
- package/dist/chunk-TVHHYFHE.js +255 -0
- package/dist/{chunk-IHKBWSXF.js → chunk-TZ3SGWZZ.js} +2 -2
- package/dist/{chunk-ECUO3KDP.js → chunk-U77AMWDL.js} +47 -7
- package/dist/{chunk-3UUY7R3Z.js → chunk-ULC6OTWO.js} +7 -7
- package/dist/{chunk-AEYBF3TB.js → chunk-UM7N4G5A.js} +2 -2
- package/dist/{chunk-3V5AYSEQ.js → chunk-UXMFQ54G.js} +43 -36
- package/dist/{chunk-YW7UVM5V.js → chunk-V5DHCITQ.js} +34 -1
- package/dist/{chunk-47CMYGET.js → chunk-VAZSBTKF.js} +2 -2
- package/dist/{chunk-3MRC2YSQ.js → chunk-VEO4AP2K.js} +2 -2
- package/dist/{chunk-PNY46YEY.js → chunk-VFA6GDY5.js} +47 -3
- package/dist/chunk-VO67MWHC.js +75 -0
- package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
- package/dist/{chunk-AD7Y7STJ.js → chunk-VYMXRQI6.js} +36 -22
- package/dist/{chunk-T3Z6VAAF.js → chunk-W5VSYASO.js} +19 -17
- package/dist/{chunk-5PVQ4SRS.js → chunk-WZR7K7ZX.js} +7 -123
- package/dist/{chunk-DG4M6ZUE.js → chunk-X3YGUTOB.js} +4 -4
- package/dist/chunk-X75E3D2N.js +686 -0
- package/dist/{chunk-P3JGPQFL.js → chunk-YDFRH54B.js} +4 -4
- package/dist/chunk-YJX4SHTD.js +40 -0
- package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
- package/dist/{chunk-YSEHGPCT.js → chunk-Z2RR6MAK.js} +3 -3
- package/dist/{chunk-HCBCAYZU.js → chunk-Z4TXYIEG.js} +5 -5
- package/dist/cli/index.js +47 -36
- package/dist/{clio-LT5V7SSZ.js → clio-2JXHBBY5.js} +7 -7
- package/dist/{code-nav-LMW275PA.js → code-nav-3YYRMYNF.js} +6 -6
- package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
- package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
- package/dist/{config-RXS5T3JT.js → config-QZPCMYSO.js} +58 -50
- package/dist/{configure-2WYWSCSD.js → configure-TEGEBYCA.js} +21 -19
- package/dist/{context-I3BTOTCS.js → context-AV7OEZ4D.js} +11 -11
- package/dist/{context-MVOORGMF.js → context-E6H5RNMC.js} +49 -41
- package/dist/{context-PALKKQYL.js → context-GSXUE4CT.js} +25 -23
- package/dist/{context-clear-N2WOYZ2K.js → context-clear-SHIBYK6T.js} +48 -40
- package/dist/{context-working-set-MIEVECVZ.js → context-working-set-5ZGKPGZQ.js} +11 -10
- package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-EFMJT4LD.js} +88 -60
- package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
- package/dist/doctor-QOA5FNY5.js +313 -0
- package/dist/{eval-IJ5VEZDJ.js → eval-TFBYQH4H.js} +66 -64
- package/dist/eval-inventory-SXH7PDKX.js +316 -0
- package/dist/{evidence-L5APPXNV.js → evidence-ERGESKGN.js} +198 -44
- package/dist/{evolve-RGNKFJ52.js → evolve-VDXTSYCJ.js} +46 -38
- package/dist/{extensions-7WYWUX5A.js → extensions-7BGBHN57.js} +6 -6
- package/dist/{fleet-6CNVBZZP.js → fleet-2RRVDF2V.js} +189 -101
- package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-VJ726XIA.js} +10 -10
- package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
- package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-JF5QOATM.js} +16 -15
- package/dist/fleet-inspect-VLY4S7QM.js +442 -0
- package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-AIZUEJOY.js} +6 -5
- package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-AJRPDMDV.js} +20 -18
- package/dist/fleet-verify-JFEL2L3H.js +175 -0
- package/dist/fleet-view-ZCON35AG.js +102 -0
- package/dist/{init-VBN2ACVA.js → init-DN2WWLFE.js} +64 -56
- package/dist/install-XGLBQY5E.js +13 -0
- package/dist/interop-OZBKXAYL.js +114 -0
- package/dist/{library-JHGUMLY2.js → library-YWZG7IMW.js} +19 -18
- package/dist/{memory-K4OQIYWG.js → memory-I4C4HMLW.js} +48 -40
- package/dist/{models-2NCZUWDD.js → models-CEYXJBO6.js} +30 -28
- package/dist/{monitor-MMVTJABD.js → monitor-NZ6GCI3P.js} +53 -47
- package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-GCGQ4N5I.js} +6311 -7145
- package/dist/panes-HMABYVO4.js +58 -0
- package/dist/panes-KY6W3V2E.js +103 -0
- package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
- package/dist/{reset-DD5JGOY3.js → reset-DQ6FGCSH.js} +13 -11
- package/dist/resources-BB3MVJMD.js +111 -0
- package/dist/{run-QEGNX7FL.js → run-H2GQDUER.js} +113 -82
- package/dist/{share-JKD3BQMW.js → share-GTJN6A5O.js} +18 -17
- package/dist/{skills-LMQIKDOZ.js → skills-L55TEW6R.js} +31 -24
- package/dist/{skills-eval-I7X2774U.js → skills-eval-XVXPH2JI.js} +61 -52
- package/dist/skills-inventory-S4MXPJFV.js +126 -0
- package/dist/slash-commands-ZSGASKJC.js +77 -0
- package/dist/{steer-CF5TDANS.js → steer-RZGSCY4R.js} +3 -3
- package/dist/{support-I7LOJLIF.js → support-PKEUNNQL.js} +4 -4
- package/dist/{targets-RUSR6B5Z.js → targets-NCPZ644J.js} +31 -29
- package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-44SV3YCN.js} +4 -4
- package/dist/tools-DAF3DI3C.js +27 -0
- package/dist/{trace-ODOQIVIW.js → trace-FYVW2MQA.js} +149 -7
- package/dist/tui-primitives-2AKXQNZK.js +13 -0
- package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
- package/dist/{upgrade-XANW3FXB.js → upgrade-3XPP6OQL.js} +26 -24
- package/dist/{usage-4H7ZRXQT.js → usage-3NLHGTU2.js} +57 -49
- package/dist/{verifiers-UZXNBZEB.js → verifiers-SSQONKRT.js} +171 -12
- package/dist/{verify-BVKWTNDL.js → verify-3U6J7FZI.js} +9 -9
- package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
- package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-CEHYGPGQ.js} +67 -59
- package/dist/with-panes-MKB46MPQ.js +782 -0
- package/dist/worker/entry.js +101 -66
- package/docs/README.md +3 -2
- package/docs/acp.md +24 -3
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +4 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +43 -5
- package/docs/commands-and-modes.md +31 -2
- package/docs/configuration-and-targets.md +40 -10
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +19 -0
- package/docs/dispatch-typed-intent.md +385 -0
- package/docs/documentation-coverage.md +2 -2
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +1 -0
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +2 -2
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +25 -1
- package/docs/fleet-dispatch.md +22 -1
- package/docs/installation-and-lifecycle.md +15 -2
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +10 -1
- package/docs/observability.md +3 -3
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +1 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/safety-model.md +2 -2
- 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 +18 -8
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +4 -2
- package/src/cli/acp.ts +6 -2
- package/src/cli/argv.ts +25 -0
- package/src/cli/configure.ts +23 -21
- package/src/cli/doctor-panes.ts +124 -0
- package/src/cli/doctor-toolchain.ts +57 -0
- package/src/cli/doctor.ts +20 -1
- package/src/cli/eval-inventory.ts +436 -0
- package/src/cli/eval.ts +13 -0
- package/src/cli/evidence-detail.ts +88 -0
- package/src/cli/evidence-inventory.ts +183 -0
- package/src/cli/evidence.ts +30 -5
- package/src/cli/fleet-decisions.ts +69 -0
- package/src/cli/fleet-inspect.ts +334 -0
- package/src/cli/fleet-verify.ts +133 -0
- package/src/cli/fleet-view.ts +810 -0
- package/src/cli/fleet.ts +148 -37
- package/src/cli/index.ts +14 -2
- package/src/cli/interop-inspect.ts +128 -0
- package/src/cli/interop.ts +34 -0
- package/src/cli/panes.ts +35 -0
- package/src/cli/reset.ts +5 -2
- package/src/cli/run.ts +58 -0
- package/src/cli/skills-inventory.ts +185 -0
- package/src/cli/skills.ts +16 -13
- package/src/cli/tools.ts +321 -0
- package/src/cli/trace-inspect.ts +252 -0
- package/src/cli/trace.ts +23 -2
- package/src/cli/verifiers-inspect.ts +347 -0
- package/src/cli/verifiers.ts +9 -0
- package/src/core/bus-events.ts +4 -0
- package/src/core/config.ts +49 -0
- package/src/core/defaults.ts +93 -7
- package/src/core/response-schema.ts +44 -4
- package/src/core/theme-token-hex.ts +43 -0
- package/src/core/tool-names.ts +2 -1
- package/src/core/xdg.ts +1 -1
- package/src/domains/agents/fleets/build-review.md +0 -3
- package/src/domains/agents/fleets/build-test.md +0 -3
- package/src/domains/agents/result-contract-filesystem.ts +32 -0
- package/src/domains/agents/result-contract.ts +164 -35
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/dispatch/admission-error.ts +9 -0
- package/src/domains/dispatch/admission.ts +22 -21
- package/src/domains/dispatch/capacity-lease.ts +21 -1
- package/src/domains/dispatch/council-topology.ts +398 -0
- package/src/domains/dispatch/extension.ts +222 -50
- package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
- package/src/domains/dispatch/fleet-plan.ts +7 -2
- package/src/domains/dispatch/fleet-run.ts +64 -1
- package/src/domains/dispatch/gate-decisions.ts +11 -1
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/gate-topology.ts +289 -0
- package/src/domains/dispatch/index.ts +19 -0
- package/src/domains/dispatch/intent-compatibility.ts +330 -0
- package/src/domains/dispatch/intent.ts +85 -1
- package/src/domains/dispatch/reservation-store.ts +24 -4
- package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
- package/src/domains/dispatch/run-event-journal.ts +598 -0
- package/src/domains/dispatch/state.ts +45 -1
- package/src/domains/dispatch/types.ts +13 -0
- package/src/domains/dispatch/validation.ts +33 -8
- package/src/domains/eval/compare/compare.ts +3 -16
- package/src/domains/eval/inventory.ts +113 -0
- package/src/domains/eval/schema/serving.ts +31 -0
- package/src/domains/evidence/store.ts +6 -0
- package/src/domains/memory/index.ts +0 -1
- package/src/domains/memory/task-memory-policy.ts +6 -10
- package/src/domains/memory/task-memory-telemetry.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +33 -21
- package/src/domains/mux/contract.ts +434 -0
- package/src/domains/mux/detect.ts +158 -0
- package/src/domains/mux/extension.ts +47 -0
- package/src/domains/mux/index.ts +96 -0
- package/src/domains/mux/manifest.ts +6 -0
- package/src/domains/mux/operations.ts +164 -0
- package/src/domains/mux/pane-registry.ts +90 -0
- package/src/domains/mux/protocol.ts +49 -0
- package/src/domains/mux/socket-client.ts +816 -0
- package/src/domains/mux/types.ts +222 -0
- package/src/domains/mux/viewer-command.ts +59 -0
- package/src/domains/mux/yazi/assets/init.lua +2 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
- package/src/domains/mux/yazi/assets/yazi.toml +17 -0
- package/src/domains/mux/yazi/event-stream.ts +180 -0
- package/src/domains/mux/yazi/profile.ts +299 -0
- package/src/domains/mux/yazi/session.ts +228 -0
- package/src/domains/mux/yazi/theme.ts +30 -0
- package/src/domains/observability/cost.ts +21 -0
- package/src/domains/observability/index.ts +2 -0
- package/src/domains/observability/trace-store.ts +42 -0
- package/src/domains/providers/endpoint-capacity.ts +154 -22
- package/src/domains/providers/endpoint-slots-store.ts +189 -0
- package/src/domains/providers/extension.ts +20 -3
- package/src/domains/providers/index.ts +22 -0
- package/src/domains/providers/model-runtime-capabilities.ts +32 -0
- package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
- package/src/domains/providers/runtimes/builtins.ts +2 -0
- package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
- package/src/domains/providers/support.ts +1 -0
- package/src/domains/providers/target-model-cache.ts +124 -0
- package/src/domains/resources/index.ts +3 -0
- package/src/domains/resources/skills/loader.ts +33 -0
- package/src/domains/safety/action-classifier.ts +6 -0
- package/src/domains/toolchain/archive.ts +175 -0
- package/src/domains/toolchain/contract.ts +28 -0
- package/src/domains/toolchain/extension.ts +47 -0
- package/src/domains/toolchain/index.ts +39 -0
- package/src/domains/toolchain/install.ts +327 -0
- package/src/domains/toolchain/manifest.ts +8 -0
- package/src/domains/toolchain/paths.ts +34 -0
- package/src/domains/toolchain/registry.ts +265 -0
- package/src/domains/toolchain/remove.ts +218 -0
- package/src/domains/toolchain/resolve.ts +182 -0
- package/src/domains/toolchain/types.ts +113 -0
- package/src/domains/toolchain/version.ts +88 -0
- package/src/engine/acp/server.ts +413 -70
- package/src/engine/acp/types.ts +19 -1
- package/src/engine/claude/sdk-module.ts +98 -0
- package/src/engine/claude/sdk-runtime.ts +19 -11
- package/src/engine/tui-primitives.ts +21 -0
- package/src/engine/tui.ts +1 -0
- package/src/engine/worker-runtime.ts +2 -13
- package/src/entry/boot-options.ts +2 -0
- package/src/entry/orchestrator.ts +103 -5
- package/src/entry/panes-activation.ts +31 -0
- package/src/entry/with-panes.ts +20 -0
- package/src/interactive/context-overlay.ts +1 -0
- package/src/interactive/cost-overlay.ts +2 -0
- package/src/interactive/council-grid.ts +1 -3
- package/src/interactive/council.ts +11 -0
- package/src/interactive/dispatch-board.ts +26 -16
- package/src/interactive/interactive-application.ts +153 -6
- package/src/interactive/interactive-input-runtime.ts +7 -0
- package/src/interactive/interactive-slash-runtime.ts +36 -6
- package/src/interactive/memory-overlay.ts +1 -0
- package/src/interactive/modal-marker.ts +170 -0
- package/src/interactive/mux-bridge.ts +214 -0
- package/src/interactive/overlay-frame.ts +58 -2
- package/src/interactive/overlay-general-openers.ts +1 -0
- package/src/interactive/overlay-key-routing.ts +14 -3
- package/src/interactive/overlay-lifecycle.ts +19 -3
- package/src/interactive/overlay-model-selectors.ts +40 -3
- package/src/interactive/overlay-permission-lifecycle.ts +90 -22
- package/src/interactive/overlay-transitions.ts +18 -4
- package/src/interactive/overlays/agents.ts +1 -0
- package/src/interactive/overlays/ask-user.ts +136 -30
- package/src/interactive/overlays/auth-dialog.ts +1 -0
- package/src/interactive/overlays/context-reset.ts +1 -0
- package/src/interactive/overlays/cwd-fallback.ts +1 -0
- package/src/interactive/overlays/decisions.ts +11 -11
- package/src/interactive/overlays/extensions.ts +1 -0
- package/src/interactive/overlays/fleet-run-approval.ts +1 -0
- package/src/interactive/overlays/handoff-review.ts +1 -0
- package/src/interactive/overlays/help-reference.ts +2 -0
- package/src/interactive/overlays/interop.ts +1 -0
- package/src/interactive/overlays/library-install-confirm.ts +1 -0
- package/src/interactive/overlays/library-tabs.ts +28 -0
- package/src/interactive/overlays/list-overlay.ts +10 -1
- package/src/interactive/overlays/message-picker.ts +1 -0
- package/src/interactive/overlays/model-scope.ts +86 -0
- package/src/interactive/overlays/model-selector.ts +1 -0
- package/src/interactive/overlays/prompts.ts +1 -0
- package/src/interactive/overlays/session-selector.ts +1 -0
- package/src/interactive/overlays/settings-sections.ts +30 -0
- package/src/interactive/overlays/settings.ts +579 -44
- package/src/interactive/overlays/side-question.ts +1 -0
- package/src/interactive/overlays/skills-hub.ts +3 -11
- package/src/interactive/overlays/tree-selector.ts +1 -0
- package/src/interactive/pane-policy.ts +46 -0
- package/src/interactive/panes-runtime.ts +292 -0
- package/src/interactive/renderers/compaction-summary.ts +29 -0
- package/src/interactive/renderers/worker-entry.ts +122 -14
- package/src/interactive/slash-commands.ts +251 -15
- package/src/interactive/tasks-overlay.ts +1 -0
- package/src/interactive/theme/tokens.ts +3 -14
- package/src/interactive/turn-context.ts +47 -2
- package/src/interactive/view/artifacts.ts +109 -1
- package/src/interactive/view/view-overlay.ts +1 -0
- package/src/interactive/watch-pane.ts +152 -0
- package/src/interactive/worker-progress.ts +7 -1
- package/src/interactive/worker-receipts.ts +19 -1
- package/src/interactive/worker-stream.ts +5 -0
- package/src/interactive/yazi-bridge.ts +444 -0
- package/src/tools/bootstrap.ts +26 -2
- package/src/tools/builtin-tool-catalog.ts +15 -0
- package/src/tools/compete-worktrees.ts +83 -2
- package/src/tools/core-bootstrap.ts +2 -1
- package/src/tools/dispatch-admission.ts +14 -3
- package/src/tools/dispatch-arguments.ts +20 -20
- package/src/tools/dispatch-run-events.ts +134 -19
- package/src/tools/dispatch-runner.ts +29 -7
- package/src/tools/dispatch-types.ts +15 -3
- package/src/tools/dispatch.ts +1 -1
- package/src/tools/executables.ts +17 -14
- package/src/tools/observation.ts +54 -4
- package/src/tools/panes-surface.ts +38 -0
- package/src/tools/panes.ts +112 -0
- package/src/tools/policy.ts +10 -1
- package/src/tools/presentation.ts +1 -0
- package/dist/chunk-D73KXYPF.js +0 -37
- package/dist/chunk-MLOK6ZOS.js +0 -2888
- package/dist/chunk-YKOFT37S.js +0 -279
- package/dist/doctor-TWBWFK5V.js +0 -165
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one migration and refusal policy for typed dispatch intent.
|
|
3
|
+
*
|
|
4
|
+
* Typed intent (#155) is authoritative for policy-bearing scope (#158) and the
|
|
5
|
+
* legacy prose-inference path (#159) survives beside it. Those three landed the
|
|
6
|
+
* mechanism; this module owns the lifecycle question they left open: for any
|
|
7
|
+
* dispatch producer, what happens when intent is omitted, partially declared,
|
|
8
|
+
* carries a version this build does not speak, or contradicts another field on
|
|
9
|
+
* the same request.
|
|
10
|
+
*
|
|
11
|
+
* Every answer here is one of three decisions, and the boundary between them is
|
|
12
|
+
* the load-bearing rule:
|
|
13
|
+
*
|
|
14
|
+
* - `accept`: the request is unambiguous. Nothing is reported.
|
|
15
|
+
* - `warn`: the request is compatible but its policy-bearing scope was resolved
|
|
16
|
+
* from something weaker than a declaration. The dispatch proceeds with the
|
|
17
|
+
* authority it would have had anyway; the warning exists so the weaker basis
|
|
18
|
+
* is visible before execution and countable afterwards.
|
|
19
|
+
* - `refuse`: the request states two incompatible things about authority, or
|
|
20
|
+
* states one this build cannot interpret. The dispatch never runs.
|
|
21
|
+
*
|
|
22
|
+
* A `warn` may never be the difference between a narrow and a wide grant. That
|
|
23
|
+
* is the invariant every rule below is written to preserve: no path through this
|
|
24
|
+
* module widens read, write, or verification authority to resolve an ambiguity.
|
|
25
|
+
* When the compatible reading and the declared reading disagree about what a
|
|
26
|
+
* worker may touch, the answer is a refusal, never the union.
|
|
27
|
+
*
|
|
28
|
+
* The module is pure: no filesystem, no clock, no package-layout lookup. A
|
|
29
|
+
* source checkout and an installed package classify identical input identically,
|
|
30
|
+
* which is what makes the version rules verifiable rather than environmental.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { pathBoundaryCovers, resolvePathBoundary } from "../../core/path-boundary.js";
|
|
34
|
+
import { type DispatchIntent, isDispatchIntent } from "./intent.js";
|
|
35
|
+
|
|
36
|
+
/** The intent shape this build speaks. Bump with the `DispatchIntent` interface. */
|
|
37
|
+
export const DISPATCH_INTENT_VERSION = 2;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Every intent version this build accepts. Deliberately not a range: a reader
|
|
41
|
+
* that accepts "2 or newer" accepts fields it cannot interpret, and a reader
|
|
42
|
+
* that accepts "2 or older" reads a v1 declaration under v2 rules. Membership in
|
|
43
|
+
* this list is the whole test.
|
|
44
|
+
*/
|
|
45
|
+
export const DISPATCH_INTENT_SUPPORTED_VERSIONS: ReadonlyArray<number> = [DISPATCH_INTENT_VERSION];
|
|
46
|
+
|
|
47
|
+
export type DispatchIntentCompatibilityDecision = "accept" | "warn" | "refuse";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Stable reason codes. These are a contract: they appear in refusal messages,
|
|
51
|
+
* diagnostics, and contract tests, and callers may branch on them. Renaming one
|
|
52
|
+
* is a breaking change to every surface that reports dispatch admission.
|
|
53
|
+
*/
|
|
54
|
+
export type DispatchIntentCompatibilityCode =
|
|
55
|
+
/** No typed intent at all; policy-bearing scope came from legacy inference. */
|
|
56
|
+
| "intent_absent_legacy_inference"
|
|
57
|
+
/** Typed intent declared paths but no verification requirement. */
|
|
58
|
+
| "intent_partial_verification_absent"
|
|
59
|
+
/** `intent.version` names a version this build does not speak. */
|
|
60
|
+
| "intent_version_unsupported"
|
|
61
|
+
/** Present but not a normalized intent for a reason other than its version. */
|
|
62
|
+
| "intent_malformed"
|
|
63
|
+
/** Declared `intent.write_roots` and legacy `writeRoots` name different trees. */
|
|
64
|
+
| "intent_write_roots_contradiction"
|
|
65
|
+
/** A declared expected output lies outside every declared write root. */
|
|
66
|
+
| "intent_outputs_outside_write_roots"
|
|
67
|
+
/** Write roots declared on a request whose autonomy cannot write. */
|
|
68
|
+
| "intent_write_without_authority"
|
|
69
|
+
/** A narrowed intent reaches outside the intent it is supposed to narrow. */
|
|
70
|
+
| "intent_scope_widening";
|
|
71
|
+
|
|
72
|
+
export interface DispatchIntentCompatibilityFinding {
|
|
73
|
+
code: DispatchIntentCompatibilityCode;
|
|
74
|
+
decision: DispatchIntentCompatibilityDecision;
|
|
75
|
+
/** Operator-facing text, prefixed with the code and naming the fix. */
|
|
76
|
+
message: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface DispatchIntentCompatibilityInput {
|
|
80
|
+
/** The request's `intent` field exactly as the producer set it. */
|
|
81
|
+
intent?: unknown;
|
|
82
|
+
/** The request's legacy `writeRoots` field exactly as the producer set it. */
|
|
83
|
+
writeRoots?: unknown;
|
|
84
|
+
/** Request-level autonomy narrowing, when the producer set one. */
|
|
85
|
+
autonomy?: unknown;
|
|
86
|
+
/** Job cwd used to compare the two write declarations on one footing. */
|
|
87
|
+
cwd?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function finding(
|
|
91
|
+
code: DispatchIntentCompatibilityCode,
|
|
92
|
+
decision: DispatchIntentCompatibilityDecision,
|
|
93
|
+
detail: string,
|
|
94
|
+
): DispatchIntentCompatibilityFinding {
|
|
95
|
+
return { code, decision, message: `${code}: ${detail}` };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
99
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function declaredVersion(value: Record<string, unknown>): number | null {
|
|
103
|
+
return typeof value.version === "number" && Number.isInteger(value.version) ? value.version : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Whether an intent-shaped value carries a version this build can interpret. */
|
|
107
|
+
export function isSupportedDispatchIntentVersion(value: unknown): boolean {
|
|
108
|
+
if (!isRecord(value)) return false;
|
|
109
|
+
const version = declaredVersion(value);
|
|
110
|
+
return version !== null && DISPATCH_INTENT_SUPPORTED_VERSIONS.includes(version);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function versionFinding(value: Record<string, unknown>): DispatchIntentCompatibilityFinding {
|
|
114
|
+
const version = declaredVersion(value);
|
|
115
|
+
const supported = DISPATCH_INTENT_SUPPORTED_VERSIONS.join(", ");
|
|
116
|
+
if (version === null) {
|
|
117
|
+
return finding(
|
|
118
|
+
"intent_version_unsupported",
|
|
119
|
+
"refuse",
|
|
120
|
+
`intent carries no integer version; this build speaks dispatch intent version ${supported}. Re-declare the intent through the dispatch tool rather than replaying a stored object.`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return finding(
|
|
124
|
+
"intent_version_unsupported",
|
|
125
|
+
"refuse",
|
|
126
|
+
`intent declares version ${version}; this build speaks dispatch intent version ${supported}. A stored plan or spec at another version is refused rather than migrated: re-declare read_roots, write_roots, relevant_paths, expected_outputs, and verification on a fresh dispatch call.`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function legacyWriteRoots(value: unknown): string[] | null {
|
|
131
|
+
if (!Array.isArray(value)) return null;
|
|
132
|
+
const roots = value.filter((entry): entry is string => typeof entry === "string" && entry.length > 0);
|
|
133
|
+
return roots.length === value.length ? roots : null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function writeRootsContradiction(
|
|
137
|
+
intent: DispatchIntent,
|
|
138
|
+
writeRoots: ReadonlyArray<string>,
|
|
139
|
+
cwd: string,
|
|
140
|
+
): DispatchIntentCompatibilityFinding | null {
|
|
141
|
+
const resolve = (roots: ReadonlyArray<string>): string =>
|
|
142
|
+
JSON.stringify([...new Set(roots.map((root) => resolvePathBoundary(cwd, root)))].sort());
|
|
143
|
+
if (resolve(writeRoots) === resolve(intent.writeRoots)) return null;
|
|
144
|
+
return finding(
|
|
145
|
+
"intent_write_roots_contradiction",
|
|
146
|
+
"refuse",
|
|
147
|
+
`legacy writeRoots [${[...writeRoots].sort().join(", ")}] and intent.write_roots [${intent.writeRoots.join(", ")}] name different trees. Neither the union nor the legacy field wins: drop writeRoots and declare the exact write scope once in intent.write_roots.`,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function outputsOutsideWriteRoots(intent: DispatchIntent): DispatchIntentCompatibilityFinding | null {
|
|
152
|
+
if (intent.writeRoots.length === 0 || intent.expectedOutputs.length === 0) return null;
|
|
153
|
+
const outside = intent.expectedOutputs.filter((output) => !pathBoundaryCovers(intent.writeRoots, output));
|
|
154
|
+
if (outside.length === 0) return null;
|
|
155
|
+
return finding(
|
|
156
|
+
"intent_outputs_outside_write_roots",
|
|
157
|
+
"refuse",
|
|
158
|
+
`expected_outputs [${outside.join(", ")}] lie outside intent.write_roots [${intent.writeRoots.join(", ")}]. The write boundary would block exactly the outputs the task is required to produce, so add each output's root to write_roots or drop the output from the declaration.`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function writeWithoutAuthority(intent: DispatchIntent, autonomy: unknown): DispatchIntentCompatibilityFinding | null {
|
|
163
|
+
if (intent.writeRoots.length === 0 || autonomy !== "read-only") return null;
|
|
164
|
+
return finding(
|
|
165
|
+
"intent_write_without_authority",
|
|
166
|
+
"refuse",
|
|
167
|
+
`intent.write_roots [${intent.writeRoots.join(", ")}] was declared on a read-only request. A read-only worker cannot be granted the declared trees and the declaration cannot be silently dropped, because dropping it would leave the request claiming a write scope nothing enforces. Declare write_roots only on a writing request.`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Classify one dispatch request's intent against the compatibility rules.
|
|
173
|
+
*
|
|
174
|
+
* Findings are returned in a stable order: version and shape first (a value this
|
|
175
|
+
* build cannot read makes every later question meaningless), then the
|
|
176
|
+
* contradictions between intent and its neighbouring fields, then the
|
|
177
|
+
* lower-confidence notices. Callers treat any `refuse` as terminal.
|
|
178
|
+
*/
|
|
179
|
+
export function classifyDispatchIntentCompatibility(
|
|
180
|
+
input: DispatchIntentCompatibilityInput,
|
|
181
|
+
): DispatchIntentCompatibilityFinding[] {
|
|
182
|
+
const findings: DispatchIntentCompatibilityFinding[] = [];
|
|
183
|
+
if (input.intent === undefined) {
|
|
184
|
+
findings.push(
|
|
185
|
+
finding(
|
|
186
|
+
"intent_absent_legacy_inference",
|
|
187
|
+
"warn",
|
|
188
|
+
"no typed intent was declared, so policy-bearing scope was resolved from legacy writeRoots plus path-like task and briefing tokens. Inferred paths select project rules and worker context only; they never widen write or verification authority. Declare intent.read_roots, intent.write_roots, and intent.relevant_paths to make the scope exact.",
|
|
189
|
+
),
|
|
190
|
+
);
|
|
191
|
+
return findings;
|
|
192
|
+
}
|
|
193
|
+
if (!isRecord(input.intent)) {
|
|
194
|
+
findings.push(
|
|
195
|
+
finding(
|
|
196
|
+
"intent_malformed",
|
|
197
|
+
"refuse",
|
|
198
|
+
"intent must be an object carrying normalized read_roots, write_roots, relevant_paths, expected_outputs, and verification.",
|
|
199
|
+
),
|
|
200
|
+
);
|
|
201
|
+
return findings;
|
|
202
|
+
}
|
|
203
|
+
if (!isSupportedDispatchIntentVersion(input.intent)) {
|
|
204
|
+
findings.push(versionFinding(input.intent));
|
|
205
|
+
return findings;
|
|
206
|
+
}
|
|
207
|
+
if (!isDispatchIntent(input.intent)) {
|
|
208
|
+
findings.push(
|
|
209
|
+
finding(
|
|
210
|
+
"intent_malformed",
|
|
211
|
+
"refuse",
|
|
212
|
+
`intent declares supported version ${DISPATCH_INTENT_VERSION} but is not a normalized intent: path lists must be deduplicated and sorted, pathProvenance must match the declared fields exactly, and every verification entry must name a declared check with an integer timeout. Re-declare it through the dispatch tool rather than hand-building the normalized object.`,
|
|
213
|
+
),
|
|
214
|
+
);
|
|
215
|
+
return findings;
|
|
216
|
+
}
|
|
217
|
+
const intent = input.intent;
|
|
218
|
+
const cwd = typeof input.cwd === "string" && input.cwd.length > 0 ? input.cwd : process.cwd();
|
|
219
|
+
const legacy = input.writeRoots === undefined ? null : legacyWriteRoots(input.writeRoots);
|
|
220
|
+
if (legacy !== null && legacy.length > 0 && intent.writeRoots.length > 0) {
|
|
221
|
+
const contradiction = writeRootsContradiction(intent, legacy, cwd);
|
|
222
|
+
if (contradiction !== null) findings.push(contradiction);
|
|
223
|
+
}
|
|
224
|
+
const outputs = outputsOutsideWriteRoots(intent);
|
|
225
|
+
if (outputs !== null) findings.push(outputs);
|
|
226
|
+
const authority = writeWithoutAuthority(intent, input.autonomy);
|
|
227
|
+
if (authority !== null) findings.push(authority);
|
|
228
|
+
if (intent.verification.length === 0 && (intent.writeRoots.length > 0 || intent.expectedOutputs.length > 0)) {
|
|
229
|
+
findings.push(
|
|
230
|
+
finding(
|
|
231
|
+
"intent_partial_verification_absent",
|
|
232
|
+
"warn",
|
|
233
|
+
"intent declares work that changes the tree but no verification requirement, so nothing the orchestrator runs proves the change is sound. Add intent.verification entries naming declared check ids from package scripts or .clio-coder/verifiers.yaml.",
|
|
234
|
+
),
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
return findings;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Every terminal refusal in a classification, in reporting order. */
|
|
241
|
+
export function dispatchIntentRefusals(
|
|
242
|
+
findings: ReadonlyArray<DispatchIntentCompatibilityFinding>,
|
|
243
|
+
): DispatchIntentCompatibilityFinding[] {
|
|
244
|
+
return findings.filter((entry) => entry.decision === "refuse");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Whether a narrowed intent stays inside the intent it narrows.
|
|
249
|
+
*
|
|
250
|
+
* Batch tasks, agent profiles, and fleet contracts may all restate scope more
|
|
251
|
+
* tightly than the call that contains them. Narrowing is monotonic: a per-task
|
|
252
|
+
* declaration may shrink the ceiling and may never reach outside it. Read scope
|
|
253
|
+
* is checked against reads plus writes because a declared write root is
|
|
254
|
+
* necessarily readable; write scope is checked against writes alone.
|
|
255
|
+
*/
|
|
256
|
+
export function dispatchIntentScopeWidening(
|
|
257
|
+
ceiling: Pick<DispatchIntent, "readRoots" | "writeRoots" | "relevantPaths">,
|
|
258
|
+
narrowed: Pick<DispatchIntent, "readRoots" | "writeRoots" | "relevantPaths">,
|
|
259
|
+
): DispatchIntentCompatibilityFinding | null {
|
|
260
|
+
const ceilings = {
|
|
261
|
+
readRoots: [...ceiling.readRoots, ...ceiling.writeRoots],
|
|
262
|
+
writeRoots: [...ceiling.writeRoots],
|
|
263
|
+
relevantPaths: [...ceiling.readRoots, ...ceiling.writeRoots, ...ceiling.relevantPaths],
|
|
264
|
+
} as const;
|
|
265
|
+
for (const field of ["readRoots", "writeRoots", "relevantPaths"] as const) {
|
|
266
|
+
const outside = narrowed[field].find((candidate) => !pathBoundaryCovers(ceilings[field], candidate));
|
|
267
|
+
if (outside === undefined) continue;
|
|
268
|
+
return finding(
|
|
269
|
+
"intent_scope_widening",
|
|
270
|
+
"refuse",
|
|
271
|
+
`${field} entry '${outside}' is outside the enclosing intent ceiling [${ceilings[field].join(", ") || "empty"}]. A narrower scope may shrink the ceiling and never reach outside it; add the path to the enclosing declaration or drop it here.`,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The retirement criterion for the legacy inference fallback.
|
|
279
|
+
*
|
|
280
|
+
* `pathScope.mode` is sealed on every receipt, so the share of dispatches still
|
|
281
|
+
* resolving policy-bearing scope from prose is measurable from evidence rather
|
|
282
|
+
* than estimated. Removal stays a separate, explicit issue; this is the gate
|
|
283
|
+
* that issue has to clear before it can be opened.
|
|
284
|
+
*/
|
|
285
|
+
export const DISPATCH_INTENT_RETIREMENT_MAX_LEGACY_SHARE = 0.02;
|
|
286
|
+
|
|
287
|
+
/** Receipts below this count cannot decide the criterion either way. */
|
|
288
|
+
export const DISPATCH_INTENT_RETIREMENT_MIN_SAMPLE = 200;
|
|
289
|
+
|
|
290
|
+
export interface DispatchIntentAdoption {
|
|
291
|
+
/** Receipts carrying a resolved path scope; receipts without one are not evidence. */
|
|
292
|
+
measured: number;
|
|
293
|
+
declared: number;
|
|
294
|
+
legacyInferred: number;
|
|
295
|
+
/** `null` when nothing was measured, so an empty window never reads as full adoption. */
|
|
296
|
+
legacyShare: number | null;
|
|
297
|
+
/** True only when the sample is large enough and the legacy share is at or under the bound. */
|
|
298
|
+
retirementReady: boolean;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Measure typed-intent adoption from sealed receipts.
|
|
303
|
+
*
|
|
304
|
+
* Reads only `pathScope.mode`. No task text, briefing prose, or declared path
|
|
305
|
+
* ever enters the aggregate, so the measurement is safe to report from a fleet
|
|
306
|
+
* whose receipts this caller may count but must not quote.
|
|
307
|
+
*/
|
|
308
|
+
export function dispatchIntentAdoption(
|
|
309
|
+
receipts: ReadonlyArray<{ pathScope?: { mode: "declared" | "legacy-inferred" } }>,
|
|
310
|
+
): DispatchIntentAdoption {
|
|
311
|
+
let declared = 0;
|
|
312
|
+
let legacyInferred = 0;
|
|
313
|
+
for (const receipt of receipts) {
|
|
314
|
+
if (receipt.pathScope === undefined) continue;
|
|
315
|
+
if (receipt.pathScope.mode === "declared") declared += 1;
|
|
316
|
+
else legacyInferred += 1;
|
|
317
|
+
}
|
|
318
|
+
const measured = declared + legacyInferred;
|
|
319
|
+
const legacyShare = measured === 0 ? null : legacyInferred / measured;
|
|
320
|
+
return {
|
|
321
|
+
measured,
|
|
322
|
+
declared,
|
|
323
|
+
legacyInferred,
|
|
324
|
+
legacyShare,
|
|
325
|
+
retirementReady:
|
|
326
|
+
measured >= DISPATCH_INTENT_RETIREMENT_MIN_SAMPLE &&
|
|
327
|
+
legacyShare !== null &&
|
|
328
|
+
legacyShare <= DISPATCH_INTENT_RETIREMENT_MAX_LEGACY_SHARE,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
@@ -32,7 +32,20 @@ export type DispatchIntentNormalizationResult =
|
|
|
32
32
|
| { ok: true; intent: DispatchIntent }
|
|
33
33
|
| { ok: false; reason: string; message: string };
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* `version` is accepted on raw model-facing intent so a caller replaying a
|
|
37
|
+
* declaration it was shown can echo the version back. It is validated, never
|
|
38
|
+
* trusted: an unsupported value is a terminal refusal rather than a field the
|
|
39
|
+
* normalizer quietly overwrites with the version this build speaks.
|
|
40
|
+
*/
|
|
41
|
+
const RAW_FIELDS = new Set([
|
|
42
|
+
"version",
|
|
43
|
+
"read_roots",
|
|
44
|
+
"write_roots",
|
|
45
|
+
"relevant_paths",
|
|
46
|
+
"expected_outputs",
|
|
47
|
+
"verification",
|
|
48
|
+
]);
|
|
36
49
|
|
|
37
50
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
38
51
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -129,6 +142,47 @@ function normalizePathList(
|
|
|
129
142
|
return [...new Set(normalized)].sort(compareCodepoints);
|
|
130
143
|
}
|
|
131
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Build typed intent from paths a non-model producer already holds.
|
|
147
|
+
*
|
|
148
|
+
* Fleet contracts, CLI entry points, and extensions declare repository-relative
|
|
149
|
+
* scope in their own artifacts long before a dispatch request exists. Without
|
|
150
|
+
* this they hand the request nothing, and the scope resolver falls back to
|
|
151
|
+
* reading path-like tokens out of the rendered prompt, which is the exact
|
|
152
|
+
* ambiguity typed intent removes. The paths go through the same normalization,
|
|
153
|
+
* caps, and provenance construction as a model-facing declaration, so a
|
|
154
|
+
* producer cannot mint an intent shape the dispatch tool could not.
|
|
155
|
+
*
|
|
156
|
+
* Verification and expected outputs are deliberately not accepted here.
|
|
157
|
+
* A declared check id has to be resolved against the project's verification
|
|
158
|
+
* catalog before it means anything, and that resolution belongs to the
|
|
159
|
+
* admission controller that owns the catalog, not to a scope builder.
|
|
160
|
+
*/
|
|
161
|
+
export function declaredScopeIntent(input: {
|
|
162
|
+
readRoots?: ReadonlyArray<string>;
|
|
163
|
+
writeRoots?: ReadonlyArray<string>;
|
|
164
|
+
relevantPaths?: ReadonlyArray<string>;
|
|
165
|
+
}): DispatchIntentNormalizationResult {
|
|
166
|
+
const readRoots = normalizePathList(input.readRoots, "intent.read_roots");
|
|
167
|
+
if (!Array.isArray(readRoots)) return readRoots;
|
|
168
|
+
const writeRoots = normalizePathList(input.writeRoots, "intent.write_roots");
|
|
169
|
+
if (!Array.isArray(writeRoots)) return writeRoots;
|
|
170
|
+
const relevantPaths = normalizePathList(input.relevantPaths, "intent.relevant_paths");
|
|
171
|
+
if (!Array.isArray(relevantPaths)) return relevantPaths;
|
|
172
|
+
return {
|
|
173
|
+
ok: true,
|
|
174
|
+
intent: {
|
|
175
|
+
version: 2,
|
|
176
|
+
readRoots,
|
|
177
|
+
writeRoots,
|
|
178
|
+
relevantPaths,
|
|
179
|
+
pathProvenance: declaredIntentPathProvenance({ readRoots, writeRoots, relevantPaths }),
|
|
180
|
+
expectedOutputs: [],
|
|
181
|
+
verification: [],
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
132
186
|
export function isDispatchIntent(value: unknown): value is DispatchIntent {
|
|
133
187
|
if (!isRecord(value) || value.version !== 2) return false;
|
|
134
188
|
for (const field of ["readRoots", "writeRoots", "relevantPaths", "expectedOutputs"] as const) {
|
|
@@ -164,6 +218,30 @@ export function isDispatchIntent(value: unknown): value is DispatchIntent {
|
|
|
164
218
|
);
|
|
165
219
|
}
|
|
166
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Project a declared intent onto a run that may not write.
|
|
223
|
+
*
|
|
224
|
+
* Council members, reviewers, judges, and any other read-only expansion of a
|
|
225
|
+
* caller's single task inherit the caller's declared scope. Inheriting
|
|
226
|
+
* `writeRoots` verbatim would leave the request claiming a write scope the run's
|
|
227
|
+
* autonomy makes unenforceable, so the write roots become read roots: the same
|
|
228
|
+
* trees, strictly less authority. Nothing is added, so the projection can only
|
|
229
|
+
* narrow. Returns the input unchanged when there is no write scope to demote.
|
|
230
|
+
*/
|
|
231
|
+
export function narrowDispatchIntentToReadOnly(intent: DispatchIntent): DispatchIntent {
|
|
232
|
+
if (intent.writeRoots.length === 0) return intent;
|
|
233
|
+
const readRoots = [...new Set([...intent.readRoots, ...intent.writeRoots])].sort(compareCodepoints);
|
|
234
|
+
return {
|
|
235
|
+
version: 2,
|
|
236
|
+
readRoots,
|
|
237
|
+
writeRoots: [],
|
|
238
|
+
relevantPaths: [...intent.relevantPaths],
|
|
239
|
+
pathProvenance: declaredIntentPathProvenance({ readRoots, writeRoots: [], relevantPaths: intent.relevantPaths }),
|
|
240
|
+
expectedOutputs: [...intent.expectedOutputs],
|
|
241
|
+
verification: intent.verification.map((entry) => ({ ...entry })),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
167
245
|
export function normalizeDispatchIntent(
|
|
168
246
|
raw: unknown,
|
|
169
247
|
checks: ReadonlyMap<string, DispatchIntentCheckBound>,
|
|
@@ -173,6 +251,12 @@ export function normalizeDispatchIntent(
|
|
|
173
251
|
.filter((key) => !RAW_FIELDS.has(key))
|
|
174
252
|
.sort(compareCodepoints);
|
|
175
253
|
if (unknown.length > 0) return fail("intent_malformed", `intent contains unknown field '${unknown[0]}'`);
|
|
254
|
+
if (raw.version !== undefined && raw.version !== 2) {
|
|
255
|
+
return fail(
|
|
256
|
+
"intent_version_unsupported",
|
|
257
|
+
`intent declares version ${JSON.stringify(raw.version)}; this build speaks dispatch intent version 2. A declaration at another version is refused rather than migrated: restate read_roots, write_roots, relevant_paths, expected_outputs, and verification under version 2`,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
176
260
|
const readRoots = normalizePathList(raw.read_roots, "intent.read_roots");
|
|
177
261
|
if (!Array.isArray(readRoots)) return readRoots;
|
|
178
262
|
const writeRoots = normalizePathList(raw.write_roots, "intent.write_roots");
|
|
@@ -9,14 +9,29 @@ import {
|
|
|
9
9
|
type CapacityStateFile,
|
|
10
10
|
capacityStateLockPath,
|
|
11
11
|
describeEndpointCapacityHolders,
|
|
12
|
+
endpointCapacityRemedy,
|
|
12
13
|
readCapacityStateUnsafe,
|
|
13
14
|
writeCapacityStateUnsafe,
|
|
14
15
|
} from "./capacity-lease.js";
|
|
16
|
+
import { COUNCIL_MIN_MEMBERS } from "./gate-role-prompts.js";
|
|
15
17
|
|
|
16
18
|
const MAX_RESERVATION_RECORDS = 500;
|
|
17
19
|
export const DEFAULT_RESERVATION_TTL_MS = 15 * 60_000;
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
/**
|
|
22
|
+
* `council` is carried rather than flattened into `parallel` because it is the
|
|
23
|
+
* one topology with a floor on its own width. Every other plan can answer a
|
|
24
|
+
* capacity denial by dispatching fewer at once; a council of two cannot, and a
|
|
25
|
+
* denial that tells it to is advice it cannot take.
|
|
26
|
+
*/
|
|
27
|
+
export type ReservationTopology =
|
|
28
|
+
| "parallel"
|
|
29
|
+
| "detached"
|
|
30
|
+
| "sequential"
|
|
31
|
+
| "pipeline"
|
|
32
|
+
| "review"
|
|
33
|
+
| "compete"
|
|
34
|
+
| "council";
|
|
20
35
|
export type ReservationMemberStatus = "held" | "consumed" | "released";
|
|
21
36
|
export type ReservationStatus = "active" | "released" | "rolled_back" | "expired";
|
|
22
37
|
|
|
@@ -79,8 +94,10 @@ function endpointCapacityDenial(input: {
|
|
|
79
94
|
leases: number;
|
|
80
95
|
reservations: number;
|
|
81
96
|
foregroundStreams: number;
|
|
97
|
+
topology?: ReservationTopology;
|
|
82
98
|
}): string {
|
|
83
|
-
|
|
99
|
+
const remedy = endpointCapacityRemedy(input.topology === "council" ? { rosterFloor: COUNCIL_MIN_MEMBERS } : undefined);
|
|
100
|
+
return `dispatch: admission denied: endpoint '${endpointLabel(input.endpointKey)}' capacity exceeded (${input.used + input.requested}/${input.limit} slots): ${describeEndpointCapacityHolders(input)}; ${remedy}`;
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
function copyRecord(record: DispatchReservationRecord): DispatchReservationRecord {
|
|
@@ -100,7 +117,8 @@ function validRecord(value: unknown): value is DispatchReservationRecord {
|
|
|
100
117
|
record.topology === "sequential" ||
|
|
101
118
|
record.topology === "pipeline" ||
|
|
102
119
|
record.topology === "review" ||
|
|
103
|
-
record.topology === "compete"
|
|
120
|
+
record.topology === "compete" ||
|
|
121
|
+
record.topology === "council") &&
|
|
104
122
|
(record.status === "active" ||
|
|
105
123
|
record.status === "released" ||
|
|
106
124
|
record.status === "rolled_back" ||
|
|
@@ -191,6 +209,7 @@ function allocateReservation(
|
|
|
191
209
|
tasks: ReadonlyArray<ReservationPlanTask>,
|
|
192
210
|
existing: ReadonlyArray<DispatchReservationRecord>,
|
|
193
211
|
capacity: ReservationCapacitySnapshot,
|
|
212
|
+
topology?: ReservationTopology,
|
|
194
213
|
): ReservationPlanResult {
|
|
195
214
|
if (tasks.length === 0) return { ok: false, reason: "reservation requires at least one task" };
|
|
196
215
|
const duplicate = tasks.find((task, index) => tasks.slice(0, index).some((entry) => entry.memberId === task.memberId));
|
|
@@ -230,6 +249,7 @@ function allocateReservation(
|
|
|
230
249
|
leases: endpoint.leases ?? endpoint.active - (endpoint.foregroundStreams ?? 0),
|
|
231
250
|
reservations: heldSlots,
|
|
232
251
|
foregroundStreams: endpoint.foregroundStreams ?? 0,
|
|
252
|
+
...(topology === undefined ? {} : { topology }),
|
|
233
253
|
}),
|
|
234
254
|
};
|
|
235
255
|
}
|
|
@@ -264,7 +284,7 @@ export function createDispatchReservation(input: {
|
|
|
264
284
|
`dispatch: reservation denied: capacity is draining (requested by pid ${drain.requestedByPid} at ${drain.requestedAt})`,
|
|
265
285
|
);
|
|
266
286
|
}
|
|
267
|
-
const planned = allocateReservation(input.tasks, records, input.capacity);
|
|
287
|
+
const planned = allocateReservation(input.tasks, records, input.capacity, input.topology);
|
|
268
288
|
if (!planned.ok) {
|
|
269
289
|
replaceReservations(state, records);
|
|
270
290
|
writeCapacityStateUnsafe(state);
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-owned writer for the run event journal.
|
|
3
|
+
*
|
|
4
|
+
* The journal was originally teed from `createDispatchRunEventRegistry`
|
|
5
|
+
* (src/tools/dispatch-run-events.ts), the display-tail helper behind the
|
|
6
|
+
* model-facing `dispatch` tool. That helper is not on the operator paths:
|
|
7
|
+
* `clio-coder run --agent` (src/cli/run.ts), `clio-coder fleet run`
|
|
8
|
+
* (src/cli/fleet.ts), and the TUI `/run` slash command each take a handle
|
|
9
|
+
* straight off `DispatchContract.dispatch` and iterate `handle.events`
|
|
10
|
+
* themselves, so no registry is ever built and the sink never fired. The
|
|
11
|
+
* writer had to move to something every dispatch reaches.
|
|
12
|
+
*
|
|
13
|
+
* `BusChannels.DispatchProgress` is that thing. The dispatch domain publishes
|
|
14
|
+
* it from inside the event pump for every consumer-visible event of every run,
|
|
15
|
+
* "attached, detached, batched, and retry runs, exactly once" (see
|
|
16
|
+
* src/core/bus-events.ts), whether or not anyone iterates the handle. The
|
|
17
|
+
* terminal pair `DispatchCompleted`/`DispatchFailed` closes every run the same
|
|
18
|
+
* way. Subscribing here makes the journal a property of dispatching, not of
|
|
19
|
+
* which caller happened to build a registry.
|
|
20
|
+
*
|
|
21
|
+
* Ownership is explicit rather than ambient: the bridge attaches only when a
|
|
22
|
+
* composition root asks for it (`DispatchBundleOptions.journalRunEvents`), and
|
|
23
|
+
* `registerAllTools` hands its registry `journal: null` in that same process.
|
|
24
|
+
* One writer per file, decided at one place, so a tool-path run in the TUI is
|
|
25
|
+
* not transcribed twice.
|
|
26
|
+
*
|
|
27
|
+
* Every write is best-effort. The sink degrades itself on I/O failure and a
|
|
28
|
+
* listener that throws is contained by the safe bus, so nothing here can fail a
|
|
29
|
+
* dispatch.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type {
|
|
33
|
+
DispatchCompletedPayload,
|
|
34
|
+
DispatchFailedPayload,
|
|
35
|
+
DispatchProgressPayload,
|
|
36
|
+
} from "../../core/bus-events.js";
|
|
37
|
+
import { BusChannels } from "../../core/bus-events.js";
|
|
38
|
+
import type { SafeEventBus } from "../../core/event-bus.js";
|
|
39
|
+
import { runTailEntryFromEvent } from "../../tools/dispatch-run-events.js";
|
|
40
|
+
import { defaultRunEventJournal, type RunEventJournalSink } from "./run-event-journal.js";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Runs whose `open` line this bridge has already written. Bounded so a
|
|
44
|
+
* long-lived orchestrator cannot grow it without limit; the journal writer
|
|
45
|
+
* keeps its own bound for the same reason. Evicting a run only forgets that it
|
|
46
|
+
* was opened, and a later event for it writes a second `open` line, which the
|
|
47
|
+
* reader and the writer both tolerate.
|
|
48
|
+
*/
|
|
49
|
+
const OPENED_RUN_LIMIT = 512;
|
|
50
|
+
|
|
51
|
+
export interface RunEventJournalBridge {
|
|
52
|
+
/** Unsubscribe from the bus. Idempotent. */
|
|
53
|
+
stop(): void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface AttachRunEventJournalBridgeOptions {
|
|
57
|
+
/** Sink override; defaults to the process-wide journal. */
|
|
58
|
+
journal?: RunEventJournalSink;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
62
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function runIdentity(payload: unknown): { runId: string; agentId: string } | null {
|
|
66
|
+
if (!isRecord(payload)) return null;
|
|
67
|
+
if (typeof payload.runId !== "string" || payload.runId.length === 0) return null;
|
|
68
|
+
return { runId: payload.runId, agentId: typeof payload.agentId === "string" ? payload.agentId : "unknown" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Subscribe the journal to a dispatch bus. Returns a handle whose `stop()` the
|
|
73
|
+
* owning bundle calls on shutdown so a second bundle in the same process (tests
|
|
74
|
+
* do this) never inherits the first one's listeners.
|
|
75
|
+
*/
|
|
76
|
+
export function attachRunEventJournalBridge(
|
|
77
|
+
bus: SafeEventBus,
|
|
78
|
+
options: AttachRunEventJournalBridgeOptions = {},
|
|
79
|
+
): RunEventJournalBridge {
|
|
80
|
+
const journal = options.journal ?? defaultRunEventJournal();
|
|
81
|
+
const opened = new Set<string>();
|
|
82
|
+
|
|
83
|
+
const ensureOpen = (runId: string, agentId: string): void => {
|
|
84
|
+
if (opened.has(runId)) return;
|
|
85
|
+
opened.add(runId);
|
|
86
|
+
while (opened.size > OPENED_RUN_LIMIT) {
|
|
87
|
+
const oldest = opened.values().next();
|
|
88
|
+
if (oldest.done === true) break;
|
|
89
|
+
opened.delete(oldest.value);
|
|
90
|
+
}
|
|
91
|
+
journal.open(runId, agentId);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Write the receipt facts and close the run. Read defensively for the same
|
|
96
|
+
* reason the registry's seal is: a terminal payload that is missing a field
|
|
97
|
+
* it is typed to have must degrade the transcript, never throw back into the
|
|
98
|
+
* finalizer that emitted it.
|
|
99
|
+
*/
|
|
100
|
+
const seal = (outcome: string, detail: string | null | undefined, payload: unknown): void => {
|
|
101
|
+
const identity = runIdentity(payload);
|
|
102
|
+
if (identity === null) return;
|
|
103
|
+
ensureOpen(identity.runId, identity.agentId);
|
|
104
|
+
const exitCode = isRecord(payload) && typeof payload.exitCode === "number" ? payload.exitCode : null;
|
|
105
|
+
journal.receipt(identity.runId, { outcome, exitCode });
|
|
106
|
+
journal.terminal(identity.runId, outcome, detail ?? undefined);
|
|
107
|
+
opened.delete(identity.runId);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const unsubscribes = [
|
|
111
|
+
bus.on(BusChannels.DispatchProgress, (payload: DispatchProgressPayload) => {
|
|
112
|
+
const identity = runIdentity(payload);
|
|
113
|
+
if (identity === null) return;
|
|
114
|
+
// The display projection decides what a transcript line is; a
|
|
115
|
+
// heartbeat or a streaming delta is not one. Opening the run only
|
|
116
|
+
// once an event survives that filter keeps a heartbeat-only run from
|
|
117
|
+
// creating an empty journal directory.
|
|
118
|
+
const entry = runTailEntryFromEvent(payload.event);
|
|
119
|
+
if (entry === null) return;
|
|
120
|
+
ensureOpen(identity.runId, identity.agentId);
|
|
121
|
+
// Route resolution warnings use `message`, while worker transcript
|
|
122
|
+
// events generally project their text through `detail`. Preserve the
|
|
123
|
+
// warning at this dispatch-owned durability seam so fleet view does not
|
|
124
|
+
// render a content-free `route_warning` line.
|
|
125
|
+
const message =
|
|
126
|
+
isRecord(payload.event) && typeof payload.event.message === "string" ? payload.event.message : undefined;
|
|
127
|
+
journal.append(
|
|
128
|
+
identity.runId,
|
|
129
|
+
entry.detail === undefined && message !== undefined ? { ...entry, detail: message } : entry,
|
|
130
|
+
);
|
|
131
|
+
}),
|
|
132
|
+
bus.on(BusChannels.DispatchCompleted, (payload: DispatchCompletedPayload) => {
|
|
133
|
+
seal(payload.outcome, payload.outcomeDetail, payload);
|
|
134
|
+
}),
|
|
135
|
+
bus.on(BusChannels.DispatchFailed, (payload: DispatchFailedPayload) => {
|
|
136
|
+
seal(payload.outcome, payload.outcomeDetail, payload);
|
|
137
|
+
}),
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
let stopped = false;
|
|
141
|
+
return {
|
|
142
|
+
stop(): void {
|
|
143
|
+
if (stopped) return;
|
|
144
|
+
stopped = true;
|
|
145
|
+
for (const unsubscribe of unsubscribes) unsubscribe();
|
|
146
|
+
opened.clear();
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|