@iowarp/clio-coder 0.3.8 → 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 +49 -0
- package/README.md +7 -3
- package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
- package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
- package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
- package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
- package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
- package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
- package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
- package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
- package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
- package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
- package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
- package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
- package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
- package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
- package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
- package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
- package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
- package/dist/chunk-7C6RYZGQ.js +391 -0
- package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
- package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
- package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
- package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
- package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
- package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
- package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
- package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
- package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
- package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
- package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
- package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
- package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
- package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
- package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
- package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
- package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
- package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
- package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
- package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
- package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
- package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
- package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
- package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
- package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
- package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
- package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
- package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
- package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
- package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
- package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
- package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
- package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
- package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
- package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
- package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
- package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
- package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
- package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
- package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
- package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
- package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
- package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
- package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
- package/dist/chunk-YSEHGPCT.js +127 -0
- package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
- package/dist/cli/index.js +27 -27
- package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
- package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
- package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
- package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
- package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
- package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
- package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
- package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
- package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
- package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
- package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
- package/dist/doctor-TWBWFK5V.js +165 -0
- package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
- package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
- package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
- package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
- package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
- package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
- package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
- package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
- package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
- package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
- package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
- package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
- package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
- package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
- package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
- package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
- package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
- package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
- package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
- package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
- package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
- package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
- package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
- package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
- package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
- package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
- package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
- package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
- package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
- package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
- package/dist/worker/entry.js +29 -33
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +6 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +23 -2
- package/docs/commands-and-modes.md +1 -1
- package/docs/configuration-and-targets.md +30 -3
- 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 +11 -10
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +3 -1
- package/docs/fleet-dispatch.md +4 -4
- 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 +53 -2
- package/docs/proactive-memory.md +127 -14
- package/docs/prompt-envelope-and-tools.md +19 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +19 -3
- 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 -1
- package/src/cli/agents.ts +1 -1
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor.ts +3 -1
- package/src/cli/eval.ts +80 -16
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet.ts +32 -3
- package/src/cli/targets.ts +44 -13
- package/src/cli/trace.ts +63 -4
- package/src/cli/usage.ts +63 -14
- package/src/core/bus-events.ts +29 -1
- package/src/core/cache-telemetry.ts +42 -0
- 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/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +36 -2
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/dispatch/admission.ts +40 -3
- package/src/domains/dispatch/capacity-lease.ts +98 -9
- package/src/domains/dispatch/contract.ts +11 -0
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/extension.ts +166 -42
- package/src/domains/dispatch/fleet-run.ts +23 -3
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +3 -0
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/reservation-store.ts +116 -8
- package/src/domains/dispatch/state.ts +4 -0
- package/src/domains/dispatch/worker-spawn.ts +25 -11
- package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
- package/src/domains/dispatch/write-boundary.ts +62 -1
- 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/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 +127 -0
- 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 +2 -13
- package/src/domains/evidence/eval.ts +2 -12
- package/src/domains/evidence/findings-markdown.ts +33 -0
- package/src/domains/evidence/run-trust.ts +7 -113
- package/src/domains/evidence/trust-projection.ts +2 -2
- package/src/domains/extensions/compatibility.ts +285 -0
- package/src/domains/extensions/discovery.ts +38 -3
- package/src/domains/extensions/resources.ts +1 -1
- package/src/domains/extensions/state.ts +12 -3
- package/src/domains/extensions/types.ts +2 -0
- package/src/domains/lifecycle/doctor.ts +69 -1
- 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/providers/endpoint-capacity.ts +96 -0
- 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/prompts/loader.ts +95 -33
- package/src/domains/safety/call-target.ts +52 -0
- package/src/domains/safety/run-effects.ts +35 -4
- 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/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/provider-payload.ts +29 -1
- 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 +84 -12
- 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-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 +35 -1
- 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/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/view-overlay.ts +28 -3
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/dispatch-plan.ts +17 -9
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/registry.ts +16 -0
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-R346GLFC.js +0 -31
- package/dist/doctor-M7YEDGAE.js +0 -91
|
@@ -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";
|
|
@@ -1482,6 +1486,7 @@ export function buildSettingItems(
|
|
|
1482
1486
|
}),
|
|
1483
1487
|
fleetGroupHeader("fleet.group.placement", "Placement"),
|
|
1484
1488
|
...fleetNodeRows(options?.getFleetNodes?.() ?? []),
|
|
1489
|
+
...fleetEndpointRows(options?.providers),
|
|
1485
1490
|
settingItem("targets", "", {
|
|
1486
1491
|
description: "Live inference target inventory and routing roles.",
|
|
1487
1492
|
affordance: "column heading",
|
|
@@ -1860,6 +1865,35 @@ function fleetNodeRows(nodes: ReadonlyArray<FleetNodeSnapshot>): SettingsCenterI
|
|
|
1860
1865
|
});
|
|
1861
1866
|
}
|
|
1862
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
|
+
|
|
1863
1897
|
export function buildSettingsSections(items: readonly SettingsCenterItem[]): SettingsCenterSection[] {
|
|
1864
1898
|
return SETTINGS_SECTIONS.map((section) => ({
|
|
1865
1899
|
id: section.id,
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import type { HintEntry } from "./overlay-frame.js";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* The key that opens and closes local inspection of a parked mutation. It lives
|
|
5
|
+
* here rather than beside the preview builder so the composer rail can name it
|
|
6
|
+
* without pulling the diff machinery into the Stage 0 boot shell closure.
|
|
7
|
+
*/
|
|
8
|
+
export const MUTATION_PREVIEW_KEY = "v";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Whether this card has a mutation to read, and whether it is open.
|
|
12
|
+
* `none` covers every ask with nothing local to inspect, including a worker
|
|
13
|
+
* escalation, whose arguments never leave the worker.
|
|
14
|
+
*/
|
|
15
|
+
export type PermissionInspectionHint = "none" | "closed" | "open";
|
|
16
|
+
|
|
3
17
|
/**
|
|
4
18
|
* The keys that answer a parked call, as data, so the dialog footer and the
|
|
5
19
|
* composer rail render the same policy at any width.
|
|
@@ -16,17 +30,35 @@ import type { HintEntry } from "./overlay-frame.js";
|
|
|
16
30
|
*
|
|
17
31
|
* Esc says `deny`, not `close`: closing the dialog denies the call, and on the
|
|
18
32
|
* one surface where a misread is a wrong decision about a tool call the key
|
|
19
|
-
* names its effect.
|
|
33
|
+
* names its effect. It keeps that meaning while the mutation is open, so the
|
|
34
|
+
* inspect key is what puts the mutation away again.
|
|
20
35
|
*
|
|
21
36
|
* With a draft in the composer, Enter is inert (issue #186): the habitual send
|
|
22
37
|
* key must not resolve toward "allow" on a safety rail. The entry then says so
|
|
23
38
|
* and names the key that clears the draft, which the router passes through.
|
|
39
|
+
*
|
|
40
|
+
* A parked `write` or `edit` adds the inspect key (issue #254). It is
|
|
41
|
+
* droppable, and deliberately so: at 40 columns the four entries do not fit,
|
|
42
|
+
* and the three keys that answer the call outrank the one that reads it. The
|
|
43
|
+
* key keeps working at every width; the `/help` Autonomy & safety net topic is
|
|
44
|
+
* where it is documented for the widths that cannot show it.
|
|
24
45
|
*/
|
|
25
|
-
export function permissionHintEntries(
|
|
46
|
+
export function permissionHintEntries(
|
|
47
|
+
composerHasDraft = false,
|
|
48
|
+
inspection: PermissionInspectionHint = "none",
|
|
49
|
+
): HintEntry[] {
|
|
26
50
|
return [
|
|
27
51
|
composerHasDraft
|
|
28
52
|
? { key: "Backspace", verb: "clear the draft to allow", short: "clear draft", critical: true }
|
|
29
53
|
: { key: "Enter", verb: "allow once", short: "allow", critical: true },
|
|
54
|
+
...(inspection === "none"
|
|
55
|
+
? []
|
|
56
|
+
: inspection === "open"
|
|
57
|
+
? [
|
|
58
|
+
{ key: "↑↓", verb: "scroll", critical: false },
|
|
59
|
+
{ key: MUTATION_PREVIEW_KEY, verb: "hide mutation", short: "hide", critical: false },
|
|
60
|
+
]
|
|
61
|
+
: [{ key: MUTATION_PREVIEW_KEY, verb: "inspect mutation", short: "inspect", critical: false }]),
|
|
30
62
|
{ key: "s", verb: "stop turn", short: "stop", critical: true },
|
|
31
63
|
// With a draft, deny and stop are the only immediate answers, so Esc
|
|
32
64
|
// outranks the narrowing that would otherwise drop it first.
|
|
@@ -6,13 +6,21 @@ import {
|
|
|
6
6
|
decisionFactsForPermission,
|
|
7
7
|
} from "../domains/safety/decision-presentation.js";
|
|
8
8
|
import type { Component, OverlayOptions, TUI } from "../engine/tui.js";
|
|
9
|
+
import {
|
|
10
|
+
MUTATION_PREVIEW_VISIBLE_ROWS,
|
|
11
|
+
type MutationFacts,
|
|
12
|
+
type MutationInspector,
|
|
13
|
+
type MutationPreview,
|
|
14
|
+
mutationFactsLine,
|
|
15
|
+
mutationPreviewWindow,
|
|
16
|
+
} from "./mutation-preview.js";
|
|
9
17
|
import { fitHintEntries } from "./overlay-frame.js";
|
|
10
|
-
import { permissionHintEntries } from "./permission-hint.js";
|
|
18
|
+
import { MUTATION_PREVIEW_KEY, type PermissionInspectionHint, permissionHintEntries } from "./permission-hint.js";
|
|
11
19
|
import type { ClioToken } from "./theme/index.js";
|
|
12
20
|
|
|
13
21
|
export { type AskAxis, askAxis } from "../domains/safety/approval-axis.js";
|
|
14
22
|
export { describeCallTarget, sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
15
|
-
export { permissionHintEntries } from "./permission-hint.js";
|
|
23
|
+
export { MUTATION_PREVIEW_KEY, type PermissionInspectionHint, permissionHintEntries } from "./permission-hint.js";
|
|
16
24
|
|
|
17
25
|
export interface ApprovalRequestView {
|
|
18
26
|
requestId: string;
|
|
@@ -34,6 +42,14 @@ export interface ApprovalRequestView {
|
|
|
34
42
|
* when nothing meaningful is derivable.
|
|
35
43
|
*/
|
|
36
44
|
target?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Size and digest facts for a parked `write` or `edit`, and nothing else
|
|
47
|
+
* about it. The mutation text is deliberately absent: this view is what
|
|
48
|
+
* reaches the transcript row, the parked notice, and the approval-state
|
|
49
|
+
* event, so anything on it has already left the overlay. The text itself is
|
|
50
|
+
* held by the inspector the overlay opener is given (issue #254).
|
|
51
|
+
*/
|
|
52
|
+
mutation?: MutationFacts;
|
|
37
53
|
queueDepth?: number;
|
|
38
54
|
}
|
|
39
55
|
|
|
@@ -79,11 +95,59 @@ export function permissionOverlayPlacement(
|
|
|
79
95
|
};
|
|
80
96
|
}
|
|
81
97
|
|
|
82
|
-
|
|
83
|
-
|
|
98
|
+
/**
|
|
99
|
+
* The overlay body, plus the inspection state the key router drives. The
|
|
100
|
+
* preview is built on the first toggle rather than per frame, so the file read
|
|
101
|
+
* and the diff happen once when the operator asks for them.
|
|
102
|
+
*/
|
|
103
|
+
export interface PermissionOverlayBodyHandle extends Component {
|
|
104
|
+
/** Whether this card has a mutation the operator can read locally. */
|
|
105
|
+
canInspect(): boolean;
|
|
106
|
+
isInspecting(): boolean;
|
|
107
|
+
toggleInspect(): void;
|
|
108
|
+
scrollInspect(delta: number): void;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class PermissionOverlayBody implements PermissionOverlayBodyHandle {
|
|
112
|
+
private preview: MutationPreview | null = null;
|
|
113
|
+
private scroll = 0;
|
|
114
|
+
private lastLineCount = 0;
|
|
115
|
+
|
|
116
|
+
constructor(
|
|
117
|
+
private readonly view: ApprovalRequestView,
|
|
118
|
+
private readonly inspect?: MutationInspector,
|
|
119
|
+
) {}
|
|
120
|
+
|
|
121
|
+
canInspect(): boolean {
|
|
122
|
+
return this.inspect !== undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
isInspecting(): boolean {
|
|
126
|
+
return this.preview !== null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
toggleInspect(): void {
|
|
130
|
+
if (this.inspect === undefined) return;
|
|
131
|
+
if (this.preview !== null) {
|
|
132
|
+
this.preview = null;
|
|
133
|
+
this.scroll = 0;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this.preview = this.inspect();
|
|
137
|
+
this.scroll = 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
scrollInspect(delta: number): void {
|
|
141
|
+
if (this.preview === null) return;
|
|
142
|
+
const maxScroll = Math.max(0, this.lastLineCount - MUTATION_PREVIEW_VISIBLE_ROWS);
|
|
143
|
+
this.scroll = Math.max(0, Math.min(this.scroll + delta, maxScroll));
|
|
144
|
+
}
|
|
84
145
|
|
|
85
146
|
render(width: number): string[] {
|
|
86
|
-
return permissionOverlayLines(this.view, width);
|
|
147
|
+
if (this.preview === null) return permissionOverlayLines(this.view, width);
|
|
148
|
+
const rendered = permissionInspectionLines(this.view, this.preview, width, this.scroll);
|
|
149
|
+
this.lastLineCount = rendered.wrappedLineCount;
|
|
150
|
+
return rendered.lines;
|
|
87
151
|
}
|
|
88
152
|
|
|
89
153
|
invalidate(): void {}
|
|
@@ -167,14 +231,74 @@ export function permissionOverlayTone(view: ApprovalRequestView): ClioToken {
|
|
|
167
231
|
* three columns the bottom border spends on `─ ` and the trailing space come
|
|
168
232
|
* off the top, which is what `buildResponsiveHint` does for every other frame.
|
|
169
233
|
*/
|
|
170
|
-
export function permissionOverlayHint(
|
|
171
|
-
|
|
234
|
+
export function permissionOverlayHint(
|
|
235
|
+
innerWidth: number,
|
|
236
|
+
composerHasDraft = false,
|
|
237
|
+
inspection: PermissionInspectionHint = "none",
|
|
238
|
+
): string {
|
|
239
|
+
return fitHintEntries(permissionHintEntries(composerHasDraft, inspection), innerWidth - 3);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** The tools whose parked call mutates a file, so a card without a preview owes the operator a reason. */
|
|
243
|
+
function isFileMutationTool(tool: string): boolean {
|
|
244
|
+
return tool === "write" || tool === "edit";
|
|
172
245
|
}
|
|
173
246
|
|
|
174
247
|
function actionConsequence(presentation: DecisionPresentation, id: "deny" | "stop"): string {
|
|
175
248
|
return presentation.requiredActions.find((action) => action.id === id)?.consequence ?? "";
|
|
176
249
|
}
|
|
177
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Hard wrap for mutation text. Prose wraps on words; a proposed file line and a
|
|
253
|
+
* diff row do not, because the column a character sits in is part of what the
|
|
254
|
+
* operator is reading. The continuation marker says the row was folded rather
|
|
255
|
+
* than cut, which is the difference between a wrap and unmarked truncation.
|
|
256
|
+
*/
|
|
257
|
+
function wrapMutationLine(value: string, max: number): string[] {
|
|
258
|
+
if (max <= 1) return [value];
|
|
259
|
+
if (value.length <= max) return [value];
|
|
260
|
+
const lines: string[] = [];
|
|
261
|
+
let remaining = value;
|
|
262
|
+
while (remaining.length > max) {
|
|
263
|
+
lines.push(`${remaining.slice(0, max - 1)}↩`);
|
|
264
|
+
remaining = remaining.slice(max - 1);
|
|
265
|
+
}
|
|
266
|
+
lines.push(remaining);
|
|
267
|
+
return lines;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The card with the mutation open. The tool, target, and digest facts stay at
|
|
272
|
+
* the top so the bytes on screen remain tied to the call they belong to, and
|
|
273
|
+
* the standing safety prose gives way to the mutation itself: the operator
|
|
274
|
+
* opened this to read what changes, and the keys that answer the ask are on the
|
|
275
|
+
* footer throughout.
|
|
276
|
+
*/
|
|
277
|
+
function permissionInspectionLines(
|
|
278
|
+
view: ApprovalRequestView,
|
|
279
|
+
preview: MutationPreview,
|
|
280
|
+
width: number,
|
|
281
|
+
scroll: number,
|
|
282
|
+
): { lines: string[]; wrappedLineCount: number } {
|
|
283
|
+
const content = Math.max(8, Math.floor(width));
|
|
284
|
+
const wrapped = preview.body.flatMap((line) => wrapMutationLine(line, content));
|
|
285
|
+
const pane = mutationPreviewWindow(wrapped, scroll);
|
|
286
|
+
const notes: string[] = [];
|
|
287
|
+
if (preview.neutralized) notes.push("control characters shown as ·");
|
|
288
|
+
if (preview.tabsExpanded) notes.push("tabs shown as spaces");
|
|
289
|
+
const lines = [
|
|
290
|
+
field("Tool: ", `${view.tool} · Action: ${view.actionClass}`, content),
|
|
291
|
+
...(view.target !== undefined && view.target.length > 0 ? [field("Target: ", view.target, content)] : []),
|
|
292
|
+
...wrapSentence(`Mutation: ${mutationFactsLine(preview.facts)}`, content),
|
|
293
|
+
"",
|
|
294
|
+
...wrapSentence(preview.heading, content),
|
|
295
|
+
...pane.window,
|
|
296
|
+
...(pane.position === null ? [] : [pane.position]),
|
|
297
|
+
...(notes.length > 0 ? [`Rendered safely: ${notes.join(", ")}.`] : []),
|
|
298
|
+
];
|
|
299
|
+
return { lines, wrappedLineCount: wrapped.length };
|
|
300
|
+
}
|
|
301
|
+
|
|
178
302
|
/**
|
|
179
303
|
* The overlay's body, laid out for the width the frame gives it.
|
|
180
304
|
*
|
|
@@ -193,6 +317,24 @@ export function permissionOverlayLines(view: ApprovalRequestView, width: number)
|
|
|
193
317
|
const lines = [
|
|
194
318
|
field("Tool: ", `${view.tool} · Action: ${view.actionClass}`, content),
|
|
195
319
|
...(view.target !== undefined && view.target.length > 0 ? [field("Target: ", view.target, content)] : []),
|
|
320
|
+
// Size and digest stay on the collapsed card whether or not the operator
|
|
321
|
+
// opens the mutation, so the decision always carries the identity of the
|
|
322
|
+
// bytes it applies to.
|
|
323
|
+
// Wrapped, never ellipsized: at 40 columns the digest is the tail of the
|
|
324
|
+
// line, and ellipsizing it would drop the one fact that ties the decision
|
|
325
|
+
// to the bytes it applies to.
|
|
326
|
+
...(view.mutation !== undefined
|
|
327
|
+
? [
|
|
328
|
+
...wrapSentence(`Mutation: ${mutationFactsLine(view.mutation)}`, content),
|
|
329
|
+
...wrapSentence(`Press ${MUTATION_PREVIEW_KEY} to read it before deciding.`, content),
|
|
330
|
+
]
|
|
331
|
+
: []),
|
|
332
|
+
...(view.mutation === undefined && view.origin.kind === "worker" && isFileMutationTool(view.tool)
|
|
333
|
+
? wrapSentence(
|
|
334
|
+
"No local preview: this call's arguments stay inside the worker, so you are approving the target above without inspecting the mutation.",
|
|
335
|
+
content,
|
|
336
|
+
)
|
|
337
|
+
: []),
|
|
196
338
|
...wrapSentence(`Requested by: ${presentation.requestedByCopy}`, content),
|
|
197
339
|
...(view.artifact !== undefined
|
|
198
340
|
? [
|
|
@@ -215,6 +357,14 @@ export function permissionOverlayLines(view: ApprovalRequestView, width: number)
|
|
|
215
357
|
return lines;
|
|
216
358
|
}
|
|
217
359
|
|
|
218
|
-
|
|
219
|
-
|
|
360
|
+
/**
|
|
361
|
+
* The overlay body for one parked call. The inspector, when present, is the
|
|
362
|
+
* only path the mutation text takes: it is never placed on `view`, so nothing
|
|
363
|
+
* that renders or forwards the view can carry the bytes with it.
|
|
364
|
+
*/
|
|
365
|
+
export function createPermissionOverlayBody(
|
|
366
|
+
view: ApprovalRequestView,
|
|
367
|
+
inspect?: MutationInspector,
|
|
368
|
+
): PermissionOverlayBodyHandle {
|
|
369
|
+
return new PermissionOverlayBody(view, inspect);
|
|
220
370
|
}
|