@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
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { isSkillActivation, type SkillActivation } from "../../core/skill-activation.js";
|
|
15
15
|
import type { ClioTurnRecord } from "../../engine/session.js";
|
|
16
|
+
import { AUTONOMY_EXPOSURES, type AutonomyExposure } from "../safety/autonomy.js";
|
|
16
17
|
|
|
17
18
|
export interface SessionHeader {
|
|
18
19
|
type: "session";
|
|
@@ -229,6 +230,7 @@ export interface DecisionLedgerEntry extends BaseSessionEntry {
|
|
|
229
230
|
roundCount: number;
|
|
230
231
|
summary?: string;
|
|
231
232
|
transcriptPath?: string;
|
|
233
|
+
exposure?: AutonomyExposure;
|
|
232
234
|
decisions: DecisionRecord[];
|
|
233
235
|
}
|
|
234
236
|
|
|
@@ -276,6 +278,75 @@ export interface WorkerRunEntry extends BaseSessionEntry {
|
|
|
276
278
|
parentToolCallId?: string;
|
|
277
279
|
}
|
|
278
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Working-set layer (context domain) ledger records. Eviction is a projection:
|
|
283
|
+
* these entries say what left the model's working set and what came back; the
|
|
284
|
+
* original bodies stay in the ledger untouched. The context domain owns the
|
|
285
|
+
* semantics (`src/domains/context/working-set/contract.ts`); the session
|
|
286
|
+
* domain owns the wire shape because it owns the ledger format.
|
|
287
|
+
*/
|
|
288
|
+
export const EVICTION_REASONS = [
|
|
289
|
+
"superseded_read",
|
|
290
|
+
"stale_after_mutation",
|
|
291
|
+
"listing_consumed",
|
|
292
|
+
"failure_resolved",
|
|
293
|
+
"thinking_turn_closed",
|
|
294
|
+
"age_horizon",
|
|
295
|
+
"operator",
|
|
296
|
+
] as const;
|
|
297
|
+
export type EvictionReason = (typeof EVICTION_REASONS)[number];
|
|
298
|
+
|
|
299
|
+
export const EVICTION_TRIGGERS = ["pressure", "operator"] as const;
|
|
300
|
+
export type EvictionTrigger = (typeof EVICTION_TRIGGERS)[number];
|
|
301
|
+
|
|
302
|
+
export const RECALL_TRIGGERS = ["tool", "operator"] as const;
|
|
303
|
+
export type RecallTrigger = (typeof RECALL_TRIGGERS)[number];
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Identity of an evictable unit: the `turnId` of a ledger entry. For a
|
|
307
|
+
* `tool_result` message the unit is the result body; for an `assistant`
|
|
308
|
+
* message the unit is every thinking block it carries. Partial (per-block)
|
|
309
|
+
* eviction is deliberately not modelled; add a `block` field here when it is.
|
|
310
|
+
*/
|
|
311
|
+
export interface WorkingSetRef {
|
|
312
|
+
entry: string;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface EvictedItem {
|
|
316
|
+
ref: WorkingSetRef;
|
|
317
|
+
reason: EvictionReason;
|
|
318
|
+
/** Estimated tokens the projection removes for this item (marker cost already subtracted). */
|
|
319
|
+
tokensFreed: number;
|
|
320
|
+
/**
|
|
321
|
+
* Byte-stable one-line stub the projection renders in place of the body.
|
|
322
|
+
* Empty for thinking-block eviction, which removes without a marker.
|
|
323
|
+
*/
|
|
324
|
+
marker: string;
|
|
325
|
+
/** Ref key of the entry that superseded or resolved this one, when the reason names one. */
|
|
326
|
+
by?: string;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export interface ContextEvictionEntry extends BaseSessionEntry {
|
|
330
|
+
kind: "contextEviction";
|
|
331
|
+
policyId: string;
|
|
332
|
+
trigger: EvictionTrigger;
|
|
333
|
+
evicted: ReadonlyArray<EvictedItem>;
|
|
334
|
+
tokensBefore: number;
|
|
335
|
+
tokensAfter: number;
|
|
336
|
+
/** Used/window ratio that fired the event; null for operator-triggered events. */
|
|
337
|
+
pressureBefore: number | null;
|
|
338
|
+
snapshotIdBefore: string | null;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export interface ContextRecallEntry extends BaseSessionEntry {
|
|
342
|
+
kind: "contextRecall";
|
|
343
|
+
ref: WorkingSetRef;
|
|
344
|
+
trigger: RecallTrigger;
|
|
345
|
+
tokensReadmitted: number;
|
|
346
|
+
/** The tool call that performed the recall, when `trigger` is `tool`. */
|
|
347
|
+
toolCallId?: string;
|
|
348
|
+
}
|
|
349
|
+
|
|
279
350
|
export type SessionEntry =
|
|
280
351
|
| MessageEntry
|
|
281
352
|
| BashExecutionEntry
|
|
@@ -291,7 +362,9 @@ export type SessionEntry =
|
|
|
291
362
|
| SkillActivationEntry
|
|
292
363
|
| TaskLedgerEntry
|
|
293
364
|
| DecisionLedgerEntry
|
|
294
|
-
| WorkerRunEntry
|
|
365
|
+
| WorkerRunEntry
|
|
366
|
+
| ContextEvictionEntry
|
|
367
|
+
| ContextRecallEntry;
|
|
295
368
|
|
|
296
369
|
export type SessionFileEntry = SessionHeader | SessionEntry;
|
|
297
370
|
|
|
@@ -317,6 +390,8 @@ export const SESSION_ENTRY_KINDS = [
|
|
|
317
390
|
"taskLedger",
|
|
318
391
|
"decisionLedger",
|
|
319
392
|
"workerRun",
|
|
393
|
+
"contextEviction",
|
|
394
|
+
"contextRecall",
|
|
320
395
|
] as const;
|
|
321
396
|
|
|
322
397
|
export type SessionEntryKind = (typeof SESSION_ENTRY_KINDS)[number];
|
|
@@ -477,6 +552,21 @@ export function isSessionHeader(value: unknown): value is SessionHeader {
|
|
|
477
552
|
);
|
|
478
553
|
}
|
|
479
554
|
|
|
555
|
+
function isWorkingSetRef(value: unknown): value is WorkingSetRef {
|
|
556
|
+
return isRecord(value) && isString(value.entry);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function isEvictedItem(value: unknown): value is EvictedItem {
|
|
560
|
+
return (
|
|
561
|
+
isRecord(value) &&
|
|
562
|
+
isWorkingSetRef(value.ref) &&
|
|
563
|
+
isOneOf(value.reason, EVICTION_REASONS) &&
|
|
564
|
+
isNumber(value.tokensFreed) &&
|
|
565
|
+
isString(value.marker) &&
|
|
566
|
+
isOptionalString(value.by)
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
|
|
480
570
|
export function isSessionEntry(value: unknown): value is SessionEntry {
|
|
481
571
|
if (!value || typeof value !== "object") return false;
|
|
482
572
|
const v = value as Record<string, unknown>;
|
|
@@ -555,6 +645,7 @@ export function isSessionEntry(value: unknown): value is SessionEntry {
|
|
|
555
645
|
v.roundCount >= 0 &&
|
|
556
646
|
isOptionalString(v.summary) &&
|
|
557
647
|
isOptionalString(v.transcriptPath) &&
|
|
648
|
+
(v.exposure === undefined || isOneOf(v.exposure, AUTONOMY_EXPOSURES)) &&
|
|
558
649
|
Array.isArray(v.decisions) &&
|
|
559
650
|
v.decisions.every(isDecisionRecord)
|
|
560
651
|
);
|
|
@@ -567,6 +658,24 @@ export function isSessionEntry(value: unknown): value is SessionEntry {
|
|
|
567
658
|
isWorkerRunRuntime(v.runtime) &&
|
|
568
659
|
isOptionalString(v.parentToolCallId)
|
|
569
660
|
);
|
|
661
|
+
case "contextEviction":
|
|
662
|
+
return (
|
|
663
|
+
isString(v.policyId) &&
|
|
664
|
+
isOneOf(v.trigger, EVICTION_TRIGGERS) &&
|
|
665
|
+
Array.isArray(v.evicted) &&
|
|
666
|
+
v.evicted.every(isEvictedItem) &&
|
|
667
|
+
isNumber(v.tokensBefore) &&
|
|
668
|
+
isNumber(v.tokensAfter) &&
|
|
669
|
+
(v.pressureBefore === null || isNumber(v.pressureBefore)) &&
|
|
670
|
+
(v.snapshotIdBefore === null || isString(v.snapshotIdBefore))
|
|
671
|
+
);
|
|
672
|
+
case "contextRecall":
|
|
673
|
+
return (
|
|
674
|
+
isWorkingSetRef(v.ref) &&
|
|
675
|
+
isOneOf(v.trigger, RECALL_TRIGGERS) &&
|
|
676
|
+
isNumber(v.tokensReadmitted) &&
|
|
677
|
+
isOptionalString(v.toolCallId)
|
|
678
|
+
);
|
|
570
679
|
}
|
|
571
680
|
return false;
|
|
572
681
|
}
|
|
@@ -48,21 +48,15 @@ function collapseWhitespace(text: string): string {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
* Pull the first non-empty piece of
|
|
52
|
-
*
|
|
53
|
-
*
|
|
51
|
+
* Pull the first non-empty piece of text out of a message payload. Structured
|
|
52
|
+
* message payloads may contain a bare string, a text property, or pi-ai
|
|
53
|
+
* content blocks.
|
|
54
54
|
*/
|
|
55
|
-
function
|
|
56
|
-
if (typeof payload === "string")
|
|
57
|
-
const collapsed = collapseWhitespace(payload);
|
|
58
|
-
return collapsed.length > 0 ? collapsed : null;
|
|
59
|
-
}
|
|
55
|
+
function rawMessageText(payload: unknown): string | null {
|
|
56
|
+
if (typeof payload === "string") return payload.trim().length > 0 ? payload : null;
|
|
60
57
|
if (!payload || typeof payload !== "object") return null;
|
|
61
58
|
const obj = payload as Record<string, unknown>;
|
|
62
|
-
if (typeof obj.text === "string")
|
|
63
|
-
const collapsed = collapseWhitespace(obj.text);
|
|
64
|
-
if (collapsed.length > 0) return collapsed;
|
|
65
|
-
}
|
|
59
|
+
if (typeof obj.text === "string" && obj.text.trim().length > 0) return obj.text;
|
|
66
60
|
if (Array.isArray(obj.content)) {
|
|
67
61
|
const parts: string[] = [];
|
|
68
62
|
for (const part of obj.content) {
|
|
@@ -75,14 +69,65 @@ function extractUserText(payload: unknown): string | null {
|
|
|
75
69
|
parts.push((part as { text: string }).text);
|
|
76
70
|
}
|
|
77
71
|
}
|
|
78
|
-
const joined =
|
|
79
|
-
if (joined.length > 0) return joined;
|
|
72
|
+
const joined = parts.join("\n");
|
|
73
|
+
if (joined.trim().length > 0) return joined;
|
|
80
74
|
}
|
|
81
75
|
return null;
|
|
82
76
|
}
|
|
83
77
|
|
|
78
|
+
function extractMessageText(payload: unknown): string | null {
|
|
79
|
+
const raw = rawMessageText(payload);
|
|
80
|
+
if (raw === null) return null;
|
|
81
|
+
const collapsed = collapseWhitespace(raw);
|
|
82
|
+
return collapsed.length > 0 ? collapsed : null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* What the operator typed for a user turn, for the picker preview.
|
|
87
|
+
*
|
|
88
|
+
* The persisted text is the composed prompt: a `<system-reminder>` block and
|
|
89
|
+
* any `[Skill request]` preamble ride ahead of the operator's words as visible
|
|
90
|
+
* text the model receives. Reading that verbatim made every row of the
|
|
91
|
+
* `/resume` picker read `<system-reminder> [Skills] 9 installed…`, so sessions
|
|
92
|
+
* were indistinguishable and the type-to-filter matched them all (issue #188).
|
|
93
|
+
* Entries written since the operator text was persisted carry it as
|
|
94
|
+
* `operatorText`; older ones drop the leading scaffolding. Null when nothing
|
|
95
|
+
* operator-authored remains, so the caller moves on to the next turn.
|
|
96
|
+
*/
|
|
97
|
+
function operatorTextOfUserPayload(payload: unknown): string | null {
|
|
98
|
+
if (
|
|
99
|
+
payload &&
|
|
100
|
+
typeof payload === "object" &&
|
|
101
|
+
typeof (payload as { operatorText?: unknown }).operatorText === "string"
|
|
102
|
+
) {
|
|
103
|
+
const collapsed = collapseWhitespace((payload as { operatorText: string }).operatorText);
|
|
104
|
+
return collapsed.length > 0 ? collapsed : null;
|
|
105
|
+
}
|
|
106
|
+
// Strip before collapsing: the preamble's line structure is what the
|
|
107
|
+
// patterns anchor on.
|
|
108
|
+
const text = rawMessageText(payload);
|
|
109
|
+
if (text === null) return null;
|
|
110
|
+
const stripped = collapseWhitespace(stripInjectedPreamble(text));
|
|
111
|
+
return stripped.length > 0 ? stripped : null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const LEADING_SYSTEM_REMINDER = /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*/u;
|
|
115
|
+
const LEADING_SKILL_REQUEST =
|
|
116
|
+
/^\s*\[Skill request\][\s\S]*?Only these pending skill names are allowed this turn\.[^\n]*\s*/u;
|
|
117
|
+
|
|
118
|
+
function stripInjectedPreamble(text: string): string {
|
|
119
|
+
let current = text;
|
|
120
|
+
for (;;) {
|
|
121
|
+
const next = current.replace(LEADING_SYSTEM_REMINDER, "").replace(LEADING_SKILL_REQUEST, "");
|
|
122
|
+
if (next === current) return current;
|
|
123
|
+
current = next;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
84
127
|
interface ScanResult {
|
|
85
128
|
firstUserMessage: string | null;
|
|
129
|
+
/** The first assistant text, the preview when no turn carries operator words. */
|
|
130
|
+
firstAssistantMessage: string | null;
|
|
86
131
|
messageCount: number;
|
|
87
132
|
lastTimestamp: string | null;
|
|
88
133
|
name: string | null;
|
|
@@ -92,6 +137,7 @@ interface ScanResult {
|
|
|
92
137
|
function scanCurrentJsonl(currentPath: string): ScanResult {
|
|
93
138
|
const result: ScanResult = {
|
|
94
139
|
firstUserMessage: null,
|
|
140
|
+
firstAssistantMessage: null,
|
|
95
141
|
messageCount: 0,
|
|
96
142
|
lastTimestamp: null,
|
|
97
143
|
name: null,
|
|
@@ -106,10 +152,14 @@ function scanCurrentJsonl(currentPath: string): ScanResult {
|
|
|
106
152
|
if (parsed.kind === "message" && parsed.role === "user") {
|
|
107
153
|
result.messageCount += 1;
|
|
108
154
|
if (result.firstUserMessage === null) {
|
|
109
|
-
const text =
|
|
155
|
+
const text = operatorTextOfUserPayload(parsed.payload);
|
|
110
156
|
if (text !== null) result.firstUserMessage = text;
|
|
111
157
|
}
|
|
112
158
|
}
|
|
159
|
+
if (parsed.kind === "message" && parsed.role === "assistant" && result.firstAssistantMessage === null) {
|
|
160
|
+
const text = extractMessageText(parsed.payload);
|
|
161
|
+
if (text !== null) result.firstAssistantMessage = text;
|
|
162
|
+
}
|
|
113
163
|
if (parsed.kind === "sessionInfo") {
|
|
114
164
|
const info = parsed as SessionInfoEntry;
|
|
115
165
|
if (info.name !== undefined) {
|
|
@@ -139,11 +189,10 @@ function enrichMetaForListing(meta: SessionMeta, currentPath: string): void {
|
|
|
139
189
|
if (scan.name) meta.name = scan.name;
|
|
140
190
|
const labelValues = [...scan.labels.values()].map((entry) => entry.label.trim()).filter((label) => label.length > 0);
|
|
141
191
|
if (labelValues.length > 0) meta.labels = labelValues;
|
|
142
|
-
|
|
192
|
+
const preview = scan.firstUserMessage ?? scan.firstAssistantMessage;
|
|
193
|
+
if (preview) {
|
|
143
194
|
meta.firstMessagePreview =
|
|
144
|
-
|
|
145
|
-
? `${scan.firstUserMessage.slice(0, PREVIEW_MAX_CHARS - 1)}…`
|
|
146
|
-
: scan.firstUserMessage;
|
|
195
|
+
preview.length > PREVIEW_MAX_CHARS ? `${preview.slice(0, PREVIEW_MAX_CHARS - 1)}…` : preview;
|
|
147
196
|
}
|
|
148
197
|
if (scan.messageCount > 0) meta.messageCount = scan.messageCount;
|
|
149
198
|
const fallbackMtime = readMtimeIso(currentPath);
|
|
@@ -70,9 +70,16 @@ export function resumeSessionState(sessionId: string): {
|
|
|
70
70
|
} {
|
|
71
71
|
const persistedMeta = readSessionMeta(sessionId) as SessionMeta;
|
|
72
72
|
const paths = sessionPaths(persistedMeta);
|
|
73
|
-
runMigrations(persistedMeta, paths.meta);
|
|
73
|
+
const migration = runMigrations(persistedMeta, paths.meta);
|
|
74
74
|
const { meta, writer, tree } = engineResumeSession(sessionId);
|
|
75
|
-
|
|
75
|
+
const state: SessionManagerState = { meta: meta as SessionMeta, writer };
|
|
76
|
+
if (migration.migrated) {
|
|
77
|
+
// The ledger needed no transformation; only the stamp moves, so the next
|
|
78
|
+
// reader does not re-run the same no-op.
|
|
79
|
+
state.meta.sessionFormatVersion = migration.to;
|
|
80
|
+
persistSessionMeta(state);
|
|
81
|
+
}
|
|
82
|
+
return { state, nodes: tree };
|
|
76
83
|
}
|
|
77
84
|
|
|
78
85
|
export function appendTurn(state: SessionManagerState, input: TurnInput): ClioTurnRecord {
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
* Strict session-format version reader.
|
|
3
3
|
*
|
|
4
4
|
* Runs before every session resume. Earlier pre-1.0 formats are disposable
|
|
5
|
-
* local state and must not be transformed into the current format.
|
|
5
|
+
* local state and must not be transformed into the current format. A version
|
|
6
|
+
* from the future is refused for the opposite reason: the file is not
|
|
7
|
+
* disposable, it belongs to a newer Clio, and this build would silently drop
|
|
8
|
+
* whatever that build understood and this one does not. Downgrading and
|
|
9
|
+
* resuming would then write the truncated reading back over the original.
|
|
10
|
+
*
|
|
11
|
+
* Version 3 to 4 is the one additive step: 4 only adds the working-set kinds
|
|
12
|
+
* (`contextEviction`, `contextRecall`), so a version-3 ledger is read as-is
|
|
13
|
+
* and the metadata is restamped. Nothing in the file changes.
|
|
6
14
|
*/
|
|
7
15
|
|
|
8
16
|
import { CURRENT_SESSION_FORMAT_VERSION } from "../../../engine/session.js";
|
|
@@ -10,6 +18,9 @@ import type { SessionMeta } from "../contract.js";
|
|
|
10
18
|
|
|
11
19
|
export { CURRENT_SESSION_FORMAT_VERSION };
|
|
12
20
|
|
|
21
|
+
/** Oldest version this build reads without transforming the ledger. */
|
|
22
|
+
export const OLDEST_READABLE_SESSION_FORMAT_VERSION = 3;
|
|
23
|
+
|
|
13
24
|
export interface MigrationResult {
|
|
14
25
|
migrated: boolean;
|
|
15
26
|
from: number;
|
|
@@ -18,10 +29,18 @@ export interface MigrationResult {
|
|
|
18
29
|
|
|
19
30
|
export function runMigrations(meta: SessionMeta, sessionPath: string): MigrationResult {
|
|
20
31
|
const from = meta.sessionFormatVersion ?? 1;
|
|
21
|
-
if (from <
|
|
32
|
+
if (from < OLDEST_READABLE_SESSION_FORMAT_VERSION) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`session metadata has an unsupported format version (expected version ${CURRENT_SESSION_FORMAT_VERSION}, got ${from}): ${sessionPath}. Remove the session directory to start a new session.`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (from > CURRENT_SESSION_FORMAT_VERSION) {
|
|
22
38
|
throw new Error(
|
|
23
|
-
`session
|
|
39
|
+
`session was written by a newer Clio (format version ${from}, this build reads version ${CURRENT_SESSION_FORMAT_VERSION}): ${sessionPath}. Upgrade clio-coder to resume this session.`,
|
|
24
40
|
);
|
|
25
41
|
}
|
|
42
|
+
if (from < CURRENT_SESSION_FORMAT_VERSION) {
|
|
43
|
+
return { migrated: true, from, to: CURRENT_SESSION_FORMAT_VERSION };
|
|
44
|
+
}
|
|
26
45
|
return { migrated: false, from, to: from };
|
|
27
46
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fold a session ledger's assistant turns into the per-call usage the rest of
|
|
3
3
|
* Clio reports: tokens in and out, cache reads and writes, reasoning tokens,
|
|
4
|
-
* and provider-reported cost, attributed to the target and
|
|
5
|
-
*
|
|
4
|
+
* and provider-reported cost, attributed to the target and observed model id
|
|
5
|
+
* for each call.
|
|
6
6
|
*
|
|
7
7
|
* This lives in the session domain because the ledger is the only durable
|
|
8
8
|
* record of what a session spent, and three surfaces read it: the `/cost`
|
|
@@ -19,12 +19,22 @@
|
|
|
19
19
|
* counting it would add an API call worth nothing.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
import {
|
|
23
|
+
attributedModelId,
|
|
24
|
+
type ResponseModelIdObservation,
|
|
25
|
+
responseModelIdObservationFromRecord,
|
|
26
|
+
} from "../../core/response-model-id.js";
|
|
22
27
|
import type { SessionEntry } from "./entries.js";
|
|
23
28
|
|
|
24
29
|
/** One completed assistant API call, as the ledger recorded it. */
|
|
25
30
|
export interface LedgerUsageCall {
|
|
26
31
|
providerId: string;
|
|
27
|
-
|
|
32
|
+
/** Model id the accounting row uses, or `unknown` when an observed response omitted it. */
|
|
33
|
+
attributedModelId: string;
|
|
34
|
+
/** The id the session asked for (the configured model, or the message's own `model`). */
|
|
35
|
+
requestedModelId: string;
|
|
36
|
+
/** Direct presence observation, with the pre-#193 difference-only shape labeled separately. */
|
|
37
|
+
responseModelIdObservation: ResponseModelIdObservation;
|
|
28
38
|
input: number;
|
|
29
39
|
output: number;
|
|
30
40
|
cacheRead: number;
|
|
@@ -71,8 +81,8 @@ function stringAt(source: Record<string, unknown>, ...keys: string[]): string |
|
|
|
71
81
|
* payload instead split one endpoint into two blocks in `/cost`, so a single
|
|
72
82
|
* `node-a` target on `llamacpp` rendered as two providers whose turn counts
|
|
73
83
|
* diverged with every resume. `modelChange` rows are replayed in order so a
|
|
74
|
-
* session that switched targets mid-way attributes each call to the
|
|
75
|
-
*
|
|
84
|
+
* session that switched targets mid-way attributes each call to the active
|
|
85
|
+
* target.
|
|
76
86
|
*/
|
|
77
87
|
export function ledgerUsageCalls(
|
|
78
88
|
entries: ReadonlyArray<SessionEntry>,
|
|
@@ -92,7 +102,9 @@ export function ledgerUsageCalls(
|
|
|
92
102
|
if (!usage || usage.totalTokens <= 0) continue;
|
|
93
103
|
calls.push({
|
|
94
104
|
providerId: currentTarget ?? "unknown",
|
|
95
|
-
|
|
105
|
+
attributedModelId: currentModel ?? "unknown",
|
|
106
|
+
requestedModelId: currentModel ?? "unknown",
|
|
107
|
+
responseModelIdObservation: { state: "not-observed" },
|
|
96
108
|
input: usage.input,
|
|
97
109
|
output: usage.output,
|
|
98
110
|
cacheRead: usage.cacheRead,
|
|
@@ -132,9 +144,14 @@ export function ledgerUsageCalls(
|
|
|
132
144
|
if (totalTokens === 0) continue;
|
|
133
145
|
const cost = usage.cost;
|
|
134
146
|
const costUsd = cost && typeof cost === "object" ? numberAt(cost as Record<string, unknown>, "total") : 0;
|
|
147
|
+
const requestedModelId = currentModel ?? stringAt(record, "model") ?? "unknown";
|
|
148
|
+
const differingResponseModelId = stringAt(record, "responseModel");
|
|
149
|
+
const responseModelIdObservation = responseModelIdObservationFromRecord(record, "legacy-difference-only");
|
|
135
150
|
calls.push({
|
|
136
151
|
providerId: currentTarget ?? stringAt(record, "provider", "api") ?? "unknown",
|
|
137
|
-
|
|
152
|
+
attributedModelId: attributedModelId(responseModelIdObservation, requestedModelId, differingResponseModelId),
|
|
153
|
+
requestedModelId,
|
|
154
|
+
responseModelIdObservation,
|
|
138
155
|
input,
|
|
139
156
|
output,
|
|
140
157
|
cacheRead,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks";
|
|
2
|
+
import { describeCallAction } from "../../domains/safety/call-target.js";
|
|
2
3
|
import type { AgentEvent, AgentMessage } from "../types.js";
|
|
3
4
|
import type { ClioWorkerEvent } from "../worker-events.js";
|
|
4
5
|
import type { AcpPromptResponse, AcpSessionUpdateParams, AcpToolCallUpdate } from "./types.js";
|
|
@@ -111,12 +112,17 @@ export class AcpEventMapper {
|
|
|
111
112
|
// keeps the monotonic twin, which is what the duration below spans.
|
|
112
113
|
const startedAt = Date.now();
|
|
113
114
|
this.toolStarts.set(toolCallId, performance.now());
|
|
115
|
+
// A peer's `rawInput` is its own argument object and never reaches an
|
|
116
|
+
// operator surface. The descriptor composed from it here does.
|
|
117
|
+
const action = describeCallAction(update.kind ?? title, update.rawInput);
|
|
114
118
|
out.push({
|
|
115
119
|
type: "clio_tool_start",
|
|
116
120
|
payload: {
|
|
117
121
|
tool: update.kind ?? title,
|
|
122
|
+
toolCallId,
|
|
118
123
|
posture: "operating",
|
|
119
124
|
startedAt,
|
|
125
|
+
...(action !== null ? { action } : {}),
|
|
120
126
|
},
|
|
121
127
|
});
|
|
122
128
|
out.push({
|
|
@@ -142,6 +148,7 @@ export class AcpEventMapper {
|
|
|
142
148
|
type: "clio_tool_finish",
|
|
143
149
|
payload: {
|
|
144
150
|
tool: update.kind ?? title,
|
|
151
|
+
toolCallId,
|
|
145
152
|
posture: "operating",
|
|
146
153
|
durationMs: Math.round(performance.now() - startedAtClock),
|
|
147
154
|
outcome: isError ? "error" : "ok",
|
package/src/engine/acp/server.ts
CHANGED
|
@@ -5,13 +5,20 @@ import { BusChannels, type LoopBlockedPayload } from "../../core/bus-events.js";
|
|
|
5
5
|
import { DEFAULT_DELEGATION_PERMISSION_TIMEOUT_MS } from "../../core/defaults.js";
|
|
6
6
|
import type { SafeEventBus } from "../../core/event-bus.js";
|
|
7
7
|
import { MAX_TIMER_DELAY_MS } from "../../core/timers.js";
|
|
8
|
+
import { ToolNames } from "../../core/tool-names.js";
|
|
8
9
|
import type { ProvidersContract } from "../../domains/providers/contract.js";
|
|
9
10
|
import { isOrchestratorEligibleRuntime } from "../../domains/providers/eligibility.js";
|
|
10
11
|
import { type AutonomyLevel, DEFAULT_AUTONOMY_LEVEL } from "../../domains/safety/autonomy.js";
|
|
12
|
+
import {
|
|
13
|
+
classifyDecisionPresentation,
|
|
14
|
+
decisionFactsForPermission,
|
|
15
|
+
} from "../../domains/safety/decision-presentation.js";
|
|
11
16
|
import type { SessionContract, SessionMeta } from "../../domains/session/contract.js";
|
|
12
17
|
import type { MessageEntry, SessionEntry } from "../../domains/session/entries.js";
|
|
13
18
|
import { filterEntriesToActivePath } from "../../domains/session/tree/active-path.js";
|
|
19
|
+
import { askUserExposure } from "../../tools/ask-user.js";
|
|
14
20
|
import type { ToolRegistry } from "../../tools/registry.js";
|
|
21
|
+
import { toolResultPresentationText } from "../../tools/result-disposition.js";
|
|
15
22
|
import type { AgentMessage } from "../types.js";
|
|
16
23
|
import { ACP_TURN_FAILED_MESSAGE, AcpRequestError, AcpTimeoutError, acpErrorMessage } from "./errors.js";
|
|
17
24
|
import type { AcpJsonRpcPeerTransport } from "./transport.js";
|
|
@@ -537,6 +544,8 @@ function applyStopReason(active: ActivePrompt, message: unknown): void {
|
|
|
537
544
|
function outputText(value: unknown): string {
|
|
538
545
|
if (typeof value === "string") return value;
|
|
539
546
|
if (!isRecord(value)) return "";
|
|
547
|
+
const presentationText = toolResultPresentationText(value);
|
|
548
|
+
if (presentationText !== null) return presentationText;
|
|
540
549
|
if (Array.isArray(value.content)) return contentText(value.content);
|
|
541
550
|
if (typeof value.output === "string") return value.output;
|
|
542
551
|
if (typeof value.text === "string") return value.text;
|
|
@@ -1262,6 +1271,19 @@ function installPermissionBridge(input: {
|
|
|
1262
1271
|
const queuedRequestDetails = new Map<string, { tool: string; actionClass: string }>();
|
|
1263
1272
|
const unregister = input.toolRegistry.onPermissionRequired((call, decision, meta) => {
|
|
1264
1273
|
if (queuedRequestIds.has(meta.requestId)) return;
|
|
1274
|
+
const presentation = classifyDecisionPresentation(
|
|
1275
|
+
decisionFactsForPermission({
|
|
1276
|
+
tool: call.tool,
|
|
1277
|
+
actionClass: decision.classification.actionClass,
|
|
1278
|
+
axis: meta.axis.startsWith("net:")
|
|
1279
|
+
? { kind: "safety-net", ruleId: meta.axis.slice("net:".length) || "unknown" }
|
|
1280
|
+
: { kind: "autonomy", level: meta.axis.slice("autonomy:".length) || DEFAULT_AUTONOMY_LEVEL },
|
|
1281
|
+
origin: { kind: "main" },
|
|
1282
|
+
...(call.tool === ToolNames.AskUser ? { exposure: askUserExposure(call.args) } : {}),
|
|
1283
|
+
}),
|
|
1284
|
+
);
|
|
1285
|
+
const approveAction = presentation.requiredActions.find((action) => action.id === "approve-once");
|
|
1286
|
+
const denyAction = presentation.requiredActions.find((action) => action.id === "deny");
|
|
1265
1287
|
queuedRequestIds.add(meta.requestId);
|
|
1266
1288
|
queuedRequestDetails.set(meta.requestId, {
|
|
1267
1289
|
tool: call.tool,
|
|
@@ -1378,8 +1400,16 @@ function installPermissionBridge(input: {
|
|
|
1378
1400
|
...(snapshot.locations !== undefined ? { locations: snapshot.locations } : {}),
|
|
1379
1401
|
},
|
|
1380
1402
|
options: [
|
|
1381
|
-
{
|
|
1382
|
-
|
|
1403
|
+
{
|
|
1404
|
+
optionId: "allow-once",
|
|
1405
|
+
name: approveAction?.label ?? "Approve once",
|
|
1406
|
+
kind: "allow_once",
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
optionId: "reject-once",
|
|
1410
|
+
name: denyAction?.label ?? "Deny this request",
|
|
1411
|
+
kind: "reject_once",
|
|
1412
|
+
},
|
|
1383
1413
|
],
|
|
1384
1414
|
},
|
|
1385
1415
|
input.permissionTimeoutMs,
|
package/src/engine/agent.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { Agent, type AgentOptions, type StreamFn } from "@earendil-works/pi-agent-core";
|
|
14
|
+
import { isDispositionedToolResultError } from "../tools/result-disposition.js";
|
|
14
15
|
import { engineStreamSimple } from "./api-registry.js";
|
|
15
16
|
|
|
16
17
|
export type EngineAgentOptions = Omit<AgentOptions, "streamFn"> & { streamFn?: StreamFn };
|
|
@@ -20,8 +21,24 @@ export interface EngineAgentHandle {
|
|
|
20
21
|
state(): Agent["state"];
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
function dispositionAwareAfterToolCall(
|
|
25
|
+
delegate: AgentOptions["afterToolCall"],
|
|
26
|
+
): NonNullable<AgentOptions["afterToolCall"]> {
|
|
27
|
+
return async (context, signal) => {
|
|
28
|
+
const override = await delegate?.(context, signal);
|
|
29
|
+
const effectiveResult =
|
|
30
|
+
override?.details === undefined ? context.result : { ...context.result, details: override.details };
|
|
31
|
+
if (isDispositionedToolResultError(effectiveResult)) return { ...override, isError: true };
|
|
32
|
+
return override;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
export function createEngineAgent(options: EngineAgentOptions = {}): EngineAgentHandle {
|
|
24
|
-
const agent = new Agent({
|
|
37
|
+
const agent = new Agent({
|
|
38
|
+
streamFn: engineStreamSimple,
|
|
39
|
+
...options,
|
|
40
|
+
afterToolCall: dispositionAwareAfterToolCall(options.afterToolCall),
|
|
41
|
+
});
|
|
25
42
|
return {
|
|
26
43
|
agent,
|
|
27
44
|
state: () => agent.state,
|
|
@@ -130,6 +130,25 @@ async function loadOwnedInstance(
|
|
|
130
130
|
return instanceId;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Residency runs before every request, and the same co-residency facts held
|
|
135
|
+
* every turn, so the peer warning printed once per turn (issue #185). Each
|
|
136
|
+
* distinct fact is said once per process; a new target, model, or peer set is
|
|
137
|
+
* a new fact.
|
|
138
|
+
*/
|
|
139
|
+
const announcedResidencyFacts = new Set<string>();
|
|
140
|
+
|
|
141
|
+
function emitResidencyNoticeOnce(key: string, notice: Parameters<typeof emitResidencyNotice>[0]): void {
|
|
142
|
+
if (announcedResidencyFacts.has(key)) return;
|
|
143
|
+
announcedResidencyFacts.add(key);
|
|
144
|
+
emitResidencyNotice(notice);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Test seam: forget which residency facts were announced. */
|
|
148
|
+
export function resetAnnouncedResidencyFacts(): void {
|
|
149
|
+
announcedResidencyFacts.clear();
|
|
150
|
+
}
|
|
151
|
+
|
|
133
152
|
export async function ensureLmStudioResidency(
|
|
134
153
|
model: Model<"openai-completions">,
|
|
135
154
|
options: { apiKey?: string; signal?: AbortSignal } = {},
|
|
@@ -155,7 +174,7 @@ export async function ensureLmStudioResidency(
|
|
|
155
174
|
);
|
|
156
175
|
}
|
|
157
176
|
if (resolution.wireModelId !== model.id) {
|
|
158
|
-
|
|
177
|
+
emitResidencyNoticeOnce(`resolved|${info.targetId}|${model.id}|${resolution.wireModelId}`, {
|
|
159
178
|
kind: "co-resident",
|
|
160
179
|
level: "info",
|
|
161
180
|
targetId: info.targetId,
|
|
@@ -166,14 +185,16 @@ export async function ensureLmStudioResidency(
|
|
|
166
185
|
});
|
|
167
186
|
}
|
|
168
187
|
if (resolution.peerTargets.length > 0) {
|
|
169
|
-
|
|
188
|
+
const peers = resolution.peerTargets.join(", ");
|
|
189
|
+
const requested = resolution.wireModelId === model.id ? "" : ` (requested '${model.id}')`;
|
|
190
|
+
emitResidencyNoticeOnce(`peer|${info.targetId}|${model.id}|${resolution.wireModelId}|${peers}`, {
|
|
170
191
|
kind: "co-resident",
|
|
171
192
|
level: "warning",
|
|
172
193
|
targetId: info.targetId,
|
|
173
194
|
runtimeId: "lmstudio",
|
|
174
195
|
model: resolution.wireModelId,
|
|
175
|
-
message: `LM Studio instance '${resolution.wireModelId}' is also loaded on ${
|
|
176
|
-
detail: {
|
|
196
|
+
message: `LM Studio instance '${resolution.wireModelId}'${requested} is also loaded on ${peers}; a request may be served by that LM Link peer, and the footer and usage ledger name the id that answered when it differs.`,
|
|
197
|
+
detail: { requestedModel: model.id, wireModel: resolution.wireModelId, peerTargets: peers },
|
|
177
198
|
});
|
|
178
199
|
}
|
|
179
200
|
if (!load || Object.keys(load).length === 0 || resolution.instance) return resolution.wireModelId;
|