@iowarp/clio-coder 0.3.6 → 0.3.8
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 +59 -0
- package/README.md +22 -6
- package/dist/{acp-2BEHC4DL.js → acp-U67UHUK2.js} +14 -14
- package/dist/{agents-LNNFTM53.js → agents-YU6SGALZ.js} +42 -35
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-KXXFI2VS.js → auth-5ZPJOIVG.js} +32 -24
- package/dist/builtins-C6JMZVV6.js +17 -0
- package/dist/{chunk-E25LMLRW.js → chunk-26LEYJZH.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-2HEJ2F35.js} +5 -5
- package/dist/{chunk-PBTHKCPN.js → chunk-2HFZQUHL.js} +7 -7
- package/dist/{chunk-FO5ZOVUY.js → chunk-3BINW3FP.js} +5 -5
- package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
- package/dist/{chunk-CKXWIANG.js → chunk-4SPRNWDE.js} +18 -16
- package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
- package/dist/{chunk-XF5N4U5A.js → chunk-5DHKRSMQ.js} +9 -8
- package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
- package/dist/{chunk-EKY57CSP.js → chunk-5H3GB5BO.js} +68 -772
- package/dist/{chunk-6XXKFVSN.js → chunk-5Q2VVUKB.js} +4 -4
- package/dist/{chunk-DJVECN66.js → chunk-7RFXX52T.js} +295 -3705
- package/dist/{chunk-ZXF4XRKW.js → chunk-7RGZWPB6.js} +158 -8
- package/dist/{chunk-FYYLNIL5.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-XXQNGV4M.js → chunk-A3WNZD3P.js} +548 -72
- package/dist/{chunk-VEZEGCGW.js → chunk-B5CSFE7B.js} +23 -21
- package/dist/{chunk-2SFS6XQE.js → chunk-DGSYXYMX.js} +3 -2
- package/dist/chunk-DR52UMZW.js +21 -0
- package/dist/{chunk-ZWLZP4ZT.js → chunk-DYIM5TJT.js} +98 -12
- package/dist/{chunk-QKMUKYO7.js → chunk-E77JEWSD.js} +270 -125
- package/dist/{chunk-LYF7OHWH.js → chunk-EMYUUSFG.js} +12 -467
- package/dist/{chunk-4VP4KH3K.js → chunk-EQ63NRB7.js} +8 -8
- package/dist/chunk-FBVTI2TJ.js +518 -0
- package/dist/{chunk-R46L2BIR.js → chunk-FHJEP5SW.js} +19 -25
- package/dist/{chunk-WR67VIZY.js → chunk-GN57SG4G.js} +66 -8
- package/dist/{chunk-RD5U66HV.js → chunk-GPIEI3LY.js} +9 -9
- package/dist/{chunk-PCZJO5TI.js → chunk-GU2UIAFZ.js} +13 -178
- package/dist/chunk-GWS3VEIW.js +195 -0
- package/dist/chunk-H7IXIC72.js +103 -0
- package/dist/{chunk-3BPUFZDL.js → chunk-HLE42MG7.js} +3 -3
- package/dist/{chunk-24I7BN55.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-QNQHSOLF.js → chunk-IGWKHNIQ.js} +114 -55
- package/dist/chunk-IIZWH4XA.js +172 -0
- package/dist/{chunk-AD2SYQYC.js → chunk-IJ7RPIYJ.js} +124 -6
- package/dist/chunk-J3YUBZWY.js +382 -0
- package/dist/{chunk-5JGRAMKL.js → chunk-JOZYP4GM.js} +8 -6
- package/dist/{chunk-OH3TOQTB.js → chunk-K4XHGFR5.js} +751 -15
- package/dist/{chunk-EYPA3EGJ.js → chunk-KTYTFRMB.js} +178 -14
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/chunk-ME6CCNFO.js +108 -0
- package/dist/chunk-MXKJU4JB.js +1100 -0
- package/dist/{chunk-RY3LY4J5.js → chunk-N22QMJKY.js} +21 -18
- package/dist/chunk-NMPKI6XL.js +3006 -0
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-22NAGB7X.js → chunk-P43ETTHK.js} +5 -94
- package/dist/{chunk-NILBFAPG.js → chunk-PMDBGQSJ.js} +2 -2
- package/dist/chunk-PT7HYKEM.js +165 -0
- package/dist/chunk-RVG5JXAL.js +41 -0
- package/dist/{chunk-GEYXPTRF.js → chunk-RWSI4YD7.js} +2 -2
- package/dist/{chunk-4BPJXDWC.js → chunk-TANS5ZJS.js} +35 -19
- package/dist/{verifiers-NCBTHHN2.js → chunk-TB5666IT.js} +67 -324
- package/dist/{chunk-QM3F2GKX.js → chunk-TLQJPP24.js} +7927 -8088
- package/dist/{chunk-G7MUEIGA.js → chunk-TT36MB5S.js} +2 -1
- package/dist/chunk-TTHACPOM.js +961 -0
- package/dist/{chunk-6US73PDB.js → chunk-TYPGUK6W.js} +7 -7
- package/dist/{chunk-MFFY33HR.js → chunk-U6MBIEMB.js} +554 -209
- package/dist/chunk-VAWNZU7Z.js +242 -0
- package/dist/{chunk-IR4CFBFN.js → chunk-VCBR6CU7.js} +12 -12
- package/dist/{chunk-WHJYKASB.js → chunk-VHN4MY6O.js} +2 -2
- package/dist/{chunk-XYDYPRZI.js → chunk-VWZOAB7K.js} +10 -10
- package/dist/{chunk-ZRGEBJ4T.js → chunk-WLFILSD5.js} +48 -48
- package/dist/{chunk-K7T3E2SR.js → chunk-WNIJTQQK.js} +12 -11
- package/dist/{chunk-CYQKWTG3.js → chunk-WSB3FPX7.js} +68 -20
- package/dist/{chunk-KHSFENX2.js → chunk-WWCZ5F23.js} +116 -14
- package/dist/{chunk-XE2VEJHX.js → chunk-WXY7KU3G.js} +2 -2
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-KOHPCX4K.js → chunk-XWSF374K.js} +5 -5
- package/dist/{chunk-CJUB2JJ2.js → chunk-YS5VLNH5.js} +10 -10
- package/dist/{chunk-ZZMN5OM4.js → chunk-ZNLWCMVZ.js} +2 -2
- package/dist/{chunk-WHGPSPT5.js → chunk-ZVJ5BLO2.js} +2 -2
- package/dist/cli/index.js +33 -31
- package/dist/{clio-M2KGYUFZ.js → clio-QVTYJ57A.js} +10 -10
- package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-5TTYYX6G.js → components-ZFA3SAER.js} +9 -9
- package/dist/{config-XUUYQIWO.js → config-LW5IJFQN.js} +65 -55
- package/dist/{configure-IHJ7YOMV.js → configure-7XIZCOU4.js} +29 -24
- package/dist/{context-75MIWW3U.js → context-L3WL3X7K.js} +54 -43
- package/dist/{context-ZQ7SIFJV.js → context-N52ZA626.js} +30 -14
- package/dist/{context-74JLXAWD.js → context-Y6Y7QPR6.js} +12 -12
- package/dist/{context-clear-GYKWNUML.js → context-clear-MBQRLSDQ.js} +54 -43
- package/dist/{context-index-SSR5ECNE.js → context-index-HVMFQHK3.js} +5 -5
- package/dist/{context-working-set-UX5KEP4J.js → context-working-set-GS6DSO7F.js} +18 -18
- package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-22ZCNOM3.js} +367 -79
- package/dist/{docs-6FZSCG5B.js → docs-7LQ23DLM.js} +9 -9
- package/dist/{doctor-SVJ5BZCW.js → doctor-M7YEDGAE.js} +27 -23
- package/dist/{eval-CG6LLBLD.js → eval-BEC2WHDA.js} +73 -20
- package/dist/{evidence-ZYFIEN42.js → evidence-REJUMSKM.js} +70 -59
- package/dist/{evolve-QGEXEMDW.js → evolve-PY5ZBA5K.js} +50 -39
- package/dist/{extensions-ADGNCJJD.js → extensions-HVKU65YU.js} +7 -7
- package/dist/{fleet-S5R4ZOQY.js → fleet-7WZEWRFA.js} +236 -377
- package/dist/fleet-commands-UVHWM76J.js +70 -0
- package/dist/fleet-graph-6ULH7PES.js +125 -0
- package/dist/fleet-new-RDVJLHHH.js +48 -0
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-J53T6CCE.js} +5 -5
- package/dist/fleet-validate-72PC4SLA.js +79 -0
- package/dist/{init-5DRU55YR.js → init-OG3TPGQG.js} +69 -57
- package/dist/library-CNTMPLRF.js +217 -0
- package/dist/{memory-7YKKR6UC.js → memory-6IS7F275.js} +52 -41
- package/dist/{models-ZPOLRU2C.js → models-ENRJDA5W.js} +37 -31
- package/dist/{monitor-US5F5YGZ.js → monitor-XLDVO7TN.js} +79 -42
- package/dist/{orchestrator-E2AL4T5N.js → orchestrator-6KSPYRHA.js} +4126 -762
- package/dist/{paths-E7KYAQWE.js → paths-DBXMZMDU.js} +6 -6
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-KZ652EK6.js → reset-RZ4ER727.js} +12 -12
- package/dist/{run-SRNBKDWD.js → run-Y2CNK5RU.js} +89 -76
- package/dist/{share-CGZE33UP.js → share-A55GYP6Z.js} +37 -13
- package/dist/{skills-S2X4DLY5.js → skills-ALC5J6AT.js} +29 -13
- package/dist/{skills-eval-W2GGIC4R.js → skills-eval-JPBEBYQU.js} +60 -48
- package/dist/support-MIETYA5E.js +38 -0
- package/dist/{targets-54SWINWB.js → targets-VGNXIR3S.js} +46 -36
- package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WOBR64YA.js} +4 -4
- package/dist/{uninstall-BVLWXKBT.js → uninstall-ZJF5H5ZN.js} +9 -9
- package/dist/{upgrade-JKAR27XC.js → upgrade-FUSUAGHR.js} +31 -28
- package/dist/{usage-MSAWCLX4.js → usage-N4MKVHKD.js} +140 -68
- package/dist/verifiers-YAWOJ3H2.js +336 -0
- package/dist/{verify-X5HDROLA.js → verify-LTDHYBGY.js} +10 -9
- package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-6M7GHTBJ.js} +75 -62
- package/dist/worker/entry.js +67 -57
- package/docs/README.md +3 -2
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +11 -6
- package/docs/built-in-agents.md +26 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +83 -3
- package/docs/configuration-and-targets.md +86 -4
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +1 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +3 -3
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +74 -10
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +4 -1
- package/docs/extensions-and-sharing.md +6 -2
- package/docs/fleet-demo-runbook.md +2 -2
- package/docs/fleet-dispatch.md +228 -16
- package/docs/git-commit-provenance.md +2 -2
- package/docs/glossary.md +22 -2
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +2 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +56 -9
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +4 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +83 -65
- package/docs/resource-library.md +59 -0
- package/docs/safety-model.md +2 -2
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +37 -1
- package/docs/skills-marketplace.md +16 -3
- package/docs/tool-usage.md +14 -7
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +1 -2
- package/src/cli/argv.ts +5 -0
- package/src/cli/configure.ts +107 -23
- package/src/cli/doctor.ts +5 -1
- package/src/cli/evidence.ts +30 -25
- package/src/cli/fleet-commands.ts +37 -0
- package/src/cli/fleet-graph.ts +102 -0
- package/src/cli/fleet-new.ts +36 -0
- package/src/cli/fleet-preflight.ts +111 -0
- package/src/cli/fleet-validate.ts +30 -0
- package/src/cli/fleet.ts +173 -335
- package/src/cli/index.ts +3 -1
- package/src/cli/library.ts +190 -0
- package/src/cli/share.ts +13 -1
- package/src/cli/shared.ts +1 -0
- package/src/cli/targets.ts +4 -1
- package/src/cli/usage.ts +111 -19
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +29 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +130 -0
- package/src/core/defaults.ts +81 -0
- package/src/core/path-boundary.ts +100 -0
- package/src/domains/agents/builtins/architect.md +1 -0
- package/src/domains/agents/builtins/oracle.md +33 -0
- package/src/domains/agents/catalog.ts +13 -1
- package/src/domains/agents/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +304 -24
- package/src/domains/agents/index.ts +14 -0
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +360 -15
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/config/classify.ts +4 -0
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/active-route-planner.ts +14 -0
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- package/src/domains/dispatch/backoff.ts +2 -1
- package/src/domains/dispatch/capability-match.ts +1 -0
- package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
- package/src/domains/dispatch/contract.ts +48 -0
- package/src/domains/dispatch/delegation-plan.ts +164 -0
- package/src/domains/dispatch/execution-plan.ts +76 -5
- package/src/domains/dispatch/execution-role.ts +12 -2
- package/src/domains/dispatch/execution-scheduler.ts +183 -67
- package/src/domains/dispatch/extension.ts +412 -74
- package/src/domains/dispatch/fleet-gate.ts +14 -0
- package/src/domains/dispatch/fleet-plan.ts +63 -3
- package/src/domains/dispatch/fleet-run.ts +791 -0
- package/src/domains/dispatch/gate-role-prompts.ts +47 -0
- package/src/domains/dispatch/host-verification.ts +178 -0
- package/src/domains/dispatch/index.ts +41 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +235 -0
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +9 -4
- package/src/domains/dispatch/state.ts +36 -3
- package/src/domains/dispatch/types.ts +64 -12
- package/src/domains/dispatch/validation.ts +69 -6
- package/src/domains/dispatch/write-boundary-enforcer.ts +45 -0
- package/src/domains/dispatch/write-boundary.ts +201 -22
- package/src/domains/eval/metrics/evidence.ts +79 -2
- package/src/domains/eval/runners/clio-run.ts +12 -2
- package/src/domains/evidence/build.ts +69 -11
- package/src/domains/evidence/index.ts +21 -0
- package/src/domains/evidence/provenance.ts +46 -11
- package/src/domains/evidence/trust-projection.ts +274 -0
- package/src/domains/evidence/trust-status.ts +155 -18
- package/src/domains/evidence/types.ts +4 -0
- package/src/domains/extensions/discovery.ts +88 -1
- package/src/domains/extensions/resources.ts +20 -8
- package/src/domains/extensions/state.ts +6 -2
- package/src/domains/extensions/types.ts +4 -1
- package/src/domains/lifecycle/doctor.ts +140 -1
- package/src/domains/middleware/index.ts +15 -0
- package/src/domains/middleware/watchdog.ts +281 -0
- package/src/domains/observability/contract.ts +3 -1
- package/src/domains/observability/cost.ts +12 -1
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/observability/index.ts +10 -0
- package/src/domains/observability/out-of-turn-usage.ts +223 -0
- package/src/domains/prompts/contract.ts +3 -5
- package/src/domains/providers/extension.ts +30 -2
- package/src/domains/resources/common-loader.ts +3 -0
- package/src/domains/resources/index.ts +20 -0
- package/src/domains/resources/library.ts +326 -0
- package/src/domains/resources/prompts/loader.ts +119 -14
- package/src/domains/resources/skills/marketplace.ts +37 -12
- package/src/domains/safety/policy-engine.ts +5 -5
- package/src/domains/safety/run-effects.ts +64 -1
- package/src/domains/safety/scope.ts +7 -12
- package/src/domains/session/handoff.ts +629 -0
- package/src/domains/share/archive.ts +67 -2
- package/src/engine/acp/server.ts +4 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/worker-runtime.ts +6 -3
- package/src/entry/orchestrator.ts +37 -0
- package/src/interactive/bus-notices.ts +26 -0
- package/src/interactive/chat-loop.ts +235 -1
- package/src/interactive/chat-renderer.ts +22 -0
- package/src/interactive/cost-overlay.ts +31 -3
- package/src/interactive/council-dispatch.ts +30 -0
- package/src/interactive/council-grid.ts +213 -0
- package/src/interactive/council.ts +99 -0
- package/src/interactive/dispatch-board.ts +311 -17
- package/src/interactive/fleet-run-preview.ts +307 -0
- package/src/interactive/footer/notifications.ts +219 -0
- package/src/interactive/handoff-round.ts +56 -0
- package/src/interactive/interactive-application.ts +43 -1
- package/src/interactive/interactive-event-projection.ts +23 -1
- package/src/interactive/interactive-slash-runtime.ts +52 -2
- package/src/interactive/interactive-subscriptions.ts +14 -2
- package/src/interactive/oracle.ts +179 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +6 -0
- package/src/interactive/overlay-general-openers.ts +190 -1
- package/src/interactive/overlay-key-routing.ts +17 -1
- package/src/interactive/overlay-lifecycle.ts +41 -1
- package/src/interactive/overlay-permission-lifecycle.ts +10 -0
- package/src/interactive/overlay-resource-openers.ts +11 -3
- package/src/interactive/overlay-session-lifecycle.ts +234 -2
- package/src/interactive/overlays/fleet-run-approval.ts +208 -0
- package/src/interactive/overlays/handoff-review.ts +185 -0
- package/src/interactive/overlays/library-install-confirm.ts +151 -0
- package/src/interactive/overlays/list-overlay.ts +168 -2
- package/src/interactive/overlays/settings.ts +221 -30
- package/src/interactive/overlays/side-question.ts +139 -0
- package/src/interactive/overlays/skills-hub.ts +401 -15
- package/src/interactive/side-question.ts +171 -0
- package/src/interactive/slash-commands.ts +439 -7
- package/src/interactive/slash-spec.ts +19 -6
- package/src/interactive/theme/tokens.ts +30 -0
- package/src/interactive/turn-middleware.ts +15 -1
- package/src/interactive/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +15 -3
- package/src/interactive/watchdog-run.ts +75 -0
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-share.ts +56 -1
- package/src/interactive/worker-stream.ts +15 -0
- package/src/tools/bootstrap.ts +3 -0
- package/src/tools/compete-worktrees.ts +13 -79
- package/src/tools/dispatch-admission.ts +251 -18
- package/src/tools/dispatch-arguments.ts +84 -1
- package/src/tools/dispatch-plan.ts +165 -6
- package/src/tools/dispatch-runner.ts +364 -23
- package/src/tools/dispatch-types.ts +20 -1
- package/src/tools/dispatch.ts +72 -2
- package/src/tools/monitor.ts +29 -0
- package/src/tools/profiles.ts +18 -4
- package/src/tools/task-worktree.ts +238 -0
- package/src/tools/verify/authoring.ts +61 -1
- package/src/tools/verify/scripts.ts +62 -0
- package/src/tools/worker-evidence.ts +21 -14
- package/src/worker/spec-contract.ts +3 -1
- package/dist/chunk-HC4CLZ2Y.js +0 -68
|
@@ -135,6 +135,7 @@ export const SETTINGS_SECTIONS = [
|
|
|
135
135
|
{ id: "compaction", label: "Compaction", group: "RUNTIME" },
|
|
136
136
|
{ id: "retry", label: "Retry", group: "RUNTIME" },
|
|
137
137
|
{ id: "terminal", label: "Terminal", group: "EXPERIENCE" },
|
|
138
|
+
{ id: "watchdog", label: "Watchdog", group: "EXPERIENCE" },
|
|
138
139
|
{ id: "advanced", label: "Advanced", group: "EXPERIENCE" },
|
|
139
140
|
] as const;
|
|
140
141
|
|
|
@@ -150,6 +151,7 @@ const SETTINGS_SECTION_DESCRIPTIONS = {
|
|
|
150
151
|
compaction: "When and how the context window is summarized under pressure.",
|
|
151
152
|
retry: "Automatic recovery from transient provider and network errors.",
|
|
152
153
|
terminal: "Terminal integration and the Clio color palette.",
|
|
154
|
+
watchdog: "The opt-in turn-end verifier run, where it is routed, and whether it also fires mid-turn.",
|
|
153
155
|
advanced: "Commit provenance, runtime plugins, delegation timeouts, and links to other surfaces.",
|
|
154
156
|
} as const satisfies Record<SettingsSectionId, string>;
|
|
155
157
|
|
|
@@ -199,6 +201,10 @@ export const SETTINGS_LABELS_BY_ID = {
|
|
|
199
201
|
"terminal.tuiMode": "TUI mode",
|
|
200
202
|
"terminal.fullscreenScrollbar": "Fullscreen scrollbar",
|
|
201
203
|
"terminal.smoothStreaming": "Smooth streaming",
|
|
204
|
+
"terminal.notify": "Desktop notifications",
|
|
205
|
+
"watchdog.enabled": "Turn-end watchdog",
|
|
206
|
+
"watchdog.target": "Watchdog target",
|
|
207
|
+
"watchdog.cadenceToolCalls": "Watchdog cadence (tools)",
|
|
202
208
|
theme: "Theme",
|
|
203
209
|
runtimePlugins: "Runtime plugins",
|
|
204
210
|
"compaction.model": "Compaction model",
|
|
@@ -269,8 +275,10 @@ export const SETTINGS_SECTION_ROWS = {
|
|
|
269
275
|
"terminal.tuiMode",
|
|
270
276
|
"terminal.fullscreenScrollbar",
|
|
271
277
|
"terminal.smoothStreaming",
|
|
278
|
+
"terminal.notify",
|
|
272
279
|
"theme",
|
|
273
280
|
],
|
|
281
|
+
watchdog: ["watchdog.enabled", "watchdog.target", "watchdog.cadenceToolCalls"],
|
|
274
282
|
advanced: [
|
|
275
283
|
"runtimePlugins",
|
|
276
284
|
"attribution.gitCommits",
|
|
@@ -329,6 +337,11 @@ const SETTINGS_DESCRIPTIONS_BY_ID = {
|
|
|
329
337
|
"terminal.tuiMode": "Use regular terminal scrollback or a fullscreen transcript with a sticky composer and footer.",
|
|
330
338
|
"terminal.fullscreenScrollbar": "When the draggable transcript scrollbar is visible in fullscreen mode.",
|
|
331
339
|
"terminal.smoothStreaming": "Presentation-only pacing for streamed assistant text and thinking.",
|
|
340
|
+
"terminal.notify":
|
|
341
|
+
"Content-free desktop notification when a turn ends, a detached batch settles, or an approval parks.",
|
|
342
|
+
"watchdog.enabled": "When enabled, a turn that changed the tree is reviewed by one read-only verifier run.",
|
|
343
|
+
"watchdog.target": "Set target to route the run at a cheap local model; blank uses the session's active target.",
|
|
344
|
+
"watchdog.cadenceToolCalls": "Also fire every N tool calls inside a turn; blank fires at turn end only.",
|
|
332
345
|
theme: "Color palette. Clio ships a single tuned palette.",
|
|
333
346
|
runtimePlugins: "npm packages exporting clioRuntimes: RuntimeDescriptor[].",
|
|
334
347
|
"compaction.model": "Dedicated summarization model; blank uses the orchestrator.",
|
|
@@ -362,6 +375,14 @@ const SETTINGS_HELP_BY_ID: Partial<Record<EditableSettingId, string>> = {
|
|
|
362
375
|
"clio-policy gates the agent through Clio's safety net; agent-managed trusts the agent; deny-all blocks every tool.",
|
|
363
376
|
scope: "Choose target-level or exact target/model refs. Alt+J / Alt+K step the chat target through this list.",
|
|
364
377
|
runtimePlugins: "Comma-separated package names, loaded at startup. Restart Clio after changing.",
|
|
378
|
+
"terminal.notify":
|
|
379
|
+
"OSC 777, or OSC 9 on iTerm2, Windows Terminal, and ConEmu. Interactive TTY runs only; the body never carries prompt text, file paths, or model output.",
|
|
380
|
+
"watchdog.enabled":
|
|
381
|
+
"The verifier run is briefed with the turn's coalesced diff and the task board's current scope; its blockers become one transcript notice and nothing else. Headless and ACP runs never fire it.",
|
|
382
|
+
"watchdog.target":
|
|
383
|
+
"A watchdog run costs a worker run per mutating turn, so routing it at a local target keeps the review cheap. Leave blank to reuse whatever the session is already talking to.",
|
|
384
|
+
"watchdog.cadenceToolCalls":
|
|
385
|
+
"Mid-turn firing is how scope drift becomes visible before the turn ends. Leave blank and the watchdog fires at turn end only.",
|
|
365
386
|
keybindings:
|
|
366
387
|
"Renderer controls: Alt+O newest tool or worker details, Ctrl+Alt+O or Alt+Shift+O all of them, Alt+P live tool output, Alt+R latest reasoning, Ctrl+Alt+R or Alt+Shift+R all reasoning. Override these in settings.yaml or use /help.",
|
|
367
388
|
};
|
|
@@ -422,6 +443,14 @@ const SETTINGS_VALUE_HELP_BY_ID: Partial<Record<EditableSettingId, Record<string
|
|
|
422
443
|
auto: "show the scrollbar while scrolling or dragging",
|
|
423
444
|
always: "reserve the rightmost column for the scrollbar",
|
|
424
445
|
},
|
|
446
|
+
"terminal.notify": {
|
|
447
|
+
true: "post a content-free desktop notification on turn end, batch settlement, and a parked approval",
|
|
448
|
+
false: "never post a desktop notification",
|
|
449
|
+
},
|
|
450
|
+
"watchdog.enabled": {
|
|
451
|
+
true: "review every mutating turn with one read-only verifier run",
|
|
452
|
+
false: "no verifier run; a turn ends without a second opinion",
|
|
453
|
+
},
|
|
425
454
|
"terminal.smoothStreaming": {
|
|
426
455
|
off: "preserve the current immediate 16ms-coalesced streaming behavior",
|
|
427
456
|
auto: "pace only on a capable local TTY without accessibility or backpressure risk",
|
|
@@ -478,6 +507,13 @@ export interface SettingsCenterItem extends SettingItem {
|
|
|
478
507
|
help?: string;
|
|
479
508
|
valueHelp?: Record<string, string>;
|
|
480
509
|
defaultValue?: string;
|
|
510
|
+
/**
|
|
511
|
+
* What a free-text editor opens with when it differs from the rendered
|
|
512
|
+
* value. A row that renders its absence as prose such as `(session target)`
|
|
513
|
+
* must not hand that prose to the editor, or the operator's typing lands
|
|
514
|
+
* behind it and the placeholder is written into settings.yaml.
|
|
515
|
+
*/
|
|
516
|
+
editValue?: string;
|
|
481
517
|
}
|
|
482
518
|
|
|
483
519
|
export interface SettingsCenterSection {
|
|
@@ -557,6 +593,13 @@ function delegationAgentsAffordance(
|
|
|
557
593
|
type SubmenuTitle = string | ((width: number) => string);
|
|
558
594
|
|
|
559
595
|
export class SubmenuWrapper implements Component {
|
|
596
|
+
/**
|
|
597
|
+
* Why the last submission was refused, or null while nothing is wrong. The
|
|
598
|
+
* editor sets it instead of closing, so the operator corrects the value with
|
|
599
|
+
* the reason on screen rather than back at the row list with no reason.
|
|
600
|
+
*/
|
|
601
|
+
private problem: string | null = null;
|
|
602
|
+
|
|
560
603
|
constructor(
|
|
561
604
|
private readonly title: SubmenuTitle,
|
|
562
605
|
private readonly child: Component,
|
|
@@ -564,6 +607,10 @@ export class SubmenuWrapper implements Component {
|
|
|
564
607
|
private readonly note?: string,
|
|
565
608
|
) {}
|
|
566
609
|
|
|
610
|
+
setProblem(problem: string | null): void {
|
|
611
|
+
this.problem = problem;
|
|
612
|
+
}
|
|
613
|
+
|
|
567
614
|
render(width: number): string[] {
|
|
568
615
|
const theme = clioTheme();
|
|
569
616
|
const lines: string[] = [];
|
|
@@ -583,6 +630,11 @@ export class SubmenuWrapper implements Component {
|
|
|
583
630
|
// two columns off each one and marked the cut, so every entry wore a
|
|
584
631
|
// trailing … it had not earned.
|
|
585
632
|
lines.push(...this.child.render(Math.max(1, width - 2)).map((line) => ` ${line}`));
|
|
633
|
+
if (this.problem !== null) {
|
|
634
|
+
for (const line of wrapTextWithAnsi(this.problem, Math.max(1, width - 2))) {
|
|
635
|
+
lines.push(theme.fg("error", ` ${line}`));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
586
638
|
lines.push("");
|
|
587
639
|
lines.push(theme.fg("dim", ` ${this.hint}`));
|
|
588
640
|
return lines;
|
|
@@ -657,26 +709,108 @@ function editTextSubmenu(title: string, note?: string): SettingSubmenuBuilder {
|
|
|
657
709
|
return textInputSubmenu(title, note);
|
|
658
710
|
}
|
|
659
711
|
|
|
660
|
-
|
|
712
|
+
/**
|
|
713
|
+
* The bound a number row enforces, shared by the editor and the apply path so
|
|
714
|
+
* the two cannot drift: whatever the editor forwards, the apply path stores.
|
|
715
|
+
* The bounds mirror the config validator's, so a value the Settings Center
|
|
716
|
+
* refuses is one settings.yaml would have refused too, in the same words.
|
|
717
|
+
*/
|
|
718
|
+
interface NumberSettingRule {
|
|
719
|
+
readonly min: number;
|
|
720
|
+
readonly max?: number;
|
|
721
|
+
readonly integer: boolean;
|
|
722
|
+
/** A blank submission clears the key instead of being a parse failure. */
|
|
723
|
+
readonly allowBlank?: boolean;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const NUMBER_SETTING_RULES = {
|
|
727
|
+
"budget.sessionCeilingUsd": { min: 0, integer: false },
|
|
728
|
+
"watchdog.cadenceToolCalls": { min: 1, integer: true, allowBlank: true },
|
|
729
|
+
"delegation.defaults.connectTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
|
|
730
|
+
"delegation.defaults.turnTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
|
|
731
|
+
"delegation.defaults.permissionTimeoutMs": { min: 1, max: MAX_TIMER_DELAY_MS, integer: true },
|
|
732
|
+
} as const satisfies Partial<Record<EditableSettingId, NumberSettingRule>>;
|
|
733
|
+
|
|
734
|
+
export type NumberSettingId = keyof typeof NUMBER_SETTING_RULES;
|
|
735
|
+
|
|
736
|
+
export const NUMBER_SETTING_IDS = Object.keys(NUMBER_SETTING_RULES) as readonly NumberSettingId[];
|
|
737
|
+
|
|
738
|
+
type NumberSettingOutcome = { readonly value: number | null } | { readonly refusal: string };
|
|
739
|
+
|
|
740
|
+
function describeSubmittedText(value: string): string {
|
|
741
|
+
const trimmed = value.trim();
|
|
742
|
+
return trimmed.length === 0 ? "an empty string" : JSON.stringify(trimmed);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Parse one submitted number-row value under its rule. A null value means the
|
|
747
|
+
* operator cleared an optional key. The refusal is worded like the config
|
|
748
|
+
* validator's issue for the same key ("expected an integer >= 1, got 0"), with
|
|
749
|
+
* a "Not applied:" prefix so it reads as the outcome of this submission.
|
|
750
|
+
*/
|
|
751
|
+
function parseNumberSetting(value: string, rule: NumberSettingRule): NumberSettingOutcome {
|
|
752
|
+
const trimmed = value.trim();
|
|
753
|
+
const noun = rule.integer ? "an integer" : "a number";
|
|
754
|
+
if (trimmed.length === 0 && rule.allowBlank === true) return { value: null };
|
|
755
|
+
const parsed = trimmed.length === 0 ? Number.NaN : Number(trimmed);
|
|
756
|
+
if (!Number.isFinite(parsed) || (rule.integer && !Number.isInteger(parsed))) {
|
|
757
|
+
const shown = Number.isFinite(parsed) ? String(parsed) : describeSubmittedText(value);
|
|
758
|
+
return { refusal: `Not applied: expected ${noun}, got ${shown}.` };
|
|
759
|
+
}
|
|
760
|
+
if (parsed < rule.min) return { refusal: `Not applied: expected ${noun} >= ${rule.min}, got ${parsed}.` };
|
|
761
|
+
if (rule.max !== undefined && parsed > rule.max) {
|
|
762
|
+
return { refusal: `Not applied: expected ${noun} <= ${rule.max}, got ${parsed}.` };
|
|
763
|
+
}
|
|
764
|
+
return { value: parsed };
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* The reason a number row would refuse `value`, or null when it would apply.
|
|
769
|
+
* This is the same check the editor and `applySettingChange` run, exposed so a
|
|
770
|
+
* caller that bypasses the editor can still name the bound it hit.
|
|
771
|
+
*/
|
|
772
|
+
export function describeNumberSettingRefusal(id: NumberSettingId, value: string): string | null {
|
|
773
|
+
const outcome = parseNumberSetting(value, NUMBER_SETTING_RULES[id]);
|
|
774
|
+
return "refusal" in outcome ? outcome.refusal : null;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function numberSettingNote(rule: NumberSettingRule): string {
|
|
778
|
+
const noun = rule.integer ? "a whole number" : "a number";
|
|
779
|
+
const clears = rule.allowBlank === true ? "; blank clears it" : "";
|
|
780
|
+
if (rule.min > 0 && rule.max !== undefined) return `Use ${noun} from ${rule.min} to ${rule.max}${clears}.`;
|
|
781
|
+
if (rule.min > 0) return `Use ${noun} of at least ${rule.min}${clears}.`;
|
|
782
|
+
return `Use a non-negative ${rule.integer ? "whole number" : "number"}${clears}.`;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* A number editor that stays open on a refused submission. Closing with no
|
|
787
|
+
* value looked like a successful edit that changed nothing, so the reason is
|
|
788
|
+
* rendered under the input and the operator corrects the text in place; Esc
|
|
789
|
+
* still leaves without applying.
|
|
790
|
+
*/
|
|
791
|
+
function editNumberSubmenu(title: string, id: NumberSettingId): SettingSubmenuBuilder {
|
|
792
|
+
const rule = NUMBER_SETTING_RULES[id];
|
|
661
793
|
return (currentValue: string, done: (val?: string) => void) => {
|
|
662
794
|
const input = new Input();
|
|
663
795
|
input.setValue(currentValue);
|
|
664
796
|
input.focused = true;
|
|
665
|
-
|
|
666
|
-
const num = Number(val);
|
|
667
|
-
if (Number.isFinite(num) && num >= 0) {
|
|
668
|
-
done(val);
|
|
669
|
-
} else {
|
|
670
|
-
done();
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
input.onEscape = () => done();
|
|
674
|
-
return new SubmenuWrapper(
|
|
797
|
+
const wrapper = new SubmenuWrapper(
|
|
675
798
|
title,
|
|
676
799
|
input,
|
|
677
800
|
buildHint([{ key: "Enter", verb: "confirm" }], "back"),
|
|
678
|
-
|
|
801
|
+
numberSettingNote(rule),
|
|
679
802
|
);
|
|
803
|
+
input.onSubmit = (val) => {
|
|
804
|
+
const outcome = parseNumberSetting(val, rule);
|
|
805
|
+
if ("refusal" in outcome) {
|
|
806
|
+
wrapper.setProblem(outcome.refusal);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
wrapper.setProblem(null);
|
|
810
|
+
done(outcome.value === null ? "" : val.trim());
|
|
811
|
+
};
|
|
812
|
+
input.onEscape = () => done();
|
|
813
|
+
return wrapper;
|
|
680
814
|
};
|
|
681
815
|
}
|
|
682
816
|
|
|
@@ -1122,6 +1256,7 @@ function settingItem(
|
|
|
1122
1256
|
label?: string;
|
|
1123
1257
|
description?: string;
|
|
1124
1258
|
help?: string;
|
|
1259
|
+
editValue?: string;
|
|
1125
1260
|
},
|
|
1126
1261
|
): SettingsCenterItem {
|
|
1127
1262
|
const item: SettingsCenterItem = {
|
|
@@ -1139,6 +1274,7 @@ function settingItem(
|
|
|
1139
1274
|
};
|
|
1140
1275
|
const help = options.help ?? SETTINGS_HELP_BY_ID[id];
|
|
1141
1276
|
if (help) item.help = help;
|
|
1277
|
+
if (options.editValue !== undefined) item.editValue = options.editValue;
|
|
1142
1278
|
const valueHelp = SETTINGS_VALUE_HELP_BY_ID[id];
|
|
1143
1279
|
if (valueHelp) item.valueHelp = valueHelp;
|
|
1144
1280
|
const def = defaultValueFor(id);
|
|
@@ -1211,6 +1347,7 @@ export function buildSettingItems(
|
|
|
1211
1347
|
const compaction = settings.compaction;
|
|
1212
1348
|
const retry = settings.retry;
|
|
1213
1349
|
const terminal = settings.terminal;
|
|
1350
|
+
const watchdog = settings.watchdog;
|
|
1214
1351
|
const orchestratorThinking = thinkingChoices(
|
|
1215
1352
|
options?.providers,
|
|
1216
1353
|
settings.orchestrator.target,
|
|
@@ -1366,7 +1503,7 @@ export function buildSettingItems(
|
|
|
1366
1503
|
readOnly: true,
|
|
1367
1504
|
}),
|
|
1368
1505
|
settingItem("budget.sessionCeilingUsd", String(settings.budget.sessionCeilingUsd), {
|
|
1369
|
-
submenu: editNumberSubmenu("Edit session cost ceiling USD"),
|
|
1506
|
+
submenu: editNumberSubmenu("Edit session cost ceiling USD", "budget.sessionCeilingUsd"),
|
|
1370
1507
|
affordance: "free text",
|
|
1371
1508
|
}),
|
|
1372
1509
|
settingItem("defaults.maxTokens", String(settings.defaults.maxTokens), {
|
|
@@ -1411,10 +1548,36 @@ export function buildSettingItems(
|
|
|
1411
1548
|
settingItem("terminal.smoothStreaming", terminal.smoothStreaming, {
|
|
1412
1549
|
values: ["off", "auto", "on"],
|
|
1413
1550
|
}),
|
|
1551
|
+
settingItem("terminal.notify", String(terminal.notify), {
|
|
1552
|
+
values: ["false", "true"],
|
|
1553
|
+
}),
|
|
1414
1554
|
settingItem("theme", settings.theme, {
|
|
1415
1555
|
affordance: "single clio-coder palette",
|
|
1416
1556
|
readOnly: true,
|
|
1417
1557
|
}),
|
|
1558
|
+
settingItem("watchdog.enabled", String(watchdog.enabled), {
|
|
1559
|
+
values: ["false", "true"],
|
|
1560
|
+
}),
|
|
1561
|
+
// Both optional keys render their absence rather than a fabricated value:
|
|
1562
|
+
// an unset target means the session's own, and an unset cadence means the
|
|
1563
|
+
// watchdog fires at turn end only. Submitting an empty value clears them.
|
|
1564
|
+
settingItem("watchdog.target", watchdog.target ?? "(session target)", {
|
|
1565
|
+
submenu: editTextSubmenu("Edit watchdog target; blank uses the session's active target"),
|
|
1566
|
+
affordance: "free text",
|
|
1567
|
+
editValue: watchdog.target ?? "",
|
|
1568
|
+
}),
|
|
1569
|
+
settingItem(
|
|
1570
|
+
"watchdog.cadenceToolCalls",
|
|
1571
|
+
watchdog.cadenceToolCalls === undefined ? "(turn end only)" : String(watchdog.cadenceToolCalls),
|
|
1572
|
+
{
|
|
1573
|
+
submenu: editNumberSubmenu(
|
|
1574
|
+
"Edit watchdog cadence in tool calls; blank fires at turn end only",
|
|
1575
|
+
"watchdog.cadenceToolCalls",
|
|
1576
|
+
),
|
|
1577
|
+
affordance: "free text",
|
|
1578
|
+
editValue: watchdog.cadenceToolCalls === undefined ? "" : String(watchdog.cadenceToolCalls),
|
|
1579
|
+
},
|
|
1580
|
+
),
|
|
1418
1581
|
settingItem("runtimePlugins", settings.runtimePlugins.length > 0 ? settings.runtimePlugins.join(", ") : "(none)", {
|
|
1419
1582
|
submenu: editTextSubmenu("Edit runtime plugins comma-separated list", "Restart Clio to load changes."),
|
|
1420
1583
|
affordance: "free text",
|
|
@@ -1431,15 +1594,15 @@ export function buildSettingItems(
|
|
|
1431
1594
|
affordance: "free text",
|
|
1432
1595
|
}),
|
|
1433
1596
|
settingItem("delegation.defaults.connectTimeoutMs", String(settings.delegation.defaults.connectTimeoutMs), {
|
|
1434
|
-
submenu: editNumberSubmenu("Edit delegate connect timeout (ms)"),
|
|
1597
|
+
submenu: editNumberSubmenu("Edit delegate connect timeout (ms)", "delegation.defaults.connectTimeoutMs"),
|
|
1435
1598
|
affordance: "free text",
|
|
1436
1599
|
}),
|
|
1437
1600
|
settingItem("delegation.defaults.turnTimeoutMs", String(settings.delegation.defaults.turnTimeoutMs), {
|
|
1438
|
-
submenu: editNumberSubmenu("Edit delegate turn timeout (ms)"),
|
|
1601
|
+
submenu: editNumberSubmenu("Edit delegate turn timeout (ms)", "delegation.defaults.turnTimeoutMs"),
|
|
1439
1602
|
affordance: "free text",
|
|
1440
1603
|
}),
|
|
1441
1604
|
settingItem("delegation.defaults.permissionTimeoutMs", String(settings.delegation.defaults.permissionTimeoutMs), {
|
|
1442
|
-
submenu: editNumberSubmenu("Edit delegate permission timeout (ms)"),
|
|
1605
|
+
submenu: editNumberSubmenu("Edit delegate permission timeout (ms)", "delegation.defaults.permissionTimeoutMs"),
|
|
1443
1606
|
affordance: "free text",
|
|
1444
1607
|
}),
|
|
1445
1608
|
settingItem("keybindings", keybindingCount > 0 ? `${keybindingCount} override(s)` : "(defaults)", {
|
|
@@ -1747,9 +1910,15 @@ function applyNonNegativeInteger(value: string, set: (next: number) => void): vo
|
|
|
1747
1910
|
if (Number.isFinite(parsed) && parsed >= 0) set(Math.floor(parsed));
|
|
1748
1911
|
}
|
|
1749
1912
|
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1913
|
+
/**
|
|
1914
|
+
* Store a number-row submission under the row's shared rule. A refused value
|
|
1915
|
+
* leaves the setting alone; the editor has already shown the operator why, and
|
|
1916
|
+
* `describeNumberSettingRefusal` names the reason for any other caller.
|
|
1917
|
+
*/
|
|
1918
|
+
function applyNumberSetting(id: NumberSettingId, value: string, set: (next: number | null) => void): void {
|
|
1919
|
+
const outcome = parseNumberSetting(value, NUMBER_SETTING_RULES[id]);
|
|
1920
|
+
if ("refusal" in outcome) return;
|
|
1921
|
+
set(outcome.value);
|
|
1753
1922
|
}
|
|
1754
1923
|
|
|
1755
1924
|
/**
|
|
@@ -1898,6 +2067,28 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
|
|
|
1898
2067
|
settings.terminal.fullscreenScrollbar = value;
|
|
1899
2068
|
}
|
|
1900
2069
|
return;
|
|
2070
|
+
case "terminal.notify":
|
|
2071
|
+
if (value === "true" || value === "false") settings.terminal.notify = value === "true";
|
|
2072
|
+
return;
|
|
2073
|
+
case "watchdog.enabled":
|
|
2074
|
+
if (value === "true" || value === "false") settings.watchdog.enabled = value === "true";
|
|
2075
|
+
return;
|
|
2076
|
+
// Both watchdog options are absent-by-default, and an empty submission is
|
|
2077
|
+
// how the operator says "go back to the default" from a text row. Deleting
|
|
2078
|
+
// the key rather than storing a blank keeps settings.yaml matching what the
|
|
2079
|
+
// config validator accepts.
|
|
2080
|
+
case "watchdog.target": {
|
|
2081
|
+
const trimmed = value.trim();
|
|
2082
|
+
if (trimmed) settings.watchdog.target = trimmed;
|
|
2083
|
+
else delete settings.watchdog.target;
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
case "watchdog.cadenceToolCalls":
|
|
2087
|
+
applyNumberSetting("watchdog.cadenceToolCalls", value, (next) => {
|
|
2088
|
+
if (next === null) delete settings.watchdog.cadenceToolCalls;
|
|
2089
|
+
else settings.watchdog.cadenceToolCalls = next;
|
|
2090
|
+
});
|
|
2091
|
+
return;
|
|
1901
2092
|
case "terminal.smoothStreaming":
|
|
1902
2093
|
if (value === "off" || value === "auto" || value === "on") settings.terminal.smoothStreaming = value;
|
|
1903
2094
|
return;
|
|
@@ -1908,18 +2099,18 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
|
|
|
1908
2099
|
.filter(Boolean);
|
|
1909
2100
|
return;
|
|
1910
2101
|
case "delegation.defaults.connectTimeoutMs":
|
|
1911
|
-
|
|
1912
|
-
settings.delegation.defaults.connectTimeoutMs = next;
|
|
2102
|
+
applyNumberSetting("delegation.defaults.connectTimeoutMs", value, (next) => {
|
|
2103
|
+
if (next !== null) settings.delegation.defaults.connectTimeoutMs = next;
|
|
1913
2104
|
});
|
|
1914
2105
|
return;
|
|
1915
2106
|
case "delegation.defaults.turnTimeoutMs":
|
|
1916
|
-
|
|
1917
|
-
settings.delegation.defaults.turnTimeoutMs = next;
|
|
2107
|
+
applyNumberSetting("delegation.defaults.turnTimeoutMs", value, (next) => {
|
|
2108
|
+
if (next !== null) settings.delegation.defaults.turnTimeoutMs = next;
|
|
1918
2109
|
});
|
|
1919
2110
|
return;
|
|
1920
2111
|
case "delegation.defaults.permissionTimeoutMs":
|
|
1921
|
-
|
|
1922
|
-
settings.delegation.defaults.permissionTimeoutMs = next;
|
|
2112
|
+
applyNumberSetting("delegation.defaults.permissionTimeoutMs", value, (next) => {
|
|
2113
|
+
if (next !== null) settings.delegation.defaults.permissionTimeoutMs = next;
|
|
1923
2114
|
});
|
|
1924
2115
|
return;
|
|
1925
2116
|
case "orchestrator.target": {
|
|
@@ -1970,11 +2161,11 @@ export function applySettingChange(settings: ClioSettings, id: string, value: st
|
|
|
1970
2161
|
.map((v) => v.trim())
|
|
1971
2162
|
.filter(Boolean);
|
|
1972
2163
|
return;
|
|
1973
|
-
case "budget.sessionCeilingUsd":
|
|
1974
|
-
|
|
1975
|
-
|
|
2164
|
+
case "budget.sessionCeilingUsd":
|
|
2165
|
+
applyNumberSetting("budget.sessionCeilingUsd", value, (next) => {
|
|
2166
|
+
if (next !== null) settings.budget.sessionCeilingUsd = next;
|
|
2167
|
+
});
|
|
1976
2168
|
return;
|
|
1977
|
-
}
|
|
1978
2169
|
}
|
|
1979
2170
|
}
|
|
1980
2171
|
|
|
@@ -2851,7 +3042,7 @@ export class SettingsCenter implements Component {
|
|
|
2851
3042
|
const item = this.selectedItem();
|
|
2852
3043
|
if (!item || item.readOnly) return;
|
|
2853
3044
|
if (item.submenu) {
|
|
2854
|
-
this.submenuComponent = item.submenu(item.currentValue, (selectedValue) => {
|
|
3045
|
+
this.submenuComponent = item.submenu(item.editValue ?? item.currentValue, (selectedValue) => {
|
|
2855
3046
|
this.submenuComponent = null;
|
|
2856
3047
|
if (selectedValue !== undefined) this.prepareScopeConfirm(item, selectedValue);
|
|
2857
3048
|
this.options.requestRender?.();
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/btw` overlay: the question, then the answer as it streams.
|
|
3
|
+
*
|
|
4
|
+
* The overlay is the whole surface for a side question. Nothing it shows is
|
|
5
|
+
* appended to the transcript, so closing it is the end of the exchange: the
|
|
6
|
+
* answer was for the operator, in the moment, and the session never saw it.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { type Component, type OverlayHandle, type TUI, wrapTextWithAnsi } from "../../engine/tui.js";
|
|
10
|
+
import { buildResponsiveHint, showClioOverlayFrame } from "../overlay-frame.js";
|
|
11
|
+
import { clioTheme, GLYPH, rule } from "../theme/index.js";
|
|
12
|
+
|
|
13
|
+
export const SIDE_QUESTION_OVERLAY_TITLE = "Side question";
|
|
14
|
+
|
|
15
|
+
/** What the overlay is showing right now. */
|
|
16
|
+
export type SideQuestionOverlayPhase =
|
|
17
|
+
| { kind: "streaming"; text: string }
|
|
18
|
+
| { kind: "answered"; text: string }
|
|
19
|
+
| { kind: "aborted"; text: string }
|
|
20
|
+
| { kind: "error"; reason: string };
|
|
21
|
+
|
|
22
|
+
export interface SideQuestionOverlaySession extends OverlayHandle {
|
|
23
|
+
/** Replace the streamed answer text. */
|
|
24
|
+
setAnswer(text: string): void;
|
|
25
|
+
/** Settle the overlay on a final phase. */
|
|
26
|
+
settle(phase: SideQuestionOverlayPhase): void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface OpenSideQuestionOverlayOptions {
|
|
30
|
+
question: string;
|
|
31
|
+
/** Live terminal width, so the box tracks the window it opened in. */
|
|
32
|
+
columns: number;
|
|
33
|
+
/** Esc: abort a streaming round, then close. */
|
|
34
|
+
onClose: () => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const SIDE_QUESTION_OVERLAY_MIN_WIDTH = 44;
|
|
38
|
+
const SIDE_QUESTION_OVERLAY_MAX_WIDTH = 100;
|
|
39
|
+
|
|
40
|
+
export function sideQuestionOverlayWidth(columns: number): number {
|
|
41
|
+
return Math.max(SIDE_QUESTION_OVERLAY_MIN_WIDTH, Math.min(SIDE_QUESTION_OVERLAY_MAX_WIDTH, columns - 4));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Render the overlay body. Pure so the layout is unit-testable without a TUI:
|
|
46
|
+
* the question in dim above a rule, then the answer, then one status line that
|
|
47
|
+
* says whether the round is still running, cancelled, or failed.
|
|
48
|
+
*/
|
|
49
|
+
export function formatSideQuestionBody(
|
|
50
|
+
question: string,
|
|
51
|
+
phase: SideQuestionOverlayPhase,
|
|
52
|
+
width: number,
|
|
53
|
+
spinner: string,
|
|
54
|
+
): string[] {
|
|
55
|
+
const theme = clioTheme();
|
|
56
|
+
const contentWidth = Math.max(1, Math.floor(width));
|
|
57
|
+
const lines: string[] = [];
|
|
58
|
+
for (const line of wrapTextWithAnsi(theme.fg("dim", `${GLYPH.user} ${question}`), contentWidth)) lines.push(line);
|
|
59
|
+
lines.push(rule(theme, contentWidth));
|
|
60
|
+
if (phase.kind === "error") {
|
|
61
|
+
for (const line of wrapTextWithAnsi(theme.fg("error", phase.reason), contentWidth)) lines.push(line);
|
|
62
|
+
return lines;
|
|
63
|
+
}
|
|
64
|
+
const body = phase.text.trim();
|
|
65
|
+
if (body.length > 0) {
|
|
66
|
+
for (const paragraph of body.split("\n")) {
|
|
67
|
+
if (paragraph.length === 0) {
|
|
68
|
+
lines.push("");
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
for (const line of wrapTextWithAnsi(theme.fg("muted", paragraph), contentWidth)) lines.push(line);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (phase.kind === "streaming") {
|
|
75
|
+
lines.push(theme.fg("dim", body.length > 0 ? spinner : `${spinner} asking…`));
|
|
76
|
+
} else if (phase.kind === "aborted") {
|
|
77
|
+
lines.push(theme.fg("warning", "cancelled"));
|
|
78
|
+
} else if (body.length === 0) {
|
|
79
|
+
lines.push(theme.fg("dim", "the model returned no text"));
|
|
80
|
+
}
|
|
81
|
+
return lines;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const SPINNER_FRAMES = ["·", "•", "●", "•"] as const;
|
|
85
|
+
|
|
86
|
+
class SideQuestionOverlayBody implements Component {
|
|
87
|
+
private phase: SideQuestionOverlayPhase = { kind: "streaming", text: "" };
|
|
88
|
+
private frame = 0;
|
|
89
|
+
|
|
90
|
+
constructor(private readonly question: string) {}
|
|
91
|
+
|
|
92
|
+
set(phase: SideQuestionOverlayPhase): void {
|
|
93
|
+
this.phase = phase;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
render(width: number): string[] {
|
|
97
|
+
if (this.phase.kind === "streaming") this.frame = (this.frame + 1) % SPINNER_FRAMES.length;
|
|
98
|
+
return formatSideQuestionBody(this.question, this.phase, width, SPINNER_FRAMES[this.frame] ?? "·");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
invalidate(): void {}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Mount the overlay. `setAnswer` is called from the stream; `settle` closes the
|
|
106
|
+
* round's live state without closing the overlay, so the operator reads the
|
|
107
|
+
* answer and leaves with Esc.
|
|
108
|
+
*/
|
|
109
|
+
export function openSideQuestionOverlay(tui: TUI, options: OpenSideQuestionOverlayOptions): SideQuestionOverlaySession {
|
|
110
|
+
const body = new SideQuestionOverlayBody(options.question);
|
|
111
|
+
let settled = false;
|
|
112
|
+
const handle = showClioOverlayFrame(tui, body, {
|
|
113
|
+
anchor: "center",
|
|
114
|
+
width: sideQuestionOverlayWidth(options.columns),
|
|
115
|
+
title: SIDE_QUESTION_OVERLAY_TITLE,
|
|
116
|
+
// Esc means two things here and the footer names the live one: it aborts a
|
|
117
|
+
// round that is still streaming, and it closes one that has settled.
|
|
118
|
+
footerHint: (innerWidth) => buildResponsiveHint([], { key: "Esc", verb: settled ? "close" : "cancel" })(innerWidth),
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
...handle,
|
|
122
|
+
setAnswer(text: string): void {
|
|
123
|
+
if (settled) return;
|
|
124
|
+
body.set({ kind: "streaming", text });
|
|
125
|
+
body.invalidate();
|
|
126
|
+
tui.requestRender();
|
|
127
|
+
},
|
|
128
|
+
settle(phase: SideQuestionOverlayPhase): void {
|
|
129
|
+
settled = true;
|
|
130
|
+
body.set(phase);
|
|
131
|
+
body.invalidate();
|
|
132
|
+
tui.requestRender();
|
|
133
|
+
},
|
|
134
|
+
hide(): void {
|
|
135
|
+
options.onClose();
|
|
136
|
+
handle.hide();
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|