@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
|
@@ -3,7 +3,9 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { BusChannels } from "../core/bus-events.js";
|
|
5
5
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
6
|
+
import { parseJsonObjectPayload } from "../core/json-payload.js";
|
|
6
7
|
import { clioStateDir } from "../core/xdg.js";
|
|
8
|
+
import type { CouncilReport, CouncilReportMember } from "../domains/agents/result-contract.js";
|
|
7
9
|
import { closeAgentLedger, openAgentLedger, renderAgentLedgerBoard } from "../domains/dispatch/agent-ledger-store.js";
|
|
8
10
|
import type { DetachedBatchRun } from "../domains/dispatch/batch-store.js";
|
|
9
11
|
import type { AbortReason, DispatchContract, DispatchRequest } from "../domains/dispatch/contract.js";
|
|
@@ -32,6 +34,7 @@ import {
|
|
|
32
34
|
} from "../domains/dispatch/gate-decisions.js";
|
|
33
35
|
import {
|
|
34
36
|
COMPETE_STANCES,
|
|
37
|
+
COUNCIL_JUDGE_PROMPT,
|
|
35
38
|
type CompeteStance,
|
|
36
39
|
JUDGE_GATE_PROMPT,
|
|
37
40
|
REVIEWER_GATE_PROMPT,
|
|
@@ -71,7 +74,12 @@ import {
|
|
|
71
74
|
type DispatchRunEventRegistry,
|
|
72
75
|
} from "./dispatch-run-events.js";
|
|
73
76
|
import { runScoutContinuationPlan, scoutPlanAuthorityGranted, scoutTransitionDetail } from "./dispatch-scout.js";
|
|
74
|
-
import type {
|
|
77
|
+
import type {
|
|
78
|
+
DispatchCompeteSettings,
|
|
79
|
+
DispatchCouncilSettings,
|
|
80
|
+
DispatchReviewSettings,
|
|
81
|
+
DispatchToolDeps,
|
|
82
|
+
} from "./dispatch-types.js";
|
|
75
83
|
import type { ToolInvokeOptions, ToolResult, ToolResultDetails } from "./registry.js";
|
|
76
84
|
import { truncateUtf8 } from "./truncate-utf8.js";
|
|
77
85
|
import {
|
|
@@ -487,7 +495,10 @@ function formatDispatchOutput(
|
|
|
487
495
|
// Evidence confidence comes from the sealed receipt. A receipt that fails
|
|
488
496
|
// integrity cannot be read as evidence at all.
|
|
489
497
|
const verification = run.integrity.ok ? receipt.verification : UNVERIFIABLE_RECEIPT_VERIFICATION;
|
|
490
|
-
const trustStatus = adaptRunReceiptTrustStatus(
|
|
498
|
+
const trustStatus = adaptRunReceiptTrustStatus(
|
|
499
|
+
{ ...receipt, verification: verification },
|
|
500
|
+
{ integrity: run.integrity },
|
|
501
|
+
);
|
|
491
502
|
const evidenceSuffix = ` ${receiptEvidenceLabels(receipt, verification, run.integrity).join(" ")}`;
|
|
492
503
|
const routingSuffix =
|
|
493
504
|
run.integrity.ok && receipt.routeDecision !== undefined && receipt.routingIntent !== undefined
|
|
@@ -566,6 +577,7 @@ function dispatchDetails(
|
|
|
566
577
|
// sealed receipt read-only, plus the integrity check so a tampered
|
|
567
578
|
// receipt is machine-visible here too.
|
|
568
579
|
verification: integrity.ok ? receipt.verification : UNVERIFIABLE_RECEIPT_VERIFICATION,
|
|
580
|
+
hostVerification: integrity.ok ? (receipt.hostVerification ?? null) : null,
|
|
569
581
|
receiptIntegrity: integrity,
|
|
570
582
|
trustStatus,
|
|
571
583
|
...(receipt.outcome !== undefined && receipt.outcome !== "succeeded"
|
|
@@ -1782,6 +1794,230 @@ function competeResult(
|
|
|
1782
1794
|
};
|
|
1783
1795
|
}
|
|
1784
1796
|
|
|
1797
|
+
const COUNCIL_BRIEFING_MAX_BYTES = 8 * 1024;
|
|
1798
|
+
const COUNCIL_BRIEFING_TRUNCATION = "\n[council peer briefing truncated]";
|
|
1799
|
+
|
|
1800
|
+
function councilVerdict(text: string): string | undefined {
|
|
1801
|
+
const parsed = parseJsonObjectPayload(text);
|
|
1802
|
+
return parsed.ok && typeof parsed.value.verdict === "string" && parsed.value.verdict.trim().length > 0
|
|
1803
|
+
? parsed.value.verdict.trim()
|
|
1804
|
+
: undefined;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
function councilJudgeText(text: string): string {
|
|
1808
|
+
const parsed = parseJsonObjectPayload(text);
|
|
1809
|
+
return parsed.ok && typeof parsed.value.text === "string" && parsed.value.text.trim().length > 0
|
|
1810
|
+
? parsed.value.text.trim()
|
|
1811
|
+
: text;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
function boundedCouncilBriefing(parts: ReadonlyArray<string>): string {
|
|
1815
|
+
return truncateUtf8(parts.join("\n\n"), COUNCIL_BRIEFING_MAX_BYTES, COUNCIL_BRIEFING_TRUNCATION);
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
async function runCouncil(
|
|
1819
|
+
deps: RegisteredDispatchToolDeps,
|
|
1820
|
+
base: DispatchRequest,
|
|
1821
|
+
council: DispatchCouncilSettings,
|
|
1822
|
+
timeoutMs: number | undefined,
|
|
1823
|
+
signal: AbortSignal | undefined,
|
|
1824
|
+
): Promise<{ report: CouncilReport; runs: CompletedRun[]; group: string }> {
|
|
1825
|
+
const group = newGateGroupId("council");
|
|
1826
|
+
const runs: CompletedRun[] = [];
|
|
1827
|
+
let prior = new Map<string, CompletedRun>();
|
|
1828
|
+
const runMember = async (request: DispatchRequest): Promise<CompletedRun> => {
|
|
1829
|
+
if (signal?.aborted) throw new Error("council dispatch aborted");
|
|
1830
|
+
const handle = await deps.dispatch.dispatch(request);
|
|
1831
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
1832
|
+
if (timeoutMs !== undefined) {
|
|
1833
|
+
timer = setTimeout(
|
|
1834
|
+
() => deps.dispatch.abort(handle.runId, { cause: "timeout", detail: `timed out after ${timeoutMs}ms` }),
|
|
1835
|
+
timeoutMs,
|
|
1836
|
+
);
|
|
1837
|
+
}
|
|
1838
|
+
const abort = (): void => deps.dispatch.abort(handle.runId);
|
|
1839
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
1840
|
+
try {
|
|
1841
|
+
const [summary, receipt] = await Promise.all([
|
|
1842
|
+
consumeGateSensitiveDispatchEvents(deps, handle.runId, request.agentId, handle.events),
|
|
1843
|
+
handle.finalPromise,
|
|
1844
|
+
]);
|
|
1845
|
+
return completeRun(deps, receipt, summary);
|
|
1846
|
+
} finally {
|
|
1847
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
1848
|
+
signal?.removeEventListener("abort", abort);
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
|
|
1852
|
+
for (let round = 1; round <= council.rounds; round += 1) {
|
|
1853
|
+
const settled = await Promise.all(
|
|
1854
|
+
council.members.map(async (member, index) => {
|
|
1855
|
+
const peerParts: string[] = base.briefing === undefined ? [] : [`[original briefing] ${base.briefing}`];
|
|
1856
|
+
if (round > 1) {
|
|
1857
|
+
for (const peer of council.members) {
|
|
1858
|
+
if (peer.label === member.label) continue;
|
|
1859
|
+
const previous = prior.get(peer.label);
|
|
1860
|
+
if (previous === undefined || isPipelineStepFailure(previous.receipt)) {
|
|
1861
|
+
peerParts.push(`[${peer.label}] failed in prior round; no answer contributed.`);
|
|
1862
|
+
} else {
|
|
1863
|
+
peerParts.push(`[${peer.label}] ${normalizedAssistantText(previous.summary)}`);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
const position = (round - 1) * council.members.length + index + 1;
|
|
1868
|
+
const pinned = council.resolvedTasks?.find((task) => task.role === "member" && task.position === position);
|
|
1869
|
+
const request: DispatchRequest = {
|
|
1870
|
+
...base,
|
|
1871
|
+
executionRole: "researcher",
|
|
1872
|
+
autonomy: "read-only",
|
|
1873
|
+
toolProfile: "council-read-only",
|
|
1874
|
+
target: member.target,
|
|
1875
|
+
...(member.model ? { model: member.model } : {}),
|
|
1876
|
+
...(member.thinking ? { thinkingLevel: member.thinking as NonNullable<DispatchRequest["thinkingLevel"]> } : {}),
|
|
1877
|
+
gate: {
|
|
1878
|
+
role: "member",
|
|
1879
|
+
group,
|
|
1880
|
+
cycle: round,
|
|
1881
|
+
...(round > 1
|
|
1882
|
+
? {
|
|
1883
|
+
subjects: [...prior.entries()]
|
|
1884
|
+
.filter(([label]) => label !== member.label)
|
|
1885
|
+
.map(([, run]) => subjectRef(run.receipt)),
|
|
1886
|
+
}
|
|
1887
|
+
: {}),
|
|
1888
|
+
},
|
|
1889
|
+
council: { group, label: member.label, ...(member.color ? { color: member.color } : {}), round },
|
|
1890
|
+
...(round > 1
|
|
1891
|
+
? { briefing: boundedCouncilBriefing(peerParts) }
|
|
1892
|
+
: base.briefing !== undefined
|
|
1893
|
+
? { briefing: base.briefing }
|
|
1894
|
+
: {}),
|
|
1895
|
+
};
|
|
1896
|
+
return [
|
|
1897
|
+
member.label,
|
|
1898
|
+
await runMember(withResolvedPlanTaskPin(request, pinned, { pinTask: false, pinBriefing: false })),
|
|
1899
|
+
] as const;
|
|
1900
|
+
}),
|
|
1901
|
+
);
|
|
1902
|
+
prior = new Map(settled);
|
|
1903
|
+
runs.push(...settled.map(([, run]) => run));
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
const members: CouncilReportMember[] = runs.map((run) => {
|
|
1907
|
+
const text = normalizedAssistantText(run.summary);
|
|
1908
|
+
const failed = isPipelineStepFailure(run.receipt);
|
|
1909
|
+
const verdict = councilVerdict(text);
|
|
1910
|
+
return {
|
|
1911
|
+
label: run.receipt.council?.label ?? "member",
|
|
1912
|
+
runId: run.receipt.runId,
|
|
1913
|
+
round: run.receipt.council?.round ?? 1,
|
|
1914
|
+
answer: text,
|
|
1915
|
+
...(verdict !== undefined ? { verdict } : {}),
|
|
1916
|
+
...(failed ? { failed: { reason: pipelineFailureReason(run.receipt) } } : {}),
|
|
1917
|
+
};
|
|
1918
|
+
});
|
|
1919
|
+
const finalMembers = members.filter((member) => member.round === council.rounds && member.failed === undefined);
|
|
1920
|
+
const finalReports = members.filter((member) => member.round === council.rounds);
|
|
1921
|
+
let synthesis: CouncilReport["synthesis"] = { kind: council.synthesis };
|
|
1922
|
+
if (council.synthesis === "vote") {
|
|
1923
|
+
const tally: Record<string, number> = {};
|
|
1924
|
+
for (const member of finalMembers)
|
|
1925
|
+
if (member.verdict !== undefined) tally[member.verdict] = (tally[member.verdict] ?? 0) + 1;
|
|
1926
|
+
const ranked = Object.entries(tally).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]));
|
|
1927
|
+
const winner = ranked[0];
|
|
1928
|
+
const verdict =
|
|
1929
|
+
winner === undefined ? "no_verdict_field" : winner[1] > finalMembers.length / 2 ? winner[0] : "no_majority";
|
|
1930
|
+
synthesis = { kind: "vote", verdict, tally };
|
|
1931
|
+
} else if (council.synthesis === "judge") {
|
|
1932
|
+
const finalRuns = [...prior.values()];
|
|
1933
|
+
const judgeRequest: DispatchRequest = {
|
|
1934
|
+
agentId: council.judge?.agent ?? base.agentId,
|
|
1935
|
+
executionRole: "judge",
|
|
1936
|
+
task: `Synthesize the council answers for this task:\n\n${base.task}`,
|
|
1937
|
+
briefing: boundedCouncilBriefing(
|
|
1938
|
+
finalReports.map((member) =>
|
|
1939
|
+
member.failed === undefined
|
|
1940
|
+
? `[${member.label}] ${member.answer}`
|
|
1941
|
+
: `[${member.label}] failed; no final answer contributed. Reason: ${member.failed.reason}`,
|
|
1942
|
+
),
|
|
1943
|
+
),
|
|
1944
|
+
systemPrompt: COUNCIL_JUDGE_PROMPT,
|
|
1945
|
+
autonomy: "read-only",
|
|
1946
|
+
toolProfile: "council-read-only",
|
|
1947
|
+
gate: { role: "synthesis", group, cycle: council.rounds, subjects: finalRuns.map((run) => subjectRef(run.receipt)) },
|
|
1948
|
+
council: { group, label: "synthesis", round: council.rounds },
|
|
1949
|
+
...(council.judge?.target ? { target: council.judge.target } : {}),
|
|
1950
|
+
...(council.judge?.model ? { model: council.judge.model } : {}),
|
|
1951
|
+
...(council.judge?.node ? { node: council.judge.node } : {}),
|
|
1952
|
+
...(base.plan ? { plan: base.plan } : {}),
|
|
1953
|
+
...(base.parentToolCallId ? { parentToolCallId: base.parentToolCallId } : {}),
|
|
1954
|
+
};
|
|
1955
|
+
const pinned = council.resolvedTasks?.find((task) => task.role === "synthesis");
|
|
1956
|
+
const judgeRun = await runMember(
|
|
1957
|
+
withResolvedPlanTaskPin(judgeRequest, pinned, { pinTask: false, pinBriefing: false }),
|
|
1958
|
+
);
|
|
1959
|
+
runs.push(judgeRun);
|
|
1960
|
+
const rawText = normalizedAssistantText(judgeRun.summary);
|
|
1961
|
+
const text = councilJudgeText(rawText);
|
|
1962
|
+
const verdict = councilVerdict(rawText);
|
|
1963
|
+
synthesis = {
|
|
1964
|
+
kind: "judge",
|
|
1965
|
+
text,
|
|
1966
|
+
...(verdict !== undefined ? { verdict } : {}),
|
|
1967
|
+
judgeRunId: judgeRun.receipt.runId,
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
if (council.synthesis !== "judge" && prior.size > 0 && deps.dispatch.sealCouncilSynthesis !== undefined) {
|
|
1971
|
+
const finalRuns = [...prior.values()];
|
|
1972
|
+
const template = finalRuns[0];
|
|
1973
|
+
if (template === undefined) throw new Error("council synthesis has no final member receipt");
|
|
1974
|
+
// The sealed text is the whole council-report, not the synthesis alone:
|
|
1975
|
+
// /share <synthesis runId> reads this receipt to bring every final
|
|
1976
|
+
// member's labelled answer and the synthesis line into the main context.
|
|
1977
|
+
const text = JSON.stringify({ members, synthesis } satisfies CouncilReport);
|
|
1978
|
+
const receipt = await deps.dispatch.sealCouncilSynthesis({
|
|
1979
|
+
group,
|
|
1980
|
+
round: council.rounds,
|
|
1981
|
+
kind: council.synthesis,
|
|
1982
|
+
text,
|
|
1983
|
+
subjects: finalRuns.map((run) => subjectRef(run.receipt)),
|
|
1984
|
+
template: template.receipt,
|
|
1985
|
+
});
|
|
1986
|
+
runs.push(
|
|
1987
|
+
completeRun(deps, receipt, {
|
|
1988
|
+
count: 0,
|
|
1989
|
+
types: [],
|
|
1990
|
+
lastAssistantText: text,
|
|
1991
|
+
terminalAttemptRunId: receipt.runId,
|
|
1992
|
+
}),
|
|
1993
|
+
);
|
|
1994
|
+
}
|
|
1995
|
+
return { report: { members, synthesis }, runs, group };
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
function councilResult(deps: DispatchToolDeps, outcome: Awaited<ReturnType<typeof runCouncil>>): ToolResult {
|
|
1999
|
+
const sections = outcome.report.members
|
|
2000
|
+
.filter((member) => member.round === Math.max(...outcome.report.members.map((entry) => entry.round)))
|
|
2001
|
+
.map((member) => {
|
|
2002
|
+
const run = outcome.runs.find((entry) => entry.receipt.runId === member.runId);
|
|
2003
|
+
return `[${member.label}] ${run?.receipt.targetId ?? "unknown"}/${run?.receipt.wireModelId ?? "unknown"}\n${member.answer}`;
|
|
2004
|
+
});
|
|
2005
|
+
sections.push(
|
|
2006
|
+
`[synthesis] ${outcome.report.synthesis.kind}\n${outcome.report.synthesis.text ?? outcome.report.synthesis.verdict ?? "none"}`,
|
|
2007
|
+
);
|
|
2008
|
+
const details: ToolResultDetails = {
|
|
2009
|
+
...dispatchDetails(deps, "council", outcome.runs),
|
|
2010
|
+
council: { group: outcome.group, ...outcome.report },
|
|
2011
|
+
};
|
|
2012
|
+
const finalRound = Math.max(...outcome.report.members.map((member) => member.round));
|
|
2013
|
+
const failed =
|
|
2014
|
+
outcome.report.members.some((member) => member.round === finalRound && member.failed !== undefined) ||
|
|
2015
|
+
outcome.runs.some((run) => run.receipt.gate?.role === "synthesis" && isPipelineStepFailure(run.receipt));
|
|
2016
|
+
return failed
|
|
2017
|
+
? { kind: "error", message: sections.join("\n\n"), details }
|
|
2018
|
+
: { kind: "ok", output: sections.join("\n\n"), details };
|
|
2019
|
+
}
|
|
2020
|
+
|
|
1785
2021
|
export async function runDispatchTool(
|
|
1786
2022
|
inputDeps: DispatchToolDeps,
|
|
1787
2023
|
admissionState: DispatchAdmissionState,
|
|
@@ -1856,6 +2092,7 @@ export async function runDispatchTool(
|
|
|
1856
2092
|
const maxOutputBytes = snapshot.maxOutputBytes;
|
|
1857
2093
|
const timeoutMs = snapshot.timeoutMs;
|
|
1858
2094
|
let review = snapshot.review === undefined ? undefined : structuredClone(snapshot.review);
|
|
2095
|
+
let council = snapshot.council === undefined ? undefined : structuredClone(snapshot.council);
|
|
1859
2096
|
|
|
1860
2097
|
// Plan-scale calls are either approved at supervised admission or run
|
|
1861
2098
|
// unstopped at full-auto; every run seals the same plan hash.
|
|
@@ -1888,7 +2125,8 @@ export async function runDispatchTool(
|
|
|
1888
2125
|
};
|
|
1889
2126
|
}
|
|
1890
2127
|
if (resolvedPlan !== null) {
|
|
1891
|
-
const primaryRole =
|
|
2128
|
+
const primaryRole =
|
|
2129
|
+
review !== undefined ? "builder" : mode === "compete" ? "candidate" : mode === "council" ? "member" : "task";
|
|
1892
2130
|
const primaryTasks = resolvedPlan.tasks.filter((task) => task.role === primaryRole);
|
|
1893
2131
|
const executionTasks = primaryRole === "task" ? primaryTasks : primaryTasks.slice(0, 1);
|
|
1894
2132
|
if (executionTasks.length !== requests.length) {
|
|
@@ -1924,6 +2162,7 @@ export async function runDispatchTool(
|
|
|
1924
2162
|
resolvedTasks: resolvedPlan.tasks,
|
|
1925
2163
|
};
|
|
1926
2164
|
}
|
|
2165
|
+
if (council !== undefined) council = { ...council, resolvedTasks: resolvedPlan.tasks };
|
|
1927
2166
|
}
|
|
1928
2167
|
if (planView.planScale) {
|
|
1929
2168
|
const plan: RunPlanProvenance = {
|
|
@@ -1955,15 +2194,17 @@ export async function runDispatchTool(
|
|
|
1955
2194
|
? "a Scout dependency plan drives its stages from this turn"
|
|
1956
2195
|
: mode === "compete"
|
|
1957
2196
|
? "compete holds its judge gate in this turn"
|
|
1958
|
-
:
|
|
1959
|
-
? "
|
|
1960
|
-
:
|
|
1961
|
-
? "a
|
|
1962
|
-
:
|
|
1963
|
-
?
|
|
1964
|
-
:
|
|
1965
|
-
?
|
|
1966
|
-
:
|
|
2197
|
+
: mode === "council"
|
|
2198
|
+
? "council holds its rounds and synthesis in this turn"
|
|
2199
|
+
: review !== undefined
|
|
2200
|
+
? "a review gate holds its cycle state in this turn"
|
|
2201
|
+
: mode === "pipeline" && requests.length > 1
|
|
2202
|
+
? "a pipeline threads each step's output through this turn"
|
|
2203
|
+
: timeoutMs !== undefined
|
|
2204
|
+
? `this call set timeout_ms=${timeoutMs} and nobody would be left to enforce the deadline`
|
|
2205
|
+
: deps.dispatch.detached === undefined
|
|
2206
|
+
? "detached batch records are unavailable in this context"
|
|
2207
|
+
: null;
|
|
1967
2208
|
const background = createBackgroundSwitch();
|
|
1968
2209
|
// Review and Scout both execute under mode=parallel, so the operator-facing
|
|
1969
2210
|
// label names the topology they actually asked for.
|
|
@@ -1994,6 +2235,7 @@ export async function runDispatchTool(
|
|
|
1994
2235
|
topology: executionPlan.topology,
|
|
1995
2236
|
rootTask: executionPlan.rootTask,
|
|
1996
2237
|
maxWorkers: executionPlan.maxWorkers,
|
|
2238
|
+
...(executionPlan.writers === 1 ? { writers: 1 as const } : {}),
|
|
1997
2239
|
onFailure: executionPlan.onFailure,
|
|
1998
2240
|
steps: requireAgentSteps(executionPlan.steps).map((step) => ({
|
|
1999
2241
|
...step,
|
|
@@ -2085,6 +2327,17 @@ export async function runDispatchTool(
|
|
|
2085
2327
|
}
|
|
2086
2328
|
}
|
|
2087
2329
|
|
|
2330
|
+
if (mode === "council") {
|
|
2331
|
+
if (requests.length !== 1 || requests[0] === undefined || council === undefined) {
|
|
2332
|
+
return { kind: "error", message: "dispatch: trusted council settings are unavailable" };
|
|
2333
|
+
}
|
|
2334
|
+
try {
|
|
2335
|
+
return councilResult(deps, await runCouncil(deps, requests[0], council, timeoutMs, options?.signal));
|
|
2336
|
+
} catch (err) {
|
|
2337
|
+
return { kind: "error", message: dispatchErrorMessage(err) };
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2088
2341
|
if (review !== undefined) {
|
|
2089
2342
|
if (requests.length !== 1 || requests[0] === undefined) {
|
|
2090
2343
|
return { kind: "error", message: REVIEW_SINGLE_TASK_MESSAGE };
|
|
@@ -2111,7 +2364,10 @@ export async function runDispatchTool(
|
|
|
2111
2364
|
// plain sequential and no pipeline-input message is sent.
|
|
2112
2365
|
runs = await runSequential(deps, requests, mode, timeoutMs, options?.signal, background);
|
|
2113
2366
|
} else {
|
|
2114
|
-
const batch =
|
|
2367
|
+
const batch =
|
|
2368
|
+
snapshot.writers === 1
|
|
2369
|
+
? await runWriterLimitedBatch(deps, requests, timeoutMs, options?.signal)
|
|
2370
|
+
: await runBatch(deps, requests, timeoutMs, options?.signal, background);
|
|
2115
2371
|
runs = batch.runs;
|
|
2116
2372
|
board = batch.board;
|
|
2117
2373
|
}
|
|
@@ -2370,3 +2626,53 @@ async function runBatch(
|
|
|
2370
2626
|
if (!backgrounded) await closeAgentLedger(ledgerId);
|
|
2371
2627
|
}
|
|
2372
2628
|
}
|
|
2629
|
+
|
|
2630
|
+
/** Admit one checkout writer at a time while every read-only peer remains concurrent. */
|
|
2631
|
+
async function runWriterLimitedBatch(
|
|
2632
|
+
deps: RegisteredDispatchToolDeps,
|
|
2633
|
+
requests: ReadonlyArray<DispatchRequest>,
|
|
2634
|
+
timeoutMs: number | undefined,
|
|
2635
|
+
signal: AbortSignal | undefined,
|
|
2636
|
+
): Promise<BatchOutcome> {
|
|
2637
|
+
const specs = new Map(deps.getAgentSpecs().map((spec) => [spec.id, spec]));
|
|
2638
|
+
const readers = requests.filter((request) => specs.get(request.agentId)?.capabilityClass === "read-only");
|
|
2639
|
+
const writers = requests.filter((request) => specs.get(request.agentId)?.capabilityClass !== "read-only");
|
|
2640
|
+
const ledgerId = newGateGroupId("ledger");
|
|
2641
|
+
await openAgentLedger(ledgerId);
|
|
2642
|
+
const activeIds = new Set<string>();
|
|
2643
|
+
const completed = new Map<DispatchRequest, CompletedRun>();
|
|
2644
|
+
const start = async (request: DispatchRequest): Promise<CompletedRun> => {
|
|
2645
|
+
const ledgered = { ...request, ledger: { id: ledgerId, sequence: 0 } };
|
|
2646
|
+
const handle = await deps.dispatch.dispatch(ledgered);
|
|
2647
|
+
activeIds.add(handle.runId);
|
|
2648
|
+
const registered = deps.runEvents.registerSingle(handle, request.agentId, fallbackProgressBus(deps));
|
|
2649
|
+
const value = await registered.completion;
|
|
2650
|
+
activeIds.delete(handle.runId);
|
|
2651
|
+
const run = completeRun(deps, value.receipt, value.summary);
|
|
2652
|
+
completed.set(request, run);
|
|
2653
|
+
return run;
|
|
2654
|
+
};
|
|
2655
|
+
const abort = (bySignal: boolean): void => {
|
|
2656
|
+
const reason = bySignal ? undefined : ({ cause: "timeout", detail: `timed out after ${timeoutMs}ms` } as const);
|
|
2657
|
+
for (const runId of activeIds) deps.dispatch.abort(runId, reason);
|
|
2658
|
+
};
|
|
2659
|
+
const onSignalAbort = (): void => abort(true);
|
|
2660
|
+
const timer = timeoutMs === undefined ? null : setTimeout(() => abort(false), timeoutMs);
|
|
2661
|
+
signal?.addEventListener("abort", onSignalAbort, { once: true });
|
|
2662
|
+
try {
|
|
2663
|
+
const readerPromises = readers.map((request) => start(request));
|
|
2664
|
+
for (const writer of writers) await start(writer);
|
|
2665
|
+
await Promise.all(readerPromises);
|
|
2666
|
+
return {
|
|
2667
|
+
runs: requests.flatMap((request) => {
|
|
2668
|
+
const run = completed.get(request);
|
|
2669
|
+
return run === undefined ? [] : [run];
|
|
2670
|
+
}),
|
|
2671
|
+
board: renderAgentLedgerBoard(ledgerId),
|
|
2672
|
+
};
|
|
2673
|
+
} finally {
|
|
2674
|
+
if (timer !== null) clearTimeout(timer);
|
|
2675
|
+
signal?.removeEventListener("abort", onSignalAbort);
|
|
2676
|
+
await closeAgentLedger(ledgerId);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
@@ -8,7 +8,23 @@ import type { DispatchBackgroundRegistry } from "./dispatch-background.js";
|
|
|
8
8
|
import type { DispatchPlanView, ResolvedDispatchPlanArtifact } from "./dispatch-plan.js";
|
|
9
9
|
import type { DispatchRunEventRegistry } from "./dispatch-run-events.js";
|
|
10
10
|
|
|
11
|
-
export type DispatchMode = "parallel" | "sequential" | "pipeline" | "compete";
|
|
11
|
+
export type DispatchMode = "parallel" | "sequential" | "pipeline" | "compete" | "council";
|
|
12
|
+
|
|
13
|
+
export interface DispatchCouncilMember {
|
|
14
|
+
label: string;
|
|
15
|
+
target: string;
|
|
16
|
+
model?: string;
|
|
17
|
+
thinking?: string;
|
|
18
|
+
color?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DispatchCouncilSettings {
|
|
22
|
+
members: DispatchCouncilMember[];
|
|
23
|
+
synthesis: "none" | "judge" | "vote";
|
|
24
|
+
rounds: number;
|
|
25
|
+
judge?: DispatchCompeteSettings["judge"];
|
|
26
|
+
resolvedTasks?: ReadonlyArray<ResolvedDispatchPlanArtifact["tasks"][number]>;
|
|
27
|
+
}
|
|
12
28
|
|
|
13
29
|
export interface DispatchReviewSettings {
|
|
14
30
|
reviewer?: string;
|
|
@@ -33,8 +49,10 @@ export interface DispatchRunExecutionSnapshot {
|
|
|
33
49
|
readonly planView: DispatchPlanView;
|
|
34
50
|
readonly requests: ReadonlyArray<DispatchRequest>;
|
|
35
51
|
readonly mode: DispatchMode;
|
|
52
|
+
readonly writers: 1 | undefined;
|
|
36
53
|
readonly review: DispatchReviewSettings | undefined;
|
|
37
54
|
readonly compete: DispatchCompeteSettings | undefined;
|
|
55
|
+
readonly council: DispatchCouncilSettings | undefined;
|
|
38
56
|
readonly detach: boolean;
|
|
39
57
|
readonly timeoutMs: number | undefined;
|
|
40
58
|
readonly maxOutputBytes: number;
|
|
@@ -74,4 +92,5 @@ export interface DispatchToolDeps {
|
|
|
74
92
|
getAgentRoleFacts?: AgentRoleFactsResolver;
|
|
75
93
|
getAutonomy?: () => AutonomyLevel;
|
|
76
94
|
getCostCeilingUsd?: () => number;
|
|
95
|
+
getWorkerRosters?: () => Readonly<Record<string, { members: ReadonlyArray<DispatchCouncilMember> }>>;
|
|
77
96
|
}
|
package/src/tools/dispatch.ts
CHANGED
|
@@ -25,6 +25,65 @@ export type { DispatchToolDeps } from "./dispatch-types.js";
|
|
|
25
25
|
|
|
26
26
|
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
27
27
|
|
|
28
|
+
const CouncilMemberSchema = Type.Object(
|
|
29
|
+
{
|
|
30
|
+
label: Type.String({ pattern: "^[a-z][a-z0-9_-]{0,31}$" }),
|
|
31
|
+
target: Type.String(),
|
|
32
|
+
model: Type.Optional(Type.String()),
|
|
33
|
+
thinking: Type.Optional(StringEnum(THINKING_LEVELS)),
|
|
34
|
+
},
|
|
35
|
+
{ additionalProperties: false },
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const DispatchBudgetPhaseSchema = Type.Object(
|
|
39
|
+
{
|
|
40
|
+
toolCalls: Type.Integer({ minimum: 1 }),
|
|
41
|
+
readReserve: Type.Integer({ minimum: 0 }),
|
|
42
|
+
},
|
|
43
|
+
{ additionalProperties: false },
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const DispatchBudgetSchema = Type.Object(
|
|
47
|
+
{
|
|
48
|
+
toolCalls: Type.Integer({ minimum: 1, description: "Requested tool-call phase boundary." }),
|
|
49
|
+
readReserve: Type.Integer({ minimum: 0, description: "Requested tail reserve for canonical read calls." }),
|
|
50
|
+
retryRevision: Type.Optional(DispatchBudgetPhaseSchema),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
additionalProperties: false,
|
|
54
|
+
description:
|
|
55
|
+
"Invocation budget inside the recipe policy. retryRevision preauthorizes one ceiling for retry, result-contract revision, or review revision phases.",
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const DispatchVerificationSchema = Type.Array(
|
|
60
|
+
Type.Object(
|
|
61
|
+
{
|
|
62
|
+
check: Type.String({ description: "Declared verification check id. This is not a shell command." }),
|
|
63
|
+
timeout_ms: Type.Optional(
|
|
64
|
+
Type.Integer({ minimum: 1, description: "Requested timeout within the declared check bounds." }),
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
{ additionalProperties: false },
|
|
68
|
+
),
|
|
69
|
+
{ maxItems: 8 },
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const DispatchIntentSchema = Type.Object(
|
|
73
|
+
{
|
|
74
|
+
read_roots: Type.Optional(Type.Array(Type.String(), { maxItems: 32 })),
|
|
75
|
+
write_roots: Type.Optional(Type.Array(Type.String(), { maxItems: 32 })),
|
|
76
|
+
relevant_paths: Type.Optional(Type.Array(Type.String(), { maxItems: 32 })),
|
|
77
|
+
expected_outputs: Type.Optional(Type.Array(Type.String(), { maxItems: 32 })),
|
|
78
|
+
verification: Type.Optional(DispatchVerificationSchema),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
description:
|
|
83
|
+
"Typed repository-relative path and output intent. Verification checks are declared ids from package scripts or .clio-coder/verifiers.yaml, not shell commands.",
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
|
|
28
87
|
/**
|
|
29
88
|
* Stable, lightweight dispatch surface. Admission remains synchronous so the
|
|
30
89
|
* policy decision and provisional reservation are bound to the exact argument
|
|
@@ -62,7 +121,7 @@ export function createDispatchTool(
|
|
|
62
121
|
return {
|
|
63
122
|
name: ToolNames.Dispatch,
|
|
64
123
|
description:
|
|
65
|
-
'Dispatch one bounded task with task, or a batch with tasks (never both). Singular example: {agent:"debugger", task:"Verify the receipt boundary", briefing:"Prior receipt evidence...", detach:true}. task is the worker assignment; briefing is separate bounded parent context/data and cannot replace task. Ordinary calls auto-wait; detach:true returns ids for monitoring/steering, and collect is the authoritative terminal batch operation before final synthesis. Batch modes are parallel (default), sequential, pipeline, or compete. Task objects may include persona and
|
|
124
|
+
'Dispatch one bounded task with task, or a batch with tasks (never both). Singular example: {agent:"debugger", task:"Verify the receipt boundary", briefing:"Prior receipt evidence...", detach:true}. task is the worker assignment; briefing is separate bounded parent context/data and cannot replace task. Ordinary calls auto-wait; detach:true returns ids for monitoring/steering, and collect is the authoritative terminal batch operation before final synthesis. Batch modes are parallel (default), sequential, pipeline, or compete. Task objects may include persona, tool_profile, and a recipe-admitted budget envelope. Sealed receipts are durable evidence; report receipt integrity, evidence verification, briefing provenance, and project-context provenance separately. Call with list:true to see agents. Do not repeat an identical successful dispatch in the same user turn. Prefer this tool over inline exploration whenever work is read-only fan-out, parallel investigation, or the operator asked for a worker by name; if you cannot dispatch, say so plainly and name the reason, and never narrate or summarize a worker you did not actually dispatch.',
|
|
66
125
|
parameters: Type.Object({
|
|
67
126
|
list: Type.Optional(Type.Boolean({ description: "List available agents instead of dispatching." })),
|
|
68
127
|
from_scout: Type.Optional(
|
|
@@ -105,21 +164,47 @@ export function createDispatchTool(
|
|
|
105
164
|
tool_profile: Type.Optional(
|
|
106
165
|
StringEnum(TOOL_PROFILE_NAMES, { description: "Narrow this worker's available tools." }),
|
|
107
166
|
),
|
|
167
|
+
budget: Type.Optional(DispatchBudgetSchema),
|
|
108
168
|
target: Type.Optional(Type.String()),
|
|
109
169
|
model: Type.Optional(Type.String()),
|
|
110
170
|
node: Type.Optional(Type.String({ description: "Fleet node pin: local or a fleet.nodes id." })),
|
|
111
171
|
cwd: Type.Optional(Type.String()),
|
|
172
|
+
worktree: Type.Optional(Type.Literal(true, { description: "Run this writer in an isolated git worktree." })),
|
|
173
|
+
apply: Type.Optional(
|
|
174
|
+
StringEnum(["merge", "preserve"], { description: "Merge successful work by default, or preserve its branch." }),
|
|
175
|
+
),
|
|
176
|
+
intent: Type.Optional(DispatchIntentSchema),
|
|
177
|
+
gate: Type.Optional(
|
|
178
|
+
Type.String({
|
|
179
|
+
description: "Shorthand for one intent.verification declared check id. This is not a shell command.",
|
|
180
|
+
}),
|
|
181
|
+
),
|
|
112
182
|
}),
|
|
113
183
|
]),
|
|
114
184
|
{ description: "Tasks to dispatch; a single object or string is accepted and wrapped." },
|
|
115
185
|
),
|
|
116
186
|
),
|
|
117
187
|
mode: Type.Optional(
|
|
118
|
-
StringEnum(["parallel", "sequential", "pipeline", "compete"], {
|
|
188
|
+
StringEnum(["parallel", "sequential", "pipeline", "compete", "council"], {
|
|
119
189
|
description:
|
|
120
190
|
"Run tasks concurrently (default), one at a time, as a pipeline where each task receives the previous task's output as input data, or as a compete where N candidates build the same single task in scratch worktrees and a judge picks the winner.",
|
|
121
191
|
}),
|
|
122
192
|
),
|
|
193
|
+
roster: Type.Optional(Type.String({ description: "Configured workers.rosters name for council mode." })),
|
|
194
|
+
members: Type.Optional(Type.Array(CouncilMemberSchema, { minItems: 2, maxItems: 5 })),
|
|
195
|
+
synthesis: Type.Optional(StringEnum(["none", "judge", "vote"] as const)),
|
|
196
|
+
rounds: Type.Optional(Type.Integer({ minimum: 1, maximum: 3 })),
|
|
197
|
+
writers: Type.Optional(
|
|
198
|
+
Type.Literal(1, {
|
|
199
|
+
description: "Serialize writer admission in declared task order while readers continue concurrently.",
|
|
200
|
+
}),
|
|
201
|
+
),
|
|
202
|
+
worktree: Type.Optional(
|
|
203
|
+
Type.Literal(true, { description: "Run a singular writer task in an isolated git worktree." }),
|
|
204
|
+
),
|
|
205
|
+
apply: Type.Optional(
|
|
206
|
+
StringEnum(["merge", "preserve"], { description: "Merge successful work by default, or preserve its branch." }),
|
|
207
|
+
),
|
|
123
208
|
detach: Type.Optional(
|
|
124
209
|
Type.Boolean({
|
|
125
210
|
description:
|
|
@@ -178,7 +263,8 @@ export function createDispatchTool(
|
|
|
178
263
|
),
|
|
179
264
|
agent: Type.Optional(
|
|
180
265
|
Type.String({
|
|
181
|
-
description:
|
|
266
|
+
description:
|
|
267
|
+
"Default agent recipe for string tasks, or auto for bounded agent selection (default coder; researcher for mode council).",
|
|
182
268
|
}),
|
|
183
269
|
),
|
|
184
270
|
briefing: Type.Optional(
|
|
@@ -186,10 +272,17 @@ export function createDispatchTool(
|
|
|
186
272
|
description: `Separate bounded parent context/data for task, or the shared default for tasks; never worker instructions and never a task replacement. Max ${DISPATCH_BRIEFING_MAX_BYTES} UTF-8 bytes.`,
|
|
187
273
|
}),
|
|
188
274
|
),
|
|
275
|
+
intent: Type.Optional(DispatchIntentSchema),
|
|
276
|
+
gate: Type.Optional(
|
|
277
|
+
Type.String({
|
|
278
|
+
description: "Shorthand for one intent.verification declared check id. This is not a shell command.",
|
|
279
|
+
}),
|
|
280
|
+
),
|
|
189
281
|
persona: Type.Optional(
|
|
190
282
|
Type.String({ description: "Default ad-hoc specialist persona for dispatched tasks, max 8000 chars." }),
|
|
191
283
|
),
|
|
192
284
|
tool_profile: Type.Optional(StringEnum(TOOL_PROFILE_NAMES, { description: "Default worker tool profile." })),
|
|
285
|
+
budget: Type.Optional(DispatchBudgetSchema),
|
|
193
286
|
target: Type.Optional(Type.String({ description: "Default configured target id (omit for fleet default)." })),
|
|
194
287
|
model: Type.Optional(Type.String({ description: "Default model override." })),
|
|
195
288
|
node: Type.Optional(
|