@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,4 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
legacyToolResultDigest,
|
|
4
|
+
sanitizeToolResultDigest,
|
|
5
|
+
type ToolResultDigest,
|
|
6
|
+
} from "../../tools/result-disposition.js";
|
|
2
7
|
import { TASK_MEMORY_DEFAULT_PROCEDURAL_CAP, type TaskMemoryBank } from "../memory/task-bank.js";
|
|
3
8
|
import {
|
|
4
9
|
runTaskMemoryPolicy,
|
|
@@ -40,7 +45,6 @@ export interface MemoryInterventionSettings {
|
|
|
40
45
|
timeoutMs: number;
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
const RESULT_DIGEST_MAX_CHARS = 240;
|
|
44
48
|
const CALL_DESCRIPTION_MAX_CHARS = 180;
|
|
45
49
|
const NO_EFFECTS: ReadonlyArray<MiddlewareEffect> = [];
|
|
46
50
|
|
|
@@ -48,7 +52,7 @@ type ToolOutcome = "ok" | "error";
|
|
|
48
52
|
|
|
49
53
|
interface PendingToolStep {
|
|
50
54
|
toolName: string;
|
|
51
|
-
|
|
55
|
+
operationFingerprint: string;
|
|
52
56
|
callDescription: string;
|
|
53
57
|
}
|
|
54
58
|
|
|
@@ -145,7 +149,7 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
145
149
|
let toolStep = 0;
|
|
146
150
|
let lastTurnEndStep = 0;
|
|
147
151
|
let reactivateAfterCompaction = false;
|
|
148
|
-
let
|
|
152
|
+
let lastInjectedOperationFingerprint: string | null = null;
|
|
149
153
|
let currentTask = "(current task unavailable)";
|
|
150
154
|
let toolsSinceMemoryStep = 0;
|
|
151
155
|
let consecutiveErrors = 0;
|
|
@@ -202,6 +206,9 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
202
206
|
return effects;
|
|
203
207
|
}
|
|
204
208
|
case "on_compaction":
|
|
209
|
+
// Recall grows the working set; it is an observability point, not
|
|
210
|
+
// context loss that should reactivate compacted task memory.
|
|
211
|
+
if (input.metadata?.stage === "working_set_recall") return NO_EFFECTS;
|
|
205
212
|
reactivateAfterCompaction = true;
|
|
206
213
|
return NO_EFFECTS;
|
|
207
214
|
case "turn_start": {
|
|
@@ -464,13 +471,13 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
464
471
|
function observeBeforeTool(input: MiddlewareHookInput): void {
|
|
465
472
|
const prepared = prepareToolStep(input);
|
|
466
473
|
if (prepared === null) return;
|
|
467
|
-
setBounded(pending, pendingKey(input, prepared.
|
|
474
|
+
setBounded(pending, pendingKey(input, prepared.operationFingerprint), prepared, settings().windowSteps);
|
|
468
475
|
}
|
|
469
476
|
|
|
470
477
|
function observeAfterTool(input: MiddlewareHookInput): ReadonlyArray<MiddlewareEffect> {
|
|
471
478
|
const fallback = prepareToolStep(input);
|
|
472
479
|
if (fallback === null) return NO_EFFECTS;
|
|
473
|
-
const key = pendingKey(input, fallback.
|
|
480
|
+
const key = pendingKey(input, fallback.operationFingerprint);
|
|
474
481
|
const prepared = pending.get(key) ?? fallback;
|
|
475
482
|
pending.delete(key);
|
|
476
483
|
const outcome = input.metadata?.resultKind === "error" ? "error" : "ok";
|
|
@@ -484,11 +491,13 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
484
491
|
} else {
|
|
485
492
|
consecutiveErrors = 0;
|
|
486
493
|
}
|
|
494
|
+
const digest = resultDigest(input, outcome);
|
|
487
495
|
const step: TrajectoryStep = {
|
|
488
496
|
...prepared,
|
|
489
497
|
step: toolStep,
|
|
490
498
|
outcome,
|
|
491
|
-
resultDigest:
|
|
499
|
+
resultDigest: digest.text,
|
|
500
|
+
resultDigestProvenance: digest.provenance,
|
|
492
501
|
};
|
|
493
502
|
trajectory.push(step);
|
|
494
503
|
if (trajectory.length > live.windowSteps) trajectory.splice(0, trajectory.length - live.windowSteps);
|
|
@@ -504,20 +513,20 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
504
513
|
* itself, which the model reads on its very next round.
|
|
505
514
|
*/
|
|
506
515
|
function annotateRepeatedFailure(step: TrajectoryStep): ReadonlyArray<MiddlewareEffect> {
|
|
507
|
-
if (annotatedThisTurn.has(step.
|
|
516
|
+
if (annotatedThisTurn.has(step.operationFingerprint)) return NO_EFFECTS;
|
|
508
517
|
const occurrences = trajectory.filter(
|
|
509
|
-
(candidate) => candidate.outcome === "error" && candidate.
|
|
518
|
+
(candidate) => candidate.outcome === "error" && candidate.operationFingerprint === step.operationFingerprint,
|
|
510
519
|
).length;
|
|
511
|
-
const failure = failures.get(step.
|
|
520
|
+
const failure = failures.get(step.operationFingerprint);
|
|
512
521
|
if (occurrences < 2 || failure === undefined) return NO_EFFECTS;
|
|
513
522
|
const message = boundedReminder(
|
|
514
523
|
`Memory: [${failure.entryId}] you already tried ${failure.callDescription} at step ${failure.firstStep} and it failed with ${failure.errorDigest}. Change the approach rather than repeating it.`,
|
|
515
524
|
settings().maxTokens,
|
|
516
525
|
);
|
|
517
526
|
if (message.length === 0) return NO_EFFECTS;
|
|
518
|
-
annotatedThisTurn.add(step.
|
|
527
|
+
annotatedThisTurn.add(step.operationFingerprint);
|
|
519
528
|
// Claim the turn-end channel too, so one repeated failure is surfaced once.
|
|
520
|
-
|
|
529
|
+
lastInjectedOperationFingerprint = step.operationFingerprint;
|
|
521
530
|
lastDecision = "injected";
|
|
522
531
|
rulesInjectedSincePromptedStep = true;
|
|
523
532
|
annotatedSinceTurnEnd = true;
|
|
@@ -527,7 +536,7 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
527
536
|
}
|
|
528
537
|
|
|
529
538
|
function rememberFailure(step: TrajectoryStep): void {
|
|
530
|
-
const previous = failures.get(step.
|
|
539
|
+
const previous = failures.get(step.operationFingerprint);
|
|
531
540
|
const attempts = (previous?.attempts ?? 0) + 1;
|
|
532
541
|
const firstStep = previous?.firstStep ?? step.step;
|
|
533
542
|
const content = proceduralContent(step.callDescription, attempts, firstStep, step.resultDigest);
|
|
@@ -543,7 +552,7 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
543
552
|
}
|
|
544
553
|
setBounded(
|
|
545
554
|
failures,
|
|
546
|
-
step.
|
|
555
|
+
step.operationFingerprint,
|
|
547
556
|
{
|
|
548
557
|
entryId,
|
|
549
558
|
attempts,
|
|
@@ -563,21 +572,21 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
563
572
|
step === undefined ||
|
|
564
573
|
step.outcome !== "error" ||
|
|
565
574
|
step.step <= lastTurnEndStep ||
|
|
566
|
-
step.
|
|
575
|
+
step.operationFingerprint === lastInjectedOperationFingerprint
|
|
567
576
|
) {
|
|
568
577
|
continue;
|
|
569
578
|
}
|
|
570
579
|
const occurrences = trajectory.filter(
|
|
571
|
-
(candidate) => candidate.outcome === "error" && candidate.
|
|
580
|
+
(candidate) => candidate.outcome === "error" && candidate.operationFingerprint === step.operationFingerprint,
|
|
572
581
|
).length;
|
|
573
|
-
const failure = failures.get(step.
|
|
582
|
+
const failure = failures.get(step.operationFingerprint);
|
|
574
583
|
if (occurrences < 2 || failure === undefined) continue;
|
|
575
584
|
const message = boundedReminder(
|
|
576
585
|
`Memory: [${failure.entryId}] you already tried ${failure.callDescription} at step ${failure.firstStep} and it failed with ${failure.errorDigest}.`,
|
|
577
586
|
settings().maxTokens,
|
|
578
587
|
);
|
|
579
588
|
if (message.length === 0) return NO_EFFECTS;
|
|
580
|
-
|
|
589
|
+
lastInjectedOperationFingerprint = step.operationFingerprint;
|
|
581
590
|
lastInjectedMessage = message;
|
|
582
591
|
lastDecision = "injected";
|
|
583
592
|
rulesInjectedSincePromptedStep = true;
|
|
@@ -621,31 +630,35 @@ function prepareToolStep(input: MiddlewareHookInput): PendingToolStep | null {
|
|
|
621
630
|
const canonical = hashToolCall(toolName, input.toolArgs ?? {});
|
|
622
631
|
return {
|
|
623
632
|
toolName,
|
|
624
|
-
|
|
633
|
+
operationFingerprint: createHash("sha256").update(canonical).digest("hex").slice(0, 16),
|
|
625
634
|
callDescription: shortText(canonical, CALL_DESCRIPTION_MAX_CHARS),
|
|
626
635
|
};
|
|
627
636
|
}
|
|
628
637
|
|
|
629
|
-
function pendingKey(input: MiddlewareHookInput,
|
|
630
|
-
return input.toolCallId ?? `anonymous:${
|
|
638
|
+
function pendingKey(input: MiddlewareHookInput, operationFingerprint: string): string {
|
|
639
|
+
return input.toolCallId ?? `anonymous:${operationFingerprint}`;
|
|
631
640
|
}
|
|
632
641
|
|
|
633
|
-
function resultDigest(input: MiddlewareHookInput, outcome: ToolOutcome):
|
|
642
|
+
function resultDigest(input: MiddlewareHookInput, outcome: ToolOutcome): ToolResultDigest {
|
|
643
|
+
if (input.toolResultDigest !== undefined) return sanitizeToolResultDigest(input.toolResultDigest);
|
|
634
644
|
if (outcome === "error") {
|
|
635
645
|
const metadataMessage = input.metadata?.errorMessage;
|
|
636
646
|
if (typeof metadataMessage === "string" && metadataMessage.trim().length > 0) {
|
|
637
|
-
return
|
|
647
|
+
return legacyToolResultDigest(diagnosticLine(metadataMessage), { outcome });
|
|
638
648
|
}
|
|
639
649
|
for (const key of ["error", "message"] as const) {
|
|
640
650
|
const value = input.toolResultDetails?.[key];
|
|
641
651
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
642
|
-
return
|
|
652
|
+
return legacyToolResultDigest(diagnosticLine(value), { outcome });
|
|
643
653
|
}
|
|
644
654
|
}
|
|
645
|
-
return "
|
|
655
|
+
return legacyToolResultDigest("", { outcome });
|
|
646
656
|
}
|
|
647
657
|
const resultFingerprint = input.metadata?.resultFingerprint;
|
|
648
|
-
return
|
|
658
|
+
return legacyToolResultDigest(
|
|
659
|
+
typeof resultFingerprint === "string" ? `ok result ${resultFingerprint.slice(0, 16)}` : "ok",
|
|
660
|
+
{ outcome },
|
|
661
|
+
);
|
|
649
662
|
}
|
|
650
663
|
|
|
651
664
|
const DIAGNOSTIC_HINT =
|
|
@@ -328,6 +328,12 @@ function cloneHookInput(input: MiddlewareHookInput): MiddlewareHookInput {
|
|
|
328
328
|
if (input.metadata !== undefined) cloned.metadata = { ...input.metadata };
|
|
329
329
|
if (input.toolArgs !== undefined) cloned.toolArgs = { ...input.toolArgs };
|
|
330
330
|
if (input.toolResultDetails !== undefined) cloned.toolResultDetails = { ...input.toolResultDetails };
|
|
331
|
+
if (input.toolResultDigest !== undefined) {
|
|
332
|
+
cloned.toolResultDigest = {
|
|
333
|
+
text: input.toolResultDigest.text,
|
|
334
|
+
provenance: { ...input.toolResultDigest.provenance },
|
|
335
|
+
};
|
|
336
|
+
}
|
|
331
337
|
if (input.text !== undefined) cloned.text = input.text.slice(0, MIDDLEWARE_HOOK_TEXT_MAX_CHARS);
|
|
332
338
|
return cloned;
|
|
333
339
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SKILL_SUGGESTION_ANCHOR } from "../../core/skill-activation.js";
|
|
2
2
|
import type { MiddlewareHookRegistration } from "./runtime.js";
|
|
3
|
+
import { isSkillSuggestionWait, SKILL_SUGGESTION_WAIT_CONTINUATION_MESSAGE } from "./stalled-turn.js";
|
|
3
4
|
import { type MiddlewareEffect, metadataNumber } from "./types.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -32,12 +33,17 @@ export function skillsReminderMessage(installed: number, installable = 0): strin
|
|
|
32
33
|
installable > 0
|
|
33
34
|
? `${installed} installed, ${installable} installable from the marketplace`
|
|
34
35
|
: `${installed} installed`;
|
|
36
|
+
// Inline and non-blocking (issue #184): a 27B model read "and wait for the
|
|
37
|
+
// operator" as the whole job, spent a 40-second turn deliberating over the
|
|
38
|
+
// suggestion, and ended without one repository read. The suggestion is one
|
|
39
|
+
// line, the task continues in the same turn, and only the operator loads.
|
|
35
40
|
return (
|
|
36
41
|
`[Skills] ${counts}. Start this task by listing them with context(scope="skills") ` +
|
|
37
42
|
"and checking for a match; if one matches, open your reply with the line " +
|
|
38
43
|
`\`${SKILL_SUGGESTION_ANCHOR}\` (a comma-separated sequence, in order, when several compose) ` +
|
|
39
|
-
"and
|
|
40
|
-
"install when the operator runs it. If none match, do not mention skills
|
|
44
|
+
"and then continue the task in the same turn without the skill. Only the operator loads a skill, and a " +
|
|
45
|
+
"marketplace skill is offered for install when the operator runs it. If none match, do not mention skills " +
|
|
46
|
+
"and continue with the task."
|
|
41
47
|
);
|
|
42
48
|
}
|
|
43
49
|
|
|
@@ -135,9 +141,18 @@ export function createSkillsReminderRegistration(deps: SkillsReminderDeps): Midd
|
|
|
135
141
|
|
|
136
142
|
return {
|
|
137
143
|
id: SKILLS_REMINDER_REGISTRATION_ID,
|
|
138
|
-
description:
|
|
139
|
-
|
|
144
|
+
description:
|
|
145
|
+
"once per session, on the first substantive turn, teaches the skill-suggestion reply protocol; keeps a turn going that stopped on the suggestion",
|
|
146
|
+
hooks: ["turn_start", "turn_end"],
|
|
140
147
|
evaluate(input): ReadonlyArray<MiddlewareEffect> {
|
|
148
|
+
// The registration that teaches "suggest, then continue" owns the
|
|
149
|
+
// consequence when a model suggests and stops. The generic stalled-turn
|
|
150
|
+
// rule cannot see this case: the listing call counts as a tool call.
|
|
151
|
+
if (input.hook === "turn_end") {
|
|
152
|
+
return isSkillSuggestionWait(input)
|
|
153
|
+
? [{ kind: "request_continuation", message: SKILL_SUGGESTION_WAIT_CONTINUATION_MESSAGE }]
|
|
154
|
+
: NO_EFFECTS;
|
|
155
|
+
}
|
|
141
156
|
const sessionId = input.sessionId ?? null;
|
|
142
157
|
if (lastSeenSessionId === undefined) {
|
|
143
158
|
lastSeenSessionId = sessionId;
|
|
@@ -1,13 +1,127 @@
|
|
|
1
|
+
import { SKILL_SUGGESTION_PREFIX } from "../../core/skill-activation.js";
|
|
2
|
+
import { ToolNames } from "../../core/tool-names.js";
|
|
1
3
|
import type { MiddlewareRuleDefinition } from "./runtime.js";
|
|
2
|
-
import type
|
|
4
|
+
import { type MiddlewareHookInput, metadataNumber, metadataString } from "./types.js";
|
|
3
5
|
|
|
4
6
|
export const STALLED_TURN_REGISTRATION_ID = "nudge.stalled-turn";
|
|
5
7
|
|
|
6
8
|
export const STALLED_TURN_REQUEST_CONTINUATION_MESSAGE =
|
|
7
9
|
"You ended your turn after announcing an action without executing it: no tools were called. Continue now and perform the announced action, or state plainly that you are finished and waiting for the user.";
|
|
8
10
|
|
|
11
|
+
export const SKILL_SUGGESTION_WAIT_CONTINUATION_MESSAGE =
|
|
12
|
+
"You ended your turn on a skill suggestion without doing the task. The suggestion line has already made the offer and only the operator loads a skill, so nothing is needed from them before you proceed. Continue the task now without the skill.";
|
|
13
|
+
|
|
9
14
|
const INTENT_PATTERN = /\b(let me|i['’]ll|i will|i am going to|i['’]m going to|now i|next i|let's|let us)\b/i;
|
|
15
|
+
|
|
16
|
+
// Bare stems only. Inflected forms are derived below so that "running",
|
|
17
|
+
// "checking", and "verifies" count as the same announced action as "run".
|
|
18
|
+
const CONCRETE_ACTION_STEMS = [
|
|
19
|
+
"add",
|
|
20
|
+
"analyse",
|
|
21
|
+
"analyze",
|
|
22
|
+
"apply",
|
|
23
|
+
"audit",
|
|
24
|
+
"build",
|
|
25
|
+
"change",
|
|
26
|
+
"check",
|
|
27
|
+
"compare",
|
|
28
|
+
"compile",
|
|
29
|
+
"create",
|
|
30
|
+
"debug",
|
|
31
|
+
"delete",
|
|
32
|
+
"edit",
|
|
33
|
+
"execute",
|
|
34
|
+
"explore",
|
|
35
|
+
"find",
|
|
36
|
+
"fix",
|
|
37
|
+
"format",
|
|
38
|
+
"grep",
|
|
39
|
+
"implement",
|
|
40
|
+
"inspect",
|
|
41
|
+
"install",
|
|
42
|
+
"investigate",
|
|
43
|
+
"list",
|
|
44
|
+
"load",
|
|
45
|
+
"look",
|
|
46
|
+
"modify",
|
|
47
|
+
"open",
|
|
48
|
+
"patch",
|
|
49
|
+
"query",
|
|
50
|
+
"read",
|
|
51
|
+
"refactor",
|
|
52
|
+
"remove",
|
|
53
|
+
"rename",
|
|
54
|
+
"replace",
|
|
55
|
+
"review",
|
|
56
|
+
"run",
|
|
57
|
+
"scan",
|
|
58
|
+
"search",
|
|
59
|
+
"test",
|
|
60
|
+
"trace",
|
|
61
|
+
"update",
|
|
62
|
+
"validate",
|
|
63
|
+
"verify",
|
|
64
|
+
"write",
|
|
65
|
+
] as const;
|
|
66
|
+
|
|
67
|
+
// Stems whose final consonant doubles before a vowel suffix.
|
|
68
|
+
const DOUBLED_CONSONANT_STEMS = new Set(["debug", "format", "run", "scan"]);
|
|
69
|
+
|
|
70
|
+
function concreteActionForms(stem: string): string[] {
|
|
71
|
+
const forms = new Set([stem, `${stem}s`, `${stem}ed`, `${stem}ing`]);
|
|
72
|
+
if (/(?:ch|sh|s|x|z)$/.test(stem)) forms.add(`${stem}es`);
|
|
73
|
+
if (stem.endsWith("e")) {
|
|
74
|
+
forms.add(`${stem}d`);
|
|
75
|
+
forms.add(`${stem.slice(0, -1)}ing`);
|
|
76
|
+
}
|
|
77
|
+
if (stem.endsWith("y")) {
|
|
78
|
+
forms.add(`${stem.slice(0, -1)}ies`);
|
|
79
|
+
forms.add(`${stem.slice(0, -1)}ied`);
|
|
80
|
+
}
|
|
81
|
+
if (DOUBLED_CONSONANT_STEMS.has(stem)) {
|
|
82
|
+
const doubled = `${stem}${stem.slice(-1)}`;
|
|
83
|
+
forms.add(`${doubled}ed`);
|
|
84
|
+
forms.add(`${doubled}ing`);
|
|
85
|
+
}
|
|
86
|
+
return [...forms];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const CONCRETE_ACTION_PATTERN = new RegExp(
|
|
90
|
+
`\\b(?:${CONCRETE_ACTION_STEMS.flatMap(concreteActionForms).join("|")})\\b`,
|
|
91
|
+
"giu",
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Extensions a model actually names when announcing work on a file. Requiring
|
|
95
|
+
// one (or an explicit ./, ../, ~/ prefix) keeps "and/or" and "I/O" from
|
|
96
|
+
// reading as paths.
|
|
97
|
+
const NAMED_PATH_EXTENSIONS =
|
|
98
|
+
"c|cc|cfg|cjs|cmake|conf|cpp|cs|css|csv|env|go|h|hpp|html|ini|java|jl|js|json|jsx|lock|log|lua|md|mjs|nc|php|py|rb|rs|rst|sh|sql|toml|ts|tsx|txt|yaml|yml";
|
|
99
|
+
const NAMED_PATH_PATTERN = new RegExp(
|
|
100
|
+
`(?:^|[\\s\`"'(\\[])(?:[\\w@.\\-/]*[\\w@\\-]\\.(?:${NAMED_PATH_EXTENSIONS})\\b|(?:\\.{1,2}|~)\\/[\\w@.\\-/]+)`,
|
|
101
|
+
"iu",
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// A named executable followed by at least one argument token. Words that are
|
|
105
|
+
// ordinary English on their own ("go", "make", "test") are deliberately absent.
|
|
106
|
+
const NAMED_COMMAND_PATTERN =
|
|
107
|
+
/\b(?:npm|npx|pnpm|yarn|bun|git|cargo|rustc|cmake|ctest|ninja|meson|bazel|gradle|mvn|pytest|pip|python3?|node|deno|tsc|biome|eslint|prettier|jest|vitest|ruff|mypy|docker|kubectl|mpirun|srun|sbatch|gcc|clang)\s+[-\w./]/iu;
|
|
108
|
+
|
|
109
|
+
const ACTION_OBJECT_CONNECTOR_PATTERN = /^(?:at|for|in|into|on|through|to)\s+/i;
|
|
110
|
+
const ACTION_OBJECT_DETERMINER_PATTERN = /^(?:a|an|my|our|that|the|their|these|this|those|your)\s+/i;
|
|
111
|
+
const ACTION_OBJECT_PATTERN = /^(?!(?:again|everyone|later|next|now|soon|then|you)\b)[`"'./~]*[\p{L}\p{N}_-]/iu;
|
|
112
|
+
const CONDITIONAL_LEAD_IN_PATTERN = /\b(?:if|once|unless|until|when|whenever)\b/i;
|
|
113
|
+
const CONDITIONAL_INVITATION_PATTERN = /\b(?:ask|give|point|say|send|share|tell)\b[\s\S]*\b(?:and|then)\s*$/i;
|
|
114
|
+
// "if needed", and any conditional whose condition is about the user: "if you
|
|
115
|
+
// want", "if that is what you need". Conditions about the work ("if the config
|
|
116
|
+
// is valid") stay announcements.
|
|
117
|
+
const CONDITIONAL_OFFER_SUFFIX_PATTERN =
|
|
118
|
+
/\b(?:if\s+(?:needed|necessary|requested)\b|if\b[^.!?]*\byou\b|when(?:ever)?\s+you\b)/i;
|
|
10
119
|
const LET_ME_KNOW_PATTERN = /\blet me know\b/i;
|
|
120
|
+
// "I'll wait for your go-ahead before I touch src/cli/index.ts" names a path
|
|
121
|
+
// and promises nothing now. A clause that opens by deferring is a wait
|
|
122
|
+
// statement whatever it goes on to name, so it is read before any verb or
|
|
123
|
+
// path inside it can count as an announcement.
|
|
124
|
+
const DEFERRAL_LEAD_PATTERN = /^\s*(?:just\s+|simply\s+|now\s+)?(?:wait|hold\s+off|hold|stand\s+by|pause|defer)\b/i;
|
|
11
125
|
const COMPLETION_PATTERN =
|
|
12
126
|
/^\s*(?:all\s+done|done|complete|completed|all\s+set)\b|^\s*(?:here(?:'s| is)\s+)?(?:a\s+)?summary\b|^\s*summary\s*:?$/i;
|
|
13
127
|
|
|
@@ -24,6 +138,43 @@ export const STALLED_TURN_RULE_DEFINITION: MiddlewareRuleDefinition = {
|
|
|
24
138
|
predicate: shouldRequestStalledTurnContinuation,
|
|
25
139
|
};
|
|
26
140
|
|
|
141
|
+
// "run /skill X or say skip", "Suggested skill: /skill x", "want me to use
|
|
142
|
+
// the skill?": the sentence hands the operator a decision the protocol does
|
|
143
|
+
// not require of them.
|
|
144
|
+
const SKILL_WAIT_PATTERN = /\/skill\b|\bskills?\b|\bskip\b/i;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* A turn that made the skill suggestion and then stopped, with only the
|
|
148
|
+
* listing call behind it (issue #184). The generic stalled-turn predicate
|
|
149
|
+
* cannot see this: `context(scope="skills")` is a tool call, so the turn
|
|
150
|
+
* looks busy, and "say skip and I'll explore the tree" reads as a
|
|
151
|
+
* conditional offer rather than an announcement. The suggestion is supposed
|
|
152
|
+
* to be one inline line before the work, so a reply that is the suggestion
|
|
153
|
+
* plus a wait has not done the task.
|
|
154
|
+
*/
|
|
155
|
+
export function isSkillSuggestionWait(input: MiddlewareHookInput): boolean {
|
|
156
|
+
if (input.hook !== "turn_end") return false;
|
|
157
|
+
if (!isNormalStopReason(input.metadata?.stopReason)) return false;
|
|
158
|
+
const text = input.text ?? "";
|
|
159
|
+
if (!text.includes(SKILL_SUGGESTION_PREFIX)) return false;
|
|
160
|
+
const toolNames = metadataString(input, "turnToolNames");
|
|
161
|
+
if (toolNames === null) {
|
|
162
|
+
if ((metadataNumber(input, "turnToolCalls") ?? 0) !== 0) return false;
|
|
163
|
+
} else if (
|
|
164
|
+
toolNames
|
|
165
|
+
.split(",")
|
|
166
|
+
.filter((name) => name.length > 0)
|
|
167
|
+
.some((name) => name !== ToolNames.Context)
|
|
168
|
+
) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
const lastLine = lastNonEmptyLine(text);
|
|
172
|
+
if (lastLine === null) return false;
|
|
173
|
+
const finalSentence = lastSentence(lastLine);
|
|
174
|
+
if (finalSentence.length === 0) return false;
|
|
175
|
+
return SKILL_WAIT_PATTERN.test(finalSentence) || isQuestion(finalSentence) || LET_ME_KNOW_PATTERN.test(finalSentence);
|
|
176
|
+
}
|
|
177
|
+
|
|
27
178
|
export function shouldRequestStalledTurnContinuation(input: MiddlewareHookInput): boolean {
|
|
28
179
|
if (input.hook !== "turn_end") return false;
|
|
29
180
|
if (input.metadata?.turnToolCalls !== 0) return false;
|
|
@@ -38,7 +189,47 @@ export function shouldRequestStalledTurnContinuation(input: MiddlewareHookInput)
|
|
|
38
189
|
if (LET_ME_KNOW_PATTERN.test(finalSentence)) return false;
|
|
39
190
|
if (COMPLETION_PATTERN.test(lastLine) || COMPLETION_PATTERN.test(finalSentence)) return false;
|
|
40
191
|
|
|
41
|
-
return
|
|
192
|
+
return announcesConcreteAction(finalSentence);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function announcesConcreteAction(sentence: string): boolean {
|
|
196
|
+
const intent = INTENT_PATTERN.exec(sentence);
|
|
197
|
+
if (intent) {
|
|
198
|
+
const leadIn = sentence.slice(0, intent.index);
|
|
199
|
+
if (
|
|
200
|
+
CONDITIONAL_LEAD_IN_PATTERN.test(leadIn) ||
|
|
201
|
+
CONDITIONAL_INVITATION_PATTERN.test(leadIn) ||
|
|
202
|
+
CONDITIONAL_OFFER_SUFFIX_PATTERN.test(sentence)
|
|
203
|
+
) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const clause = sentence.slice(intent.index + intent[0].length);
|
|
207
|
+
if (DEFERRAL_LEAD_PATTERN.test(clause)) return false;
|
|
208
|
+
if (hasActionWithObject(clause)) return true;
|
|
209
|
+
// A path or command is evidence of an immediate action only when the
|
|
210
|
+
// announcement itself is unconditional; "once package.json is updated"
|
|
211
|
+
// names a file but promises nothing now.
|
|
212
|
+
if (CONDITIONAL_LEAD_IN_PATTERN.test(clause)) return false;
|
|
213
|
+
return namesToolTarget(clause);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// A trailing colon can still signal a cut-off action lead-in, but only when
|
|
217
|
+
// the lead-in names work and its target instead of relying on punctuation.
|
|
218
|
+
return sentence.endsWith(":") && hasActionWithObject(sentence);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function namesToolTarget(clause: string): boolean {
|
|
222
|
+
return NAMED_PATH_PATTERN.test(clause) || NAMED_COMMAND_PATTERN.test(clause);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function hasActionWithObject(clause: string): boolean {
|
|
226
|
+
for (const action of clause.matchAll(CONCRETE_ACTION_PATTERN)) {
|
|
227
|
+
const remainder = clause.slice((action.index ?? 0) + action[0].length).trimStart();
|
|
228
|
+
const withoutConnector = remainder.replace(ACTION_OBJECT_CONNECTOR_PATTERN, "");
|
|
229
|
+
const object = withoutConnector.replace(ACTION_OBJECT_DETERMINER_PATTERN, "");
|
|
230
|
+
if (ACTION_OBJECT_PATTERN.test(object)) return true;
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
42
233
|
}
|
|
43
234
|
|
|
44
235
|
function isNormalStopReason(value: unknown): boolean {
|
|
@@ -60,10 +251,22 @@ function lastNonEmptyLine(text: string): string | null {
|
|
|
60
251
|
return null;
|
|
61
252
|
}
|
|
62
253
|
|
|
254
|
+
// A period only terminates a sentence when it ends a token: "bash.ts" and
|
|
255
|
+
// "package.json" must survive extraction intact.
|
|
256
|
+
const SENTENCE_TERMINATOR_PATTERN = /[.!?]+["'’)\]]*(?=\s|$)/gu;
|
|
257
|
+
|
|
63
258
|
function lastSentence(line: string): string {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
259
|
+
const trimmed = line.trimEnd();
|
|
260
|
+
let start = 0;
|
|
261
|
+
for (const terminator of trimmed.matchAll(SENTENCE_TERMINATOR_PATTERN)) {
|
|
262
|
+
const end = (terminator.index ?? 0) + terminator[0].length;
|
|
263
|
+
if (end >= trimmed.length) break;
|
|
264
|
+
start = end;
|
|
265
|
+
}
|
|
266
|
+
return trimmed
|
|
267
|
+
.slice(start)
|
|
268
|
+
.replace(/^[\s"'([{-]+/, "")
|
|
269
|
+
.trim();
|
|
67
270
|
}
|
|
68
271
|
|
|
69
272
|
function isQuestion(sentence: string): boolean {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ToolResultDigest } from "../../tools/result-disposition.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* The five lifecycle events of the unified middleware layer (w3 cutover).
|
|
3
5
|
* Every event has a live producer: the registry fires before_tool/after_tool
|
|
@@ -86,6 +88,8 @@ export interface MiddlewareHookInput {
|
|
|
86
88
|
toolArgs?: Readonly<Record<string, unknown>>;
|
|
87
89
|
/** Structured result details, supplied by the registry on after_tool. Read-only by contract. */
|
|
88
90
|
toolResultDetails?: Readonly<Record<string, unknown>>;
|
|
91
|
+
/** Bounded deterministic diagnostic supplied by the result-disposition layer on after_tool. */
|
|
92
|
+
toolResultDigest?: Readonly<ToolResultDigest>;
|
|
89
93
|
/**
|
|
90
94
|
* Free text attached to the hook occurrence; on turn_end this is the final
|
|
91
95
|
* assistant message text. The one typed exception to the scalar `metadata`
|
|
@@ -114,6 +118,12 @@ export function metadataNumber(input: MiddlewareHookInput, key: string): number
|
|
|
114
118
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
115
119
|
}
|
|
116
120
|
|
|
121
|
+
/** The string under `key`, or null when the caller did not report one. */
|
|
122
|
+
export function metadataString(input: MiddlewareHookInput, key: string): string | null {
|
|
123
|
+
const value = input.metadata?.[key];
|
|
124
|
+
return typeof value === "string" ? value : null;
|
|
125
|
+
}
|
|
126
|
+
|
|
117
127
|
export function isMiddlewareHook(value: string): value is MiddlewareHook {
|
|
118
128
|
return (MIDDLEWARE_HOOKS as ReadonlyArray<string>).includes(value);
|
|
119
129
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ResponseModelIdObservationCounts } from "../../core/response-model-id.js";
|
|
1
2
|
import type { TargetStatus } from "../providers/contract.js";
|
|
2
3
|
import type { CostProvenance } from "../providers/index.js";
|
|
3
4
|
import type { AccountabilitySummary } from "./accountability.js";
|
|
@@ -132,11 +133,15 @@ export interface ObservabilityContract {
|
|
|
132
133
|
*/
|
|
133
134
|
recordTokens(
|
|
134
135
|
providerId: string,
|
|
135
|
-
|
|
136
|
+
attributedModelId: string,
|
|
136
137
|
tokens: number,
|
|
137
138
|
costUsd?: number,
|
|
138
139
|
breakdown?: Partial<UsageBreakdown>,
|
|
139
140
|
costProvenance?: CostProvenance,
|
|
141
|
+
modelIdFacts?: {
|
|
142
|
+
requestedModelIds: ReadonlyArray<string>;
|
|
143
|
+
responseModelIdObservationCounts: Readonly<ResponseModelIdObservationCounts>;
|
|
144
|
+
},
|
|
140
145
|
): void;
|
|
141
146
|
/** Record final output token throughput for one completed assistant stream. */
|
|
142
147
|
recordTokenThroughput(snapshot: TokenThroughputSnapshot): void;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* footer consumes the session sum through `ObservabilityContract.sessionTokens()`.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import type { ResponseModelIdObservationCounts } from "../../core/response-model-id.js";
|
|
14
15
|
import { type CostProvenance, normalizeCostProvenance } from "../providers/index.js";
|
|
15
16
|
|
|
16
17
|
export interface CostAmount {
|
|
@@ -112,7 +113,9 @@ export interface UsageBreakdown {
|
|
|
112
113
|
|
|
113
114
|
export interface CostEntry {
|
|
114
115
|
providerId: string;
|
|
115
|
-
|
|
116
|
+
attributedModelId: string;
|
|
117
|
+
requestedModelIds: string[];
|
|
118
|
+
responseModelIdObservationCounts: ResponseModelIdObservationCounts;
|
|
116
119
|
tokens: number;
|
|
117
120
|
usd: number;
|
|
118
121
|
provenance: CostProvenance;
|
|
@@ -127,11 +130,15 @@ export interface CostEntry {
|
|
|
127
130
|
export interface CostTracker {
|
|
128
131
|
accumulate(
|
|
129
132
|
providerId: string,
|
|
130
|
-
|
|
133
|
+
attributedModelId: string,
|
|
131
134
|
tokens: number,
|
|
132
135
|
usd?: number,
|
|
133
136
|
breakdown?: Partial<UsageBreakdown>,
|
|
134
137
|
provenance?: CostProvenance,
|
|
138
|
+
modelIdFacts?: {
|
|
139
|
+
requestedModelIds: ReadonlyArray<string>;
|
|
140
|
+
responseModelIdObservationCounts: Readonly<ResponseModelIdObservationCounts>;
|
|
141
|
+
},
|
|
135
142
|
): number;
|
|
136
143
|
sessionTotal(): number;
|
|
137
144
|
sessionCost(): CostAggregate;
|
|
@@ -149,7 +156,7 @@ export function createCostTracker(): CostTracker {
|
|
|
149
156
|
let total = 0;
|
|
150
157
|
const totals = emptyBreakdown();
|
|
151
158
|
return {
|
|
152
|
-
accumulate(providerId,
|
|
159
|
+
accumulate(providerId, attributedModelId, tokens, usd, breakdown, costProvenance, modelIdFacts) {
|
|
153
160
|
const resolvedUsd = usd ?? 0;
|
|
154
161
|
const provenance = normalizeCostProvenance(costProvenance);
|
|
155
162
|
const input = breakdown?.input ?? 0;
|
|
@@ -160,7 +167,16 @@ export function createCostTracker(): CostTracker {
|
|
|
160
167
|
const apiCalls = breakdown?.apiCalls;
|
|
161
168
|
log.push({
|
|
162
169
|
providerId,
|
|
163
|
-
|
|
170
|
+
attributedModelId,
|
|
171
|
+
requestedModelIds: [...(modelIdFacts?.requestedModelIds ?? [attributedModelId])],
|
|
172
|
+
responseModelIdObservationCounts: modelIdFacts
|
|
173
|
+
? { ...modelIdFacts.responseModelIdObservationCounts }
|
|
174
|
+
: {
|
|
175
|
+
reportedCalls: 0,
|
|
176
|
+
notReportedCalls: 0,
|
|
177
|
+
notObservedCalls: apiCalls ?? 1,
|
|
178
|
+
legacyDifferenceOnlyCalls: 0,
|
|
179
|
+
},
|
|
164
180
|
tokens,
|
|
165
181
|
usd: resolvedUsd,
|
|
166
182
|
provenance,
|
|
@@ -259,9 +259,9 @@ export function createObservabilityBundle(
|
|
|
259
259
|
latestThroughput = null;
|
|
260
260
|
projection.refresh();
|
|
261
261
|
},
|
|
262
|
-
recordTokens(providerId,
|
|
262
|
+
recordTokens(providerId, attributedModelId, tokens, costUsd, breakdown, costProvenance, modelIdFacts) {
|
|
263
263
|
telemetry.record("counter", "tokens.total", tokens);
|
|
264
|
-
cost.accumulate(providerId,
|
|
264
|
+
cost.accumulate(providerId, attributedModelId, tokens, costUsd, breakdown, costProvenance, modelIdFacts);
|
|
265
265
|
projection.refresh();
|
|
266
266
|
},
|
|
267
267
|
recordSessionTurn(sessionTurn) {
|
|
@@ -32,6 +32,7 @@ export type { ModelCapabilityPatchTarget } from "./model-capabilities.js";
|
|
|
32
32
|
export { applyModelCapabilityPatch, resolveModelCapabilities } from "./model-capabilities.js";
|
|
33
33
|
export {
|
|
34
34
|
canonicalizeWireModelId,
|
|
35
|
+
contextSlotsForModel,
|
|
35
36
|
hasLiveModelCatalog,
|
|
36
37
|
loadedContextWindowForModel,
|
|
37
38
|
type ModelResidency,
|
|
@@ -96,6 +97,7 @@ export {
|
|
|
96
97
|
refineRuntimeTargetWithModelHints,
|
|
97
98
|
resolveRuntimeTarget,
|
|
98
99
|
runtimeResolutionWarnings,
|
|
100
|
+
runtimeResolutionWarningsBesideThinkingNotice,
|
|
99
101
|
runtimeTargetSnapshot,
|
|
100
102
|
} from "./runtime-resolution.js";
|
|
101
103
|
export {
|
|
@@ -126,6 +128,7 @@ export {
|
|
|
126
128
|
EMPTY_CAPABILITIES,
|
|
127
129
|
VALID_THINKING_LEVELS,
|
|
128
130
|
} from "./types/capability-flags.js";
|
|
131
|
+
export { type ContextWindowSlots, formatContextWindowSlots } from "./types/context-window-slots.js";
|
|
129
132
|
export { type CostProvenance, normalizeCostProvenance } from "./types/cost-provenance.js";
|
|
130
133
|
export type { KnowledgeBase, KnowledgeBaseEntry, KnowledgeBaseHit } from "./types/knowledge-base.js";
|
|
131
134
|
export type {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TargetStatus } from "./contract.js";
|
|
2
2
|
import { listKnownModelsForRuntime } from "./support.js";
|
|
3
|
+
import type { ContextWindowSlots } from "./types/context-window-slots.js";
|
|
3
4
|
|
|
4
5
|
export type ProviderModelSource = "configured" | "live" | "catalog" | "default";
|
|
5
6
|
|
|
@@ -45,6 +46,14 @@ export function loadedContextWindowForModel(
|
|
|
45
46
|
return typeof reported === "number" && Number.isFinite(reported) && reported > 0 ? reported : null;
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/** How the server splits its KV budget for this model, when the probe saw it split. */
|
|
50
|
+
export function contextSlotsForModel(
|
|
51
|
+
status: DiscoveryStatus | null | undefined,
|
|
52
|
+
modelId: string,
|
|
53
|
+
): ContextWindowSlots | null {
|
|
54
|
+
return status?.discoveredModelStates?.[modelId]?.contextSlots ?? null;
|
|
55
|
+
}
|
|
56
|
+
|
|
48
57
|
/**
|
|
49
58
|
* Residency as one view, so the planner and the "not resident" notice cannot
|
|
50
59
|
* disagree about the same model. A reported loaded window settles it whatever
|