@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LibraryEntryKind } from "../domains/resources/index.js";
|
|
1
2
|
import { appendNotice } from "./command-output.js";
|
|
2
3
|
import { createOverlayAskUserLifecycle, type OverlayAskUserLifecycle } from "./overlay-ask-user-lifecycle.js";
|
|
3
4
|
import { createOverlayAuthLifecycle } from "./overlay-auth-lifecycle.js";
|
|
@@ -12,7 +13,9 @@ import {
|
|
|
12
13
|
createPermissionOverlayBody,
|
|
13
14
|
PERMISSION_OVERLAY_WIDTH,
|
|
14
15
|
permissionOverlayHint,
|
|
16
|
+
permissionOverlayPlacement,
|
|
15
17
|
permissionOverlayTitle,
|
|
18
|
+
permissionOverlayTone,
|
|
16
19
|
} from "./permission-overlay.js";
|
|
17
20
|
|
|
18
21
|
export * from "./overlay-key-routing.js";
|
|
@@ -24,6 +27,7 @@ import type { SettingsCenterRowId, SettingsSectionId } from "./overlays/settings
|
|
|
24
27
|
// application composition root no longer owns the mutable overlay state.
|
|
25
28
|
export type OverlayLifecycleApplicationDeps = Pick<
|
|
26
29
|
import("./interactive-application.js").InteractiveDeps,
|
|
30
|
+
| "agents"
|
|
27
31
|
| "bus"
|
|
28
32
|
| "chat"
|
|
29
33
|
| "commitSetting"
|
|
@@ -40,12 +44,14 @@ export type OverlayLifecycleApplicationDeps = Pick<
|
|
|
40
44
|
| "observability"
|
|
41
45
|
| "onContextClear"
|
|
42
46
|
| "onForkSession"
|
|
47
|
+
| "onNewSession"
|
|
43
48
|
| "onResumeSession"
|
|
44
49
|
| "onSelectModel"
|
|
45
50
|
| "providers"
|
|
46
51
|
| "readSessionEntries"
|
|
47
52
|
| "registerAskUserHandler"
|
|
48
53
|
| "resources"
|
|
54
|
+
| "scheduling"
|
|
49
55
|
| "session"
|
|
50
56
|
| "stateDir"
|
|
51
57
|
| "supersedeDecision"
|
|
@@ -73,17 +79,30 @@ export interface OverlayLifecycleRuntimeDeps {
|
|
|
73
79
|
notify: (level: import("./interactive-subscriptions.js").InteractiveNoticeLevel, text: string, key?: string) => void;
|
|
74
80
|
terminal: Pick<import("../engine/tui.js").ProcessTerminal, "columns">;
|
|
75
81
|
dispatchBoard: ReturnType<typeof import("./dispatch-board.js").createDispatchBoardView>;
|
|
82
|
+
/** Record a dispatched fleet step's plan position for the board's phase column. */
|
|
83
|
+
setFleetRunPhase?: (runId: string, phase: { wave: number; stepId: string }) => void;
|
|
76
84
|
chatPanel: import("./chat-panel.js").ChatPanel;
|
|
77
85
|
/** Clears the transcript and every view folded alongside it; the session overlays call it before a replay. */
|
|
78
86
|
resetTranscript: () => void;
|
|
87
|
+
/**
|
|
88
|
+
* Stop and restart the terminal around a child process that owns the screen.
|
|
89
|
+
* `/handoff` uses it for the external editor, the same way the composer's
|
|
90
|
+
* own `$EDITOR` opener does.
|
|
91
|
+
*/
|
|
92
|
+
suspendTerminal: <T>(run: () => T) => T;
|
|
79
93
|
io: import("./slash-commands.js").RunIo;
|
|
80
94
|
readStructuredEntries: (sessionId: string) => import("../domains/session/index.js").SessionEntry[];
|
|
81
95
|
announceTaskMemorySeedOffer: () => void;
|
|
82
96
|
/** Rescopes the footer's last-turn line when a session overlay changes the branch. */
|
|
83
97
|
setLastTurnSummary?: (summary: import("./status/index.js").TurnSummary | null) => void;
|
|
84
98
|
keybindings: ReturnType<typeof import("./keybinding-manager.js").createKeybindingManager>;
|
|
85
|
-
editor: Pick<import("./clio-editor.js").ClioEditor, "getText" | "setText">;
|
|
99
|
+
editor: Pick<import("./clio-editor.js").ClioEditor, "getText" | "render" | "setText">;
|
|
86
100
|
getSlashContext: () => import("./slash-commands.js").SlashCommandContext;
|
|
101
|
+
/**
|
|
102
|
+
* A worker permission or ask_user request parked waiting for the operator.
|
|
103
|
+
* Wired to the desktop notification; absent hosts simply do not notify.
|
|
104
|
+
*/
|
|
105
|
+
onOperatorParked?: () => void;
|
|
87
106
|
showOverlayFrame?: typeof showClioOverlayFrame;
|
|
88
107
|
openAuthDialog?: typeof import("./overlays/auth-dialog.js").openAuthDialog;
|
|
89
108
|
openAskUserOverlay?: typeof import("./overlays/ask-user.js").openAskUserOverlay;
|
|
@@ -125,6 +144,11 @@ export interface OverlayLifecycleController {
|
|
|
125
144
|
openDecisionsOverlayState(): void;
|
|
126
145
|
openMemoryOverlayState(): void;
|
|
127
146
|
openViewOverlayState(initialFilter?: string): void;
|
|
147
|
+
/** `/btw <question>`: one side-question round rendered in its own overlay. */
|
|
148
|
+
openSideQuestionOverlayState(question: string): void;
|
|
149
|
+
/** `/handoff <goal>`: extract, review, and seed a successor session. */
|
|
150
|
+
startHandoffState(goal: string): void;
|
|
151
|
+
startFleetRunState(name: string, vars: Readonly<Record<string, string>>): void;
|
|
128
152
|
openModelOverlayState(): void;
|
|
129
153
|
openSettingsOverlayState(section?: SettingsSectionId, rowId?: SettingsCenterRowId): void;
|
|
130
154
|
openResumeOverlayState(): void;
|
|
@@ -132,7 +156,7 @@ export interface OverlayLifecycleController {
|
|
|
132
156
|
openMessagePickerOverlayState(): void;
|
|
133
157
|
openHelpOverlayState(query?: string): void;
|
|
134
158
|
openAgentsOverlayState(): void;
|
|
135
|
-
openSkillsHubState(): void;
|
|
159
|
+
openSkillsHubState(tab?: LibraryEntryKind): void;
|
|
136
160
|
openPromptsOverlayState(): void;
|
|
137
161
|
openExtensionsOverlayState(): void;
|
|
138
162
|
openInteropOverlayState(): void;
|
|
@@ -194,6 +218,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
194
218
|
cancelPendingAskUser: () => overlayAskUser?.cancelPending() ?? false,
|
|
195
219
|
finishAuth: (dismiss) => overlayAuth.finish(dismiss),
|
|
196
220
|
onPermissionOverlayClosed: () => overlayPermission?.onPermissionOverlayClosed(),
|
|
221
|
+
onOverlayClosed: () => overlayPermission?.retryPending(),
|
|
197
222
|
});
|
|
198
223
|
const closeOverlay = overlayTransitions.close;
|
|
199
224
|
|
|
@@ -227,10 +252,13 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
227
252
|
if (overlayTransitions.state !== "closed") return false;
|
|
228
253
|
overlayTransitions.state = "permission-confirm";
|
|
229
254
|
overlayTransitions.handle = showOverlayFrame(tui, createPermissionOverlayBody(view), {
|
|
230
|
-
|
|
255
|
+
...permissionOverlayPlacement(tui, editor, footer.view),
|
|
231
256
|
width: PERMISSION_OVERLAY_WIDTH,
|
|
232
|
-
title: permissionOverlayTitle(),
|
|
233
|
-
|
|
257
|
+
title: permissionOverlayTitle(view),
|
|
258
|
+
tone: permissionOverlayTone(view),
|
|
259
|
+
// Read per frame: the footer names what Enter does right now, and
|
|
260
|
+
// that depends on whether the composer holds a draft.
|
|
261
|
+
footerHint: (innerWidth) => permissionOverlayHint(innerWidth, editor.getText().length > 0),
|
|
234
262
|
});
|
|
235
263
|
tui.requestRender();
|
|
236
264
|
return true;
|
|
@@ -247,6 +275,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
247
275
|
source: "stream_cancel",
|
|
248
276
|
auditReason: "operator stopped the turn at a permission prompt",
|
|
249
277
|
}),
|
|
278
|
+
...(deps.onOperatorParked ? { onOperatorParked: deps.onOperatorParked } : {}),
|
|
250
279
|
});
|
|
251
280
|
|
|
252
281
|
overlayAskUser = createOverlayAskUserLifecycle({
|
|
@@ -264,6 +293,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
264
293
|
requestRender: () => tui.requestRender(),
|
|
265
294
|
...(deps.app.registerAskUserHandler ? { registerHandler: deps.app.registerAskUserHandler } : {}),
|
|
266
295
|
...(openAskUserOverlayFactory ? { openAskUserOverlay: openAskUserOverlayFactory } : {}),
|
|
296
|
+
...(deps.onOperatorParked ? { onOperatorParked: deps.onOperatorParked } : {}),
|
|
267
297
|
});
|
|
268
298
|
|
|
269
299
|
const overlayModelSelectors = createOverlayModelSelectors({
|
|
@@ -312,6 +342,10 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
312
342
|
getSlashNotice: () => deps.getSlashContext().notice,
|
|
313
343
|
...(deps.app.onResumeSession ? { onResumeSession: deps.app.onResumeSession } : {}),
|
|
314
344
|
...(deps.app.onForkSession ? { onForkSession: deps.app.onForkSession } : {}),
|
|
345
|
+
...(deps.app.onNewSession ? { onNewSession: deps.app.onNewSession } : {}),
|
|
346
|
+
...(deps.app.getDecisionBoard ? { getDecisionBoard: deps.app.getDecisionBoard } : {}),
|
|
347
|
+
terminal: deps.terminal,
|
|
348
|
+
suspendTerminal: deps.suspendTerminal,
|
|
315
349
|
announceTaskMemorySeedOffer,
|
|
316
350
|
sessionUsage: deps.app.observability,
|
|
317
351
|
...(setLastTurnSummary ? { setLastTurnSummary } : {}),
|
|
@@ -325,6 +359,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
325
359
|
...(openCwdFallbackOverlayFactory ? { openCwdFallbackOverlay: openCwdFallbackOverlayFactory } : {}),
|
|
326
360
|
});
|
|
327
361
|
|
|
362
|
+
const scheduling = deps.app.scheduling;
|
|
328
363
|
const overlayGeneralOpeners = createOverlayGeneralOpeners({
|
|
329
364
|
tui,
|
|
330
365
|
transitions: overlayTransitions,
|
|
@@ -351,6 +386,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
351
386
|
stateDir: deps.app.stateDir,
|
|
352
387
|
getSessionMeta: () => deps.app.session?.current() ?? null,
|
|
353
388
|
...(deps.app.readSessionEntries ? { readSessionEntries: deps.app.readSessionEntries } : {}),
|
|
389
|
+
...(deps.app.getSettings ? { getSettings: deps.app.getSettings } : {}),
|
|
354
390
|
terminal,
|
|
355
391
|
dispatchBoard,
|
|
356
392
|
startDispatchBoardTicker: () => interactiveTickers.startDispatchBoardTicker(),
|
|
@@ -363,6 +399,11 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
363
399
|
...(openDecisionsOverlayFactory ? { openDecisionsOverlay: openDecisionsOverlayFactory } : {}),
|
|
364
400
|
...(openMemoryOverlayFactory ? { openMemoryOverlay: openMemoryOverlayFactory } : {}),
|
|
365
401
|
...(openViewOverlayFactory ? { openViewOverlay: openViewOverlayFactory } : {}),
|
|
402
|
+
askSideQuestion: (question, options) => deps.app.chat.askSideQuestion(question, options),
|
|
403
|
+
...(deps.app.agents ? { agents: deps.app.agents } : {}),
|
|
404
|
+
...(scheduling ? { getBudgetPreflight: () => scheduling.preflight() } : {}),
|
|
405
|
+
isTurnInFlight: () => deps.app.chat.isStreaming(),
|
|
406
|
+
...(deps.setFleetRunPhase ? { setFleetRunPhase: deps.setFleetRunPhase } : {}),
|
|
366
407
|
});
|
|
367
408
|
|
|
368
409
|
const openResumeOverlayState = overlaySessions.openResume;
|
|
@@ -376,6 +417,9 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
376
417
|
const openDecisionsOverlayState = overlayGeneralOpeners.openDecisions;
|
|
377
418
|
const openMemoryOverlayState = overlayGeneralOpeners.openMemory;
|
|
378
419
|
const openViewOverlayState = overlayGeneralOpeners.openView;
|
|
420
|
+
const openSideQuestionOverlayState = overlayGeneralOpeners.openSideQuestion;
|
|
421
|
+
const startFleetRunState = overlayGeneralOpeners.startFleetRun;
|
|
422
|
+
const startHandoffState = overlaySessions.startHandoff;
|
|
379
423
|
const toggleDispatchBoardOverlay = overlayGeneralOpeners.toggleDispatchBoard;
|
|
380
424
|
|
|
381
425
|
return {
|
|
@@ -395,6 +439,9 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
395
439
|
openDecisionsOverlayState,
|
|
396
440
|
openMemoryOverlayState,
|
|
397
441
|
openViewOverlayState,
|
|
442
|
+
openSideQuestionOverlayState,
|
|
443
|
+
startFleetRunState,
|
|
444
|
+
startHandoffState,
|
|
398
445
|
openModelOverlayState: overlayModelSelectors.openModelOverlayState,
|
|
399
446
|
openSettingsOverlayState: overlayModelSelectors.openSettingsOverlayState,
|
|
400
447
|
openResumeOverlayState,
|
|
@@ -2,9 +2,11 @@ import { BusChannels, type PermissionRequestedPayload } from "../core/bus-events
|
|
|
2
2
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
3
3
|
import { ToolNames } from "../core/tool-names.js";
|
|
4
4
|
import type { DispatchContract } from "../domains/dispatch/contract.js";
|
|
5
|
-
import type { ClassifierCall } from "../domains/safety/action-classifier.js";
|
|
5
|
+
import type { ActionClass, ClassifierCall } from "../domains/safety/action-classifier.js";
|
|
6
6
|
import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
7
7
|
import type { SafetyDecision } from "../domains/safety/contract.js";
|
|
8
|
+
import { decisionActionClass } from "../domains/safety/decision-presentation.js";
|
|
9
|
+
import { askUserExposure } from "../tools/ask-user.js";
|
|
8
10
|
import type { PermissionRequiredMeta, ToolRegistry } from "../tools/registry.js";
|
|
9
11
|
import { approvalParkedNotice, autonomyDeniedNotice, workerEscalationNotice } from "./bus-notices.js";
|
|
10
12
|
import type { ToolApprovalStateEvent } from "./chat-loop.js";
|
|
@@ -29,7 +31,7 @@ interface WorkerEscalationEntry {
|
|
|
29
31
|
requestId: string;
|
|
30
32
|
agentId: string;
|
|
31
33
|
tool: string;
|
|
32
|
-
actionClass:
|
|
34
|
+
actionClass: ActionClass;
|
|
33
35
|
axis: ApprovalRequestView["axis"];
|
|
34
36
|
reason: string;
|
|
35
37
|
target?: string;
|
|
@@ -48,6 +50,12 @@ export interface OverlayPermissionLifecycleDeps {
|
|
|
48
50
|
requestRender(): void;
|
|
49
51
|
/** End the in-flight run, carrying the text the operator will see. */
|
|
50
52
|
stopActiveTurn(reason: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* A worker permission request parked waiting for the operator. Fired once per
|
|
55
|
+
* request, on the same dedup the operator-facing surfaces use, so a
|
|
56
|
+
* re-presented dialog does not notify twice.
|
|
57
|
+
*/
|
|
58
|
+
onOperatorParked?(): void;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
export interface OverlayPermissionLifecycle {
|
|
@@ -60,6 +68,14 @@ export interface OverlayPermissionLifecycle {
|
|
|
60
68
|
*/
|
|
61
69
|
stopTurn(): void;
|
|
62
70
|
onPermissionOverlayClosed(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Re-present whatever is still parked once no overlay holds the screen. A
|
|
73
|
+
* request that arrived while `/context` or a picker was open could only be
|
|
74
|
+
* announced, and nothing re-attempted the dialog when that overlay closed:
|
|
75
|
+
* the transcript said awaiting approval, the footer said confirm, and the
|
|
76
|
+
* operator had no key to press (issue #186).
|
|
77
|
+
*/
|
|
78
|
+
retryPending(): void;
|
|
63
79
|
dispose(): void;
|
|
64
80
|
}
|
|
65
81
|
|
|
@@ -114,7 +130,7 @@ function mainApprovalRequestView(
|
|
|
114
130
|
(axisFromDecision.kind === "net"
|
|
115
131
|
? { kind: "net" as const, ruleId: axisFromDecision.ruleId }
|
|
116
132
|
: { kind: "autonomy" as const, level: autonomy });
|
|
117
|
-
const target = describeCallTarget(call.args);
|
|
133
|
+
const target = describeCallTarget(call.tool, call.args);
|
|
118
134
|
return {
|
|
119
135
|
requestId: meta?.requestId ?? "permission-pending",
|
|
120
136
|
tool: call.tool,
|
|
@@ -126,6 +142,7 @@ function mainApprovalRequestView(
|
|
|
126
142
|
...(call.tool === ToolNames.Dispatch && decision.kind === "ask"
|
|
127
143
|
? { artifact: { kind: "dispatch-plan" as const, text: decision.rejection.detail } }
|
|
128
144
|
: {}),
|
|
145
|
+
...(call.tool === ToolNames.AskUser ? { exposure: askUserExposure(call.args) } : {}),
|
|
129
146
|
...(target.length > 0 ? { target } : {}),
|
|
130
147
|
...(queueDepth !== undefined && queueDepth > 1 ? { queueDepth } : {}),
|
|
131
148
|
};
|
|
@@ -141,7 +158,7 @@ function workerEscalationEntry(payload: PermissionRequestedPayload, autonomy: st
|
|
|
141
158
|
requestId: payload.requestId,
|
|
142
159
|
agentId: typeof payload.agentId === "string" ? payload.agentId : "worker",
|
|
143
160
|
tool: typeof payload.tool === "string" ? payload.tool : "unknown",
|
|
144
|
-
actionClass:
|
|
161
|
+
actionClass: decisionActionClass(payload.actionClass),
|
|
145
162
|
axis:
|
|
146
163
|
axisViewFromId(typeof payload.axis === "string" ? payload.axis : undefined, autonomy) ??
|
|
147
164
|
(typeof payload.ruleId === "string"
|
|
@@ -173,6 +190,8 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
|
|
|
173
190
|
let pendingWorker: Pick<WorkerEscalationEntry, "agentId" | "requestId" | "runId"> | null = null;
|
|
174
191
|
const workerQueue: WorkerEscalationEntry[] = [];
|
|
175
192
|
const announcedRequestIds = new Set<string>();
|
|
193
|
+
/** Worker escalations already surfaced, so one request parks the operator once. */
|
|
194
|
+
const announcedWorkerRequestIds = new Set<string>();
|
|
176
195
|
let confirmed = false;
|
|
177
196
|
let stopping = false;
|
|
178
197
|
|
|
@@ -188,6 +207,12 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
|
|
|
188
207
|
const next = workerQueue.shift();
|
|
189
208
|
if (next) openWorker(next);
|
|
190
209
|
};
|
|
210
|
+
const retryPending = (): void => {
|
|
211
|
+
maybeOpenWorker();
|
|
212
|
+
if (deps.getOverlayState() === "closed" && deps.toolRegistry?.hasParkedCalls()) {
|
|
213
|
+
deps.toolRegistry.renotifyHead();
|
|
214
|
+
}
|
|
215
|
+
};
|
|
191
216
|
|
|
192
217
|
const unsubscribePermission =
|
|
193
218
|
deps.toolRegistry?.onPermissionRequired((call, decision, meta) => {
|
|
@@ -228,8 +253,10 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
|
|
|
228
253
|
const unsubscribeWorker = deps.bus.on(BusChannels.PermissionRequested, (payload) => {
|
|
229
254
|
const entry = workerEscalationEntry(payload, deps.getAutonomy());
|
|
230
255
|
if (!entry) return;
|
|
256
|
+
if (!markPermissionRequestSurfaced(announcedWorkerRequestIds, entry.requestId)) return;
|
|
231
257
|
const notice = workerEscalationNotice(payload);
|
|
232
258
|
if (notice !== null) deps.appendNotice(notice.level, notice.text);
|
|
259
|
+
deps.onOperatorParked?.();
|
|
233
260
|
workerQueue.push(entry);
|
|
234
261
|
maybeOpenWorker();
|
|
235
262
|
});
|
|
@@ -310,10 +337,7 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
|
|
|
310
337
|
workerQueue.length = 0;
|
|
311
338
|
return;
|
|
312
339
|
}
|
|
313
|
-
|
|
314
|
-
if (deps.getOverlayState() === "closed" && deps.toolRegistry?.hasParkedCalls()) {
|
|
315
|
-
deps.toolRegistry.renotifyHead();
|
|
316
|
-
}
|
|
340
|
+
retryPending();
|
|
317
341
|
};
|
|
318
342
|
|
|
319
343
|
return {
|
|
@@ -336,6 +360,7 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
|
|
|
336
360
|
);
|
|
337
361
|
},
|
|
338
362
|
onPermissionOverlayClosed,
|
|
363
|
+
retryPending,
|
|
339
364
|
dispose: () => {
|
|
340
365
|
unsubscribePermission();
|
|
341
366
|
unsubscribeWorker();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ResourcesContract } from "../domains/resources/index.js";
|
|
1
|
+
import type { LibraryEntryKind, ResourcesContract } from "../domains/resources/index.js";
|
|
2
2
|
import { installSkill as installMarketplaceSkill } from "../domains/resources/skills/marketplace.js";
|
|
3
3
|
import type { TUI } from "../engine/tui.js";
|
|
4
4
|
import type { ClioEditor } from "./clio-editor.js";
|
|
@@ -32,7 +32,7 @@ export interface OverlayResourceOpenersDeps {
|
|
|
32
32
|
export interface OverlayResourceOpeners {
|
|
33
33
|
openHelpOverlayState(query?: string): void;
|
|
34
34
|
openAgentsOverlayState(): void;
|
|
35
|
-
openSkillsHubState(): void;
|
|
35
|
+
openSkillsHubState(tab?: LibraryEntryKind): void;
|
|
36
36
|
openPromptsOverlayState(): void;
|
|
37
37
|
openExtensionsOverlayState(): void;
|
|
38
38
|
openInteropOverlayState(): void;
|
|
@@ -61,10 +61,18 @@ export function createOverlayResourceOpeners(deps: OverlayResourceOpenersDeps):
|
|
|
61
61
|
deps.tui.requestRender();
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
-
const openSkillsHubState = (): void => {
|
|
64
|
+
const openSkillsHubState = (tab?: LibraryEntryKind): void => {
|
|
65
65
|
if (deps.transitions.state !== "closed") return;
|
|
66
66
|
deps.transitions.state = "skills-hub";
|
|
67
67
|
deps.transitions.handle = openSkills(deps.tui, {
|
|
68
|
+
...(tab ? { initialTab: tab } : {}),
|
|
69
|
+
// A fleet's `use` is its approval preview, which is a surface of its own.
|
|
70
|
+
// The hub closes first so the preview owns the overlay slot, exactly as
|
|
71
|
+
// `/fleet run <name>` typed into the composer would.
|
|
72
|
+
openFleetRun: (name) => {
|
|
73
|
+
deps.closeOverlay();
|
|
74
|
+
deps.getSlashContext().startFleetRun?.(name, {});
|
|
75
|
+
},
|
|
68
76
|
listSkills: () => deps.resources?.skills(process.cwd()) ?? { items: [], diagnostics: [] },
|
|
69
77
|
setEditorText: (text) => {
|
|
70
78
|
deps.editor.setText(text);
|
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
import { resolveSessionCwd } from "../domains/session/cwd-fallback.js";
|
|
2
|
+
import type { DecisionLedgerEntry } from "../domains/session/entries.js";
|
|
3
|
+
import {
|
|
4
|
+
buildHandoffReadLedger,
|
|
5
|
+
HANDOFF_NOTE_CUSTOM_TYPE,
|
|
6
|
+
HANDOFF_SEED_CUSTOM_TYPE,
|
|
7
|
+
type HandoffNoteData,
|
|
8
|
+
type HandoffSeedData,
|
|
9
|
+
mergeHandoffDecisions,
|
|
10
|
+
parseHandoffExtraction,
|
|
11
|
+
renderHandoffDocument,
|
|
12
|
+
validateHandoffFiles,
|
|
13
|
+
validateHandoffGoal,
|
|
14
|
+
} from "../domains/session/handoff.js";
|
|
2
15
|
import type { SessionContract, SessionEntry } from "../domains/session/index.js";
|
|
3
16
|
import type { TUI } from "../engine/tui.js";
|
|
4
17
|
import type { ChatLoop } from "./chat-loop.js";
|
|
5
18
|
import type { ChatPanel } from "./chat-panel.js";
|
|
6
19
|
import { rehydrateChatPanelFromTurns } from "./chat-renderer.js";
|
|
7
20
|
import { emitCommandNotice } from "./command-fallbacks.js";
|
|
21
|
+
import { editTextExternally, resolveExternalEditor } from "./external-editor.js";
|
|
8
22
|
import type { InteractiveNoticeLevel } from "./interactive-subscriptions.js";
|
|
9
23
|
import { buildModelReplayAgentMessagesFromTurns } from "./model-session-replay.js";
|
|
10
24
|
import type { OverlayTransitions } from "./overlay-transitions.js";
|
|
11
25
|
import { openCwdFallbackOverlay } from "./overlays/cwd-fallback.js";
|
|
26
|
+
import { openHandoffReviewOverlay } from "./overlays/handoff-review.js";
|
|
12
27
|
import { openMessagePickerOverlay } from "./overlays/message-picker.js";
|
|
13
28
|
import { openSessionOverlay } from "./overlays/session-selector.js";
|
|
14
29
|
import { openTreeOverlay } from "./overlays/tree-selector.js";
|
|
@@ -22,7 +37,7 @@ export interface OverlaySessionLifecycleDeps {
|
|
|
22
37
|
tui: TUI;
|
|
23
38
|
transitions: OverlayTransitions;
|
|
24
39
|
session?: SessionContract;
|
|
25
|
-
chat: Pick<ChatLoop, "cancel" | "isStreaming" | "resetForSession" | "whenSettled">;
|
|
40
|
+
chat: Pick<ChatLoop, "cancel" | "isStreaming" | "resetForSession" | "whenSettled" | "extractHandoff">;
|
|
26
41
|
chatPanel: ChatPanel;
|
|
27
42
|
/** The one transcript reset: the panel plus every view folded alongside it. */
|
|
28
43
|
resetTranscript(): void;
|
|
@@ -30,6 +45,18 @@ export interface OverlaySessionLifecycleDeps {
|
|
|
30
45
|
getSlashNotice(): SlashCommandContext["notice"];
|
|
31
46
|
onResumeSession?(sessionId: string): void;
|
|
32
47
|
onForkSession?(parentTurnId: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Mint a fresh session, the same hook `/new` uses. `/handoff` needs the one
|
|
50
|
+
* session-creation path the orchestrator owns rather than a second one.
|
|
51
|
+
*/
|
|
52
|
+
onNewSession?(): void;
|
|
53
|
+
/** The session's settled decision board, which wins over extracted decisions. */
|
|
54
|
+
getDecisionBoard?(): ReadonlyArray<DecisionLedgerEntry>;
|
|
55
|
+
/** Stop and restart the terminal around an external editor child process. */
|
|
56
|
+
suspendTerminal?<T>(run: () => T): T;
|
|
57
|
+
resolveEditor?: typeof resolveExternalEditor;
|
|
58
|
+
editExternally?: typeof editTextExternally;
|
|
59
|
+
openHandoffReviewOverlay?: typeof openHandoffReviewOverlay;
|
|
33
60
|
announceTaskMemorySeedOffer(): void;
|
|
34
61
|
/**
|
|
35
62
|
* Running usage totals, reseeded whenever the session under them changes.
|
|
@@ -45,6 +72,8 @@ export interface OverlaySessionLifecycleDeps {
|
|
|
45
72
|
setLastTurnSummary?(summary: TurnSummary | null): void;
|
|
46
73
|
refreshFooter(): void;
|
|
47
74
|
requestRender(): void;
|
|
75
|
+
/** Live terminal width, so the handoff review box tracks the window. */
|
|
76
|
+
terminal?: { columns: number };
|
|
48
77
|
stderr(text: string): void;
|
|
49
78
|
notify(level: InteractiveNoticeLevel, text: string, key?: string): void;
|
|
50
79
|
openSessionOverlay?: typeof openSessionOverlay;
|
|
@@ -57,6 +86,8 @@ export interface OverlaySessionLifecycle {
|
|
|
57
86
|
openResume(): void;
|
|
58
87
|
openTree(): void;
|
|
59
88
|
openMessagePicker(): void;
|
|
89
|
+
/** `/handoff <goal>`: extract, review, and seed a successor session. */
|
|
90
|
+
startHandoff(goal: string): void;
|
|
60
91
|
}
|
|
61
92
|
|
|
62
93
|
/**
|
|
@@ -327,5 +358,206 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
327
358
|
deps.requestRender();
|
|
328
359
|
}
|
|
329
360
|
|
|
330
|
-
|
|
361
|
+
/**
|
|
362
|
+
* `/handoff <goal>`: carry this session's working state into a fresh one.
|
|
363
|
+
*
|
|
364
|
+
* A handoff is a session operation. It writes no memory promotion candidate,
|
|
365
|
+
* touches no memory record, and never calls the task-memory bank; the only
|
|
366
|
+
* things it writes are one seed entry plus replayed skill activations in the
|
|
367
|
+
* new session and one terminal note in the old one, and it writes none of
|
|
368
|
+
* them until the operator has accepted the document.
|
|
369
|
+
*/
|
|
370
|
+
function startHandoff(goal: string): void {
|
|
371
|
+
if (deps.transitions.state !== "closed") return;
|
|
372
|
+
const notice = deps.getSlashNotice();
|
|
373
|
+
const verdict = validateHandoffGoal(goal);
|
|
374
|
+
if (!verdict.ok) {
|
|
375
|
+
emitCommandNotice(notice, "warn", "handoff", verdict.reason);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (!deps.session) {
|
|
379
|
+
emitCommandNotice(notice, "error", "handoff", "session contract unavailable");
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (!deps.onNewSession) {
|
|
383
|
+
emitCommandNotice(notice, "error", "handoff", "no session-creation path is wired in this session");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
// Refused, never queued. The document describes a session that has
|
|
387
|
+
// stopped; a turn still in flight is about to change what it would say.
|
|
388
|
+
if (deps.chat.isStreaming()) {
|
|
389
|
+
emitCommandNotice(
|
|
390
|
+
notice,
|
|
391
|
+
"warn",
|
|
392
|
+
"handoff",
|
|
393
|
+
"a turn is in flight; /handoff cannot summarize a session that is still moving",
|
|
394
|
+
);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const session = deps.session;
|
|
398
|
+
const fromSessionId = session.current()?.id ?? null;
|
|
399
|
+
if (fromSessionId === null) {
|
|
400
|
+
emitCommandNotice(notice, "warn", "handoff", "no current session to hand off; start one with /new or /resume");
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
void runHandoffExtraction(session, fromSessionId, verdict.goal);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async function runHandoffExtraction(session: SessionContract, fromSessionId: string, goal: string): Promise<void> {
|
|
407
|
+
const notice = deps.getSlashNotice();
|
|
408
|
+
const outcome = await deps.chat.extractHandoff(goal);
|
|
409
|
+
if (outcome.status !== "answered") {
|
|
410
|
+
const reason = outcome.status === "aborted" ? "the extraction round was cancelled" : outcome.reason;
|
|
411
|
+
emitCommandNotice(notice, outcome.status === "failed" ? "error" : "warn", "handoff", reason);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const parsed = parseHandoffExtraction(outcome.text);
|
|
415
|
+
if (!parsed.ok) {
|
|
416
|
+
emitCommandNotice(notice, "error", "handoff", parsed.reason);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const meta = session.current();
|
|
420
|
+
const cwd = typeof meta?.cwd === "string" && meta.cwd.length > 0 ? meta.cwd : null;
|
|
421
|
+
const entries = deps.readStructuredEntries(fromSessionId);
|
|
422
|
+
// The ledger is what this session's own tool calls touched, folded through
|
|
423
|
+
// the active path so an abandoned `/tree` branch is not evidence.
|
|
424
|
+
const ledger = buildHandoffReadLedger(entries, { cwd, leafTurnId: meta?.pinnedLeafTurnId ?? null });
|
|
425
|
+
const files = validateHandoffFiles(parsed.result.extraction.files, ledger, cwd);
|
|
426
|
+
const decisions = mergeHandoffDecisions(parsed.result.extraction.decisions, deps.getDecisionBoard?.() ?? []);
|
|
427
|
+
const document = renderHandoffDocument({
|
|
428
|
+
goal,
|
|
429
|
+
fromSessionId,
|
|
430
|
+
decisions,
|
|
431
|
+
facts: parsed.result.extraction.facts,
|
|
432
|
+
files: files.kept,
|
|
433
|
+
droppedFiles: files.dropped,
|
|
434
|
+
commands: parsed.result.extraction.commands,
|
|
435
|
+
openQuestions: parsed.result.extraction.openQuestions,
|
|
436
|
+
truncations: parsed.result.truncations,
|
|
437
|
+
});
|
|
438
|
+
openHandoffReview(session, fromSessionId, goal, document);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/** Hand the document to `$EDITOR`, or say why it could not go. */
|
|
442
|
+
function editHandoffDocument(current: string): string | null {
|
|
443
|
+
const resolve = deps.resolveEditor ?? resolveExternalEditor;
|
|
444
|
+
const edit = deps.editExternally ?? editTextExternally;
|
|
445
|
+
const command = resolve();
|
|
446
|
+
if (!command) {
|
|
447
|
+
deps.notify("warning", "handoff: no external editor configured; set VISUAL or EDITOR", "handoff:no-editor");
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
const suspend = deps.suspendTerminal ?? (<T>(run: () => T): T => run());
|
|
451
|
+
const result = suspend(() => edit(current, command));
|
|
452
|
+
if (result.ok) return result.text ?? current;
|
|
453
|
+
if (result.error) deps.notify("warning", `handoff: ${result.error}`, "handoff:editor-failed");
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function openHandoffReview(session: SessionContract, fromSessionId: string, goal: string, document: string): void {
|
|
458
|
+
if (deps.transitions.state !== "closed") return;
|
|
459
|
+
const openReview = deps.openHandoffReviewOverlay ?? openHandoffReviewOverlay;
|
|
460
|
+
deps.transitions.state = "handoff-review";
|
|
461
|
+
deps.transitions.handle = openReview(deps.tui, {
|
|
462
|
+
document,
|
|
463
|
+
goal,
|
|
464
|
+
columns: deps.terminal?.columns ?? 80,
|
|
465
|
+
onEdit: editHandoffDocument,
|
|
466
|
+
onAccept: (reviewed) => {
|
|
467
|
+
deps.transitions.close();
|
|
468
|
+
const text = reviewed.trim();
|
|
469
|
+
if (text.length === 0) {
|
|
470
|
+
deps.notify("warning", "handoff: the reviewed document was empty; nothing was written", "handoff:empty");
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
seedHandoffSession(session, fromSessionId, goal, text);
|
|
474
|
+
},
|
|
475
|
+
// Esc. Nothing has been written yet, so cancel really is free. The
|
|
476
|
+
// overlay settles itself on cancel and answers no further key, so the
|
|
477
|
+
// transition has to close here or the review stays on screen inert.
|
|
478
|
+
onCancel: () => {
|
|
479
|
+
deps.transitions.close();
|
|
480
|
+
deps.notify("info", "handoff cancelled; nothing was written", "handoff:cancelled");
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
deps.requestRender();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Mint the successor session and open it on the reviewed document.
|
|
488
|
+
*
|
|
489
|
+
* Order matters. The old session's terminal note is appended while it is
|
|
490
|
+
* still current, because an append always lands in the current session. Then
|
|
491
|
+
* the new session is minted through the one creation path the orchestrator
|
|
492
|
+
* owns, the document goes in as bounded data labelled by its origin, and the
|
|
493
|
+
* old session's skill activations are replayed so loaded skills carry
|
|
494
|
+
* forward. The old session is otherwise untouched.
|
|
495
|
+
*/
|
|
496
|
+
function seedHandoffSession(session: SessionContract, fromSessionId: string, goal: string, document: string): void {
|
|
497
|
+
const notice = deps.getSlashNotice();
|
|
498
|
+
const activations = session.current()?.skillActivations ?? [];
|
|
499
|
+
let toSessionId: string;
|
|
500
|
+
try {
|
|
501
|
+
deps.onNewSession?.();
|
|
502
|
+
const minted = session.current()?.id ?? null;
|
|
503
|
+
if (minted === null || minted === fromSessionId) throw new Error("the new session was not created");
|
|
504
|
+
toSessionId = minted;
|
|
505
|
+
session.appendEntry({
|
|
506
|
+
kind: "custom",
|
|
507
|
+
parentTurnId: null,
|
|
508
|
+
customType: HANDOFF_SEED_CUSTOM_TYPE,
|
|
509
|
+
display: true,
|
|
510
|
+
data: { fromSessionId, goal, document } satisfies HandoffSeedData,
|
|
511
|
+
});
|
|
512
|
+
for (const activation of activations) session.recordSkillActivation(activation);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
emitCommandNotice(
|
|
515
|
+
notice,
|
|
516
|
+
"error",
|
|
517
|
+
"handoff",
|
|
518
|
+
`could not seed the new session: ${error instanceof Error ? error.message : String(error)}`,
|
|
519
|
+
);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
// The note names the target, so it can only be written once the target
|
|
523
|
+
// exists. Appends land in the current session, so the old session is made
|
|
524
|
+
// current for exactly one append and then handed back. A failure here
|
|
525
|
+
// costs the note and nothing else: the successor session is already
|
|
526
|
+
// seeded and is where the operator continues.
|
|
527
|
+
try {
|
|
528
|
+
session.switchBranch(fromSessionId);
|
|
529
|
+
session.appendEntry({
|
|
530
|
+
kind: "custom",
|
|
531
|
+
parentTurnId: null,
|
|
532
|
+
customType: HANDOFF_NOTE_CUSTOM_TYPE,
|
|
533
|
+
display: true,
|
|
534
|
+
data: { toSessionId, goal } satisfies HandoffNoteData,
|
|
535
|
+
});
|
|
536
|
+
} catch (error) {
|
|
537
|
+
deps.stderr(`[/handoff] handoff note failed: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
538
|
+
} finally {
|
|
539
|
+
try {
|
|
540
|
+
session.switchBranch(toSessionId);
|
|
541
|
+
} catch (error) {
|
|
542
|
+
deps.stderr(
|
|
543
|
+
`[/handoff] could not return to the new session: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
try {
|
|
548
|
+
const turns = deps.readStructuredEntries(toSessionId);
|
|
549
|
+
deps.resetTranscript();
|
|
550
|
+
rehydrateChatPanelFromTurns(deps.chatPanel, turns);
|
|
551
|
+
deps.chat.resetForSession(null, buildModelReplayAgentMessagesFromTurns(turns));
|
|
552
|
+
rescopeToBranch(session, turns, null);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
deps.stderr(`[/handoff] seeding replay failed: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
555
|
+
deps.chat.resetForSession(null);
|
|
556
|
+
}
|
|
557
|
+
emitCommandNotice(notice, "info", "handoff", `handed off to session ${toSessionId}`);
|
|
558
|
+
deps.refreshFooter();
|
|
559
|
+
deps.requestRender();
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return { openResume, openTree, openMessagePicker: openMessagePickerState, startHandoff };
|
|
331
563
|
}
|
|
@@ -9,6 +9,12 @@ export interface OverlayTransitionsDeps {
|
|
|
9
9
|
cancelPendingAskUser: () => boolean;
|
|
10
10
|
finishAuth: (dismiss: boolean) => void;
|
|
11
11
|
onPermissionOverlayClosed: () => void;
|
|
12
|
+
/**
|
|
13
|
+
* Runs after any overlay has closed and the state is back to `closed`. The
|
|
14
|
+
* permission lifecycle uses it to re-present a call that parked while this
|
|
15
|
+
* overlay held the screen; nothing else re-attempts that dialog.
|
|
16
|
+
*/
|
|
17
|
+
onOverlayClosed?: () => void;
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
export interface OverlayTransitions {
|
|
@@ -26,7 +32,11 @@ export function createOverlayTransitions(deps: OverlayTransitionsDeps): OverlayT
|
|
|
26
32
|
return state;
|
|
27
33
|
},
|
|
28
34
|
set state(next) {
|
|
35
|
+
const closing = state !== "closed" && next === "closed";
|
|
29
36
|
state = next;
|
|
37
|
+
// The ask-user and auth lifecycles leave by assigning the state rather
|
|
38
|
+
// than through `close`, so the hook fires here for them.
|
|
39
|
+
if (closing) deps.onOverlayClosed?.();
|
|
30
40
|
},
|
|
31
41
|
get handle() {
|
|
32
42
|
return handle;
|
|
@@ -50,6 +60,7 @@ export function createOverlayTransitions(deps: OverlayTransitionsDeps): OverlayT
|
|
|
50
60
|
handle?.hide();
|
|
51
61
|
handle = null;
|
|
52
62
|
if (leaving === "permission-confirm") deps.onPermissionOverlayClosed();
|
|
63
|
+
else deps.onOverlayClosed?.();
|
|
53
64
|
deps.renderContextIsland();
|
|
54
65
|
deps.renderTaskIsland();
|
|
55
66
|
deps.requestRender();
|