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