@iowarp/clio-coder 0.3.3 → 0.3.6
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 +74 -0
- package/CONTRIBUTING.md +7 -7
- package/README.md +3 -3
- package/dist/{acp-P2AQILE2.js → acp-2BEHC4DL.js} +9 -8
- package/dist/{agents-72W3BI7I.js → agents-LNNFTM53.js} +29 -24
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5TWEIYDN.js → auth-KXXFI2VS.js} +14 -10
- package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
- package/dist/{chunk-YCWGATWI.js → chunk-24I7BN55.js} +2 -2
- package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
- package/dist/chunk-3BPUFZDL.js +37 -0
- package/dist/chunk-43AOLP7E.js +375 -0
- package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
- package/dist/{chunk-6SGHMWE3.js → chunk-5JGRAMKL.js} +5 -5
- package/dist/{chunk-V6RTAOC2.js → chunk-6US73PDB.js} +572 -51
- package/dist/{chunk-5UFT4SUX.js → chunk-6XXKFVSN.js} +3 -3
- package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
- package/dist/chunk-AOCYTWAV.js +449 -0
- package/dist/chunk-CFGTUFWB.js +67 -0
- package/dist/chunk-CJUB2JJ2.js +1478 -0
- package/dist/{chunk-FNTMWMX5.js → chunk-CKXWIANG.js} +14 -12
- package/dist/{chunk-PIWWS5BL.js → chunk-CYQKWTG3.js} +63 -78
- package/dist/{chunk-LZSJBIVT.js → chunk-DJVECN66.js} +271 -762
- package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
- package/dist/{chunk-ZWMF7253.js → chunk-E2ER4LJF.js} +304 -9
- package/dist/{chunk-STBPMHSX.js → chunk-EKY57CSP.js} +51 -84
- package/dist/{chunk-DUYJ5IO6.js → chunk-EYPA3EGJ.js} +12 -4
- package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
- package/dist/chunk-FYYLNIL5.js +313 -0
- package/dist/{chunk-OQ33BKR3.js → chunk-G7MUEIGA.js} +3 -60
- package/dist/chunk-GEYXPTRF.js +613 -0
- package/dist/chunk-GOXNB3AO.js +261 -0
- package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
- package/dist/chunk-HWUFFB6L.js +83 -0
- package/dist/{chunk-4XUGQOHA.js → chunk-K7T3E2SR.js} +15 -8
- package/dist/chunk-K7VKOLQQ.js +15 -0
- package/dist/{chunk-UFIIWP2H.js → chunk-KHSFENX2.js} +8 -8
- package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
- package/dist/chunk-LCGCVYZ4.js +57 -0
- package/dist/chunk-LL4KHSZI.js +22 -0
- package/dist/{chunk-PAJK6MAQ.js → chunk-LYF7OHWH.js} +42 -15
- package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
- package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
- package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
- package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
- package/dist/chunk-ODFEOB4F.js +1082 -0
- package/dist/{chunk-AGYYIBLL.js → chunk-OH3TOQTB.js} +6 -2
- package/dist/chunk-OZNBF4L3.js +23 -0
- package/dist/{chunk-DSELYM6W.js → chunk-PBTHKCPN.js} +30 -10
- package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
- package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
- package/dist/{chunk-SRDMMSEP.js → chunk-QM3F2GKX.js} +1063 -1645
- package/dist/{chunk-X6IAEBZR.js → chunk-QNQHSOLF.js} +7 -7
- package/dist/{chunk-OC7FIQPC.js → chunk-R46L2BIR.js} +10 -7
- package/dist/{chunk-2TLUCQVG.js → chunk-RD5U66HV.js} +3 -3
- package/dist/{chunk-6N5PTWMY.js → chunk-RY3LY4J5.js} +50 -13
- package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
- package/dist/{chunk-OOJYHWRB.js → chunk-TSHXZTOQ.js} +6 -5
- package/dist/chunk-TZSKNMZG.js +434 -0
- package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
- package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
- package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
- package/dist/chunk-W6GROXXM.js +69 -0
- package/dist/{chunk-OAO4GE4M.js → chunk-WHGPSPT5.js} +2 -2
- package/dist/chunk-WHJYKASB.js +677 -0
- package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
- package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
- package/dist/{chunk-TZTZS7QK.js → chunk-XE2VEJHX.js} +5 -3
- package/dist/{chunk-LM5TQCJZ.js → chunk-XF5N4U5A.js} +8 -7
- package/dist/{chunk-LWLEKMDQ.js → chunk-XXQNGV4M.js} +1073 -552
- package/dist/{chunk-KZWTDYJF.js → chunk-XYDYPRZI.js} +7 -7
- package/dist/chunk-ZGVHUX3M.js +66 -0
- package/dist/{chunk-LW6DSM3M.js → chunk-ZRGEBJ4T.js} +1192 -1119
- package/dist/{chunk-2DJ2KNFG.js → chunk-ZXF4XRKW.js} +202 -40
- package/dist/chunk-ZZMN5OM4.js +122 -0
- package/dist/cli/index.js +34 -30
- package/dist/{clio-JOU4FXVA.js → clio-M2KGYUFZ.js} +7 -6
- package/dist/{code-nav-7AX6FYE6.js → code-nav-GQNL7XA6.js} +8 -6
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-KELWS457.js → components-5TTYYX6G.js} +3 -3
- package/dist/{config-XCDVKR23.js → config-XUUYQIWO.js} +47 -35
- package/dist/{configure-4GAP54ZW.js → configure-IHJ7YOMV.js} +18 -15
- package/dist/{context-77FM5DV5.js → context-74JLXAWD.js} +18 -10
- package/dist/{context-4UOGGLQ5.js → context-75MIWW3U.js} +41 -29
- package/dist/{context-5VKGUVJJ.js → context-ZQ7SIFJV.js} +85 -9
- package/dist/{context-clear-XXJRLCJJ.js → context-clear-GYKWNUML.js} +41 -29
- package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
- package/dist/context-working-set-UX5KEP4J.js +1553 -0
- package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-GIJBHNFL.js} +47 -32
- package/dist/{docs-2C2LTVT2.js → docs-6FZSCG5B.js} +3 -3
- package/dist/{doctor-HR46URBJ.js → doctor-SVJ5BZCW.js} +12 -12
- package/dist/{eval-XSSNATB4.js → eval-CG6LLBLD.js} +54 -238
- package/dist/{evidence-6HG2PY2B.js → evidence-ZYFIEN42.js} +57 -28
- package/dist/{evolve-K7YU3NCY.js → evolve-QGEXEMDW.js} +36 -25
- package/dist/{extensions-QVDOHDGJ.js → extensions-ADGNCJJD.js} +3 -3
- package/dist/{fleet-VY3HHKN6.js → fleet-S5R4ZOQY.js} +73 -44
- package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-BHSNPBMH.js} +3 -3
- package/dist/{init-JYGXI3FK.js → init-5DRU55YR.js} +49 -37
- package/dist/memory-7YKKR6UC.js +467 -0
- package/dist/{models-I5QWSEOM.js → models-ZPOLRU2C.js} +24 -21
- package/dist/{monitor-GE4ID3IA.js → monitor-US5F5YGZ.js} +73 -46
- package/dist/{orchestrator-EM5MC3HM.js → orchestrator-E2AL4T5N.js} +1624 -1007
- package/dist/{paths-UXLN5YYZ.js → paths-E7KYAQWE.js} +3 -3
- package/dist/{reset-L2FQEE3E.js → reset-KZ652EK6.js} +6 -5
- package/dist/{run-ZU3QMZPZ.js → run-SRNBKDWD.js} +76 -54
- package/dist/{share-S5BZQC5I.js → share-CGZE33UP.js} +7 -6
- package/dist/{skills-X5VXCRNQ.js → skills-S2X4DLY5.js} +4 -4
- package/dist/{skills-eval-WKIHWTHR.js → skills-eval-W2GGIC4R.js} +40 -29
- package/dist/{targets-SNCPI2NR.js → targets-54SWINWB.js} +28 -23
- package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-SAIF2OGY.js} +6 -4
- package/dist/{uninstall-FZCQCDKC.js → uninstall-BVLWXKBT.js} +3 -3
- package/dist/{upgrade-JQHHPQ4K.js → upgrade-JKAR27XC.js} +20 -19
- package/dist/{usage-OR4O5SMZ.js → usage-MSAWCLX4.js} +79 -36
- package/dist/verifiers-NCBTHHN2.js +1220 -0
- package/dist/verify-X5HDROLA.js +25 -0
- package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-GUSOQ6ZP.js} +50 -37
- package/dist/worker/entry.js +90 -70
- package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
- package/docs/README.md +8 -7
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-placement.md +1 -2
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +60 -26
- package/docs/config-knobs-audit.md +1 -2
- package/docs/configuration-and-targets.md +26 -1
- package/docs/context-engine.md +67 -13
- package/docs/context-working-set.md +194 -0
- package/docs/development-pipeline.md +1 -1
- package/docs/documentation-coverage.md +6 -6
- package/docs/documentation-guide.md +7 -6
- package/docs/environment-variables.md +2 -1
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +4 -32
- package/docs/evidence-and-memory.md +139 -7
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +2 -2
- package/docs/fleet-dispatch.md +49 -8
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +19 -2
- package/docs/model-catalog.md +7 -9
- package/docs/observability.md +4 -4
- package/docs/proactive-memory.md +26 -16
- package/docs/prompt-envelope-and-tools.md +7 -5
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +43 -40
- package/docs/safety-model.md +49 -8
- package/docs/scientific-validation.md +21 -3
- package/docs/session-lifecycle.md +3 -3
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +79 -12
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +38 -4
- package/docs/worker-dispatch-mechanics.md +11 -1
- package/package.json +13 -13
- package/skills/meta/clio-test/SKILL.md +20 -17
- package/skills/meta/clio-test/evals.md +3 -3
- package/skills/meta/clio-test/references/harness.md +35 -6
- package/skills/meta/clio-test/references/test-map.md +20 -10
- package/skills/registry.yaml +2 -2
- package/skills/skill-marketplace.json +1 -1
- package/src/cli/agents.ts +2 -3
- package/src/cli/argv.ts +14 -1
- package/src/cli/context-working-set.ts +513 -0
- package/src/cli/context.ts +8 -0
- package/src/cli/evidence.ts +20 -2
- package/src/cli/fleet.ts +15 -0
- package/src/cli/index.ts +5 -1
- package/src/cli/memory.ts +272 -10
- package/src/cli/modes/json-stream.ts +2 -2
- package/src/cli/modes/print.ts +12 -1
- package/src/cli/run.ts +22 -2
- package/src/cli/targets.ts +12 -3
- package/src/cli/usage.ts +55 -7
- package/src/cli/verifiers.ts +325 -0
- package/src/core/bash-exec.ts +39 -14
- package/src/core/bus-events.ts +22 -4
- package/src/core/config.ts +54 -0
- package/src/core/defaults.ts +50 -3
- package/src/core/response-model-id.ts +134 -0
- package/src/core/toml.ts +62 -0
- package/src/core/verification-scripts.ts +6 -0
- package/src/core/workspace-files.ts +0 -1
- package/src/domains/agents/builtins/architect.md +1 -1
- package/src/domains/agents/builtins/verifier.md +3 -0
- package/src/domains/agents/catalog.ts +5 -4
- package/src/domains/agents/recipe.ts +54 -14
- package/src/domains/agents/result-contract.ts +7 -4
- package/src/domains/config/classify.ts +1 -0
- package/src/domains/context/bootstrap.ts +36 -27
- package/src/domains/context/project-metadata.ts +19 -63
- package/src/domains/context/prompt-context.ts +8 -0
- package/src/domains/context/working-set/contract.ts +161 -0
- package/src/domains/context/working-set/defaults.ts +28 -0
- package/src/domains/context/working-set/engine.ts +203 -0
- package/src/domains/context/working-set/fold.ts +62 -0
- package/src/domains/context/working-set/horizon.ts +38 -0
- package/src/domains/context/working-set/marker.ts +103 -0
- package/src/domains/context/working-set/path-index.ts +436 -0
- package/src/domains/context/working-set/payload.ts +152 -0
- package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
- package/src/domains/context/working-set/policies/index.ts +20 -0
- package/src/domains/context/working-set/policies/structural.ts +160 -0
- package/src/domains/context/working-set/project.ts +132 -0
- package/src/domains/context/working-set/protect.ts +109 -0
- package/src/domains/context/working-set/recall.ts +177 -0
- package/src/domains/context/working-set/replay/controls.ts +112 -0
- package/src/domains/context/working-set/replay/load-clio.ts +199 -0
- package/src/domains/context/working-set/replay/metrics.ts +185 -0
- package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
- package/src/domains/context/working-set/replay/report.ts +139 -0
- package/src/domains/context/working-set/replay/runner.ts +325 -0
- package/src/domains/context/working-set/replay/synthetic.ts +422 -0
- package/src/domains/context/working-set/replay/trace.ts +21 -0
- package/src/domains/context/working-set/visible.ts +54 -0
- package/src/domains/dispatch/budget-envelope.ts +396 -0
- package/src/domains/dispatch/contract.ts +2 -0
- package/src/domains/dispatch/extension.ts +81 -27
- package/src/domains/dispatch/orphan-recovery.ts +1 -0
- package/src/domains/dispatch/receipt-integrity.ts +4 -0
- package/src/domains/dispatch/state.ts +1 -0
- package/src/domains/dispatch/types.ts +10 -3
- package/src/domains/dispatch/validation.ts +14 -0
- package/src/domains/dispatch/worker-spawn.ts +14 -3
- package/src/domains/eval/metrics/evidence.ts +0 -116
- package/src/domains/eval/metrics/invariants.ts +1 -1
- package/src/domains/eval/runners/clio-run.ts +1 -10
- package/src/domains/eval/runners/external-command.ts +2 -29
- package/src/domains/eval/schema/suite.ts +0 -7
- package/src/domains/eval/suites/run.ts +1 -7
- package/src/domains/evidence/build.ts +112 -45
- package/src/domains/evidence/eval.ts +24 -7
- package/src/domains/evidence/index.ts +53 -0
- package/src/domains/evidence/ordering.ts +12 -0
- package/src/domains/evidence/run-trust.ts +221 -0
- package/src/domains/evidence/store.ts +46 -6
- package/src/domains/evidence/trust-status.ts +854 -0
- package/src/domains/evidence/types.ts +26 -0
- package/src/domains/memory/index.ts +22 -0
- package/src/domains/memory/operations.ts +58 -1
- package/src/domains/memory/promotion.ts +281 -0
- package/src/domains/memory/prompt-section.ts +25 -5
- package/src/domains/memory/proposal.ts +51 -7
- package/src/domains/memory/task-bank.ts +3 -2
- package/src/domains/memory/task-memory-handoff.ts +181 -24
- package/src/domains/memory/task-memory-policy.ts +3 -1
- package/src/domains/memory/types.ts +37 -0
- package/src/domains/memory/validate.ts +178 -0
- package/src/domains/middleware/memory-intervention.ts +38 -25
- package/src/domains/middleware/runtime.ts +6 -0
- package/src/domains/middleware/skills-reminder.ts +19 -4
- package/src/domains/middleware/stalled-turn.ts +208 -5
- package/src/domains/middleware/types.ts +10 -0
- package/src/domains/observability/contract.ts +6 -1
- package/src/domains/observability/cost.ts +20 -4
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/providers/index.ts +3 -0
- package/src/domains/providers/model-discovery.ts +9 -0
- package/src/domains/providers/runtime-resolution.ts +38 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
- package/src/domains/providers/types/context-window-slots.ts +18 -0
- package/src/domains/providers/types/runtime-descriptor.ts +3 -1
- package/src/domains/safety/autonomy.ts +1 -1
- package/src/domains/safety/call-target.ts +211 -14
- package/src/domains/safety/decision-presentation.ts +268 -0
- package/src/domains/safety/default-path-policy.ts +8 -0
- package/src/domains/safety/finish-contract.ts +4 -3
- package/src/domains/safety/policy-engine.ts +48 -6
- package/src/domains/safety/redaction.ts +73 -0
- package/src/domains/session/compaction/compact.ts +23 -1
- package/src/domains/session/compaction/cut-point.ts +2 -0
- package/src/domains/session/compaction/tokens.ts +16 -1
- package/src/domains/session/context-ledger.ts +12 -1
- package/src/domains/session/decision-board.ts +4 -0
- package/src/domains/session/entries.ts +110 -1
- package/src/domains/session/history.ts +68 -19
- package/src/domains/session/manager.ts +9 -2
- package/src/domains/session/migrations/index.ts +22 -3
- package/src/domains/session/usage.ts +24 -7
- package/src/engine/acp/event-mapper.ts +7 -0
- package/src/engine/acp/server.ts +32 -2
- package/src/engine/agent.ts +18 -1
- package/src/engine/apis/lmstudio.ts +25 -4
- package/src/engine/apis/openai-completions.ts +147 -22
- package/src/engine/claude/sdk-runtime.ts +8 -2
- package/src/engine/claude/tool-safety.ts +13 -0
- package/src/engine/loop-guard.ts +27 -3
- package/src/engine/session.ts +9 -3
- package/src/engine/worker-events.ts +4 -3
- package/src/engine/worker-runtime.ts +59 -54
- package/src/entry/orchestrator.ts +34 -5
- package/src/interactive/chat-loop-messages.ts +40 -6
- package/src/interactive/chat-loop.ts +13 -0
- package/src/interactive/chat-panel.ts +17 -1
- package/src/interactive/chat-renderer.ts +49 -24
- package/src/interactive/clio-editor.ts +44 -7
- package/src/interactive/context-meter.ts +10 -0
- package/src/interactive/context-overlay.ts +120 -7
- package/src/interactive/context-recall-command.ts +110 -0
- package/src/interactive/cost-overlay.ts +39 -8
- package/src/interactive/dispatch-board.ts +212 -35
- package/src/interactive/footer/widgets.ts +13 -0
- package/src/interactive/interactive-application.ts +6 -1
- package/src/interactive/interactive-input-runtime.ts +11 -1
- package/src/interactive/interactive-presentation.ts +11 -1
- package/src/interactive/interactive-slash-runtime.ts +37 -1
- package/src/interactive/memory-overlay.ts +89 -4
- package/src/interactive/model-session-replay.ts +21 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +1 -1
- package/src/interactive/overlay-frame.ts +5 -2
- package/src/interactive/overlay-general-openers.ts +46 -1
- package/src/interactive/overlay-key-routing.ts +41 -1
- package/src/interactive/overlay-lifecycle.ts +11 -4
- package/src/interactive/overlay-permission-lifecycle.ts +23 -8
- package/src/interactive/overlay-session-lifecycle.ts +8 -4
- package/src/interactive/overlay-transitions.ts +11 -0
- package/src/interactive/overlays/ask-user.ts +74 -30
- package/src/interactive/overlays/decisions.ts +3 -1
- package/src/interactive/permission-hint.ts +35 -0
- package/src/interactive/permission-overlay.ts +95 -45
- package/src/interactive/renderers/tool-execution.ts +37 -51
- package/src/interactive/session-last-turn.ts +8 -1
- package/src/interactive/session-transcript.ts +2 -2
- package/src/interactive/session-usage-reseed.ts +36 -10
- package/src/interactive/slash-commands.ts +31 -4
- package/src/interactive/status/summary.ts +5 -0
- package/src/interactive/status/types.ts +5 -0
- package/src/interactive/terminal-lease.ts +1 -0
- package/src/interactive/turn-context.ts +333 -110
- package/src/interactive/turn-middleware.ts +7 -6
- package/src/interactive/turn-runtime.ts +37 -8
- package/src/interactive/turn-state.ts +3 -0
- package/src/interactive/worker-progress.ts +440 -0
- package/src/interactive/worker-stream.ts +51 -110
- package/src/tools/agent-tools.ts +39 -7
- package/src/tools/ask-user.ts +21 -1
- package/src/tools/bash.ts +144 -82
- package/src/tools/builtin-tool-catalog.ts +11 -5
- package/src/tools/context/index.ts +107 -5
- package/src/tools/context/surface.ts +3 -2
- package/src/tools/core-bootstrap.ts +21 -0
- package/src/tools/dispatch-arguments.ts +8 -0
- package/src/tools/dispatch-event-text.ts +19 -0
- package/src/tools/dispatch-runner.ts +9 -7
- package/src/tools/dispatch.ts +24 -1
- package/src/tools/monitor.ts +43 -20
- package/src/tools/registry.ts +72 -10
- package/src/tools/result-disposition.ts +706 -0
- package/src/tools/result-shaping.ts +321 -20
- package/src/tools/safe-exec.ts +2 -0
- package/src/tools/verify/authoring.ts +1120 -0
- package/src/tools/verify/catalog.ts +346 -0
- package/src/tools/verify/index.ts +13 -3
- package/src/tools/verify/scripts.ts +135 -37
- package/src/tools/verify/surface.ts +9 -5
- package/src/tools/worker-evidence.ts +54 -12
- package/src/worker/spec-contract.ts +43 -3
- package/dist/chunk-J7CWMCQD.js +0 -255
- package/dist/chunk-T6YILFSB.js +0 -80
- package/dist/chunk-VAKQQHWR.js +0 -434
- package/dist/chunk-VPAYEGVX.js +0 -184
- package/dist/chunk-XBXAASKX.js +0 -18
- package/dist/memory-WFZMGYHX.js +0 -236
- package/src/domains/eval/metrics/chaos-stream.ts +0 -93
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifyDecisionPresentation,
|
|
3
|
+
type DecisionPresentation,
|
|
4
|
+
decisionFactsForAnswer,
|
|
5
|
+
} from "../../domains/safety/decision-presentation.js";
|
|
1
6
|
import {
|
|
2
7
|
type Component,
|
|
3
8
|
Input,
|
|
@@ -18,16 +23,14 @@ import { type ClioToken, clioTheme, dotSep, GLYPH, screenTitle } from "../theme/
|
|
|
18
23
|
import { nextContentScrollOffset, type ViewScrollAction } from "../view/view-overlay.js";
|
|
19
24
|
|
|
20
25
|
/**
|
|
21
|
-
* The
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* keyboard is the moment the operator has to act with it. Everything
|
|
25
|
-
* informational keeps the teal frame, so the one border that is not teal is the
|
|
26
|
-
* one asking for an answer. All three surfaces carry it: what changes between
|
|
27
|
-
* them is the size and the place, never the signal.
|
|
26
|
+
* The default title and border token for a local conversational answer.
|
|
27
|
+
* Outward and other consequence tiers replace both values with their typed
|
|
28
|
+
* presentation. All ask_user surfaces carry the same classified signal.
|
|
28
29
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
const DEFAULT_ASK_USER_PRESENTATION = classifyDecisionPresentation(decisionFactsForAnswer("local"));
|
|
31
|
+
|
|
32
|
+
export const ASK_USER_DECISION_TONE: ClioToken = DEFAULT_ASK_USER_PRESENTATION.semanticToken;
|
|
33
|
+
export const ASK_USER_DECISION_TITLE = DEFAULT_ASK_USER_PRESENTATION.title;
|
|
31
34
|
export const ASK_USER_WAITING_TITLE = "Ask User";
|
|
32
35
|
|
|
33
36
|
/**
|
|
@@ -73,7 +76,7 @@ interface AskUserSurfaceGeometry {
|
|
|
73
76
|
* `ASK_USER_FRAME_AND_MARGIN_ROWS` counts alongside the two border rows.
|
|
74
77
|
*/
|
|
75
78
|
export const ASK_USER_SURFACE_GEOMETRY: Record<AskUserSurface, AskUserSurfaceGeometry> = {
|
|
76
|
-
compact: { width: 68, maxInnerRows:
|
|
79
|
+
compact: { width: 68, maxInnerRows: 14, anchor: "bottom-center", margin: { top: 1, right: 2, bottom: 1, left: 2 } },
|
|
77
80
|
panel: { width: 88, maxInnerRows: 18, anchor: "center", margin: 1 },
|
|
78
81
|
// The interview is a workspace, not a modal. A cap of 42 turned a 66-row
|
|
79
82
|
// terminal into a centered box with twenty blank rows around it, so the
|
|
@@ -111,7 +114,7 @@ export interface OpenAskUserOverlayDeps {
|
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
export interface AskUserOverlaySession extends OverlayHandle {
|
|
114
|
-
ask(questions: ReadonlyArray<AskUserQuestion
|
|
117
|
+
ask(questions: ReadonlyArray<AskUserQuestion>, presentation?: DecisionPresentation): Promise<AskUserResult>;
|
|
115
118
|
cancel(): void;
|
|
116
119
|
close(): void;
|
|
117
120
|
isWaiting(): boolean;
|
|
@@ -242,16 +245,21 @@ class AskUserOverlayView implements Component {
|
|
|
242
245
|
private contentRegionHeight = 1;
|
|
243
246
|
/** Rows the scroll region wanted on the last render. */
|
|
244
247
|
private contentTotalRows = 0;
|
|
248
|
+
private presentation: DecisionPresentation = DEFAULT_ASK_USER_PRESENTATION;
|
|
245
249
|
|
|
246
250
|
constructor(private readonly deps: AskUserOverlayViewDeps) {}
|
|
247
251
|
|
|
248
|
-
begin(
|
|
252
|
+
begin(
|
|
253
|
+
questions: ReadonlyArray<AskUserQuestion>,
|
|
254
|
+
presentation: DecisionPresentation = DEFAULT_ASK_USER_PRESENTATION,
|
|
255
|
+
): Promise<AskUserResult> {
|
|
249
256
|
if (this.phase === "closed") return Promise.resolve(cancelledAskUserResult());
|
|
250
257
|
if (this.resolveCurrent) return Promise.resolve(cancelledAskUserResult());
|
|
251
258
|
this.phase = "asking";
|
|
252
259
|
this.index = 0;
|
|
253
260
|
this.status = "";
|
|
254
261
|
this.questions = [...questions];
|
|
262
|
+
this.presentation = presentation;
|
|
255
263
|
this.states = this.questions.map((question) => createQuestionState(question));
|
|
256
264
|
this.list = null;
|
|
257
265
|
this.input = null;
|
|
@@ -288,6 +296,14 @@ class AskUserOverlayView implements Component {
|
|
|
288
296
|
return this.surface;
|
|
289
297
|
}
|
|
290
298
|
|
|
299
|
+
decisionTitle(): string {
|
|
300
|
+
return this.presentation.title;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
decisionTone(): ClioToken {
|
|
304
|
+
return this.presentation.semanticToken;
|
|
305
|
+
}
|
|
306
|
+
|
|
291
307
|
invalidate(): void {
|
|
292
308
|
this.list?.invalidate();
|
|
293
309
|
this.input?.invalidate();
|
|
@@ -358,10 +374,17 @@ class AskUserOverlayView implements Component {
|
|
|
358
374
|
const statusLines = this.status.length > 0 ? ["", fitLine(clioTheme().fg("dim", this.status), safeWidth)] : [];
|
|
359
375
|
const headerLine = this.renderQuestionHeader(question, safeWidth);
|
|
360
376
|
const headerLines = headerLine.length > 0 ? [headerLine] : [];
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
377
|
+
const bottom = [...statusLines, "", ...controlLines];
|
|
378
|
+
const topBudget = Math.max(1, maxRows - bottom.length);
|
|
379
|
+
const contextLines = this.renderDecisionContext(safeWidth);
|
|
380
|
+
const top = [
|
|
381
|
+
contextLines[0] ?? "",
|
|
382
|
+
...headerLines,
|
|
383
|
+
...wrapTextWithAnsi(question.question, safeWidth),
|
|
384
|
+
...contextLines.slice(1),
|
|
385
|
+
].slice(0, topBudget);
|
|
386
|
+
|
|
387
|
+
return [...top, ...bottom].slice(0, maxRows);
|
|
365
388
|
}
|
|
366
389
|
|
|
367
390
|
/**
|
|
@@ -380,7 +403,12 @@ class AskUserOverlayView implements Component {
|
|
|
380
403
|
const headerLine = this.renderQuestionHeader(question, width);
|
|
381
404
|
const headerLines = headerLine.length > 0 ? [headerLine] : [];
|
|
382
405
|
const stripLines = [this.renderQuestionStrip(width), ""];
|
|
383
|
-
const content = [
|
|
406
|
+
const content = [
|
|
407
|
+
...wrapTextWithAnsi(question.question, width),
|
|
408
|
+
"",
|
|
409
|
+
...this.renderDecisionContext(width),
|
|
410
|
+
...this.renderAnswerLedger(width),
|
|
411
|
+
];
|
|
384
412
|
|
|
385
413
|
// Chrome sheds from the outside in when the terminal is too short to carry
|
|
386
414
|
// all of it: the round strip first, then the header, then the status, and
|
|
@@ -460,36 +488,52 @@ class AskUserOverlayView implements Component {
|
|
|
460
488
|
const question = this.currentQuestion();
|
|
461
489
|
const state = this.currentState();
|
|
462
490
|
if (!question || !state) return buildHint([]);
|
|
491
|
+
const recordAnswer =
|
|
492
|
+
this.presentation.requiredActions.find((action) => action.id === "record-answer")?.label.toLowerCase() ??
|
|
493
|
+
"record answer";
|
|
463
494
|
if (state.mode === "text") {
|
|
464
495
|
return this.questions.length > 1
|
|
465
496
|
? this.withScrollHint([
|
|
466
|
-
{ key: "Enter", verb:
|
|
497
|
+
{ key: "Enter", verb: recordAnswer },
|
|
467
498
|
{ key: "Alt+Left/Right", verb: "question" },
|
|
468
499
|
])
|
|
469
|
-
: this.withScrollHint([{ key: "Enter", verb:
|
|
500
|
+
: this.withScrollHint([{ key: "Enter", verb: recordAnswer }]);
|
|
470
501
|
}
|
|
471
|
-
//
|
|
472
|
-
//
|
|
473
|
-
//
|
|
474
|
-
// interview together.
|
|
502
|
+
// The classified action label distinguishes recording an answer from
|
|
503
|
+
// granting authority. Esc keeps the product's one word for the way out,
|
|
504
|
+
// since it closes the prompt and the interview together.
|
|
475
505
|
if (question.multi_select === true) {
|
|
476
506
|
return this.questions.length > 1
|
|
477
507
|
? this.withScrollHint([
|
|
478
508
|
{ key: "Left/Right", verb: "question" },
|
|
479
509
|
{ key: "Space", verb: "toggle" },
|
|
480
|
-
{ key: "Enter", verb:
|
|
510
|
+
{ key: "Enter", verb: recordAnswer },
|
|
481
511
|
])
|
|
482
512
|
: this.withScrollHint([
|
|
483
513
|
{ key: "Space", verb: "toggle" },
|
|
484
|
-
{ key: "Enter", verb:
|
|
514
|
+
{ key: "Enter", verb: recordAnswer },
|
|
485
515
|
]);
|
|
486
516
|
}
|
|
487
517
|
return this.questions.length > 1
|
|
488
518
|
? this.withScrollHint([
|
|
489
519
|
{ key: "Left/Right", verb: "question" },
|
|
490
|
-
{ key: "Enter", verb:
|
|
520
|
+
{ key: "Enter", verb: recordAnswer },
|
|
491
521
|
])
|
|
492
|
-
: this.withScrollHint([{ key: "Enter", verb:
|
|
522
|
+
: this.withScrollHint([{ key: "Enter", verb: recordAnswer }]);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
private renderDecisionContext(width: number): string[] {
|
|
526
|
+
const theme = clioTheme();
|
|
527
|
+
const tier = theme.style(this.presentation.semanticToken, this.presentation.tierLabel, { bold: true });
|
|
528
|
+
const requested = theme.fg("dim", `requested by ${this.presentation.requestedByCopy}`);
|
|
529
|
+
const effect = `${theme.fg("dim", "Effect:")} ${theme.fg("muted", this.presentation.authorizationCopy)}`;
|
|
530
|
+
return [
|
|
531
|
+
fitLine(`${tier}${dotSep(theme)}${requested}`, width),
|
|
532
|
+
...wrapTextWithAnsi(effect, width).map((line) => fitLine(line, width)),
|
|
533
|
+
...wrapTextWithAnsi(theme.fg("muted", this.presentation.reversibilityCopy), width).map((line) =>
|
|
534
|
+
fitLine(line, width),
|
|
535
|
+
),
|
|
536
|
+
];
|
|
493
537
|
}
|
|
494
538
|
|
|
495
539
|
/**
|
|
@@ -1012,8 +1056,8 @@ export function openAskUserOverlay(tui: TUI, deps: OpenAskUserOverlayDeps): AskU
|
|
|
1012
1056
|
// the terminal has left after the margins.
|
|
1013
1057
|
...(geometry.maxInnerRows > 0 ? { maxHeight: geometry.maxInnerRows + ASK_USER_FRAME_ROWS } : {}),
|
|
1014
1058
|
margin: geometry.margin,
|
|
1015
|
-
title: () => (view.isDecisionPending() ?
|
|
1016
|
-
tone: () => (view.isDecisionPending() ?
|
|
1059
|
+
title: () => (view.isDecisionPending() ? view.decisionTitle() : ASK_USER_WAITING_TITLE),
|
|
1060
|
+
tone: () => (view.isDecisionPending() ? view.decisionTone() : undefined),
|
|
1017
1061
|
footerHint: () => view.footerHint(),
|
|
1018
1062
|
});
|
|
1019
1063
|
mounted = surface;
|
|
@@ -1034,7 +1078,7 @@ export function openAskUserOverlay(tui: TUI, deps: OpenAskUserOverlayDeps): AskU
|
|
|
1034
1078
|
focus: () => handle?.focus(),
|
|
1035
1079
|
unfocus: (options) => (options ? handle?.unfocus(options) : handle?.unfocus()),
|
|
1036
1080
|
isFocused: () => handle?.isFocused() ?? false,
|
|
1037
|
-
ask: (questions) => view.begin(questions),
|
|
1081
|
+
ask: (questions, presentation) => view.begin(questions, presentation),
|
|
1038
1082
|
cancel: () => view.cancel(),
|
|
1039
1083
|
close,
|
|
1040
1084
|
hide: close,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { classifyDecisionPresentation, decisionFactsForAnswer } from "../../domains/safety/decision-presentation.js";
|
|
1
2
|
import type { DecisionLedgerEntry, DecisionRecord } from "../../domains/session/entries.js";
|
|
2
3
|
import {
|
|
3
4
|
type Component,
|
|
@@ -73,10 +74,11 @@ function selectableRows(interviews: ReadonlyArray<DecisionLedgerEntry>): Selecta
|
|
|
73
74
|
|
|
74
75
|
function interviewHeader(interview: DecisionLedgerEntry, width: number, now: number): string[] {
|
|
75
76
|
const theme = clioTheme();
|
|
77
|
+
const presentation = classifyDecisionPresentation(decisionFactsForAnswer(interview.exposure ?? "local"));
|
|
76
78
|
const rounds = `${interview.roundCount} round${interview.roundCount === 1 ? "" : "s"}`;
|
|
77
79
|
const status =
|
|
78
80
|
interview.interviewStatus === "complete" ? theme.fg("success", "complete") : theme.fg("warning", "cancelled");
|
|
79
|
-
const heading = `${theme.fg("accent", relativeTime(interview.endedAt, now))}${theme.fg("dim", " · ")}${theme.fg("muted", rounds)}${theme.fg("dim", " · ")}${status}`;
|
|
81
|
+
const heading = `${theme.style(presentation.semanticToken, presentation.tierLabel, { bold: true })}${theme.fg("dim", " · ")}${theme.fg("accent", relativeTime(interview.endedAt, now))}${theme.fg("dim", " · ")}${theme.fg("muted", rounds)}${theme.fg("dim", " · ")}${status}`;
|
|
80
82
|
const lines = [fitLine(heading, width)];
|
|
81
83
|
if (interview.summary) {
|
|
82
84
|
lines.push(...wrapTextWithAnsi(theme.fg("dim", interview.summary), width).map((line) => fitLine(line, width)));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { HintEntry } from "./overlay-frame.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The keys that answer a parked call, as data, so the dialog footer and the
|
|
5
|
+
* composer rail render the same policy at any width.
|
|
6
|
+
*
|
|
7
|
+
* This is its own leaf because the composer is part of the Stage 0 boot shell
|
|
8
|
+
* and the permission overlay is not: importing the overlay from the editor
|
|
9
|
+
* pulled the safety domain's approval modules into the instant-shell closure.
|
|
10
|
+
*
|
|
11
|
+
* At 40 columns the old positional elider removed `[s] stop turn` and left
|
|
12
|
+
* "allow once" and an ambiguous "close" in front of an operator trying to
|
|
13
|
+
* refuse. The key kept working, so the layout was hiding a live safety action.
|
|
14
|
+
* Allow and stop are marked critical, Esc is marked droppable, and
|
|
15
|
+
* `fitHintEntries` shortens every label before it drops anything.
|
|
16
|
+
*
|
|
17
|
+
* Esc says `deny`, not `close`: closing the dialog denies the call, and on the
|
|
18
|
+
* one surface where a misread is a wrong decision about a tool call the key
|
|
19
|
+
* names its effect.
|
|
20
|
+
*
|
|
21
|
+
* With a draft in the composer, Enter is inert (issue #186): the habitual send
|
|
22
|
+
* key must not resolve toward "allow" on a safety rail. The entry then says so
|
|
23
|
+
* and names the key that clears the draft, which the router passes through.
|
|
24
|
+
*/
|
|
25
|
+
export function permissionHintEntries(composerHasDraft = false): HintEntry[] {
|
|
26
|
+
return [
|
|
27
|
+
composerHasDraft
|
|
28
|
+
? { key: "Backspace", verb: "clear the draft to allow", short: "clear draft", critical: true }
|
|
29
|
+
: { key: "Enter", verb: "allow once", short: "allow", critical: true },
|
|
30
|
+
{ key: "s", verb: "stop turn", short: "stop", critical: true },
|
|
31
|
+
// With a draft, deny and stop are the only immediate answers, so Esc
|
|
32
|
+
// outranks the narrowing that would otherwise drop it first.
|
|
33
|
+
{ key: "Esc", verb: "deny", critical: composerHasDraft },
|
|
34
|
+
];
|
|
35
|
+
}
|
|
@@ -1,25 +1,37 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { ActionClass } from "../domains/safety/action-classifier.js";
|
|
2
|
+
import type { AutonomyExposure } from "../domains/safety/autonomy.js";
|
|
3
|
+
import {
|
|
4
|
+
classifyDecisionPresentation,
|
|
5
|
+
type DecisionPresentation,
|
|
6
|
+
decisionFactsForPermission,
|
|
7
|
+
} from "../domains/safety/decision-presentation.js";
|
|
8
|
+
import type { Component, OverlayOptions, TUI } from "../engine/tui.js";
|
|
9
|
+
import { fitHintEntries } from "./overlay-frame.js";
|
|
10
|
+
import { permissionHintEntries } from "./permission-hint.js";
|
|
11
|
+
import type { ClioToken } from "./theme/index.js";
|
|
3
12
|
|
|
4
13
|
export { type AskAxis, askAxis } from "../domains/safety/approval-axis.js";
|
|
5
14
|
export { describeCallTarget, sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
15
|
+
export { permissionHintEntries } from "./permission-hint.js";
|
|
6
16
|
|
|
7
17
|
export interface ApprovalRequestView {
|
|
8
18
|
requestId: string;
|
|
9
19
|
tool: string;
|
|
10
|
-
actionClass:
|
|
20
|
+
actionClass: ActionClass;
|
|
11
21
|
axis: { kind: "net"; ruleId: string } | { kind: "autonomy"; level: string };
|
|
12
22
|
origin: { kind: "main" } | { kind: "worker"; agentId: string; runId: string };
|
|
23
|
+
/** Admission-normalized exposure. Caller prose never supplies presentation fields. */
|
|
24
|
+
exposure?: AutonomyExposure;
|
|
13
25
|
reason: string;
|
|
14
26
|
/** Typed, sanitized multi-line artifact that this one approval authorizes. */
|
|
15
27
|
artifact?: { kind: "dispatch-plan"; text: string };
|
|
16
28
|
/**
|
|
17
|
-
* One-line preview of the call's object
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
29
|
+
* One-line preview of the call's allowlisted object fields. The operator is
|
|
30
|
+
* deciding whether to allow this exact call, so the overlay must show what
|
|
31
|
+
* the call will touch, not just the tool name. Unlisted fields appear only as
|
|
32
|
+
* type-and-size summaries. Main-agent asks derive it from the parked call's
|
|
33
|
+
* args; worker escalations carry it in the escalation payload. Absent only
|
|
34
|
+
* when nothing meaningful is derivable.
|
|
23
35
|
*/
|
|
24
36
|
target?: string;
|
|
25
37
|
queueDepth?: number;
|
|
@@ -29,6 +41,44 @@ const PERMISSION_OVERLAY_CONTENT_WIDTH = 78;
|
|
|
29
41
|
|
|
30
42
|
export const PERMISSION_OVERLAY_WIDTH = PERMISSION_OVERLAY_CONTENT_WIDTH + 4;
|
|
31
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Place a permission frame immediately above the live composer in both TUI
|
|
46
|
+
* modes. The editor grows with wrapped and multiline drafts, while the footer
|
|
47
|
+
* can add notice rows, so their rendered heights are the bottom dock rather
|
|
48
|
+
* than a fixed clearance. Regular mode preserves terminal scrollback and may
|
|
49
|
+
* leave that dock above the viewport bottom when the transcript is short.
|
|
50
|
+
* Recompute its viewport row on every frame so draft changes and terminal
|
|
51
|
+
* resizes move the dialog with the composer.
|
|
52
|
+
*/
|
|
53
|
+
export function permissionOverlayPlacement(
|
|
54
|
+
tui: Pick<TUI, "mode" | "render">,
|
|
55
|
+
editor: Pick<Component, "render">,
|
|
56
|
+
footer: Pick<Component, "render">,
|
|
57
|
+
): OverlayOptions {
|
|
58
|
+
const margin = { bottom: 0 };
|
|
59
|
+
return {
|
|
60
|
+
anchor: "bottom-center",
|
|
61
|
+
margin,
|
|
62
|
+
visible: (termWidth, termHeight) => {
|
|
63
|
+
const dockHeight = editor.render(termWidth).length + footer.render(termWidth).length;
|
|
64
|
+
if (tui.mode === "fullscreen") {
|
|
65
|
+
margin.bottom = dockHeight;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const baseHeight = tui.render(termWidth).length;
|
|
70
|
+
const composerTop = Math.max(0, baseHeight - dockHeight);
|
|
71
|
+
const viewportStart = Math.max(0, baseHeight - termHeight);
|
|
72
|
+
const composerViewportRow = composerTop - viewportStart;
|
|
73
|
+
margin.bottom =
|
|
74
|
+
composerViewportRow >= 0 && composerViewportRow < termHeight
|
|
75
|
+
? Math.max(0, termHeight - composerViewportRow)
|
|
76
|
+
: dockHeight;
|
|
77
|
+
return true;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
32
82
|
class PermissionOverlayBody implements Component {
|
|
33
83
|
constructor(private readonly view: ApprovalRequestView) {}
|
|
34
84
|
|
|
@@ -89,46 +139,41 @@ function wrapArtifactLine(value: string, max: number): string[] {
|
|
|
89
139
|
return lines;
|
|
90
140
|
}
|
|
91
141
|
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
142
|
+
export function permissionDecisionPresentation(view: ApprovalRequestView): DecisionPresentation {
|
|
143
|
+
return classifyDecisionPresentation(
|
|
144
|
+
decisionFactsForPermission({
|
|
145
|
+
tool: view.tool,
|
|
146
|
+
actionClass: view.actionClass,
|
|
147
|
+
axis:
|
|
148
|
+
view.axis.kind === "net"
|
|
149
|
+
? { kind: "safety-net", ruleId: view.axis.ruleId }
|
|
150
|
+
: { kind: "autonomy", level: view.axis.level },
|
|
151
|
+
origin: view.origin,
|
|
152
|
+
...(view.exposure !== undefined ? { exposure: view.exposure } : {}),
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
95
155
|
}
|
|
96
156
|
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
return `worker ${view.origin.agentId} (run ${view.origin.runId}), ${axisLabel(view.axis, "worker")}`;
|
|
100
|
-
}
|
|
101
|
-
return axisLabel(view.axis, "main");
|
|
157
|
+
export function permissionOverlayTitle(view: ApprovalRequestView): string {
|
|
158
|
+
return permissionDecisionPresentation(view).title;
|
|
102
159
|
}
|
|
103
160
|
|
|
104
|
-
export function
|
|
105
|
-
return
|
|
161
|
+
export function permissionOverlayTone(view: ApprovalRequestView): ClioToken {
|
|
162
|
+
return permissionDecisionPresentation(view).semanticToken;
|
|
106
163
|
}
|
|
107
164
|
|
|
108
165
|
/**
|
|
109
|
-
* The footer for a box `innerWidth` columns wide inside its borders.
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* "allow once" and an ambiguous "close" in front of an operator trying to
|
|
113
|
-
* refuse. The key kept working, so the layout was hiding a live safety action.
|
|
114
|
-
* This surface used to answer that with three hand-written width tiers; it now
|
|
115
|
-
* states the same policy as data. Allow and stop are marked critical, Esc is
|
|
116
|
-
* marked droppable, and `fitHintEntries` shortens every label before it drops
|
|
117
|
-
* anything, which reproduces the three tiers exactly.
|
|
166
|
+
* The footer for a box `innerWidth` columns wide inside its borders. The
|
|
167
|
+
* three columns the bottom border spends on `─ ` and the trailing space come
|
|
168
|
+
* off the top, which is what `buildResponsiveHint` does for every other frame.
|
|
118
169
|
*/
|
|
119
|
-
export
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const SAFETY_SENTENCES: ReadonlyArray<string> = [
|
|
128
|
-
"Parked until you decide; allow or deny applies to this call only.",
|
|
129
|
-
"Stopping the turn denies it and ends the run, so nothing asks again.",
|
|
130
|
-
"Hard-blocked actions remain blocked.",
|
|
131
|
-
];
|
|
170
|
+
export function permissionOverlayHint(innerWidth: number, composerHasDraft = false): string {
|
|
171
|
+
return fitHintEntries(permissionHintEntries(composerHasDraft), innerWidth - 3);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function actionConsequence(presentation: DecisionPresentation, id: "deny" | "stop"): string {
|
|
175
|
+
return presentation.requiredActions.find((action) => action.id === id)?.consequence ?? "";
|
|
176
|
+
}
|
|
132
177
|
|
|
133
178
|
/**
|
|
134
179
|
* The overlay's body, laid out for the width the frame gives it.
|
|
@@ -140,15 +185,15 @@ const SAFETY_SENTENCES: ReadonlyArray<string> = [
|
|
|
140
185
|
*/
|
|
141
186
|
export function permissionOverlayLines(view: ApprovalRequestView, width: number): string[] {
|
|
142
187
|
const content = Math.max(8, Math.floor(width));
|
|
188
|
+
const presentation = permissionDecisionPresentation(view);
|
|
143
189
|
// The parked call is awaiting a decision, not blocked: the raw rejection
|
|
144
190
|
// short ("<tool> blocked: <class>") is never rendered here because its
|
|
145
191
|
// wording contradicts the ask. Tool, Target, Action, and the asking axis
|
|
146
192
|
// carry everything the operator needs to decide.
|
|
147
193
|
const lines = [
|
|
148
|
-
field("Tool: ", view.tool
|
|
194
|
+
field("Tool: ", `${view.tool} · Action: ${view.actionClass}`, content),
|
|
149
195
|
...(view.target !== undefined && view.target.length > 0 ? [field("Target: ", view.target, content)] : []),
|
|
150
|
-
|
|
151
|
-
field("Asked by: ", askedBy(view), content),
|
|
196
|
+
...wrapSentence(`Requested by: ${presentation.requestedByCopy}`, content),
|
|
152
197
|
...(view.artifact !== undefined
|
|
153
198
|
? [
|
|
154
199
|
"",
|
|
@@ -157,7 +202,12 @@ export function permissionOverlayLines(view: ApprovalRequestView, width: number)
|
|
|
157
202
|
]
|
|
158
203
|
: []),
|
|
159
204
|
"",
|
|
160
|
-
...
|
|
205
|
+
...wrapSentence(`Approval: ${presentation.authorizationCopy}`, content),
|
|
206
|
+
...wrapSentence(`Consequence: ${presentation.consequenceCopy}`, content),
|
|
207
|
+
...wrapSentence(presentation.reversibilityCopy, content),
|
|
208
|
+
...wrapSentence(`Deny: ${actionConsequence(presentation, "deny")}`, content),
|
|
209
|
+
...wrapSentence(`Stop: ${actionConsequence(presentation, "stop")}`, content),
|
|
210
|
+
...wrapSentence("Hard-blocked actions remain blocked.", content),
|
|
161
211
|
];
|
|
162
212
|
if (view.queueDepth !== undefined && view.queueDepth > 1) {
|
|
163
213
|
lines.splice(lines.indexOf(""), 0, `1 of ${view.queueDepth} parked`);
|
|
@@ -13,15 +13,20 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { sanitizeCallTargetText } from "../../domains/safety/call-target.js";
|
|
16
|
+
import { redactSecretString, redactToolArgs } from "../../domains/safety/redaction.js";
|
|
16
17
|
import { formatSize } from "../../engine/truncate.js";
|
|
17
18
|
import { visibleWidth, wrapTextWithAnsi } from "../../engine/tui.js";
|
|
18
19
|
import { classifyResourceRead, toolPresentationPolicy } from "../../tools/presentation.js";
|
|
20
|
+
import { toolResultPresentationPolicy, toolResultPresentationText } from "../../tools/result-disposition.js";
|
|
19
21
|
import type { ApprovalRequestView } from "../permission-overlay.js";
|
|
20
22
|
import { clioTheme, formatCompactMs, GLYPH } from "../theme/index.js";
|
|
21
23
|
import { renderDiffLines } from "./diff.js";
|
|
22
24
|
import { tryRenderJson, tryRenderXml } from "./structured.js";
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
// The argument projection lives in the safety domain so the worker tool seam
|
|
27
|
+
// scrubs by the same rules; re-exported here because this renderer is where
|
|
28
|
+
// callers and tests have always reached for it.
|
|
29
|
+
export { classifyResourceRead, redactToolArgs };
|
|
25
30
|
|
|
26
31
|
const theme = clioTheme();
|
|
27
32
|
const dim = (text: string): string => theme.fg("dim", text);
|
|
@@ -81,6 +86,13 @@ export interface ToolExecutionFinished {
|
|
|
81
86
|
* model reading the same transcript, to guess at the rule.
|
|
82
87
|
*/
|
|
83
88
|
blockReason?: string | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* Working-set eviction reason, when the projection has replaced this
|
|
91
|
+
* result's body for the model. The transcript still renders the full body:
|
|
92
|
+
* the ledger is what the operator scrolls, the projection is only what the
|
|
93
|
+
* next request carries. The tag says the two now differ here.
|
|
94
|
+
*/
|
|
95
|
+
evictedReason?: string | undefined;
|
|
84
96
|
/** Structured exit status when the caller has one; text parsing is legacy fallback only. */
|
|
85
97
|
exitCode?: number | string | null | undefined;
|
|
86
98
|
/** Local `!!` bash output is visible to the operator but excluded from model context. */
|
|
@@ -119,52 +131,6 @@ function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
|
119
131
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
120
132
|
}
|
|
121
133
|
|
|
122
|
-
const SECRET_KEY_RE =
|
|
123
|
-
/(?:api[-_]?key|access[-_]?key|auth(?:orization)?|credential|password|private[-_]?key|secret|token)/iu;
|
|
124
|
-
const ENV_KEY_RE = /^env(?:ironment)?$/iu;
|
|
125
|
-
const SECRET_STRING_RE = /(https?:\/\/[^\s/@]+:)[^\s/@]+@/giu;
|
|
126
|
-
const SECRET_URL_PARAM_RE =
|
|
127
|
-
/([?&](?:api[-_]?key|access[-_]?token|auth(?:orization)?|credential|password|secret|token)[^=\s]*=)[^&#\s]+/giu;
|
|
128
|
-
const SECRET_ASSIGNMENT_RE =
|
|
129
|
-
/(?<![A-Z0-9_])((?:[A-Z0-9_]*(?:ACCESS|API|AUTH|CREDENTIAL|KEY|PASS(?:WORD)?|PRIVATE|SECRET|TOKEN)[A-Z0-9_]*)=)[^\s;&|]+/giu;
|
|
130
|
-
const SECRET_FLAG_RE =
|
|
131
|
-
/(--?(?:[a-z0-9-]*(?:api[-_]?key|access[-_]?token|auth(?:orization)?|credential|password|secret|token)[a-z0-9-]*))(=|\s+)([^\s;&|]+)/giu;
|
|
132
|
-
|
|
133
|
-
function redactSecretString(value: string): string {
|
|
134
|
-
return value
|
|
135
|
-
.replace(SECRET_STRING_RE, "$1[redacted]@")
|
|
136
|
-
.replace(SECRET_URL_PARAM_RE, "$1[redacted]")
|
|
137
|
-
.replace(SECRET_ASSIGNMENT_RE, "$1[redacted]")
|
|
138
|
-
.replace(SECRET_FLAG_RE, "$1$2[redacted]");
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function redactEnvironmentValue(value: unknown, depth: number): unknown {
|
|
142
|
-
if (depth > 8) return "[redacted nested values]";
|
|
143
|
-
if (typeof value === "string") return "[redacted]";
|
|
144
|
-
if (Array.isArray(value)) return value.map((item) => redactEnvironmentValue(item, depth + 1));
|
|
145
|
-
if (!isPlainObject(value)) return value;
|
|
146
|
-
const out: Record<string, unknown> = {};
|
|
147
|
-
for (const [childKey, childValue] of Object.entries(value)) {
|
|
148
|
-
out[childKey] = redactEnvironmentValue(childValue, depth + 1);
|
|
149
|
-
}
|
|
150
|
-
return out;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/** Project tool arguments for display only; execution and receipts retain the original values. */
|
|
154
|
-
export function redactToolArgs(value: unknown, key = "", depth = 0): unknown {
|
|
155
|
-
if (SECRET_KEY_RE.test(key)) return "[redacted]";
|
|
156
|
-
if (ENV_KEY_RE.test(key)) return redactEnvironmentValue(value, depth);
|
|
157
|
-
if (typeof value === "string") return redactSecretString(value);
|
|
158
|
-
if (depth > 8) return "[redacted nested values]";
|
|
159
|
-
if (Array.isArray(value)) return value.map((item) => redactToolArgs(item, key, depth + 1));
|
|
160
|
-
if (!isPlainObject(value)) return value;
|
|
161
|
-
const out: Record<string, unknown> = {};
|
|
162
|
-
for (const [childKey, childValue] of Object.entries(value)) {
|
|
163
|
-
out[childKey] = redactToolArgs(childValue, childKey, depth + 1);
|
|
164
|
-
}
|
|
165
|
-
return out;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
134
|
function readStringField(args: unknown, key: string): string | null {
|
|
169
135
|
if (!isPlainObject(args)) return null;
|
|
170
136
|
const value = args[key];
|
|
@@ -381,6 +347,10 @@ function offloadPathOf(finished: ToolExecutionFinished): string | null {
|
|
|
381
347
|
return isPlainObject(resultSize) ? stringField(resultSize, "offloadPath") : null;
|
|
382
348
|
}
|
|
383
349
|
|
|
350
|
+
function offloadFileMissing(finished: ToolExecutionFinished): boolean {
|
|
351
|
+
return finished.resultSummary?.offloadFileMissing === true;
|
|
352
|
+
}
|
|
353
|
+
|
|
384
354
|
function shownBytesOf(finished: ToolExecutionFinished): number | null {
|
|
385
355
|
const observation = observationOf(finished);
|
|
386
356
|
const fromObservation = numberField(observation, "shownBytes");
|
|
@@ -456,10 +426,16 @@ function ledgerTail(finished: ToolExecutionFinished): { facts: string; offload:
|
|
|
456
426
|
if (details?.outputCapped === true) parts.push("output capped");
|
|
457
427
|
}
|
|
458
428
|
if (finished.excludeFromContext === true) parts.push("excluded from context");
|
|
429
|
+
if (finished.evictedReason !== undefined) parts.push("evicted", finished.evictedReason);
|
|
459
430
|
const offloadPath = executed ? offloadPathOf(finished) : null;
|
|
460
431
|
return {
|
|
461
432
|
facts: parts.length > 0 ? dim(` · ${parts.join(" · ")}`) : "",
|
|
462
|
-
offload:
|
|
433
|
+
offload:
|
|
434
|
+
offloadPath === null
|
|
435
|
+
? ""
|
|
436
|
+
: offloadFileMissing(finished)
|
|
437
|
+
? dim(" · full: gone after the 14-day retention sweep")
|
|
438
|
+
: dim(` · full: ${offloadPath}`),
|
|
463
439
|
};
|
|
464
440
|
}
|
|
465
441
|
|
|
@@ -473,7 +449,9 @@ const FAILURE_EXCERPT_LIMIT = 80;
|
|
|
473
449
|
* its failures carry one; the renderer never names a tool here.
|
|
474
450
|
*/
|
|
475
451
|
function failureExcerpt(finished: ToolExecutionFinished, width: number): string {
|
|
476
|
-
|
|
452
|
+
const presentation =
|
|
453
|
+
toolResultPresentationPolicy(finished.result) ?? toolPresentationPolicy(finished.toolName, finished.args);
|
|
454
|
+
if (!finished.isError || !presentation.failureExcerpt) return "";
|
|
477
455
|
if (isNonExecutedOutcome(finished.outcome)) return "";
|
|
478
456
|
const text = unwrapResultEnvelope(finished.result);
|
|
479
457
|
if (typeof text !== "string") return "";
|
|
@@ -871,6 +849,8 @@ function renderArgsBody(toolName: string, args: unknown, width: number, isError:
|
|
|
871
849
|
*/
|
|
872
850
|
function unwrapResultEnvelope(result: unknown): unknown {
|
|
873
851
|
if (typeof result === "string" || result === null || result === undefined) return result;
|
|
852
|
+
const presentationText = toolResultPresentationText(result);
|
|
853
|
+
if (presentationText !== null) return presentationText;
|
|
874
854
|
const blocks = Array.isArray(result)
|
|
875
855
|
? result
|
|
876
856
|
: isPlainObject(result) && Array.isArray(result.content)
|
|
@@ -1041,6 +1021,7 @@ function outputFacts(finished: ToolExecutionFinished): string[] {
|
|
|
1041
1021
|
}
|
|
1042
1022
|
if (isPlainObject(finished.result) && finished.result.terminate === true) parts.push("terminal result");
|
|
1043
1023
|
if (finished.excludeFromContext === true) parts.push("excluded from context");
|
|
1024
|
+
if (finished.evictedReason !== undefined) parts.push("evicted", finished.evictedReason);
|
|
1044
1025
|
return parts;
|
|
1045
1026
|
}
|
|
1046
1027
|
|
|
@@ -1058,7 +1039,10 @@ function renderOutputMeta(
|
|
|
1058
1039
|
function renderOutputFooter(finished: ToolExecutionFinished, width: number, isError: boolean): string[] {
|
|
1059
1040
|
const out: string[] = [];
|
|
1060
1041
|
const offloadPath = isNonExecutedOutcome(finished.outcome) ? null : offloadPathOf(finished);
|
|
1061
|
-
if (offloadPath !== null)
|
|
1042
|
+
if (offloadPath !== null) {
|
|
1043
|
+
const pointer = offloadFileMissing(finished) ? "gone after the 14-day retention sweep" : offloadPath;
|
|
1044
|
+
out.push(...indentAndWrap(`${yellow("full output")} ${pointer}`, width, isError));
|
|
1045
|
+
}
|
|
1062
1046
|
const hint =
|
|
1063
1047
|
stringField(resultSizeOf(finished), "followUpHint") ?? stringField(finished.resultSummary ?? null, "followUpHint");
|
|
1064
1048
|
if (hint !== null) out.push(...indentAndWrap(`${dim("next")} ${hint}`, width, isError));
|
|
@@ -1174,7 +1158,9 @@ export interface ToolSublineRenderOptions {
|
|
|
1174
1158
|
function foldedDiffRows(finished: ToolExecutionFinished, width: number, opts: ToolSublineRenderOptions): string[] {
|
|
1175
1159
|
if (opts.foldedExtras === "none") return [];
|
|
1176
1160
|
if (finished.isError || isNonExecutedOutcome(finished.outcome)) return [];
|
|
1177
|
-
|
|
1161
|
+
const presentation =
|
|
1162
|
+
toolResultPresentationPolicy(finished.result) ?? toolPresentationPolicy(finished.toolName, finished.args);
|
|
1163
|
+
if (!presentation.showDiffWhenFolded) return [];
|
|
1178
1164
|
const diff = resultDiff(finished.result);
|
|
1179
1165
|
if (diff === null) return [];
|
|
1180
1166
|
return truncateRowsMiddle(
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* back off replayed thinking text.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import { type ResponseModelIdObservation, responseModelIdObservationFromRecord } from "../core/response-model-id.js";
|
|
20
21
|
import { rawDurationMs } from "../core/timers.js";
|
|
21
22
|
import { extractReasoningTokens } from "../domains/session/context-accounting.js";
|
|
22
23
|
import type { MessageEntry, SessionEntry, SessionUsageDefaults } from "../domains/session/index.js";
|
|
@@ -133,6 +134,10 @@ export function lastTurnSummaryFromLedger(
|
|
|
133
134
|
let toolErrorCount = 0;
|
|
134
135
|
let targetId = turn.targetId;
|
|
135
136
|
let modelId = turn.modelId;
|
|
137
|
+
let responseModelIdObservation: ResponseModelIdObservation = {
|
|
138
|
+
state: "legacy-difference-only",
|
|
139
|
+
differingModelId: null,
|
|
140
|
+
};
|
|
136
141
|
|
|
137
142
|
for (const row of turn.rows) {
|
|
138
143
|
const record = payloadRecord(row.payload);
|
|
@@ -148,7 +153,8 @@ export function lastTurnSummaryFromLedger(
|
|
|
148
153
|
const reason = stopReasonOf(record.stopReason);
|
|
149
154
|
if (reason && reason !== "stop") stopReason = reason;
|
|
150
155
|
targetId = targetId ?? nonEmptyString(record, "provider", "api");
|
|
151
|
-
modelId = modelId ?? nonEmptyString(record, "
|
|
156
|
+
modelId = modelId ?? nonEmptyString(record, "model");
|
|
157
|
+
responseModelIdObservation = responseModelIdObservationFromRecord(record, "legacy-difference-only");
|
|
152
158
|
const usage = payloadRecord(record.usage);
|
|
153
159
|
if (!usage) continue;
|
|
154
160
|
inputTokens += positiveNumber(usage.input);
|
|
@@ -177,6 +183,7 @@ export function lastTurnSummaryFromLedger(
|
|
|
177
183
|
stopReason,
|
|
178
184
|
watchdogPeak: 0,
|
|
179
185
|
truncated: false,
|
|
186
|
+
responseModelIdObservation,
|
|
180
187
|
};
|
|
181
188
|
if (sawReasoning) {
|
|
182
189
|
summary.reasoningTokens = reasoningTokens;
|