@iowarp/clio-coder 0.3.3 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/CONTRIBUTING.md +7 -7
- package/README.md +3 -3
- package/dist/{acp-P2AQILE2.js → acp-2BEHC4DL.js} +9 -8
- package/dist/{agents-72W3BI7I.js → agents-LNNFTM53.js} +29 -24
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5TWEIYDN.js → auth-KXXFI2VS.js} +14 -10
- package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
- package/dist/{chunk-YCWGATWI.js → chunk-24I7BN55.js} +2 -2
- package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
- package/dist/chunk-3BPUFZDL.js +37 -0
- package/dist/chunk-43AOLP7E.js +375 -0
- package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
- package/dist/{chunk-6SGHMWE3.js → chunk-5JGRAMKL.js} +5 -5
- package/dist/{chunk-V6RTAOC2.js → chunk-6US73PDB.js} +572 -51
- package/dist/{chunk-5UFT4SUX.js → chunk-6XXKFVSN.js} +3 -3
- package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
- package/dist/chunk-AOCYTWAV.js +449 -0
- package/dist/chunk-CFGTUFWB.js +67 -0
- package/dist/chunk-CJUB2JJ2.js +1478 -0
- package/dist/{chunk-FNTMWMX5.js → chunk-CKXWIANG.js} +14 -12
- package/dist/{chunk-PIWWS5BL.js → chunk-CYQKWTG3.js} +63 -78
- package/dist/{chunk-LZSJBIVT.js → chunk-DJVECN66.js} +271 -762
- package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
- package/dist/{chunk-ZWMF7253.js → chunk-E2ER4LJF.js} +304 -9
- package/dist/{chunk-STBPMHSX.js → chunk-EKY57CSP.js} +51 -84
- package/dist/{chunk-DUYJ5IO6.js → chunk-EYPA3EGJ.js} +12 -4
- package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
- package/dist/chunk-FYYLNIL5.js +313 -0
- package/dist/{chunk-OQ33BKR3.js → chunk-G7MUEIGA.js} +3 -60
- package/dist/chunk-GEYXPTRF.js +613 -0
- package/dist/chunk-GOXNB3AO.js +261 -0
- package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
- package/dist/chunk-HWUFFB6L.js +83 -0
- package/dist/{chunk-4XUGQOHA.js → chunk-K7T3E2SR.js} +15 -8
- package/dist/chunk-K7VKOLQQ.js +15 -0
- package/dist/{chunk-UFIIWP2H.js → chunk-KHSFENX2.js} +8 -8
- package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
- package/dist/chunk-LCGCVYZ4.js +57 -0
- package/dist/chunk-LL4KHSZI.js +22 -0
- package/dist/{chunk-PAJK6MAQ.js → chunk-LYF7OHWH.js} +42 -15
- package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
- package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
- package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
- package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
- package/dist/chunk-ODFEOB4F.js +1082 -0
- package/dist/{chunk-AGYYIBLL.js → chunk-OH3TOQTB.js} +6 -2
- package/dist/chunk-OZNBF4L3.js +23 -0
- package/dist/{chunk-DSELYM6W.js → chunk-PBTHKCPN.js} +30 -10
- package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
- package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
- package/dist/{chunk-SRDMMSEP.js → chunk-QM3F2GKX.js} +1063 -1645
- package/dist/{chunk-X6IAEBZR.js → chunk-QNQHSOLF.js} +7 -7
- package/dist/{chunk-OC7FIQPC.js → chunk-R46L2BIR.js} +10 -7
- package/dist/{chunk-2TLUCQVG.js → chunk-RD5U66HV.js} +3 -3
- package/dist/{chunk-6N5PTWMY.js → chunk-RY3LY4J5.js} +50 -13
- package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
- package/dist/{chunk-OOJYHWRB.js → chunk-TSHXZTOQ.js} +6 -5
- package/dist/chunk-TZSKNMZG.js +434 -0
- package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
- package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
- package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
- package/dist/chunk-W6GROXXM.js +69 -0
- package/dist/{chunk-OAO4GE4M.js → chunk-WHGPSPT5.js} +2 -2
- package/dist/chunk-WHJYKASB.js +677 -0
- package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
- package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
- package/dist/{chunk-TZTZS7QK.js → chunk-XE2VEJHX.js} +5 -3
- package/dist/{chunk-LM5TQCJZ.js → chunk-XF5N4U5A.js} +8 -7
- package/dist/{chunk-LWLEKMDQ.js → chunk-XXQNGV4M.js} +1073 -552
- package/dist/{chunk-KZWTDYJF.js → chunk-XYDYPRZI.js} +7 -7
- package/dist/chunk-ZGVHUX3M.js +66 -0
- package/dist/{chunk-LW6DSM3M.js → chunk-ZRGEBJ4T.js} +1192 -1119
- package/dist/{chunk-2DJ2KNFG.js → chunk-ZXF4XRKW.js} +202 -40
- package/dist/chunk-ZZMN5OM4.js +122 -0
- package/dist/cli/index.js +34 -30
- package/dist/{clio-JOU4FXVA.js → clio-M2KGYUFZ.js} +7 -6
- package/dist/{code-nav-7AX6FYE6.js → code-nav-GQNL7XA6.js} +8 -6
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-KELWS457.js → components-5TTYYX6G.js} +3 -3
- package/dist/{config-XCDVKR23.js → config-XUUYQIWO.js} +47 -35
- package/dist/{configure-4GAP54ZW.js → configure-IHJ7YOMV.js} +18 -15
- package/dist/{context-77FM5DV5.js → context-74JLXAWD.js} +18 -10
- package/dist/{context-4UOGGLQ5.js → context-75MIWW3U.js} +41 -29
- package/dist/{context-5VKGUVJJ.js → context-ZQ7SIFJV.js} +85 -9
- package/dist/{context-clear-XXJRLCJJ.js → context-clear-GYKWNUML.js} +41 -29
- package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
- package/dist/context-working-set-UX5KEP4J.js +1553 -0
- package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-GIJBHNFL.js} +47 -32
- package/dist/{docs-2C2LTVT2.js → docs-6FZSCG5B.js} +3 -3
- package/dist/{doctor-HR46URBJ.js → doctor-SVJ5BZCW.js} +12 -12
- package/dist/{eval-XSSNATB4.js → eval-CG6LLBLD.js} +54 -238
- package/dist/{evidence-6HG2PY2B.js → evidence-ZYFIEN42.js} +57 -28
- package/dist/{evolve-K7YU3NCY.js → evolve-QGEXEMDW.js} +36 -25
- package/dist/{extensions-QVDOHDGJ.js → extensions-ADGNCJJD.js} +3 -3
- package/dist/{fleet-VY3HHKN6.js → fleet-S5R4ZOQY.js} +73 -44
- package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-BHSNPBMH.js} +3 -3
- package/dist/{init-JYGXI3FK.js → init-5DRU55YR.js} +49 -37
- package/dist/memory-7YKKR6UC.js +467 -0
- package/dist/{models-I5QWSEOM.js → models-ZPOLRU2C.js} +24 -21
- package/dist/{monitor-GE4ID3IA.js → monitor-US5F5YGZ.js} +73 -46
- package/dist/{orchestrator-EM5MC3HM.js → orchestrator-E2AL4T5N.js} +1624 -1007
- package/dist/{paths-UXLN5YYZ.js → paths-E7KYAQWE.js} +3 -3
- package/dist/{reset-L2FQEE3E.js → reset-KZ652EK6.js} +6 -5
- package/dist/{run-ZU3QMZPZ.js → run-SRNBKDWD.js} +76 -54
- package/dist/{share-S5BZQC5I.js → share-CGZE33UP.js} +7 -6
- package/dist/{skills-X5VXCRNQ.js → skills-S2X4DLY5.js} +4 -4
- package/dist/{skills-eval-WKIHWTHR.js → skills-eval-W2GGIC4R.js} +40 -29
- package/dist/{targets-SNCPI2NR.js → targets-54SWINWB.js} +28 -23
- package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-SAIF2OGY.js} +6 -4
- package/dist/{uninstall-FZCQCDKC.js → uninstall-BVLWXKBT.js} +3 -3
- package/dist/{upgrade-JQHHPQ4K.js → upgrade-JKAR27XC.js} +20 -19
- package/dist/{usage-OR4O5SMZ.js → usage-MSAWCLX4.js} +79 -36
- package/dist/verifiers-NCBTHHN2.js +1220 -0
- package/dist/verify-X5HDROLA.js +25 -0
- package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-GUSOQ6ZP.js} +50 -37
- package/dist/worker/entry.js +90 -70
- package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
- package/docs/README.md +8 -7
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-placement.md +1 -2
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +60 -26
- package/docs/config-knobs-audit.md +1 -2
- package/docs/configuration-and-targets.md +26 -1
- package/docs/context-engine.md +67 -13
- package/docs/context-working-set.md +194 -0
- package/docs/development-pipeline.md +1 -1
- package/docs/documentation-coverage.md +6 -6
- package/docs/documentation-guide.md +7 -6
- package/docs/environment-variables.md +2 -1
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +4 -32
- package/docs/evidence-and-memory.md +139 -7
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +2 -2
- package/docs/fleet-dispatch.md +49 -8
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +19 -2
- package/docs/model-catalog.md +7 -9
- package/docs/observability.md +4 -4
- package/docs/proactive-memory.md +26 -16
- package/docs/prompt-envelope-and-tools.md +7 -5
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +43 -40
- package/docs/safety-model.md +49 -8
- package/docs/scientific-validation.md +21 -3
- package/docs/session-lifecycle.md +3 -3
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +79 -12
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +38 -4
- package/docs/worker-dispatch-mechanics.md +11 -1
- package/package.json +13 -13
- package/skills/meta/clio-test/SKILL.md +20 -17
- package/skills/meta/clio-test/evals.md +3 -3
- package/skills/meta/clio-test/references/harness.md +35 -6
- package/skills/meta/clio-test/references/test-map.md +20 -10
- package/skills/registry.yaml +2 -2
- package/skills/skill-marketplace.json +1 -1
- package/src/cli/agents.ts +2 -3
- package/src/cli/argv.ts +14 -1
- package/src/cli/context-working-set.ts +513 -0
- package/src/cli/context.ts +8 -0
- package/src/cli/evidence.ts +20 -2
- package/src/cli/fleet.ts +15 -0
- package/src/cli/index.ts +5 -1
- package/src/cli/memory.ts +272 -10
- package/src/cli/modes/json-stream.ts +2 -2
- package/src/cli/modes/print.ts +12 -1
- package/src/cli/run.ts +22 -2
- package/src/cli/targets.ts +12 -3
- package/src/cli/usage.ts +55 -7
- package/src/cli/verifiers.ts +325 -0
- package/src/core/bash-exec.ts +39 -14
- package/src/core/bus-events.ts +22 -4
- package/src/core/config.ts +54 -0
- package/src/core/defaults.ts +50 -3
- package/src/core/response-model-id.ts +134 -0
- package/src/core/toml.ts +62 -0
- package/src/core/verification-scripts.ts +6 -0
- package/src/core/workspace-files.ts +0 -1
- package/src/domains/agents/builtins/architect.md +1 -1
- package/src/domains/agents/builtins/verifier.md +3 -0
- package/src/domains/agents/catalog.ts +5 -4
- package/src/domains/agents/recipe.ts +54 -14
- package/src/domains/agents/result-contract.ts +7 -4
- package/src/domains/config/classify.ts +1 -0
- package/src/domains/context/bootstrap.ts +36 -27
- package/src/domains/context/project-metadata.ts +19 -63
- package/src/domains/context/prompt-context.ts +8 -0
- package/src/domains/context/working-set/contract.ts +161 -0
- package/src/domains/context/working-set/defaults.ts +28 -0
- package/src/domains/context/working-set/engine.ts +203 -0
- package/src/domains/context/working-set/fold.ts +62 -0
- package/src/domains/context/working-set/horizon.ts +38 -0
- package/src/domains/context/working-set/marker.ts +103 -0
- package/src/domains/context/working-set/path-index.ts +436 -0
- package/src/domains/context/working-set/payload.ts +152 -0
- package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
- package/src/domains/context/working-set/policies/index.ts +20 -0
- package/src/domains/context/working-set/policies/structural.ts +160 -0
- package/src/domains/context/working-set/project.ts +132 -0
- package/src/domains/context/working-set/protect.ts +109 -0
- package/src/domains/context/working-set/recall.ts +177 -0
- package/src/domains/context/working-set/replay/controls.ts +112 -0
- package/src/domains/context/working-set/replay/load-clio.ts +199 -0
- package/src/domains/context/working-set/replay/metrics.ts +185 -0
- package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
- package/src/domains/context/working-set/replay/report.ts +139 -0
- package/src/domains/context/working-set/replay/runner.ts +325 -0
- package/src/domains/context/working-set/replay/synthetic.ts +422 -0
- package/src/domains/context/working-set/replay/trace.ts +21 -0
- package/src/domains/context/working-set/visible.ts +54 -0
- package/src/domains/dispatch/budget-envelope.ts +396 -0
- package/src/domains/dispatch/contract.ts +2 -0
- package/src/domains/dispatch/extension.ts +81 -27
- package/src/domains/dispatch/orphan-recovery.ts +1 -0
- package/src/domains/dispatch/receipt-integrity.ts +4 -0
- package/src/domains/dispatch/state.ts +1 -0
- package/src/domains/dispatch/types.ts +10 -3
- package/src/domains/dispatch/validation.ts +14 -0
- package/src/domains/dispatch/worker-spawn.ts +14 -3
- package/src/domains/eval/metrics/evidence.ts +0 -116
- package/src/domains/eval/metrics/invariants.ts +1 -1
- package/src/domains/eval/runners/clio-run.ts +1 -10
- package/src/domains/eval/runners/external-command.ts +2 -29
- package/src/domains/eval/schema/suite.ts +0 -7
- package/src/domains/eval/suites/run.ts +1 -7
- package/src/domains/evidence/build.ts +112 -45
- package/src/domains/evidence/eval.ts +24 -7
- package/src/domains/evidence/index.ts +53 -0
- package/src/domains/evidence/ordering.ts +12 -0
- package/src/domains/evidence/run-trust.ts +221 -0
- package/src/domains/evidence/store.ts +46 -6
- package/src/domains/evidence/trust-status.ts +854 -0
- package/src/domains/evidence/types.ts +26 -0
- package/src/domains/memory/index.ts +22 -0
- package/src/domains/memory/operations.ts +58 -1
- package/src/domains/memory/promotion.ts +281 -0
- package/src/domains/memory/prompt-section.ts +25 -5
- package/src/domains/memory/proposal.ts +51 -7
- package/src/domains/memory/task-bank.ts +3 -2
- package/src/domains/memory/task-memory-handoff.ts +181 -24
- package/src/domains/memory/task-memory-policy.ts +3 -1
- package/src/domains/memory/types.ts +37 -0
- package/src/domains/memory/validate.ts +178 -0
- package/src/domains/middleware/memory-intervention.ts +38 -25
- package/src/domains/middleware/runtime.ts +6 -0
- package/src/domains/middleware/skills-reminder.ts +19 -4
- package/src/domains/middleware/stalled-turn.ts +208 -5
- package/src/domains/middleware/types.ts +10 -0
- package/src/domains/observability/contract.ts +6 -1
- package/src/domains/observability/cost.ts +20 -4
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/providers/index.ts +3 -0
- package/src/domains/providers/model-discovery.ts +9 -0
- package/src/domains/providers/runtime-resolution.ts +38 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
- package/src/domains/providers/types/context-window-slots.ts +18 -0
- package/src/domains/providers/types/runtime-descriptor.ts +3 -1
- package/src/domains/safety/autonomy.ts +1 -1
- package/src/domains/safety/call-target.ts +211 -14
- package/src/domains/safety/decision-presentation.ts +268 -0
- package/src/domains/safety/default-path-policy.ts +8 -0
- package/src/domains/safety/finish-contract.ts +4 -3
- package/src/domains/safety/policy-engine.ts +48 -6
- package/src/domains/safety/redaction.ts +73 -0
- package/src/domains/session/compaction/compact.ts +23 -1
- package/src/domains/session/compaction/cut-point.ts +2 -0
- package/src/domains/session/compaction/tokens.ts +16 -1
- package/src/domains/session/context-ledger.ts +12 -1
- package/src/domains/session/decision-board.ts +4 -0
- package/src/domains/session/entries.ts +110 -1
- package/src/domains/session/history.ts +68 -19
- package/src/domains/session/manager.ts +9 -2
- package/src/domains/session/migrations/index.ts +22 -3
- package/src/domains/session/usage.ts +24 -7
- package/src/engine/acp/event-mapper.ts +7 -0
- package/src/engine/acp/server.ts +32 -2
- package/src/engine/agent.ts +18 -1
- package/src/engine/apis/lmstudio.ts +25 -4
- package/src/engine/apis/openai-completions.ts +147 -22
- package/src/engine/claude/sdk-runtime.ts +8 -2
- package/src/engine/claude/tool-safety.ts +13 -0
- package/src/engine/loop-guard.ts +27 -3
- package/src/engine/session.ts +9 -3
- package/src/engine/worker-events.ts +4 -3
- package/src/engine/worker-runtime.ts +59 -54
- package/src/entry/orchestrator.ts +34 -5
- package/src/interactive/chat-loop-messages.ts +40 -6
- package/src/interactive/chat-loop.ts +13 -0
- package/src/interactive/chat-panel.ts +17 -1
- package/src/interactive/chat-renderer.ts +49 -24
- package/src/interactive/clio-editor.ts +44 -7
- package/src/interactive/context-meter.ts +10 -0
- package/src/interactive/context-overlay.ts +120 -7
- package/src/interactive/context-recall-command.ts +110 -0
- package/src/interactive/cost-overlay.ts +39 -8
- package/src/interactive/dispatch-board.ts +212 -35
- package/src/interactive/footer/widgets.ts +13 -0
- package/src/interactive/interactive-application.ts +6 -1
- package/src/interactive/interactive-input-runtime.ts +11 -1
- package/src/interactive/interactive-presentation.ts +11 -1
- package/src/interactive/interactive-slash-runtime.ts +37 -1
- package/src/interactive/memory-overlay.ts +89 -4
- package/src/interactive/model-session-replay.ts +21 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +1 -1
- package/src/interactive/overlay-frame.ts +5 -2
- package/src/interactive/overlay-general-openers.ts +46 -1
- package/src/interactive/overlay-key-routing.ts +41 -1
- package/src/interactive/overlay-lifecycle.ts +11 -4
- package/src/interactive/overlay-permission-lifecycle.ts +23 -8
- package/src/interactive/overlay-session-lifecycle.ts +8 -4
- package/src/interactive/overlay-transitions.ts +11 -0
- package/src/interactive/overlays/ask-user.ts +74 -30
- package/src/interactive/overlays/decisions.ts +3 -1
- package/src/interactive/permission-hint.ts +35 -0
- package/src/interactive/permission-overlay.ts +95 -45
- package/src/interactive/renderers/tool-execution.ts +37 -51
- package/src/interactive/session-last-turn.ts +8 -1
- package/src/interactive/session-transcript.ts +2 -2
- package/src/interactive/session-usage-reseed.ts +36 -10
- package/src/interactive/slash-commands.ts +31 -4
- package/src/interactive/status/summary.ts +5 -0
- package/src/interactive/status/types.ts +5 -0
- package/src/interactive/terminal-lease.ts +1 -0
- package/src/interactive/turn-context.ts +333 -110
- package/src/interactive/turn-middleware.ts +7 -6
- package/src/interactive/turn-runtime.ts +37 -8
- package/src/interactive/turn-state.ts +3 -0
- package/src/interactive/worker-progress.ts +440 -0
- package/src/interactive/worker-stream.ts +51 -110
- package/src/tools/agent-tools.ts +39 -7
- package/src/tools/ask-user.ts +21 -1
- package/src/tools/bash.ts +144 -82
- package/src/tools/builtin-tool-catalog.ts +11 -5
- package/src/tools/context/index.ts +107 -5
- package/src/tools/context/surface.ts +3 -2
- package/src/tools/core-bootstrap.ts +21 -0
- package/src/tools/dispatch-arguments.ts +8 -0
- package/src/tools/dispatch-event-text.ts +19 -0
- package/src/tools/dispatch-runner.ts +9 -7
- package/src/tools/dispatch.ts +24 -1
- package/src/tools/monitor.ts +43 -20
- package/src/tools/registry.ts +72 -10
- package/src/tools/result-disposition.ts +706 -0
- package/src/tools/result-shaping.ts +321 -20
- package/src/tools/safe-exec.ts +2 -0
- package/src/tools/verify/authoring.ts +1120 -0
- package/src/tools/verify/catalog.ts +346 -0
- package/src/tools/verify/index.ts +13 -3
- package/src/tools/verify/scripts.ts +135 -37
- package/src/tools/verify/surface.ts +9 -5
- package/src/tools/worker-evidence.ts +54 -12
- package/src/worker/spec-contract.ts +43 -3
- package/dist/chunk-J7CWMCQD.js +0 -255
- package/dist/chunk-T6YILFSB.js +0 -80
- package/dist/chunk-VAKQQHWR.js +0 -434
- package/dist/chunk-VPAYEGVX.js +0 -184
- package/dist/chunk-XBXAASKX.js +0 -18
- package/dist/memory-WFZMGYHX.js +0 -236
- package/src/domains/eval/metrics/chaos-stream.ts +0 -93
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TextDecoder } from "node:util";
|
|
1
2
|
import {
|
|
2
3
|
type Api,
|
|
3
4
|
type AssistantMessage,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
type Usage,
|
|
16
17
|
} from "@earendil-works/pi-ai";
|
|
17
18
|
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
19
|
+
import type { ResponseModelIdObservation } from "../../core/response-model-id.js";
|
|
18
20
|
import {
|
|
19
21
|
type AppliedThinking,
|
|
20
22
|
type ResolvedModelRuntimeCapabilities,
|
|
@@ -35,6 +37,13 @@ import { residencyManagedFor } from "./residency.js";
|
|
|
35
37
|
import { mergeSamplingOverride } from "./sampling-overrides.js";
|
|
36
38
|
import type { EngineApiProvider } from "./types.js";
|
|
37
39
|
|
|
40
|
+
declare module "@earendil-works/pi-ai" {
|
|
41
|
+
interface AssistantMessage {
|
|
42
|
+
/** Direct observation of model-id presence in an OpenAI-compatible response. */
|
|
43
|
+
responseModelIdObservation?: ResponseModelIdObservation;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
38
47
|
const piOpenAICompletions = openAICompletionsApi();
|
|
39
48
|
|
|
40
49
|
/**
|
|
@@ -83,6 +92,124 @@ function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
|
83
92
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
84
93
|
}
|
|
85
94
|
|
|
95
|
+
interface ResponseModelIdCapture {
|
|
96
|
+
reportedModelId: string | null;
|
|
97
|
+
observed: boolean;
|
|
98
|
+
done: boolean;
|
|
99
|
+
buffer: string;
|
|
100
|
+
decoder: TextDecoder | null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function observeResponseModelIdLine(line: string, capture: ResponseModelIdCapture): void {
|
|
104
|
+
if (capture.done) return;
|
|
105
|
+
const normalized = line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
106
|
+
if (!normalized.startsWith("data:")) return;
|
|
107
|
+
const data = normalized.slice("data:".length).trimStart();
|
|
108
|
+
if (data.length === 0 || data === "[DONE]") return;
|
|
109
|
+
try {
|
|
110
|
+
const payload = JSON.parse(data) as unknown;
|
|
111
|
+
if (!isPlainRecord(payload)) return;
|
|
112
|
+
const model = payload.model;
|
|
113
|
+
if (typeof model === "string" && model.trim().length > 0) {
|
|
114
|
+
capture.reportedModelId = model.trim();
|
|
115
|
+
capture.done = true;
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
// A partial or vendor-specific event is pi-ai's parsing concern. This
|
|
119
|
+
// observer records only complete OpenAI-compatible JSON data lines.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function observeResponseModelIdBytes(
|
|
124
|
+
chunk: Uint8Array | undefined,
|
|
125
|
+
capture: ResponseModelIdCapture,
|
|
126
|
+
flush = false,
|
|
127
|
+
): void {
|
|
128
|
+
if (capture.done || !capture.decoder) return;
|
|
129
|
+
capture.buffer += chunk ? capture.decoder.decode(chunk, { stream: !flush }) : capture.decoder.decode();
|
|
130
|
+
let newline = capture.buffer.indexOf("\n");
|
|
131
|
+
while (newline >= 0) {
|
|
132
|
+
const line = capture.buffer.slice(0, newline);
|
|
133
|
+
capture.buffer = capture.buffer.slice(newline + 1);
|
|
134
|
+
observeResponseModelIdLine(line, capture);
|
|
135
|
+
if (capture.done) {
|
|
136
|
+
capture.buffer = "";
|
|
137
|
+
capture.decoder = null;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
newline = capture.buffer.indexOf("\n");
|
|
141
|
+
}
|
|
142
|
+
if (flush && capture.buffer.length > 0) {
|
|
143
|
+
observeResponseModelIdLine(capture.buffer, capture);
|
|
144
|
+
capture.buffer = "";
|
|
145
|
+
}
|
|
146
|
+
if (capture.done || flush) capture.decoder = null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function captureResponseModelId(response: Response, capture: ResponseModelIdCapture): Response {
|
|
150
|
+
if (!response.body || !response.headers.get("content-type")?.toLowerCase().includes("text/event-stream")) {
|
|
151
|
+
return response;
|
|
152
|
+
}
|
|
153
|
+
const body = response.body.pipeThrough(
|
|
154
|
+
new TransformStream<Uint8Array, Uint8Array>({
|
|
155
|
+
transform(chunk, controller) {
|
|
156
|
+
capture.observed = true;
|
|
157
|
+
if (!capture.done) observeResponseModelIdBytes(chunk, capture);
|
|
158
|
+
controller.enqueue(chunk);
|
|
159
|
+
},
|
|
160
|
+
flush() {
|
|
161
|
+
capture.observed = true;
|
|
162
|
+
if (!capture.done) observeResponseModelIdBytes(undefined, capture, true);
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
return new Response(body, {
|
|
167
|
+
status: response.status,
|
|
168
|
+
statusText: response.statusText,
|
|
169
|
+
headers: response.headers,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function withResponseModelIdCapture<TOptions extends StreamOptions>(
|
|
174
|
+
options: TOptions,
|
|
175
|
+
sourceFactory: (capturedOptions: TOptions) => AssistantMessageEventStream,
|
|
176
|
+
): AssistantMessageEventStream {
|
|
177
|
+
const capture: ResponseModelIdCapture = {
|
|
178
|
+
reportedModelId: null,
|
|
179
|
+
observed: false,
|
|
180
|
+
done: false,
|
|
181
|
+
buffer: "",
|
|
182
|
+
decoder: new TextDecoder(),
|
|
183
|
+
};
|
|
184
|
+
const fetchImpl: NonNullable<StreamOptions["fetch"]> =
|
|
185
|
+
options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
186
|
+
const capturedOptions = {
|
|
187
|
+
...options,
|
|
188
|
+
fetch: async (input, init) => captureResponseModelId(await fetchImpl(input, init), capture),
|
|
189
|
+
} as TOptions;
|
|
190
|
+
const source = sourceFactory(capturedOptions);
|
|
191
|
+
const annotated = createAssistantMessageEventStream();
|
|
192
|
+
(async () => {
|
|
193
|
+
try {
|
|
194
|
+
for await (const event of source) {
|
|
195
|
+
const observation: ResponseModelIdObservation = capture.observed
|
|
196
|
+
? capture.reportedModelId === null
|
|
197
|
+
? { state: "not-reported" }
|
|
198
|
+
: { state: "reported", reportedModelId: capture.reportedModelId }
|
|
199
|
+
: { state: "not-observed" };
|
|
200
|
+
if (event.type === "done") event.message.responseModelIdObservation = observation;
|
|
201
|
+
else if (event.type === "error") event.error.responseModelIdObservation = observation;
|
|
202
|
+
annotated.push(event as AssistantMessageEvent);
|
|
203
|
+
}
|
|
204
|
+
annotated.end();
|
|
205
|
+
} catch (err) {
|
|
206
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
207
|
+
throw new Error(message);
|
|
208
|
+
}
|
|
209
|
+
})();
|
|
210
|
+
return annotated;
|
|
211
|
+
}
|
|
212
|
+
|
|
86
213
|
function samplingParamsFromProfile(profile: SamplingProfile): Record<string, unknown> {
|
|
87
214
|
return {
|
|
88
215
|
...(profile.topP !== undefined ? { top_p: profile.topP } : {}),
|
|
@@ -735,17 +862,16 @@ export const openAICompletionsApiProvider: EngineApiProvider<"openai-completions
|
|
|
735
862
|
stripSentinelsFromStream(
|
|
736
863
|
stripNeverReasoningFromStream(
|
|
737
864
|
filterGemmaChannelStream(
|
|
738
|
-
|
|
739
|
-
model,
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
effectiveContext,
|
|
748
|
-
withRemainingContextBudget(model, effectiveContext, withSamplers),
|
|
865
|
+
withResponseModelIdCapture(
|
|
866
|
+
withRemainingContextBudget(model, effectiveContext, withSamplers),
|
|
867
|
+
(capturedOptions) =>
|
|
868
|
+
withLocalResidency(
|
|
869
|
+
model,
|
|
870
|
+
{
|
|
871
|
+
...(options?.apiKey !== undefined ? { apiKey: options.apiKey } : {}),
|
|
872
|
+
...(options?.signal !== undefined ? { signal: options.signal } : {}),
|
|
873
|
+
},
|
|
874
|
+
(requestModel) => piOpenAICompletions.stream(requestModel, effectiveContext, capturedOptions),
|
|
749
875
|
),
|
|
750
876
|
),
|
|
751
877
|
resolved.family === "gemma-4",
|
|
@@ -768,17 +894,16 @@ export const openAICompletionsApiProvider: EngineApiProvider<"openai-completions
|
|
|
768
894
|
stripSentinelsFromStream(
|
|
769
895
|
stripNeverReasoningFromStream(
|
|
770
896
|
filterGemmaChannelStream(
|
|
771
|
-
|
|
772
|
-
model,
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
effectiveContext,
|
|
781
|
-
withRemainingContextBudget(model, effectiveContext, withSamplers),
|
|
897
|
+
withResponseModelIdCapture(
|
|
898
|
+
withRemainingContextBudget(model, effectiveContext, withSamplers),
|
|
899
|
+
(capturedOptions) =>
|
|
900
|
+
withLocalResidency(
|
|
901
|
+
model,
|
|
902
|
+
{
|
|
903
|
+
...(options?.apiKey !== undefined ? { apiKey: options.apiKey } : {}),
|
|
904
|
+
...(options?.signal !== undefined ? { signal: options.signal } : {}),
|
|
905
|
+
},
|
|
906
|
+
(requestModel) => piOpenAICompletions.streamSimple(requestModel, effectiveContext, capturedOptions),
|
|
782
907
|
),
|
|
783
908
|
),
|
|
784
909
|
resolved.family === "gemma-4",
|
|
@@ -413,9 +413,15 @@ function permissionResultForDecision(
|
|
|
413
413
|
return result;
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
-
function decideToolUse(
|
|
416
|
+
function decideToolUse(
|
|
417
|
+
input: PermissionGateInput,
|
|
418
|
+
toolCallId: string | undefined,
|
|
419
|
+
toolName: string,
|
|
420
|
+
toolInput: unknown,
|
|
421
|
+
): ClaudeToolPermissionDecision {
|
|
417
422
|
return emitClaudeToolPermissionDecision({
|
|
418
423
|
toolName,
|
|
424
|
+
...(toolCallId !== undefined ? { toolCallId } : {}),
|
|
419
425
|
input: coerceToolInput(toolInput),
|
|
420
426
|
safety: input.safety,
|
|
421
427
|
cwd: input.cwd,
|
|
@@ -447,7 +453,7 @@ function decideToolUseOnce(
|
|
|
447
453
|
toolInput: unknown,
|
|
448
454
|
): ClaudeToolPermissionDecision {
|
|
449
455
|
return decideClaudeSdkToolUseOnce(input.handledToolDecisions, toolUseID, () =>
|
|
450
|
-
decideToolUse(input, toolName, toolInput),
|
|
456
|
+
decideToolUse(input, toolUseID, toolName, toolInput),
|
|
451
457
|
);
|
|
452
458
|
}
|
|
453
459
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
DEFAULT_AUTONOMY_LEVEL,
|
|
9
9
|
mapAutonomy,
|
|
10
10
|
} from "../../domains/safety/autonomy.js";
|
|
11
|
+
import { describeCallAction } from "../../domains/safety/call-target.js";
|
|
11
12
|
import type { SafetyContract, SafetyDecision } from "../../domains/safety/contract.js";
|
|
12
13
|
import type { RejectionMessage } from "../../domains/safety/rejection-feedback.js";
|
|
13
14
|
import type { ToolFinishEvent, ToolStartEvent } from "../../tools/agent-tools.js";
|
|
@@ -68,6 +69,8 @@ export interface EvaluateClaudeToolPermissionInput {
|
|
|
68
69
|
export interface EmitClaudeToolPermissionInput extends EvaluateClaudeToolPermissionInput {
|
|
69
70
|
emit(event: ClioWorkerEvent): void;
|
|
70
71
|
onPermission?: "deny" | "fail";
|
|
72
|
+
/** SDK tool-use id shared by the start and finish telemetry events. */
|
|
73
|
+
toolCallId?: string;
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -324,9 +327,11 @@ function emitToolFinish(
|
|
|
324
327
|
outcome: ToolFinishEvent["outcome"],
|
|
325
328
|
reason: string,
|
|
326
329
|
reasonCode?: string,
|
|
330
|
+
toolCallId?: string,
|
|
327
331
|
): void {
|
|
328
332
|
const event: ToolFinishEvent = {
|
|
329
333
|
tool: mapped.clioToolName,
|
|
334
|
+
...(toolCallId !== undefined ? { toolCallId } : {}),
|
|
330
335
|
posture: "operating",
|
|
331
336
|
durationMs: Math.round(performance.now() - startedAtClock),
|
|
332
337
|
outcome,
|
|
@@ -350,10 +355,16 @@ export function emitClaudeToolPermissionDecision(input: EmitClaudeToolPermission
|
|
|
350
355
|
const startedAt = Date.now();
|
|
351
356
|
const startedAtClock = performance.now();
|
|
352
357
|
const decision = evaluateClaudeToolPermission(input);
|
|
358
|
+
const toolCallId = input.toolCallId?.trim() ? input.toolCallId : undefined;
|
|
359
|
+
// Mapped args are this side's own translation of the subprocess call, so the
|
|
360
|
+
// descriptor is composed from them here rather than downstream of the seam.
|
|
361
|
+
const action = describeCallAction(decision.mapped.clioToolName, decision.mapped.args);
|
|
353
362
|
const start: ToolStartEvent = {
|
|
354
363
|
tool: decision.mapped.clioToolName,
|
|
364
|
+
...(toolCallId !== undefined ? { toolCallId } : {}),
|
|
355
365
|
posture: "operating",
|
|
356
366
|
startedAt,
|
|
367
|
+
...(action !== null ? { action } : {}),
|
|
357
368
|
};
|
|
358
369
|
input.emit({ type: "clio_tool_start", payload: start });
|
|
359
370
|
if (decision.kind === "allow") {
|
|
@@ -365,6 +376,7 @@ export function emitClaudeToolPermissionDecision(input: EmitClaudeToolPermission
|
|
|
365
376
|
"ok",
|
|
366
377
|
decision.reason,
|
|
367
378
|
decision.reasonCode,
|
|
379
|
+
toolCallId,
|
|
368
380
|
);
|
|
369
381
|
return decision;
|
|
370
382
|
}
|
|
@@ -391,6 +403,7 @@ export function emitClaudeToolPermissionDecision(input: EmitClaudeToolPermission
|
|
|
391
403
|
"blocked",
|
|
392
404
|
decision.reason,
|
|
393
405
|
decision.reasonCode,
|
|
406
|
+
toolCallId,
|
|
394
407
|
);
|
|
395
408
|
return decision;
|
|
396
409
|
}
|
package/src/engine/loop-guard.ts
CHANGED
|
@@ -458,12 +458,14 @@ export interface CreateLoopGuardRegistrationOptions {
|
|
|
458
458
|
export interface LoopGuardRegistration extends MiddlewareHookRegistration {
|
|
459
459
|
/** Read-only attempt counter for tests and telemetry. */
|
|
460
460
|
callCount(): number;
|
|
461
|
+
/** One-way worker phase growth admitted before launch for a result-contract revision. */
|
|
462
|
+
extendWorkerToolCallPhase(phase: { toolCalls: number; readReserve: number }): boolean;
|
|
461
463
|
}
|
|
462
464
|
|
|
463
465
|
export function createLoopGuardRegistration(options: CreateLoopGuardRegistrationOptions): LoopGuardRegistration {
|
|
464
466
|
const budget = options.turnBlockBudget ?? INTERACTIVE_LOOP_BLOCK_BUDGET;
|
|
465
467
|
const cap = options.toolCallCap;
|
|
466
|
-
|
|
468
|
+
let softLimit =
|
|
467
469
|
options.toolCallSoftLimit !== undefined &&
|
|
468
470
|
Number.isSafeInteger(options.toolCallSoftLimit) &&
|
|
469
471
|
options.toolCallSoftLimit > 0 &&
|
|
@@ -472,8 +474,8 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
|
|
|
472
474
|
: undefined;
|
|
473
475
|
const turnBudget = options.turnToolCallBudget;
|
|
474
476
|
const synthesisLockout = options.turnSynthesisLockout === true;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
+
let softReadReserve = options.toolCallSoftReadReserve ?? 0;
|
|
478
|
+
let softReadReserveThreshold =
|
|
477
479
|
softLimit !== undefined && softReadReserve > 0 && softLimit > softReadReserve ? softLimit - softReadReserve : null;
|
|
478
480
|
const deliveryTools = [...new Set(options.deliveryTools ?? [])];
|
|
479
481
|
const reserveAdmits = (tool: string | undefined): boolean => isReserveAdmittedTool(tool, deliveryTools);
|
|
@@ -850,6 +852,28 @@ export function createLoopGuardRegistration(options: CreateLoopGuardRegistration
|
|
|
850
852
|
"blocks repeated tool calls, enforces tool-call caps, and annotates substantial identical read results across distinct arguments",
|
|
851
853
|
hooks: ["before_tool", "after_tool"],
|
|
852
854
|
callCount: () => count,
|
|
855
|
+
extendWorkerToolCallPhase(phase): boolean {
|
|
856
|
+
if (
|
|
857
|
+
softLimit === undefined ||
|
|
858
|
+
!Number.isSafeInteger(phase.toolCalls) ||
|
|
859
|
+
!Number.isSafeInteger(phase.readReserve) ||
|
|
860
|
+
phase.toolCalls <= 0 ||
|
|
861
|
+
phase.readReserve < 0 ||
|
|
862
|
+
phase.readReserve >= phase.toolCalls ||
|
|
863
|
+
(cap !== undefined && phase.toolCalls > cap) ||
|
|
864
|
+
phase.toolCalls < softLimit ||
|
|
865
|
+
(phase.toolCalls === softLimit && phase.readReserve <= softReadReserve)
|
|
866
|
+
) {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
softLimit = phase.toolCalls;
|
|
870
|
+
softReadReserve = phase.readReserve;
|
|
871
|
+
softReadReserveThreshold = softReadReserve > 0 && softLimit > softReadReserve ? softLimit - softReadReserve : null;
|
|
872
|
+
softReadReserveEntered = false;
|
|
873
|
+
softLockout = null;
|
|
874
|
+
reserveDenials = { denials: 0 };
|
|
875
|
+
return true;
|
|
876
|
+
},
|
|
853
877
|
evaluate(input): ReadonlyArray<MiddlewareEffect> {
|
|
854
878
|
if (input.hook === "after_tool") {
|
|
855
879
|
recordSuccessfulResult(input);
|
package/src/engine/session.ts
CHANGED
|
@@ -57,13 +57,19 @@ export interface ClioSessionMeta {
|
|
|
57
57
|
platform: string;
|
|
58
58
|
nodeVersion: string;
|
|
59
59
|
/**
|
|
60
|
-
* Version
|
|
61
|
-
*
|
|
60
|
+
* Version 4 on every new session. Readers reject earlier versions, sessions
|
|
61
|
+
* that omit this field, and versions from the future.
|
|
62
62
|
*/
|
|
63
63
|
sessionFormatVersion?: number;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Version 4 adds the working-set ledger kinds (`contextEviction`,
|
|
68
|
+
* `contextRecall`). A version-3 reader would parse those entries as unknown
|
|
69
|
+
* kinds and replay a session as if nothing had been evicted, so the bump is
|
|
70
|
+
* what makes an older Clio refuse the file instead of misreading it.
|
|
71
|
+
*/
|
|
72
|
+
export const CURRENT_SESSION_FORMAT_VERSION = 4;
|
|
67
73
|
|
|
68
74
|
export interface ClioSessionJsonlHeader {
|
|
69
75
|
type: "session";
|
|
@@ -53,9 +53,10 @@ export interface ClioPermissionEscalatedEvent {
|
|
|
53
53
|
tool: string;
|
|
54
54
|
summary: string;
|
|
55
55
|
/**
|
|
56
|
-
* Sanitized one-line preview of the call's object
|
|
57
|
-
*
|
|
58
|
-
*
|
|
56
|
+
* Sanitized one-line preview of the call's allowlisted object fields, so
|
|
57
|
+
* the operator's approval overlay can show what the call will touch.
|
|
58
|
+
* Unlisted fields carry only type-and-size summaries, and the args
|
|
59
|
+
* themselves never cross the stdout seam.
|
|
59
60
|
*/
|
|
60
61
|
target?: string;
|
|
61
62
|
axis?: string;
|
|
@@ -443,6 +443,42 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
|
|
|
443
443
|
// loop guard counts synthesis-lock noncompliance by model round, preventing
|
|
444
444
|
// one wide parallel batch from consuming the entire denial backstop.
|
|
445
445
|
let workerModelRound = 0;
|
|
446
|
+
const loopGuardRegistration = createLoopGuardRegistration({
|
|
447
|
+
safety,
|
|
448
|
+
toolCallCap: workerBudget.hardCap,
|
|
449
|
+
toolCallSoftLimit: workerBudget.toolCalls,
|
|
450
|
+
// A worker's blocks all land in one run-long bucket, so the bound on
|
|
451
|
+
// them is a statement about this run's length, not about a turn.
|
|
452
|
+
turnBlockBudget: workerLoopBlockBudget(workerBudget.revision?.toolCalls ?? workerBudget.toolCalls),
|
|
453
|
+
toolCallSoftReadReserve: readReserve,
|
|
454
|
+
...(deliveryTools.length > 0 ? { deliveryTools } : {}),
|
|
455
|
+
turnSynthesisLockout: workerBudget.synthesis,
|
|
456
|
+
// Once locked, the next model round is forced text-only at the
|
|
457
|
+
// request level. The lockout directive alone relies on model compliance.
|
|
458
|
+
onSynthesisLockout: () => {
|
|
459
|
+
if (workerBudget.synthesis) synthesisToolLock = true;
|
|
460
|
+
},
|
|
461
|
+
...(!workerBudget.synthesis
|
|
462
|
+
? {
|
|
463
|
+
onSoftLimitFinalCallAdmitted: (toolCallId: string | undefined) => {
|
|
464
|
+
if (workerBoundFailure === null) {
|
|
465
|
+
workerBoundFailure = `worker agent budget reached (${workerBudget.toolCalls}); synthesis is disabled`;
|
|
466
|
+
}
|
|
467
|
+
// Native agent tool calls carry a stable provider id. The wildcard
|
|
468
|
+
// keeps even an invariant violation on the post-result path.
|
|
469
|
+
stopAfterToolResultCallId = toolCallId ?? STOP_AFTER_ANY_TOOL_RESULT;
|
|
470
|
+
},
|
|
471
|
+
}
|
|
472
|
+
: {}),
|
|
473
|
+
// Requiring read is correct only when reading is the whole reserve.
|
|
474
|
+
...(readReserve > 0 && deliveryTools.length === 0
|
|
475
|
+
? {
|
|
476
|
+
onSoftReadReserve: () => {
|
|
477
|
+
middlewareToolChoice.apply([{ kind: "require_tool", toolName: ToolNames.Read }]);
|
|
478
|
+
},
|
|
479
|
+
}
|
|
480
|
+
: {}),
|
|
481
|
+
});
|
|
446
482
|
const registry = createWorkerToolRegistry(
|
|
447
483
|
input.middlewareSnapshot,
|
|
448
484
|
safety,
|
|
@@ -461,52 +497,7 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
|
|
|
461
497
|
// and has no persistence sink. It can still absorb worker-local
|
|
462
498
|
// protect_path effects from snapshot rules for the rest of this run.
|
|
463
499
|
[
|
|
464
|
-
|
|
465
|
-
safety,
|
|
466
|
-
toolCallCap: workerBudget.hardCap,
|
|
467
|
-
toolCallSoftLimit: workerBudget.toolCalls,
|
|
468
|
-
// A worker's blocks all land in one run-long bucket, so the bound on
|
|
469
|
-
// them is a statement about this run's length, not about a turn.
|
|
470
|
-
turnBlockBudget: workerLoopBlockBudget(workerBudget.toolCalls),
|
|
471
|
-
toolCallSoftReadReserve: readReserve,
|
|
472
|
-
...(deliveryTools.length > 0 ? { deliveryTools } : {}),
|
|
473
|
-
turnSynthesisLockout: workerBudget.synthesis,
|
|
474
|
-
// Once locked, the next model round is forced text-only at the
|
|
475
|
-
// request level (the tool surface is removed in onPayload below):
|
|
476
|
-
// the lockout directive alone relies on model compliance, and
|
|
477
|
-
// measured local models kept calling tools until the backstop
|
|
478
|
-
// aborted the run, or answered the forced round with tool-call
|
|
479
|
-
// markup that the loop guard then removed.
|
|
480
|
-
onSynthesisLockout: () => {
|
|
481
|
-
if (workerBudget.synthesis) {
|
|
482
|
-
synthesisToolLock = true;
|
|
483
|
-
}
|
|
484
|
-
},
|
|
485
|
-
...(!workerBudget.synthesis
|
|
486
|
-
? {
|
|
487
|
-
onSoftLimitFinalCallAdmitted: (toolCallId: string | undefined) => {
|
|
488
|
-
if (workerBoundFailure === null) {
|
|
489
|
-
workerBoundFailure = `worker agent budget reached (${workerBudget.toolCalls}); synthesis is disabled`;
|
|
490
|
-
}
|
|
491
|
-
// Native agent tool calls carry a stable provider id. The wildcard
|
|
492
|
-
// keeps even an invariant violation on the post-result path
|
|
493
|
-
// instead of reintroducing a before_tool abort.
|
|
494
|
-
stopAfterToolResultCallId = toolCallId ?? STOP_AFTER_ANY_TOOL_RESULT;
|
|
495
|
-
},
|
|
496
|
-
}
|
|
497
|
-
: {}),
|
|
498
|
-
// Forcing the next round to `read` is only correct when reading is
|
|
499
|
-
// the whole reserve. An agent with delivery tools must be able to
|
|
500
|
-
// write in its own reserve window, so it gets the steering directive
|
|
501
|
-
// without the request-level lock.
|
|
502
|
-
...(readReserve > 0 && deliveryTools.length === 0
|
|
503
|
-
? {
|
|
504
|
-
onSoftReadReserve: () => {
|
|
505
|
-
middlewareToolChoice.apply([{ kind: "require_tool", toolName: ToolNames.Read }]);
|
|
506
|
-
},
|
|
507
|
-
}
|
|
508
|
-
: {}),
|
|
509
|
-
}),
|
|
500
|
+
loopGuardRegistration,
|
|
510
501
|
createProtectedArtifactsRegistration({
|
|
511
502
|
...(input.protectedArtifactState !== undefined
|
|
512
503
|
? { initialState: { artifacts: [...input.protectedArtifactState.artifacts] } }
|
|
@@ -519,6 +510,7 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
|
|
|
519
510
|
);
|
|
520
511
|
const contractCwd = input.cwd ?? process.cwd();
|
|
521
512
|
let resultContractRepairsQueued = 0;
|
|
513
|
+
let resultContractRevisionActive = false;
|
|
522
514
|
/** Read tool call id -> what was asked for, pending that call's result. */
|
|
523
515
|
const pendingReadCitations = new Map<string, { path: string; offset: number | null; tail: boolean }>();
|
|
524
516
|
const observedReadRanges = new Map<string, Array<readonly [number, number]>>();
|
|
@@ -702,10 +694,17 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
|
|
|
702
694
|
if (violation !== null) {
|
|
703
695
|
if (resultContractRepairsQueued < RESULT_CONTRACT_REPAIR_LIMIT) {
|
|
704
696
|
resultContractRepairsQueued += 1;
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
697
|
+
if (!resultContractRevisionActive && workerBudget.revision !== undefined) {
|
|
698
|
+
resultContractRevisionActive = loopGuardRegistration.extendWorkerToolCallPhase(workerBudget.revision);
|
|
699
|
+
if (resultContractRevisionActive) {
|
|
700
|
+
synthesisToolLock = false;
|
|
701
|
+
middlewareToolChoice.reset();
|
|
702
|
+
stopAfterToolResultCallId = null;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const revisionToolsAvailable = resultContractRevisionActive && !synthesisToolLock;
|
|
706
|
+
// A repair without preauthorized growth remains terminal and text-only.
|
|
707
|
+
if (!revisionToolsAvailable) synthesisToolLock = true;
|
|
709
708
|
// Delivered as a paired tool exchange, never a user turn. Templates
|
|
710
709
|
// that key history rendering off the last `user` message re-render
|
|
711
710
|
// every earlier assistant turn when one is appended mid-run, which
|
|
@@ -714,7 +713,13 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
|
|
|
714
713
|
// bytes as a tool result (#55). The synthetic assistant call gives
|
|
715
714
|
// the result a real tool_call_id for strict endpoints (#62).
|
|
716
715
|
const repair = resultContractRepairMessages(
|
|
717
|
-
{
|
|
716
|
+
{
|
|
717
|
+
contract,
|
|
718
|
+
reason: violation,
|
|
719
|
+
attempt: resultContractRepairsQueued,
|
|
720
|
+
anchors: observedReadAnchors(),
|
|
721
|
+
...(revisionToolsAvailable ? { toolsAvailable: true } : {}),
|
|
722
|
+
},
|
|
718
723
|
{ provider: model.provider, api: model.api, model: model.id },
|
|
719
724
|
);
|
|
720
725
|
for (const message of repair) agent.followUp(message as unknown as AgentMessage);
|
|
@@ -915,9 +920,9 @@ export function startWorkerRun(input: WorkerRunInput, emit: WorkerEventEmit): Wo
|
|
|
915
920
|
const timer = setTimeout(() => resolveEscalation(requestId, "deny", "timeout"), escalationConfig.timeoutMs);
|
|
916
921
|
activeEscalation = { requestId, tool: call.tool, actionClass, callKey, timer };
|
|
917
922
|
// The operator decides on this exact call, so the escalation carries a
|
|
918
|
-
// sanitized preview of its object
|
|
919
|
-
//
|
|
920
|
-
const target = describeCallTarget(call.
|
|
923
|
+
// sanitized allowlisted preview of its object. Unlisted fields cross only
|
|
924
|
+
// as type-and-size summaries, and the args stay inside the worker.
|
|
925
|
+
const target = describeCallTarget(call.tool, call.args);
|
|
921
926
|
emit({
|
|
922
927
|
type: "clio_permission_escalated",
|
|
923
928
|
payload: {
|
|
@@ -161,8 +161,8 @@ import {
|
|
|
161
161
|
import { openSession, readSessionTailTurns, sessionCurrentPath, sessionPaths } from "../engine/session.js";
|
|
162
162
|
import type { EngineModel } from "../engine/types.js";
|
|
163
163
|
import { createChatLoop } from "../interactive/chat-loop.js";
|
|
164
|
-
import { buildReplayAgentMessagesFromTurns } from "../interactive/chat-renderer.js";
|
|
165
164
|
import { type RunIo, startInteractive } from "../interactive/index.js";
|
|
165
|
+
import { buildModelReplayAgentMessagesFromTurns } from "../interactive/model-session-replay.js";
|
|
166
166
|
import type { BootOptions } from "./boot-options.js";
|
|
167
167
|
|
|
168
168
|
export type { BootOptions, HeadlessSamplingOverrides } from "./boot-options.js";
|
|
@@ -183,6 +183,7 @@ import { isGitRepository, recoverCleanupReadyCompeteGroups } from "../tools/comp
|
|
|
183
183
|
import { createDispatchBackgroundRegistry } from "../tools/dispatch-background.js";
|
|
184
184
|
import { createFileMutationObserver, createSkillActivationObserver } from "../tools/observers.js";
|
|
185
185
|
import { createRegistry } from "../tools/registry.js";
|
|
186
|
+
import { sweepExpiredToolOffloads } from "../tools/result-shaping.js";
|
|
186
187
|
|
|
187
188
|
export interface BootResult {
|
|
188
189
|
exitCode: number;
|
|
@@ -726,6 +727,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
|
|
|
726
727
|
termination.installSignalHandlers();
|
|
727
728
|
|
|
728
729
|
ensureClioState();
|
|
730
|
+
sweepExpiredToolOffloads();
|
|
729
731
|
timer.mark("install check");
|
|
730
732
|
|
|
731
733
|
// A hard-killed coordinator cannot run domain drains. Reconcile its compete
|
|
@@ -1236,7 +1238,16 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
|
|
|
1236
1238
|
// one object the dispatch tool and the keypress both hold.
|
|
1237
1239
|
const dispatchBackground = createDispatchBackgroundRegistry();
|
|
1238
1240
|
registerAllTools(toolRegistry, {
|
|
1239
|
-
...(session
|
|
1241
|
+
...(session
|
|
1242
|
+
? {
|
|
1243
|
+
session,
|
|
1244
|
+
readSessionEntries: () => {
|
|
1245
|
+
const meta = session.current();
|
|
1246
|
+
return meta ? readSessionEntriesForCompact(meta.id) : [];
|
|
1247
|
+
},
|
|
1248
|
+
onContextRecalled: (payload) => bus.emit(BusChannels.ContextRecalled, payload),
|
|
1249
|
+
}
|
|
1250
|
+
: {}),
|
|
1240
1251
|
taskBoard,
|
|
1241
1252
|
userTasks,
|
|
1242
1253
|
dispatch,
|
|
@@ -1544,8 +1555,13 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
|
|
|
1544
1555
|
getMemorySection: () => {
|
|
1545
1556
|
try {
|
|
1546
1557
|
const records = loadMemoryRecordsSync(clioDataDir());
|
|
1558
|
+
const settings = getCurrentSettings();
|
|
1559
|
+
const targetId = session?.current()?.target ?? settings.orchestrator?.target;
|
|
1560
|
+
const runtimeId = targetId ? providers.getTarget(targetId)?.runtime : undefined;
|
|
1547
1561
|
return buildMemoryPromptSection(records, {
|
|
1562
|
+
scopes: ["global", "repo", "runtime"],
|
|
1548
1563
|
activeRepository: canonicalMemoryRepositoryIdentity(process.cwd()),
|
|
1564
|
+
activeRuntime: runtimeId === undefined ? null : { kind: "runtime", key: runtimeId },
|
|
1549
1565
|
}).section;
|
|
1550
1566
|
} catch {
|
|
1551
1567
|
return "";
|
|
@@ -1553,7 +1569,17 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
|
|
|
1553
1569
|
},
|
|
1554
1570
|
getTaskMemoryHandoffSource: () => {
|
|
1555
1571
|
ensureTaskMemorySession();
|
|
1556
|
-
|
|
1572
|
+
const meta = session?.current();
|
|
1573
|
+
if (!meta) throw new Error("task memory handoff requires an active session");
|
|
1574
|
+
const settings = getCurrentSettings();
|
|
1575
|
+
const targetId = meta.target ?? settings.orchestrator?.target;
|
|
1576
|
+
const runtimeId = targetId ? providers.getTarget(targetId)?.runtime : undefined;
|
|
1577
|
+
return renderTaskMemoryHandoffSource(taskMemoryBank.snapshot(), {
|
|
1578
|
+
sessionId: meta.id,
|
|
1579
|
+
evidenceRefs: [`session-${meta.id}`],
|
|
1580
|
+
runtimeIds: runtimeId === undefined ? [] : [runtimeId],
|
|
1581
|
+
agentIds: [],
|
|
1582
|
+
});
|
|
1557
1583
|
},
|
|
1558
1584
|
registerDeferredReminderSink: (sink) => {
|
|
1559
1585
|
deferredMemoryReminderSink = sink;
|
|
@@ -1612,7 +1638,10 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
|
|
|
1612
1638
|
// parent onto.
|
|
1613
1639
|
chat.resetForSession(
|
|
1614
1640
|
leafTurnId,
|
|
1615
|
-
|
|
1641
|
+
buildModelReplayAgentMessagesFromTurns(
|
|
1642
|
+
readCurrentSessionEntries(),
|
|
1643
|
+
leafTurnId ? { activeLeafTurnId: leafTurnId } : {},
|
|
1644
|
+
),
|
|
1616
1645
|
);
|
|
1617
1646
|
} catch (err) {
|
|
1618
1647
|
chat.resetForSession(leafTurnId);
|
|
@@ -1664,7 +1693,7 @@ export async function bootOrchestrator(options: BootOptions = {}): Promise<BootR
|
|
|
1664
1693
|
? {
|
|
1665
1694
|
readSessionEntries: readSessionEntriesForCompact,
|
|
1666
1695
|
buildReplayMessages: (entries: ReadonlyArray<SessionEntry>, leafTurnId: string | null) =>
|
|
1667
|
-
|
|
1696
|
+
buildModelReplayAgentMessagesFromTurns(entries, leafTurnId === null ? {} : { activeLeafTurnId: leafTurnId }),
|
|
1668
1697
|
}
|
|
1669
1698
|
: {}),
|
|
1670
1699
|
providers,
|