@iowarp/clio-coder 0.3.7 → 0.3.9
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 +80 -0
- package/README.md +13 -4
- package/dist/{acp-SK4MD6MM.js → acp-7LOELQFP.js} +13 -13
- package/dist/{agents-2FN2K6ME.js → agents-FIBG2SHA.js} +41 -37
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-QIYZWM5I.js → auth-OI4LIH2I.js} +31 -24
- package/dist/builtins-AD25UL3C.js +17 -0
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-EOOQZZDE.js → chunk-3DUR4WUA.js} +19 -19
- package/dist/{chunk-WHJYKASB.js → chunk-3MRC2YSQ.js} +2 -2
- package/dist/{chunk-EBEFWSGL.js → chunk-3UUY7R3Z.js} +14 -10
- package/dist/{chunk-LADCF22A.js → chunk-3V5AYSEQ.js} +113 -54
- package/dist/{chunk-BMWK7ZIZ.js → chunk-465CC7FK.js} +16 -13
- package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
- package/dist/{chunk-CEYBNUGC.js → chunk-4H6ULJ3H.js} +378 -36
- package/dist/{chunk-YTYFXUI3.js → chunk-4LJX2PUC.js} +9 -9
- package/dist/{chunk-DOOEX22V.js → chunk-56KB5IJP.js} +5 -5
- package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-5HFBWUMU.js} +23 -11
- package/dist/{chunk-5UJ6ECTS.js → chunk-5PVQ4SRS.js} +80 -8
- package/dist/{chunk-ZWLZP4ZT.js → chunk-5QKCQQ3E.js} +359 -17
- package/dist/{chunk-6M7VS3J3.js → chunk-5T7RBWN2.js} +111 -5
- package/dist/chunk-774ILSRL.js +172 -0
- package/dist/chunk-7C6RYZGQ.js +391 -0
- package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-C4JBQ5SR.js → chunk-AD7Y7STJ.js} +6 -6
- package/dist/{chunk-GEYXPTRF.js → chunk-AEYBF3TB.js} +33 -12
- package/dist/{chunk-2SFS6XQE.js → chunk-AMKHQW3C.js} +3 -2
- package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
- package/dist/{chunk-MXI6J5JF.js → chunk-B5XRQOLB.js} +10 -10
- package/dist/{chunk-X2KV5FXT.js → chunk-BVDVID7E.js} +2 -2
- package/dist/{chunk-JNXPYBB4.js → chunk-CA42X6KT.js} +3 -3
- package/dist/{chunk-VREKEFLL.js → chunk-D73KXYPF.js} +3 -3
- package/dist/{chunk-JTSEDYVQ.js → chunk-DG4M6ZUE.js} +7 -7
- package/dist/{chunk-DQA7QLMD.js → chunk-EBOC7MT3.js} +10 -25
- package/dist/{chunk-KZ2H5X4G.js → chunk-ECUO3KDP.js} +129 -14
- package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
- package/dist/{chunk-YD734TPH.js → chunk-FALJGAWU.js} +2 -2
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-XEGB6BCN.js → chunk-GAYUJ7LE.js} +68 -14
- package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
- package/dist/{chunk-IR4CFBFN.js → chunk-HAY4ZE2P.js} +12 -12
- package/dist/{chunk-UVDSQ6LW.js → chunk-HCBCAYZU.js} +74 -147
- package/dist/{chunk-4DWFMQDR.js → chunk-HJB5IUKP.js} +89 -145
- package/dist/{chunk-M4AKACEO.js → chunk-HKO36JWF.js} +33 -5
- package/dist/{chunk-KCMKRQX4.js → chunk-HPCTNZM2.js} +45 -82
- package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
- package/dist/chunk-JEQQR47K.js +3025 -0
- package/dist/{chunk-FO5ZOVUY.js → chunk-KV2AOLDF.js} +27 -7
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/{chunk-6TUKSZVF.js → chunk-LXPJXFM5.js} +11 -11
- package/dist/{chunk-VQNODYQ4.js → chunk-MIX5N5AC.js} +488 -3668
- package/dist/chunk-MLOK6ZOS.js +2888 -0
- package/dist/{chunk-ZZMN5OM4.js → chunk-MV2VUEJC.js} +2 -2
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/{chunk-OBMAI2DP.js → chunk-N3PBVRTZ.js} +12 -388
- package/dist/{chunk-WJHBC77E.js → chunk-N5XKWMDW.js} +17 -7
- package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
- package/dist/{chunk-UFQ3F4FW.js → chunk-NQ6UCCOD.js} +4 -4
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-DMD2AGVS.js → chunk-NZU6YDNV.js} +20 -18
- package/dist/{chunk-WHGPSPT5.js → chunk-O6I4CIEU.js} +151 -13
- package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
- package/dist/{chunk-PD3MESLB.js → chunk-P3JGPQFL.js} +4 -4
- package/dist/{chunk-UHXRNZ2J.js → chunk-PNY46YEY.js} +23 -6
- package/dist/{chunk-THKY7CD7.js → chunk-PZ4I4JE2.js} +134 -29
- package/dist/{chunk-SROCI7ZU.js → chunk-QQ7EKM72.js} +5 -5
- package/dist/{chunk-QCTRSGHQ.js → chunk-R7LNVMCS.js} +91 -53
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-OB5HIGJY.js → chunk-RKRLDWD3.js} +4 -1
- package/dist/{chunk-DJNLUABN.js → chunk-S4COXYBG.js} +588 -32
- package/dist/{chunk-3HAPLH5M.js → chunk-T3Z6VAAF.js} +172 -11
- package/dist/{chunk-FOT2FX5J.js → chunk-TD7UE2L5.js} +12 -10
- package/dist/{chunk-UUANF5CR.js → chunk-TEO2TLVN.js} +856 -967
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-EELBMBT6.js → chunk-VKBMFOYV.js} +74 -15
- package/dist/chunk-VO2LKSTM.js +165 -0
- package/dist/{chunk-5C77SEEY.js → chunk-VPTUJU4P.js} +3 -3
- package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
- package/dist/{chunk-J7PIKKWC.js → chunk-WXCJ7VME.js} +8 -8
- package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-AB4XIIVB.js → chunk-YKOFT37S.js} +6 -6
- package/dist/chunk-YSEHGPCT.js +127 -0
- package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
- package/dist/cli/index.js +32 -32
- package/dist/{clio-WBVQEBKO.js → clio-LT5V7SSZ.js} +9 -9
- package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
- package/dist/{config-TRBL3RCF.js → config-RXS5T3JT.js} +98 -65
- package/dist/{configure-OLCVPHNM.js → configure-2WYWSCSD.js} +26 -22
- package/dist/{context-MJIJ6GOX.js → context-I3BTOTCS.js} +12 -12
- package/dist/{context-XEWE3MOJ.js → context-MVOORGMF.js} +54 -47
- package/dist/{context-WFPKQSM6.js → context-PALKKQYL.js} +28 -28
- package/dist/{context-clear-KNOS2JPB.js → context-clear-N2WOYZ2K.js} +53 -46
- package/dist/{context-index-SSR5ECNE.js → context-index-HNG3MOME.js} +6 -6
- package/dist/{context-working-set-EUXAZI6N.js → context-working-set-MIEVECVZ.js} +17 -18
- package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-VVA4SRRH.js} +90 -61
- package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
- package/dist/doctor-TWBWFK5V.js +165 -0
- package/dist/eval-IJ5VEZDJ.js +4483 -0
- package/dist/{evidence-JZNBUOQZ.js → evidence-L5APPXNV.js} +68 -61
- package/dist/{evolve-FJVC4KKI.js → evolve-RGNKFJ52.js} +47 -40
- package/dist/{extensions-IQL36S7K.js → extensions-7WYWUX5A.js} +13 -7
- package/dist/{fleet-BDKYJFCP.js → fleet-6CNVBZZP.js} +113 -76
- package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-L2SXSYEI.js} +10 -10
- package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-2J3OOIPO.js} +17 -15
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-CZRJ4JP5.js} +5 -6
- package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-C5RI6DP7.js} +20 -19
- package/dist/{init-LQUB5COQ.js → init-VBN2ACVA.js} +70 -63
- package/dist/{library-NJAHIGG4.js → library-JHGUMLY2.js} +22 -20
- package/dist/{memory-OG6HOYKM.js → memory-K4OQIYWG.js} +49 -42
- package/dist/{models-5ZG5XY7J.js → models-2NCZUWDD.js} +35 -29
- package/dist/{monitor-TJ7AMTGB.js → monitor-MMVTJABD.js} +64 -45
- package/dist/{orchestrator-WZYB54DM.js → orchestrator-ZKBPCHW6.js} +1971 -520
- package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-PXQT45IY.js → reset-DD5JGOY3.js} +11 -11
- package/dist/{run-FQ74YF62.js → run-QEGNX7FL.js} +89 -83
- package/dist/{share-FW7SVCL3.js → share-JKD3BQMW.js} +20 -18
- package/dist/{skills-7E7IRB3R.js → skills-LMQIKDOZ.js} +23 -21
- package/dist/{skills-eval-LI75W6OK.js → skills-eval-I7X2774U.js} +59 -52
- package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
- package/dist/support-I7LOJLIF.js +38 -0
- package/dist/{targets-4CIFKCTW.js → targets-RUSR6B5Z.js} +77 -42
- package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-QYVORFR4.js} +6 -4
- package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
- package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
- package/dist/{upgrade-K2HVIVMQ.js → upgrade-XANW3FXB.js} +29 -26
- package/dist/{usage-GTZELZQX.js → usage-4H7ZRXQT.js} +110 -61
- package/dist/{verifiers-RLAHT27O.js → verifiers-UZXNBZEB.js} +13 -13
- package/dist/{verify-BX3BRKH5.js → verify-BVKWTNDL.js} +9 -9
- package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-MY7WV2QI.js} +76 -69
- package/dist/worker/entry.js +69 -66
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +11 -5
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +23 -2
- package/docs/commands-and-modes.md +2 -2
- package/docs/configuration-and-targets.md +37 -5
- package/docs/context-engine.md +63 -4
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +2 -0
- package/docs/eval-runner.md +262 -11
- package/docs/evals-internal.md +72 -2
- package/docs/evidence-and-memory.md +77 -12
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +3 -1
- package/docs/fleet-dispatch.md +34 -9
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +1 -1
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +54 -3
- package/docs/proactive-memory.md +127 -14
- package/docs/prompt-envelope-and-tools.md +22 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +60 -41
- package/docs/safety-model.md +1 -1
- package/docs/scientific-validation.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +1 -1
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +87 -0
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +2 -2
- package/src/cli/agents.ts +1 -1
- package/src/cli/argv.ts +5 -0
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/configure.ts +107 -23
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor.ts +7 -1
- package/src/cli/eval.ts +80 -16
- package/src/cli/evidence.ts +30 -25
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet-preflight.ts +2 -12
- package/src/cli/fleet.ts +32 -3
- package/src/cli/shared.ts +1 -0
- package/src/cli/targets.ts +45 -11
- package/src/cli/trace.ts +63 -4
- package/src/cli/usage.ts +63 -14
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +54 -1
- package/src/core/cache-telemetry.ts +42 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +18 -0
- package/src/core/defaults.ts +36 -6
- package/src/core/endpoint-key.ts +27 -0
- package/src/core/path-boundary.ts +100 -0
- package/src/core/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +36 -2
- package/src/domains/agents/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +30 -12
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +128 -17
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/admission.ts +40 -3
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- package/src/domains/dispatch/capacity-lease.ts +98 -9
- package/src/domains/dispatch/contract.ts +26 -1
- package/src/domains/dispatch/delegation-plan.ts +2 -5
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/execution-role.ts +9 -1
- package/src/domains/dispatch/extension.ts +309 -96
- package/src/domains/dispatch/fleet-run.ts +78 -4
- package/src/domains/dispatch/gate-role-prompts.ts +38 -0
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +6 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +84 -8
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +2 -1
- package/src/domains/dispatch/reservation-store.ts +116 -8
- package/src/domains/dispatch/state.ts +4 -0
- package/src/domains/dispatch/types.ts +14 -7
- package/src/domains/dispatch/validation.ts +6 -3
- package/src/domains/dispatch/worker-spawn.ts +25 -11
- package/src/domains/dispatch/write-boundary-enforcer.ts +62 -0
- package/src/domains/dispatch/write-boundary.ts +262 -22
- package/src/domains/eval/artifacts/store.ts +62 -0
- package/src/domains/eval/compare/behavioral.ts +224 -0
- package/src/domains/eval/compare/compare.ts +355 -2
- package/src/domains/eval/compare/envelope.ts +128 -0
- package/src/domains/eval/compare/gates.ts +24 -6
- package/src/domains/eval/compare/thresholds.ts +30 -3
- package/src/domains/eval/execution-provenance.ts +240 -0
- package/src/domains/eval/metrics/aggregate.ts +136 -0
- package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
- package/src/domains/eval/metrics/evidence.ts +79 -2
- package/src/domains/eval/metrics/tracked.ts +413 -0
- package/src/domains/eval/provenance.ts +117 -0
- package/src/domains/eval/reports/comparison.ts +128 -0
- package/src/domains/eval/reports/junit.ts +17 -3
- package/src/domains/eval/reports/markdown.ts +3 -3
- package/src/domains/eval/reports/text.ts +14 -0
- package/src/domains/eval/run-compare.ts +20 -0
- package/src/domains/eval/runners/clio-run.ts +139 -2
- package/src/domains/eval/runners/external-command.ts +28 -3
- package/src/domains/eval/schema/adapter.ts +111 -0
- package/src/domains/eval/schema/artifact.ts +20 -0
- package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
- package/src/domains/eval/schema/behavioral.ts +520 -0
- package/src/domains/eval/schema/execution-envelope.ts +194 -0
- package/src/domains/eval/schema/serving.ts +74 -0
- package/src/domains/eval/schema/suite.ts +38 -8
- package/src/domains/eval/schema/validate.ts +58 -3
- package/src/domains/eval/schema/verdict.ts +237 -0
- package/src/domains/eval/suites/resolve.ts +2 -0
- package/src/domains/eval/suites/run.ts +264 -33
- package/src/domains/eval/verifiers/command.ts +2 -1
- package/src/domains/eval/workspaces/temp-copy.ts +145 -13
- package/src/domains/evidence/build.ts +68 -21
- package/src/domains/evidence/eval.ts +2 -12
- package/src/domains/evidence/findings-markdown.ts +33 -0
- package/src/domains/evidence/index.ts +21 -0
- package/src/domains/evidence/provenance.ts +46 -11
- package/src/domains/evidence/run-trust.ts +7 -113
- package/src/domains/evidence/trust-projection.ts +274 -0
- package/src/domains/evidence/trust-status.ts +145 -17
- package/src/domains/evidence/types.ts +4 -0
- package/src/domains/extensions/compatibility.ts +285 -0
- package/src/domains/extensions/discovery.ts +126 -4
- package/src/domains/extensions/resources.ts +21 -9
- package/src/domains/extensions/state.ts +18 -5
- package/src/domains/extensions/types.ts +6 -1
- package/src/domains/lifecycle/doctor.ts +209 -2
- package/src/domains/memory/index.ts +14 -0
- package/src/domains/memory/task-bank-promotion.ts +64 -0
- package/src/domains/memory/task-memory-policy.ts +77 -8
- package/src/domains/memory/task-memory-spend.ts +131 -0
- package/src/domains/memory/task-memory-status.ts +7 -0
- package/src/domains/memory/task-memory-telemetry.ts +2 -0
- package/src/domains/middleware/index.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +69 -5
- package/src/domains/middleware/memory-step-endpoint.ts +71 -0
- package/src/domains/observability/background-memory-usage.ts +140 -0
- package/src/domains/observability/cost.ts +1 -1
- package/src/domains/observability/index.ts +7 -0
- package/src/domains/observability/out-of-turn-usage.ts +51 -2
- package/src/domains/observability/trace-store.ts +192 -2
- package/src/domains/prompts/compiler.ts +100 -13
- package/src/domains/prompts/contract.ts +3 -5
- package/src/domains/providers/endpoint-capacity.ts +96 -0
- package/src/domains/providers/extension.ts +30 -2
- package/src/domains/providers/index.ts +10 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
- package/src/domains/providers/runtime-resolution.ts +8 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
- package/src/domains/providers/types/capability-flags.ts +2 -0
- package/src/domains/providers/types/target-descriptor.ts +2 -0
- package/src/domains/resources/common-loader.ts +3 -0
- package/src/domains/resources/prompts/loader.ts +184 -17
- package/src/domains/safety/call-target.ts +52 -0
- package/src/domains/safety/policy-engine.ts +5 -5
- package/src/domains/safety/run-effects.ts +96 -2
- package/src/domains/safety/scope.ts +7 -12
- package/src/domains/session/context-accounting.ts +52 -1
- package/src/domains/session/context-ledger.ts +37 -13
- package/src/domains/session/index.ts +6 -0
- package/src/domains/session/prompt-cache.ts +140 -0
- package/src/domains/session/prompt-manifest.ts +42 -0
- package/src/engine/acp/adapter.ts +18 -3
- package/src/engine/acp/server.ts +4 -1
- package/src/engine/ai.ts +35 -0
- package/src/engine/apis/llamacpp-residency.ts +55 -3
- package/src/engine/apis/lmstudio.ts +25 -5
- package/src/engine/apis/ollama-native.ts +2 -1
- package/src/engine/apis/openai-completions.ts +80 -17
- package/src/engine/apis/residency-lock.ts +3 -1
- package/src/engine/apis/residency.ts +34 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/provider-payload.ts +29 -1
- package/src/engine/worker-runtime.ts +6 -3
- package/src/entry/orchestrator.ts +176 -30
- package/src/interactive/chat-loop-messages.ts +26 -7
- package/src/interactive/chat-loop.ts +318 -41
- package/src/interactive/chat-panel.ts +62 -8
- package/src/interactive/clio-editor.ts +45 -8
- package/src/interactive/context-activity.ts +5 -1
- package/src/interactive/context-meter.ts +1 -1
- package/src/interactive/context-overlay.ts +40 -10
- package/src/interactive/cost-overlay.ts +64 -6
- package/src/interactive/dispatch-board.ts +127 -5
- package/src/interactive/fleet-run-preview.ts +41 -15
- package/src/interactive/handoff-round.ts +41 -2
- package/src/interactive/interactive-application.ts +24 -1
- package/src/interactive/interactive-event-projection.ts +14 -0
- package/src/interactive/interactive-input-runtime.ts +8 -0
- package/src/interactive/interactive-presentation.ts +4 -0
- package/src/interactive/interactive-shell.ts +20 -17
- package/src/interactive/interactive-slash-runtime.ts +27 -4
- package/src/interactive/memory-overlay.ts +8 -0
- package/src/interactive/mutation-preview.ts +295 -0
- package/src/interactive/overlay-general-openers.ts +16 -0
- package/src/interactive/overlay-key-routing.ts +38 -0
- package/src/interactive/overlay-lifecycle.ts +38 -5
- package/src/interactive/overlay-permission-lifecycle.ts +22 -2
- package/src/interactive/overlay-session-lifecycle.ts +73 -9
- package/src/interactive/overlays/ask-user.ts +91 -19
- package/src/interactive/overlays/help-reference.ts +4 -0
- package/src/interactive/overlays/prompts.ts +11 -1
- package/src/interactive/overlays/settings.ts +176 -48
- package/src/interactive/permission-hint.ts +34 -2
- package/src/interactive/permission-overlay.ts +159 -9
- package/src/interactive/prewarm.ts +197 -0
- package/src/interactive/render-trace.ts +162 -15
- package/src/interactive/renderers/tool-execution.ts +4 -0
- package/src/interactive/side-question.ts +58 -1
- package/src/interactive/slash-commands.ts +7 -2
- package/src/interactive/status/controller.ts +11 -0
- package/src/interactive/status/state-machine.ts +54 -2
- package/src/interactive/status/types.ts +7 -0
- package/src/interactive/terminal-lease.ts +2 -0
- package/src/interactive/turn-context.ts +299 -31
- package/src/interactive/turn-persistence.ts +14 -4
- package/src/interactive/turn-prewarm.ts +364 -0
- package/src/interactive/turn-queues.ts +7 -4
- package/src/interactive/turn-runtime.ts +8 -1
- package/src/interactive/turn-state.ts +23 -0
- package/src/interactive/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +43 -6
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-stream.ts +8 -0
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/dispatch-admission.ts +12 -13
- package/src/tools/dispatch-arguments.ts +27 -0
- package/src/tools/dispatch-plan.ts +46 -9
- package/src/tools/dispatch-runner.ts +48 -13
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/monitor.ts +13 -0
- package/src/tools/registry.ts +16 -0
- package/src/tools/worker-evidence.ts +19 -13
- package/src/worker/spec-contract.ts +2 -1
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-R346GLFC.js +0 -31
- package/dist/chunk-ZGH7FGS5.js +0 -1079
- package/dist/doctor-RN4YKO2X.js +0 -87
- package/dist/eval-RUBJVSNQ.js +0 -2557
|
@@ -403,19 +403,83 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
403
403
|
void runHandoffExtraction(session, fromSessionId, verdict.goal);
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
+
/** Characters of the second round's answer quoted in the terminal refusal. */
|
|
407
|
+
const HANDOFF_REFUSAL_QUOTE_CHARS = 200;
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* A refusal that says what was asked for and what came back, not only that no
|
|
411
|
+
* JSON object arrived. Both rounds are named, so an operator reading it knows
|
|
412
|
+
* two model calls were spent and on what.
|
|
413
|
+
*/
|
|
414
|
+
function terminalHandoffRefusal(firstReason: string, secondReason: string, secondText: string): string {
|
|
415
|
+
const answered = secondText.replace(/\s+/g, " ").trim().slice(0, HANDOFF_REFUSAL_QUOTE_CHARS);
|
|
416
|
+
return [
|
|
417
|
+
"the extraction round could not produce a handoff record after one repair attempt.",
|
|
418
|
+
`Asked for: one JSON object with decisions, facts, files, commands, and openQuestions.`,
|
|
419
|
+
`Round 1: ${firstReason}.`,
|
|
420
|
+
`Round 2: ${secondReason}.`,
|
|
421
|
+
`Round 2 returned: ${answered.length > 0 ? answered : "(nothing)"}`,
|
|
422
|
+
].join(" ");
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Extract, then repair once.
|
|
427
|
+
*
|
|
428
|
+
* A local model that answers with prose around the object, or with nothing
|
|
429
|
+
* parseable, used to end `/handoff` outright: every downstream behavior (the
|
|
430
|
+
* dropped-path listing, the `e` editor, accept-mints-a-session) was
|
|
431
|
+
* unreachable and the operator had paid for the round either way (issue
|
|
432
|
+
* #223). Exactly one repair round follows, quoting what came back and what
|
|
433
|
+
* the parser objected to, and both rounds bill through the same out-of-turn
|
|
434
|
+
* usage store.
|
|
435
|
+
*/
|
|
436
|
+
async function extractWithOneRepair(
|
|
437
|
+
goal: string,
|
|
438
|
+
): Promise<
|
|
439
|
+
| { ok: true; parsed: Extract<ReturnType<typeof parseHandoffExtraction>, { ok: true }> }
|
|
440
|
+
| { ok: false; level: "error" | "warn"; reason: string }
|
|
441
|
+
> {
|
|
442
|
+
const first = await deps.chat.extractHandoff(goal);
|
|
443
|
+
if (first.status !== "answered") {
|
|
444
|
+
return {
|
|
445
|
+
ok: false,
|
|
446
|
+
level: first.status === "failed" ? "error" : "warn",
|
|
447
|
+
reason: first.status === "aborted" ? "the extraction round was cancelled" : first.reason,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
const parsedFirst = parseHandoffExtraction(first.text);
|
|
451
|
+
if (parsedFirst.ok) return { ok: true, parsed: parsedFirst };
|
|
452
|
+
|
|
453
|
+
const second = await deps.chat.extractHandoff(goal, {
|
|
454
|
+
repair: { complaint: parsedFirst.reason, previous: first.text },
|
|
455
|
+
});
|
|
456
|
+
if (second.status !== "answered") {
|
|
457
|
+
return {
|
|
458
|
+
ok: false,
|
|
459
|
+
level: second.status === "failed" ? "error" : "warn",
|
|
460
|
+
reason:
|
|
461
|
+
second.status === "aborted"
|
|
462
|
+
? "the repair round was cancelled"
|
|
463
|
+
: `round 1 could not be read (${parsedFirst.reason}); the repair round then failed: ${second.reason}`,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
const parsedSecond = parseHandoffExtraction(second.text);
|
|
467
|
+
if (parsedSecond.ok) return { ok: true, parsed: parsedSecond };
|
|
468
|
+
return {
|
|
469
|
+
ok: false,
|
|
470
|
+
level: "error",
|
|
471
|
+
reason: terminalHandoffRefusal(parsedFirst.reason, parsedSecond.reason, second.text),
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
406
475
|
async function runHandoffExtraction(session: SessionContract, fromSessionId: string, goal: string): Promise<void> {
|
|
407
476
|
const notice = deps.getSlashNotice();
|
|
408
|
-
const
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
emitCommandNotice(notice, outcome.status === "failed" ? "error" : "warn", "handoff", reason);
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
const parsed = parseHandoffExtraction(outcome.text);
|
|
415
|
-
if (!parsed.ok) {
|
|
416
|
-
emitCommandNotice(notice, "error", "handoff", parsed.reason);
|
|
477
|
+
const extraction = await extractWithOneRepair(goal);
|
|
478
|
+
if (!extraction.ok) {
|
|
479
|
+
emitCommandNotice(notice, extraction.level, "handoff", extraction.reason);
|
|
417
480
|
return;
|
|
418
481
|
}
|
|
482
|
+
const parsed = extraction.parsed;
|
|
419
483
|
const meta = session.current();
|
|
420
484
|
const cwd = typeof meta?.cwd === "string" && meta.cwd.length > 0 ? meta.cwd : null;
|
|
421
485
|
const entries = deps.readStructuredEntries(fromSessionId);
|
|
@@ -129,6 +129,14 @@ interface QuestionState {
|
|
|
129
129
|
customAnswer: string;
|
|
130
130
|
inputValue: string;
|
|
131
131
|
answer: string;
|
|
132
|
+
/**
|
|
133
|
+
* The operator's typed text exactly as submitted, empty when they typed
|
|
134
|
+
* nothing. `answer` is the one-line rendering of the whole decision and gets
|
|
135
|
+
* trimmed and joined; this is the record of what they actually typed, and
|
|
136
|
+
* losing it is what made an interview re-ask for the same figures three times
|
|
137
|
+
* (issue #228).
|
|
138
|
+
*/
|
|
139
|
+
rawValue: string;
|
|
132
140
|
focusedValue?: string;
|
|
133
141
|
}
|
|
134
142
|
|
|
@@ -154,6 +162,7 @@ function createQuestionState(question: AskUserQuestion): QuestionState {
|
|
|
154
162
|
customAnswer: "",
|
|
155
163
|
inputValue: "",
|
|
156
164
|
answer: "",
|
|
165
|
+
rawValue: "",
|
|
157
166
|
};
|
|
158
167
|
}
|
|
159
168
|
|
|
@@ -205,12 +214,18 @@ function optionIndexFromValue(value: string): number | null {
|
|
|
205
214
|
return Number.isInteger(index) && index >= 0 ? index : null;
|
|
206
215
|
}
|
|
207
216
|
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
/** The labels the operator chose, in list order. */
|
|
218
|
+
function selectedOptionLabels(question: AskUserQuestion, selected: ReadonlySet<number>): string[] {
|
|
219
|
+
const labels: string[] = [];
|
|
210
220
|
for (const index of [...selected].sort((a, b) => a - b)) {
|
|
211
221
|
const label = question.options?.[index]?.label;
|
|
212
|
-
if (label)
|
|
222
|
+
if (label) labels.push(label);
|
|
213
223
|
}
|
|
224
|
+
return labels;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function answerText(question: AskUserQuestion, selected: ReadonlySet<number>, customAnswer: string): string {
|
|
228
|
+
const parts = selectedOptionLabels(question, selected);
|
|
214
229
|
const custom = customAnswer.trim();
|
|
215
230
|
if (custom.length > 0) parts.push(custom);
|
|
216
231
|
return parts.join("; ");
|
|
@@ -348,6 +363,10 @@ class AskUserOverlayView implements Component {
|
|
|
348
363
|
this.commitMultiSelectOrOpenOther(state);
|
|
349
364
|
return;
|
|
350
365
|
}
|
|
366
|
+
if (this.isAddTextKey(data)) {
|
|
367
|
+
this.chooseFocusedAndOpenText(question, state);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
351
370
|
this.list?.handleInput(data);
|
|
352
371
|
}
|
|
353
372
|
|
|
@@ -502,24 +521,22 @@ class AskUserOverlayView implements Component {
|
|
|
502
521
|
// The classified action label distinguishes recording an answer from
|
|
503
522
|
// granting authority. Esc keeps the product's one word for the way out,
|
|
504
523
|
// since it closes the prompt and the interview together.
|
|
524
|
+
// `t` is on every select footer because the operator cannot tell from a
|
|
525
|
+
// label whether the option needs a figure attached until they read it.
|
|
526
|
+
const addText: HintEntry = { key: "t", verb: "add text", short: "text" };
|
|
505
527
|
if (question.multi_select === true) {
|
|
506
528
|
return this.questions.length > 1
|
|
507
529
|
? this.withScrollHint([
|
|
508
530
|
{ key: "Left/Right", verb: "question" },
|
|
509
531
|
{ key: "Space", verb: "toggle" },
|
|
532
|
+
addText,
|
|
510
533
|
{ key: "Enter", verb: recordAnswer },
|
|
511
534
|
])
|
|
512
|
-
: this.withScrollHint([
|
|
513
|
-
{ key: "Space", verb: "toggle" },
|
|
514
|
-
{ key: "Enter", verb: recordAnswer },
|
|
515
|
-
]);
|
|
535
|
+
: this.withScrollHint([{ key: "Space", verb: "toggle" }, addText, { key: "Enter", verb: recordAnswer }]);
|
|
516
536
|
}
|
|
517
537
|
return this.questions.length > 1
|
|
518
|
-
? this.withScrollHint([
|
|
519
|
-
|
|
520
|
-
{ key: "Enter", verb: recordAnswer },
|
|
521
|
-
])
|
|
522
|
-
: this.withScrollHint([{ key: "Enter", verb: recordAnswer }]);
|
|
538
|
+
? this.withScrollHint([{ key: "Left/Right", verb: "question" }, addText, { key: "Enter", verb: recordAnswer }])
|
|
539
|
+
: this.withScrollHint([addText, { key: "Enter", verb: recordAnswer }]);
|
|
523
540
|
}
|
|
524
541
|
|
|
525
542
|
private renderDecisionContext(width: number): string[] {
|
|
@@ -644,6 +661,34 @@ class AskUserOverlayView implements Component {
|
|
|
644
661
|
return this.questions.length > 1 && (matchesKey(data, "alt+right") || matchesKey(data, "ctrl+right"));
|
|
645
662
|
}
|
|
646
663
|
|
|
664
|
+
/**
|
|
665
|
+
* The key that adds typed text to the option under the cursor.
|
|
666
|
+
*
|
|
667
|
+
* Choosing an option used to be the whole answer, so a model that offered
|
|
668
|
+
* "Exact number - I'll type it" gave the operator a label and nowhere to put
|
|
669
|
+
* the number; the interview then spent two of its four rounds asking for the
|
|
670
|
+
* figures again (issue #228). The list itself only reads the arrows, Enter,
|
|
671
|
+
* and Esc, so a letter is free here.
|
|
672
|
+
*/
|
|
673
|
+
private isAddTextKey(data: string): boolean {
|
|
674
|
+
return matchesKey(data, "t");
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Take the focused option and open the text field for it, without committing.
|
|
679
|
+
* Submitting the text records the label and the text together.
|
|
680
|
+
*/
|
|
681
|
+
private chooseFocusedAndOpenText(question: AskUserQuestion, state: QuestionState): void {
|
|
682
|
+
const current = this.list?.getSelectedItem();
|
|
683
|
+
const optionIndex = current && current.value !== "other" ? optionIndexFromValue(current.value) : null;
|
|
684
|
+
if (optionIndex !== null) {
|
|
685
|
+
if (question.multi_select === true) state.selected.add(optionIndex);
|
|
686
|
+
else state.selected = new Set<number>([optionIndex]);
|
|
687
|
+
state.focusedValue = `option:${optionIndex}`;
|
|
688
|
+
}
|
|
689
|
+
this.openTextInput(state.selected.size > 0 ? "Add your answer to the chosen option" : "Other answer");
|
|
690
|
+
}
|
|
691
|
+
|
|
647
692
|
/**
|
|
648
693
|
* The row above the question, or nothing.
|
|
649
694
|
*
|
|
@@ -829,14 +874,20 @@ class AskUserOverlayView implements Component {
|
|
|
829
874
|
return;
|
|
830
875
|
}
|
|
831
876
|
state.inputValue = answer;
|
|
877
|
+
// Verbatim, before any joining or trimming the display line does. The
|
|
878
|
+
// typed text is the answer; the one-line `answer` is a rendering of it.
|
|
879
|
+
state.rawValue = value;
|
|
832
880
|
if (questionHasOptions(question) && question.multi_select === true) {
|
|
833
881
|
state.customAnswer = answer;
|
|
834
882
|
this.commitCurrentAnswer();
|
|
835
|
-
|
|
836
|
-
state.customAnswer = questionHasOptions(question) ? answer : "";
|
|
837
|
-
state.answer = answer;
|
|
838
|
-
this.finishIfCompleteOrAdvance();
|
|
883
|
+
return;
|
|
839
884
|
}
|
|
885
|
+
state.customAnswer = questionHasOptions(question) ? answer : "";
|
|
886
|
+
// The chosen labels and the typed text compose, so a question answered
|
|
887
|
+
// with an option that says "I will type it" keeps both. With nothing
|
|
888
|
+
// selected, which is the implicit Other path, this is the text alone.
|
|
889
|
+
state.answer = questionHasOptions(question) ? answerText(question, state.selected, state.customAnswer) : answer;
|
|
890
|
+
this.finishIfCompleteOrAdvance();
|
|
840
891
|
};
|
|
841
892
|
activeInput.onEscape = () => this.cancel();
|
|
842
893
|
this.input = activeInput;
|
|
@@ -866,7 +917,14 @@ class AskUserOverlayView implements Component {
|
|
|
866
917
|
return;
|
|
867
918
|
}
|
|
868
919
|
state.selected = new Set<number>([optionIndex]);
|
|
869
|
-
|
|
920
|
+
// Choosing a plain option is a label-only answer, so the typed text
|
|
921
|
+
// clears with the choice: it was given for a label the operator has
|
|
922
|
+
// just moved off, and the composed answer runs through the same
|
|
923
|
+
// `answerText` every other path uses.
|
|
924
|
+
state.customAnswer = "";
|
|
925
|
+
state.rawValue = "";
|
|
926
|
+
state.inputValue = "";
|
|
927
|
+
state.answer = answerText(question, state.selected, state.customAnswer) || item.label;
|
|
870
928
|
state.focusedValue = item.value;
|
|
871
929
|
this.status = "";
|
|
872
930
|
this.finishIfCompleteOrAdvance();
|
|
@@ -992,12 +1050,26 @@ class AskUserOverlayView implements Component {
|
|
|
992
1050
|
return this.states.length > 0 && this.states.every((state) => state.answer.trim().length > 0);
|
|
993
1051
|
}
|
|
994
1052
|
|
|
1053
|
+
/**
|
|
1054
|
+
* What the round captured, as three separable facts per question: the one-line
|
|
1055
|
+
* answer, the labels chosen, and the text typed. A reader tells a label-only
|
|
1056
|
+
* answer from a label-plus-value one by whether `value` is there, which the
|
|
1057
|
+
* joined string alone could never say.
|
|
1058
|
+
*/
|
|
995
1059
|
private answers(): AskUserResult["answers"] {
|
|
996
1060
|
const answers: AskUserResult["answers"] = [];
|
|
997
1061
|
for (let index = 0; index < this.questions.length; index += 1) {
|
|
998
1062
|
const question = this.questions[index];
|
|
999
|
-
const
|
|
1000
|
-
|
|
1063
|
+
const state = this.states[index];
|
|
1064
|
+
const answer = state?.answer.trim();
|
|
1065
|
+
if (!question || !state || !answer || answer.length === 0) continue;
|
|
1066
|
+
const options = selectedOptionLabels(question, state.selected);
|
|
1067
|
+
answers.push({
|
|
1068
|
+
question: question.question,
|
|
1069
|
+
answer,
|
|
1070
|
+
...(options.length > 0 ? { options } : {}),
|
|
1071
|
+
...(state.rawValue.length > 0 ? { value: state.rawValue } : {}),
|
|
1072
|
+
});
|
|
1001
1073
|
}
|
|
1002
1074
|
return answers;
|
|
1003
1075
|
}
|
|
@@ -129,6 +129,10 @@ export function openHelpOverlay(
|
|
|
129
129
|
"auto-edit parks unrecognized commands, and full-auto runs them.",
|
|
130
130
|
"**Approvals**: a parked call waits for a one-shot operator decision; approving resumes only that call. " +
|
|
131
131
|
"Workers resolve asks per `workers.onPermission` (Approvals Routing); headless runs auto-deny.",
|
|
132
|
+
"**Inspecting a mutation**: a parked `write` or `edit` card carries the target, the byte count, and a `sha256` digest of the exact call arguments. " +
|
|
133
|
+
"Press `v` to read the complete proposed content, or the complete effective diff against the file on disk, and `v` again to put it away; ↑/↓ and PageUp/PageDown scroll it. " +
|
|
134
|
+
"Enter still allows, `s` still stops, and Esc still denies while it is open. The mutation text is shown locally and never enters the transcript, a notice, a desktop notification, or the render trace. " +
|
|
135
|
+
"A worker escalation has no preview because the call's arguments never leave the worker, and its card says so.",
|
|
132
136
|
],
|
|
133
137
|
},
|
|
134
138
|
];
|
|
@@ -22,13 +22,23 @@ export function openPromptsOverlay(tui: TUI, ctx: SlashCommandContext, onClose:
|
|
|
22
22
|
lines.push(`**Argument Hint:** \`${template.argumentHint}\``);
|
|
23
23
|
}
|
|
24
24
|
lines.push(`**Source:** ${template.sourceInfo.source ?? template.sourceInfo.scope}`);
|
|
25
|
+
if (template.unavailable !== undefined) {
|
|
26
|
+
lines.push(`**Unavailable:** ${template.unavailable}`);
|
|
27
|
+
}
|
|
25
28
|
if (!template.trusted) {
|
|
26
29
|
lines.push("**Untrusted:** set `skills.trustProjectCompatRoots` before this template will expand.");
|
|
27
30
|
}
|
|
28
31
|
return lines;
|
|
29
32
|
},
|
|
30
33
|
};
|
|
31
|
-
|
|
34
|
+
// A template that loaded in a refusing state is listed, because the
|
|
35
|
+
// operator's command does exist; the marker is what says it will not run.
|
|
36
|
+
const marker =
|
|
37
|
+
template.unavailable !== undefined
|
|
38
|
+
? clioTheme().fg("error", "unavailable")
|
|
39
|
+
: template.trusted
|
|
40
|
+
? undefined
|
|
41
|
+
: clioTheme().fg("warning", "untrusted");
|
|
32
42
|
if (marker) item.meta = template.argumentHint ? `${template.argumentHint} · ${marker}` : marker;
|
|
33
43
|
else if (template.argumentHint) item.meta = template.argumentHint;
|
|
34
44
|
return item;
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { DEFAULT_SETTINGS, THINKING_LEVELS } from "../../core/defaults.js";
|
|
10
10
|
import { getAtPath, isRoutingPath } from "../../core/session-routing.js";
|
|
11
11
|
import { MAX_TIMER_DELAY_MS } from "../../core/timers.js";
|
|
12
|
+
import { capacityLeaseUsage } from "../../domains/dispatch/capacity-lease.js";
|
|
12
13
|
import {
|
|
13
14
|
delegationEntryForKind,
|
|
14
15
|
type InteropAgentId,
|
|
@@ -17,6 +18,8 @@ import {
|
|
|
17
18
|
} from "../../domains/interop/index.js";
|
|
18
19
|
import {
|
|
19
20
|
type CapabilityFlags,
|
|
21
|
+
endpointCapacitiesForStatuses,
|
|
22
|
+
endpointCapacityForStatus,
|
|
20
23
|
isDispatchEligibleRuntime,
|
|
21
24
|
isOrchestratorEligibleRuntime,
|
|
22
25
|
type ProvidersContract,
|
|
@@ -225,7 +228,8 @@ type EntrySettingId =
|
|
|
225
228
|
| `workers.profiles.${string}`
|
|
226
229
|
| `workers.agentBindings.${string}`
|
|
227
230
|
| `targets.${string}`
|
|
228
|
-
| `fleet.nodes.${string}
|
|
231
|
+
| `fleet.nodes.${string}`
|
|
232
|
+
| `fleet.endpoints.${string}`;
|
|
229
233
|
export type EditableSettingId = keyof typeof SETTINGS_LABELS_BY_ID | EntrySettingId;
|
|
230
234
|
type FleetGroupHeaderId = `fleet.group.${"defaults" | "profiles" | "agent-routes" | "placement"}`;
|
|
231
235
|
type TargetsCtaId = "targets.add-cta";
|
|
@@ -593,6 +597,13 @@ function delegationAgentsAffordance(
|
|
|
593
597
|
type SubmenuTitle = string | ((width: number) => string);
|
|
594
598
|
|
|
595
599
|
export class SubmenuWrapper implements Component {
|
|
600
|
+
/**
|
|
601
|
+
* Why the last submission was refused, or null while nothing is wrong. The
|
|
602
|
+
* editor sets it instead of closing, so the operator corrects the value with
|
|
603
|
+
* the reason on screen rather than back at the row list with no reason.
|
|
604
|
+
*/
|
|
605
|
+
private problem: string | null = null;
|
|
606
|
+
|
|
596
607
|
constructor(
|
|
597
608
|
private readonly title: SubmenuTitle,
|
|
598
609
|
private readonly child: Component,
|
|
@@ -600,6 +611,10 @@ export class SubmenuWrapper implements Component {
|
|
|
600
611
|
private readonly note?: string,
|
|
601
612
|
) {}
|
|
602
613
|
|
|
614
|
+
setProblem(problem: string | null): void {
|
|
615
|
+
this.problem = problem;
|
|
616
|
+
}
|
|
617
|
+
|
|
603
618
|
render(width: number): string[] {
|
|
604
619
|
const theme = clioTheme();
|
|
605
620
|
const lines: string[] = [];
|
|
@@ -619,6 +634,11 @@ export class SubmenuWrapper implements Component {
|
|
|
619
634
|
// two columns off each one and marked the cut, so every entry wore a
|
|
620
635
|
// trailing … it had not earned.
|
|
621
636
|
lines.push(...this.child.render(Math.max(1, width - 2)).map((line) => ` ${line}`));
|
|
637
|
+
if (this.problem !== null) {
|
|
638
|
+
for (const line of wrapTextWithAnsi(this.problem, Math.max(1, width - 2))) {
|
|
639
|
+
lines.push(theme.fg("error", ` ${line}`));
|
|
640
|
+
}
|
|
641
|
+
}
|
|
622
642
|
lines.push("");
|
|
623
643
|
lines.push(theme.fg("dim", ` ${this.hint}`));
|
|
624
644
|
return lines;
|
|
@@ -693,33 +713,108 @@ function editTextSubmenu(title: string, note?: string): SettingSubmenuBuilder {
|
|
|
693
713
|
return textInputSubmenu(title, note);
|
|
694
714
|
}
|
|
695
715
|
|
|
696
|
-
|
|
697
|
-
|
|
716
|
+
/**
|
|
717
|
+
* The bound a number row enforces, shared by the editor and the apply path so
|
|
718
|
+
* the two cannot drift: whatever the editor forwards, the apply path stores.
|
|
719
|
+
* The bounds mirror the config validator's, so a value the Settings Center
|
|
720
|
+
* refuses is one settings.yaml would have refused too, in the same words.
|
|
721
|
+
*/
|
|
722
|
+
interface NumberSettingRule {
|
|
723
|
+
readonly min: number;
|
|
724
|
+
readonly max?: number;
|
|
725
|
+
readonly integer: boolean;
|
|
726
|
+
/** A blank submission clears the key instead of being a parse failure. */
|
|
727
|
+
readonly allowBlank?: boolean;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const NUMBER_SETTING_RULES = {
|
|
731
|
+
"budget.sessionCeilingUsd": { min: 0, integer: false },
|
|
732
|
+
"watchdog.cadenceToolCalls": { min: 1, integer: true, allowBlank: true },
|
|
733
|
+
"delegation.defaults.connectTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
|
|
734
|
+
"delegation.defaults.turnTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
|
|
735
|
+
"delegation.defaults.permissionTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
|
|
736
|
+
} as const satisfies Partial<Record<EditableSettingId, NumberSettingRule>>;
|
|
737
|
+
|
|
738
|
+
export type NumberSettingId = keyof typeof NUMBER_SETTING_RULES;
|
|
739
|
+
|
|
740
|
+
export const NUMBER_SETTING_IDS = Object.keys(NUMBER_SETTING_RULES) as readonly NumberSettingId[];
|
|
741
|
+
|
|
742
|
+
type NumberSettingOutcome = { readonly value: number | null } | { readonly refusal: string };
|
|
743
|
+
|
|
744
|
+
function describeSubmittedText(value: string): string {
|
|
745
|
+
const trimmed = value.trim();
|
|
746
|
+
return trimmed.length === 0 ? "an empty string" : JSON.stringify(trimmed);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Parse one submitted number-row value under its rule. A null value means the
|
|
751
|
+
* operator cleared an optional key. The refusal is worded like the config
|
|
752
|
+
* validator's issue for the same key ("expected an integer >= 1, got 0"), with
|
|
753
|
+
* a "Not applied:" prefix so it reads as the outcome of this submission.
|
|
754
|
+
*/
|
|
755
|
+
function parseNumberSetting(value: string, rule: NumberSettingRule): NumberSettingOutcome {
|
|
756
|
+
const trimmed = value.trim();
|
|
757
|
+
const noun = rule.integer ? "an integer" : "a number";
|
|
758
|
+
if (trimmed.length === 0 && rule.allowBlank === true) return { value: null };
|
|
759
|
+
const parsed = trimmed.length === 0 ? Number.NaN : Number(trimmed);
|
|
760
|
+
if (!Number.isFinite(parsed) || (rule.integer && !Number.isInteger(parsed))) {
|
|
761
|
+
const shown = Number.isFinite(parsed) ? String(parsed) : describeSubmittedText(value);
|
|
762
|
+
return { refusal: `Not applied: expected ${noun}, got ${shown}.` };
|
|
763
|
+
}
|
|
764
|
+
if (parsed < rule.min) return { refusal: `Not applied: expected ${noun} >= ${rule.min}, got ${parsed}.` };
|
|
765
|
+
if (rule.max !== undefined && parsed > rule.max) {
|
|
766
|
+
return { refusal: `Not applied: expected ${noun} <= ${rule.max}, got ${parsed}.` };
|
|
767
|
+
}
|
|
768
|
+
return { value: parsed };
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* The reason a number row would refuse `value`, or null when it would apply.
|
|
773
|
+
* This is the same check the editor and `applySettingChange` run, exposed so a
|
|
774
|
+
* caller that bypasses the editor can still name the bound it hit.
|
|
775
|
+
*/
|
|
776
|
+
export function describeNumberSettingRefusal(id: NumberSettingId, value: string): string | null {
|
|
777
|
+
const outcome = parseNumberSetting(value, NUMBER_SETTING_RULES[id]);
|
|
778
|
+
return "refusal" in outcome ? outcome.refusal : null;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function numberSettingNote(rule: NumberSettingRule): string {
|
|
782
|
+
const noun = rule.integer ? "a whole number" : "a number";
|
|
783
|
+
const clears = rule.allowBlank === true ? "; blank clears it" : "";
|
|
784
|
+
if (rule.min > 0 && rule.max !== undefined) return `Use ${noun} from ${rule.min} to ${rule.max}${clears}.`;
|
|
785
|
+
if (rule.min > 0) return `Use ${noun} of at least ${rule.min}${clears}.`;
|
|
786
|
+
return `Use a non-negative ${rule.integer ? "whole number" : "number"}${clears}.`;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* A number editor that stays open on a refused submission. Closing with no
|
|
791
|
+
* value looked like a successful edit that changed nothing, so the reason is
|
|
792
|
+
* rendered under the input and the operator corrects the text in place; Esc
|
|
793
|
+
* still leaves without applying.
|
|
794
|
+
*/
|
|
795
|
+
function editNumberSubmenu(title: string, id: NumberSettingId): SettingSubmenuBuilder {
|
|
796
|
+
const rule = NUMBER_SETTING_RULES[id];
|
|
698
797
|
return (currentValue: string, done: (val?: string) => void) => {
|
|
699
798
|
const input = new Input();
|
|
700
799
|
input.setValue(currentValue);
|
|
701
800
|
input.focused = true;
|
|
801
|
+
const wrapper = new SubmenuWrapper(
|
|
802
|
+
title,
|
|
803
|
+
input,
|
|
804
|
+
buildHint([{ key: "Enter", verb: "confirm" }], "back"),
|
|
805
|
+
numberSettingNote(rule),
|
|
806
|
+
);
|
|
702
807
|
input.onSubmit = (val) => {
|
|
703
|
-
|
|
704
|
-
|
|
808
|
+
const outcome = parseNumberSetting(val, rule);
|
|
809
|
+
if ("refusal" in outcome) {
|
|
810
|
+
wrapper.setProblem(outcome.refusal);
|
|
705
811
|
return;
|
|
706
812
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
done(val);
|
|
710
|
-
} else {
|
|
711
|
-
done();
|
|
712
|
-
}
|
|
813
|
+
wrapper.setProblem(null);
|
|
814
|
+
done(outcome.value === null ? "" : val.trim());
|
|
713
815
|
};
|
|
714
816
|
input.onEscape = () => done();
|
|
715
|
-
return
|
|
716
|
-
title,
|
|
717
|
-
input,
|
|
718
|
-
buildHint([{ key: "Enter", verb: "confirm" }], "back"),
|
|
719
|
-
min > 0
|
|
720
|
-
? `Use a whole number of at least ${min}${options.allowBlank === true ? "; blank clears it" : ""}.`
|
|
721
|
-
: "Use a non-negative number.",
|
|
722
|
-
);
|
|
817
|
+
return wrapper;
|
|
723
818
|
};
|
|
724
819
|
}
|
|
725
820
|
|
|
@@ -1391,6 +1486,7 @@ export function buildSettingItems(
|
|
|
1391
1486
|
}),
|
|
1392
1487
|
fleetGroupHeader("fleet.group.placement", "Placement"),
|
|
1393
1488
|
...fleetNodeRows(options?.getFleetNodes?.() ?? []),
|
|
1489
|
+
...fleetEndpointRows(options?.providers),
|
|
1394
1490
|
settingItem("targets", "", {
|
|
1395
1491
|
description: "Live inference target inventory and routing roles.",
|
|
1396
1492
|
affordance: "column heading",
|
|
@@ -1412,7 +1508,7 @@ export function buildSettingItems(
|
|
|
1412
1508
|
readOnly: true,
|
|
1413
1509
|
}),
|
|
1414
1510
|
settingItem("budget.sessionCeilingUsd", String(settings.budget.sessionCeilingUsd), {
|
|
1415
|
-
submenu: editNumberSubmenu("Edit session cost ceiling USD"),
|
|
1511
|
+
submenu: editNumberSubmenu("Edit session cost ceiling USD", "budget.sessionCeilingUsd"),
|
|
1416
1512
|
affordance: "free text",
|
|
1417
1513
|
}),
|
|
1418
1514
|
settingItem("defaults.maxTokens", String(settings.defaults.maxTokens), {
|
|
@@ -1479,10 +1575,10 @@ export function buildSettingItems(
|
|
|
1479
1575
|
"watchdog.cadenceToolCalls",
|
|
1480
1576
|
watchdog.cadenceToolCalls === undefined ? "(turn end only)" : String(watchdog.cadenceToolCalls),
|
|
1481
1577
|
{
|
|
1482
|
-
submenu: editNumberSubmenu(
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1578
|
+
submenu: editNumberSubmenu(
|
|
1579
|
+
"Edit watchdog cadence in tool calls; blank fires at turn end only",
|
|
1580
|
+
"watchdog.cadenceToolCalls",
|
|
1581
|
+
),
|
|
1486
1582
|
affordance: "free text",
|
|
1487
1583
|
editValue: watchdog.cadenceToolCalls === undefined ? "" : String(watchdog.cadenceToolCalls),
|
|
1488
1584
|
},
|
|
@@ -1503,15 +1599,15 @@ export function buildSettingItems(
|
|
|
1503
1599
|
affordance: "free text",
|
|
1504
1600
|
}),
|
|
1505
1601
|
settingItem("delegation.defaults.connectTimeoutMs", String(settings.delegation.defaults.connectTimeoutMs), {
|
|
1506
|
-
submenu: editNumberSubmenu("Edit delegate connect timeout (ms)"),
|
|
1602
|
+
submenu: editNumberSubmenu("Edit delegate connect timeout (ms)", "delegation.defaults.connectTimeoutMs"),
|
|
1507
1603
|
affordance: "free text",
|
|
1508
1604
|
}),
|
|
1509
1605
|
settingItem("delegation.defaults.turnTimeoutMs", String(settings.delegation.defaults.turnTimeoutMs), {
|
|
1510
|
-
submenu: editNumberSubmenu("Edit delegate turn timeout (ms)"),
|
|
1606
|
+
submenu: editNumberSubmenu("Edit delegate turn timeout (ms)", "delegation.defaults.turnTimeoutMs"),
|
|
1511
1607
|
affordance: "free text",
|
|
1512
1608
|
}),
|
|
1513
1609
|
settingItem("delegation.defaults.permissionTimeoutMs", String(settings.delegation.defaults.permissionTimeoutMs), {
|
|
1514
|
-
submenu: editNumberSubmenu("Edit delegate permission timeout (ms)"),
|
|
1610
|
+
submenu: editNumberSubmenu("Edit delegate permission timeout (ms)", "delegation.defaults.permissionTimeoutMs"),
|
|
1515
1611
|
affordance: "free text",
|
|
1516
1612
|
}),
|
|
1517
1613
|
settingItem("keybindings", keybindingCount > 0 ? `${keybindingCount} override(s)` : "(defaults)", {
|
|
@@ -1769,6 +1865,35 @@ function fleetNodeRows(nodes: ReadonlyArray<FleetNodeSnapshot>): SettingsCenterI
|
|
|
1769
1865
|
});
|
|
1770
1866
|
}
|
|
1771
1867
|
|
|
1868
|
+
/** Read-only endpoint rows show the independent inference scheduler dimension. */
|
|
1869
|
+
function fleetEndpointRows(providers: ProvidersContract | undefined): SettingsCenterItem[] {
|
|
1870
|
+
if (providers === undefined) return [];
|
|
1871
|
+
let usage: Readonly<Record<string, number>> = {};
|
|
1872
|
+
try {
|
|
1873
|
+
usage = capacityLeaseUsage().endpoints;
|
|
1874
|
+
} catch {
|
|
1875
|
+
// A damaged capacity file belongs to admission. Settings still shows the configured limits.
|
|
1876
|
+
}
|
|
1877
|
+
const capacities = endpointCapacitiesForStatuses(providers.list());
|
|
1878
|
+
const targetsByEndpoint = new Map<string, string[]>();
|
|
1879
|
+
for (const status of providers.list()) {
|
|
1880
|
+
const endpoint = endpointCapacityForStatus(status);
|
|
1881
|
+
if (endpoint === null) continue;
|
|
1882
|
+
targetsByEndpoint.set(endpoint.key, [...(targetsByEndpoint.get(endpoint.key) ?? []), status.target.id]);
|
|
1883
|
+
}
|
|
1884
|
+
return Object.entries(capacities).map(([key, endpoint]) => {
|
|
1885
|
+
const slots = `slots ${usage[key] ?? 0}/${endpoint.limit}`;
|
|
1886
|
+
return settingItem(`fleet.endpoints.${key}`, slots, {
|
|
1887
|
+
label: `endpoint ${endpoint.label}`,
|
|
1888
|
+
description: `Targets: ${(targetsByEndpoint.get(key) ?? []).join(", ")} · Limit source: ${endpoint.source}`,
|
|
1889
|
+
affordance: "read-only inference endpoint capacity",
|
|
1890
|
+
readOnly: true,
|
|
1891
|
+
presentationKind: "status",
|
|
1892
|
+
valueSegments: [{ text: slots, tone: "neutral" }],
|
|
1893
|
+
});
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1772
1897
|
export function buildSettingsSections(items: readonly SettingsCenterItem[]): SettingsCenterSection[] {
|
|
1773
1898
|
return SETTINGS_SECTIONS.map((section) => ({
|
|
1774
1899
|
id: section.id,
|
|
@@ -1819,9 +1944,15 @@ function applyNonNegativeInteger(value: string, set: (next: number) => void): vo
|
|
|
1819
1944
|
if (Number.isFinite(parsed) && parsed >= 0) set(Math.floor(parsed));
|
|
1820
1945
|
}
|
|
1821
1946
|
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1947
|
+
/**
|
|
1948
|
+
* Store a number-row submission under the row's shared rule. A refused value
|
|
1949
|
+
* leaves the setting alone; the editor has already shown the operator why, and
|
|
1950
|
+
* `describeNumberSettingRefusal` names the reason for any other caller.
|
|
1951
|
+
*/
|
|
1952
|
+
function applyNumberSetting(id: NumberSettingId, value: string, set: (next: number | null) => void): void {
|
|
1953
|
+
const outcome = parseNumberSetting(value, NUMBER_SETTING_RULES[id]);
|
|
1954
|
+
if ("refusal" in outcome) return;
|
|
1955
|
+
set(outcome.value);
|
|
1825
1956
|
}
|
|
1826
1957
|
|
|
1827
1958
|
/**
|
|
@@ -1986,15 +2117,12 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
|
|
|
1986
2117
|
else delete settings.watchdog.target;
|
|
1987
2118
|
return;
|
|
1988
2119
|
}
|
|
1989
|
-
case "watchdog.cadenceToolCalls":
|
|
1990
|
-
|
|
1991
|
-
delete settings.watchdog.cadenceToolCalls;
|
|
1992
|
-
|
|
1993
|
-
}
|
|
1994
|
-
const parsed = Number(value);
|
|
1995
|
-
if (Number.isFinite(parsed) && Math.floor(parsed) >= 1) settings.watchdog.cadenceToolCalls = Math.floor(parsed);
|
|
2120
|
+
case "watchdog.cadenceToolCalls":
|
|
2121
|
+
applyNumberSetting("watchdog.cadenceToolCalls", value, (next) => {
|
|
2122
|
+
if (next === null) delete settings.watchdog.cadenceToolCalls;
|
|
2123
|
+
else settings.watchdog.cadenceToolCalls = next;
|
|
2124
|
+
});
|
|
1996
2125
|
return;
|
|
1997
|
-
}
|
|
1998
2126
|
case "terminal.smoothStreaming":
|
|
1999
2127
|
if (value === "off" || value === "auto" || value === "on") settings.terminal.smoothStreaming = value;
|
|
2000
2128
|
return;
|
|
@@ -2005,18 +2133,18 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
|
|
|
2005
2133
|
.filter(Boolean);
|
|
2006
2134
|
return;
|
|
2007
2135
|
case "delegation.defaults.connectTimeoutMs":
|
|
2008
|
-
|
|
2009
|
-
settings.delegation.defaults.connectTimeoutMs = next;
|
|
2136
|
+
applyNumberSetting("delegation.defaults.connectTimeoutMs", value, (next) => {
|
|
2137
|
+
if (next !== null) settings.delegation.defaults.connectTimeoutMs = next;
|
|
2010
2138
|
});
|
|
2011
2139
|
return;
|
|
2012
2140
|
case "delegation.defaults.turnTimeoutMs":
|
|
2013
|
-
|
|
2014
|
-
settings.delegation.defaults.turnTimeoutMs = next;
|
|
2141
|
+
applyNumberSetting("delegation.defaults.turnTimeoutMs", value, (next) => {
|
|
2142
|
+
if (next !== null) settings.delegation.defaults.turnTimeoutMs = next;
|
|
2015
2143
|
});
|
|
2016
2144
|
return;
|
|
2017
2145
|
case "delegation.defaults.permissionTimeoutMs":
|
|
2018
|
-
|
|
2019
|
-
settings.delegation.defaults.permissionTimeoutMs = next;
|
|
2146
|
+
applyNumberSetting("delegation.defaults.permissionTimeoutMs", value, (next) => {
|
|
2147
|
+
if (next !== null) settings.delegation.defaults.permissionTimeoutMs = next;
|
|
2020
2148
|
});
|
|
2021
2149
|
return;
|
|
2022
2150
|
case "orchestrator.target": {
|
|
@@ -2067,11 +2195,11 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
|
|
|
2067
2195
|
.map((v) => v.trim())
|
|
2068
2196
|
.filter(Boolean);
|
|
2069
2197
|
return;
|
|
2070
|
-
case "budget.sessionCeilingUsd":
|
|
2071
|
-
|
|
2072
|
-
|
|
2198
|
+
case "budget.sessionCeilingUsd":
|
|
2199
|
+
applyNumberSetting("budget.sessionCeilingUsd", value, (next) => {
|
|
2200
|
+
if (next !== null) settings.budget.sessionCeilingUsd = next;
|
|
2201
|
+
});
|
|
2073
2202
|
return;
|
|
2074
|
-
}
|
|
2075
2203
|
}
|
|
2076
2204
|
}
|
|
2077
2205
|
|