@iowarp/clio-coder 0.3.0 → 0.3.2
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 +123 -2
- package/CONTRIBUTING.md +23 -23
- package/README.md +295 -627
- package/dist/acp-BIYHVZIM.js +128 -0
- package/dist/{agents-Q4MYPMUW.js → agents-YT6SSRIT.js} +52 -27
- package/dist/assets/codewiki.json +1 -0
- package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/LICENSE +21 -0
- package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/cgmanifest.json +16 -0
- package/dist/assets/grammars/notices/tree-sitter-wasms/LICENSE +24 -0
- package/dist/assets/grammars/tree-sitter-c-sharp.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-c.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-cpp.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-go.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-java.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-python.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-ruby.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-rust.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/assets/grammars/tree-sitter.wasm +0 -0
- package/dist/{auth-O6HYIJ6J.js → auth-5TWEIYDN.js} +30 -15
- package/dist/chunk-2EHAIA3X.js +481 -0
- package/dist/{chunk-ME6DNWIU.js → chunk-2IR2NMPA.js} +12 -4
- package/dist/chunk-2SFS6XQE.js +122 -0
- package/dist/{chunk-KKKPTZLM.js → chunk-2STGEDFV.js} +8 -1
- package/dist/chunk-2VTFPG5O.js +48 -0
- package/dist/chunk-3I7MS7N2.js +40 -0
- package/dist/chunk-3R73A4XB.js +69 -0
- package/dist/{chunk-JPOWPFCU.js → chunk-3ZXDFGR5.js} +52 -23
- package/dist/chunk-4BJ5BYCE.js +61 -0
- package/dist/{chunk-26BZQOAD.js → chunk-4BPJXDWC.js} +161 -260
- package/dist/chunk-4KLWL3UC.js +18 -0
- package/dist/{chunk-5LDRNKX2.js → chunk-4VP4KH3K.js} +161 -262
- package/dist/chunk-4ZG3XFUR.js +77 -0
- package/dist/chunk-5B2AEOW5.js +5407 -0
- package/dist/chunk-5TSRNF4G.js +510 -0
- package/dist/{chunk-WD6AJM35.js → chunk-5UUP6MWO.js} +48 -69
- package/dist/{chunk-X3BR7HWV.js → chunk-5WIGXA4T.js} +7 -1
- package/dist/chunk-65DEGPJ6.js +52 -0
- package/dist/chunk-6EJMN2Y3.js +17 -0
- package/dist/chunk-6EJV5X2W.js +16405 -0
- package/dist/chunk-6N5PTWMY.js +136 -0
- package/dist/chunk-6XLNIQDB.js +27 -0
- package/dist/{chunk-XNISANGE.js → chunk-77VKQEHF.js} +171 -74
- package/dist/chunk-7CR24IG7.js +242 -0
- package/dist/{chunk-UWIJNAOB.js → chunk-7EYHLWU7.js} +907 -638
- package/dist/chunk-7MNJORFF.js +22 -0
- package/dist/{chunk-JWHCJDCI.js → chunk-A3CYT5EX.js} +257 -79
- package/dist/chunk-AGYYIBLL.js +1069 -0
- package/dist/chunk-AO4RKG4M.js +277 -0
- package/dist/chunk-APJ265NV.js +7410 -0
- package/dist/chunk-ARBGF5F7.js +174 -0
- package/dist/{chunk-TFSSEXL6.js → chunk-BMEMKKIT.js} +14 -4
- package/dist/{chunk-X3NE4WVW.js → chunk-BNAZZHFG.js} +7 -1
- package/dist/chunk-CBCAPZAA.js +229 -0
- package/dist/{chunk-QV47YRF4.js → chunk-CGKSTWHD.js} +7 -1
- package/dist/chunk-CMZWFGD2.js +352 -0
- package/dist/chunk-ECH6PKUQ.js +39 -0
- package/dist/{chunk-67MTHP2E.js → chunk-ED4KHGC3.js} +13 -5
- package/dist/chunk-EKMEHE4H.js +340 -0
- package/dist/{chunk-O4EJEDHO.js → chunk-EPVUXGXG.js} +50 -35
- package/dist/chunk-FCSXB6T2.js +338 -0
- package/dist/chunk-FJ3H4MN5.js +48 -0
- package/dist/chunk-FQ4SKYE4.js +29 -0
- package/dist/chunk-G2DE3C7R.js +644 -0
- package/dist/chunk-G4BMMOKF.js +182 -0
- package/dist/{chunk-7BHLZB3A.js → chunk-GGXXDWE4.js} +190 -1218
- package/dist/chunk-HC4CLZ2Y.js +68 -0
- package/dist/{chunk-E3NYWENM.js → chunk-HFSBBKSQ.js} +55 -61
- package/dist/chunk-HKIYEGME.js +30 -0
- package/dist/chunk-I4HZDVNP.js +73 -0
- package/dist/chunk-IGLFWIYI.js +161 -0
- package/dist/chunk-IHXBNWMM.js +137 -0
- package/dist/chunk-IKCO5N3L.js +162 -0
- package/dist/chunk-IR4CFBFN.js +56 -0
- package/dist/{chunk-DXQNI4PC.js → chunk-IWHMRKLL.js} +8 -1
- package/dist/{chunk-VOO7NYPP.js → chunk-J5HN4RYU.js} +27 -9
- package/dist/{chunk-4UQA7NCT.js → chunk-J5Q24KAG.js} +7 -2
- package/dist/{chunk-KBR4MZZR.js → chunk-JVCV3ICN.js} +7 -1
- package/dist/chunk-KJ5LWLOE.js +1077 -0
- package/dist/chunk-LBMZMYH2.js +285 -0
- package/dist/{chunk-3DANZDGR.js → chunk-LBNRH5WM.js} +100 -161
- package/dist/{chunk-TKHQ4DGZ.js → chunk-LZSJBIVT.js} +7358 -7414
- package/dist/{chunk-UGYAX5YI.js → chunk-M6SHUN7Q.js} +13 -8
- package/dist/chunk-MAW544W2.js +1882 -0
- package/dist/chunk-MBS4V7ZP.js +217 -0
- package/dist/{chunk-2J63S4SF.js → chunk-MFFY33HR.js} +167 -140
- package/dist/chunk-MNA4JGU4.js +255 -0
- package/dist/chunk-MQSRRFWA.js +3428 -0
- package/dist/{chunk-RQDWMVRB.js → chunk-MVVUPGPW.js} +138 -137
- package/dist/chunk-OAO4GE4M.js +619 -0
- package/dist/chunk-OHHN2SO4.js +5135 -0
- package/dist/chunk-OKGUZO2U.js +34 -0
- package/dist/{chunk-BBUH4VAA.js → chunk-OOJYHWRB.js} +60 -361
- package/dist/{chunk-D5YD55AR.js → chunk-OQ33BKR3.js} +12 -1
- package/dist/chunk-OQE5J4C6.js +73 -0
- package/dist/{chunk-AMFR5YA3.js → chunk-ORBHGJC5.js} +26 -12
- package/dist/chunk-POHLU5DW.js +1186 -0
- package/dist/chunk-QKMUKYO7.js +4961 -0
- package/dist/{chunk-7RBKDI66.js → chunk-QTYWRVRA.js} +104 -12
- package/dist/{chunk-5N2FG33Q.js → chunk-R346GLFC.js} +7 -1
- package/dist/chunk-SRF2PJNW.js +184 -0
- package/dist/chunk-SST6Z5JA.js +80 -0
- package/dist/{chunk-XV4ZJ6ZM.js → chunk-STBPMHSX.js} +473 -1194
- package/dist/{chunk-HBU5MTAM.js → chunk-SVNXH4L2.js} +7 -1
- package/dist/chunk-T6YILFSB.js +80 -0
- package/dist/chunk-TXOTCRLG.js +15 -0
- package/dist/chunk-TZTZS7QK.js +227 -0
- package/dist/chunk-UOV2BYIW.js +107 -0
- package/dist/{chunk-527KG6XR.js → chunk-V4RXGQ5Q.js} +72 -185
- package/dist/chunk-VAKQQHWR.js +434 -0
- package/dist/chunk-VG7TBQIY.js +128 -0
- package/dist/chunk-VJWL6YS5.js +244 -0
- package/dist/chunk-WEH5XRJQ.js +32 -0
- package/dist/{chunk-VPAWTYLY.js → chunk-WEPFGWHJ.js} +7 -1
- package/dist/chunk-WMSVI4G2.js +2095 -0
- package/dist/chunk-WVO7V2QY.js +797 -0
- package/dist/chunk-X4RCMKVQ.js +641 -0
- package/dist/chunk-X75S7HFS.js +374 -0
- package/dist/chunk-XN3L4EYL.js +46 -0
- package/dist/{chunk-HMYNFFY4.js → chunk-XPLRXC72.js} +7 -2
- package/dist/{chunk-PIDUD6M2.js → chunk-YCWGATWI.js} +12 -5
- package/dist/chunk-YHZX5GEU.js +193 -0
- package/dist/chunk-YXLYO42X.js +91 -0
- package/dist/{chunk-UHTSULZS.js → chunk-ZDOOVTXZ.js} +38 -73
- package/dist/chunk-ZI647VB5.js +37 -0
- package/dist/{chunk-CLJ5HLUD.js → chunk-ZWLZP4ZT.js} +25 -6
- package/dist/cli/index.js +70 -56
- package/dist/clio-4LY5K2AC.js +25 -0
- package/dist/code-nav-7AX6FYE6.js +600 -0
- package/dist/codewiki/build-worker.js +66 -0
- package/dist/compile-cache-CVJMMODC.js +18 -0
- package/dist/{components-JVHMUBEB.js → components-KELWS457.js} +31 -9
- package/dist/{config-ZFCDBMDC.js → config-GTLUW2PR.js} +104 -56
- package/dist/configure-R6A64DHX.js +42 -0
- package/dist/context-5VKGUVJJ.js +866 -0
- package/dist/{context-CDXTP2MP.js → context-JFZEJ7W5.js} +21 -13
- package/dist/{context-E3KIFVXI.js → context-RW5HC47S.js} +77 -36
- package/dist/{context-clear-3F4PLXOS.js → context-clear-6ZHBAZZT.js} +64 -30
- package/dist/{context-index-Q7YSYTR3.js → context-index-BZ4UYMTC.js} +36 -24
- package/dist/dispatch-runner-VKBRCWQC.js +1997 -0
- package/dist/{docs-YIETIWZI.js → docs-2C2LTVT2.js} +32 -13
- package/dist/doctor-KI767GSN.js +87 -0
- package/dist/{eval-B3KZZESM.js → eval-XSSNATB4.js} +93 -27
- package/dist/{evidence-V67CHM35.js → evidence-UA6AWDQQ.js} +55 -27
- package/dist/{evolve-YDZSUQYA.js → evolve-QNTFGV6Z.js} +59 -26
- package/dist/{extensions-SRG7XCAH.js → extensions-QVDOHDGJ.js} +23 -8
- package/dist/{fleet-CA2CRTVG.js → fleet-Q7UOMUSG.js} +173 -56
- package/dist/fleet-preflight-DDN536IT.js +26 -0
- package/dist/{init-2OZDJE2D.js → init-WBB65ZHQ.js} +79 -34
- package/dist/{memory-3PIQQAKX.js → memory-MD3O64RI.js} +57 -28
- package/dist/{models-DY35XI7Y.js → models-BZU34YWD.js} +49 -24
- package/dist/monitor-MEQA5C3I.js +661 -0
- package/dist/{chunk-PS4PFJQP.js → orchestrator-CGFKEP27.js} +22837 -18564
- package/dist/{paths-5OMXW7Z4.js → paths-UXLN5YYZ.js} +17 -6
- package/dist/preload-P6DGH2PZ.js +13 -0
- package/dist/{reset-PIFYNOS3.js → reset-L2FQEE3E.js} +25 -13
- package/dist/{run-3VSPP24F.js → run-IV4Q6RLN.js} +115 -63
- package/dist/{share-D36RQCXM.js → share-S5BZQC5I.js} +23 -10
- package/dist/{skills-F2MRLELY.js → skills-LQEKRDTN.js} +42 -15
- package/dist/{skills-eval-E2ZTW4PL.js → skills-eval-3DC4HEWS.js} +90 -45
- package/dist/steer-GGWFUJUD.js +77 -0
- package/dist/{targets-DZMEZAH4.js → targets-C4SSGQOB.js} +78 -60
- package/dist/terminal-lease-IT5JW2NR.js +395 -0
- package/dist/{trace-7NYCUI2J.js → trace-PNCASAXC.js} +48 -17
- package/dist/tree-sitter-HGKH6LG4.js +4673 -0
- package/dist/{uninstall-AD3JWHBB.js → uninstall-FZCQCDKC.js} +19 -8
- package/dist/{upgrade-WYYBKGDY.js → upgrade-7TT7SQ3G.js} +66 -24
- package/dist/{usage-ULIDAGFF.js → usage-GV4PKT3M.js} +71 -32
- package/dist/verify-G6V4D2G7.js +716 -0
- package/dist/version-KGEG7EPO.js +21 -0
- package/dist/web-fetch-2YHJ3KTG.js +638 -0
- package/dist/{wiki-generate-PKFIX6OB.js → wiki-generate-DQF6Z66B.js} +98 -48
- package/dist/worker/entry.js +297 -54
- package/dist/workspace-G4ZWUIPR.js +22 -0
- package/docs/README.md +29 -21
- package/docs/acp.md +191 -16
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +157 -7
- package/docs/artifact-placement.md +101 -0
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +4 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +224 -97
- package/docs/config-knobs-audit.md +1 -3
- package/docs/configuration-and-targets.md +236 -38
- package/docs/context-engine.md +34 -7
- package/docs/development-pipeline.md +102 -0
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +4 -4
- package/docs/documentation-guide.md +3 -3
- package/docs/environment-variables.md +15 -6
- package/docs/eval-runner.md +3 -3
- package/docs/evals-internal.md +2 -2
- package/docs/evidence-and-memory.md +10 -2
- package/docs/evolution.md +2 -2
- package/docs/exit-codes-and-output.md +15 -9
- package/docs/extensions-and-sharing.md +25 -18
- package/docs/fleet-demo-runbook.md +22 -23
- package/docs/fleet-dispatch.md +117 -16
- package/docs/git-commit-provenance.md +120 -0
- package/docs/glossary.md +107 -15
- package/docs/installation-and-lifecycle.md +75 -17
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +46 -15
- package/docs/observability.md +8 -5
- package/docs/performance-methodology.md +491 -0
- package/docs/pi-boundary.md +72 -0
- package/docs/proactive-memory.md +14 -14
- package/docs/prompt-envelope-and-tools.md +25 -4
- package/docs/provider-adapter-cookbook.md +57 -4
- package/docs/release-cut-checklist.md +129 -111
- package/docs/safety-model.md +28 -8
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +55 -12
- package/docs/skills-marketplace.md +12 -8
- package/docs/time-conventions.md +101 -0
- package/docs/tool-usage.md +6 -5
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +10 -7
- package/docs/tui-design.md +121 -28
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +33 -36
- package/skills/README.md +5 -6
- package/skills/coding/ast-grep/SKILL.md +136 -0
- package/skills/coding/ast-grep/evals.md +56 -0
- package/skills/coding/ast-grep/references/rule_reference.md +297 -0
- package/skills/coding/coding-standards/SKILL.md +113 -0
- package/skills/coding/coding-standards/evals.md +34 -0
- package/skills/coding/prototype/SKILL.md +86 -0
- package/skills/coding/prototype/evals.md +42 -0
- package/skills/coding/prototype/references/LOGIC.md +67 -0
- package/skills/coding/prototype/references/UI.md +112 -0
- package/skills/coding/tdd/SKILL.md +101 -0
- package/skills/coding/tdd/evals.md +41 -0
- package/skills/coding/tdd/references/mocking.md +59 -0
- package/skills/coding/tdd/references/tests.md +77 -0
- package/skills/context/context-handoff/SKILL.md +126 -0
- package/skills/context/context-handoff/evals.md +57 -0
- package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
- package/skills/context/context-prime/SKILL.md +95 -0
- package/skills/context/context-prime/evals.md +54 -0
- package/skills/git/file-ticket/SKILL.md +107 -0
- package/skills/git/file-ticket/evals.md +54 -0
- package/skills/git/fix-issue/SKILL.md +106 -0
- package/skills/git/fix-issue/evals.md +71 -0
- package/skills/git/ship/SKILL.md +92 -0
- package/skills/git/ship/evals.md +72 -0
- package/skills/meta/clio-dev/SKILL.md +91 -0
- package/skills/meta/clio-dev/evals.md +45 -0
- package/skills/meta/clio-test/SKILL.md +130 -0
- package/skills/meta/clio-test/evals.md +43 -0
- package/skills/meta/clio-test/references/harness.md +97 -0
- package/skills/meta/clio-test/references/test-map.md +59 -0
- package/skills/meta/credentials/SKILL.md +125 -0
- package/skills/meta/credentials/evals.md +104 -0
- package/skills/meta/find-skills/SKILL.md +72 -0
- package/skills/meta/find-skills/evals.md +47 -0
- package/skills/meta/herdr/SKILL.md +127 -0
- package/skills/meta/herdr/evals.md +38 -0
- package/skills/meta/skill-craft/SKILL.md +102 -0
- package/skills/meta/skill-craft/evals.md +41 -0
- package/skills/planning/architecture/SKILL.md +129 -0
- package/skills/planning/architecture/evals.md +36 -0
- package/skills/planning/backlog/SKILL.md +90 -0
- package/skills/planning/backlog/evals.md +43 -0
- package/skills/planning/prd/SKILL.md +82 -0
- package/skills/planning/prd/evals.md +49 -0
- package/skills/planning/product-intent/SKILL.md +112 -0
- package/skills/planning/product-intent/evals.md +36 -0
- package/skills/planning/tech-spec/SKILL.md +115 -0
- package/skills/planning/tech-spec/evals.md +47 -0
- package/skills/registry.yaml +136 -0
- package/skills/research/arxiv-literature/SKILL.md +104 -0
- package/skills/research/arxiv-literature/evals.md +58 -0
- package/skills/research/experiment-protocol/SKILL.md +122 -0
- package/skills/research/experiment-protocol/evals.md +91 -0
- package/skills/research/scientific-debugging/SKILL.md +119 -0
- package/skills/research/scientific-debugging/evals.md +138 -0
- package/skills/research/scientific-modernization/SKILL.md +138 -0
- package/skills/research/scientific-modernization/evals.md +84 -0
- package/skills/skill-marketplace.json +12 -20
- package/skills/workflow/design-council/SKILL.md +139 -0
- package/skills/workflow/design-council/evals.md +97 -0
- package/skills/workflow/grill-me/SKILL.md +186 -0
- package/skills/workflow/grill-me/evals.md +78 -0
- package/skills/workflow/workflow-distiller/SKILL.md +136 -0
- package/skills/workflow/workflow-distiller/evals.md +107 -0
- package/src/cli/acp.ts +115 -0
- package/src/cli/agents.ts +60 -0
- package/src/cli/args.ts +280 -0
- package/src/cli/argv.ts +170 -0
- package/src/cli/ask.ts +34 -0
- package/src/cli/auth.ts +468 -0
- package/src/cli/bootstrap-generate.ts +498 -0
- package/src/cli/clio.ts +96 -0
- package/src/cli/components.ts +195 -0
- package/src/cli/config-inspect.ts +336 -0
- package/src/cli/config.ts +72 -0
- package/src/cli/configure-interop.ts +67 -0
- package/src/cli/configure-layout.ts +172 -0
- package/src/cli/configure.ts +1525 -0
- package/src/cli/context-clear.ts +76 -0
- package/src/cli/context-index.ts +100 -0
- package/src/cli/context.ts +321 -0
- package/src/cli/default-target.ts +55 -0
- package/src/cli/docs.ts +346 -0
- package/src/cli/doctor.ts +54 -0
- package/src/cli/eval.ts +315 -0
- package/src/cli/evidence.ts +225 -0
- package/src/cli/evolve.ts +219 -0
- package/src/cli/extensions.ts +214 -0
- package/src/cli/fleet.ts +868 -0
- package/src/cli/index.ts +338 -0
- package/src/cli/init.ts +219 -0
- package/src/cli/initial-message.ts +47 -0
- package/src/cli/internal-dispatch.ts +55 -0
- package/src/cli/memory.ts +182 -0
- package/src/cli/models.ts +257 -0
- package/src/cli/modes/json-stream.ts +121 -0
- package/src/cli/modes/jsonl.ts +9 -0
- package/src/cli/modes/print.ts +604 -0
- package/src/cli/oauth-manual-input.ts +72 -0
- package/src/cli/oauth-select.ts +36 -0
- package/src/cli/output-guard.ts +48 -0
- package/src/cli/paths.ts +44 -0
- package/src/cli/provider-target.ts +113 -0
- package/src/cli/removal.ts +72 -0
- package/src/cli/reset.ts +253 -0
- package/src/cli/run.ts +497 -0
- package/src/cli/share.ts +243 -0
- package/src/cli/shared.ts +60 -0
- package/src/cli/skills-eval.ts +1240 -0
- package/src/cli/skills.ts +485 -0
- package/src/cli/steer-channel.ts +86 -0
- package/src/cli/targets.ts +1154 -0
- package/src/cli/text-layout.ts +112 -0
- package/src/cli/trace.ts +322 -0
- package/src/cli/uninstall.ts +405 -0
- package/src/cli/upgrade.ts +286 -0
- package/src/cli/usage.ts +876 -0
- package/src/cli/validate-model.ts +28 -0
- package/src/cli/version.ts +7 -0
- package/src/cli/wiki-generate.ts +510 -0
- package/src/core/agent-environment.ts +7 -0
- package/src/core/artifact-paths.ts +43 -0
- package/src/core/bash-exec.ts +313 -0
- package/src/core/boot-trace.ts +42 -0
- package/src/core/bus-events.ts +744 -0
- package/src/core/bus-trace.ts +53 -0
- package/src/core/c-header-language.ts +265 -0
- package/src/core/clio-repo.ts +78 -0
- package/src/core/commit-attribution.ts +157 -0
- package/src/core/compile-cache.ts +159 -0
- package/src/core/config.ts +1722 -0
- package/src/core/context-floor.ts +33 -0
- package/src/core/defaults.ts +659 -0
- package/src/core/domain-loader.ts +177 -0
- package/src/core/event-bus.ts +94 -0
- package/src/core/file-references.ts +282 -0
- package/src/core/git-commit-attribution.ts +362 -0
- package/src/core/guardrails.ts +185 -0
- package/src/core/headless-permission.ts +21 -0
- package/src/core/incomplete-installation.ts +45 -0
- package/src/core/init.ts +223 -0
- package/src/core/json-payload.ts +58 -0
- package/src/core/package-root.ts +54 -0
- package/src/core/path-canonical.ts +37 -0
- package/src/core/process-identity.ts +41 -0
- package/src/core/recent-models.ts +91 -0
- package/src/core/residency-protection.ts +56 -0
- package/src/core/resolve-config-value.ts +88 -0
- package/src/core/response-schema.ts +279 -0
- package/src/core/run-overrides.ts +77 -0
- package/src/core/safe-exec.ts +205 -0
- package/src/core/safe-id.ts +37 -0
- package/src/core/safe-resource-write.ts +162 -0
- package/src/core/session-routing.ts +339 -0
- package/src/core/settings-layers.ts +354 -0
- package/src/core/shared-bus.ts +8 -0
- package/src/core/shell-quote.ts +26 -0
- package/src/core/skill-activation.ts +190 -0
- package/src/core/startup-timer.ts +37 -0
- package/src/core/state-file-lock.ts +219 -0
- package/src/core/termination.ts +275 -0
- package/src/core/timers.ts +38 -0
- package/src/core/tool-names.ts +65 -0
- package/src/core/verification-scripts.ts +13 -0
- package/src/core/workspace-files.ts +492 -0
- package/src/core/xdg.ts +177 -0
- package/src/domains/agents/builtins/architect.md +4 -4
- package/src/domains/agents/builtins/coder.md +3 -2
- package/src/domains/agents/builtins/debugger.md +1 -1
- package/src/domains/agents/builtins/documenter.md +1 -1
- package/src/domains/agents/builtins/git-master.md +3 -3
- package/src/domains/agents/builtins/provenance.md +1 -1
- package/src/domains/agents/builtins/researcher.md +1 -1
- package/src/domains/agents/builtins/scout.md +1 -1
- package/src/domains/agents/builtins/tester.md +1 -1
- package/src/domains/agents/builtins/verifier.md +1 -1
- package/src/domains/agents/builtins/wiki-writer.md +1 -1
- package/src/domains/agents/catalog.ts +172 -0
- package/src/domains/agents/contract.ts +16 -0
- package/src/domains/agents/extension.ts +110 -0
- package/src/domains/agents/fleet-commands.ts +154 -0
- package/src/domains/agents/fleet-contract.ts +797 -0
- package/src/domains/agents/frontmatter.ts +40 -0
- package/src/domains/agents/index.ts +102 -0
- package/src/domains/agents/manifest.ts +6 -0
- package/src/domains/agents/recipe-schema.ts +202 -0
- package/src/domains/agents/recipe.ts +125 -0
- package/src/domains/agents/registry.ts +111 -0
- package/src/domains/agents/result-contract.ts +1265 -0
- package/src/domains/agents/spec.ts +371 -0
- package/src/domains/agents/write-boundary.ts +109 -0
- package/src/domains/components/diff.ts +99 -0
- package/src/domains/components/hash.ts +21 -0
- package/src/domains/components/index.ts +3 -0
- package/src/domains/components/scan.ts +304 -0
- package/src/domains/components/snapshot.ts +108 -0
- package/src/domains/components/types.ts +82 -0
- package/src/domains/config/agent-namespace.ts +13 -0
- package/src/domains/config/classify.ts +106 -0
- package/src/domains/config/contract.ts +22 -0
- package/src/domains/config/extension.ts +142 -0
- package/src/domains/config/index.ts +23 -0
- package/src/domains/config/keybindings.ts +200 -0
- package/src/domains/config/manifest.ts +6 -0
- package/src/domains/config/watcher.ts +41 -0
- package/src/domains/context/adoption.ts +880 -0
- package/src/domains/context/bootstrap-prompt.ts +273 -0
- package/src/domains/context/bootstrap.ts +1502 -0
- package/src/domains/context/clear.ts +91 -0
- package/src/domains/context/clio-md.ts +429 -0
- package/src/domains/context/codewiki/artifact.ts +400 -0
- package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
- package/src/domains/context/codewiki/build-worker.ts +54 -0
- package/src/domains/context/codewiki/cmake.ts +356 -0
- package/src/domains/context/codewiki/cooperative.ts +46 -0
- package/src/domains/context/codewiki/coordinator.ts +182 -0
- package/src/domains/context/codewiki/digest.ts +135 -0
- package/src/domains/context/codewiki/grammar-assets.ts +33 -0
- package/src/domains/context/codewiki/indexer.ts +1529 -0
- package/src/domains/context/codewiki/paths.ts +67 -0
- package/src/domains/context/codewiki/schema.ts +80 -0
- package/src/domains/context/codewiki/tree-sitter.ts +771 -0
- package/src/domains/context/contract.ts +68 -0
- package/src/domains/context/excluded-dirs.ts +1 -0
- package/src/domains/context/extension.ts +358 -0
- package/src/domains/context/fingerprint.ts +198 -0
- package/src/domains/context/index.ts +154 -0
- package/src/domains/context/init-options.ts +69 -0
- package/src/domains/context/manifest.ts +6 -0
- package/src/domains/context/operator-profile.ts +157 -0
- package/src/domains/context/project-metadata.ts +529 -0
- package/src/domains/context/project-rules.ts +227 -0
- package/src/domains/context/prompt-context.ts +61 -0
- package/src/domains/context/refresh.ts +178 -0
- package/src/domains/context/runtime.ts +17 -0
- package/src/domains/context/sibling-files.ts +10 -0
- package/src/domains/context/state.ts +237 -0
- package/src/domains/context/wiki/assemble.ts +303 -0
- package/src/domains/context/wiki/frontmatter.ts +210 -0
- package/src/domains/context/wiki/generate.ts +595 -0
- package/src/domains/context/wiki/layout.ts +111 -0
- package/src/domains/context/wiki/meta.ts +221 -0
- package/src/domains/context/wiki/plan-store.ts +217 -0
- package/src/domains/context/wiki/plan.ts +250 -0
- package/src/domains/context/wiki/prompts.ts +283 -0
- package/src/domains/context/wiki/staleness.ts +198 -0
- package/src/domains/dispatch/active-route-planner.ts +168 -0
- package/src/domains/dispatch/admission-queue.ts +150 -0
- package/src/domains/dispatch/admission.ts +334 -0
- package/src/domains/dispatch/agent-candidates.ts +307 -0
- package/src/domains/dispatch/agent-ledger-hub.ts +71 -0
- package/src/domains/dispatch/agent-ledger-store.ts +257 -0
- package/src/domains/dispatch/agent-ledger.ts +212 -0
- package/src/domains/dispatch/agent-plan-adapter.ts +85 -0
- package/src/domains/dispatch/assignment-events.ts +132 -0
- package/src/domains/dispatch/assignment-reconcile.ts +76 -0
- package/src/domains/dispatch/assignment-store.ts +162 -0
- package/src/domains/dispatch/assignment.ts +278 -0
- package/src/domains/dispatch/backoff.ts +59 -0
- package/src/domains/dispatch/batch-store.ts +134 -0
- package/src/domains/dispatch/batch-tracker.ts +66 -0
- package/src/domains/dispatch/capability-match.ts +155 -0
- package/src/domains/dispatch/capacity-lease.ts +413 -0
- package/src/domains/dispatch/code-step-store.ts +52 -0
- package/src/domains/dispatch/code-step.ts +387 -0
- package/src/domains/dispatch/contract.ts +278 -0
- package/src/domains/dispatch/dedup.ts +133 -0
- package/src/domains/dispatch/event-pump.ts +251 -0
- package/src/domains/dispatch/execution-handoff.ts +29 -0
- package/src/domains/dispatch/execution-plan.ts +328 -0
- package/src/domains/dispatch/execution-role.ts +277 -0
- package/src/domains/dispatch/execution-scheduler.ts +583 -0
- package/src/domains/dispatch/extension.ts +6006 -0
- package/src/domains/dispatch/failure-classification.ts +147 -0
- package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
- package/src/domains/dispatch/fleet-plan.ts +225 -0
- package/src/domains/dispatch/fleet-preflight.ts +394 -0
- package/src/domains/dispatch/gate-decisions.ts +1040 -0
- package/src/domains/dispatch/gate-role-prompts.ts +58 -0
- package/src/domains/dispatch/heartbeat.ts +27 -0
- package/src/domains/dispatch/index.ts +137 -0
- package/src/domains/dispatch/joint-route-adapter.ts +114 -0
- package/src/domains/dispatch/joint-route-resolver.ts +283 -0
- package/src/domains/dispatch/manifest.ts +6 -0
- package/src/domains/dispatch/orphan-recovery.ts +266 -0
- package/src/domains/dispatch/outcome.ts +109 -0
- package/src/domains/dispatch/phase-timing.ts +47 -0
- package/src/domains/dispatch/placement.ts +202 -0
- package/src/domains/dispatch/receipt-findings.ts +255 -0
- package/src/domains/dispatch/receipt-integrity.ts +383 -0
- package/src/domains/dispatch/recovery-candidates.ts +74 -0
- package/src/domains/dispatch/reproducibility.ts +48 -0
- package/src/domains/dispatch/reservation-store.ts +501 -0
- package/src/domains/dispatch/route-approval.ts +177 -0
- package/src/domains/dispatch/route-candidates.ts +57 -0
- package/src/domains/dispatch/route-decision.ts +1079 -0
- package/src/domains/dispatch/route-facts.ts +187 -0
- package/src/domains/dispatch/route-history.ts +238 -0
- package/src/domains/dispatch/route-observer.ts +488 -0
- package/src/domains/dispatch/route-policy.ts +327 -0
- package/src/domains/dispatch/route-quality.ts +256 -0
- package/src/domains/dispatch/route-readiness.ts +120 -0
- package/src/domains/dispatch/routing-intent.ts +287 -0
- package/src/domains/dispatch/run-identity.ts +56 -0
- package/src/domains/dispatch/scout-transition.ts +130 -0
- package/src/domains/dispatch/state.ts +302 -0
- package/src/domains/dispatch/tool-stats.ts +132 -0
- package/src/domains/dispatch/transport.ts +210 -0
- package/src/domains/dispatch/types.ts +799 -0
- package/src/domains/dispatch/validation-grounding.ts +154 -0
- package/src/domains/dispatch/validation.ts +721 -0
- package/src/domains/dispatch/worker-protocol.ts +275 -0
- package/src/domains/dispatch/worker-spawn.ts +585 -0
- package/src/domains/dispatch/write-boundary-enforcer.ts +97 -0
- package/src/domains/dispatch/write-boundary.ts +426 -0
- package/src/domains/eval/artifacts/redact.ts +42 -0
- package/src/domains/eval/artifacts/store.ts +173 -0
- package/src/domains/eval/compare/compare.ts +33 -0
- package/src/domains/eval/compare/gates.ts +66 -0
- package/src/domains/eval/compare/thresholds.ts +92 -0
- package/src/domains/eval/harness-metrics.ts +62 -0
- package/src/domains/eval/index.ts +66 -0
- package/src/domains/eval/metrics/chaos-stream.ts +93 -0
- package/src/domains/eval/metrics/context.ts +34 -0
- package/src/domains/eval/metrics/coverage.ts +21 -0
- package/src/domains/eval/metrics/evidence.ts +182 -0
- package/src/domains/eval/metrics/fleet-loop-stream.ts +196 -0
- package/src/domains/eval/metrics/invariants.ts +736 -0
- package/src/domains/eval/metrics/latency.ts +4 -0
- package/src/domains/eval/metrics/token-stream.ts +139 -0
- package/src/domains/eval/metrics/tokens.ts +51 -0
- package/src/domains/eval/metrics/tool-calls.ts +7 -0
- package/src/domains/eval/proactive-memory.ts +279 -0
- package/src/domains/eval/provenance.ts +35 -0
- package/src/domains/eval/report.ts +234 -0
- package/src/domains/eval/reports/json.ts +5 -0
- package/src/domains/eval/reports/junit.ts +23 -0
- package/src/domains/eval/reports/markdown.ts +20 -0
- package/src/domains/eval/reports/swe-jsonl.ts +16 -0
- package/src/domains/eval/reports/text.ts +25 -0
- package/src/domains/eval/run-compare.ts +274 -0
- package/src/domains/eval/runner.ts +244 -0
- package/src/domains/eval/runners/clio-run.ts +148 -0
- package/src/domains/eval/runners/context-index.ts +56 -0
- package/src/domains/eval/runners/context-init.ts +203 -0
- package/src/domains/eval/runners/external-command.ts +350 -0
- package/src/domains/eval/runtime-paths.ts +171 -0
- package/src/domains/eval/schema/artifact.ts +75 -0
- package/src/domains/eval/schema/suite.ts +235 -0
- package/src/domains/eval/schema/validate.ts +339 -0
- package/src/domains/eval/store.ts +392 -0
- package/src/domains/eval/suites/load.ts +74 -0
- package/src/domains/eval/suites/matrix.ts +19 -0
- package/src/domains/eval/suites/resolve.ts +44 -0
- package/src/domains/eval/suites/run.ts +325 -0
- package/src/domains/eval/task-file.ts +186 -0
- package/src/domains/eval/types.ts +140 -0
- package/src/domains/eval/verifiers/command.ts +27 -0
- package/src/domains/eval/verifiers/file-exists.ts +6 -0
- package/src/domains/eval/verifiers/patch.ts +22 -0
- package/src/domains/eval/workspaces/git.ts +41 -0
- package/src/domains/eval/workspaces/local.ts +17 -0
- package/src/domains/eval/workspaces/temp-copy.ts +29 -0
- package/src/domains/evidence/build.ts +1754 -0
- package/src/domains/evidence/eval.ts +605 -0
- package/src/domains/evidence/failure-attribution.ts +54 -0
- package/src/domains/evidence/finish-contract-map.ts +42 -0
- package/src/domains/evidence/index.ts +55 -0
- package/src/domains/evidence/provenance.ts +200 -0
- package/src/domains/evidence/redact.ts +81 -0
- package/src/domains/evidence/store.ts +291 -0
- package/src/domains/evidence/types.ts +325 -0
- package/src/domains/evolution/SELF_EDIT_GATE.md +91 -0
- package/src/domains/evolution/index.ts +26 -0
- package/src/domains/evolution/manifest.ts +154 -0
- package/src/domains/evolution/validate.ts +304 -0
- package/src/domains/extensions/contract.ts +22 -0
- package/src/domains/extensions/discovery.ts +193 -0
- package/src/domains/extensions/extension.ts +46 -0
- package/src/domains/extensions/index.ts +34 -0
- package/src/domains/extensions/manager.ts +30 -0
- package/src/domains/extensions/manifest.ts +6 -0
- package/src/domains/extensions/resources.ts +38 -0
- package/src/domains/extensions/state.ts +224 -0
- package/src/domains/extensions/types.ts +83 -0
- package/src/domains/interop/consent.ts +185 -0
- package/src/domains/interop/contract.ts +29 -0
- package/src/domains/interop/detect.ts +173 -0
- package/src/domains/interop/extension.ts +59 -0
- package/src/domains/interop/index.ts +34 -0
- package/src/domains/interop/manifest.ts +6 -0
- package/src/domains/interop/registry.ts +139 -0
- package/src/domains/interop/state.ts +92 -0
- package/src/domains/interop/types.ts +109 -0
- package/src/domains/lifecycle/contract.ts +11 -0
- package/src/domains/lifecycle/doctor.ts +536 -0
- package/src/domains/lifecycle/extension.ts +23 -0
- package/src/domains/lifecycle/index.ts +21 -0
- package/src/domains/lifecycle/install-method.ts +51 -0
- package/src/domains/lifecycle/manifest.ts +6 -0
- package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
- package/src/domains/lifecycle/migrations/index.ts +114 -0
- package/src/domains/lifecycle/state.ts +93 -0
- package/src/domains/lifecycle/upgrade-notice.ts +35 -0
- package/src/domains/lifecycle/version.ts +35 -0
- package/src/domains/memory/index.ts +128 -0
- package/src/domains/memory/operations.ts +143 -0
- package/src/domains/memory/prompt-section.ts +86 -0
- package/src/domains/memory/proposal.ts +180 -0
- package/src/domains/memory/store.ts +163 -0
- package/src/domains/memory/task-bank.ts +229 -0
- package/src/domains/memory/task-memory-handoff.ts +222 -0
- package/src/domains/memory/task-memory-policy.ts +602 -0
- package/src/domains/memory/task-memory-status.ts +58 -0
- package/src/domains/memory/task-memory-telemetry.ts +346 -0
- package/src/domains/memory/task-memory-trace.ts +89 -0
- package/src/domains/memory/types.ts +74 -0
- package/src/domains/memory/validate.ts +297 -0
- package/src/domains/middleware/budget.ts +216 -0
- package/src/domains/middleware/contract.ts +35 -0
- package/src/domains/middleware/dispatch-nudge.ts +469 -0
- package/src/domains/middleware/extension.ts +129 -0
- package/src/domains/middleware/hook-receipts.ts +70 -0
- package/src/domains/middleware/hooks-io.ts +186 -0
- package/src/domains/middleware/hooks.ts +432 -0
- package/src/domains/middleware/index.ts +154 -0
- package/src/domains/middleware/manifest.ts +6 -0
- package/src/domains/middleware/memory-intervention.ts +701 -0
- package/src/domains/middleware/rules.ts +42 -0
- package/src/domains/middleware/runtime.ts +333 -0
- package/src/domains/middleware/skills-reminder.ts +202 -0
- package/src/domains/middleware/snapshot.ts +71 -0
- package/src/domains/middleware/stalled-turn.ts +71 -0
- package/src/domains/middleware/task-board-reminder.ts +87 -0
- package/src/domains/middleware/task-nudge.ts +77 -0
- package/src/domains/middleware/tool-choice-control.ts +42 -0
- package/src/domains/middleware/types.ts +131 -0
- package/src/domains/middleware/validate.ts +329 -0
- package/src/domains/observability/accountability.ts +67 -0
- package/src/domains/observability/contract.ts +168 -0
- package/src/domains/observability/cost.ts +206 -0
- package/src/domains/observability/evidence-index.ts +123 -0
- package/src/domains/observability/extension.ts +281 -0
- package/src/domains/observability/index.ts +78 -0
- package/src/domains/observability/manifest.ts +10 -0
- package/src/domains/observability/metrics.ts +37 -0
- package/src/domains/observability/projection.ts +477 -0
- package/src/domains/observability/telemetry.ts +89 -0
- package/src/domains/observability/trace-store.ts +1454 -0
- package/src/domains/prompts/compiler.ts +614 -0
- package/src/domains/prompts/contract.ts +46 -0
- package/src/domains/prompts/extension.ts +336 -0
- package/src/domains/prompts/fragment-loader.ts +138 -0
- package/src/domains/prompts/fragments/identity/clio.md +5 -6
- package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
- package/src/domains/prompts/fragments/identity/self-awareness.md +19 -0
- package/src/domains/prompts/fragments/operating/contract.md +4 -41
- package/src/domains/prompts/fragments/operating/delegation.md +42 -0
- package/src/domains/prompts/fragments/operating/skills.md +26 -0
- package/src/domains/prompts/fragments/operating/worker.md +16 -0
- package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
- package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
- package/src/domains/prompts/fragments/safety/read-only.md +4 -4
- package/src/domains/prompts/fragments/safety/suggest.md +2 -2
- package/src/domains/prompts/fragments/wiki/page.md +10 -0
- package/src/domains/prompts/fragments/wiki/plan.md +10 -0
- package/src/domains/prompts/hash.ts +73 -0
- package/src/domains/prompts/index.ts +32 -0
- package/src/domains/prompts/manifest.ts +8 -0
- package/src/domains/prompts/memory-intervention.ts +57 -0
- package/src/domains/prompts/preload.ts +82 -0
- package/src/domains/providers/auth/api-key.ts +47 -0
- package/src/domains/providers/auth/backend-file.ts +79 -0
- package/src/domains/providers/auth/backend-memory.ts +72 -0
- package/src/domains/providers/auth/boot-status.ts +65 -0
- package/src/domains/providers/auth/index.ts +37 -0
- package/src/domains/providers/auth/oauth.ts +37 -0
- package/src/domains/providers/auth/storage.ts +726 -0
- package/src/domains/providers/capabilities.ts +58 -0
- package/src/domains/providers/catalog.ts +162 -0
- package/src/domains/providers/contract.ts +162 -0
- package/src/domains/providers/credentials.ts +20 -0
- package/src/domains/providers/eligibility.ts +24 -0
- package/src/domains/providers/errors.ts +64 -0
- package/src/domains/providers/extension.ts +548 -0
- package/src/domains/providers/index.ts +142 -0
- package/src/domains/providers/knowledge-base-path.ts +102 -0
- package/src/domains/providers/manifest.ts +6 -0
- package/src/domains/providers/model-capabilities.ts +136 -0
- package/src/domains/providers/model-discovery.ts +144 -0
- package/src/domains/providers/model-family.ts +32 -0
- package/src/domains/providers/model-runtime-capabilities.ts +723 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +120 -37
- package/src/domains/providers/plugins.ts +61 -0
- package/src/domains/providers/probe/fingerprint.ts +64 -0
- package/src/domains/providers/probe/http.ts +114 -0
- package/src/domains/providers/probe/reasoning.ts +111 -0
- package/src/domains/providers/registry.ts +187 -0
- package/src/domains/providers/resolver.ts +156 -0
- package/src/domains/providers/runtime-resolution.ts +720 -0
- package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +90 -0
- package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
- package/src/domains/providers/runtimes/builtins.ts +78 -0
- package/src/domains/providers/runtimes/claude/claude-code.ts +38 -0
- package/src/domains/providers/runtimes/claude/claude-sdk.ts +38 -0
- package/src/domains/providers/runtimes/claude/common.ts +55 -0
- package/src/domains/providers/runtimes/cloud/alcf.ts +171 -0
- package/src/domains/providers/runtimes/cloud/anthropic-max.ts +71 -0
- package/src/domains/providers/runtimes/cloud/anthropic.ts +43 -0
- package/src/domains/providers/runtimes/cloud/bedrock.ts +35 -0
- package/src/domains/providers/runtimes/cloud/deepseek.ts +50 -0
- package/src/domains/providers/runtimes/cloud/google.ts +41 -0
- package/src/domains/providers/runtimes/cloud/groq.ts +46 -0
- package/src/domains/providers/runtimes/cloud/mistral.ts +46 -0
- package/src/domains/providers/runtimes/cloud/openai-codex.ts +49 -0
- package/src/domains/providers/runtimes/cloud/openai.ts +46 -0
- package/src/domains/providers/runtimes/cloud/openrouter.ts +111 -0
- package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
- package/src/domains/providers/runtimes/common/local-synth.ts +164 -0
- package/src/domains/providers/runtimes/common/probe-helpers.ts +474 -0
- package/src/domains/providers/runtimes/local-native/lemonade-anthropic.ts +27 -0
- package/src/domains/providers/runtimes/local-native/lemonade-openai.ts +26 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +88 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +207 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +180 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +126 -0
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +98 -0
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +117 -0
- package/src/domains/providers/runtimes/local-native/sglang.ts +26 -0
- package/src/domains/providers/runtimes/local-native/vllm.ts +27 -0
- package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +104 -0
- package/src/domains/providers/runtimes/protocol/anthropic-messages.ts +28 -0
- package/src/domains/providers/runtimes/protocol/bedrock.ts +26 -0
- package/src/domains/providers/runtimes/protocol/google.ts +27 -0
- package/src/domains/providers/runtimes/protocol/openai-compat.ts +145 -0
- package/src/domains/providers/support.ts +241 -0
- package/src/domains/providers/thinking-control-policy.ts +85 -0
- package/src/domains/providers/types/capability-flags.ts +114 -0
- package/src/domains/providers/types/cost-provenance.ts +7 -0
- package/src/domains/providers/types/inference.ts +41 -0
- package/src/domains/providers/types/knowledge-base.ts +156 -0
- package/src/domains/providers/types/local-model-quirks.ts +216 -0
- package/src/domains/providers/types/runtime-descriptor.ts +181 -0
- package/src/domains/providers/types/target-descriptor.ts +60 -0
- package/src/domains/resources/collision.ts +92 -0
- package/src/domains/resources/common-loader.ts +140 -0
- package/src/domains/resources/contract.ts +24 -0
- package/src/domains/resources/extension.ts +59 -0
- package/src/domains/resources/index.ts +74 -0
- package/src/domains/resources/loader.ts +85 -0
- package/src/domains/resources/manifest.ts +6 -0
- package/src/domains/resources/prompts/loader.ts +246 -0
- package/src/domains/resources/prompts/substitute.ts +1 -0
- package/src/domains/resources/skills/content-hash.ts +107 -0
- package/src/domains/resources/skills/evals.ts +175 -0
- package/src/domains/resources/skills/install.ts +427 -0
- package/src/domains/resources/skills/loader.ts +985 -0
- package/src/domains/resources/skills/marketplace.ts +301 -0
- package/src/domains/resources/skills/provenance-pin.ts +115 -0
- package/src/domains/safety/action-classifier.ts +270 -0
- package/src/domains/safety/approval-axis.ts +23 -0
- package/src/domains/safety/audit.ts +493 -0
- package/src/domains/safety/autonomy.ts +174 -0
- package/src/domains/safety/call-target.ts +59 -0
- package/src/domains/safety/contract.ts +65 -0
- package/src/domains/safety/damage-control.ts +44 -0
- package/src/domains/safety/default-path-policy.ts +117 -0
- package/src/domains/safety/extension.ts +383 -0
- package/src/domains/safety/finish-contract-registration.ts +128 -0
- package/src/domains/safety/finish-contract.ts +554 -0
- package/src/domains/safety/index.ts +44 -0
- package/src/domains/safety/loop-detector.ts +143 -0
- package/src/domains/safety/manifest.ts +6 -0
- package/src/domains/safety/path-policy.ts +164 -0
- package/src/domains/safety/policy-engine.ts +994 -0
- package/src/domains/safety/project-policy.ts +391 -0
- package/src/domains/safety/protected-artifacts-registration.ts +189 -0
- package/src/domains/safety/protected-artifacts.ts +993 -0
- package/src/domains/safety/rejection-feedback.ts +102 -0
- package/src/domains/safety/rigor.ts +72 -0
- package/src/domains/safety/rule-compiler.ts +48 -0
- package/src/domains/safety/rule-pack-loader.ts +92 -0
- package/src/domains/safety/run-effects.ts +197 -0
- package/src/domains/safety/scope.ts +61 -0
- package/src/domains/scheduling/budget.ts +35 -0
- package/src/domains/scheduling/cluster.ts +175 -0
- package/src/domains/scheduling/contract.ts +30 -0
- package/src/domains/scheduling/extension.ts +91 -0
- package/src/domains/scheduling/index.ts +12 -0
- package/src/domains/scheduling/manifest.ts +6 -0
- package/src/domains/session/archive-readers.ts +208 -0
- package/src/domains/session/checkpoint.ts +38 -0
- package/src/domains/session/compaction/auto.ts +89 -0
- package/src/domains/session/compaction/branch-summary.ts +187 -0
- package/src/domains/session/compaction/compact.ts +493 -0
- package/src/domains/session/compaction/cut-point.ts +172 -0
- package/src/domains/session/compaction/defaults.ts +34 -0
- package/src/domains/session/compaction/mask-observations.ts +265 -0
- package/src/domains/session/compaction/session-entries.ts +18 -0
- package/src/domains/session/compaction/tokens.ts +148 -0
- package/src/domains/session/context-accounting.ts +583 -0
- package/src/domains/session/context-ledger.ts +362 -0
- package/src/domains/session/contract.ts +159 -0
- package/src/domains/session/cwd-fallback.ts +65 -0
- package/src/domains/session/decision-board.ts +190 -0
- package/src/domains/session/entries.ts +572 -0
- package/src/domains/session/extension.ts +403 -0
- package/src/domains/session/history.ts +172 -0
- package/src/domains/session/index.ts +84 -0
- package/src/domains/session/manager.ts +128 -0
- package/src/domains/session/manifest.ts +6 -0
- package/src/domains/session/migrations/index.ts +27 -0
- package/src/domains/session/prompt-manifest.ts +173 -0
- package/src/domains/session/protected-artifact-journal.ts +241 -0
- package/src/domains/session/protected-artifacts.ts +90 -0
- package/src/domains/session/retry.ts +230 -0
- package/src/domains/session/session-artifacts.ts +107 -0
- package/src/domains/session/task-board.ts +569 -0
- package/src/domains/session/tree/active-path.ts +137 -0
- package/src/domains/session/tree/fork.ts +159 -0
- package/src/domains/session/tree/manager.ts +128 -0
- package/src/domains/session/tree/navigator.ts +177 -0
- package/src/domains/session/tree/preview.ts +259 -0
- package/src/domains/session/usage.ts +148 -0
- package/src/domains/session/workspace/git-probe.ts +167 -0
- package/src/domains/session/workspace/index.ts +4 -0
- package/src/domains/session/workspace/project-type.ts +217 -0
- package/src/domains/session/workspace/snapshot.ts +74 -0
- package/src/domains/share/archive.ts +684 -0
- package/src/domains/share/contract.ts +9 -0
- package/src/domains/share/extension.ts +26 -0
- package/src/domains/share/index.ts +28 -0
- package/src/domains/share/manifest.ts +6 -0
- package/src/domains/user-tasks/store.ts +297 -0
- package/src/engine/acp/adapter.ts +397 -0
- package/src/engine/acp/errors.ts +128 -0
- package/src/engine/acp/event-mapper.ts +154 -0
- package/src/engine/acp/server.ts +2238 -0
- package/src/engine/acp/tool-mediator.ts +686 -0
- package/src/engine/acp/transport.ts +775 -0
- package/src/engine/acp/types.ts +196 -0
- package/src/engine/agent.ts +29 -0
- package/src/engine/ai.ts +207 -0
- package/src/engine/alcf-oauth.ts +205 -0
- package/src/engine/antigravity/subprocess-runtime.ts +205 -0
- package/src/engine/api-registry.ts +223 -0
- package/src/engine/apis/degraded-inference.ts +95 -0
- package/src/engine/apis/index.ts +16 -0
- package/src/engine/apis/llamacpp-residency.ts +292 -0
- package/src/engine/apis/lmstudio-residency.ts +76 -0
- package/src/engine/apis/lmstudio.ts +243 -0
- package/src/engine/apis/ollama-native.ts +528 -0
- package/src/engine/apis/openai-completions.ts +795 -0
- package/src/engine/apis/output-budget.ts +99 -0
- package/src/engine/apis/residency-lock.ts +29 -0
- package/src/engine/apis/residency.ts +776 -0
- package/src/engine/apis/resident-models.ts +42 -0
- package/src/engine/apis/sampling-overrides.ts +38 -0
- package/src/engine/apis/types.ts +16 -0
- package/src/engine/claude/sdk-runtime.ts +685 -0
- package/src/engine/claude/session-id.ts +5 -0
- package/src/engine/claude/subprocess-runtime.ts +316 -0
- package/src/engine/claude/tool-safety.ts +400 -0
- package/src/engine/env-api-keys.ts +98 -0
- package/src/engine/external-subprocess.ts +23 -0
- package/src/engine/gemma-channel-filter.ts +223 -0
- package/src/engine/harmony-response.ts +165 -0
- package/src/engine/instrumented-tui.ts +192 -0
- package/src/engine/loop-guard.ts +1127 -0
- package/src/engine/messages.ts +14 -0
- package/src/engine/models.ts +42 -0
- package/src/engine/oauth.ts +196 -0
- package/src/engine/pi-mono-names.ts +13 -0
- package/src/engine/prompt-templates.ts +1 -0
- package/src/engine/provider-payload.ts +272 -0
- package/src/engine/select-list.ts +53 -0
- package/src/engine/session.ts +834 -0
- package/src/engine/strip-tokenizer-sentinels.ts +125 -0
- package/src/engine/truncate.ts +9 -0
- package/src/engine/tui.ts +111 -0
- package/src/engine/types.ts +63 -0
- package/src/engine/worker-events.ts +100 -0
- package/src/engine/worker-runtime-capabilities.ts +5 -0
- package/src/engine/worker-runtime.ts +1022 -0
- package/src/engine/worker-tools.ts +208 -0
- package/src/entry/boot-options.ts +50 -0
- package/src/entry/orchestrator.ts +2011 -0
- package/src/interactive/application-controller.ts +433 -0
- package/src/interactive/bus-notices.ts +265 -0
- package/src/interactive/chat-loop-messages.ts +626 -0
- package/src/interactive/chat-loop-policy.ts +18 -0
- package/src/interactive/chat-loop.ts +1090 -0
- package/src/interactive/chat-panel.ts +1820 -0
- package/src/interactive/chat-renderer.ts +1228 -0
- package/src/interactive/clio-editor.ts +161 -0
- package/src/interactive/command-fallbacks.ts +23 -0
- package/src/interactive/command-output.ts +85 -0
- package/src/interactive/context-activity.ts +203 -0
- package/src/interactive/context-meter.ts +223 -0
- package/src/interactive/context-overlay.ts +246 -0
- package/src/interactive/cost-overlay.ts +279 -0
- package/src/interactive/dispatch-board.ts +1319 -0
- package/src/interactive/dispatch-steering.ts +86 -0
- package/src/interactive/editor-bash.ts +46 -0
- package/src/interactive/editor-steer.ts +51 -0
- package/src/interactive/editor-submit.ts +507 -0
- package/src/interactive/export-html/ansi-to-html.ts +161 -0
- package/src/interactive/export-html/index.ts +51 -0
- package/src/interactive/export-html/template.ts +45 -0
- package/src/interactive/export-html/tool-renderer.ts +54 -0
- package/src/interactive/external-editor.ts +67 -0
- package/src/interactive/follow-up-queue-panel.ts +75 -0
- package/src/interactive/footer/dashboard.ts +538 -0
- package/src/interactive/footer/notifications.ts +275 -0
- package/src/interactive/footer/widgets.ts +1264 -0
- package/src/interactive/footer-panel.ts +187 -0
- package/src/interactive/format-time.ts +97 -0
- package/src/interactive/index.ts +11 -0
- package/src/interactive/interactive-application.ts +939 -0
- package/src/interactive/interactive-event-projection.ts +385 -0
- package/src/interactive/interactive-input-runtime.ts +307 -0
- package/src/interactive/interactive-presentation.ts +584 -0
- package/src/interactive/interactive-shell.ts +388 -0
- package/src/interactive/interactive-slash-runtime.ts +554 -0
- package/src/interactive/interactive-subscriptions.ts +111 -0
- package/src/interactive/interactive-tickers.ts +183 -0
- package/src/interactive/keybinding-manager.ts +496 -0
- package/src/interactive/layout.ts +57 -0
- package/src/interactive/leader-key.ts +143 -0
- package/src/interactive/loop-guard-interrupt.ts +101 -0
- package/src/interactive/memory-overlay.ts +280 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +110 -0
- package/src/interactive/overlay-auth-lifecycle.ts +308 -0
- package/src/interactive/overlay-frame.ts +502 -0
- package/src/interactive/overlay-general-openers.ts +274 -0
- package/src/interactive/overlay-key-routing.ts +216 -0
- package/src/interactive/overlay-lifecycle.ts +426 -0
- package/src/interactive/overlay-model-selectors.ts +127 -0
- package/src/interactive/overlay-permission-lifecycle.ts +345 -0
- package/src/interactive/overlay-resource-openers.ts +112 -0
- package/src/interactive/overlay-session-lifecycle.ts +327 -0
- package/src/interactive/overlay-transitions.ts +57 -0
- package/src/interactive/overlays/agents.ts +69 -0
- package/src/interactive/overlays/ask-user.ts +921 -0
- package/src/interactive/overlays/auth-dialog.ts +221 -0
- package/src/interactive/overlays/context-reset.ts +108 -0
- package/src/interactive/overlays/cwd-fallback.ts +141 -0
- package/src/interactive/overlays/decisions.ts +300 -0
- package/src/interactive/overlays/extensions.ts +54 -0
- package/src/interactive/overlays/help-reference.ts +145 -0
- package/src/interactive/overlays/interop.ts +139 -0
- package/src/interactive/overlays/keybinding-detail.ts +90 -0
- package/src/interactive/overlays/list-overlay.ts +606 -0
- package/src/interactive/overlays/message-picker.ts +155 -0
- package/src/interactive/overlays/model-selector.ts +1109 -0
- package/src/interactive/overlays/prompts.ts +72 -0
- package/src/interactive/overlays/session-selector-search.ts +31 -0
- package/src/interactive/overlays/session-selector.ts +284 -0
- package/src/interactive/overlays/settings.ts +3451 -0
- package/src/interactive/overlays/skills-hub.ts +237 -0
- package/src/interactive/overlays/tree-selector.ts +422 -0
- package/src/interactive/permission-overlay.ts +170 -0
- package/src/interactive/render-trace.ts +534 -0
- package/src/interactive/renderers/branch-summary.ts +86 -0
- package/src/interactive/renderers/code-ink.ts +420 -0
- package/src/interactive/renderers/compaction-summary.ts +112 -0
- package/src/interactive/renderers/diff.ts +137 -0
- package/src/interactive/renderers/mermaid.ts +53 -0
- package/src/interactive/renderers/notice.ts +28 -0
- package/src/interactive/renderers/retry-status.ts +31 -0
- package/src/interactive/renderers/structured.ts +89 -0
- package/src/interactive/renderers/tool-execution.ts +1357 -0
- package/src/interactive/renderers/worker-entry.ts +280 -0
- package/src/interactive/session-last-turn.ts +186 -0
- package/src/interactive/session-switch-settlement.ts +10 -0
- package/src/interactive/session-transcript.ts +105 -0
- package/src/interactive/session-usage-reseed.ts +74 -0
- package/src/interactive/slash-autocomplete.ts +256 -0
- package/src/interactive/slash-commands.ts +1407 -0
- package/src/interactive/slash-spec.ts +424 -0
- package/src/interactive/status/controller.ts +260 -0
- package/src/interactive/status/index.ts +23 -0
- package/src/interactive/status/state-machine.ts +530 -0
- package/src/interactive/status/summary.ts +170 -0
- package/src/interactive/status/types.ts +121 -0
- package/src/interactive/status/verbs.ts +163 -0
- package/src/interactive/status/watchdog.ts +24 -0
- package/src/interactive/stdout-backpressure.ts +99 -0
- package/src/interactive/stream-pacer.ts +530 -0
- package/src/interactive/stream-pacing-policy.ts +66 -0
- package/src/interactive/tasks-overlay.ts +540 -0
- package/src/interactive/terminal-lease.ts +485 -0
- package/src/interactive/theme/components.ts +55 -0
- package/src/interactive/theme/glyphs.ts +70 -0
- package/src/interactive/theme/index.ts +15 -0
- package/src/interactive/theme/labels.ts +88 -0
- package/src/interactive/theme/rules.ts +138 -0
- package/src/interactive/theme/segments.ts +88 -0
- package/src/interactive/theme/tokens.ts +121 -0
- package/src/interactive/thinking-level-policy.ts +6 -0
- package/src/interactive/tool-prose-loop.ts +122 -0
- package/src/interactive/tool-prose-registration.ts +48 -0
- package/src/interactive/turn-context.ts +855 -0
- package/src/interactive/turn-middleware.ts +283 -0
- package/src/interactive/turn-persistence.ts +458 -0
- package/src/interactive/turn-queues.ts +181 -0
- package/src/interactive/turn-recovery.ts +323 -0
- package/src/interactive/turn-runtime.ts +869 -0
- package/src/interactive/turn-state.ts +133 -0
- package/src/interactive/view/artifacts.ts +1452 -0
- package/src/interactive/view/view-overlay.ts +735 -0
- package/src/interactive/welcome-dashboard.ts +537 -0
- package/src/interactive/worker-receipts.ts +143 -0
- package/src/interactive/worker-replay.ts +111 -0
- package/src/interactive/worker-share.ts +100 -0
- package/src/interactive/worker-stream.ts +446 -0
- package/src/interactive/workspace-facts.ts +141 -0
- package/src/tools/agent-tools.ts +402 -0
- package/src/tools/artifact.ts +103 -0
- package/src/tools/ask-user.ts +666 -0
- package/src/tools/bash.ts +204 -0
- package/src/tools/bootstrap.ts +77 -0
- package/src/tools/builtin-tool-catalog.ts +265 -0
- package/src/tools/codewiki/code-nav-surface.ts +29 -0
- package/src/tools/codewiki/code-nav.ts +549 -0
- package/src/tools/codewiki/shared.ts +118 -0
- package/src/tools/compete-worktrees.ts +898 -0
- package/src/tools/context/docs-engine.ts +630 -0
- package/src/tools/context/index.ts +514 -0
- package/src/tools/context/surface.ts +19 -0
- package/src/tools/core-bootstrap.ts +168 -0
- package/src/tools/credential-present.ts +151 -0
- package/src/tools/dispatch-admission.ts +533 -0
- package/src/tools/dispatch-arguments.ts +228 -0
- package/src/tools/dispatch-background.ts +54 -0
- package/src/tools/dispatch-event-text.ts +6 -0
- package/src/tools/dispatch-plan.ts +746 -0
- package/src/tools/dispatch-run-events.ts +238 -0
- package/src/tools/dispatch-runner.ts +2370 -0
- package/src/tools/dispatch-scout-admission.ts +295 -0
- package/src/tools/dispatch-scout.ts +461 -0
- package/src/tools/dispatch-types.ts +77 -0
- package/src/tools/dispatch.ts +244 -0
- package/src/tools/edit-diff.ts +534 -0
- package/src/tools/edit.ts +128 -0
- package/src/tools/executables.ts +22 -0
- package/src/tools/file-mutation-queue.ts +35 -0
- package/src/tools/find.ts +294 -0
- package/src/tools/grep.ts +495 -0
- package/src/tools/ignore-policy.ts +169 -0
- package/src/tools/lazy-tool.ts +60 -0
- package/src/tools/ledger.ts +206 -0
- package/src/tools/ls.ts +110 -0
- package/src/tools/monitor-surface.ts +36 -0
- package/src/tools/monitor.ts +767 -0
- package/src/tools/network-policy.ts +29 -0
- package/src/tools/observation.ts +421 -0
- package/src/tools/observers.ts +99 -0
- package/src/tools/path-utils.ts +79 -0
- package/src/tools/policy.ts +154 -0
- package/src/tools/profiles.ts +108 -0
- package/src/tools/read.ts +173 -0
- package/src/tools/registry.ts +1169 -0
- package/src/tools/result-shaping.ts +204 -0
- package/src/tools/safe-exec.ts +128 -0
- package/src/tools/spawn-hygiene.ts +182 -0
- package/src/tools/steer-surface.ts +17 -0
- package/src/tools/steer.ts +77 -0
- package/src/tools/tasks.ts +265 -0
- package/src/tools/truncate-utf8.ts +17 -0
- package/src/tools/truncate.ts +43 -0
- package/src/tools/verify/frontend.ts +687 -0
- package/src/tools/verify/index.ts +31 -0
- package/src/tools/verify/scripts.ts +108 -0
- package/src/tools/verify/surface.ts +46 -0
- package/src/tools/web-fetch-surface.ts +23 -0
- package/src/tools/web-fetch.ts +759 -0
- package/src/tools/worker-evidence.ts +215 -0
- package/src/tools/write.ts +57 -0
- package/src/utils/exif-orientation.ts +192 -0
- package/src/utils/git.ts +24 -0
- package/src/utils/image-resize.ts +148 -0
- package/src/utils/photon.ts +103 -0
- package/src/worker/control-lane.ts +22 -0
- package/src/worker/entry.ts +294 -0
- package/src/worker/event-projection.ts +56 -0
- package/src/worker/heartbeat.ts +24 -0
- package/src/worker/ledger-mirror.ts +95 -0
- package/src/worker/ndjson.ts +40 -0
- package/src/worker/protocol.ts +529 -0
- package/src/worker/resource-facts.ts +79 -0
- package/src/worker/runtime-registry.ts +12 -0
- package/src/worker/spec-contract.ts +675 -0
- package/src/worker/stdin-demux.ts +316 -0
- package/dist/acp-UMLFVA3F.js +0 -92
- package/dist/chunk-262G75JS.js +0 -35
- package/dist/chunk-6CWDTGUC.js +0 -20
- package/dist/chunk-BYEU76JP.js +0 -899
- package/dist/chunk-GNGDQYDU.js +0 -34688
- package/dist/chunk-GOTUR54M.js +0 -9
- package/dist/chunk-NI4DEJMC.js +0 -88
- package/dist/chunk-TPOCL34A.js +0 -2876
- package/dist/chunk-UU3R62TT.js +0 -128
- package/dist/clio-KIQ5SNDS.js +0 -53
- package/dist/configure-G4E3A2PG.js +0 -27
- package/dist/doctor-M5HJJZOL.js +0 -61
- package/dist/domains/agents/builtins/architect.md +0 -33
- package/dist/domains/agents/builtins/coder.md +0 -31
- package/dist/domains/agents/builtins/context-bootstrap.md +0 -38
- package/dist/domains/agents/builtins/debugger.md +0 -30
- package/dist/domains/agents/builtins/documenter.md +0 -31
- package/dist/domains/agents/builtins/git-master.md +0 -30
- package/dist/domains/agents/builtins/provenance.md +0 -30
- package/dist/domains/agents/builtins/researcher.md +0 -71
- package/dist/domains/agents/builtins/scout.md +0 -42
- package/dist/domains/agents/builtins/tester.md +0 -31
- package/dist/domains/agents/builtins/verifier.md +0 -30
- package/dist/domains/agents/builtins/wiki-writer.md +0 -41
- package/dist/fleet-preflight-CLIAX7YR.js +0 -21
- package/dist/preload-KZVHET2B.js +0 -11
- package/dist/version-ROZ6CZKH.js +0 -16
- package/docs/html/agents_blueprint.html +0 -936
- package/docs/html/alcf_blueprint.html +0 -324
- package/docs/html/architecture_blueprint.html +0 -850
- package/docs/html/commands_blueprint.html +0 -794
- package/docs/html/config_knobs_audit_blueprint.html +0 -178
- package/docs/html/configuration_blueprint.html +0 -1080
- package/docs/html/context_blueprint.html +0 -603
- package/docs/html/documentation_blueprint.html +0 -832
- package/docs/html/environment_blueprint.html +0 -404
- package/docs/html/eval_blueprint.html +0 -743
- package/docs/html/evals_internal_blueprint.html +0 -190
- package/docs/html/evolution_blueprint.html +0 -674
- package/docs/html/extensions_blueprint.html +0 -2065
- package/docs/html/fleet_dispatch_blueprint.html +0 -286
- package/docs/html/index.html +0 -919
- package/docs/html/lifecycle_blueprint.html +0 -723
- package/docs/html/memory_blueprint.html +0 -699
- package/docs/html/middleware_blueprint.html +0 -664
- package/docs/html/models_blueprint.html +0 -2366
- package/docs/html/observability_blueprint.html +0 -683
- package/docs/html/provider_adapter_blueprint.html +0 -245
- package/docs/html/safety_blueprint.html +0 -1386
- package/docs/html/shared.css +0 -571
- package/docs/html/shared.js +0 -143
- package/docs/html/skills_blueprint.html +0 -671
- package/docs/html/soak_blueprint.html +0 -182
- package/docs/html/tool_usage_blueprint.html +0 -350
- package/docs/html/tools_blueprint.html +0 -2249
- package/docs/html/trace_blueprint.html +0 -235
- package/docs/html/tui_design_blueprint.html +0 -314
- package/docs/html/validation_blueprint.html +0 -961
- package/docs/html/worker_dispatch_blueprint.html +0 -231
- package/skills/git/commit-crafting/SKILL.md +0 -79
- package/skills/git/commit-crafting/evals.md +0 -92
- package/skills/git/create-pr/SKILL.md +0 -116
- package/skills/git/create-pr/evals.md +0 -114
- package/skills/git/investigate-issue/SKILL.md +0 -139
- package/skills/git/investigate-issue/evals.md +0 -94
- package/skills/git/review-changes/SKILL.md +0 -103
- package/skills/git/review-changes/evals.md +0 -85
|
@@ -0,0 +1,2011 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { modelBootstrapGenerate, resolveBootstrapRoute } from "../cli/bootstrap-generate.js";
|
|
4
|
+
import { runHeadlessMainAgent } from "../cli/modes/print.js";
|
|
5
|
+
import { formatBootTrace } from "../core/boot-trace.js";
|
|
6
|
+
import { BusChannels } from "../core/bus-events.js";
|
|
7
|
+
import { installBusTracer } from "../core/bus-trace.js";
|
|
8
|
+
import { type ClioSettings, readSettings, type SettingsMutator, updateSettings } from "../core/config.js";
|
|
9
|
+
import { DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS } from "../core/defaults.js";
|
|
10
|
+
import { loadDomains } from "../core/domain-loader.js";
|
|
11
|
+
import { expandInlineFileReferencesAsync } from "../core/file-references.js";
|
|
12
|
+
import { setGitCommitAttributionEnabled } from "../core/git-commit-attribution.js";
|
|
13
|
+
import { configureGuardrails } from "../core/guardrails.js";
|
|
14
|
+
import { HEADLESS_PERMISSION_DENIED_REASON } from "../core/headless-permission.js";
|
|
15
|
+
import { rememberRecentModel } from "../core/recent-models.js";
|
|
16
|
+
import { protectedResidencyModels } from "../core/residency-protection.js";
|
|
17
|
+
import {
|
|
18
|
+
applyOverrides,
|
|
19
|
+
applyRoutingPatch,
|
|
20
|
+
applySessionRouting,
|
|
21
|
+
diffRouting,
|
|
22
|
+
getAtPath,
|
|
23
|
+
isRoutingPath,
|
|
24
|
+
mergeRoutingPatchIntoSettings,
|
|
25
|
+
type RoutingPatch,
|
|
26
|
+
restoreRoutingFields,
|
|
27
|
+
routingChangeNotices,
|
|
28
|
+
routingPatchForId,
|
|
29
|
+
type SessionOverrides,
|
|
30
|
+
seedSessionRouting,
|
|
31
|
+
setAtPath,
|
|
32
|
+
} from "../core/session-routing.js";
|
|
33
|
+
import { getSharedBus } from "../core/shared-bus.js";
|
|
34
|
+
import { isSkillActivation } from "../core/skill-activation.js";
|
|
35
|
+
import { StartupTimer } from "../core/startup-timer.js";
|
|
36
|
+
import { getTerminationCoordinator } from "../core/termination.js";
|
|
37
|
+
import { clioDataDir, clioStateDir } from "../core/xdg.js";
|
|
38
|
+
import { renderAgentCatalogSectionsFromSpecs } from "../domains/agents/catalog.js";
|
|
39
|
+
import type { AgentsContract } from "../domains/agents/contract.js";
|
|
40
|
+
import { AgentsDomainModule } from "../domains/agents/index.js";
|
|
41
|
+
import type { ConfigContract } from "../domains/config/contract.js";
|
|
42
|
+
import { ConfigDomainModule, createConfigDomainModule } from "../domains/config/index.js";
|
|
43
|
+
import type { ContextContract } from "../domains/context/contract.js";
|
|
44
|
+
import { bootstrapInputFromInitOptions } from "../domains/context/init-options.js";
|
|
45
|
+
import { createContextDomainModule } from "../domains/context/runtime.js";
|
|
46
|
+
import type { DispatchContract } from "../domains/dispatch/contract.js";
|
|
47
|
+
import { createDispatchDedupRegistration } from "../domains/dispatch/dedup.js";
|
|
48
|
+
import { agentRoleFactsResolver } from "../domains/dispatch/execution-role.js";
|
|
49
|
+
import { readGateDecisionArtifacts, readPendingGateDecisions } from "../domains/dispatch/gate-decisions.js";
|
|
50
|
+
import { createDispatchDomainModule } from "../domains/dispatch/index.js";
|
|
51
|
+
import { type ExtensionsContract, ExtensionsDomainModule } from "../domains/extensions/index.js";
|
|
52
|
+
import { type InteropContract, InteropDomainModule } from "../domains/interop/index.js";
|
|
53
|
+
import {
|
|
54
|
+
describeUpgradeNotice,
|
|
55
|
+
ensureClioState,
|
|
56
|
+
LifecycleDomainModule,
|
|
57
|
+
takeUpgradeNotice,
|
|
58
|
+
} from "../domains/lifecycle/index.js";
|
|
59
|
+
import { getVersionInfo } from "../domains/lifecycle/version.js";
|
|
60
|
+
import {
|
|
61
|
+
buildMemoryPromptSection,
|
|
62
|
+
canonicalMemoryRepositoryIdentity,
|
|
63
|
+
createTaskMemoryTelemetrySink,
|
|
64
|
+
createTaskMemoryTrace,
|
|
65
|
+
loadMemoryRecordsSync,
|
|
66
|
+
renderTaskMemoryHandoffSource,
|
|
67
|
+
seedTaskMemoryFromNewestHandoff,
|
|
68
|
+
type TaskMemoryModelClient,
|
|
69
|
+
taskMemoryBankSize,
|
|
70
|
+
taskMemoryHandoffSeedOffer,
|
|
71
|
+
taskMemoryTracePath,
|
|
72
|
+
} from "../domains/memory/index.js";
|
|
73
|
+
import { TaskMemoryBank } from "../domains/memory/task-bank.js";
|
|
74
|
+
import {
|
|
75
|
+
createDetachedDispatchNudgeRegistration,
|
|
76
|
+
createReadOnlyExplorationNudgeRegistration,
|
|
77
|
+
createUnbackedWorkerClaimRegistration,
|
|
78
|
+
openDetachedBatchViews,
|
|
79
|
+
} from "../domains/middleware/dispatch-nudge.js";
|
|
80
|
+
import {
|
|
81
|
+
createHookReceiptLog,
|
|
82
|
+
createMiddlewareToolChoiceControl,
|
|
83
|
+
createSkillsReminderRegistration,
|
|
84
|
+
type ExtensionHookRoot,
|
|
85
|
+
installUserHooks,
|
|
86
|
+
type MiddlewareContract,
|
|
87
|
+
MiddlewareDomainModule,
|
|
88
|
+
writeMiddlewareDiagnosticToStderr,
|
|
89
|
+
} from "../domains/middleware/index.js";
|
|
90
|
+
import { createMemoryInterventionRegistration } from "../domains/middleware/memory-intervention.js";
|
|
91
|
+
import { createTaskBoardReminderRegistration } from "../domains/middleware/task-board-reminder.js";
|
|
92
|
+
import { createTaskNudgeRegistration } from "../domains/middleware/task-nudge.js";
|
|
93
|
+
import type { ObservabilityContract } from "../domains/observability/index.js";
|
|
94
|
+
import { ObservabilityDomainModule } from "../domains/observability/index.js";
|
|
95
|
+
import type { PromptsContract } from "../domains/prompts/contract.js";
|
|
96
|
+
import { createPromptsDomainModule } from "../domains/prompts/index.js";
|
|
97
|
+
import type { ProvidersContract, TargetDescriptor, ThinkingLevel } from "../domains/providers/index.js";
|
|
98
|
+
import {
|
|
99
|
+
AGENT_ROLE_TOOLS_REQUIRED_REASON,
|
|
100
|
+
applyModelCapabilityPatch,
|
|
101
|
+
firstRuntimeResolutionError,
|
|
102
|
+
isOrchestratorEligibleRuntime,
|
|
103
|
+
ProvidersDomainModule,
|
|
104
|
+
refineRuntimeTargetWithModelHints,
|
|
105
|
+
resolveModelCapabilities,
|
|
106
|
+
resolveModelRuntimeCapabilitiesForProviders,
|
|
107
|
+
resolveRuntimeTarget,
|
|
108
|
+
supportsAgentRoleTools,
|
|
109
|
+
targetRequiresAuth,
|
|
110
|
+
VALID_THINKING_LEVELS,
|
|
111
|
+
} from "../domains/providers/index.js";
|
|
112
|
+
import { getRuntimeRegistry } from "../domains/providers/registry.js";
|
|
113
|
+
import { registerBuiltinRuntimes } from "../domains/providers/runtimes/builtins.js";
|
|
114
|
+
import {
|
|
115
|
+
createResourcesDomainModule,
|
|
116
|
+
discoverMarketplaceSkills,
|
|
117
|
+
modelVisibleSkills,
|
|
118
|
+
type ResourcesContract,
|
|
119
|
+
} from "../domains/resources/index.js";
|
|
120
|
+
import { DEFAULT_RECENT_ENTRY_LIMIT } from "../domains/safety/finish-contract.js";
|
|
121
|
+
import { createFinishContractRegistration } from "../domains/safety/finish-contract-registration.js";
|
|
122
|
+
import type { AutonomyLevel, SafetyContract } from "../domains/safety/index.js";
|
|
123
|
+
import { parseRigorOverride, resolveRigor, SafetyDomainModule } from "../domains/safety/index.js";
|
|
124
|
+
import type { ProtectedArtifactState } from "../domains/safety/protected-artifacts.js";
|
|
125
|
+
import {
|
|
126
|
+
createProtectedArtifactsRegistration,
|
|
127
|
+
type ProtectedArtifactProtectEvent,
|
|
128
|
+
} from "../domains/safety/protected-artifacts-registration.js";
|
|
129
|
+
import type { SchedulingContract } from "../domains/scheduling/contract.js";
|
|
130
|
+
import { SchedulingDomainModule } from "../domains/scheduling/index.js";
|
|
131
|
+
import { type CompactResult, compact } from "../domains/session/compaction/compact.js";
|
|
132
|
+
import { collectSessionEntries } from "../domains/session/compaction/session-entries.js";
|
|
133
|
+
import { estimateTokens } from "../domains/session/compaction/tokens.js";
|
|
134
|
+
import { ceilChars } from "../domains/session/context-accounting.js";
|
|
135
|
+
import type { SessionContract, SessionMeta } from "../domains/session/contract.js";
|
|
136
|
+
import { createDecisionBoardStore } from "../domains/session/decision-board.js";
|
|
137
|
+
import type { CompactionSummaryEntry, CompactionTrigger, SessionEntry } from "../domains/session/entries.js";
|
|
138
|
+
import { SessionDomainModule } from "../domains/session/index.js";
|
|
139
|
+
import {
|
|
140
|
+
clearPendingProtectedArtifact,
|
|
141
|
+
reconcilePendingProtectedArtifacts,
|
|
142
|
+
stagePendingProtectedArtifact,
|
|
143
|
+
} from "../domains/session/protected-artifact-journal.js";
|
|
144
|
+
import {
|
|
145
|
+
protectedArtifactEntryFromArtifact,
|
|
146
|
+
protectedArtifactStateFromSessionEntries,
|
|
147
|
+
} from "../domains/session/protected-artifacts.js";
|
|
148
|
+
import { createTaskBoardStore } from "../domains/session/task-board.js";
|
|
149
|
+
import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
|
|
150
|
+
import { type ShareContract, ShareDomainModule } from "../domains/share/index.js";
|
|
151
|
+
import { createUserTasksStore } from "../domains/user-tasks/store.js";
|
|
152
|
+
import { type AcpSafeSettingsPatch, type AcpSafeSettingsSnapshot, serveClioAcpAgent } from "../engine/acp/server.js";
|
|
153
|
+
import { createStdioServerTransport } from "../engine/acp/transport.js";
|
|
154
|
+
import { completeEngineText } from "../engine/ai.js";
|
|
155
|
+
import { setProtectedModelsProvider } from "../engine/apis/residency.js";
|
|
156
|
+
import {
|
|
157
|
+
createLoopGuardRegistration,
|
|
158
|
+
INTERACTIVE_LOOP_BLOCK_BUDGET,
|
|
159
|
+
readOrchTurnToolCallBudget,
|
|
160
|
+
} from "../engine/loop-guard.js";
|
|
161
|
+
import { openSession, readSessionTailTurns, sessionCurrentPath, sessionPaths } from "../engine/session.js";
|
|
162
|
+
import type { EngineModel } from "../engine/types.js";
|
|
163
|
+
import { createChatLoop } from "../interactive/chat-loop.js";
|
|
164
|
+
import { buildReplayAgentMessagesFromTurns } from "../interactive/chat-renderer.js";
|
|
165
|
+
import { type RunIo, startInteractive } from "../interactive/index.js";
|
|
166
|
+
import type { BootOptions } from "./boot-options.js";
|
|
167
|
+
|
|
168
|
+
export type { BootOptions, HeadlessSamplingOverrides } from "./boot-options.js";
|
|
169
|
+
|
|
170
|
+
import {
|
|
171
|
+
detectPlatformKeybindingWarnings,
|
|
172
|
+
detectTerminalKeySupport,
|
|
173
|
+
formatInvalidKeybindingNotice,
|
|
174
|
+
formatPlatformKeybindingNotice,
|
|
175
|
+
validateKeybindings,
|
|
176
|
+
} from "../interactive/keybinding-manager.js";
|
|
177
|
+
import { subscribeLoopGuardStop } from "../interactive/loop-guard-interrupt.js";
|
|
178
|
+
import { BUILTIN_SLASH_COMMANDS } from "../interactive/slash-commands.js";
|
|
179
|
+
import { createToolProseRegistration } from "../interactive/tool-prose-registration.js";
|
|
180
|
+
import { type AskUserHandler, cancelledAskUserResult } from "../tools/ask-user.js";
|
|
181
|
+
import { registerAllTools } from "../tools/bootstrap.js";
|
|
182
|
+
import { isGitRepository, recoverCleanupReadyCompeteGroups } from "../tools/compete-worktrees.js";
|
|
183
|
+
import { createDispatchBackgroundRegistry } from "../tools/dispatch-background.js";
|
|
184
|
+
import { createFileMutationObserver, createSkillActivationObserver } from "../tools/observers.js";
|
|
185
|
+
import { createRegistry } from "../tools/registry.js";
|
|
186
|
+
|
|
187
|
+
export interface BootResult {
|
|
188
|
+
exitCode: number;
|
|
189
|
+
bootTimeMs: number;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* The bannered boot is the whole of what a piped or CI invocation of bare
|
|
194
|
+
* `clio` shows, so it is the entire first impression for a stranger who is not
|
|
195
|
+
* on a TTY. It used to end in a hardcoded `ready`, a word with no relationship
|
|
196
|
+
* to anything: a machine with no target configured at all printed it and
|
|
197
|
+
* exited 0, which is the one state where the installation can do nothing.
|
|
198
|
+
*
|
|
199
|
+
* What the line reports now is the orchestrator target the settings actually
|
|
200
|
+
* declare, and when none is declared it says so and names the command that
|
|
201
|
+
* fixes it. The exit status stays 0 either way, because this path answers
|
|
202
|
+
* "did Clio boot", which it did, and CI scripts already depend on that
|
|
203
|
+
* answer; the readiness of the configuration is reported in words instead.
|
|
204
|
+
*/
|
|
205
|
+
function bannerConfigurationLine(): string {
|
|
206
|
+
let settings: ClioSettings;
|
|
207
|
+
try {
|
|
208
|
+
settings = readSettings();
|
|
209
|
+
} catch {
|
|
210
|
+
return chalk.yellow("settings.yaml is not valid. Run `clio-coder doctor` for the exact keys.");
|
|
211
|
+
}
|
|
212
|
+
// A dangling chat target normalizes to null in the schema, so a deleted
|
|
213
|
+
// target arrives here as no target at all rather than as a name to report.
|
|
214
|
+
const targetId = settings.orchestrator?.target;
|
|
215
|
+
if (!targetId) {
|
|
216
|
+
return chalk.yellow("no model target configured. Run `clio-coder configure` to add one.");
|
|
217
|
+
}
|
|
218
|
+
const model = settings.orchestrator?.model;
|
|
219
|
+
return chalk.dim(`target ${targetId}${model ? ` · model ${model}` : " · no default model"}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function buildBanner(): string {
|
|
223
|
+
const { clio } = getVersionInfo();
|
|
224
|
+
return `
|
|
225
|
+
${chalk.cyan("Clio Coder")}
|
|
226
|
+
${chalk.dim(`v${clio} · CLIO: Context Layer for I/O · HPC & scientific software`)}
|
|
227
|
+
${bannerConfigurationLine()}
|
|
228
|
+
`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function printJsonSessionHeader(meta: SessionMeta | null): Record<string, unknown> | null {
|
|
232
|
+
if (!meta) return null;
|
|
233
|
+
return {
|
|
234
|
+
type: "session",
|
|
235
|
+
version: meta.sessionFormatVersion ?? 1,
|
|
236
|
+
id: meta.id,
|
|
237
|
+
timestamp: meta.createdAt,
|
|
238
|
+
cwd: meta.cwd,
|
|
239
|
+
target: meta.target,
|
|
240
|
+
model: meta.model,
|
|
241
|
+
clioVersion: meta.clioVersion,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function applyHeadlessSettingsOverlay(
|
|
246
|
+
settings: ClioSettings,
|
|
247
|
+
overrides: BootOptions["headless"] | undefined,
|
|
248
|
+
): ClioSettings {
|
|
249
|
+
const next = structuredClone(settings);
|
|
250
|
+
if (!overrides) return next;
|
|
251
|
+
const previousTarget = next.orchestrator.target;
|
|
252
|
+
if (overrides.target !== undefined) {
|
|
253
|
+
next.orchestrator.target = overrides.target;
|
|
254
|
+
if (overrides.model === undefined && (previousTarget !== overrides.target || !next.orchestrator.model)) {
|
|
255
|
+
const target = next.targets.find((entry) => entry.id === overrides.target);
|
|
256
|
+
if (target) next.orchestrator.model = target.defaultModel ?? null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (overrides.model !== undefined) next.orchestrator.model = overrides.model;
|
|
260
|
+
if (overrides.thinking !== undefined) next.orchestrator.thinkingLevel = overrides.thinking;
|
|
261
|
+
if (overrides.autonomy !== undefined) next.autonomy = overrides.autonomy;
|
|
262
|
+
return next;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
interface CompactionResolution {
|
|
266
|
+
model: EngineModel;
|
|
267
|
+
targetId: string;
|
|
268
|
+
apiKey?: string;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function resolveTarget(providers: ProvidersContract, targetId: string | null | undefined): TargetDescriptor | null {
|
|
272
|
+
if (!targetId) return null;
|
|
273
|
+
return providers.getTarget(targetId);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function advanceThinkingLevel(current: ThinkingLevel, available: ReadonlyArray<ThinkingLevel>): ThinkingLevel {
|
|
277
|
+
const levels = available.length > 0 ? available : VALID_THINKING_LEVELS;
|
|
278
|
+
if (!levels.includes(current)) return levels[0] ?? "off";
|
|
279
|
+
const normalized = current;
|
|
280
|
+
const idx = levels.indexOf(normalized);
|
|
281
|
+
return levels[(idx + 1) % levels.length] ?? "off";
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* pi-ai's openai-completions provider refuses to stream without an apiKey even
|
|
286
|
+
* when the target is a local server that ignores the Authorization header
|
|
287
|
+
* entirely. The chat loop, the dispatch workers, and the background memory
|
|
288
|
+
* model all send this placeholder so a local llama.cpp or LM Studio endpoint
|
|
289
|
+
* works without the user inventing a credential.
|
|
290
|
+
*/
|
|
291
|
+
const LOCAL_API_KEY_FALLBACK = "clio-local-target";
|
|
292
|
+
|
|
293
|
+
export async function resolveApiKeyForTarget(
|
|
294
|
+
target: TargetDescriptor,
|
|
295
|
+
providers: ProvidersContract,
|
|
296
|
+
signal?: AbortSignal,
|
|
297
|
+
): Promise<string | undefined> {
|
|
298
|
+
const runtime = providers.getRuntime(target.runtime);
|
|
299
|
+
if (!runtime) return undefined;
|
|
300
|
+
// A target that needs no credential still needs the placeholder. Returning
|
|
301
|
+
// nothing here left compaction as the one path that did not send it, so
|
|
302
|
+
// `/context compact` and every automatic compaction at the window threshold
|
|
303
|
+
// failed on exactly the local runtimes Clio is built for, while ordinary
|
|
304
|
+
// turns against the same target succeeded.
|
|
305
|
+
if (!targetRequiresAuth(target, runtime)) return LOCAL_API_KEY_FALLBACK;
|
|
306
|
+
const resolved = await providers.auth.resolveForTarget(target, runtime, signal ? { signal } : undefined);
|
|
307
|
+
return resolved.apiKey;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function createBackgroundMemoryModelClient(
|
|
311
|
+
providers: ProvidersContract,
|
|
312
|
+
settings: Readonly<ClioSettings>,
|
|
313
|
+
timeoutMs: number,
|
|
314
|
+
): TaskMemoryModelClient | null {
|
|
315
|
+
const targetId = settings.background.target?.trim();
|
|
316
|
+
const wireModelId = settings.background.model?.trim();
|
|
317
|
+
if (!targetId || !wireModelId) return null;
|
|
318
|
+
// One model cannot both drive the action agent and think its way through a
|
|
319
|
+
// memory step: the memory call would contend with the agent's own decoding on
|
|
320
|
+
// the same server, and a reasoning model spends most of a memory step
|
|
321
|
+
// deliberating over a task it is not executing. Memory falls back to its free
|
|
322
|
+
// deterministic tier rather than degrading the work the operator asked for.
|
|
323
|
+
if (backgroundSharesReasoningModelWithOrchestrator(providers, settings)) return null;
|
|
324
|
+
const resolved = resolveRuntimeTarget(providers, {
|
|
325
|
+
targetId,
|
|
326
|
+
wireModelId,
|
|
327
|
+
// Memory reads a trajectory and writes a fixed envelope. Reasoning adds
|
|
328
|
+
// latency and, on a small local model, routinely consumes the entire
|
|
329
|
+
// output budget before the envelope is ever written.
|
|
330
|
+
requestedThinkingLevel: "off",
|
|
331
|
+
use: "orchestrator",
|
|
332
|
+
requireTools: false,
|
|
333
|
+
requireOutputBudget: true,
|
|
334
|
+
});
|
|
335
|
+
if (!resolved.ok) {
|
|
336
|
+
const detail = firstRuntimeResolutionError(resolved.diagnostics) ?? "background target resolution failed";
|
|
337
|
+
throw new Error(detail);
|
|
338
|
+
}
|
|
339
|
+
const kbHit = providers.knowledgeBase?.lookup(resolved.target.wireModelId) ?? null;
|
|
340
|
+
const model = resolved.target.runtime.synthesizeModel(resolved.target.target, resolved.target.wireModelId, kbHit);
|
|
341
|
+
const refined = refineRuntimeTargetWithModelHints(resolved.target, model, providers.knowledgeBase);
|
|
342
|
+
applyModelCapabilityPatch(model, refined.capabilities);
|
|
343
|
+
return {
|
|
344
|
+
async complete(request) {
|
|
345
|
+
const apiKey = targetRequiresAuth(refined.target, refined.runtime)
|
|
346
|
+
? (await providers.auth.resolveForTarget(refined.target, refined.runtime, { signal: request.signal })).apiKey
|
|
347
|
+
: LOCAL_API_KEY_FALLBACK;
|
|
348
|
+
return completeEngineText({
|
|
349
|
+
model,
|
|
350
|
+
systemPrompt: request.systemPrompt,
|
|
351
|
+
userPrompt: request.userPrompt,
|
|
352
|
+
maxTokens: request.maxTokens,
|
|
353
|
+
// Always off, never the operator's chat thinking level. A model that
|
|
354
|
+
// reasons anyway still works: `completeEngineText` keeps only text
|
|
355
|
+
// blocks, and the memory output budget leaves room for the preamble.
|
|
356
|
+
thinkingLevel: "off",
|
|
357
|
+
signal: request.signal,
|
|
358
|
+
timeoutMs,
|
|
359
|
+
...(apiKey === undefined ? {} : { apiKey }),
|
|
360
|
+
});
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Warn about a configured agent-role model the provider reports cannot call
|
|
367
|
+
* tools. Selection already refuses these, so reaching here means the config
|
|
368
|
+
* predates the check or was hand-edited; a run would otherwise fail later at
|
|
369
|
+
* dispatch admission with a message that names a missing tool rather than the
|
|
370
|
+
* model that cannot use any.
|
|
371
|
+
*/
|
|
372
|
+
function agentRoleToolWarnings(providers: ProvidersContract, settings: Readonly<ClioSettings>): string[] {
|
|
373
|
+
const roles: ReadonlyArray<{ label: string; target: string | null; model: string | null }> = [
|
|
374
|
+
{ label: "orchestrator", target: settings.orchestrator.target, model: settings.orchestrator.model },
|
|
375
|
+
{ label: "workers.default", target: settings.workers.default.target, model: settings.workers.default.model },
|
|
376
|
+
];
|
|
377
|
+
const warnings: string[] = [];
|
|
378
|
+
for (const role of roles) {
|
|
379
|
+
const targetId = role.target?.trim();
|
|
380
|
+
const wireModelId = role.model?.trim();
|
|
381
|
+
if (!targetId || !wireModelId) continue;
|
|
382
|
+
try {
|
|
383
|
+
const status = providers.list().find((entry) => entry.target.id === targetId);
|
|
384
|
+
if (!status) continue;
|
|
385
|
+
const capabilities = resolveModelCapabilities(status, wireModelId, providers.knowledgeBase);
|
|
386
|
+
if (supportsAgentRoleTools(capabilities)) continue;
|
|
387
|
+
warnings.push(
|
|
388
|
+
`${role.label} model '${wireModelId}' on target '${targetId}' ${AGENT_ROLE_TOOLS_REQUIRED_REASON}. ` +
|
|
389
|
+
`Pick another model, or state the correction in a model-catalog.d entry if the provider's flag is wrong.`,
|
|
390
|
+
);
|
|
391
|
+
} catch {
|
|
392
|
+
// An unresolvable capability is not evidence of a missing one.
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return warnings;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* True when the background role names the same model the orchestrator runs and
|
|
400
|
+
* that model reasons. Distinct models, or a non-reasoning shared model, are both
|
|
401
|
+
* fine; this only catches the one-model-does-everything configuration.
|
|
402
|
+
*/
|
|
403
|
+
function backgroundSharesReasoningModelWithOrchestrator(
|
|
404
|
+
providers: ProvidersContract,
|
|
405
|
+
settings: Readonly<ClioSettings>,
|
|
406
|
+
): boolean {
|
|
407
|
+
const backgroundModel = settings.background.model?.trim();
|
|
408
|
+
const orchestratorModel = settings.orchestrator.model?.trim();
|
|
409
|
+
if (!backgroundModel || backgroundModel !== orchestratorModel) return false;
|
|
410
|
+
if (settings.background.target?.trim() !== settings.orchestrator.target?.trim()) return false;
|
|
411
|
+
try {
|
|
412
|
+
const status = providers.list().find((entry) => entry.target.id === settings.background.target?.trim());
|
|
413
|
+
if (!status) return false;
|
|
414
|
+
return resolveModelCapabilities(status, backgroundModel, providers.knowledgeBase).reasoning === true;
|
|
415
|
+
} catch {
|
|
416
|
+
// An unresolvable capability is not evidence of a reasoning model; the
|
|
417
|
+
// operator's explicit configuration stands.
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export function synthesizeOrchestratorModel(
|
|
423
|
+
providers: ProvidersContract,
|
|
424
|
+
target: TargetDescriptor,
|
|
425
|
+
wireModelId: string,
|
|
426
|
+
): EngineModel | null {
|
|
427
|
+
const runtime = providers.getRuntime(target.runtime);
|
|
428
|
+
if (!runtime) return null;
|
|
429
|
+
let model: EngineModel;
|
|
430
|
+
try {
|
|
431
|
+
const kbHit = providers.knowledgeBase?.lookup(wireModelId) ?? null;
|
|
432
|
+
model = runtime.synthesizeModel(target, wireModelId, kbHit);
|
|
433
|
+
} catch {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
try {
|
|
437
|
+
const status = providers.list().find((entry) => entry.target.id === target.id);
|
|
438
|
+
if (status) {
|
|
439
|
+
const caps = resolveModelCapabilities(status, wireModelId, providers.knowledgeBase, {
|
|
440
|
+
detectedReasoning: providers.getDetectedReasoning(target.id, wireModelId),
|
|
441
|
+
});
|
|
442
|
+
applyModelCapabilityPatch(model, caps);
|
|
443
|
+
}
|
|
444
|
+
} catch {
|
|
445
|
+
// Older test doubles and degraded provider bundles may not expose live
|
|
446
|
+
// status. The synthesized model still carries runtime and catalog caps.
|
|
447
|
+
}
|
|
448
|
+
return model;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async function resolveCompactionModel(
|
|
452
|
+
settings: ClioSettings,
|
|
453
|
+
providers: ProvidersContract,
|
|
454
|
+
): Promise<CompactionResolution | null> {
|
|
455
|
+
const targetId = settings.orchestrator?.target ?? null;
|
|
456
|
+
const wireModelId = settings.orchestrator?.model ?? null;
|
|
457
|
+
if (!targetId || !wireModelId) return null;
|
|
458
|
+
const target = resolveTarget(providers, targetId);
|
|
459
|
+
if (!target) return null;
|
|
460
|
+
const model = synthesizeOrchestratorModel(providers, target, wireModelId);
|
|
461
|
+
if (!model) return null;
|
|
462
|
+
const apiKey = await resolveApiKeyForTarget(target, providers);
|
|
463
|
+
const resolution: CompactionResolution = { model, targetId };
|
|
464
|
+
if (apiKey !== undefined) resolution.apiKey = apiKey;
|
|
465
|
+
return resolution;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function readSessionEntriesForCompact(sessionId: string): SessionEntry[] {
|
|
469
|
+
const reader = openSession(sessionId);
|
|
470
|
+
return collectSessionEntries(reader.turns(), sessionPaths(reader.meta()).current);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* The finish-contract only inspects the window since the last user message
|
|
475
|
+
* (`recentEntries`, capped at 80), so it reads a bounded tail of the ledger
|
|
476
|
+
* instead of parsing the whole file every turn_end. 160 = twice the 80-entry cap
|
|
477
|
+
* leaves ample margin above any entries appended after the assistant turn, so the
|
|
478
|
+
* assessed window is byte-identical to the whole-file read (see the
|
|
479
|
+
* behaviour-equivalence contract test) while cost stays bounded by session
|
|
480
|
+
* *shape*, not session *length*.
|
|
481
|
+
*/
|
|
482
|
+
const FINISH_CONTRACT_TAIL_ENTRIES = DEFAULT_RECENT_ENTRY_LIMIT * 2;
|
|
483
|
+
|
|
484
|
+
function readRecentSessionEntriesForContract(sessionId: string): SessionEntry[] {
|
|
485
|
+
return collectSessionEntries(
|
|
486
|
+
readSessionTailTurns(sessionId, FINISH_CONTRACT_TAIL_ENTRIES).entries,
|
|
487
|
+
sessionCurrentPath(sessionId),
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function protectedArtifactStateForCurrentSession(
|
|
492
|
+
session: SessionContract,
|
|
493
|
+
): ReturnType<typeof protectedArtifactStateFromSessionEntries> {
|
|
494
|
+
const meta = session.current();
|
|
495
|
+
if (!meta) return { artifacts: [] };
|
|
496
|
+
reconcilePendingProtectedArtifacts(session);
|
|
497
|
+
return protectedArtifactStateFromSessionEntries(readSessionEntriesForCompact(meta.id));
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function appendProtectedArtifactRegistryEvent(
|
|
501
|
+
session: SessionContract | undefined,
|
|
502
|
+
event: ProtectedArtifactProtectEvent,
|
|
503
|
+
): void {
|
|
504
|
+
const current = session?.current();
|
|
505
|
+
if (session === undefined || current === null || current === undefined) {
|
|
506
|
+
throw new Error("no active session is available for protected artifact persistence");
|
|
507
|
+
}
|
|
508
|
+
const pending = stagePendingProtectedArtifact(current.id, event);
|
|
509
|
+
session.appendEntry(
|
|
510
|
+
protectedArtifactEntryFromArtifact(event.artifact, {
|
|
511
|
+
parentTurnId: event.turnId ?? null,
|
|
512
|
+
toolName: event.toolName,
|
|
513
|
+
...(event.toolCallId !== undefined ? { toolCallId: event.toolCallId } : {}),
|
|
514
|
+
...(event.runId !== undefined ? { runId: event.runId } : {}),
|
|
515
|
+
...(event.correlationId !== undefined ? { correlationId: event.correlationId } : {}),
|
|
516
|
+
}),
|
|
517
|
+
);
|
|
518
|
+
if (session.flushAppends === undefined) {
|
|
519
|
+
throw new Error("session does not expose the durable append flush required by protected artifact persistence");
|
|
520
|
+
}
|
|
521
|
+
session.flushAppends();
|
|
522
|
+
clearPendingProtectedArtifact(pending);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Fold a terminal dispatch payload's worker skill activations into the session
|
|
527
|
+
* ledger, tagged with the runId, so worker skill provenance sits next to
|
|
528
|
+
* main-agent activations.
|
|
529
|
+
*
|
|
530
|
+
* Both terminal channels carry them and the run finalizer emits exactly one of
|
|
531
|
+
* the two, so this cannot double-record. The orchestrator never observes worker
|
|
532
|
+
* tool calls directly, because a worker runs its own registry in its own
|
|
533
|
+
* subprocess, which makes this the only recording path there is. Returns how
|
|
534
|
+
* many were folded.
|
|
535
|
+
*/
|
|
536
|
+
export function foldDispatchSkillActivations(
|
|
537
|
+
session: SessionContract | undefined,
|
|
538
|
+
payload: { runId?: unknown; skillActivations?: ReadonlyArray<unknown> } | undefined,
|
|
539
|
+
): number {
|
|
540
|
+
const runId = payload?.runId;
|
|
541
|
+
if (typeof runId !== "string") return 0;
|
|
542
|
+
let folded = 0;
|
|
543
|
+
for (const activation of payload?.skillActivations ?? []) {
|
|
544
|
+
if (!isSkillActivation(activation)) continue;
|
|
545
|
+
appendSkillActivationRegistryEvent(session, { ...activation, runId });
|
|
546
|
+
folded += 1;
|
|
547
|
+
}
|
|
548
|
+
return folded;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function appendSkillActivationRegistryEvent(
|
|
552
|
+
session: SessionContract | undefined,
|
|
553
|
+
activation: Parameters<SessionContract["recordSkillActivation"]>[0],
|
|
554
|
+
): void {
|
|
555
|
+
if (!session?.current()) return;
|
|
556
|
+
try {
|
|
557
|
+
session.recordSkillActivation(activation);
|
|
558
|
+
} catch {
|
|
559
|
+
// Activation metadata should never alter the result of a completed
|
|
560
|
+
// context(scope=skills) call. Missing ledger data is visible in diagnostics.
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async function runCompactionFlow(
|
|
565
|
+
session: SessionContract,
|
|
566
|
+
settings: ClioSettings,
|
|
567
|
+
providers: ProvidersContract,
|
|
568
|
+
instructions?: string,
|
|
569
|
+
trigger?: CompactionTrigger,
|
|
570
|
+
): Promise<CompactResult | null> {
|
|
571
|
+
const meta = session.current();
|
|
572
|
+
if (!meta) {
|
|
573
|
+
throw new Error("no current session to compact; start one with /new or /resume first");
|
|
574
|
+
}
|
|
575
|
+
const resolved = await resolveCompactionModel(settings, providers);
|
|
576
|
+
if (!resolved) {
|
|
577
|
+
throw new Error("no model configured; set orchestrator.target + orchestrator.model");
|
|
578
|
+
}
|
|
579
|
+
// Summarize only the active branch: after a /tree switch the raw file
|
|
580
|
+
// still holds abandoned sibling turns, and a summary that folds them in
|
|
581
|
+
// would persist abandoned content back into the active context. The full
|
|
582
|
+
// file read stays in place for protected artifacts and the masking
|
|
583
|
+
// rewrite, which are session-global. The task board is not: it used to
|
|
584
|
+
// read the full file too (last taskLedger entry in file order, with no
|
|
585
|
+
// branch filter at all), which was a second, independent instance of this
|
|
586
|
+
// same bug. See the taskBoard wiring below for the fix.
|
|
587
|
+
const activeLeafTurnId = session.tree(meta.id).leafId ?? undefined;
|
|
588
|
+
const entries = filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), activeLeafTurnId);
|
|
589
|
+
if (entries.length === 0) return null;
|
|
590
|
+
|
|
591
|
+
const result = await compact({
|
|
592
|
+
entries,
|
|
593
|
+
model: resolved.model,
|
|
594
|
+
...(resolved.apiKey !== undefined ? { apiKey: resolved.apiKey } : {}),
|
|
595
|
+
...(instructions !== undefined ? { instructions } : {}),
|
|
596
|
+
});
|
|
597
|
+
if (result.messagesSummarized === 0 || result.summary.length === 0) return null;
|
|
598
|
+
|
|
599
|
+
const entry: Omit<CompactionSummaryEntry, "turnId" | "timestamp"> = {
|
|
600
|
+
kind: "compactionSummary",
|
|
601
|
+
parentTurnId: result.firstKeptTurnId ?? null,
|
|
602
|
+
summary: result.summary,
|
|
603
|
+
tokensBefore: result.tokensBefore,
|
|
604
|
+
firstKeptTurnId: result.firstKeptTurnId ?? "",
|
|
605
|
+
messagesSummarized: result.messagesSummarized,
|
|
606
|
+
isSplitTurn: result.isSplitTurn,
|
|
607
|
+
tokensAfter: estimateTokensAfterCompaction(entries, result),
|
|
608
|
+
// The summarization call is a real model call. Persisting its provider
|
|
609
|
+
// usage on the entry is what puts it in front of `/cost` and `clio-coder usage
|
|
610
|
+
// report`, which folded the ledger and so counted every call but this one.
|
|
611
|
+
...(result.usage !== undefined ? { usage: result.usage } : {}),
|
|
612
|
+
};
|
|
613
|
+
if (trigger !== undefined) entry.trigger = trigger;
|
|
614
|
+
session.appendEntry(entry);
|
|
615
|
+
return result;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Compose the production chat-loop compaction callback. Errors intentionally
|
|
620
|
+
* propagate: the chat loop owns activity failure reporting and distinguishes
|
|
621
|
+
* a thrown read/model/persistence failure from the legitimate null no-op that
|
|
622
|
+
* `runCompactionFlow` returns for an empty session or an unavailable cut.
|
|
623
|
+
*/
|
|
624
|
+
export function createProductionAutoCompact(
|
|
625
|
+
session: SessionContract,
|
|
626
|
+
getSettings: () => ClioSettings,
|
|
627
|
+
providers: ProvidersContract,
|
|
628
|
+
): (instructions?: string, trigger?: CompactionTrigger) => Promise<CompactResult | null> {
|
|
629
|
+
return (instructions, trigger) => runCompactionFlow(session, getSettings(), providers, instructions, trigger);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function estimateTokensFromSummary(summary: string): number {
|
|
633
|
+
// Mirrors the rough byte/4 heuristic the rest of the compaction stack
|
|
634
|
+
// uses for unmeasured payloads. Kept inline because this is the only
|
|
635
|
+
// caller; pi-mono's token estimator is provider-specific and we do not
|
|
636
|
+
// have a model handle at the persistence layer.
|
|
637
|
+
return Math.max(1, ceilChars(summary.length));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* The post-compaction size, on the same scale as `tokensBefore`.
|
|
642
|
+
*
|
|
643
|
+
* `tokensBefore` is a whole-prompt figure: `calculateContextTokens` anchors it
|
|
644
|
+
* on the last assistant call's measured usage, so it carries the system prompt
|
|
645
|
+
* and tool schemas that compaction never touches. The after figure has to stay
|
|
646
|
+
* on that scale to be comparable, and the persistence layer has no model handle
|
|
647
|
+
* to re-measure with, so it is arithmetic on that same scale: drop what stops
|
|
648
|
+
* being replayed and add the summary that replaces it.
|
|
649
|
+
*
|
|
650
|
+
* Estimating it from the rebuilt message list instead reported `tokensBefore`
|
|
651
|
+
* back unchanged, which is what made /tree render "~16276 -> ~16276 tokens"
|
|
652
|
+
* beside a footer that said 16276 -> 11008 for the same compaction. That
|
|
653
|
+
* estimator anchors on the newest assistant usage, and the retained suffix
|
|
654
|
+
* still holds the assistant message whose usage describes the pre-compaction
|
|
655
|
+
* prompt. Anchoring on it reports precisely the number compaction removed.
|
|
656
|
+
*/
|
|
657
|
+
function estimateTokensAfterCompaction(entries: ReadonlyArray<SessionEntry>, result: CompactResult): number {
|
|
658
|
+
let droppedTokens = 0;
|
|
659
|
+
for (const entry of entries.slice(0, result.firstKeptEntryIndex)) droppedTokens += estimateTokens(entry);
|
|
660
|
+
const summaryTokens = estimateTokensFromSummary(result.summary);
|
|
661
|
+
// The summary alone is the floor: a session whose dropped estimate exceeds
|
|
662
|
+
// the measured anchor must not report a negative or sub-summary context.
|
|
663
|
+
return Math.max(summaryTokens, result.tokensBefore - droppedTokens + summaryTokens);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Alt+J / Alt+K step the orchestrator through the `scope` list of target
|
|
668
|
+
* ids or target/model refs. Absent scope is a no-op so unconfigured users
|
|
669
|
+
* feel nothing.
|
|
670
|
+
*/
|
|
671
|
+
export function advanceScopedTarget(
|
|
672
|
+
settings: Readonly<ClioSettings>,
|
|
673
|
+
direction: "forward" | "backward",
|
|
674
|
+
): { target: string; model: string | null } | null {
|
|
675
|
+
const scope = settings.scope ?? [];
|
|
676
|
+
if (scope.length === 0) return null;
|
|
677
|
+
const registry = getRuntimeRegistry();
|
|
678
|
+
if (registry.list().length === 0) registerBuiltinRuntimes(registry);
|
|
679
|
+
const filteredScope = scope.filter((entry) => {
|
|
680
|
+
const [targetId] = entry.split("/");
|
|
681
|
+
const target = settings.targets.find((e) => e.id === targetId);
|
|
682
|
+
if (!target) return false;
|
|
683
|
+
const runtime = registry.get(target.runtime);
|
|
684
|
+
return runtime !== null && isOrchestratorEligibleRuntime(runtime);
|
|
685
|
+
});
|
|
686
|
+
if (filteredScope.length === 0) return null;
|
|
687
|
+
const activeTarget = settings.orchestrator.target ?? "";
|
|
688
|
+
const activeModel = settings.orchestrator.model ?? "";
|
|
689
|
+
const activeCombinedRef = activeTarget.length > 0 && activeModel.length > 0 ? `${activeTarget}/${activeModel}` : "";
|
|
690
|
+
const idx = filteredScope.findIndex((entry) => entry === activeCombinedRef || entry === activeTarget);
|
|
691
|
+
const base = idx === -1 ? 0 : idx + (direction === "forward" ? 1 : filteredScope.length - 1);
|
|
692
|
+
const next = filteredScope[base % filteredScope.length];
|
|
693
|
+
if (!next) return null;
|
|
694
|
+
const [targetId, ...modelParts] = next.split("/");
|
|
695
|
+
if (!targetId) return null;
|
|
696
|
+
if (modelParts.length > 0) {
|
|
697
|
+
return { target: targetId, model: modelParts.join("/") };
|
|
698
|
+
}
|
|
699
|
+
if (activeTarget === targetId) {
|
|
700
|
+
return { target: targetId, model: activeModel || null };
|
|
701
|
+
}
|
|
702
|
+
const descriptor = settings.targets.find((entry) => entry.id === targetId);
|
|
703
|
+
return { target: targetId, model: descriptor?.defaultModel ?? null };
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
export async function bootOrchestrator(options: BootOptions = {}): Promise<BootResult> {
|
|
707
|
+
const bootStdout = (text: string): void => {
|
|
708
|
+
if (options.terminalLease) options.terminalLease.writeDiagnostic("stdout", text);
|
|
709
|
+
else process.stdout.write(text);
|
|
710
|
+
};
|
|
711
|
+
const bootStderr = (text: string): void => {
|
|
712
|
+
if (options.terminalLease) options.terminalLease.writeDiagnostic("stderr", text);
|
|
713
|
+
else process.stderr.write(text);
|
|
714
|
+
};
|
|
715
|
+
const timer = new StartupTimer(
|
|
716
|
+
options.terminalLease
|
|
717
|
+
? (phase) => {
|
|
718
|
+
const line = formatBootTrace(phase);
|
|
719
|
+
if (line) options.terminalLease?.deferDiagnostic("stderr", line);
|
|
720
|
+
}
|
|
721
|
+
: undefined,
|
|
722
|
+
);
|
|
723
|
+
const bus = getSharedBus();
|
|
724
|
+
const termination = getTerminationCoordinator();
|
|
725
|
+
installBusTracer();
|
|
726
|
+
termination.installSignalHandlers();
|
|
727
|
+
|
|
728
|
+
ensureClioState();
|
|
729
|
+
timer.mark("install check");
|
|
730
|
+
|
|
731
|
+
// A hard-killed coordinator cannot run domain drains. Reconcile its compete
|
|
732
|
+
// process leases before dispatch startup scans the ledger, so abandoned rows
|
|
733
|
+
// observe dead workers and no stale worktree can be reused by this boot.
|
|
734
|
+
if (isGitRepository(process.cwd())) {
|
|
735
|
+
try {
|
|
736
|
+
const pendingGate = readPendingGateDecisions();
|
|
737
|
+
const pendingCompeteGroups = new Set<string>();
|
|
738
|
+
for (const handle of pendingGate.records) {
|
|
739
|
+
if (handle.record.kind === "output" && handle.record.topology === "compete") {
|
|
740
|
+
pendingCompeteGroups.add(handle.record.group);
|
|
741
|
+
} else if (handle.record.kind === "decision" && handle.record.decision.topology === "compete") {
|
|
742
|
+
pendingCompeteGroups.add(handle.record.decision.group);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
const durableDecisions = readGateDecisionArtifacts();
|
|
746
|
+
const confirmedGroups = new Set(
|
|
747
|
+
durableDecisions
|
|
748
|
+
.filter(
|
|
749
|
+
({ artifact }) =>
|
|
750
|
+
artifact.topology === "compete" &&
|
|
751
|
+
(artifact.outcome === "operator-confirmed" || artifact.outcome === "full-auto-applied"),
|
|
752
|
+
)
|
|
753
|
+
.map(({ artifact }) => artifact.group),
|
|
754
|
+
);
|
|
755
|
+
for (const { artifact } of durableDecisions) {
|
|
756
|
+
if (artifact.topology === "compete" && artifact.outcome === "winner" && !confirmedGroups.has(artifact.group)) {
|
|
757
|
+
pendingCompeteGroups.add(artifact.group);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
const recovery = recoverCleanupReadyCompeteGroups(process.cwd(), {
|
|
761
|
+
preserveActiveGroups: pendingCompeteGroups,
|
|
762
|
+
preserveAllActive: pendingGate.errors.length > 0,
|
|
763
|
+
});
|
|
764
|
+
for (const failure of recovery.failed) {
|
|
765
|
+
bootStderr(`[dispatch] compete recovery preserved ${failure.group}: ${failure.message}\n`);
|
|
766
|
+
}
|
|
767
|
+
} catch (err) {
|
|
768
|
+
bootStderr(`[dispatch] compete recovery failed closed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
let effectiveSettingsForDispatch: (() => Readonly<ClioSettings>) | null = null;
|
|
773
|
+
let protectedArtifactStateForDispatch: (() => ProtectedArtifactState) | null = null;
|
|
774
|
+
|
|
775
|
+
const result = await loadDomains(
|
|
776
|
+
[
|
|
777
|
+
options.startupSettings ? createConfigDomainModule(options.startupSettings) : ConfigDomainModule,
|
|
778
|
+
ExtensionsDomainModule,
|
|
779
|
+
InteropDomainModule,
|
|
780
|
+
createResourcesDomainModule({
|
|
781
|
+
reservedPromptNames: new Set(BUILTIN_SLASH_COMMANDS.map((entry) => entry.name)),
|
|
782
|
+
skills: () => ({
|
|
783
|
+
disableDiscovery: options.noSkills === true || options.headless?.noSkills === true,
|
|
784
|
+
...(options.skillPaths && options.skillPaths.length > 0
|
|
785
|
+
? { explicitSkillPaths: options.skillPaths }
|
|
786
|
+
: options.headless?.skillPaths && options.headless.skillPaths.length > 0
|
|
787
|
+
? { explicitSkillPaths: options.headless.skillPaths }
|
|
788
|
+
: {}),
|
|
789
|
+
}),
|
|
790
|
+
}),
|
|
791
|
+
ShareDomainModule,
|
|
792
|
+
createContextDomainModule({ noContextFiles: options.noContextFiles === true }),
|
|
793
|
+
ProvidersDomainModule,
|
|
794
|
+
SafetyDomainModule,
|
|
795
|
+
createPromptsDomainModule({
|
|
796
|
+
noContextFiles: options.noContextFiles === true,
|
|
797
|
+
}),
|
|
798
|
+
AgentsDomainModule,
|
|
799
|
+
MiddlewareDomainModule,
|
|
800
|
+
SessionDomainModule,
|
|
801
|
+
ObservabilityDomainModule,
|
|
802
|
+
SchedulingDomainModule,
|
|
803
|
+
// Dispatch resolves worker targets through the session's effective
|
|
804
|
+
// settings view once it exists (assigned below, after the config
|
|
805
|
+
// contract loads); until then it falls back to the shared snapshot.
|
|
806
|
+
createDispatchDomainModule({
|
|
807
|
+
getSettings: () => effectiveSettingsForDispatch?.(),
|
|
808
|
+
getProtectedArtifactState: () => protectedArtifactStateForDispatch?.() ?? { artifacts: [] },
|
|
809
|
+
autonomyOverride: options.headless?.autonomy !== undefined,
|
|
810
|
+
}),
|
|
811
|
+
LifecycleDomainModule,
|
|
812
|
+
],
|
|
813
|
+
{ diagnostic: bootStderr },
|
|
814
|
+
);
|
|
815
|
+
timer.mark(`domains loaded (${result.loaded.length})`);
|
|
816
|
+
|
|
817
|
+
const dispatch = result.getContract<DispatchContract>("dispatch");
|
|
818
|
+
if (dispatch) {
|
|
819
|
+
termination.onDrain(async () => {
|
|
820
|
+
await dispatch.drain();
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
termination.onPersist(async () => {
|
|
824
|
+
await result.stop();
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
bus.emit(BusChannels.SessionStart, { at: Date.now() });
|
|
828
|
+
timer.mark("session_start fired");
|
|
829
|
+
|
|
830
|
+
const acpMode = options.acp !== undefined;
|
|
831
|
+
const interactive = !options.headless && !acpMode && process.env.CLIO_CODER_INTERACTIVE === "1";
|
|
832
|
+
if (!interactive && !options.headless && !acpMode) {
|
|
833
|
+
bootStdout(buildBanner());
|
|
834
|
+
if (process.env.CLIO_CODER_TIMING === "1") bootStdout(`${timer.report()}\n`);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const config = result.getContract<ConfigContract>("config");
|
|
838
|
+
const providers = result.getContract<ProvidersContract>("providers");
|
|
839
|
+
timer.mark("providers resolved");
|
|
840
|
+
|
|
841
|
+
if (options.apiKey) {
|
|
842
|
+
if (!providers) {
|
|
843
|
+
bootStderr("Clio Coder: --api-key supplied but providers domain unavailable; ignoring.\n");
|
|
844
|
+
} else {
|
|
845
|
+
const settingsNow = applyHeadlessSettingsOverlay(config?.get() ?? readSettings(), options.headless);
|
|
846
|
+
const activeTargetId = settingsNow.orchestrator?.target;
|
|
847
|
+
const target = resolveTarget(providers, activeTargetId);
|
|
848
|
+
const runtime = target ? providers.getRuntime(target.runtime) : null;
|
|
849
|
+
if (target && runtime) {
|
|
850
|
+
providers.auth.setRuntimeOverrideForTarget(target, runtime, options.apiKey);
|
|
851
|
+
} else {
|
|
852
|
+
bootStderr("Clio Coder: --api-key supplied but no active orchestrator target is configured; ignoring.\n");
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (!interactive && !options.headless && !acpMode) {
|
|
858
|
+
bootStdout(`${chalk.dim(" (non-interactive boot. pass CLIO_CODER_INTERACTIVE=1 to launch the TUI.)")}\n`);
|
|
859
|
+
await termination.shutdown(0);
|
|
860
|
+
return { exitCode: 0, bootTimeMs: timer.snapshot().totalMs };
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const middleware = result.getContract<MiddlewareContract>("middleware");
|
|
864
|
+
const observability = result.getContract<ObservabilityContract>("observability");
|
|
865
|
+
const safety = result.getContract<SafetyContract>("safety");
|
|
866
|
+
const session = result.getContract<SessionContract>("session");
|
|
867
|
+
const prompts = result.getContract<PromptsContract>("prompts");
|
|
868
|
+
const agents = result.getContract<AgentsContract>("agents");
|
|
869
|
+
const resources = result.getContract<ResourcesContract>("resources");
|
|
870
|
+
const extensions = result.getContract<ExtensionsContract>("extensions");
|
|
871
|
+
const share = result.getContract<ShareContract>("share");
|
|
872
|
+
const contextDomain = result.getContract<ContextContract>("context");
|
|
873
|
+
const interop = result.getContract<InteropContract>("interop");
|
|
874
|
+
// Boot detection resolves paths only: no `--version` subprocess and no skill
|
|
875
|
+
// walk on the boot path. The hint is gated on `interactive`, which is already
|
|
876
|
+
// false under headless and ACP.
|
|
877
|
+
const interopReport = interactive && interop ? await interop.detect({ cwd: process.cwd() }) : null;
|
|
878
|
+
const initialNotices = interactive ? [...(contextDomain?.startupHints() ?? [])] : [];
|
|
879
|
+
// Once per version, interactive only: headless and ACP have no operator at
|
|
880
|
+
// the keyboard to tell, and the record is left unclaimed for the boot that does.
|
|
881
|
+
const upgrade = interactive ? takeUpgradeNotice() : null;
|
|
882
|
+
if (upgrade !== null) initialNotices.push(describeUpgradeNotice(upgrade));
|
|
883
|
+
if (interop && interopReport) {
|
|
884
|
+
const interopHint = interop.bootHint(interopReport);
|
|
885
|
+
if (interopHint !== null) initialNotices.push(interopHint);
|
|
886
|
+
}
|
|
887
|
+
if (!providers || !dispatch || !observability || !safety || !middleware) {
|
|
888
|
+
bootStderr(
|
|
889
|
+
"Clio Coder: chat mode requires safety + middleware + providers + dispatch + observability contracts; aborting.\n",
|
|
890
|
+
);
|
|
891
|
+
await termination.shutdown(1);
|
|
892
|
+
return { exitCode: 1, bootTimeMs: timer.snapshot().totalMs };
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// A headless `--session`/`--continue` resolves here, where the session
|
|
896
|
+
// domain exists. It is a hard requirement rather than a hint: a caller that
|
|
897
|
+
// asked to continue a conversation must not receive an answer written
|
|
898
|
+
// without that conversation's history.
|
|
899
|
+
const requestedResume = options.headless?.resumeSession;
|
|
900
|
+
let headlessResumeFailure: string | null = null;
|
|
901
|
+
let resolvedResumeId: string | undefined;
|
|
902
|
+
if (requestedResume !== undefined) {
|
|
903
|
+
if (!session) {
|
|
904
|
+
headlessResumeFailure = "session continuation requires the session domain, which is not loaded";
|
|
905
|
+
} else if (requestedResume.kind === "id") {
|
|
906
|
+
resolvedResumeId = requestedResume.id;
|
|
907
|
+
} else {
|
|
908
|
+
const latest = session.history()[0];
|
|
909
|
+
if (latest === undefined) headlessResumeFailure = `no previous session recorded for ${process.cwd()}`;
|
|
910
|
+
else resolvedResumeId = latest.id;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
const resumeId = resolvedResumeId ?? process.env.CLIO_CODER_RESUME_SESSION_ID?.trim();
|
|
914
|
+
let resumedSessionAtBoot = false;
|
|
915
|
+
if (resumeId && session && headlessResumeFailure === null) {
|
|
916
|
+
try {
|
|
917
|
+
session.resume(resumeId);
|
|
918
|
+
resumedSessionAtBoot = true;
|
|
919
|
+
} catch (err) {
|
|
920
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
921
|
+
if (requestedResume !== undefined) headlessResumeFailure = `failed to resume session ${resumeId}: ${detail}`;
|
|
922
|
+
else bootStderr(`Clio Coder: failed to resume session ${resumeId}: ${detail}\n`);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
Reflect.deleteProperty(process.env, "CLIO_CODER_RESUME_SESSION_ID");
|
|
926
|
+
if (headlessResumeFailure !== null) {
|
|
927
|
+
bootStderr(`clio-coder run: ${headlessResumeFailure}\n`);
|
|
928
|
+
await termination.shutdown(2);
|
|
929
|
+
return { exitCode: 2, bootTimeMs: timer.snapshot().totalMs };
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Hook diagnostics ride the typed bus. The domain loader constructed the
|
|
933
|
+
// bundle with the stderr default; swap in a sink that publishes
|
|
934
|
+
// middleware.hookFailed (the interactive warn notice consumes it) and keep
|
|
935
|
+
// stderr for non-interactive runs, which have no notice subscriber.
|
|
936
|
+
middleware.setDiagnosticSink((diagnostic) => {
|
|
937
|
+
bus.emit(BusChannels.MiddlewareHookFailed, {
|
|
938
|
+
kind: diagnostic.kind,
|
|
939
|
+
registrationId: diagnostic.registrationId,
|
|
940
|
+
hook: diagnostic.hook,
|
|
941
|
+
at: Date.now(),
|
|
942
|
+
...(diagnostic.kind === "hook_failed"
|
|
943
|
+
? { message: diagnostic.message }
|
|
944
|
+
: {
|
|
945
|
+
elapsedMs: diagnostic.elapsedMs,
|
|
946
|
+
budgetMs: diagnostic.budgetMs,
|
|
947
|
+
steadyStateWarn: diagnostic.steadyStateWarn,
|
|
948
|
+
p50Ms: diagnostic.stats.p50Ms,
|
|
949
|
+
p95Ms: diagnostic.stats.p95Ms,
|
|
950
|
+
overCount: diagnostic.stats.overCount,
|
|
951
|
+
windowSamples: diagnostic.stats.window,
|
|
952
|
+
}),
|
|
953
|
+
});
|
|
954
|
+
if (!interactive) writeMiddlewareDiagnosticToStderr(diagnostic);
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
// Residency and runtime notices (model swaps, double residency, VRAM
|
|
958
|
+
// stress) reach the operator through the interactive notice renderer; a
|
|
959
|
+
// headless run has no subscriber, so mirror them to stderr there. Silent
|
|
960
|
+
// model swaps are exactly the failure mode the residency policy forbids.
|
|
961
|
+
if (!interactive) {
|
|
962
|
+
bus.on(BusChannels.RuntimeNotice, (payload: unknown) => {
|
|
963
|
+
const notice = payload as { level?: string; message?: string } | undefined;
|
|
964
|
+
if (typeof notice?.message !== "string") return;
|
|
965
|
+
process.stderr.write(`[clio:runtime] ${notice.level ?? "info"}: ${notice.message}\n`);
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// Guardrail policy (tool-call budgets, tool byte caps, dispatch ledger cap)
|
|
970
|
+
// resolves settings-first with env as emergency override; install the
|
|
971
|
+
// settings section before any guard registration or tool reads it.
|
|
972
|
+
configureGuardrails((config?.get() ?? readSettings()).guardrails);
|
|
973
|
+
|
|
974
|
+
// Guard registrations on the middleware contract, in order: loop guard,
|
|
975
|
+
// protected artifacts (last among guards so it absorbs protect_path effects
|
|
976
|
+
// from everything before it), dispatch dedup. Workers register their own
|
|
977
|
+
// loop guard and protected-artifacts instances inside their subprocess in
|
|
978
|
+
// worker-runtime.ts; the orchestrator instances carry the bus and the
|
|
979
|
+
// session persistence sink.
|
|
980
|
+
middleware.registerHook(
|
|
981
|
+
createLoopGuardRegistration({
|
|
982
|
+
safety,
|
|
983
|
+
bus,
|
|
984
|
+
turnBlockBudget: INTERACTIVE_LOOP_BLOCK_BUDGET,
|
|
985
|
+
turnToolCallBudget: readOrchTurnToolCallBudget(),
|
|
986
|
+
// Interactive/headless/ACP all share this orchestrator guard: at the
|
|
987
|
+
// block budget, lock tools for the rest of the turn so the model
|
|
988
|
+
// answers from what it gathered instead of hard-cancelling a turn that
|
|
989
|
+
// may already hold the answer. The bounded backstop still cancels a
|
|
990
|
+
// model that keeps calling tools.
|
|
991
|
+
turnSynthesisLockout: true,
|
|
992
|
+
}),
|
|
993
|
+
);
|
|
994
|
+
let initialProtectedArtifactState: ProtectedArtifactState | undefined;
|
|
995
|
+
let initialProtectionReadError: string | null = null;
|
|
996
|
+
if (session) {
|
|
997
|
+
try {
|
|
998
|
+
initialProtectedArtifactState = protectedArtifactStateForCurrentSession(session);
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
initialProtectionReadError = error instanceof Error ? error.message : String(error);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
const protectedArtifactsGuard = createProtectedArtifactsRegistration({
|
|
1004
|
+
...(initialProtectedArtifactState !== undefined ? { initialState: initialProtectedArtifactState } : {}),
|
|
1005
|
+
onProtect: (event) => appendProtectedArtifactRegistryEvent(session, event),
|
|
1006
|
+
onDurabilityFailure: (health) => {
|
|
1007
|
+
bus.emit(BusChannels.MiddlewareHookFailed, {
|
|
1008
|
+
kind: "hook_failed",
|
|
1009
|
+
registrationId: "guard.protected-artifacts",
|
|
1010
|
+
hook: "before_tool",
|
|
1011
|
+
at: Date.now(),
|
|
1012
|
+
message: health.reason,
|
|
1013
|
+
});
|
|
1014
|
+
},
|
|
1015
|
+
});
|
|
1016
|
+
if (initialProtectionReadError !== null) {
|
|
1017
|
+
protectedArtifactsGuard.markDegraded(
|
|
1018
|
+
`initial session protection history could not be read: ${initialProtectionReadError}`,
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
protectedArtifactStateForDispatch = () => {
|
|
1022
|
+
const health = protectedArtifactsGuard.health();
|
|
1023
|
+
if (health.kind === "degraded") {
|
|
1024
|
+
throw new Error(`dispatch: protected artifact durability degraded: ${health.reason}`);
|
|
1025
|
+
}
|
|
1026
|
+
return protectedArtifactsGuard.state();
|
|
1027
|
+
};
|
|
1028
|
+
middleware.registerHook(protectedArtifactsGuard);
|
|
1029
|
+
middleware.registerHook(createDispatchDedupRegistration());
|
|
1030
|
+
// Observers run after the guards; they emit no effects and their sinks are
|
|
1031
|
+
// best-effort (session ledger, codewiki refresh).
|
|
1032
|
+
middleware.registerHook(
|
|
1033
|
+
createSkillActivationObserver((activation) => appendSkillActivationRegistryEvent(session, activation)),
|
|
1034
|
+
);
|
|
1035
|
+
// First-turn skills reminder: user-message-visible text is the one channel
|
|
1036
|
+
// the battery-tested local models act on; suggestion protocol only, the
|
|
1037
|
+
// operator load gate is untouched.
|
|
1038
|
+
if (resources) {
|
|
1039
|
+
middleware.registerHook(
|
|
1040
|
+
createSkillsReminderRegistration({
|
|
1041
|
+
countModelVisibleSkills: () => modelVisibleSkills(resources.skills(process.cwd()).items).length,
|
|
1042
|
+
// Same lookup context(scope="skills") lists under its Marketplace
|
|
1043
|
+
// heading, minus what is already installed, so the count the
|
|
1044
|
+
// reminder quotes is the count the listing will show.
|
|
1045
|
+
countInstallableSkills: () => {
|
|
1046
|
+
const installed = new Set(resources.skills(process.cwd()).items.map((skill) => skill.name));
|
|
1047
|
+
return discoverMarketplaceSkills({ cwd: process.cwd() }).skills.filter((skill) => !installed.has(skill.name))
|
|
1048
|
+
.length;
|
|
1049
|
+
},
|
|
1050
|
+
}),
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
// Task-board reminder: same user-message-visible channel, fired once per
|
|
1054
|
+
// session when a request literally enumerates three or more steps. The
|
|
1055
|
+
// static routing line and tasks hint ask for the same board; battery-tested
|
|
1056
|
+
// local models only comply when the instruction rides the user message.
|
|
1057
|
+
middleware.registerHook(createTaskBoardReminderRegistration());
|
|
1058
|
+
const taskMemoryBank = new TaskMemoryBank();
|
|
1059
|
+
let taskMemorySessionId = session?.current()?.id ?? null;
|
|
1060
|
+
const ensureTaskMemorySession = (): void => {
|
|
1061
|
+
const currentSessionId = session?.current()?.id ?? null;
|
|
1062
|
+
if (currentSessionId === taskMemorySessionId) return;
|
|
1063
|
+
taskMemoryBank.clear();
|
|
1064
|
+
taskMemorySessionId = currentSessionId;
|
|
1065
|
+
};
|
|
1066
|
+
const memorySettings = (config?.get() ?? readSettings()).memory.intervention;
|
|
1067
|
+
// Bound late: the registration is built here, but the buffer a deferred
|
|
1068
|
+
// reminder lands in belongs to the chat loop that has not been composed yet.
|
|
1069
|
+
let deferredMemoryReminderSink: ((message: string) => void) | null = null;
|
|
1070
|
+
// Content-bearing, so it exists only when the operator named a file. The
|
|
1071
|
+
// telemetry row says which silence happened; this says what the model wrote.
|
|
1072
|
+
const memoryTracePath = taskMemoryTracePath();
|
|
1073
|
+
const memoryTrace = memoryTracePath === null ? null : createTaskMemoryTrace(memoryTracePath);
|
|
1074
|
+
const memoryIntervention = createMemoryInterventionRegistration({
|
|
1075
|
+
bank: taskMemoryBank,
|
|
1076
|
+
telemetry: createTaskMemoryTelemetrySink(),
|
|
1077
|
+
...(memoryTrace === null ? {} : { onEnvelope: (envelope) => memoryTrace.record(envelope) }),
|
|
1078
|
+
// A headless run submits no further turn, so a detached step could only
|
|
1079
|
+
// finish after the process that would have read it has exited.
|
|
1080
|
+
deliversDeferredReminders: options.headless === undefined,
|
|
1081
|
+
onDeferredReminder: (message) => deferredMemoryReminderSink?.(message),
|
|
1082
|
+
getSettings: () => {
|
|
1083
|
+
ensureTaskMemorySession();
|
|
1084
|
+
return effectiveSettingsForDispatch?.().memory.intervention ?? memorySettings;
|
|
1085
|
+
},
|
|
1086
|
+
getModelClient: () => {
|
|
1087
|
+
const settings = effectiveSettingsForDispatch?.();
|
|
1088
|
+
return settings === undefined
|
|
1089
|
+
? null
|
|
1090
|
+
: createBackgroundMemoryModelClient(providers, settings, settings.memory.intervention.timeoutMs);
|
|
1091
|
+
},
|
|
1092
|
+
});
|
|
1093
|
+
middleware.registerHook(memoryIntervention);
|
|
1094
|
+
const unsubscribeMemoryLoop = bus.on(BusChannels.LoopBlocked, () => memoryIntervention.signalLoop());
|
|
1095
|
+
termination.onDrain(() => unsubscribeMemoryLoop());
|
|
1096
|
+
if (contextDomain) {
|
|
1097
|
+
middleware.registerHook(createFileMutationObserver(({ paths }) => contextDomain.noteFileChanges(paths)));
|
|
1098
|
+
}
|
|
1099
|
+
// User-defined hooks: extensions and the project (.clio-coder/hooks.yaml,
|
|
1100
|
+
// .clio-coder/hooks.local.yaml) declare a conservative, receipted hook set on the
|
|
1101
|
+
// same effect machinery. They register after the guards, so safety stays
|
|
1102
|
+
// authoritative: a hook may add effects (including request block_tool) but
|
|
1103
|
+
// cannot grant a permission safety would deny. Loading is best-effort.
|
|
1104
|
+
const hookReceiptLog = createHookReceiptLog({ persistPath: join(clioStateDir(), "hook-receipts.json") });
|
|
1105
|
+
const extensionHookRoots: ExtensionHookRoot[] = (extensions?.list(process.cwd()) ?? [])
|
|
1106
|
+
.filter((ext) => ext.enabled && ext.effective)
|
|
1107
|
+
.map((ext) => ({ id: ext.id, rootPath: ext.rootPath }));
|
|
1108
|
+
const userHooks = installUserHooks({
|
|
1109
|
+
cwd: process.cwd(),
|
|
1110
|
+
extensions: extensionHookRoots,
|
|
1111
|
+
registerHook: (registration) => middleware.registerHook(registration),
|
|
1112
|
+
recordReceipt: (receipt) => hookReceiptLog.record(receipt),
|
|
1113
|
+
});
|
|
1114
|
+
if (!interactive) {
|
|
1115
|
+
for (const issue of userHooks.fileIssues) {
|
|
1116
|
+
process.stderr.write(`[clio:hooks] ${issue.message}\n`);
|
|
1117
|
+
}
|
|
1118
|
+
for (const issue of userHooks.issues) {
|
|
1119
|
+
process.stderr.write(`[clio:hooks] ${issue.source.sourcePath}#${issue.index}: ${issue.issues.join("; ")}\n`);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
termination.onDrain(() => hookReceiptLog.flush());
|
|
1123
|
+
// Autonomy is hot-reloaded for interactive and headless admissions. ACP
|
|
1124
|
+
// server prompts use the snapshot captured at session/new.
|
|
1125
|
+
let activeAcpSessionAutonomy: AutonomyLevel | null = null;
|
|
1126
|
+
// The one effective-autonomy resolution. Every admission surface (registry
|
|
1127
|
+
// admission, dispatch plan provenance, ACP session snapshot) resolves
|
|
1128
|
+
// through these two functions so a fallback added to one surface cannot
|
|
1129
|
+
// silently skip another.
|
|
1130
|
+
const resolveBaselineAutonomy = (): AutonomyLevel =>
|
|
1131
|
+
effectiveSettingsForDispatch?.().autonomy ??
|
|
1132
|
+
options.headless?.autonomy ??
|
|
1133
|
+
(config?.get() ?? readSettings()).autonomy ??
|
|
1134
|
+
"auto-edit";
|
|
1135
|
+
const resolveEffectiveAutonomy = (): AutonomyLevel => activeAcpSessionAutonomy ?? resolveBaselineAutonomy();
|
|
1136
|
+
const middlewareToolChoice = createMiddlewareToolChoiceControl();
|
|
1137
|
+
const toolRegistry = createRegistry({
|
|
1138
|
+
safety,
|
|
1139
|
+
middleware,
|
|
1140
|
+
onMiddlewareEffects: (effects) => middlewareToolChoice.apply(effects),
|
|
1141
|
+
autonomy: resolveEffectiveAutonomy,
|
|
1142
|
+
});
|
|
1143
|
+
const mainPermissionOrigin = acpMode ? "acp-server" : "main";
|
|
1144
|
+
toolRegistry.onPermissionRequired((call, decision, meta) => {
|
|
1145
|
+
bus.emit(BusChannels.PermissionRequested, {
|
|
1146
|
+
tool: call.tool,
|
|
1147
|
+
actionClass: decision.classification.actionClass,
|
|
1148
|
+
requestId: meta.requestId,
|
|
1149
|
+
origin: mainPermissionOrigin,
|
|
1150
|
+
axis: meta.axis,
|
|
1151
|
+
...(decision.kind === "ask" ? { rejection: decision.rejection } : {}),
|
|
1152
|
+
...(decision.policy?.ruleId !== undefined ? { ruleId: decision.policy.ruleId } : {}),
|
|
1153
|
+
...(decision.policy?.policySource !== undefined ? { policySource: decision.policy.policySource } : {}),
|
|
1154
|
+
...(decision.policy?.reasonCode !== undefined ? { reasonCode: decision.policy.reasonCode } : {}),
|
|
1155
|
+
});
|
|
1156
|
+
});
|
|
1157
|
+
let askUserHandler: AskUserHandler | null = null;
|
|
1158
|
+
// ask_user is interactive-only by design: it is a human interview tool and
|
|
1159
|
+
// headless/ACP surfaces have no operator to interview, so the tool is not
|
|
1160
|
+
// registered there at all (documented in `clio-coder run --help`). Skills that
|
|
1161
|
+
// interview fall back to their stated defaults when the tool is absent.
|
|
1162
|
+
const askUserBridge: AskUserHandler = async (questions, invokeOptions) =>
|
|
1163
|
+
askUserHandler ? await askUserHandler(questions, invokeOptions) : cancelledAskUserResult();
|
|
1164
|
+
const userTasks = createUserTasksStore({ cwd: process.cwd() });
|
|
1165
|
+
// One task board per orchestrator: the tasks tool mutates it, the turn-end
|
|
1166
|
+
// open-tasks nudge reads it, and the footer/overlay render it. Keyed on the
|
|
1167
|
+
// current session id so resume/fork/new refolds it from taskLedger entries.
|
|
1168
|
+
// Folding through filterEntriesToActivePath (not the raw file) is what
|
|
1169
|
+
// keeps a /resume from picking up whichever branch happened to write its
|
|
1170
|
+
// taskLedger entry last in file order; readEntries here used to skip that
|
|
1171
|
+
// filter entirely (issue #94).
|
|
1172
|
+
const taskBoard = createTaskBoardStore({
|
|
1173
|
+
getSessionId: () => session?.current()?.id ?? null,
|
|
1174
|
+
readEntries: () => {
|
|
1175
|
+
const meta = session?.current();
|
|
1176
|
+
if (!meta) return [];
|
|
1177
|
+
const leafTurnId = session?.tree(meta.id).leafId ?? undefined;
|
|
1178
|
+
return filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), leafTurnId);
|
|
1179
|
+
},
|
|
1180
|
+
appendEntry: (entry) => {
|
|
1181
|
+
session?.appendEntry(entry);
|
|
1182
|
+
},
|
|
1183
|
+
});
|
|
1184
|
+
// Prime the projection once at composition. Interactive repaint paths use
|
|
1185
|
+
// cachedSnapshot() below, so a first paint can never become a ledger read.
|
|
1186
|
+
taskBoard.snapshot();
|
|
1187
|
+
const decisionBoard = createDecisionBoardStore({
|
|
1188
|
+
getSessionId: () => session?.current()?.id ?? null,
|
|
1189
|
+
readEntries: () => {
|
|
1190
|
+
const meta = session?.current();
|
|
1191
|
+
if (!meta) return [];
|
|
1192
|
+
const leafTurnId = session?.tree(meta.id).leafId ?? undefined;
|
|
1193
|
+
return filterEntriesToActivePath(readSessionEntriesForCompact(meta.id), leafTurnId);
|
|
1194
|
+
},
|
|
1195
|
+
getActiveLeafTurnId: () => {
|
|
1196
|
+
const meta = session?.current();
|
|
1197
|
+
return meta ? (session?.tree(meta.id).leafId ?? null) : null;
|
|
1198
|
+
},
|
|
1199
|
+
appendEntry: (entry) => {
|
|
1200
|
+
if (!session) throw new Error("decision board: no session ledger is available");
|
|
1201
|
+
session.appendEntry(entry);
|
|
1202
|
+
},
|
|
1203
|
+
});
|
|
1204
|
+
// getSessionId alone never notices a /tree switch: it moves the active
|
|
1205
|
+
// append point inside the same session, so the id-keyed cache above kept
|
|
1206
|
+
// showing the abandoned branch's board (issue #94). SessionTurnSwitched is
|
|
1207
|
+
// the signal that switch actually happened; invalidate() forces the next
|
|
1208
|
+
// read to refold from the now-current leaf.
|
|
1209
|
+
bus.on(BusChannels.SessionTurnSwitched, () => {
|
|
1210
|
+
taskBoard.invalidate();
|
|
1211
|
+
// The tree switch is the I/O boundary: refold eagerly here so the 250-ms
|
|
1212
|
+
// island ticker remains a cache-only consumer after changing branches.
|
|
1213
|
+
taskBoard.snapshot();
|
|
1214
|
+
decisionBoard.invalidate();
|
|
1215
|
+
});
|
|
1216
|
+
// Link in-flight dispatch runs to the live board via the ledger's
|
|
1217
|
+
// activeRunIds field: a run is tracked from the moment its child process is
|
|
1218
|
+
// live until it finalizes either way. attach/detach are no-ops when no board
|
|
1219
|
+
// is declared, so an ambient dispatch never forces a board into existence.
|
|
1220
|
+
bus.on(BusChannels.DispatchStarted, (payload) => {
|
|
1221
|
+
if (typeof payload?.runId === "string") taskBoard.attachRun(payload.runId);
|
|
1222
|
+
});
|
|
1223
|
+
bus.on(BusChannels.DispatchCompleted, (payload) => {
|
|
1224
|
+
if (typeof payload?.runId !== "string") return;
|
|
1225
|
+
taskBoard.detachRun(payload.runId);
|
|
1226
|
+
foldDispatchSkillActivations(session, payload);
|
|
1227
|
+
});
|
|
1228
|
+
bus.on(BusChannels.DispatchFailed, (payload) => {
|
|
1229
|
+
if (typeof payload?.runId !== "string") return;
|
|
1230
|
+
taskBoard.detachRun(payload.runId);
|
|
1231
|
+
// A run that failed after loading a skill is the case the operator most
|
|
1232
|
+
// needs the provenance for, and the receipt already carried it here.
|
|
1233
|
+
foldDispatchSkillActivations(session, payload);
|
|
1234
|
+
});
|
|
1235
|
+
// Operator-initiated backgrounding is a TUI affordance: the registry is the
|
|
1236
|
+
// one object the dispatch tool and the keypress both hold.
|
|
1237
|
+
const dispatchBackground = createDispatchBackgroundRegistry();
|
|
1238
|
+
registerAllTools(toolRegistry, {
|
|
1239
|
+
...(session ? { session } : {}),
|
|
1240
|
+
taskBoard,
|
|
1241
|
+
userTasks,
|
|
1242
|
+
dispatch,
|
|
1243
|
+
bus,
|
|
1244
|
+
...(interactive ? { askUser: askUserBridge } : {}),
|
|
1245
|
+
...(agents ? { getAgentCatalog: () => renderAgentCatalogSectionsFromSpecs(agents.listSpecs()).stable } : {}),
|
|
1246
|
+
...(agents ? { getAgentSpecs: () => agents.listSpecs() } : {}),
|
|
1247
|
+
...(agents ? { getAgentRoleFacts: agentRoleFactsResolver((id: string) => agents.getSpec(id)) } : {}),
|
|
1248
|
+
// Same effective-autonomy resolution the registry admission uses, so plan
|
|
1249
|
+
// provenance and compete winner handling agree with the approval surface.
|
|
1250
|
+
getAutonomy: resolveEffectiveAutonomy,
|
|
1251
|
+
...(interactive ? { dispatchBackground } : {}),
|
|
1252
|
+
getCostCeilingUsd: () => result.getContract<SchedulingContract>("scheduling")?.ceilingUsd() ?? 0,
|
|
1253
|
+
getSkillLoaderOptions: () => ({
|
|
1254
|
+
trustProjectCompatRoots: config?.get().skills.trustProjectCompatRoots === true,
|
|
1255
|
+
disableDiscovery: options.noSkills === true || options.headless?.noSkills === true,
|
|
1256
|
+
...(options.skillPaths && options.skillPaths.length > 0
|
|
1257
|
+
? { explicitSkillPaths: options.skillPaths }
|
|
1258
|
+
: options.headless?.skillPaths && options.headless.skillPaths.length > 0
|
|
1259
|
+
? { explicitSkillPaths: options.headless.skillPaths }
|
|
1260
|
+
: {}),
|
|
1261
|
+
}),
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
// Live routing is owned by this process. Seed it once from saved settings
|
|
1265
|
+
// (with any headless CLI overrides baked in); from here on every consumer
|
|
1266
|
+
// reads the effective view — shared snapshot + session routing overlay — so
|
|
1267
|
+
// another process writing settings.yaml can update defaults and the
|
|
1268
|
+
// target catalog but never redirect this session's routing.
|
|
1269
|
+
const sessionRouting = seedSessionRouting(
|
|
1270
|
+
applyHeadlessSettingsOverlay(config?.get() ?? readSettings(), options.headless),
|
|
1271
|
+
);
|
|
1272
|
+
// Non-routing settings a session changed "for this session only" via the
|
|
1273
|
+
// /settings overlay. Layered under the routing overlay in the effective
|
|
1274
|
+
// view, so the live session reflects them immediately while settings.yaml
|
|
1275
|
+
// (the global default for new sessions) stays untouched until the operator
|
|
1276
|
+
// chooses to save globally.
|
|
1277
|
+
const sessionOverrides: SessionOverrides = new Map(
|
|
1278
|
+
options.headless?.autonomy === undefined ? [] : [["autonomy", options.headless.autonomy]],
|
|
1279
|
+
);
|
|
1280
|
+
// The effective view is derived by deep-cloning the saved snapshot, and it
|
|
1281
|
+
// is read on the tool-admission hot path (every call resolves autonomy
|
|
1282
|
+
// through it). Memoize on the two things it depends on: the config
|
|
1283
|
+
// domain's snapshot identity (swapped wholesale on every update and
|
|
1284
|
+
// external hot reload) and a generation counter bumped by every session
|
|
1285
|
+
// routing/override mutation below. Without this, one tool call cost a full
|
|
1286
|
+
// settings structuredClone.
|
|
1287
|
+
let sessionStateGeneration = 0;
|
|
1288
|
+
let cachedSettingsBase: Readonly<ClioSettings> | null = null;
|
|
1289
|
+
let cachedSettingsGeneration = -1;
|
|
1290
|
+
let cachedSettingsView: ClioSettings | null = null;
|
|
1291
|
+
const bumpSessionState = (): void => {
|
|
1292
|
+
sessionStateGeneration += 1;
|
|
1293
|
+
// Child-process seams read the effective setting from the environment,
|
|
1294
|
+
// and the session view may override the saved value.
|
|
1295
|
+
setGitCommitAttributionEnabled(getCurrentSettings().attribution.gitCommits);
|
|
1296
|
+
};
|
|
1297
|
+
const getCurrentSettings = (): ClioSettings => {
|
|
1298
|
+
// Recents live in the data dir (core/recent-models.ts), never in
|
|
1299
|
+
// settings.yaml; consumers that need them call listRecentModels
|
|
1300
|
+
// directly, so an Alt+L pick in another session does not churn the
|
|
1301
|
+
// config watcher here.
|
|
1302
|
+
const base = config?.get();
|
|
1303
|
+
// No config domain (unit tests, degraded boot): readSettings() returns a
|
|
1304
|
+
// fresh object every call, so there is nothing stable to key a cache on.
|
|
1305
|
+
if (base === undefined) return applySessionRouting(applyOverrides(readSettings(), sessionOverrides), sessionRouting);
|
|
1306
|
+
if (
|
|
1307
|
+
cachedSettingsView !== null &&
|
|
1308
|
+
cachedSettingsBase === base &&
|
|
1309
|
+
cachedSettingsGeneration === sessionStateGeneration
|
|
1310
|
+
) {
|
|
1311
|
+
return cachedSettingsView;
|
|
1312
|
+
}
|
|
1313
|
+
const view = applySessionRouting(applyOverrides(base, sessionOverrides), sessionRouting);
|
|
1314
|
+
cachedSettingsBase = base;
|
|
1315
|
+
cachedSettingsGeneration = sessionStateGeneration;
|
|
1316
|
+
cachedSettingsView = view;
|
|
1317
|
+
return view;
|
|
1318
|
+
};
|
|
1319
|
+
effectiveSettingsForDispatch = getCurrentSettings;
|
|
1320
|
+
bumpSessionState();
|
|
1321
|
+
// The config bundle publishes the saved value on reload; a session-scoped
|
|
1322
|
+
// override must win, so re-derive from the session view after it.
|
|
1323
|
+
const unsubscribeCommitAttributionSync = bus.on(BusChannels.ConfigHotReload, () => bumpSessionState());
|
|
1324
|
+
termination.onDrain(() => unsubscribeCommitAttributionSync());
|
|
1325
|
+
const getTaskMemorySeedOffer = (): { source: string; count: number } | null => {
|
|
1326
|
+
return taskMemoryHandoffSeedOffer(process.cwd(), getCurrentSettings().memory.intervention.enabled);
|
|
1327
|
+
};
|
|
1328
|
+
const seedCurrentTaskMemoryFromHandoff = () => {
|
|
1329
|
+
ensureTaskMemorySession();
|
|
1330
|
+
return seedTaskMemoryFromNewestHandoff(
|
|
1331
|
+
taskMemoryBank,
|
|
1332
|
+
process.cwd(),
|
|
1333
|
+
getCurrentSettings().memory.intervention.enabled,
|
|
1334
|
+
);
|
|
1335
|
+
};
|
|
1336
|
+
if (resumedSessionAtBoot) {
|
|
1337
|
+
const offer = getTaskMemorySeedOffer();
|
|
1338
|
+
if (offer && offer.count > 0) {
|
|
1339
|
+
initialNotices.push(
|
|
1340
|
+
`task memory: ${offer.count} handoff entr${offer.count === 1 ? "y" : "ies"} available from ${offer.source}; run /memory seed to import`,
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
for (const warning of agentRoleToolWarnings(providers, getCurrentSettings())) {
|
|
1345
|
+
if (interactive) initialNotices.push(warning);
|
|
1346
|
+
else process.stderr.write(`${warning}\n`);
|
|
1347
|
+
}
|
|
1348
|
+
// Residency protection follows the live effective settings: the models the
|
|
1349
|
+
// operator's config references (orchestrator, worker default/profiles,
|
|
1350
|
+
// target defaults) may never be evicted by another Clio stream, and a
|
|
1351
|
+
// routing change updates the set on the next read.
|
|
1352
|
+
setProtectedModelsProvider(() => protectedResidencyModels(getCurrentSettings()));
|
|
1353
|
+
|
|
1354
|
+
const validatedKeybindings = validateKeybindings((config?.get() ?? readSettings()).keybindings ?? {});
|
|
1355
|
+
const invalidBindings = validatedKeybindings.invalid;
|
|
1356
|
+
if (invalidBindings.length > 0) {
|
|
1357
|
+
const notice = formatInvalidKeybindingNotice(invalidBindings);
|
|
1358
|
+
if (interactive) initialNotices.push(notice);
|
|
1359
|
+
else process.stderr.write(notice);
|
|
1360
|
+
}
|
|
1361
|
+
const platformWarnings = process.stdin.isTTY
|
|
1362
|
+
? detectPlatformKeybindingWarnings(validatedKeybindings.valid, detectTerminalKeySupport(process.env))
|
|
1363
|
+
: [];
|
|
1364
|
+
if (platformWarnings.length > 0) {
|
|
1365
|
+
const notice = formatPlatformKeybindingNotice(platformWarnings);
|
|
1366
|
+
if (interactive) initialNotices.push(notice);
|
|
1367
|
+
else process.stderr.write(notice);
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Locked read-modify-write of saved settings. Routes through the config
|
|
1371
|
+
* contract (which refreshes its snapshot and dispatches change events) when
|
|
1372
|
+
* available, else straight through core updateSettings. Either way the
|
|
1373
|
+
* mutator runs against the freshest on-disk state under the advisory
|
|
1374
|
+
* settings lock, so two processes saving defaults at the same time cannot
|
|
1375
|
+
* interleave and drop each other's patches.
|
|
1376
|
+
*/
|
|
1377
|
+
const persistSavedMutation = (mutator: SettingsMutator): void => {
|
|
1378
|
+
if (config?.update) config.update(mutator);
|
|
1379
|
+
else updateSettings(mutator);
|
|
1380
|
+
bumpSessionState();
|
|
1381
|
+
};
|
|
1382
|
+
/**
|
|
1383
|
+
* Apply a routing change with one consistent scope: it takes effect in this
|
|
1384
|
+
* session immediately and writes through to saved settings as the default
|
|
1385
|
+
* for future sessions. Only the patched fields hit the file, so concurrent
|
|
1386
|
+
* sessions cannot clobber each other's saved defaults wholesale.
|
|
1387
|
+
*/
|
|
1388
|
+
const updateSessionRouting = (patch: RoutingPatch, mutateSaved?: (saved: ClioSettings) => void): void => {
|
|
1389
|
+
applyRoutingPatch(sessionRouting, patch);
|
|
1390
|
+
bumpSessionState();
|
|
1391
|
+
persistSavedMutation((saved) => {
|
|
1392
|
+
mergeRoutingPatchIntoSettings(saved, patch);
|
|
1393
|
+
mutateSaved?.(saved);
|
|
1394
|
+
});
|
|
1395
|
+
};
|
|
1396
|
+
const readAcpSafeSettings = (): AcpSafeSettingsSnapshot => {
|
|
1397
|
+
const settings = getCurrentSettings();
|
|
1398
|
+
return {
|
|
1399
|
+
target: settings.orchestrator.target,
|
|
1400
|
+
model: settings.orchestrator.model,
|
|
1401
|
+
thinkingLevel: settings.orchestrator.thinkingLevel ?? "off",
|
|
1402
|
+
autonomy: settings.autonomy,
|
|
1403
|
+
};
|
|
1404
|
+
};
|
|
1405
|
+
/**
|
|
1406
|
+
* ACP safe settings are one atomic persisted mutation followed by infallible
|
|
1407
|
+
* in-process routing assignment. Persisting first avoids reporting a live
|
|
1408
|
+
* route that failed to become the future-session default.
|
|
1409
|
+
*/
|
|
1410
|
+
const commitAcpSafeSettings = (patch: AcpSafeSettingsPatch): AcpSafeSettingsSnapshot => {
|
|
1411
|
+
const orchestrator: NonNullable<RoutingPatch["orchestrator"]> = {};
|
|
1412
|
+
if (patch["orchestrator.target"] !== undefined) orchestrator.target = patch["orchestrator.target"];
|
|
1413
|
+
if (patch["orchestrator.model"] !== undefined) orchestrator.model = patch["orchestrator.model"];
|
|
1414
|
+
if (patch["orchestrator.thinkingLevel"] !== undefined) {
|
|
1415
|
+
orchestrator.thinkingLevel = patch["orchestrator.thinkingLevel"];
|
|
1416
|
+
}
|
|
1417
|
+
const routingPatch: RoutingPatch | null = Object.keys(orchestrator).length > 0 ? { orchestrator } : null;
|
|
1418
|
+
persistSavedMutation((saved) => {
|
|
1419
|
+
if (routingPatch !== null) mergeRoutingPatchIntoSettings(saved, routingPatch);
|
|
1420
|
+
if (patch.autonomy !== undefined) saved.autonomy = patch.autonomy;
|
|
1421
|
+
});
|
|
1422
|
+
if (routingPatch !== null) {
|
|
1423
|
+
applyRoutingPatch(sessionRouting, routingPatch);
|
|
1424
|
+
bumpSessionState();
|
|
1425
|
+
}
|
|
1426
|
+
return readAcpSafeSettings();
|
|
1427
|
+
};
|
|
1428
|
+
/**
|
|
1429
|
+
* Persist a whole-settings blob coming from the effective view (the
|
|
1430
|
+
* /settings overlay, favorites toggles). Routing edits in the blob are
|
|
1431
|
+
* absorbed into the session state and written through; everything else is
|
|
1432
|
+
* persisted without leaking this session's routing into the saved defaults.
|
|
1433
|
+
*/
|
|
1434
|
+
const applySettingsBlob = (next: ClioSettings): void => {
|
|
1435
|
+
const patch = diffRouting(getCurrentSettings(), next);
|
|
1436
|
+
if (patch) {
|
|
1437
|
+
applyRoutingPatch(sessionRouting, patch);
|
|
1438
|
+
bumpSessionState();
|
|
1439
|
+
}
|
|
1440
|
+
persistSavedMutation((fresh) => {
|
|
1441
|
+
const persisted = structuredClone(next);
|
|
1442
|
+
restoreRoutingFields(persisted, fresh);
|
|
1443
|
+
// A whole-blob write (providers, favorites) must not globalize a
|
|
1444
|
+
// session-only override: restore every overridden leaf from the
|
|
1445
|
+
// fresh file so it stays session-local until explicitly saved.
|
|
1446
|
+
for (const path of sessionOverrides.keys()) setAtPath(persisted, path, getAtPath(fresh, path));
|
|
1447
|
+
if (patch) mergeRoutingPatchIntoSettings(persisted, patch);
|
|
1448
|
+
return persisted;
|
|
1449
|
+
});
|
|
1450
|
+
};
|
|
1451
|
+
/**
|
|
1452
|
+
* Commit a single /settings edit, keyed by its config-path id. `next` is the
|
|
1453
|
+
* effective view with the one leaf already changed.
|
|
1454
|
+
* - scope "session": apply live only. Routing ids feed the routing state;
|
|
1455
|
+
* every other id becomes a session override. settings.yaml is untouched.
|
|
1456
|
+
* - scope "global": apply live and persist just that leaf as the new
|
|
1457
|
+
* default, clearing any prior session override for it.
|
|
1458
|
+
* Restart-required ids (budget.concurrency, runtimePlugins,
|
|
1459
|
+
* terminal.tuiMode, terminal.fullscreenScrollbar) cannot apply
|
|
1460
|
+
* live, so the overlay only offers "global" for them; the file write is what
|
|
1461
|
+
* a later restart picks up.
|
|
1462
|
+
*/
|
|
1463
|
+
const commitSetting = (id: string, next: ClioSettings, scope: "session" | "global"): void => {
|
|
1464
|
+
if (isRoutingPath(id)) {
|
|
1465
|
+
// Build the patch from `next` keyed by the edited id, not by diffing
|
|
1466
|
+
// against the live view: a prior session-only apply already moved the
|
|
1467
|
+
// routing state, so a diff would be empty and the global save would
|
|
1468
|
+
// silently no-op. Only the touched fields are persisted, so concurrent
|
|
1469
|
+
// sessions never clobber each other's saved routing.
|
|
1470
|
+
const patch = routingPatchForId(id, next);
|
|
1471
|
+
if (!patch) return;
|
|
1472
|
+
applyRoutingPatch(sessionRouting, patch);
|
|
1473
|
+
bumpSessionState();
|
|
1474
|
+
if (scope === "global") persistSavedMutation((saved) => mergeRoutingPatchIntoSettings(saved, patch));
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
const value = getAtPath(next, id);
|
|
1478
|
+
if (scope === "session") {
|
|
1479
|
+
sessionOverrides.set(id, value);
|
|
1480
|
+
bumpSessionState();
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
sessionOverrides.delete(id);
|
|
1484
|
+
bumpSessionState();
|
|
1485
|
+
persistSavedMutation((saved) => setAtPath(saved, id, value));
|
|
1486
|
+
};
|
|
1487
|
+
/** Alt+J / Alt+K: step this session's orchestrator through the scope list. */
|
|
1488
|
+
const cycleScopedSession = (direction: "forward" | "backward"): boolean => {
|
|
1489
|
+
const next = advanceScopedTarget(getCurrentSettings(), direction);
|
|
1490
|
+
if (!next) return false;
|
|
1491
|
+
updateSessionRouting({ orchestrator: { target: next.target, model: next.model } });
|
|
1492
|
+
return true;
|
|
1493
|
+
};
|
|
1494
|
+
|
|
1495
|
+
const readCurrentSessionEntries = (): ReadonlyArray<SessionEntry> => {
|
|
1496
|
+
if (session === undefined) return [];
|
|
1497
|
+
const meta = session.current();
|
|
1498
|
+
if (!meta) return [];
|
|
1499
|
+
reconcilePendingProtectedArtifacts(session);
|
|
1500
|
+
return readSessionEntriesForCompact(meta.id);
|
|
1501
|
+
};
|
|
1502
|
+
|
|
1503
|
+
// turn_end assessors, fired by the chat-loop when the final assistant
|
|
1504
|
+
// message of a run lands. Tool-prose first so its hard-block interruption
|
|
1505
|
+
// precedes the finish-contract advisory in effect order.
|
|
1506
|
+
middleware.registerHook(createToolProseRegistration());
|
|
1507
|
+
middleware.registerHook(createTaskNudgeRegistration({ getBoard: () => taskBoard.snapshot() }));
|
|
1508
|
+
middleware.registerHook(createReadOnlyExplorationNudgeRegistration());
|
|
1509
|
+
middleware.registerHook(createUnbackedWorkerClaimRegistration());
|
|
1510
|
+
middleware.registerHook(
|
|
1511
|
+
createDetachedDispatchNudgeRegistration({ getOpenBatches: () => openDetachedBatchViews(dispatch) }),
|
|
1512
|
+
);
|
|
1513
|
+
if (session) {
|
|
1514
|
+
middleware.registerHook(
|
|
1515
|
+
createFinishContractRegistration({
|
|
1516
|
+
// Tail-scoped: the contract only needs the last-user-message window, so
|
|
1517
|
+
// it parses a bounded ledger tail per turn_end rather than the whole
|
|
1518
|
+
// file (which grows unbounded with session length).
|
|
1519
|
+
readSessionEntries: () => {
|
|
1520
|
+
const meta = session.current();
|
|
1521
|
+
return meta ? readRecentSessionEntriesForContract(meta.id) : null;
|
|
1522
|
+
},
|
|
1523
|
+
resolveRigor: () =>
|
|
1524
|
+
resolveRigor({ cwd: process.cwd(), override: parseRigorOverride(process.env.CLIO_CODER_RIGOR) }),
|
|
1525
|
+
recordDecision: (record) => safety.audit.recordCompletionContract?.(record),
|
|
1526
|
+
}),
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
const chat = createChatLoop({
|
|
1531
|
+
getSettings: getCurrentSettings,
|
|
1532
|
+
providers,
|
|
1533
|
+
middleware,
|
|
1534
|
+
middlewareToolChoice,
|
|
1535
|
+
protectedArtifacts: {
|
|
1536
|
+
replace: (state) => protectedArtifactsGuard.replaceState(state),
|
|
1537
|
+
markDegraded: (reason) => protectedArtifactsGuard.markDegraded(reason),
|
|
1538
|
+
},
|
|
1539
|
+
knownTargets: () => new Set(providers.list().map((entry) => entry.target.id)),
|
|
1540
|
+
observability,
|
|
1541
|
+
bus,
|
|
1542
|
+
...(prompts ? { prompts } : {}),
|
|
1543
|
+
...(session ? { session } : {}),
|
|
1544
|
+
getMemorySection: () => {
|
|
1545
|
+
try {
|
|
1546
|
+
const records = loadMemoryRecordsSync(clioDataDir());
|
|
1547
|
+
return buildMemoryPromptSection(records, {
|
|
1548
|
+
activeRepository: canonicalMemoryRepositoryIdentity(process.cwd()),
|
|
1549
|
+
}).section;
|
|
1550
|
+
} catch {
|
|
1551
|
+
return "";
|
|
1552
|
+
}
|
|
1553
|
+
},
|
|
1554
|
+
getTaskMemoryHandoffSource: () => {
|
|
1555
|
+
ensureTaskMemorySession();
|
|
1556
|
+
return renderTaskMemoryHandoffSource(taskMemoryBank.snapshot());
|
|
1557
|
+
},
|
|
1558
|
+
registerDeferredReminderSink: (sink) => {
|
|
1559
|
+
deferredMemoryReminderSink = sink;
|
|
1560
|
+
},
|
|
1561
|
+
onAskUserFinalized: (policy) => {
|
|
1562
|
+
decisionBoard.recordFinalizedInterview(policy);
|
|
1563
|
+
},
|
|
1564
|
+
...(session
|
|
1565
|
+
? {
|
|
1566
|
+
readSessionEntries: readCurrentSessionEntries,
|
|
1567
|
+
autoCompact: createProductionAutoCompact(session, getCurrentSettings, providers),
|
|
1568
|
+
}
|
|
1569
|
+
: {}),
|
|
1570
|
+
toolRegistry,
|
|
1571
|
+
hasAttachedDispatch: () => dispatchBackground.size() > 0,
|
|
1572
|
+
});
|
|
1573
|
+
|
|
1574
|
+
// Coordinated shutdown (SIGINT/SIGTERM, TUI quit) must abort any in-flight
|
|
1575
|
+
// turn before domains stop. The agent abort fans out to every running
|
|
1576
|
+
// tool's AbortSignal, and bash-exec answers it by signalling the tool's
|
|
1577
|
+
// detached process group. Without this, a headless SIGINT exited the CLI
|
|
1578
|
+
// while a running tool's children survived as orphans of init.
|
|
1579
|
+
termination.onDrain(async () => {
|
|
1580
|
+
chat.dispose();
|
|
1581
|
+
// The abort fans out to running tools, but their results still land and
|
|
1582
|
+
// persist through the aborted run's subscribers. Domains (the session
|
|
1583
|
+
// writer among them) stop in the persist phase, strictly after drain, so
|
|
1584
|
+
// awaiting settlement here makes a session append after session stop
|
|
1585
|
+
// impossible by ordering.
|
|
1586
|
+
await chat.whenSettled();
|
|
1587
|
+
});
|
|
1588
|
+
|
|
1589
|
+
// A boot-time resume (CLIO_CODER_RESUME_SESSION_ID) must replay the resumed
|
|
1590
|
+
// session into the chat loop the same way the interactive /resume overlay
|
|
1591
|
+
// does. Without this, the first submit runs with an empty provider context
|
|
1592
|
+
// and parents its user turn at null, appending a second root that silently
|
|
1593
|
+
// abandons the resumed session's active path. The leaf id is restored even
|
|
1594
|
+
// when rebuilding replay messages fails, so parenting stays correct and
|
|
1595
|
+
// only the provider context degrades.
|
|
1596
|
+
if (resumedSessionAtBoot && session) {
|
|
1597
|
+
const resumedMeta = session.current();
|
|
1598
|
+
if (resumedMeta) {
|
|
1599
|
+
let leafTurnId: string | null = null;
|
|
1600
|
+
try {
|
|
1601
|
+
leafTurnId = session.tree(resumedMeta.id).leafId;
|
|
1602
|
+
} catch (err) {
|
|
1603
|
+
bootStderr(
|
|
1604
|
+
`Clio Coder: failed to read resumed session tree ${resumedMeta.id}: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
try {
|
|
1608
|
+
// Scoped to the leaf resume landed on for the same reason the /resume
|
|
1609
|
+
// overlay is (issue #107): with a /tree pin persisted, the file still
|
|
1610
|
+
// holds the abandoned branch after the pinned turn, and replaying it
|
|
1611
|
+
// unfiltered seeds the provider with turns the next append does not
|
|
1612
|
+
// parent onto.
|
|
1613
|
+
chat.resetForSession(
|
|
1614
|
+
leafTurnId,
|
|
1615
|
+
buildReplayAgentMessagesFromTurns(readCurrentSessionEntries(), leafTurnId ? { activeLeafTurnId: leafTurnId } : {}),
|
|
1616
|
+
);
|
|
1617
|
+
} catch (err) {
|
|
1618
|
+
chat.resetForSession(leafTurnId);
|
|
1619
|
+
bootStderr(
|
|
1620
|
+
`Clio Coder: failed to replay resumed session context ${resumedMeta.id}: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
if (options.acp) {
|
|
1627
|
+
// ACP-served sessions get the same routing isolation as interactive
|
|
1628
|
+
// ones, but ACP v1 has no channel for agent-initiated advisory text:
|
|
1629
|
+
// the session/update union (agent_message_chunk, agent_thought_chunk,
|
|
1630
|
+
// tool_call*, plan, …) carries turn content, and notifications outside
|
|
1631
|
+
// an active session/prompt would break strict clients (see the matching
|
|
1632
|
+
// note in src/engine/acp/server.ts). The external-divergence and
|
|
1633
|
+
// target-removed notices therefore go to the session ledger as `custom`
|
|
1634
|
+
// entries, where /resume and session tooling can surface them.
|
|
1635
|
+
// ACP is operatorless too: bound a runaway turn with the shared
|
|
1636
|
+
// interrupt->stop subscriber, the same way the headless path does.
|
|
1637
|
+
const unsubscribeAcpLoopGuardStop = subscribeLoopGuardStop(bus, chat);
|
|
1638
|
+
const unsubscribeAcpRoutingNotices = bus.on(BusChannels.ConfigNextTurn, (payload) => {
|
|
1639
|
+
const evt = payload as { diff?: { nextTurn?: string[] }; settings?: Readonly<ClioSettings> } | null | undefined;
|
|
1640
|
+
if (!evt?.settings || !Array.isArray(evt.diff?.nextTurn)) return;
|
|
1641
|
+
if (!session?.current()) return;
|
|
1642
|
+
const notices = routingChangeNotices(evt.diff.nextTurn, evt.settings, getCurrentSettings());
|
|
1643
|
+
for (const notice of notices) {
|
|
1644
|
+
try {
|
|
1645
|
+
session.appendEntry({
|
|
1646
|
+
kind: "custom",
|
|
1647
|
+
customType: "clio.routing-notice",
|
|
1648
|
+
parentTurnId: null,
|
|
1649
|
+
data: { kind: notice.kind, level: notice.level, text: notice.text },
|
|
1650
|
+
});
|
|
1651
|
+
} catch {
|
|
1652
|
+
// Advisory only; a ledger write failure must not affect the
|
|
1653
|
+
// ACP turn loop.
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
try {
|
|
1658
|
+
const transport = options.acp.transport ?? createStdioServerTransport(options.acp.transportOptions);
|
|
1659
|
+
const code = await serveClioAcpAgent({
|
|
1660
|
+
transport,
|
|
1661
|
+
chat,
|
|
1662
|
+
...(session ? { session } : {}),
|
|
1663
|
+
...(session
|
|
1664
|
+
? {
|
|
1665
|
+
readSessionEntries: readSessionEntriesForCompact,
|
|
1666
|
+
buildReplayMessages: (entries: ReadonlyArray<SessionEntry>, leafTurnId: string | null) =>
|
|
1667
|
+
buildReplayAgentMessagesFromTurns(entries, leafTurnId === null ? {} : { activeLeafTurnId: leafTurnId }),
|
|
1668
|
+
}
|
|
1669
|
+
: {}),
|
|
1670
|
+
providers,
|
|
1671
|
+
settings: {
|
|
1672
|
+
read: readAcpSafeSettings,
|
|
1673
|
+
commit: commitAcpSafeSettings,
|
|
1674
|
+
},
|
|
1675
|
+
toolRegistry,
|
|
1676
|
+
bus,
|
|
1677
|
+
autonomy: resolveBaselineAutonomy,
|
|
1678
|
+
routing: () => {
|
|
1679
|
+
const settings = getCurrentSettings();
|
|
1680
|
+
return {
|
|
1681
|
+
target: settings.orchestrator.target,
|
|
1682
|
+
model: settings.orchestrator.model,
|
|
1683
|
+
};
|
|
1684
|
+
},
|
|
1685
|
+
onActiveSessionAutonomyChange: (level) => {
|
|
1686
|
+
activeAcpSessionAutonomy = level;
|
|
1687
|
+
},
|
|
1688
|
+
cwd: process.cwd(),
|
|
1689
|
+
version: getVersionInfo().clio,
|
|
1690
|
+
// Stdout belongs to JSON-RPC. Text this process did not author, such
|
|
1691
|
+
// as a provider's failure body, is kept off the wire and written to
|
|
1692
|
+
// the unstructured stderr tail instead.
|
|
1693
|
+
diagnostics: (line) => {
|
|
1694
|
+
process.stderr.write(`[clio:acp] ${line}\n`);
|
|
1695
|
+
},
|
|
1696
|
+
permissionTimeoutMs:
|
|
1697
|
+
options.acp.permissionTimeoutMs ??
|
|
1698
|
+
config?.get().delegation.defaults.permissionTimeoutMs ??
|
|
1699
|
+
DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS,
|
|
1700
|
+
});
|
|
1701
|
+
// Same ordering the termination drain hook relies on: an aborted turn's
|
|
1702
|
+
// tool results still land and persist through the run's subscribers,
|
|
1703
|
+
// and the session writer stops in result.stop() below. Awaiting
|
|
1704
|
+
// settlement here makes a session append after session stop impossible
|
|
1705
|
+
// by ordering rather than by timing.
|
|
1706
|
+
await chat.whenSettled();
|
|
1707
|
+
chat.dispose();
|
|
1708
|
+
await dispatch.drain();
|
|
1709
|
+
await result.stop();
|
|
1710
|
+
return { exitCode: code, bootTimeMs: timer.snapshot().totalMs };
|
|
1711
|
+
} finally {
|
|
1712
|
+
unsubscribeAcpRoutingNotices();
|
|
1713
|
+
unsubscribeAcpLoopGuardStop();
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
if (options.headless) {
|
|
1718
|
+
// Operatorless: there is no TUI subscriber to turn a loop-guard interrupt
|
|
1719
|
+
// into a run stop, so a degenerate local model would spin until an
|
|
1720
|
+
// external timeout (each call blocked, the agent loop never aborted). Wire
|
|
1721
|
+
// the shared interrupt->stop subscriber so the run ends with the same
|
|
1722
|
+
// durable closing turn the interactive surface produces.
|
|
1723
|
+
const unsubscribeLoopGuardStop = subscribeLoopGuardStop(bus, chat);
|
|
1724
|
+
const headlessPermissionReason = HEADLESS_PERMISSION_DENIED_REASON;
|
|
1725
|
+
const unsubscribeHeadlessPermission = toolRegistry.onPermissionRequired((call, decision, meta) => {
|
|
1726
|
+
bus.emit(BusChannels.PermissionResolved, {
|
|
1727
|
+
status: "denied",
|
|
1728
|
+
requestId: meta.requestId,
|
|
1729
|
+
origin: "main",
|
|
1730
|
+
decidedBy: "policy:no-operator",
|
|
1731
|
+
tool: call.tool,
|
|
1732
|
+
actionClass: decision.classification.actionClass,
|
|
1733
|
+
reason: headlessPermissionReason,
|
|
1734
|
+
requestedBy: "headless",
|
|
1735
|
+
});
|
|
1736
|
+
toolRegistry.cancelParkedCalls(headlessPermissionReason);
|
|
1737
|
+
});
|
|
1738
|
+
try {
|
|
1739
|
+
const parsedSkillRequest = resources?.parsePendingSkillRequests(options.headless.prompt, process.cwd()) ?? {
|
|
1740
|
+
text: options.headless.prompt,
|
|
1741
|
+
pendingSkillRequests: [],
|
|
1742
|
+
};
|
|
1743
|
+
const promptExpansion = resources?.expandPromptTemplate(parsedSkillRequest.text, process.cwd());
|
|
1744
|
+
// A prompt named as `/name` is a request for that template. When the
|
|
1745
|
+
// template refuses, the run says why and stops; sending the literal
|
|
1746
|
+
// `/name` on to the model spends a turn answering a command it cannot
|
|
1747
|
+
// run, and the operator never sees the refusal.
|
|
1748
|
+
if (promptExpansion?.expanded === false && promptExpansion.refusal) {
|
|
1749
|
+
process.stderr.write(`clio-coder: ${promptExpansion.refusal.message}\n`);
|
|
1750
|
+
await termination.shutdown(1);
|
|
1751
|
+
return { exitCode: 1, bootTimeMs: timer.snapshot().totalMs };
|
|
1752
|
+
}
|
|
1753
|
+
const fileExpansion = await expandInlineFileReferencesAsync(
|
|
1754
|
+
promptExpansion?.expanded ? promptExpansion.text : parsedSkillRequest.text,
|
|
1755
|
+
{
|
|
1756
|
+
cwd: process.cwd(),
|
|
1757
|
+
includeImages: true,
|
|
1758
|
+
missing: "leave",
|
|
1759
|
+
},
|
|
1760
|
+
);
|
|
1761
|
+
const images = [...(options.headless.images ?? []), ...fileExpansion.images];
|
|
1762
|
+
const workingContextPaths = [...(options.headless.workingContextPaths ?? []), ...fileExpansion.referencedPaths];
|
|
1763
|
+
const code = await runHeadlessMainAgent(chat, {
|
|
1764
|
+
prompt: fileExpansion.text,
|
|
1765
|
+
...(images.length > 0 ? { images } : {}),
|
|
1766
|
+
...(workingContextPaths.length > 0 ? { workingContextPaths } : {}),
|
|
1767
|
+
...(options.headless.sampling ? { sampling: options.headless.sampling } : {}),
|
|
1768
|
+
...(parsedSkillRequest.pendingSkillRequests.length > 0
|
|
1769
|
+
? { pendingSkillRequests: parsedSkillRequest.pendingSkillRequests }
|
|
1770
|
+
: {}),
|
|
1771
|
+
mode: options.headless.mode ?? "text",
|
|
1772
|
+
...(options.headless.jsonEvents ? { jsonEvents: options.headless.jsonEvents } : {}),
|
|
1773
|
+
...(options.headless.steerChannel ? { steerChannel: options.headless.steerChannel } : {}),
|
|
1774
|
+
getSessionHeader: () => printJsonSessionHeader(session?.current() ?? null),
|
|
1775
|
+
});
|
|
1776
|
+
await termination.shutdown(code);
|
|
1777
|
+
return { exitCode: code, bootTimeMs: timer.snapshot().totalMs };
|
|
1778
|
+
} finally {
|
|
1779
|
+
unsubscribeHeadlessPermission();
|
|
1780
|
+
unsubscribeLoopGuardStop();
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
if (options.terminalLease) {
|
|
1785
|
+
for (const diagnostic of options.terminalLease.takeDiagnostics()) {
|
|
1786
|
+
const text = diagnostic.text.trimEnd();
|
|
1787
|
+
if (text.length > 0) initialNotices.push(text);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
await startInteractive({
|
|
1792
|
+
bus,
|
|
1793
|
+
providers,
|
|
1794
|
+
dispatch,
|
|
1795
|
+
...(agents ? { agents } : {}),
|
|
1796
|
+
observability,
|
|
1797
|
+
chat,
|
|
1798
|
+
...(options.terminalLease
|
|
1799
|
+
? {
|
|
1800
|
+
terminalLease: options.terminalLease,
|
|
1801
|
+
onHydratedFrameCommit: () => timer.mark("Stage 1 hydration"),
|
|
1802
|
+
}
|
|
1803
|
+
: { onFirstFrameCommit: () => timer.mark("first TUI paint") }),
|
|
1804
|
+
...(initialNotices.length > 0 ? { initialNotices } : {}),
|
|
1805
|
+
...(resources ? { resources } : {}),
|
|
1806
|
+
...(extensions ? { extensions } : {}),
|
|
1807
|
+
...(interop ? { interop } : {}),
|
|
1808
|
+
...(share ? { share } : {}),
|
|
1809
|
+
toolRegistry,
|
|
1810
|
+
...(session ? { session } : {}),
|
|
1811
|
+
...(session ? { readSessionEntries: readCurrentSessionEntries } : {}),
|
|
1812
|
+
getTaskBoard: () => taskBoard.cachedSnapshot(),
|
|
1813
|
+
getDecisionBoard: () => decisionBoard.snapshot(),
|
|
1814
|
+
supersedeDecision: (interviewId, key, correction) => decisionBoard.supersede(interviewId, key, correction),
|
|
1815
|
+
userTasks,
|
|
1816
|
+
getTaskMemoryStatus: () => {
|
|
1817
|
+
ensureTaskMemorySession();
|
|
1818
|
+
const settings = getCurrentSettings();
|
|
1819
|
+
const bank = taskMemoryBank.snapshot();
|
|
1820
|
+
return {
|
|
1821
|
+
enabled: settings.memory.intervention.enabled,
|
|
1822
|
+
tier: settings.background.target && settings.background.model ? "llm" : "rules",
|
|
1823
|
+
size: taskMemoryBankSize(bank),
|
|
1824
|
+
lastDecision: memoryIntervention.lastDecision(),
|
|
1825
|
+
bank,
|
|
1826
|
+
activity: memoryIntervention.recentActivity(),
|
|
1827
|
+
stepInFlight: memoryIntervention.stepInFlight(),
|
|
1828
|
+
};
|
|
1829
|
+
},
|
|
1830
|
+
getTaskMemorySeedOffer,
|
|
1831
|
+
seedTaskMemory: seedCurrentTaskMemoryFromHandoff,
|
|
1832
|
+
stateDir: clioStateDir(),
|
|
1833
|
+
dataDir: clioDataDir(),
|
|
1834
|
+
registerAskUserHandler: (handler) => {
|
|
1835
|
+
askUserHandler = handler;
|
|
1836
|
+
return () => {
|
|
1837
|
+
if (askUserHandler === handler) askUserHandler = null;
|
|
1838
|
+
};
|
|
1839
|
+
},
|
|
1840
|
+
getSettings: getCurrentSettings,
|
|
1841
|
+
getFleetNodes: () => result.getContract<SchedulingContract>("scheduling")?.fleet?.list() ?? [],
|
|
1842
|
+
onBackgroundDispatch: () => dispatchBackground.backgroundNewest(),
|
|
1843
|
+
...(session ? { getSessionId: () => session.current()?.id ?? null } : {}),
|
|
1844
|
+
...(contextDomain
|
|
1845
|
+
? {
|
|
1846
|
+
getContextState: (cwd?: string) => contextDomain.contextState(cwd),
|
|
1847
|
+
onInit: async (
|
|
1848
|
+
options: {
|
|
1849
|
+
preview?: boolean;
|
|
1850
|
+
adopt?: boolean;
|
|
1851
|
+
applyClioMd?: boolean;
|
|
1852
|
+
rewriteClioMd?: boolean;
|
|
1853
|
+
proposeClioMd?: boolean;
|
|
1854
|
+
includeGlobalImports?: boolean;
|
|
1855
|
+
heuristic?: boolean;
|
|
1856
|
+
},
|
|
1857
|
+
runIo?: RunIo,
|
|
1858
|
+
) => {
|
|
1859
|
+
// Interactive context-init explores the repo with the configured target by
|
|
1860
|
+
// default, grounded in the freshly built codewiki, and falls back to the
|
|
1861
|
+
// deterministic heuristic when no target is reachable. --heuristic and
|
|
1862
|
+
// --preview skip model generation.
|
|
1863
|
+
const useModel = options.heuristic !== true && options.preview !== true;
|
|
1864
|
+
const bootstrapOptions = bootstrapInputFromInitOptions(options);
|
|
1865
|
+
await contextDomain.runBootstrap({
|
|
1866
|
+
cwd: process.cwd(),
|
|
1867
|
+
confirmGitignore: () => true,
|
|
1868
|
+
adopt: options.adopt === true,
|
|
1869
|
+
...bootstrapOptions,
|
|
1870
|
+
...(useModel
|
|
1871
|
+
? {
|
|
1872
|
+
generate: modelBootstrapGenerate({
|
|
1873
|
+
dispatch,
|
|
1874
|
+
resolveRoute: () => {
|
|
1875
|
+
if (!config) throw new Error("context-bootstrap configuration unavailable");
|
|
1876
|
+
return resolveBootstrapRoute(config.get());
|
|
1877
|
+
},
|
|
1878
|
+
// Names the agent that actually ran and reports the throw as
|
|
1879
|
+
// what it is. "Scout unavailable" was wrong twice over: the
|
|
1880
|
+
// agent is context-bootstrap, and the same line was printed
|
|
1881
|
+
// for a worker that failed, a worker whose answer the loop
|
|
1882
|
+
// guard removed, and a worker that succeeded and whose
|
|
1883
|
+
// payload the reader then refused.
|
|
1884
|
+
onFallback: (err, mode) =>
|
|
1885
|
+
runIo?.stderr(
|
|
1886
|
+
`context init: context-bootstrap did not produce a handbook, using ${mode === "existing" ? "the existing CLIO-CODER.md" : "the heuristic writer"} (${err.message})\n`,
|
|
1887
|
+
),
|
|
1888
|
+
}),
|
|
1889
|
+
modelId: "configured-clio-target",
|
|
1890
|
+
}
|
|
1891
|
+
: {}),
|
|
1892
|
+
});
|
|
1893
|
+
},
|
|
1894
|
+
onContextClear: async (options: { all?: boolean; confirmed?: boolean; confirmedAll?: boolean }) => {
|
|
1895
|
+
await contextDomain.runContextClear({
|
|
1896
|
+
cwd: process.cwd(),
|
|
1897
|
+
all: options.all === true,
|
|
1898
|
+
io: {
|
|
1899
|
+
stdout: (s) => process.stdout.write(s),
|
|
1900
|
+
stderr: (s) => process.stderr.write(s),
|
|
1901
|
+
},
|
|
1902
|
+
confirmContext: () => options.confirmed === true,
|
|
1903
|
+
confirmAll: () => options.confirmedAll === true,
|
|
1904
|
+
});
|
|
1905
|
+
},
|
|
1906
|
+
onContextRefresh: async () => {
|
|
1907
|
+
await contextDomain.runContextRefresh({
|
|
1908
|
+
cwd: process.cwd(),
|
|
1909
|
+
io: {
|
|
1910
|
+
stdout: (s) => process.stdout.write(s),
|
|
1911
|
+
stderr: (s) => process.stderr.write(s),
|
|
1912
|
+
},
|
|
1913
|
+
});
|
|
1914
|
+
},
|
|
1915
|
+
}
|
|
1916
|
+
: {}),
|
|
1917
|
+
onSetThinkingLevel: (level) => {
|
|
1918
|
+
const current = getCurrentSettings();
|
|
1919
|
+
const nextLevel =
|
|
1920
|
+
resolveModelRuntimeCapabilitiesForProviders(
|
|
1921
|
+
providers,
|
|
1922
|
+
current.orchestrator.target,
|
|
1923
|
+
current.orchestrator.model,
|
|
1924
|
+
level,
|
|
1925
|
+
)?.thinking.effectiveLevel ?? "off";
|
|
1926
|
+
updateSessionRouting({ orchestrator: { thinkingLevel: nextLevel } });
|
|
1927
|
+
},
|
|
1928
|
+
onCycleThinking: () => {
|
|
1929
|
+
const current = getCurrentSettings();
|
|
1930
|
+
const thinking = resolveModelRuntimeCapabilitiesForProviders(
|
|
1931
|
+
providers,
|
|
1932
|
+
current.orchestrator.target,
|
|
1933
|
+
current.orchestrator.model,
|
|
1934
|
+
current.orchestrator.thinkingLevel ?? "off",
|
|
1935
|
+
)?.thinking;
|
|
1936
|
+
const effectiveAvailable = thinking?.supportedLevels ?? (["off"] as ThinkingLevel[]);
|
|
1937
|
+
const nextLevel = advanceThinkingLevel(
|
|
1938
|
+
thinking?.effectiveLevel ?? current.orchestrator.thinkingLevel ?? "off",
|
|
1939
|
+
effectiveAvailable,
|
|
1940
|
+
);
|
|
1941
|
+
updateSessionRouting({ orchestrator: { thinkingLevel: nextLevel } });
|
|
1942
|
+
},
|
|
1943
|
+
onSelectModel: ({ target, model }) => {
|
|
1944
|
+
const registry = getRuntimeRegistry();
|
|
1945
|
+
const settings = getCurrentSettings();
|
|
1946
|
+
const descriptor = settings.targets.find((e) => e.id === target);
|
|
1947
|
+
if (descriptor) {
|
|
1948
|
+
const runtime = registry.get(descriptor.runtime);
|
|
1949
|
+
if (!runtime) {
|
|
1950
|
+
throw new Error(
|
|
1951
|
+
`cannot use target '${target}' as orchestrator target because runtime '${descriptor.runtime}' is not registered`,
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
if (!isOrchestratorEligibleRuntime(runtime)) {
|
|
1955
|
+
throw new Error(
|
|
1956
|
+
`cannot use target '${target}' as orchestrator target because runtime '${runtime.id}' is not an HTTP/native runtime`,
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
updateSessionRouting({ orchestrator: { target, model } });
|
|
1961
|
+
rememberRecentModel(`${target}/${model}`, getCurrentSettings().modelSelector.recentLimit);
|
|
1962
|
+
},
|
|
1963
|
+
writeSettings: (next) => applySettingsBlob(next),
|
|
1964
|
+
commitSetting: (id, next, scope) => commitSetting(id, next, scope),
|
|
1965
|
+
...(session
|
|
1966
|
+
? {
|
|
1967
|
+
onResumeSession: (sessionId) => {
|
|
1968
|
+
try {
|
|
1969
|
+
const previousSessionId = session.current()?.id ?? null;
|
|
1970
|
+
session.resume(sessionId);
|
|
1971
|
+
taskBoard.snapshot();
|
|
1972
|
+
if (previousSessionId !== sessionId) ensureTaskMemorySession();
|
|
1973
|
+
} catch (err) {
|
|
1974
|
+
process.stderr.write(
|
|
1975
|
+
`[/resume] failed to resume ${sessionId}: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1978
|
+
},
|
|
1979
|
+
onNewSession: () => {
|
|
1980
|
+
const settings = getCurrentSettings();
|
|
1981
|
+
const input: { cwd: string; target?: string; model?: string } = { cwd: process.cwd() };
|
|
1982
|
+
if (settings.orchestrator.target) input.target = settings.orchestrator.target;
|
|
1983
|
+
if (settings.orchestrator.model) input.model = settings.orchestrator.model;
|
|
1984
|
+
session.create(input);
|
|
1985
|
+
taskBoard.snapshot();
|
|
1986
|
+
ensureTaskMemorySession();
|
|
1987
|
+
},
|
|
1988
|
+
onForkSession: (parentTurnId) => {
|
|
1989
|
+
try {
|
|
1990
|
+
session.fork(parentTurnId);
|
|
1991
|
+
taskBoard.snapshot();
|
|
1992
|
+
ensureTaskMemorySession();
|
|
1993
|
+
} catch (err) {
|
|
1994
|
+
process.stderr.write(
|
|
1995
|
+
`[/fork] failed at turn ${parentTurnId}: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
},
|
|
1999
|
+
onCompact: async (instructions) => {
|
|
2000
|
+
await chat.compact(instructions);
|
|
2001
|
+
},
|
|
2002
|
+
}
|
|
2003
|
+
: {}),
|
|
2004
|
+
onCycleScopedModelForward: () => cycleScopedSession("forward"),
|
|
2005
|
+
onCycleScopedModelBackward: () => cycleScopedSession("backward"),
|
|
2006
|
+
onShutdown: async () => {
|
|
2007
|
+
await termination.shutdown(0);
|
|
2008
|
+
},
|
|
2009
|
+
});
|
|
2010
|
+
return { exitCode: 0, bootTimeMs: timer.snapshot().totalMs };
|
|
2011
|
+
}
|