@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
|
@@ -187,6 +187,32 @@ export function middlewareHookFailedSessionNotice(payload: unknown, seenBudgetWa
|
|
|
187
187
|
return middlewareHookFailedNotice(payload, { noteBudgetWarningSuppression: key !== null });
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
/** How many failed checks a watchdog notice names before it counts the rest. */
|
|
191
|
+
export const WATCHDOG_NOTICE_CHECK_LIMIT = 3;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The watchdog's finding, as the one transcript line it is allowed.
|
|
195
|
+
*
|
|
196
|
+
* A passing report renders nothing: the operator asked for a warning, not a
|
|
197
|
+
* receipt, and a green line after every mutating turn is noise that trains
|
|
198
|
+
* people to stop reading the channel. Only the failed checks of the
|
|
199
|
+
* `verifier-report` contract reach here, and only their names: the evidence
|
|
200
|
+
* body belongs to the receipt, which the operator can open. Returning null
|
|
201
|
+
* means the turn passed or the report carried nothing usable.
|
|
202
|
+
*/
|
|
203
|
+
export function watchdogBlockersNotice(checks: ReadonlyArray<{ name: string; passed: boolean }>): BusNotice | null {
|
|
204
|
+
const failed = checks.filter((check) => !check.passed && check.name.trim().length > 0);
|
|
205
|
+
if (failed.length === 0) return null;
|
|
206
|
+
const named = failed.slice(0, WATCHDOG_NOTICE_CHECK_LIMIT).map((check) => check.name.trim());
|
|
207
|
+
const rest = failed.length - named.length;
|
|
208
|
+
const more = rest > 0 ? `, and ${rest} more` : "";
|
|
209
|
+
const blockers = failed.length === 1 ? "blocker" : "blockers";
|
|
210
|
+
return {
|
|
211
|
+
level: "warn",
|
|
212
|
+
text: `[watchdog] ${failed.length} ${blockers} after this turn: ${named.join("; ")}${more}. Nothing was changed or queued.`,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
190
216
|
/**
|
|
191
217
|
* A tool call parked for one-shot approval (sd-01 §3.3). The text names the
|
|
192
218
|
* axis that produced the ask: a safety-net rail asks at every level, while an
|
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { settingsPath } from "../core/config.js";
|
|
10
|
+
import {
|
|
11
|
+
addResponseModelIdObservationCount,
|
|
12
|
+
emptyResponseModelIdObservationCounts,
|
|
13
|
+
type ResponseModelIdObservation,
|
|
14
|
+
type ResponseModelIdObservationCounts,
|
|
15
|
+
responseModelIdObservationFromRecord,
|
|
16
|
+
} from "../core/response-model-id.js";
|
|
10
17
|
import type { PendingSkillRequest, PendingSkillToolPolicy, SkillDeclaredToolPolicy } from "../core/skill-activation.js";
|
|
11
18
|
import { ToolNames } from "../core/tool-names.js";
|
|
12
19
|
import { canonicalJson, sha256 } from "../domains/prompts/hash.js";
|
|
@@ -334,6 +341,7 @@ export function createAskUserToolPolicy(activeTools: ReadonlyArray<{ name: strin
|
|
|
334
341
|
status: "idle",
|
|
335
342
|
startedAt: now,
|
|
336
343
|
updatedAt: now,
|
|
344
|
+
exposure: "local",
|
|
337
345
|
rounds: [],
|
|
338
346
|
decisions: [],
|
|
339
347
|
inFlight: false,
|
|
@@ -356,6 +364,9 @@ export interface RunUsageSummary {
|
|
|
356
364
|
apiCalls: number;
|
|
357
365
|
hadReasoning: boolean;
|
|
358
366
|
hadUsage: boolean;
|
|
367
|
+
responseModelIdObservationCounts: ResponseModelIdObservationCounts;
|
|
368
|
+
lastResponseModelIdObservation: ResponseModelIdObservation;
|
|
369
|
+
lastDifferingResponseModelId: string | null;
|
|
359
370
|
}
|
|
360
371
|
|
|
361
372
|
/**
|
|
@@ -380,6 +391,9 @@ export function sumRunUsage(messages: ReadonlyArray<AgentMessage>): RunUsageSumm
|
|
|
380
391
|
apiCalls: 0,
|
|
381
392
|
hadReasoning: false,
|
|
382
393
|
hadUsage: false,
|
|
394
|
+
responseModelIdObservationCounts: emptyResponseModelIdObservationCounts(),
|
|
395
|
+
lastResponseModelIdObservation: { state: "not-observed" },
|
|
396
|
+
lastDifferingResponseModelId: null,
|
|
383
397
|
};
|
|
384
398
|
for (const raw of messages) {
|
|
385
399
|
const message = raw as
|
|
@@ -397,8 +411,15 @@ export function sumRunUsage(messages: ReadonlyArray<AgentMessage>): RunUsageSumm
|
|
|
397
411
|
};
|
|
398
412
|
if (!message || typeof message !== "object") continue;
|
|
399
413
|
if (message.role !== "assistant") continue;
|
|
414
|
+
const record = message as unknown as Record<string, unknown>;
|
|
400
415
|
const usage = message.usage;
|
|
401
416
|
if (!usage || typeof usage !== "object") continue;
|
|
417
|
+
summary.lastResponseModelIdObservation = responseModelIdObservationFromRecord(record, "not-observed");
|
|
418
|
+
addResponseModelIdObservationCount(summary.responseModelIdObservationCounts, summary.lastResponseModelIdObservation);
|
|
419
|
+
summary.lastDifferingResponseModelId =
|
|
420
|
+
typeof record.responseModel === "string" && record.responseModel.trim().length > 0
|
|
421
|
+
? record.responseModel.trim()
|
|
422
|
+
: null;
|
|
402
423
|
summary.hadUsage = true;
|
|
403
424
|
summary.apiCalls += 1;
|
|
404
425
|
const input = typeof usage.input === "number" ? usage.input : 0;
|
|
@@ -459,6 +480,7 @@ export function assistantSessionPayload(
|
|
|
459
480
|
const raw = message as unknown as Record<string, unknown>;
|
|
460
481
|
if (Array.isArray(raw.content)) payload.content = raw.content;
|
|
461
482
|
if (thinking.length > 0) payload.thinking = thinking;
|
|
483
|
+
payload.responseModelIdObservation = responseModelIdObservationFromRecord(raw, "not-observed");
|
|
462
484
|
for (const key of ["usage", "api", "provider", "model", "responseModel", "responseId", "diagnostics"]) {
|
|
463
485
|
if (raw[key] !== undefined) payload[key] = raw[key];
|
|
464
486
|
}
|
|
@@ -17,16 +17,19 @@ import { BusChannels, type RunAbortSource } from "../core/bus-events.js";
|
|
|
17
17
|
import type { ClioSettings } from "../core/config.js";
|
|
18
18
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
19
19
|
import type { PendingSkillRequest } from "../core/skill-activation.js";
|
|
20
|
+
import { clioStateDir } from "../core/xdg.js";
|
|
20
21
|
import {
|
|
21
22
|
createMiddlewareToolChoiceControl,
|
|
22
23
|
type MiddlewareContract,
|
|
23
24
|
type MiddlewareToolChoiceControl,
|
|
24
25
|
} from "../domains/middleware/index.js";
|
|
25
26
|
import type { ObservabilityContract } from "../domains/observability/contract.js";
|
|
27
|
+
import type { CostEntryLabel } from "../domains/observability/cost.js";
|
|
28
|
+
import { appendOutOfTurnUsageRow, type OutOfTurnUsageRow } from "../domains/observability/out-of-turn-usage.js";
|
|
26
29
|
import type { PromptsContract } from "../domains/prompts/contract.js";
|
|
27
30
|
import { toContextOverflowError } from "../domains/providers/errors.js";
|
|
28
31
|
import type { ProvidersContract } from "../domains/providers/index.js";
|
|
29
|
-
import { runtimeTargetSnapshot } from "../domains/providers/index.js";
|
|
32
|
+
import { normalizeCostProvenance, runtimeTargetSnapshot, targetRequiresAuth } from "../domains/providers/index.js";
|
|
30
33
|
import type { ProtectedArtifactState } from "../domains/safety/protected-artifacts.js";
|
|
31
34
|
import type { CompactResult } from "../domains/session/compaction/compact.js";
|
|
32
35
|
import type { ContextSnapshot, ContextUsageSnapshot } from "../domains/session/context-accounting.js";
|
|
@@ -38,6 +41,7 @@ import { protectedArtifactStateFromSessionEntries } from "../domains/session/pro
|
|
|
38
41
|
import { isRetryableErrorMessage, type RetrySettings } from "../domains/session/retry.js";
|
|
39
42
|
import { createEngineAgent } from "../engine/agent.js";
|
|
40
43
|
import { resolveReservedOutputTokens } from "../engine/apis/output-budget.js";
|
|
44
|
+
import { cwdHash } from "../engine/session.js";
|
|
41
45
|
import type { AgentEvent, AgentMessage, ImageContent } from "../engine/types.js";
|
|
42
46
|
import { resolveSessionTools } from "../tools/agent-tools.js";
|
|
43
47
|
import { finalizeAskUserInterviewForHost } from "../tools/ask-user.js";
|
|
@@ -53,7 +57,9 @@ import {
|
|
|
53
57
|
toolSignatureFromState,
|
|
54
58
|
} from "./chat-loop-messages.js";
|
|
55
59
|
import { normalizeRetrySettings } from "./chat-loop-policy.js";
|
|
60
|
+
import { runHandoffRound } from "./handoff-round.js";
|
|
56
61
|
import type { ApprovalRequestView } from "./permission-overlay.js";
|
|
62
|
+
import { runSideQuestion, type SideQuestionResult } from "./side-question.js";
|
|
57
63
|
import type { AgentStatusEvent } from "./status/types.js";
|
|
58
64
|
import { createTurnContext } from "./turn-context.js";
|
|
59
65
|
import { createTurnMiddleware } from "./turn-middleware.js";
|
|
@@ -184,6 +190,13 @@ export interface ChatSubmitOptions {
|
|
|
184
190
|
onAdmitted?: () => void;
|
|
185
191
|
}
|
|
186
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Placeholder key a target that needs no auth still has to be handed. Mirrors
|
|
195
|
+
* the turn runtime's own local fallback, so a `/btw` round against a local
|
|
196
|
+
* server authenticates exactly the way a turn against it does.
|
|
197
|
+
*/
|
|
198
|
+
const LOCAL_SIDE_QUESTION_API_KEY = "clio-local-target";
|
|
199
|
+
|
|
187
200
|
/** Closing notice an operator interrupt leaves in the transcript and the ledger. */
|
|
188
201
|
const INTERRUPT_CANCEL_REASON = "[Clio Coder] run interrupted by operator; delivering the new message now.";
|
|
189
202
|
const ENGINE_ACTIVE_PROMPT_ERROR =
|
|
@@ -212,6 +225,24 @@ export interface ChatCancelOptions {
|
|
|
212
225
|
auditReason?: string;
|
|
213
226
|
}
|
|
214
227
|
|
|
228
|
+
export interface SideQuestionOptions {
|
|
229
|
+
/** Cancels the round. Esc and Ctrl+C in the overlay abort through it. */
|
|
230
|
+
signal?: AbortSignal;
|
|
231
|
+
/** Streamed answer text so the overlay fills as the provider produces it. */
|
|
232
|
+
onDelta?: (partialText: string) => void;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* How a `/btw` round ended. `refused` is a round that never started (a turn was
|
|
237
|
+
* in flight, or no orchestrator target is configured); `failed` is a round that
|
|
238
|
+
* started and the provider rejected.
|
|
239
|
+
*/
|
|
240
|
+
export type SideQuestionOutcome =
|
|
241
|
+
| { status: "answered"; text: string }
|
|
242
|
+
| { status: "aborted"; text: string }
|
|
243
|
+
| { status: "refused"; reason: string }
|
|
244
|
+
| { status: "failed"; reason: string };
|
|
245
|
+
|
|
215
246
|
export interface ChatLoop {
|
|
216
247
|
submit(text: string, options?: ChatSubmitOptions): Promise<void>;
|
|
217
248
|
steer(text: string): boolean;
|
|
@@ -250,6 +281,28 @@ export interface ChatLoop {
|
|
|
250
281
|
* notice so the `/context compact` handler does not have to mirror the logic.
|
|
251
282
|
*/
|
|
252
283
|
compact(instructions?: string): Promise<void>;
|
|
284
|
+
/**
|
|
285
|
+
* `/btw`: answer one side question against the session's active target,
|
|
286
|
+
* model, and compiled message history without starting a turn.
|
|
287
|
+
*
|
|
288
|
+
* Nothing this produces reaches the session ledger, the transcript, the
|
|
289
|
+
* context ledger, or the footer token counters; the message history is read,
|
|
290
|
+
* never mutated. The round's provider usage is still reported to `/cost`,
|
|
291
|
+
* labeled as a side question, because money was spent. Refused outright
|
|
292
|
+
* while a turn is in flight rather than queued.
|
|
293
|
+
*/
|
|
294
|
+
askSideQuestion(question: string, options?: SideQuestionOptions): Promise<SideQuestionOutcome>;
|
|
295
|
+
/**
|
|
296
|
+
* `/handoff`: run the extraction round for a goal against the same target,
|
|
297
|
+
* model, and compiled message history, and return its raw JSON answer.
|
|
298
|
+
*
|
|
299
|
+
* Like a side question this is out of turn: no tools are sent, the message
|
|
300
|
+
* history is read and never mutated, and nothing the round produces reaches
|
|
301
|
+
* the ledger. Validating, bounding, and reviewing the answer belong to the
|
|
302
|
+
* caller; this method only owns the provider call. Refused outright while a
|
|
303
|
+
* turn is in flight rather than queued.
|
|
304
|
+
*/
|
|
305
|
+
extractHandoff(goal: string, options?: SideQuestionOptions): Promise<SideQuestionOutcome>;
|
|
253
306
|
/**
|
|
254
307
|
* Drop or replace the chat-loop's in-memory state after a session switch
|
|
255
308
|
* (/resume, /fork, /new). `leafTurnId` is the id the next user turn
|
|
@@ -292,6 +345,20 @@ export interface CreateChatLoopDeps {
|
|
|
292
345
|
*/
|
|
293
346
|
prompts?: PromptsContract;
|
|
294
347
|
createAgent?: typeof createEngineAgent;
|
|
348
|
+
/**
|
|
349
|
+
* The `/btw` round. Defaults to the real provider call; contracts inject a
|
|
350
|
+
* stub so they can assert what a side question does to the session without
|
|
351
|
+
* standing up a provider, exactly as `createAgent` does for a turn.
|
|
352
|
+
*/
|
|
353
|
+
runSideQuestion?: typeof runSideQuestion;
|
|
354
|
+
/** The `/handoff` extraction round. Injectable for the same reason. */
|
|
355
|
+
runHandoffRound?: typeof runHandoffRound;
|
|
356
|
+
/**
|
|
357
|
+
* Append one priced out-of-turn call to the durable out-of-turn usage store.
|
|
358
|
+
* Defaults to the real writer under the state dir. Contracts inject a spy so
|
|
359
|
+
* they can assert the row was written without touching a real state dir.
|
|
360
|
+
*/
|
|
361
|
+
recordOutOfTurnUsageRow?: (row: OutOfTurnUsageRow) => void;
|
|
295
362
|
/**
|
|
296
363
|
* Return the current session's entries for token estimation. The chat-loop
|
|
297
364
|
* calls this on every submit so the auto-compaction threshold sees the
|
|
@@ -369,6 +436,12 @@ export interface CreateChatLoopDeps {
|
|
|
369
436
|
* composition; the loop owns the buffer the reminder lands in.
|
|
370
437
|
*/
|
|
371
438
|
registerDeferredReminderSink?: (sink: (message: string) => void) => void;
|
|
439
|
+
/**
|
|
440
|
+
* The same seam for findings that are for the operator rather than the model.
|
|
441
|
+
* The watchdog uses it: its run settles after the turn it reviewed, and its
|
|
442
|
+
* blockers become one transcript notice that never enters model context.
|
|
443
|
+
*/
|
|
444
|
+
registerDeferredNoticeSink?: (sink: (text: string) => void) => void;
|
|
372
445
|
/**
|
|
373
446
|
* Host-finalizer seam for branch-anchored interview snapshots. Called once,
|
|
374
447
|
* after the ask-user host finalizer has settled the policy and its transcript.
|
|
@@ -399,6 +472,8 @@ export function reloadProtectedArtifactsForSession(
|
|
|
399
472
|
export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
400
473
|
const listeners = new Set<(event: ChatLoopEvent) => void>();
|
|
401
474
|
const createAgent = deps.createAgent ?? createEngineAgent;
|
|
475
|
+
const sideQuestionRound = deps.runSideQuestion ?? runSideQuestion;
|
|
476
|
+
const handoffRound = deps.runHandoffRound ?? runHandoffRound;
|
|
402
477
|
const middlewareToolChoice = deps.middlewareToolChoice ?? createMiddlewareToolChoiceControl();
|
|
403
478
|
const state = createTurnState(deps.getSettings().orchestrator.thinkingLevel ?? "off");
|
|
404
479
|
const toolStartTimes = new Map<string, number>();
|
|
@@ -485,6 +560,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
485
560
|
|
|
486
561
|
try {
|
|
487
562
|
deps.registerDeferredReminderSink?.((message) => middleware.injectDeferredReminder(message));
|
|
563
|
+
deps.registerDeferredNoticeSink?.((text) => middleware.emitDeferredNotice(text));
|
|
488
564
|
} catch {
|
|
489
565
|
// A background observer losing its delivery path must not stop the loop
|
|
490
566
|
// from starting; it simply stays silent.
|
|
@@ -549,6 +625,12 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
549
625
|
emitNotice,
|
|
550
626
|
toolStartTimes,
|
|
551
627
|
});
|
|
628
|
+
// A fresh ledger and footer render before the first submit. Start the same
|
|
629
|
+
// live capability probe that submit and resume use so those boot surfaces
|
|
630
|
+
// refresh to the probed model window as soon as provider discovery lands.
|
|
631
|
+
// The turn runtime coalesces the first submit onto this request, then its
|
|
632
|
+
// target and model TTL keeps later submits from repeating it.
|
|
633
|
+
void turnRuntime.ensureLiveCapabilitiesForSelectedModel().catch(() => {});
|
|
552
634
|
|
|
553
635
|
// --- bus subscriptions --------------------------------------------------
|
|
554
636
|
|
|
@@ -586,6 +668,102 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
586
668
|
return null;
|
|
587
669
|
};
|
|
588
670
|
|
|
671
|
+
/**
|
|
672
|
+
* Everything an out-of-turn round needs, or the reason it cannot run.
|
|
673
|
+
*
|
|
674
|
+
* `/btw` and `/handoff` are the two callers. Both read the compiled history
|
|
675
|
+
* the next turn would see, both authenticate exactly the way a turn against
|
|
676
|
+
* the same target does, and both are refused rather than queued while a turn
|
|
677
|
+
* is in flight, so the admission decision is made once here.
|
|
678
|
+
*/
|
|
679
|
+
type OutOfTurnPreparation =
|
|
680
|
+
| { ok: true; runtime: AgentRuntime; apiKey: string | undefined }
|
|
681
|
+
| { ok: false; reason: string };
|
|
682
|
+
|
|
683
|
+
const prepareOutOfTurnRound = async (inFlightRefusal: string, signal?: AbortSignal): Promise<OutOfTurnPreparation> => {
|
|
684
|
+
if (state.streaming) return { ok: false, reason: inFlightRefusal };
|
|
685
|
+
let agentRuntime: AgentRuntime | null;
|
|
686
|
+
try {
|
|
687
|
+
agentRuntime = turnRuntime.ensureRuntime();
|
|
688
|
+
} catch (err) {
|
|
689
|
+
return { ok: false, reason: err instanceof Error ? err.message : String(err) };
|
|
690
|
+
}
|
|
691
|
+
if (!agentRuntime) return { ok: false, reason: notConfiguredNotice() };
|
|
692
|
+
const resolution = agentRuntime.runtimeResolution;
|
|
693
|
+
try {
|
|
694
|
+
const apiKey = targetRequiresAuth(resolution.target, resolution.runtime)
|
|
695
|
+
? (
|
|
696
|
+
await deps.providers.auth.resolveForTarget(resolution.target, resolution.runtime, signal ? { signal } : undefined)
|
|
697
|
+
).apiKey
|
|
698
|
+
: LOCAL_SIDE_QUESTION_API_KEY;
|
|
699
|
+
return { ok: true, runtime: agentRuntime, apiKey };
|
|
700
|
+
} catch (err) {
|
|
701
|
+
return { ok: false, reason: err instanceof Error ? err.message : String(err) };
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
const writeOutOfTurnUsageRow =
|
|
706
|
+
deps.recordOutOfTurnUsageRow ?? ((row: OutOfTurnUsageRow): void => appendOutOfTurnUsageRow(clioStateDir(), row));
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Report an out-of-turn round's provider usage. Money was spent, so `/cost`
|
|
710
|
+
* says so under its own label; turn persistence, the working-set ledger,
|
|
711
|
+
* compaction inputs, and the footer counters never see it.
|
|
712
|
+
*
|
|
713
|
+
* The same call is also appended to the out-of-turn usage store under the
|
|
714
|
+
* state dir. That store exists because `/cost` only knows what this process
|
|
715
|
+
* spent: the round appends nothing to the session JSONL by design, so an
|
|
716
|
+
* archive reader such as `clio-coder usage report` had no record of the
|
|
717
|
+
* spend at all once the process exited. The session ledger stays untouched.
|
|
718
|
+
*/
|
|
719
|
+
const recordOutOfTurnUsage = (
|
|
720
|
+
runtime: AgentRuntime,
|
|
721
|
+
usage: SideQuestionResult["usage"],
|
|
722
|
+
label: CostEntryLabel,
|
|
723
|
+
): void => {
|
|
724
|
+
if (!usage) return;
|
|
725
|
+
const costProvenance = runtime.runtimeResolution.costProvenance;
|
|
726
|
+
deps.observability?.recordTokens(
|
|
727
|
+
runtime.targetId,
|
|
728
|
+
runtime.wireModelId,
|
|
729
|
+
usage.totalTokens,
|
|
730
|
+
usage.costUsd,
|
|
731
|
+
{
|
|
732
|
+
input: usage.input,
|
|
733
|
+
output: usage.output,
|
|
734
|
+
cacheRead: usage.cacheRead,
|
|
735
|
+
cacheWrite: usage.cacheWrite,
|
|
736
|
+
reasoningTokens: usage.reasoning,
|
|
737
|
+
totalTokens: usage.totalTokens,
|
|
738
|
+
apiCalls: 1,
|
|
739
|
+
},
|
|
740
|
+
costProvenance,
|
|
741
|
+
undefined,
|
|
742
|
+
label,
|
|
743
|
+
);
|
|
744
|
+
const meta = deps.session?.current() ?? null;
|
|
745
|
+
writeOutOfTurnUsageRow({
|
|
746
|
+
label,
|
|
747
|
+
sessionId: meta?.id ?? null,
|
|
748
|
+
// The identity the session ledger is filed under, so `usage report
|
|
749
|
+
// --repo` selects these rows with the same hash it selects ledgers with.
|
|
750
|
+
repoIdentity: meta ? meta.cwdHash || cwdHash(meta.cwd || process.cwd()) : null,
|
|
751
|
+
timestamp: new Date().toISOString(),
|
|
752
|
+
target: runtime.targetId,
|
|
753
|
+
attributedModelId: runtime.wireModelId,
|
|
754
|
+
usage: {
|
|
755
|
+
input: usage.input,
|
|
756
|
+
output: usage.output,
|
|
757
|
+
cacheRead: usage.cacheRead,
|
|
758
|
+
cacheWrite: usage.cacheWrite,
|
|
759
|
+
reasoning: usage.reasoning,
|
|
760
|
+
totalTokens: usage.totalTokens,
|
|
761
|
+
costUsd: usage.costUsd,
|
|
762
|
+
costProvenance: normalizeCostProvenance(costProvenance),
|
|
763
|
+
},
|
|
764
|
+
});
|
|
765
|
+
};
|
|
766
|
+
|
|
589
767
|
const api: ChatLoop = {
|
|
590
768
|
steer: (text) => queues.steer(text),
|
|
591
769
|
queueFollowUp: (text) => queues.queueFollowUp(text),
|
|
@@ -664,6 +842,7 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
664
842
|
// 1. Accept the prompt: reset per-turn accounting, freeze the tool
|
|
665
843
|
// surface, fire turn_start, and assemble the submitted text.
|
|
666
844
|
state.turnToolCalls = 0;
|
|
845
|
+
state.turnToolNames = [];
|
|
667
846
|
state.turnSharedWorkerNote = isWorkerShareNote(text);
|
|
668
847
|
middlewareToolChoice.reset();
|
|
669
848
|
if (options.requestContinuation !== true) state.stalledTurnNudgeSpent = false;
|
|
@@ -988,6 +1167,12 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
988
1167
|
middlewareToolChoice.reset();
|
|
989
1168
|
state.lastTurnId = leafTurnId;
|
|
990
1169
|
context.resetForSession();
|
|
1170
|
+
// The resumed ledger renders before the first new turn (issue #189),
|
|
1171
|
+
// and the window it renders against should be the probed one rather
|
|
1172
|
+
// than the catalog's, so the live capability probe the first submit
|
|
1173
|
+
// would run runs now, under the same TTL; the footer refreshes on the
|
|
1174
|
+
// provider-health event the probe publishes.
|
|
1175
|
+
void turnRuntime.ensureLiveCapabilitiesForSelectedModel().catch(() => {});
|
|
991
1176
|
state.replayedContextMessages = replayMessages ? [...replayMessages] : [];
|
|
992
1177
|
if (state.runtime) {
|
|
993
1178
|
state.runtime.agent.state.messages = [...state.replayedContextMessages];
|
|
@@ -1011,6 +1196,68 @@ export function createChatLoop(deps: CreateChatLoopDeps): ChatLoop {
|
|
|
1011
1196
|
middlewareToolChoice.reset();
|
|
1012
1197
|
},
|
|
1013
1198
|
|
|
1199
|
+
async askSideQuestion(question: string, options: SideQuestionOptions = {}): Promise<SideQuestionOutcome> {
|
|
1200
|
+
const text = question.trim();
|
|
1201
|
+
if (text.length === 0) {
|
|
1202
|
+
return { status: "refused", reason: "a side question needs a question" };
|
|
1203
|
+
}
|
|
1204
|
+
// Never queued. A side question exists to be answered now, beside a run
|
|
1205
|
+
// the operator is watching; holding it until the run settles would
|
|
1206
|
+
// deliver it after the moment it was asked in had passed.
|
|
1207
|
+
const prepared = await prepareOutOfTurnRound(
|
|
1208
|
+
"a turn is in flight; /btw runs beside the session, not in its queue",
|
|
1209
|
+
options.signal,
|
|
1210
|
+
);
|
|
1211
|
+
if (!prepared.ok) return { status: "refused", reason: prepared.reason };
|
|
1212
|
+
let result: SideQuestionResult;
|
|
1213
|
+
try {
|
|
1214
|
+
result = await sideQuestionRound({
|
|
1215
|
+
model: prepared.runtime.agent.state.model,
|
|
1216
|
+
// Read-only: runSideQuestion copies before appending its own
|
|
1217
|
+
// message, so the live agent's history is untouched.
|
|
1218
|
+
messages: prepared.runtime.agent.state.messages,
|
|
1219
|
+
question: text,
|
|
1220
|
+
...(prepared.apiKey !== undefined ? { apiKey: prepared.apiKey } : {}),
|
|
1221
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
1222
|
+
...(options.onDelta ? { onDelta: options.onDelta } : {}),
|
|
1223
|
+
});
|
|
1224
|
+
} catch (err) {
|
|
1225
|
+
return { status: "failed", reason: err instanceof Error ? err.message : String(err) };
|
|
1226
|
+
}
|
|
1227
|
+
recordOutOfTurnUsage(prepared.runtime, result.usage, "side-question");
|
|
1228
|
+
return result.aborted ? { status: "aborted", text: result.text } : { status: "answered", text: result.text };
|
|
1229
|
+
},
|
|
1230
|
+
|
|
1231
|
+
async extractHandoff(goal: string, options: SideQuestionOptions = {}): Promise<SideQuestionOutcome> {
|
|
1232
|
+
const text = goal.trim();
|
|
1233
|
+
if (text.length === 0) {
|
|
1234
|
+
return { status: "refused", reason: "a handoff needs a goal" };
|
|
1235
|
+
}
|
|
1236
|
+
// Refused, never queued: a handoff describes a session that has stopped
|
|
1237
|
+
// working, and a turn still in flight is about to change what the
|
|
1238
|
+
// document would say.
|
|
1239
|
+
const prepared = await prepareOutOfTurnRound(
|
|
1240
|
+
"a turn is in flight; /handoff cannot summarize a session that is still moving",
|
|
1241
|
+
options.signal,
|
|
1242
|
+
);
|
|
1243
|
+
if (!prepared.ok) return { status: "refused", reason: prepared.reason };
|
|
1244
|
+
let result: SideQuestionResult;
|
|
1245
|
+
try {
|
|
1246
|
+
result = await handoffRound({
|
|
1247
|
+
model: prepared.runtime.agent.state.model,
|
|
1248
|
+
// Read-only, exactly as the side-question round treats it.
|
|
1249
|
+
messages: prepared.runtime.agent.state.messages,
|
|
1250
|
+
goal: text,
|
|
1251
|
+
...(prepared.apiKey !== undefined ? { apiKey: prepared.apiKey } : {}),
|
|
1252
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
1253
|
+
});
|
|
1254
|
+
} catch (err) {
|
|
1255
|
+
return { status: "failed", reason: err instanceof Error ? err.message : String(err) };
|
|
1256
|
+
}
|
|
1257
|
+
recordOutOfTurnUsage(prepared.runtime, result.usage, "handoff");
|
|
1258
|
+
return result.aborted ? { status: "aborted", text: result.text } : { status: "answered", text: result.text };
|
|
1259
|
+
},
|
|
1260
|
+
|
|
1014
1261
|
async compact(instructions?: string): Promise<void> {
|
|
1015
1262
|
// Session check runs BEFORE orchestrator-configuration so a fresh
|
|
1016
1263
|
// TUI with nothing configured still reports the actionable "no
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* `isTransparentAssistantWrapper`.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
16
17
|
import { ToolNames } from "../core/tool-names.js";
|
|
17
18
|
import { foldWorkingSet } from "../domains/context/working-set/fold.js";
|
|
18
19
|
import { compactionCut } from "../domains/context/working-set/visible.js";
|
|
@@ -30,6 +31,13 @@ import type {
|
|
|
30
31
|
ThinkingLevelChangeEntry,
|
|
31
32
|
WorkerRunEntry,
|
|
32
33
|
} from "../domains/session/entries.js";
|
|
34
|
+
import {
|
|
35
|
+
HANDOFF_NOTE_CUSTOM_TYPE,
|
|
36
|
+
HANDOFF_SEED_CUSTOM_TYPE,
|
|
37
|
+
handoffSeedContextText,
|
|
38
|
+
isHandoffNoteData,
|
|
39
|
+
isHandoffSeedData,
|
|
40
|
+
} from "../domains/session/handoff.js";
|
|
33
41
|
import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
|
|
34
42
|
import {
|
|
35
43
|
type BashExecutionMessage,
|
|
@@ -44,7 +52,7 @@ import type { AgentMessage } from "../engine/types.js";
|
|
|
44
52
|
import { toolPresentationPolicy } from "../tools/presentation.js";
|
|
45
53
|
import { toolResultPresentationText } from "../tools/result-disposition.js";
|
|
46
54
|
import type { ChatLoopEvent, RetryStatusPayload } from "./chat-loop.js";
|
|
47
|
-
import { isSelfExplainingAbort } from "./chat-loop-messages.js";
|
|
55
|
+
import { isSelfExplainingAbort, toolResultSummary } from "./chat-loop-messages.js";
|
|
48
56
|
import type { ChatPanel } from "./chat-panel.js";
|
|
49
57
|
import { renderBranchSummaryEntry } from "./renderers/branch-summary.js";
|
|
50
58
|
import { renderCompactionSummaryEntry } from "./renderers/compaction-summary.js";
|
|
@@ -440,6 +448,7 @@ function richMessageFromEntry(entry: MessageEntry, maxTextChars?: number): Agent
|
|
|
440
448
|
"api",
|
|
441
449
|
"provider",
|
|
442
450
|
"model",
|
|
451
|
+
"responseModelIdObservation",
|
|
443
452
|
"responseModel",
|
|
444
453
|
"responseId",
|
|
445
454
|
"diagnostics",
|
|
@@ -671,6 +680,19 @@ interface ReplayToolResult {
|
|
|
671
680
|
blockReason?: string;
|
|
672
681
|
}
|
|
673
682
|
|
|
683
|
+
/**
|
|
684
|
+
* Resolve a replayed offload pointer once, when the ledger row becomes a
|
|
685
|
+
* display event. Terminal repaints consume the recorded fact and never touch
|
|
686
|
+
* the filesystem.
|
|
687
|
+
*/
|
|
688
|
+
function replayResultSummary(result: ReplayToolResult): Record<string, unknown> | undefined {
|
|
689
|
+
const summary = result.resultSummary ?? toolResultSummary(result.result);
|
|
690
|
+
const offloadPath =
|
|
691
|
+
typeof summary.offloadPath === "string" && summary.offloadPath.length > 0 ? summary.offloadPath : null;
|
|
692
|
+
if (offloadPath === null) return result.resultSummary;
|
|
693
|
+
return { ...summary, offloadFileMissing: !existsSync(offloadPath) };
|
|
694
|
+
}
|
|
695
|
+
|
|
674
696
|
function extractToolResult(entry: MessageEntry): ReplayToolResult {
|
|
675
697
|
const payload = entry.payload;
|
|
676
698
|
const obj = payloadObject(payload);
|
|
@@ -780,11 +802,18 @@ function rendersCustomEntry(entry: CustomEntry): boolean {
|
|
|
780
802
|
if (entry.display === false) return false;
|
|
781
803
|
if (entry.customType === "retryStatus") return true;
|
|
782
804
|
if (entry.customType === "finishContractAdvisory" || entry.customType === "middlewareReminder") return true;
|
|
805
|
+
if (entry.customType === HANDOFF_SEED_CUSTOM_TYPE || entry.customType === HANDOFF_NOTE_CUSTOM_TYPE) return true;
|
|
783
806
|
return entry.display === true;
|
|
784
807
|
}
|
|
785
808
|
|
|
786
809
|
function renderCustomEntry(entry: CustomEntry, width: number): string[] {
|
|
787
810
|
if (entry.customType === "retryStatus") return renderRetryStatusEntry(entry, width);
|
|
811
|
+
if (entry.customType === HANDOFF_SEED_CUSTOM_TYPE && isHandoffSeedData(entry.data)) {
|
|
812
|
+
return wrapTextWithAnsi(styleTaggedNotice(`[handoff] carried from session ${entry.data.fromSessionId}`), width);
|
|
813
|
+
}
|
|
814
|
+
if (entry.customType === HANDOFF_NOTE_CUSTOM_TYPE && isHandoffNoteData(entry.data)) {
|
|
815
|
+
return wrapTextWithAnsi(styleTaggedNotice(`[handoff] handed off to session ${entry.data.toSessionId}`), width);
|
|
816
|
+
}
|
|
788
817
|
// "finishContractAdvisory" is the pre-middleware name for the same entry
|
|
789
818
|
// shape; older session ledgers still carry it.
|
|
790
819
|
if (entry.customType === "finishContractAdvisory" || entry.customType === "middlewareReminder") {
|
|
@@ -1024,7 +1053,15 @@ export function buildReplayAgentMessagesFromTurns(
|
|
|
1024
1053
|
case "skillActivation":
|
|
1025
1054
|
appendContextMessage(out, "user", skillActivationContextText(entry), entry.timestamp);
|
|
1026
1055
|
break;
|
|
1056
|
+
// The one custom entry that becomes a model message. `/handoff` seeds a
|
|
1057
|
+
// new session with a reviewed document, and the seed is the first thing
|
|
1058
|
+
// the model reads there; it is labelled by its origin session and
|
|
1059
|
+
// carried as data, never as a user turn the operator did not write.
|
|
1027
1060
|
case "custom":
|
|
1061
|
+
if (entry.customType === HANDOFF_SEED_CUSTOM_TYPE && isHandoffSeedData(entry.data)) {
|
|
1062
|
+
appendContextMessage(out, "user", handoffSeedContextText(entry.data), entry.timestamp);
|
|
1063
|
+
}
|
|
1064
|
+
break;
|
|
1028
1065
|
case "modelChange":
|
|
1029
1066
|
case "thinkingLevelChange":
|
|
1030
1067
|
case "fileEntry":
|
|
@@ -1131,6 +1168,7 @@ export function rehydrateChatPanelFromTurns(
|
|
|
1131
1168
|
}
|
|
1132
1169
|
if (entry.role === "tool_result") {
|
|
1133
1170
|
const result = extractToolResult(entry);
|
|
1171
|
+
const resultSummary = replayResultSummary(result);
|
|
1134
1172
|
const evictedReason = workingSet.evicted.get(entry.turnId)?.reason;
|
|
1135
1173
|
const fallbackId = result.id ?? pendingToolIds.pop() ?? null;
|
|
1136
1174
|
if (fallbackId) {
|
|
@@ -1143,7 +1181,7 @@ export function rehydrateChatPanelFromTurns(
|
|
|
1143
1181
|
result: displayReplayToolResult(result.result, options.unboundedToolBodies === true),
|
|
1144
1182
|
isError: result.isError,
|
|
1145
1183
|
...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),
|
|
1146
|
-
...(
|
|
1184
|
+
...(resultSummary !== undefined ? { resultSummary } : {}),
|
|
1147
1185
|
...(result.outcome !== undefined ? { outcome: result.outcome } : {}),
|
|
1148
1186
|
...(result.blockReason !== undefined ? { blockReason: result.blockReason } : {}),
|
|
1149
1187
|
...(evictedReason !== undefined ? { evictedReason } : {}),
|
|
@@ -1157,7 +1195,7 @@ export function rehydrateChatPanelFromTurns(
|
|
|
1157
1195
|
result: displayReplayToolResult(result.result, options.unboundedToolBodies === true),
|
|
1158
1196
|
isError: result.isError,
|
|
1159
1197
|
...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),
|
|
1160
|
-
...(
|
|
1198
|
+
...(resultSummary !== undefined ? { resultSummary } : {}),
|
|
1161
1199
|
...(result.outcome === "blocked" ? { outcome: "blocked" as const } : {}),
|
|
1162
1200
|
...(result.blockReason !== undefined ? { blockReason: result.blockReason } : {}),
|
|
1163
1201
|
...(evictedReason !== undefined ? { evictedReason } : {}),
|