@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
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/oracle <question>`: the briefing an advisor is given, and the note its
|
|
3
|
+
* answer becomes.
|
|
4
|
+
*
|
|
5
|
+
* Oracle never receives a forked transcript. A transcript is the session's raw
|
|
6
|
+
* history, and handing it to an advisor makes the advisor agree with whatever
|
|
7
|
+
* the session already believes, which is the opposite of what an operator asks
|
|
8
|
+
* an oracle for. It is briefed on the record instead: the decisions the
|
|
9
|
+
* operator settled, the work the board still owes, and the last compaction
|
|
10
|
+
* summary when one exists.
|
|
11
|
+
*
|
|
12
|
+
* Pure: no I/O, no bus, no session, no dispatch. The caller owns the run.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { OracleResult } from "../domains/agents/index.js";
|
|
16
|
+
import type { DecisionLedgerEntry } from "../domains/session/entries.js";
|
|
17
|
+
import type { TaskBoardTask } from "../domains/session/task-board.js";
|
|
18
|
+
|
|
19
|
+
/** The recipe `/oracle` dispatches. Shadow, so `/run` can never reach it. */
|
|
20
|
+
export const ORACLE_AGENT_ID = "oracle";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Total bound on the packed briefing, in UTF-8 bytes. Well under the 64 KiB
|
|
24
|
+
* an internal-origin dispatch briefing may carry, because the point of the
|
|
25
|
+
* digest is that a small local advisor can hold all of it at once.
|
|
26
|
+
*/
|
|
27
|
+
export const ORACLE_DIGEST_MAX_BYTES = 12 * 1024;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Per-section byte caps. They sum to 11776, which leaves 512 bytes of the
|
|
31
|
+
* total for the section headers and blank lines the packer writes between
|
|
32
|
+
* them. Each section is clamped on its own before the whole digest is clamped
|
|
33
|
+
* again, so one runaway section can never starve the others: a 40 KiB
|
|
34
|
+
* compaction summary loses its own tail rather than the question's.
|
|
35
|
+
*/
|
|
36
|
+
export const ORACLE_DECISIONS_MAX_BYTES = 5120;
|
|
37
|
+
export const ORACLE_TASKS_MAX_BYTES = 3072;
|
|
38
|
+
export const ORACLE_COMPACTION_MAX_BYTES = 2048;
|
|
39
|
+
export const ORACLE_QUESTION_MAX_BYTES = 1536;
|
|
40
|
+
|
|
41
|
+
/** Row caps, applied before the byte caps. Newest decisions and lowest task ids first. */
|
|
42
|
+
export const ORACLE_DECISIONS_MAX_ROWS = 24;
|
|
43
|
+
export const ORACLE_TASKS_MAX_ROWS = 24;
|
|
44
|
+
|
|
45
|
+
/** Appended wherever a cap cut content, so the advisor knows it is reading a tail-less record. */
|
|
46
|
+
export const ORACLE_TRUNCATION_MARKER = "\n[truncated]";
|
|
47
|
+
|
|
48
|
+
export const ORACLE_NO_DECISIONS = "(no settled decisions on this branch)";
|
|
49
|
+
export const ORACLE_NO_TASKS = "(no open tasks on the board)";
|
|
50
|
+
|
|
51
|
+
export interface OracleDigestSources {
|
|
52
|
+
/** Settled interviews, newest first, exactly as the decision board folds them. */
|
|
53
|
+
decisions: ReadonlyArray<DecisionLedgerEntry>;
|
|
54
|
+
/** The live task board's tasks; the packer keeps the open ones. */
|
|
55
|
+
tasks: ReadonlyArray<TaskBoardTask>;
|
|
56
|
+
/** The most recent compaction summary on the active branch, or null when the session never compacted. */
|
|
57
|
+
compactionSummary: string | null;
|
|
58
|
+
question: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface OracleDigest {
|
|
62
|
+
text: string;
|
|
63
|
+
/** True when any cap cut content. The digest says so in its own text as well. */
|
|
64
|
+
truncated: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function byteLength(text: string): number {
|
|
68
|
+
return Buffer.byteLength(text, "utf8");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Clamp to `maxBytes` including the marker, never past a UTF-8 code point. A
|
|
73
|
+
* marker that pushed the result over its own cap would make the section caps
|
|
74
|
+
* unable to add up to the total, which is the one property the packer relies
|
|
75
|
+
* on.
|
|
76
|
+
*/
|
|
77
|
+
function clamp(text: string, maxBytes: number): { text: string; truncated: boolean } {
|
|
78
|
+
if (byteLength(text) <= maxBytes) return { text, truncated: false };
|
|
79
|
+
const room = Math.max(0, maxBytes - byteLength(ORACLE_TRUNCATION_MARKER));
|
|
80
|
+
const buffer = Buffer.from(text, "utf8");
|
|
81
|
+
let cut = Math.min(room, buffer.byteLength);
|
|
82
|
+
while (cut > 0) {
|
|
83
|
+
const next = buffer[cut];
|
|
84
|
+
if (next === undefined || (next & 0xc0) !== 0x80) break;
|
|
85
|
+
cut -= 1;
|
|
86
|
+
}
|
|
87
|
+
return { text: `${buffer.subarray(0, cut).toString("utf8")}${ORACLE_TRUNCATION_MARKER}`, truncated: true };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** One decision line. Superseded decisions carry their correction so a reversal is visible. */
|
|
91
|
+
function decisionLine(decision: DecisionLedgerEntry["decisions"][number]): string {
|
|
92
|
+
const label = decision.label ? ` (${decision.label})` : "";
|
|
93
|
+
if (decision.status === "superseded") {
|
|
94
|
+
const correction = decision.correction ? `; corrected to ${decision.correction}` : "";
|
|
95
|
+
return `- ${decision.key}${label}: ${decision.value} [superseded${correction}]`;
|
|
96
|
+
}
|
|
97
|
+
return `- ${decision.key}${label}: ${decision.value}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function taskLine(task: TaskBoardTask): string {
|
|
101
|
+
return `- ${task.id} [${task.status}] ${task.title}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isOpenTask(task: TaskBoardTask): boolean {
|
|
105
|
+
return task.status === "pending" || task.status === "active";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Pack the briefing. Sections are fixed and always present, so an advisor
|
|
110
|
+
* reading an empty one learns that the board is empty rather than that the
|
|
111
|
+
* packer omitted it.
|
|
112
|
+
*/
|
|
113
|
+
export function packOracleDigest(sources: OracleDigestSources): OracleDigest {
|
|
114
|
+
let truncated = false;
|
|
115
|
+
|
|
116
|
+
const decisionRows = sources.decisions
|
|
117
|
+
.flatMap((entry) => entry.decisions.map(decisionLine))
|
|
118
|
+
.slice(0, ORACLE_DECISIONS_MAX_ROWS);
|
|
119
|
+
if (decisionRows.length < sources.decisions.reduce((total, entry) => total + entry.decisions.length, 0)) {
|
|
120
|
+
truncated = true;
|
|
121
|
+
}
|
|
122
|
+
const decisions = clamp(decisionRows.join("\n") || ORACLE_NO_DECISIONS, ORACLE_DECISIONS_MAX_BYTES);
|
|
123
|
+
truncated = truncated || decisions.truncated;
|
|
124
|
+
|
|
125
|
+
const openTasks = sources.tasks.filter(isOpenTask);
|
|
126
|
+
const taskRows = openTasks.map(taskLine).slice(0, ORACLE_TASKS_MAX_ROWS);
|
|
127
|
+
if (taskRows.length < openTasks.length) truncated = true;
|
|
128
|
+
const tasks = clamp(taskRows.join("\n") || ORACLE_NO_TASKS, ORACLE_TASKS_MAX_BYTES);
|
|
129
|
+
truncated = truncated || tasks.truncated;
|
|
130
|
+
|
|
131
|
+
const summaryText = sources.compactionSummary?.trim() ?? "";
|
|
132
|
+
const summary = clamp(summaryText, ORACLE_COMPACTION_MAX_BYTES);
|
|
133
|
+
truncated = truncated || summary.truncated;
|
|
134
|
+
|
|
135
|
+
const question = clamp(sources.question.trim(), ORACLE_QUESTION_MAX_BYTES);
|
|
136
|
+
truncated = truncated || question.truncated;
|
|
137
|
+
|
|
138
|
+
const sections = [
|
|
139
|
+
"## Settled decisions",
|
|
140
|
+
decisions.text,
|
|
141
|
+
"",
|
|
142
|
+
"## Open tasks",
|
|
143
|
+
tasks.text,
|
|
144
|
+
"",
|
|
145
|
+
"## Last compaction summary",
|
|
146
|
+
summary.text.length > 0 ? summary.text : "(this session has not compacted)",
|
|
147
|
+
"",
|
|
148
|
+
"## Question",
|
|
149
|
+
question.text,
|
|
150
|
+
];
|
|
151
|
+
const whole = clamp(sections.join("\n"), ORACLE_DIGEST_MAX_BYTES);
|
|
152
|
+
return { text: whole.text, truncated: truncated || whole.truncated };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The bounded task an oracle run is given. The briefing carries the record; this carries the job. */
|
|
156
|
+
export const ORACLE_TASK =
|
|
157
|
+
"Answer the question in the briefing's Question section. Check it against the settled decisions first, " +
|
|
158
|
+
"then return the strongest challenge you can support and the evidence that would change your mind.";
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The advisor's structured answer as the prose the operator note carries. The
|
|
162
|
+
* contract keeps the four fields typed so a run cannot half-answer; the note
|
|
163
|
+
* renders them because the main agent reads prose, not a payload.
|
|
164
|
+
*/
|
|
165
|
+
export function formatOracleAnswer(result: OracleResult): string {
|
|
166
|
+
const cited =
|
|
167
|
+
result.citedDecisions.length === 0
|
|
168
|
+
? "Cited decisions: none bear on this question."
|
|
169
|
+
: `Cited decisions: ${result.citedDecisions.join(", ")}`;
|
|
170
|
+
return [
|
|
171
|
+
`Verdict: ${result.verdict}`,
|
|
172
|
+
"",
|
|
173
|
+
`Strongest challenge: ${result.challenge}`,
|
|
174
|
+
"",
|
|
175
|
+
`What would change its mind: ${result.changesMyMind}`,
|
|
176
|
+
"",
|
|
177
|
+
cited,
|
|
178
|
+
].join("\n");
|
|
179
|
+
}
|
|
@@ -14,6 +14,11 @@ export interface OverlayAskUserLifecycleDeps {
|
|
|
14
14
|
requestRender(): void;
|
|
15
15
|
registerHandler?(handler: AskUserHandler): () => void;
|
|
16
16
|
openAskUserOverlay?: typeof openAskUserOverlay;
|
|
17
|
+
/**
|
|
18
|
+
* An ask_user request parked waiting for the operator. Fired when the
|
|
19
|
+
* overlay session opens, not per question, so one interview notifies once.
|
|
20
|
+
*/
|
|
21
|
+
onOperatorParked?(): void;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
export interface OverlayAskUserLifecycle {
|
|
@@ -61,6 +66,7 @@ export function createOverlayAskUserLifecycle(deps: OverlayAskUserLifecycleDeps)
|
|
|
61
66
|
deps.setOverlayState("ask-user");
|
|
62
67
|
session = openSession(deps.tui, { onCancel: () => pendingCancel?.() });
|
|
63
68
|
deps.setOverlayHandle(session);
|
|
69
|
+
deps.onOperatorParked?.();
|
|
64
70
|
deps.requestRender();
|
|
65
71
|
return session;
|
|
66
72
|
};
|
|
@@ -77,7 +83,7 @@ export function createOverlayAskUserLifecycle(deps: OverlayAskUserLifecycleDeps)
|
|
|
77
83
|
const activeSession = ensureSession();
|
|
78
84
|
if (!activeSession) return cancelledAskUserResult();
|
|
79
85
|
pendingCancel = cancel;
|
|
80
|
-
const result = await activeSession.ask(questions);
|
|
86
|
+
const result = await activeSession.ask(questions, invokeOptions?.decisionPresentation);
|
|
81
87
|
if (result.cancelled === true || !toolBacked) {
|
|
82
88
|
if (result.cancelled === true) cancelledForTurn = true;
|
|
83
89
|
close();
|
|
@@ -478,16 +478,19 @@ export function showClioOverlayFrame(
|
|
|
478
478
|
const { title, footerHint, tone, width, visible, maxHeight, margin, ...overlayOptions } = options;
|
|
479
479
|
const boxWidth = typeof width === "number" ? width : 0;
|
|
480
480
|
const frame = new ClioOverlayFrame(child, title, footerHint, boxWidth, frameAlignForAnchor(options.anchor), tone);
|
|
481
|
-
const marginRows = typeof margin === "number" ? margin * 2 : (margin?.top ?? 0) + (margin?.bottom ?? 0);
|
|
482
481
|
return tui.showOverlay(frame, {
|
|
483
482
|
...overlayOptions,
|
|
484
483
|
...(margin !== undefined ? { margin } : {}),
|
|
485
484
|
width: "100%",
|
|
486
485
|
visible: (termWidth, termHeight) => {
|
|
486
|
+
const shown = visible ? visible(termWidth, termHeight) : true;
|
|
487
|
+
// A caller may update an object margin in its visibility callback to
|
|
488
|
+
// follow live content geometry. Read it after that callback, every frame.
|
|
489
|
+
const marginRows = typeof margin === "number" ? margin * 2 : (margin?.top ?? 0) + (margin?.bottom ?? 0);
|
|
487
490
|
const available = Math.max(1, termHeight - marginRows);
|
|
488
491
|
const requested = resolveRowSize(maxHeight, termHeight);
|
|
489
492
|
frame.setRowBudget(requested === null ? available : Math.min(requested, available));
|
|
490
|
-
return
|
|
493
|
+
return shown;
|
|
491
494
|
},
|
|
492
495
|
});
|
|
493
496
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import type { ClioSettings } from "../core/config.js";
|
|
1
3
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
4
|
+
import type { AgentsContract } from "../domains/agents/contract.js";
|
|
2
5
|
import { foldWorkingSet } from "../domains/context/working-set/fold.js";
|
|
3
|
-
import type { DispatchContract } from "../domains/dispatch/index.js";
|
|
4
|
-
import {
|
|
6
|
+
import type { DispatchContract, DispatchRequest } from "../domains/dispatch/index.js";
|
|
7
|
+
import { agentRoleFactsResolver, executeFleetRun, requestExecutionRole } from "../domains/dispatch/index.js";
|
|
8
|
+
import {
|
|
9
|
+
canonicalMemoryRepositoryIdentity,
|
|
10
|
+
loadMemoryRecordsSync,
|
|
11
|
+
type MemoryRecord,
|
|
12
|
+
proposeMemoryPromotion,
|
|
13
|
+
} from "../domains/memory/index.js";
|
|
5
14
|
import type { ObservabilityContract } from "../domains/observability/index.js";
|
|
6
15
|
import type { ContextLedger } from "../domains/session/context-ledger.js";
|
|
7
16
|
import type { SessionMeta } from "../domains/session/index.js";
|
|
@@ -17,6 +26,7 @@ import {
|
|
|
17
26
|
isDispatchBoardRowCancellable,
|
|
18
27
|
isDispatchBoardRowSteerable,
|
|
19
28
|
} from "./dispatch-board.js";
|
|
29
|
+
import { compileFleetRunPreview, type FleetRunPreview, type FleetRunPreviewInput } from "./fleet-run-preview.js";
|
|
20
30
|
import { openMemoryOverlay } from "./memory-overlay.js";
|
|
21
31
|
import { buildHint, showClioOverlayFrame } from "./overlay-frame.js";
|
|
22
32
|
import type { OverlayTransitions } from "./overlay-transitions.js";
|
|
@@ -26,6 +36,8 @@ import {
|
|
|
26
36
|
openContextResetOverlay,
|
|
27
37
|
} from "./overlays/context-reset.js";
|
|
28
38
|
import { formatDecisionCorrectionTurn, openDecisionsOverlay } from "./overlays/decisions.js";
|
|
39
|
+
import { openFleetRunApprovalOverlay } from "./overlays/fleet-run-approval.js";
|
|
40
|
+
import { openSideQuestionOverlay } from "./overlays/side-question.js";
|
|
29
41
|
import { type ContextClearCommandOptions, formatUserTaskHandoff } from "./slash-commands.js";
|
|
30
42
|
import { openTasksOverlay } from "./tasks-overlay.js";
|
|
31
43
|
import { type ArtifactProviderDeps, createDefaultArtifactProviders } from "./view/artifacts.js";
|
|
@@ -57,6 +69,8 @@ export interface OverlayGeneralOpenersDeps {
|
|
|
57
69
|
stateDir: string;
|
|
58
70
|
getSessionMeta: () => SessionMeta | null;
|
|
59
71
|
readSessionEntries?: ArtifactProviderDeps["readSessionEntries"];
|
|
72
|
+
/** Live settings, so `/context` can state the configured working-set policy. */
|
|
73
|
+
getSettings?: () => Readonly<ClioSettings>;
|
|
60
74
|
terminal: { columns: number };
|
|
61
75
|
dispatchBoard: ReturnType<typeof createDispatchBoardView>;
|
|
62
76
|
startDispatchBoardTicker: () => void;
|
|
@@ -69,6 +83,37 @@ export interface OverlayGeneralOpenersDeps {
|
|
|
69
83
|
openDecisionsOverlay?: typeof openDecisionsOverlay;
|
|
70
84
|
openMemoryOverlay?: typeof openMemoryOverlay;
|
|
71
85
|
openViewOverlay?: typeof openViewOverlay;
|
|
86
|
+
openSideQuestionOverlay?: typeof openSideQuestionOverlay;
|
|
87
|
+
openFleetRunApprovalOverlay?: typeof openFleetRunApprovalOverlay;
|
|
88
|
+
/** Recipe registry `/fleet run` resolves every step's agent against. */
|
|
89
|
+
agents?: AgentsContract;
|
|
90
|
+
/** Session budget state the run would be admitted under; absent leaves it unknown. */
|
|
91
|
+
getBudgetPreflight?: () => { ceilingUsd: number; currentUsd: number; verdict: "under" | "at" | "over" };
|
|
92
|
+
/**
|
|
93
|
+
* True while a turn is streaming. `/fleet run` is refused then, never
|
|
94
|
+
* queued: an approved plan describes the workspace as it stands, and a turn
|
|
95
|
+
* still in flight is about to change it.
|
|
96
|
+
*/
|
|
97
|
+
isTurnInFlight?: () => boolean;
|
|
98
|
+
/** Record which wave and step a dispatched run belongs to, for the board's phase column. */
|
|
99
|
+
setFleetRunPhase?: (runId: string, phase: { wave: number; stepId: string }) => void;
|
|
100
|
+
/** The shared fleet-run path; overridable so a test can observe one call. */
|
|
101
|
+
runFleet?: typeof executeFleetRun;
|
|
102
|
+
/** Contract and command-registry loader; overridable so a test projects a fixture. */
|
|
103
|
+
loadFleetSources?: FleetRunPreviewInput["load"];
|
|
104
|
+
/**
|
|
105
|
+
* Run one `/btw` round. Absent on a host with no chat loop, in which case
|
|
106
|
+
* `/btw` reports that instead of opening an empty overlay.
|
|
107
|
+
*/
|
|
108
|
+
askSideQuestion?: (
|
|
109
|
+
question: string,
|
|
110
|
+
options: { signal: AbortSignal; onDelta: (partialText: string) => void },
|
|
111
|
+
) => Promise<
|
|
112
|
+
| { status: "answered"; text: string }
|
|
113
|
+
| { status: "aborted"; text: string }
|
|
114
|
+
| { status: "refused"; reason: string }
|
|
115
|
+
| { status: "failed"; reason: string }
|
|
116
|
+
>;
|
|
72
117
|
}
|
|
73
118
|
|
|
74
119
|
export interface OverlayGeneralOpeners {
|
|
@@ -81,6 +126,9 @@ export interface OverlayGeneralOpeners {
|
|
|
81
126
|
openMemory(): void;
|
|
82
127
|
openView(initialFilter?: string): void;
|
|
83
128
|
toggleDispatchBoard(): void;
|
|
129
|
+
openSideQuestion(question: string): void;
|
|
130
|
+
/** `/fleet run <name> [--var k=v ...]`: preview the plan, then dispatch on approval. */
|
|
131
|
+
startFleetRun(name: string, vars: Readonly<Record<string, string>>): void;
|
|
84
132
|
}
|
|
85
133
|
|
|
86
134
|
export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): OverlayGeneralOpeners {
|
|
@@ -92,6 +140,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
92
140
|
const openMemoryOverlayFactory = deps.openMemoryOverlay ?? openMemoryOverlay;
|
|
93
141
|
const openViewOverlayFactory = deps.openViewOverlay ?? openViewOverlay;
|
|
94
142
|
const showOverlayFrameFactory = deps.showOverlayFrame ?? showClioOverlayFrame;
|
|
143
|
+
const openSideQuestionOverlayFactory = deps.openSideQuestionOverlay ?? openSideQuestionOverlay;
|
|
95
144
|
|
|
96
145
|
const openCost = (): void => {
|
|
97
146
|
if (deps.transitions.state !== "closed") return;
|
|
@@ -113,6 +162,10 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
113
162
|
if (!readSessionEntries) return null;
|
|
114
163
|
return foldWorkingSet(readSessionEntries(), deps.getSessionMeta()?.pinnedLeafTurnId ?? undefined);
|
|
115
164
|
},
|
|
165
|
+
getWorkingSetConfig: () => {
|
|
166
|
+
const workingSet = deps.getSettings?.().context.workingSet;
|
|
167
|
+
return workingSet ? { enabled: workingSet.enabled, policy: workingSet.policy } : null;
|
|
168
|
+
},
|
|
116
169
|
});
|
|
117
170
|
deps.requestRender();
|
|
118
171
|
};
|
|
@@ -218,6 +271,32 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
218
271
|
deps.transitions.state = "memory";
|
|
219
272
|
deps.transitions.handle = openMemoryOverlayFactory(deps.tui, deps.getTaskMemoryStatus, () => records, {
|
|
220
273
|
onClose: deps.closeOverlay,
|
|
274
|
+
onPromote: async (entry, scope) => {
|
|
275
|
+
const meta = deps.getSessionMeta();
|
|
276
|
+
if (!meta) throw new Error("memory promotion requires an active session");
|
|
277
|
+
const selection =
|
|
278
|
+
scope === "global"
|
|
279
|
+
? ({ scope: "global", acknowledgeGlobal: true } as const)
|
|
280
|
+
: (() => {
|
|
281
|
+
const repository = canonicalMemoryRepositoryIdentity(meta.cwd);
|
|
282
|
+
if (repository === null) {
|
|
283
|
+
throw new Error("repo promotion requires a canonical active repository identity");
|
|
284
|
+
}
|
|
285
|
+
return { scope: "repo", repository } as const;
|
|
286
|
+
})();
|
|
287
|
+
const result = await proposeMemoryPromotion(
|
|
288
|
+
deps.dataDir,
|
|
289
|
+
{
|
|
290
|
+
kind: "task-bank-entry",
|
|
291
|
+
sessionId: meta.id,
|
|
292
|
+
evidenceRefs: [`session-${meta.id}`],
|
|
293
|
+
entry,
|
|
294
|
+
},
|
|
295
|
+
selection,
|
|
296
|
+
);
|
|
297
|
+
records = loadMemoryRecordsSync(deps.dataDir);
|
|
298
|
+
return result;
|
|
299
|
+
},
|
|
221
300
|
});
|
|
222
301
|
deps.requestRender();
|
|
223
302
|
};
|
|
@@ -261,11 +340,158 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
261
340
|
const dispatchBoardHint = (): string => {
|
|
262
341
|
const row = deps.dispatchBoard.selectedRow();
|
|
263
342
|
const entries = [{ key: "↑↓", verb: "select" }];
|
|
343
|
+
if (row) entries.push({ key: "Enter", verb: deps.dispatchBoard.detailExpanded() ? "hide detail" : "detail" });
|
|
264
344
|
if (row && isDispatchBoardRowSteerable(row)) entries.push({ key: "s", verb: "steer" });
|
|
265
345
|
if (row && isDispatchBoardRowCancellable(row)) entries.push({ key: "x", verb: "cancel" });
|
|
266
346
|
return buildHint(entries);
|
|
267
347
|
};
|
|
268
348
|
|
|
349
|
+
/**
|
|
350
|
+
* `/btw <question>`: one model round beside the session, rendered in an
|
|
351
|
+
* overlay and nowhere else. Esc aborts a round that is still streaming and
|
|
352
|
+
* closes one that has settled; both paths run through the same abort
|
|
353
|
+
* controller, so a cancelled round never keeps a stream alive behind a closed
|
|
354
|
+
* overlay.
|
|
355
|
+
*/
|
|
356
|
+
const openSideQuestion = (question: string): void => {
|
|
357
|
+
if (deps.transitions.state !== "closed") return;
|
|
358
|
+
const ask = deps.askSideQuestion;
|
|
359
|
+
if (!ask) {
|
|
360
|
+
deps.notify("error", "/btw is not wired in this session", "btw:unavailable");
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const controller = new AbortController();
|
|
364
|
+
deps.transitions.state = "side-question";
|
|
365
|
+
const session = openSideQuestionOverlayFactory(deps.tui, {
|
|
366
|
+
question,
|
|
367
|
+
columns: deps.terminal.columns,
|
|
368
|
+
onClose: () => controller.abort(),
|
|
369
|
+
});
|
|
370
|
+
deps.transitions.handle = session;
|
|
371
|
+
deps.requestRender();
|
|
372
|
+
void ask(question, { signal: controller.signal, onDelta: (partialText) => session.setAnswer(partialText) })
|
|
373
|
+
.then((outcome) => {
|
|
374
|
+
if (outcome.status === "answered") {
|
|
375
|
+
session.settle({ kind: "answered", text: outcome.text });
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (outcome.status === "aborted") {
|
|
379
|
+
session.settle({ kind: "aborted", text: outcome.text });
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
session.settle({ kind: "error", reason: outcome.reason });
|
|
383
|
+
})
|
|
384
|
+
.catch((error: unknown) => {
|
|
385
|
+
session.settle({ kind: "error", reason: error instanceof Error ? error.message : String(error) });
|
|
386
|
+
});
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* `/fleet run <name>`: compile the plan, show it, and dispatch only what the
|
|
391
|
+
* operator accepted.
|
|
392
|
+
*
|
|
393
|
+
* Nothing is dispatched and nothing is written before the accept key. A
|
|
394
|
+
* contract that fails preflight opens the same overlay with its diagnostics
|
|
395
|
+
* and no accept action, so the only exit from a broken plan is to leave it.
|
|
396
|
+
*/
|
|
397
|
+
const startFleetRun = (name: string, vars: Readonly<Record<string, string>>): void => {
|
|
398
|
+
if (deps.transitions.state !== "closed") return;
|
|
399
|
+
// Refused, never queued: an approved plan describes the workspace as it
|
|
400
|
+
// stands, and a turn still in flight is about to change it.
|
|
401
|
+
if (deps.isTurnInFlight?.() === true) {
|
|
402
|
+
deps.notify("warning", "a turn is in flight; /fleet run is refused rather than queued", "fleet-run:in-flight");
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const agents = deps.agents;
|
|
406
|
+
if (!agents) {
|
|
407
|
+
deps.notify("error", "/fleet run needs the agents domain, which is not wired in this session", "fleet-run:agents");
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const workspaceRoot = deps.getSessionMeta()?.cwd ?? process.cwd();
|
|
411
|
+
const roleFacts = agentRoleFactsResolver((id) => agents.getSpec(id));
|
|
412
|
+
const budget = deps.getBudgetPreflight?.();
|
|
413
|
+
const result = compileFleetRunPreview({
|
|
414
|
+
workspaceRoot,
|
|
415
|
+
name,
|
|
416
|
+
vars,
|
|
417
|
+
getAgentSpec: (agentId) => agents.getSpec(agentId),
|
|
418
|
+
roleFacts,
|
|
419
|
+
...(budget ? { budget } : {}),
|
|
420
|
+
...(deps.loadFleetSources ? { load: deps.loadFleetSources } : {}),
|
|
421
|
+
resolveRoute: (step) => {
|
|
422
|
+
const request: DispatchRequest = {
|
|
423
|
+
agentId: step.agentId,
|
|
424
|
+
executionRole: requestExecutionRole({ agentId: step.agentId, resolveFacts: roleFacts }),
|
|
425
|
+
task: step.task,
|
|
426
|
+
...(step.scope === "readonly" ? { autonomy: "read-only" as const } : {}),
|
|
427
|
+
...(step.target !== undefined ? { target: step.target } : {}),
|
|
428
|
+
...(step.profile !== undefined ? { workerProfile: step.profile } : {}),
|
|
429
|
+
};
|
|
430
|
+
const resolution = deps.dispatch.preview?.(request);
|
|
431
|
+
if (!resolution) return null;
|
|
432
|
+
return {
|
|
433
|
+
targetId: resolution.targetId,
|
|
434
|
+
wireModelId: resolution.wireModelId,
|
|
435
|
+
nodeId: resolution.node.id,
|
|
436
|
+
};
|
|
437
|
+
},
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
deps.transitions.state = "fleet-run-approval";
|
|
441
|
+
deps.transitions.handle = (deps.openFleetRunApprovalOverlay ?? openFleetRunApprovalOverlay)(deps.tui, {
|
|
442
|
+
subject: result.ok ? { ok: true, preview: result.preview } : { ok: false, name, diagnostics: result.diagnostics },
|
|
443
|
+
columns: deps.terminal.columns,
|
|
444
|
+
onAccept: () => {
|
|
445
|
+
deps.closeOverlay();
|
|
446
|
+
if (result.ok) dispatchFleetRun(result.preview, agents);
|
|
447
|
+
},
|
|
448
|
+
onCancel: () => {
|
|
449
|
+
deps.closeOverlay();
|
|
450
|
+
deps.notify("info", `fleet ${name}: cancelled; nothing was dispatched`, "fleet-run:cancelled");
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
deps.requestRender();
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const dispatchFleetRun = (preview: FleetRunPreview, agents: AgentsContract): void => {
|
|
457
|
+
const fleetRootId = `fleet-${randomBytes(6).toString("hex")}`;
|
|
458
|
+
const run = deps.runFleet ?? executeFleetRun;
|
|
459
|
+
deps.notify(
|
|
460
|
+
"info",
|
|
461
|
+
`fleet ${preview.name}: root=${fleetRootId} plan=${preview.planHash.slice(0, 12)} steps=${preview.plan.steps.length}`,
|
|
462
|
+
"fleet-run:started",
|
|
463
|
+
);
|
|
464
|
+
void run({
|
|
465
|
+
plan: preview.plan,
|
|
466
|
+
contractName: preview.name,
|
|
467
|
+
commands: preview.commands,
|
|
468
|
+
workspaceRoot: deps.getSessionMeta()?.cwd ?? process.cwd(),
|
|
469
|
+
fleetRootId,
|
|
470
|
+
dispatch: deps.dispatch,
|
|
471
|
+
agents: { getSpec: (agentId) => agents.getSpec(agentId) },
|
|
472
|
+
attributionEnabled: deps.getSettings?.().attribution.gitCommits ?? true,
|
|
473
|
+
vars: preview.vars,
|
|
474
|
+
onStepDispatched: (event) => {
|
|
475
|
+
deps.setFleetRunPhase?.(event.assignmentId, { wave: event.waveIndex, stepId: event.stepId });
|
|
476
|
+
},
|
|
477
|
+
onNotice: (text) => deps.stderr(`[fleet ${preview.name}] ${text}\n`),
|
|
478
|
+
})
|
|
479
|
+
.then((outcome) => {
|
|
480
|
+
deps.notify(
|
|
481
|
+
outcome.cleanRun ? "success" : "warning",
|
|
482
|
+
`fleet ${preview.name}: ${outcome.succeededStepCount}/${outcome.requiredStepCount} steps succeeded, cost $${outcome.totalCostUsd.toFixed(4)}`,
|
|
483
|
+
"fleet-run:settled",
|
|
484
|
+
);
|
|
485
|
+
})
|
|
486
|
+
.catch((error: unknown) => {
|
|
487
|
+
deps.notify(
|
|
488
|
+
"error",
|
|
489
|
+
`fleet ${preview.name}: ${error instanceof Error ? error.message : String(error)}`,
|
|
490
|
+
"fleet-run:failed",
|
|
491
|
+
);
|
|
492
|
+
});
|
|
493
|
+
};
|
|
494
|
+
|
|
269
495
|
return {
|
|
270
496
|
openCost,
|
|
271
497
|
openContextView,
|
|
@@ -276,5 +502,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
276
502
|
openMemory,
|
|
277
503
|
openView,
|
|
278
504
|
toggleDispatchBoard,
|
|
505
|
+
openSideQuestion,
|
|
506
|
+
startFleetRun,
|
|
279
507
|
};
|
|
280
508
|
}
|
|
@@ -25,12 +25,28 @@ export type OverlayState =
|
|
|
25
25
|
| "prompts"
|
|
26
26
|
| "extensions"
|
|
27
27
|
| "interop"
|
|
28
|
-
| "skills-hub"
|
|
28
|
+
| "skills-hub"
|
|
29
|
+
| "side-question"
|
|
30
|
+
| "handoff-review"
|
|
31
|
+
| "fleet-run-approval";
|
|
29
32
|
|
|
30
33
|
export interface PermissionOverlayKeyDeps {
|
|
31
34
|
cancelPermission: () => void;
|
|
32
35
|
confirmPermission: () => void;
|
|
33
36
|
stopTurnFromPermission: () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Whether the composer holds text. Enter must not allow a parked call while
|
|
39
|
+
* it does (issue #186): an operator who typed a message and pressed the
|
|
40
|
+
* habitual send key meant to send, and on a safety rail the ambiguous key
|
|
41
|
+
* resolves away from "allow". Absent means an empty composer.
|
|
42
|
+
*/
|
|
43
|
+
composerHasDraft?: () => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Deliver a draft-editing key to the composer while the prompt owns input,
|
|
46
|
+
* so the draft that makes Enter inert can be cleared without first denying
|
|
47
|
+
* the call. Only the deletion keys in `isDraftEditKey` arrive here.
|
|
48
|
+
*/
|
|
49
|
+
editDraft?: (data: string) => void;
|
|
34
50
|
}
|
|
35
51
|
|
|
36
52
|
export interface DispatchBoardOverlayKeyDeps {
|
|
@@ -39,6 +55,8 @@ export interface DispatchBoardOverlayKeyDeps {
|
|
|
39
55
|
selectNextDispatch: () => void;
|
|
40
56
|
steerSelectedDispatch: () => void;
|
|
41
57
|
cancelSelectedDispatch: () => void;
|
|
58
|
+
/** Open or close the selected run's worker-progress detail. */
|
|
59
|
+
toggleSelectedDispatchDetail: () => void;
|
|
42
60
|
}
|
|
43
61
|
|
|
44
62
|
interface CloseOverlayKeyDeps {
|
|
@@ -55,10 +73,29 @@ export function isEscapeKey(data: string): boolean {
|
|
|
55
73
|
return matchesKey(data, "escape") && !isKeyRelease(data);
|
|
56
74
|
}
|
|
57
75
|
|
|
76
|
+
const DRAFT_EDIT_KEYS = ["backspace", "delete", "ctrl+u", "ctrl+w", "alt+backspace", "ctrl+k"] as const;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The keys that only ever remove text from the composer. They are the one
|
|
80
|
+
* class of input a pending permission prompt lets through, because they
|
|
81
|
+
* cannot type a command, cannot submit, and are the way out of the inert-Enter
|
|
82
|
+
* state a draft puts the prompt in.
|
|
83
|
+
*/
|
|
84
|
+
function isDraftEditKey(data: string): boolean {
|
|
85
|
+
return !isKeyRelease(data) && DRAFT_EDIT_KEYS.some((key) => matchesKey(data, key));
|
|
86
|
+
}
|
|
87
|
+
|
|
58
88
|
/** Pure permission overlay key router: returns true when the input was consumed. */
|
|
59
89
|
export function routePermissionOverlayKey(data: string, deps: PermissionOverlayKeyDeps): boolean {
|
|
60
90
|
if (matchesKey(data, "enter") && !isKeyRelease(data)) {
|
|
61
|
-
|
|
91
|
+
// Consumed either way: with a draft the press changes nothing, and the
|
|
92
|
+
// composer rail says what clears it. Letting it fall through would hand
|
|
93
|
+
// Enter to the editor, which is the send path this guard exists to block.
|
|
94
|
+
if (!(deps.composerHasDraft?.() ?? false)) deps.confirmPermission();
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
if (deps.editDraft && isDraftEditKey(data)) {
|
|
98
|
+
deps.editDraft(data);
|
|
62
99
|
return true;
|
|
63
100
|
}
|
|
64
101
|
if (isEscapeKey(data)) {
|
|
@@ -98,6 +135,12 @@ export function routeDispatchBoardOverlayKey(data: string, deps: DispatchBoardOv
|
|
|
98
135
|
deps.cancelSelectedDispatch();
|
|
99
136
|
return true;
|
|
100
137
|
}
|
|
138
|
+
// Worker progress is expanded detail the operator asks for. The default list
|
|
139
|
+
// stays compact so a fan-out of scouts costs one card each.
|
|
140
|
+
if (matchesKey(data, "enter")) {
|
|
141
|
+
deps.toggleSelectedDispatchDetail();
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
101
144
|
return false;
|
|
102
145
|
}
|
|
103
146
|
|
|
@@ -180,6 +223,19 @@ export function routeOverlayKey(
|
|
|
180
223
|
routeCostOverlayKey(data, deps);
|
|
181
224
|
return true;
|
|
182
225
|
}
|
|
226
|
+
// Esc is the only key the side-question overlay answers: it aborts a round
|
|
227
|
+
// that is still streaming and closes one that has settled. Everything else is
|
|
228
|
+
// swallowed so a keystroke cannot reach the composer behind it.
|
|
229
|
+
if (overlayState === "side-question") {
|
|
230
|
+
routeCostOverlayKey(data, deps);
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
// The handoff review overlay owns Enter, `e`, the arrows, and Esc itself, so
|
|
234
|
+
// every key goes to its own focus box rather than through the router.
|
|
235
|
+
if (overlayState === "handoff-review") return false;
|
|
236
|
+
// The fleet-run approval overlay owns Enter, the arrows, and Esc itself, so
|
|
237
|
+
// every key goes to its own focus box rather than through the router.
|
|
238
|
+
if (overlayState === "fleet-run-approval") return false;
|
|
183
239
|
if (overlayState === "context-reset") return false;
|
|
184
240
|
if (overlayState === "tasks") return false;
|
|
185
241
|
if (overlayState === "decisions") return false;
|