@iowarp/clio-coder 0.3.4 → 0.3.7
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 +65 -2
- package/CONTRIBUTING.md +6 -6
- package/README.md +16 -5
- package/dist/{acp-S5R4RR5B.js → acp-SK4MD6MM.js} +11 -11
- package/dist/{agents-P6DMMVZY.js → agents-2FN2K6ME.js} +33 -26
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-2XCZLPKS.js → auth-QIYZWM5I.js} +15 -15
- package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
- package/dist/{chunk-VAWWTKDP.js → chunk-3HAPLH5M.js} +11 -11
- package/dist/{chunk-YCWGATWI.js → chunk-465YSENW.js} +3 -3
- package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
- package/dist/{chunk-UZHIZC5S.js → chunk-4DWFMQDR.js} +61 -76
- package/dist/{chunk-ZWMF7253.js → chunk-5C3AQNDW.js} +328 -9
- package/dist/{chunk-22NAGB7X.js → chunk-5C77SEEY.js} +5 -94
- package/dist/{chunk-WPQLXFOZ.js → chunk-5FR74PWO.js} +3 -2
- package/dist/{chunk-35MKKU5R.js → chunk-5UJ6ECTS.js} +18 -10
- package/dist/{chunk-BRXQQJFP.js → chunk-6M7VS3J3.js} +571 -50
- package/dist/{chunk-QQK64KLB.js → chunk-6TUKSZVF.js} +141 -23
- package/dist/{chunk-N4CZJQRK.js → chunk-AB4XIIVB.js} +8 -6
- package/dist/{chunk-KRPY7NTG.js → chunk-BMWK7ZIZ.js} +14 -20
- package/dist/{chunk-4BPJXDWC.js → chunk-C4JBQ5SR.js} +30 -14
- package/dist/{chunk-ZYKPLLNQ.js → chunk-CEYBNUGC.js} +821 -83
- package/dist/{chunk-VEZEGCGW.js → chunk-D4MDIG46.js} +20 -18
- package/dist/chunk-DJNLUABN.js +843 -0
- package/dist/{chunk-BP4OYD6A.js → chunk-DMD2AGVS.js} +21 -2
- package/dist/{chunk-KOHPCX4K.js → chunk-DOOEX22V.js} +2 -2
- package/dist/chunk-DQA7QLMD.js +123 -0
- package/dist/chunk-DR52UMZW.js +21 -0
- package/dist/{chunk-3HZ5RWN2.js → chunk-EBEFWSGL.js} +9 -7
- package/dist/{chunk-EDRHSCIE.js → chunk-EELBMBT6.js} +128 -13
- package/dist/{chunk-HV5X7OR2.js → chunk-EOOQZZDE.js} +16 -14
- package/dist/{chunk-WR67VIZY.js → chunk-FOT2FX5J.js} +63 -5
- package/dist/{chunk-BPGS2WCQ.js → chunk-GEYXPTRF.js} +2 -1
- package/dist/{chunk-FYYLNIL5.js → chunk-GH5622CP.js} +2 -2
- package/dist/{chunk-BEY543CS.js → chunk-GOXNB3AO.js} +5 -2
- package/dist/chunk-GWS3VEIW.js +195 -0
- package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
- package/dist/chunk-HWUFFB6L.js +83 -0
- package/dist/{chunk-X6COSD2O.js → chunk-J7PIKKWC.js} +8 -436
- package/dist/{chunk-NILBFAPG.js → chunk-JNXPYBB4.js} +2 -2
- package/dist/{chunk-4VP4KH3K.js → chunk-JRIO5UD2.js} +4 -4
- package/dist/{chunk-K6WL7QZT.js → chunk-JTSEDYVQ.js} +7 -7
- package/dist/{chunk-QKMUKYO7.js → chunk-KCMKRQX4.js} +236 -84
- package/dist/chunk-KZ2H5X4G.js +1026 -0
- package/dist/{chunk-A2GZF7DC.js → chunk-LADCF22A.js} +13 -13
- package/dist/chunk-LCGCVYZ4.js +57 -0
- package/dist/chunk-M4AKACEO.js +382 -0
- package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
- package/dist/{chunk-4JUF2NNX.js → chunk-MXI6J5JF.js} +7 -7
- package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
- package/dist/{chunk-TTNYS3EA.js → chunk-OB5HIGJY.js} +1 -1
- package/dist/{chunk-7RXG6QRZ.js → chunk-OBMAI2DP.js} +61 -840
- package/dist/{chunk-5M54SPOL.js → chunk-ODFEOB4F.js} +161 -5
- package/dist/chunk-PD3MESLB.js +242 -0
- package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
- package/dist/{chunk-VMNQ6OZA.js → chunk-QCTRSGHQ.js} +963 -786
- package/dist/chunk-RVG5JXAL.js +41 -0
- package/dist/{chunk-RD5U66HV.js → chunk-SROCI7ZU.js} +7 -7
- package/dist/{chunk-MFFY33HR.js → chunk-THKY7CD7.js} +466 -205
- package/dist/{chunk-34475P3I.js → chunk-TSHXZTOQ.js} +5 -4
- package/dist/{chunk-PCZJO5TI.js → chunk-UFQ3F4FW.js} +13 -178
- package/dist/{chunk-AD2SYQYC.js → chunk-UHXRNZ2J.js} +121 -3
- package/dist/chunk-UND3GU2L.js +103 -0
- package/dist/{chunk-QQL5RT5M.js → chunk-UUANF5CR.js} +2323 -2114
- package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
- package/dist/chunk-UVDSQ6LW.js +472 -0
- package/dist/{chunk-QWU7ZBO7.js → chunk-VQNODYQ4.js} +215 -56
- package/dist/chunk-VREKEFLL.js +37 -0
- package/dist/{chunk-2TZWSW76.js → chunk-WHGPSPT5.js} +2 -2
- package/dist/{chunk-TW3WDMVS.js → chunk-WHJYKASB.js} +2 -2
- package/dist/{chunk-MEQ45TQ4.js → chunk-WJHBC77E.js} +21 -7
- package/dist/{chunk-HXG4IURW.js → chunk-X2KV5FXT.js} +2 -2
- package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
- package/dist/{chunk-2LZI5CAG.js → chunk-XEGB6BCN.js} +228 -36
- package/dist/{chunk-E25LMLRW.js → chunk-YD734TPH.js} +2 -2
- package/dist/{verifiers-4UUM6TEE.js → chunk-YTYFXUI3.js} +121 -372
- package/dist/{chunk-3JLKSKD7.js → chunk-ZGH7FGS5.js} +17 -7
- package/dist/{chunk-VSNATDE6.js → chunk-ZZMN5OM4.js} +2 -2
- package/dist/cli/index.js +34 -32
- package/dist/{clio-J5JIOIDS.js → clio-WBVQEBKO.js} +7 -7
- package/dist/{code-nav-AXCXSBHX.js → code-nav-FGGFIE7L.js} +7 -7
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-KELWS457.js → components-F7OEATSO.js} +5 -5
- package/dist/{config-OEBMIN2U.js → config-TRBL3RCF.js} +48 -41
- package/dist/{configure-PUQOSIXQ.js → configure-OLCVPHNM.js} +17 -17
- package/dist/{context-URSXPBCK.js → context-MJIJ6GOX.js} +12 -12
- package/dist/{context-EKDCKUUZ.js → context-WFPKQSM6.js} +26 -9
- package/dist/{context-MGSE4Z2T.js → context-XEWE3MOJ.js} +44 -37
- package/dist/{context-clear-KDAJRNUK.js → context-clear-KNOS2JPB.js} +44 -37
- package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
- package/dist/{context-working-set-SBKMPPI2.js → context-working-set-EUXAZI6N.js} +14 -13
- package/dist/{dispatch-runner-MSWN72NK.js → dispatch-runner-B7MTOVKL.js} +321 -60
- package/dist/{docs-2C2LTVT2.js → docs-FLJTIDSE.js} +5 -5
- package/dist/{doctor-7BSE27PJ.js → doctor-RN4YKO2X.js} +15 -15
- package/dist/{eval-IZGDOO4H.js → eval-RUBJVSNQ.js} +52 -236
- package/dist/{evidence-SR7WXB5B.js → evidence-JZNBUOQZ.js} +39 -33
- package/dist/{evolve-K7VE2CBX.js → evolve-FJVC4KKI.js} +39 -33
- package/dist/{extensions-QVDOHDGJ.js → extensions-IQL36S7K.js} +5 -5
- package/dist/{fleet-7XMJNQNF.js → fleet-BDKYJFCP.js} +243 -370
- package/dist/fleet-commands-ZFIWZSB3.js +70 -0
- package/dist/fleet-graph-Y6HPXIVF.js +125 -0
- package/dist/fleet-new-RDVJLHHH.js +48 -0
- package/dist/{fleet-preflight-AQNAH644.js → fleet-preflight-BHSNPBMH.js} +2 -2
- package/dist/fleet-validate-BIYREGIK.js +79 -0
- package/dist/{init-JGNPAYXT.js → init-LQUB5COQ.js} +57 -48
- package/dist/library-NJAHIGG4.js +217 -0
- package/dist/memory-OG6HOYKM.js +472 -0
- package/dist/{models-ZMMLFJNN.js → models-5ZG5XY7J.js} +23 -22
- package/dist/{monitor-2F3T5KHP.js → monitor-TJ7AMTGB.js} +69 -35
- package/dist/{orchestrator-ORHT43JB.js → orchestrator-WZYB54DM.js} +4868 -1189
- package/dist/{paths-UXLN5YYZ.js → paths-XUC7GS6E.js} +5 -5
- package/dist/{reset-NXGTYNUO.js → reset-PXQT45IY.js} +8 -8
- package/dist/{run-RF4WJGMT.js → run-FQ74YF62.js} +82 -62
- package/dist/{share-UT3W6E4M.js → share-FW7SVCL3.js} +34 -10
- package/dist/{skills-PSACKC5Q.js → skills-7E7IRB3R.js} +25 -9
- package/dist/{skills-eval-WJSI55RZ.js → skills-eval-LI75W6OK.js} +43 -35
- package/dist/{targets-PIIRAOYS.js → targets-4CIFKCTW.js} +27 -24
- package/dist/{terminal-lease-ULWXWNVY.js → terminal-lease-WUZY7ZV5.js} +5 -4
- package/dist/{uninstall-FZCQCDKC.js → uninstall-7FV7IP4E.js} +5 -5
- package/dist/{upgrade-346TZ6AV.js → upgrade-K2HVIVMQ.js} +21 -20
- package/dist/{usage-6KKXR32N.js → usage-GTZELZQX.js} +159 -59
- package/dist/verifiers-RLAHT27O.js +336 -0
- package/dist/{verify-X5HDROLA.js → verify-BX3BRKH5.js} +7 -6
- package/dist/{wiki-generate-7STOCIFZ.js → wiki-generate-ASIFASCN.js} +58 -48
- package/dist/worker/entry.js +98 -84
- package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
- package/docs/README.md +4 -3
- 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 +10 -6
- package/docs/built-in-agents.md +26 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +90 -8
- package/docs/configuration-and-targets.md +90 -2
- package/docs/context-engine.md +4 -2
- package/docs/context-working-set.md +4 -4
- package/docs/development-pipeline.md +1 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +4 -4
- package/docs/documentation-guide.md +4 -4
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +4 -45
- package/docs/evidence-and-memory.md +70 -10
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +4 -1
- package/docs/extensions-and-sharing.md +6 -2
- package/docs/fleet-demo-runbook.md +2 -2
- package/docs/fleet-dispatch.md +224 -11
- package/docs/git-commit-provenance.md +2 -2
- package/docs/glossary.md +1 -1
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +20 -2
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +55 -8
- package/docs/proactive-memory.md +26 -16
- package/docs/prompt-envelope-and-tools.md +4 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +83 -65
- package/docs/resource-library.md +59 -0
- package/docs/safety-model.md +29 -7
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +37 -1
- package/docs/skills-marketplace.md +16 -3
- package/docs/tool-usage.md +14 -7
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +38 -4
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +7 -4
- package/src/cli/agents.ts +2 -3
- package/src/cli/argv.ts +14 -1
- package/src/cli/fleet-commands.ts +37 -0
- package/src/cli/fleet-graph.ts +102 -0
- package/src/cli/fleet-new.ts +36 -0
- package/src/cli/fleet-preflight.ts +121 -0
- package/src/cli/fleet-validate.ts +30 -0
- package/src/cli/fleet.ts +188 -335
- package/src/cli/index.ts +4 -2
- package/src/cli/library.ts +190 -0
- 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/share.ts +13 -1
- package/src/cli/targets.ts +12 -3
- package/src/cli/usage.ts +160 -20
- package/src/core/bus-events.ts +7 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +130 -0
- package/src/core/defaults.ts +81 -0
- package/src/core/response-model-id.ts +134 -0
- package/src/core/toml.ts +62 -0
- package/src/core/workspace-files.ts +0 -1
- package/src/domains/agents/builtins/architect.md +2 -1
- package/src/domains/agents/builtins/oracle.md +33 -0
- package/src/domains/agents/catalog.ts +18 -5
- package/src/domains/agents/fleet-contract.ts +278 -16
- package/src/domains/agents/index.ts +14 -0
- package/src/domains/agents/recipe.ts +54 -14
- package/src/domains/agents/result-contract.ts +242 -5
- package/src/domains/config/classify.ts +4 -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/policies/index.ts +3 -4
- package/src/domains/dispatch/active-route-planner.ts +14 -0
- package/src/domains/dispatch/backoff.ts +2 -1
- package/src/domains/dispatch/budget-envelope.ts +396 -0
- package/src/domains/dispatch/capability-match.ts +1 -0
- package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
- package/src/domains/dispatch/contract.ts +36 -0
- package/src/domains/dispatch/delegation-plan.ts +167 -0
- package/src/domains/dispatch/execution-plan.ts +76 -5
- package/src/domains/dispatch/execution-role.ts +3 -1
- package/src/domains/dispatch/execution-scheduler.ts +183 -67
- package/src/domains/dispatch/extension.ts +339 -36
- package/src/domains/dispatch/fleet-gate.ts +14 -0
- package/src/domains/dispatch/fleet-plan.ts +63 -3
- package/src/domains/dispatch/fleet-run.ts +737 -0
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/host-verification.ts +178 -0
- package/src/domains/dispatch/index.ts +38 -0
- package/src/domains/dispatch/intent.ts +159 -0
- package/src/domains/dispatch/orphan-recovery.ts +1 -0
- package/src/domains/dispatch/receipt-integrity.ts +12 -4
- package/src/domains/dispatch/state.ts +37 -3
- package/src/domains/dispatch/types.ts +61 -9
- package/src/domains/dispatch/validation.ts +80 -6
- 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/trust-status.ts +10 -1
- 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/index.ts +15 -0
- package/src/domains/middleware/memory-intervention.ts +35 -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 +43 -1
- package/src/domains/middleware/types.ts +10 -0
- package/src/domains/middleware/watchdog.ts +281 -0
- package/src/domains/observability/contract.ts +9 -2
- package/src/domains/observability/cost.ts +31 -4
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/observability/index.ts +10 -0
- package/src/domains/observability/out-of-turn-usage.ts +223 -0
- package/src/domains/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/resources/index.ts +20 -0
- package/src/domains/resources/library.ts +326 -0
- package/src/domains/resources/skills/marketplace.ts +37 -12
- package/src/domains/safety/call-target.ts +211 -14
- package/src/domains/safety/decision-presentation.ts +268 -0
- package/src/domains/safety/redaction.ts +73 -0
- package/src/domains/session/context-ledger.ts +10 -1
- package/src/domains/session/decision-board.ts +4 -0
- package/src/domains/session/entries.ts +3 -0
- package/src/domains/session/handoff.ts +629 -0
- package/src/domains/session/history.ts +68 -19
- package/src/domains/session/usage.ts +24 -7
- package/src/domains/share/archive.ts +67 -2
- package/src/engine/acp/event-mapper.ts +7 -0
- package/src/engine/acp/server.ts +29 -2
- 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/worker-events.ts +4 -3
- package/src/engine/worker-runtime.ts +59 -54
- package/src/entry/orchestrator.ts +55 -1
- package/src/interactive/bus-notices.ts +26 -0
- package/src/interactive/chat-loop-messages.ts +22 -0
- package/src/interactive/chat-loop.ts +248 -1
- package/src/interactive/chat-renderer.ts +41 -3
- package/src/interactive/clio-editor.ts +44 -7
- package/src/interactive/context-overlay.ts +43 -5
- package/src/interactive/cost-overlay.ts +70 -11
- package/src/interactive/council-dispatch.ts +30 -0
- package/src/interactive/council-grid.ts +213 -0
- package/src/interactive/council.ts +99 -0
- package/src/interactive/dispatch-board.ts +471 -50
- package/src/interactive/fleet-run-preview.ts +307 -0
- package/src/interactive/footer/notifications.ts +219 -0
- package/src/interactive/footer/widgets.ts +13 -0
- package/src/interactive/handoff-round.ts +56 -0
- package/src/interactive/interactive-application.ts +49 -2
- package/src/interactive/interactive-event-projection.ts +9 -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 +52 -2
- package/src/interactive/interactive-subscriptions.ts +14 -2
- package/src/interactive/memory-overlay.ts +89 -4
- package/src/interactive/oracle.ts +179 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +7 -1
- package/src/interactive/overlay-frame.ts +5 -2
- package/src/interactive/overlay-general-openers.ts +230 -2
- package/src/interactive/overlay-key-routing.ts +58 -2
- package/src/interactive/overlay-lifecycle.ts +52 -5
- package/src/interactive/overlay-permission-lifecycle.ts +33 -8
- package/src/interactive/overlay-resource-openers.ts +11 -3
- package/src/interactive/overlay-session-lifecycle.ts +234 -2
- 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/overlays/fleet-run-approval.ts +208 -0
- package/src/interactive/overlays/handoff-review.ts +185 -0
- package/src/interactive/overlays/library-install-confirm.ts +151 -0
- package/src/interactive/overlays/list-overlay.ts +168 -2
- package/src/interactive/overlays/settings.ts +101 -4
- package/src/interactive/overlays/side-question.ts +139 -0
- package/src/interactive/overlays/skills-hub.ts +401 -15
- package/src/interactive/permission-hint.ts +35 -0
- package/src/interactive/permission-overlay.ts +95 -45
- package/src/interactive/renderers/tool-execution.ts +19 -49
- package/src/interactive/session-last-turn.ts +8 -1
- package/src/interactive/session-usage-reseed.ts +36 -10
- package/src/interactive/side-question.ts +171 -0
- package/src/interactive/slash-commands.ts +434 -7
- package/src/interactive/slash-spec.ts +19 -6
- 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/theme/tokens.ts +30 -0
- package/src/interactive/turn-context.ts +96 -23
- package/src/interactive/turn-middleware.ts +16 -1
- package/src/interactive/turn-runtime.ts +37 -8
- package/src/interactive/turn-state.ts +3 -0
- package/src/interactive/watchdog-run.ts +75 -0
- package/src/interactive/worker-progress.ts +440 -0
- package/src/interactive/worker-share.ts +56 -1
- package/src/interactive/worker-stream.ts +58 -110
- package/src/tools/agent-tools.ts +28 -3
- package/src/tools/ask-user.ts +21 -1
- package/src/tools/bootstrap.ts +3 -0
- package/src/tools/compete-worktrees.ts +13 -79
- package/src/tools/context/index.ts +2 -2
- package/src/tools/dispatch-admission.ts +242 -8
- package/src/tools/dispatch-arguments.ts +65 -1
- package/src/tools/dispatch-event-text.ts +19 -0
- package/src/tools/dispatch-plan.ts +136 -6
- package/src/tools/dispatch-runner.ts +319 -13
- package/src/tools/dispatch-types.ts +20 -1
- package/src/tools/dispatch.ts +96 -3
- package/src/tools/monitor.ts +31 -0
- package/src/tools/profiles.ts +18 -4
- package/src/tools/registry.ts +15 -5
- package/src/tools/result-disposition.ts +156 -0
- package/src/tools/result-shaping.ts +59 -1
- package/src/tools/task-worktree.ts +238 -0
- package/src/tools/verify/authoring.ts +116 -55
- package/src/tools/verify/scripts.ts +62 -0
- package/src/tools/worker-evidence.ts +21 -1
- package/src/worker/spec-contract.ts +44 -3
- package/dist/chunk-EFADSJET.js +0 -18
- package/dist/chunk-HC4CLZ2Y.js +0 -68
- package/dist/memory-4ALKDJ4Q.js +0 -246
- package/src/domains/eval/metrics/chaos-stream.ts +0 -93
|
@@ -2,9 +2,13 @@ import { isAbsolute, resolve } from "node:path";
|
|
|
2
2
|
import {
|
|
3
3
|
MEMORY_SCOPES,
|
|
4
4
|
MEMORY_VERSION,
|
|
5
|
+
type MemoryAgentIdentity,
|
|
6
|
+
type MemoryPromotionRedaction,
|
|
5
7
|
type MemoryRecord,
|
|
8
|
+
type MemoryRecordProvenance,
|
|
6
9
|
type MemoryRecordValidationResult,
|
|
7
10
|
type MemoryRepositoryIdentity,
|
|
11
|
+
type MemoryRuntimeIdentity,
|
|
8
12
|
type MemoryScope,
|
|
9
13
|
type MemoryStoreValidationResult,
|
|
10
14
|
type MemoryValidationIssue,
|
|
@@ -65,6 +69,9 @@ function readMemoryRecord(value: unknown, path: string, issues: MemoryValidation
|
|
|
65
69
|
"approved",
|
|
66
70
|
"rejectedAt",
|
|
67
71
|
"repository",
|
|
72
|
+
"runtime",
|
|
73
|
+
"agent",
|
|
74
|
+
"provenance",
|
|
68
75
|
],
|
|
69
76
|
issues,
|
|
70
77
|
);
|
|
@@ -93,6 +100,9 @@ function readMemoryRecord(value: unknown, path: string, issues: MemoryValidation
|
|
|
93
100
|
const approved = readBoolean(value, `${path}.approved`, issues);
|
|
94
101
|
const rejectedAt = readOptionalIsoString(value, `${path}.rejectedAt`, issues);
|
|
95
102
|
const repository = readOptionalMemoryRepositoryIdentity(value, `${path}.repository`, issues);
|
|
103
|
+
const runtime = readOptionalNamedIdentity(value, `${path}.runtime`, "runtime", issues);
|
|
104
|
+
const agent = readOptionalNamedIdentity(value, `${path}.agent`, "agent", issues);
|
|
105
|
+
const provenance = readOptionalMemoryProvenance(value, `${path}.provenance`, issues);
|
|
96
106
|
if (approved === true && rejectedAt !== undefined) {
|
|
97
107
|
issues.push({ path: `${path}.rejectedAt`, message: "approved records must not be rejected" });
|
|
98
108
|
}
|
|
@@ -102,6 +112,18 @@ function readMemoryRecord(value: unknown, path: string, issues: MemoryValidation
|
|
|
102
112
|
if (repository === undefined && scope === "repo") {
|
|
103
113
|
issues.push({ path: `${path}.repository`, message: "repo scope requires repository applicability" });
|
|
104
114
|
}
|
|
115
|
+
if (runtime !== undefined && scope !== null && scope !== "runtime") {
|
|
116
|
+
issues.push({ path: `${path}.runtime`, message: "runtime applicability requires runtime scope" });
|
|
117
|
+
}
|
|
118
|
+
if (runtime === undefined && scope === "runtime") {
|
|
119
|
+
issues.push({ path: `${path}.runtime`, message: "runtime scope requires runtime applicability" });
|
|
120
|
+
}
|
|
121
|
+
if (agent !== undefined && scope !== null && scope !== "agent") {
|
|
122
|
+
issues.push({ path: `${path}.agent`, message: "agent applicability requires agent scope" });
|
|
123
|
+
}
|
|
124
|
+
if (agent === undefined && scope === "agent") {
|
|
125
|
+
issues.push({ path: `${path}.agent`, message: "agent scope requires agent applicability" });
|
|
126
|
+
}
|
|
105
127
|
if (
|
|
106
128
|
id === null ||
|
|
107
129
|
scope === null ||
|
|
@@ -132,6 +154,9 @@ function readMemoryRecord(value: unknown, path: string, issues: MemoryValidation
|
|
|
132
154
|
if (regressions !== undefined) record.regressions = regressions;
|
|
133
155
|
if (rejectedAt !== undefined) record.rejectedAt = rejectedAt;
|
|
134
156
|
if (repository !== undefined) record.repository = repository;
|
|
157
|
+
if (runtime !== undefined) record.runtime = runtime;
|
|
158
|
+
if (agent !== undefined) record.agent = agent;
|
|
159
|
+
if (provenance !== undefined) record.provenance = provenance;
|
|
135
160
|
return record;
|
|
136
161
|
}
|
|
137
162
|
|
|
@@ -160,10 +185,163 @@ function readOptionalMemoryRepositoryIdentity(
|
|
|
160
185
|
return { kind, key };
|
|
161
186
|
}
|
|
162
187
|
|
|
188
|
+
function readOptionalNamedIdentity<T extends "runtime" | "agent">(
|
|
189
|
+
record: Record<string, unknown>,
|
|
190
|
+
path: string,
|
|
191
|
+
kind: T,
|
|
192
|
+
issues: MemoryValidationIssue[],
|
|
193
|
+
): (T extends "runtime" ? MemoryRuntimeIdentity : MemoryAgentIdentity) | undefined {
|
|
194
|
+
const field = fieldName(path);
|
|
195
|
+
if (!Object.hasOwn(record, field)) return undefined;
|
|
196
|
+
const value = record[field];
|
|
197
|
+
if (!isRecord(value)) {
|
|
198
|
+
issues.push({ path, message: "expected object" });
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
rejectUnexpectedFields(value, path, ["kind", "key"], issues);
|
|
202
|
+
const actualKind = readString(value, `${path}.kind`, issues);
|
|
203
|
+
if (actualKind !== null && actualKind !== kind) {
|
|
204
|
+
issues.push({ path: `${path}.kind`, message: `expected ${kind}` });
|
|
205
|
+
}
|
|
206
|
+
const key = readString(value, `${path}.key`, issues);
|
|
207
|
+
if (key !== null && !isValidNamedIdentityKey(key)) {
|
|
208
|
+
issues.push({ path: `${path}.key`, message: `expected valid ${kind} identity` });
|
|
209
|
+
}
|
|
210
|
+
if (actualKind !== kind || key === null || !isValidNamedIdentityKey(key)) return undefined;
|
|
211
|
+
return { kind, key } as T extends "runtime" ? MemoryRuntimeIdentity : MemoryAgentIdentity;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function readOptionalMemoryProvenance(
|
|
215
|
+
record: Record<string, unknown>,
|
|
216
|
+
path: string,
|
|
217
|
+
issues: MemoryValidationIssue[],
|
|
218
|
+
): MemoryRecordProvenance | undefined {
|
|
219
|
+
const field = fieldName(path);
|
|
220
|
+
if (!Object.hasOwn(record, field)) return undefined;
|
|
221
|
+
const value = record[field];
|
|
222
|
+
if (!isRecord(value)) {
|
|
223
|
+
issues.push({ path, message: "expected object" });
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
rejectUnexpectedFields(
|
|
227
|
+
value,
|
|
228
|
+
path,
|
|
229
|
+
[
|
|
230
|
+
"sourceKind",
|
|
231
|
+
"evidenceId",
|
|
232
|
+
"sourceSessionId",
|
|
233
|
+
"sourceEntryId",
|
|
234
|
+
"sourceEntryKind",
|
|
235
|
+
"sourceEntryCreatedAt",
|
|
236
|
+
"sourceEntryLastTouchedAt",
|
|
237
|
+
"redaction",
|
|
238
|
+
],
|
|
239
|
+
issues,
|
|
240
|
+
);
|
|
241
|
+
const sourceKind = readString(value, `${path}.sourceKind`, issues);
|
|
242
|
+
if (sourceKind !== null && !["evidence", "task-bank-entry", "handoff-snapshot"].includes(sourceKind)) {
|
|
243
|
+
issues.push({ path: `${path}.sourceKind`, message: "expected evidence, task-bank-entry, or handoff-snapshot" });
|
|
244
|
+
}
|
|
245
|
+
const evidenceId = readOptionalSourceString(value, `${path}.evidenceId`, issues);
|
|
246
|
+
const sourceSessionId = readOptionalSourceString(value, `${path}.sourceSessionId`, issues);
|
|
247
|
+
const sourceEntryId = readOptionalSourceString(value, `${path}.sourceEntryId`, issues);
|
|
248
|
+
const sourceEntryKind = readOptionalSourceString(value, `${path}.sourceEntryKind`, issues);
|
|
249
|
+
if (sourceEntryKind !== undefined && sourceEntryKind !== "knowledge" && sourceEntryKind !== "procedural") {
|
|
250
|
+
issues.push({ path: `${path}.sourceEntryKind`, message: "expected knowledge or procedural" });
|
|
251
|
+
}
|
|
252
|
+
const sourceEntryCreatedAt = readOptionalIsoString(value, `${path}.sourceEntryCreatedAt`, issues);
|
|
253
|
+
const sourceEntryLastTouchedAt = readOptionalIsoString(value, `${path}.sourceEntryLastTouchedAt`, issues);
|
|
254
|
+
const redaction = readOptionalPromotionRedaction(value, `${path}.redaction`, issues);
|
|
255
|
+
if (sourceKind === "evidence") {
|
|
256
|
+
if (evidenceId === undefined)
|
|
257
|
+
issues.push({ path: `${path}.evidenceId`, message: "evidence source requires evidence id" });
|
|
258
|
+
if (sourceEntryId !== undefined || sourceEntryKind !== undefined) {
|
|
259
|
+
issues.push({ path, message: "evidence source must not carry task-memory entry fields" });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (sourceKind === "task-bank-entry" || sourceKind === "handoff-snapshot") {
|
|
263
|
+
if (sourceSessionId === undefined) {
|
|
264
|
+
issues.push({ path: `${path}.sourceSessionId`, message: "promotion source requires session id" });
|
|
265
|
+
}
|
|
266
|
+
if (sourceEntryId === undefined) {
|
|
267
|
+
issues.push({ path: `${path}.sourceEntryId`, message: "promotion source requires entry id" });
|
|
268
|
+
}
|
|
269
|
+
if (sourceEntryKind !== "knowledge" && sourceEntryKind !== "procedural") {
|
|
270
|
+
issues.push({ path: `${path}.sourceEntryKind`, message: "promotion source requires entry kind" });
|
|
271
|
+
}
|
|
272
|
+
if (redaction === undefined) {
|
|
273
|
+
issues.push({ path: `${path}.redaction`, message: "promotion source requires redaction provenance" });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (sourceKind === null || !["evidence", "task-bank-entry", "handoff-snapshot"].includes(sourceKind)) {
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
const provenance: MemoryRecordProvenance = { sourceKind: sourceKind as MemoryRecordProvenance["sourceKind"] };
|
|
280
|
+
if (evidenceId !== undefined) provenance.evidenceId = evidenceId;
|
|
281
|
+
if (sourceSessionId !== undefined) provenance.sourceSessionId = sourceSessionId;
|
|
282
|
+
if (sourceEntryId !== undefined) provenance.sourceEntryId = sourceEntryId;
|
|
283
|
+
if (sourceEntryKind === "knowledge" || sourceEntryKind === "procedural") {
|
|
284
|
+
provenance.sourceEntryKind = sourceEntryKind;
|
|
285
|
+
}
|
|
286
|
+
if (sourceEntryCreatedAt !== undefined) provenance.sourceEntryCreatedAt = sourceEntryCreatedAt;
|
|
287
|
+
if (sourceEntryLastTouchedAt !== undefined) provenance.sourceEntryLastTouchedAt = sourceEntryLastTouchedAt;
|
|
288
|
+
if (redaction !== undefined) provenance.redaction = redaction;
|
|
289
|
+
return provenance;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function readOptionalPromotionRedaction(
|
|
293
|
+
record: Record<string, unknown>,
|
|
294
|
+
path: string,
|
|
295
|
+
issues: MemoryValidationIssue[],
|
|
296
|
+
): MemoryPromotionRedaction | undefined {
|
|
297
|
+
const field = fieldName(path);
|
|
298
|
+
if (!Object.hasOwn(record, field)) return undefined;
|
|
299
|
+
const value = record[field];
|
|
300
|
+
if (!isRecord(value)) {
|
|
301
|
+
issues.push({ path, message: "expected object" });
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
rejectUnexpectedFields(value, path, ["appliedBeforePersistence", "replacementCount", "sourceFields"], issues);
|
|
305
|
+
const appliedBeforePersistence = readBoolean(value, `${path}.appliedBeforePersistence`, issues);
|
|
306
|
+
if (appliedBeforePersistence !== null && appliedBeforePersistence !== true) {
|
|
307
|
+
issues.push({ path: `${path}.appliedBeforePersistence`, message: "expected true" });
|
|
308
|
+
}
|
|
309
|
+
const replacementCount = readNumber(value, `${path}.replacementCount`, issues);
|
|
310
|
+
if (replacementCount !== null && (!Number.isInteger(replacementCount) || replacementCount < 0)) {
|
|
311
|
+
issues.push({ path: `${path}.replacementCount`, message: "expected non-negative integer" });
|
|
312
|
+
}
|
|
313
|
+
const sourceFields = readStringArray(value, `${path}.sourceFields`, issues);
|
|
314
|
+
if (appliedBeforePersistence !== true || replacementCount === null || sourceFields === null) return undefined;
|
|
315
|
+
return { appliedBeforePersistence, replacementCount, sourceFields };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function readOptionalSourceString(
|
|
319
|
+
record: Record<string, unknown>,
|
|
320
|
+
path: string,
|
|
321
|
+
issues: MemoryValidationIssue[],
|
|
322
|
+
): string | undefined {
|
|
323
|
+
const field = fieldName(path);
|
|
324
|
+
if (!Object.hasOwn(record, field)) return undefined;
|
|
325
|
+
const value = record[field];
|
|
326
|
+
if (typeof value !== "string" || !isValidSourceValue(value)) {
|
|
327
|
+
issues.push({ path, message: "expected valid non-empty source value" });
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
return value;
|
|
331
|
+
}
|
|
332
|
+
|
|
163
333
|
function isNormalizedAbsoluteRepositoryPath(value: string): boolean {
|
|
164
334
|
return !/[\0\r\n]/u.test(value) && isAbsolute(value) && resolve(value) === value;
|
|
165
335
|
}
|
|
166
336
|
|
|
337
|
+
function isValidNamedIdentityKey(value: string): boolean {
|
|
338
|
+
return value.length > 0 && value.length <= 256 && value.trim() === value && !/[\0\r\n\t ]/u.test(value);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function isValidSourceValue(value: string): boolean {
|
|
342
|
+
return value.length > 0 && value.length <= 1_024 && value.trim() === value && !/[\0\r\n]/u.test(value);
|
|
343
|
+
}
|
|
344
|
+
|
|
167
345
|
function rejectUnexpectedFields(
|
|
168
346
|
record: Record<string, unknown>,
|
|
169
347
|
path: string,
|
|
@@ -152,3 +152,18 @@ export type {
|
|
|
152
152
|
MiddlewareValidationIssue,
|
|
153
153
|
} from "./validate.js";
|
|
154
154
|
export { validateMiddlewareEffect, validateMiddlewareRule } from "./validate.js";
|
|
155
|
+
export {
|
|
156
|
+
coalesceTurnDiff,
|
|
157
|
+
createWatchdogRegistration,
|
|
158
|
+
WATCHDOG_DIFF_MAX_BYTES,
|
|
159
|
+
WATCHDOG_PATHS_MAX,
|
|
160
|
+
WATCHDOG_REGISTRATION_ID,
|
|
161
|
+
WATCHDOG_TASK,
|
|
162
|
+
WATCHDOG_TRUNCATION_MARKER,
|
|
163
|
+
type WatchdogDeps,
|
|
164
|
+
type WatchdogRegistration,
|
|
165
|
+
type WatchdogSettingsView,
|
|
166
|
+
type WatchdogTrigger,
|
|
167
|
+
type WatchdogTriggerReason,
|
|
168
|
+
watchdogBriefing,
|
|
169
|
+
} from "./watchdog.js";
|
|
@@ -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;
|
|
@@ -467,13 +471,13 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
467
471
|
function observeBeforeTool(input: MiddlewareHookInput): void {
|
|
468
472
|
const prepared = prepareToolStep(input);
|
|
469
473
|
if (prepared === null) return;
|
|
470
|
-
setBounded(pending, pendingKey(input, prepared.
|
|
474
|
+
setBounded(pending, pendingKey(input, prepared.operationFingerprint), prepared, settings().windowSteps);
|
|
471
475
|
}
|
|
472
476
|
|
|
473
477
|
function observeAfterTool(input: MiddlewareHookInput): ReadonlyArray<MiddlewareEffect> {
|
|
474
478
|
const fallback = prepareToolStep(input);
|
|
475
479
|
if (fallback === null) return NO_EFFECTS;
|
|
476
|
-
const key = pendingKey(input, fallback.
|
|
480
|
+
const key = pendingKey(input, fallback.operationFingerprint);
|
|
477
481
|
const prepared = pending.get(key) ?? fallback;
|
|
478
482
|
pending.delete(key);
|
|
479
483
|
const outcome = input.metadata?.resultKind === "error" ? "error" : "ok";
|
|
@@ -487,11 +491,13 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
487
491
|
} else {
|
|
488
492
|
consecutiveErrors = 0;
|
|
489
493
|
}
|
|
494
|
+
const digest = resultDigest(input, outcome);
|
|
490
495
|
const step: TrajectoryStep = {
|
|
491
496
|
...prepared,
|
|
492
497
|
step: toolStep,
|
|
493
498
|
outcome,
|
|
494
|
-
resultDigest:
|
|
499
|
+
resultDigest: digest.text,
|
|
500
|
+
resultDigestProvenance: digest.provenance,
|
|
495
501
|
};
|
|
496
502
|
trajectory.push(step);
|
|
497
503
|
if (trajectory.length > live.windowSteps) trajectory.splice(0, trajectory.length - live.windowSteps);
|
|
@@ -507,20 +513,20 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
507
513
|
* itself, which the model reads on its very next round.
|
|
508
514
|
*/
|
|
509
515
|
function annotateRepeatedFailure(step: TrajectoryStep): ReadonlyArray<MiddlewareEffect> {
|
|
510
|
-
if (annotatedThisTurn.has(step.
|
|
516
|
+
if (annotatedThisTurn.has(step.operationFingerprint)) return NO_EFFECTS;
|
|
511
517
|
const occurrences = trajectory.filter(
|
|
512
|
-
(candidate) => candidate.outcome === "error" && candidate.
|
|
518
|
+
(candidate) => candidate.outcome === "error" && candidate.operationFingerprint === step.operationFingerprint,
|
|
513
519
|
).length;
|
|
514
|
-
const failure = failures.get(step.
|
|
520
|
+
const failure = failures.get(step.operationFingerprint);
|
|
515
521
|
if (occurrences < 2 || failure === undefined) return NO_EFFECTS;
|
|
516
522
|
const message = boundedReminder(
|
|
517
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.`,
|
|
518
524
|
settings().maxTokens,
|
|
519
525
|
);
|
|
520
526
|
if (message.length === 0) return NO_EFFECTS;
|
|
521
|
-
annotatedThisTurn.add(step.
|
|
527
|
+
annotatedThisTurn.add(step.operationFingerprint);
|
|
522
528
|
// Claim the turn-end channel too, so one repeated failure is surfaced once.
|
|
523
|
-
|
|
529
|
+
lastInjectedOperationFingerprint = step.operationFingerprint;
|
|
524
530
|
lastDecision = "injected";
|
|
525
531
|
rulesInjectedSincePromptedStep = true;
|
|
526
532
|
annotatedSinceTurnEnd = true;
|
|
@@ -530,7 +536,7 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
530
536
|
}
|
|
531
537
|
|
|
532
538
|
function rememberFailure(step: TrajectoryStep): void {
|
|
533
|
-
const previous = failures.get(step.
|
|
539
|
+
const previous = failures.get(step.operationFingerprint);
|
|
534
540
|
const attempts = (previous?.attempts ?? 0) + 1;
|
|
535
541
|
const firstStep = previous?.firstStep ?? step.step;
|
|
536
542
|
const content = proceduralContent(step.callDescription, attempts, firstStep, step.resultDigest);
|
|
@@ -546,7 +552,7 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
546
552
|
}
|
|
547
553
|
setBounded(
|
|
548
554
|
failures,
|
|
549
|
-
step.
|
|
555
|
+
step.operationFingerprint,
|
|
550
556
|
{
|
|
551
557
|
entryId,
|
|
552
558
|
attempts,
|
|
@@ -566,21 +572,21 @@ export function createMemoryInterventionRegistration(deps: MemoryInterventionDep
|
|
|
566
572
|
step === undefined ||
|
|
567
573
|
step.outcome !== "error" ||
|
|
568
574
|
step.step <= lastTurnEndStep ||
|
|
569
|
-
step.
|
|
575
|
+
step.operationFingerprint === lastInjectedOperationFingerprint
|
|
570
576
|
) {
|
|
571
577
|
continue;
|
|
572
578
|
}
|
|
573
579
|
const occurrences = trajectory.filter(
|
|
574
|
-
(candidate) => candidate.outcome === "error" && candidate.
|
|
580
|
+
(candidate) => candidate.outcome === "error" && candidate.operationFingerprint === step.operationFingerprint,
|
|
575
581
|
).length;
|
|
576
|
-
const failure = failures.get(step.
|
|
582
|
+
const failure = failures.get(step.operationFingerprint);
|
|
577
583
|
if (occurrences < 2 || failure === undefined) continue;
|
|
578
584
|
const message = boundedReminder(
|
|
579
585
|
`Memory: [${failure.entryId}] you already tried ${failure.callDescription} at step ${failure.firstStep} and it failed with ${failure.errorDigest}.`,
|
|
580
586
|
settings().maxTokens,
|
|
581
587
|
);
|
|
582
588
|
if (message.length === 0) return NO_EFFECTS;
|
|
583
|
-
|
|
589
|
+
lastInjectedOperationFingerprint = step.operationFingerprint;
|
|
584
590
|
lastInjectedMessage = message;
|
|
585
591
|
lastDecision = "injected";
|
|
586
592
|
rulesInjectedSincePromptedStep = true;
|
|
@@ -624,31 +630,35 @@ function prepareToolStep(input: MiddlewareHookInput): PendingToolStep | null {
|
|
|
624
630
|
const canonical = hashToolCall(toolName, input.toolArgs ?? {});
|
|
625
631
|
return {
|
|
626
632
|
toolName,
|
|
627
|
-
|
|
633
|
+
operationFingerprint: createHash("sha256").update(canonical).digest("hex").slice(0, 16),
|
|
628
634
|
callDescription: shortText(canonical, CALL_DESCRIPTION_MAX_CHARS),
|
|
629
635
|
};
|
|
630
636
|
}
|
|
631
637
|
|
|
632
|
-
function pendingKey(input: MiddlewareHookInput,
|
|
633
|
-
return input.toolCallId ?? `anonymous:${
|
|
638
|
+
function pendingKey(input: MiddlewareHookInput, operationFingerprint: string): string {
|
|
639
|
+
return input.toolCallId ?? `anonymous:${operationFingerprint}`;
|
|
634
640
|
}
|
|
635
641
|
|
|
636
|
-
function resultDigest(input: MiddlewareHookInput, outcome: ToolOutcome):
|
|
642
|
+
function resultDigest(input: MiddlewareHookInput, outcome: ToolOutcome): ToolResultDigest {
|
|
643
|
+
if (input.toolResultDigest !== undefined) return sanitizeToolResultDigest(input.toolResultDigest);
|
|
637
644
|
if (outcome === "error") {
|
|
638
645
|
const metadataMessage = input.metadata?.errorMessage;
|
|
639
646
|
if (typeof metadataMessage === "string" && metadataMessage.trim().length > 0) {
|
|
640
|
-
return
|
|
647
|
+
return legacyToolResultDigest(diagnosticLine(metadataMessage), { outcome });
|
|
641
648
|
}
|
|
642
649
|
for (const key of ["error", "message"] as const) {
|
|
643
650
|
const value = input.toolResultDetails?.[key];
|
|
644
651
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
645
|
-
return
|
|
652
|
+
return legacyToolResultDigest(diagnosticLine(value), { outcome });
|
|
646
653
|
}
|
|
647
654
|
}
|
|
648
|
-
return "
|
|
655
|
+
return legacyToolResultDigest("", { outcome });
|
|
649
656
|
}
|
|
650
657
|
const resultFingerprint = input.metadata?.resultFingerprint;
|
|
651
|
-
return
|
|
658
|
+
return legacyToolResultDigest(
|
|
659
|
+
typeof resultFingerprint === "string" ? `ok result ${resultFingerprint.slice(0, 16)}` : "ok",
|
|
660
|
+
{ outcome },
|
|
661
|
+
);
|
|
652
662
|
}
|
|
653
663
|
|
|
654
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,11 +1,16 @@
|
|
|
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;
|
|
10
15
|
|
|
11
16
|
// Bare stems only. Inflected forms are derived below so that "running",
|
|
@@ -133,6 +138,43 @@ export const STALLED_TURN_RULE_DEFINITION: MiddlewareRuleDefinition = {
|
|
|
133
138
|
predicate: shouldRequestStalledTurnContinuation,
|
|
134
139
|
};
|
|
135
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
|
+
|
|
136
178
|
export function shouldRequestStalledTurnContinuation(input: MiddlewareHookInput): boolean {
|
|
137
179
|
if (input.hook !== "turn_end") return false;
|
|
138
180
|
if (input.metadata?.turnToolCalls !== 0) return false;
|
|
@@ -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
|
}
|