@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
|
@@ -7,10 +7,21 @@ export type ResultContract =
|
|
|
7
7
|
| { kind: "architect-plan"; path: string }
|
|
8
8
|
| { kind: "scout-report" }
|
|
9
9
|
| { kind: "verifier-report" }
|
|
10
|
+
| { kind: "council-report" }
|
|
10
11
|
| { kind: "debugger-report" }
|
|
11
12
|
| { kind: "research-report" }
|
|
12
13
|
| { kind: "mutation-report" }
|
|
13
14
|
| { kind: "provenance-report" }
|
|
15
|
+
| { kind: "delegation-plan" }
|
|
16
|
+
/**
|
|
17
|
+
* The advisory answer `/oracle` asks for. It exists as its own kind because
|
|
18
|
+
* the answer shape is the whole point of the command: a verdict, the
|
|
19
|
+
* strongest challenge the advisor can mount, the evidence that would change
|
|
20
|
+
* its mind, and the prior decisions it read. Folding it onto
|
|
21
|
+
* `research-report` would ask for claims and evidence instead, and the
|
|
22
|
+
* challenge and the reversal condition would have nowhere to go.
|
|
23
|
+
*/
|
|
24
|
+
| { kind: "oracle-report" }
|
|
14
25
|
| { kind: "external-delegation" }
|
|
15
26
|
/**
|
|
16
27
|
* The run's postcondition is the artifact it left on disk, so its terminal
|
|
@@ -58,6 +69,7 @@ export interface ResultContractValidation {
|
|
|
58
69
|
scout?: ScoutResult;
|
|
59
70
|
/** Parsed Verifier data for the dispatch review-gate projection. */
|
|
60
71
|
verifier?: VerifierResult;
|
|
72
|
+
delegationPlan?: DelegationPlanResult;
|
|
61
73
|
reason?: string;
|
|
62
74
|
}
|
|
63
75
|
|
|
@@ -211,6 +223,159 @@ export interface VerifierResult {
|
|
|
211
223
|
checks: ReadonlyArray<VerifierCheck>;
|
|
212
224
|
}
|
|
213
225
|
|
|
226
|
+
export interface DelegationPlanResultTask {
|
|
227
|
+
id: string;
|
|
228
|
+
agent: string;
|
|
229
|
+
description: string;
|
|
230
|
+
depends_on: ReadonlyArray<string>;
|
|
231
|
+
writes: ReadonlyArray<string>;
|
|
232
|
+
mode?: "sequential" | "parallel";
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface DelegationPlanResult {
|
|
236
|
+
tasks: ReadonlyArray<DelegationPlanResultTask>;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function parseDelegationPlanResult(output: string | null): DelegationPlanResult | null {
|
|
240
|
+
const parsed = parseJson(output);
|
|
241
|
+
if (
|
|
242
|
+
!parsed.ok ||
|
|
243
|
+
!hasOnlyKeys(parsed.value, ["tasks"]) ||
|
|
244
|
+
!Array.isArray(parsed.value.tasks) ||
|
|
245
|
+
parsed.value.tasks.length === 0
|
|
246
|
+
)
|
|
247
|
+
return null;
|
|
248
|
+
const tasks: DelegationPlanResultTask[] = [];
|
|
249
|
+
for (const raw of parsed.value.tasks) {
|
|
250
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
251
|
+
const task = raw as Record<string, unknown>;
|
|
252
|
+
if (
|
|
253
|
+
!hasOnlyKeys(task, ["id", "agent", "description", "depends_on", "writes", "mode"]) ||
|
|
254
|
+
!string(task.id) ||
|
|
255
|
+
!string(task.agent) ||
|
|
256
|
+
!string(task.description) ||
|
|
257
|
+
!Array.isArray(task.depends_on) ||
|
|
258
|
+
task.depends_on.some((entry) => !string(entry)) ||
|
|
259
|
+
!Array.isArray(task.writes) ||
|
|
260
|
+
task.writes.some((entry) => !string(entry)) ||
|
|
261
|
+
(task.mode !== undefined && task.mode !== "sequential" && task.mode !== "parallel")
|
|
262
|
+
)
|
|
263
|
+
return null;
|
|
264
|
+
tasks.push({
|
|
265
|
+
id: task.id,
|
|
266
|
+
agent: task.agent,
|
|
267
|
+
description: task.description,
|
|
268
|
+
depends_on: [...task.depends_on] as string[],
|
|
269
|
+
writes: [...task.writes] as string[],
|
|
270
|
+
...(task.mode !== undefined ? { mode: task.mode as "sequential" | "parallel" } : {}),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return { tasks };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export interface CouncilReportMember {
|
|
277
|
+
label: string;
|
|
278
|
+
runId: string;
|
|
279
|
+
round: number;
|
|
280
|
+
answer: string;
|
|
281
|
+
verdict?: string;
|
|
282
|
+
failed?: { reason: string };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export interface CouncilReport {
|
|
286
|
+
members: CouncilReportMember[];
|
|
287
|
+
synthesis: {
|
|
288
|
+
kind: "none" | "judge" | "vote";
|
|
289
|
+
text?: string;
|
|
290
|
+
verdict?: string;
|
|
291
|
+
tally?: Record<string, number>;
|
|
292
|
+
judgeRunId?: string;
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function parseCouncilReport(output: string | null): CouncilReport | null {
|
|
297
|
+
if (output === null) return null;
|
|
298
|
+
const parsed = parseJsonObjectPayload(output);
|
|
299
|
+
if (!parsed.ok) return null;
|
|
300
|
+
const value = parsed.value;
|
|
301
|
+
if (!hasOnlyKeys(value, ["members", "synthesis"]) || !Array.isArray(value.members) || value.members.length === 0) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
const members: CouncilReportMember[] = [];
|
|
305
|
+
for (const raw of value.members) {
|
|
306
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
307
|
+
const member = raw as Record<string, unknown>;
|
|
308
|
+
if (
|
|
309
|
+
!hasOnlyKeys(member, ["label", "runId", "round", "answer", "verdict", "failed"]) ||
|
|
310
|
+
!string(member.label) ||
|
|
311
|
+
!string(member.runId) ||
|
|
312
|
+
!Number.isInteger(member.round) ||
|
|
313
|
+
Number(member.round) < 1 ||
|
|
314
|
+
typeof member.answer !== "string" ||
|
|
315
|
+
(member.verdict !== undefined && !string(member.verdict))
|
|
316
|
+
)
|
|
317
|
+
return null;
|
|
318
|
+
let failed: { reason: string } | undefined;
|
|
319
|
+
if (member.failed !== undefined) {
|
|
320
|
+
if (
|
|
321
|
+
member.failed === null ||
|
|
322
|
+
typeof member.failed !== "object" ||
|
|
323
|
+
Array.isArray(member.failed) ||
|
|
324
|
+
!hasOnlyKeys(member.failed as Record<string, unknown>, ["reason"]) ||
|
|
325
|
+
!string((member.failed as Record<string, unknown>).reason)
|
|
326
|
+
)
|
|
327
|
+
return null;
|
|
328
|
+
failed = { reason: (member.failed as { reason: string }).reason };
|
|
329
|
+
}
|
|
330
|
+
members.push({
|
|
331
|
+
label: member.label,
|
|
332
|
+
runId: member.runId,
|
|
333
|
+
round: Number(member.round),
|
|
334
|
+
answer: member.answer,
|
|
335
|
+
...(member.verdict !== undefined ? { verdict: member.verdict as string } : {}),
|
|
336
|
+
...(failed !== undefined ? { failed } : {}),
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
if (value.synthesis === null || typeof value.synthesis !== "object" || Array.isArray(value.synthesis)) return null;
|
|
340
|
+
const rawSynthesis = value.synthesis as Record<string, unknown>;
|
|
341
|
+
if (!hasOnlyKeys(rawSynthesis, ["kind", "text", "verdict", "tally", "judgeRunId"])) return null;
|
|
342
|
+
if (rawSynthesis.kind !== "none" && rawSynthesis.kind !== "judge" && rawSynthesis.kind !== "vote") return null;
|
|
343
|
+
for (const key of ["text", "verdict", "judgeRunId"] as const) {
|
|
344
|
+
if (rawSynthesis[key] !== undefined && !string(rawSynthesis[key])) return null;
|
|
345
|
+
}
|
|
346
|
+
let tally: Record<string, number> | undefined;
|
|
347
|
+
if (rawSynthesis.tally !== undefined) {
|
|
348
|
+
if (rawSynthesis.tally === null || typeof rawSynthesis.tally !== "object" || Array.isArray(rawSynthesis.tally))
|
|
349
|
+
return null;
|
|
350
|
+
tally = {};
|
|
351
|
+
for (const [key, count] of Object.entries(rawSynthesis.tally as Record<string, unknown>)) {
|
|
352
|
+
if (!key || !Number.isInteger(count) || Number(count) < 0) return null;
|
|
353
|
+
tally[key] = Number(count);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
members,
|
|
358
|
+
synthesis: {
|
|
359
|
+
kind: rawSynthesis.kind,
|
|
360
|
+
...(rawSynthesis.text !== undefined ? { text: rawSynthesis.text as string } : {}),
|
|
361
|
+
...(rawSynthesis.verdict !== undefined ? { verdict: rawSynthesis.verdict as string } : {}),
|
|
362
|
+
...(tally !== undefined ? { tally } : {}),
|
|
363
|
+
...(rawSynthesis.judgeRunId !== undefined ? { judgeRunId: rawSynthesis.judgeRunId as string } : {}),
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Parsed `oracle-report` payload. `/oracle` renders these four fields into the
|
|
370
|
+
* operator note it hands the main agent, so the shape is the answer shape.
|
|
371
|
+
*/
|
|
372
|
+
export interface OracleResult {
|
|
373
|
+
verdict: string;
|
|
374
|
+
challenge: string;
|
|
375
|
+
changesMyMind: string;
|
|
376
|
+
citedDecisions: ReadonlyArray<string>;
|
|
377
|
+
}
|
|
378
|
+
|
|
214
379
|
/**
|
|
215
380
|
* Parsed `code-report` payload. `passed` restates `exitCode === 0` so a
|
|
216
381
|
* downstream reader never has to know a runner's exit conventions, and
|
|
@@ -269,7 +434,7 @@ function success(
|
|
|
269
434
|
contract: ResultContract,
|
|
270
435
|
quality: ResultContractQuality,
|
|
271
436
|
value: unknown,
|
|
272
|
-
extra: Pick<ResultContractValidation, "scout" | "verifier" | "reason"> = {},
|
|
437
|
+
extra: Pick<ResultContractValidation, "scout" | "verifier" | "delegationPlan" | "reason"> = {},
|
|
273
438
|
): ResultContractValidation {
|
|
274
439
|
return {
|
|
275
440
|
conformance: "pass",
|
|
@@ -335,6 +500,7 @@ const RESULT_CONTRACT_KINDS: ReadonlyArray<ResultContract["kind"]> = [
|
|
|
335
500
|
"architect-plan",
|
|
336
501
|
"scout-report",
|
|
337
502
|
"verifier-report",
|
|
503
|
+
"council-report",
|
|
338
504
|
"debugger-report",
|
|
339
505
|
"research-report",
|
|
340
506
|
"mutation-report",
|
|
@@ -664,6 +830,47 @@ export function parseVerifierResult(output: string | null): VerifierResult | nul
|
|
|
664
830
|
return validation.verifier ?? null;
|
|
665
831
|
}
|
|
666
832
|
|
|
833
|
+
/**
|
|
834
|
+
* An advisory opinion is never a correctness signal about the route that
|
|
835
|
+
* produced it, so conformance is judged on the shape alone and quality stays
|
|
836
|
+
* `unmeasured`. `citedDecisions` may be empty: an honest advisor that found no
|
|
837
|
+
* settled decision bearing on the question must be able to say so.
|
|
838
|
+
*/
|
|
839
|
+
function validateOracle(contract: ResultContract, output: string | null): ResultContractValidation {
|
|
840
|
+
const parsed = parseJson(output);
|
|
841
|
+
if (!parsed.ok) return failure(contract, "unmeasured", parsed.reason);
|
|
842
|
+
const value = parsed.value;
|
|
843
|
+
if (
|
|
844
|
+
!hasOnlyKeys(value, ["verdict", "challenge", "changesMyMind", "citedDecisions"]) ||
|
|
845
|
+
!string(value.verdict) ||
|
|
846
|
+
!string(value.challenge) ||
|
|
847
|
+
!string(value.changesMyMind) ||
|
|
848
|
+
!Array.isArray(value.citedDecisions) ||
|
|
849
|
+
value.citedDecisions.some((entry) => !string(entry))
|
|
850
|
+
) {
|
|
851
|
+
return failure(
|
|
852
|
+
contract,
|
|
853
|
+
"unmeasured",
|
|
854
|
+
'Oracle result must carry verdict, challenge, changesMyMind, and a citedDecisions array of strings, for example {"verdict":"consistent with decision routing.target","challenge":"...","changesMyMind":"...","citedDecisions":["routing.target"]}',
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
return success(contract, "unmeasured", value);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/** Parse an oracle answer under the same schema its recipe contract enforces. */
|
|
861
|
+
export function parseOracleResult(output: string | null): OracleResult | null {
|
|
862
|
+
const validation = validateOracle({ kind: "oracle-report" }, output);
|
|
863
|
+
if (validation.conformance !== "pass") return null;
|
|
864
|
+
const parsed = parseJson(output);
|
|
865
|
+
if (!parsed.ok) return null;
|
|
866
|
+
return {
|
|
867
|
+
verdict: String(parsed.value.verdict),
|
|
868
|
+
challenge: String(parsed.value.challenge),
|
|
869
|
+
changesMyMind: String(parsed.value.changesMyMind),
|
|
870
|
+
citedDecisions: (parsed.value.citedDecisions as ReadonlyArray<string>).map((entry) => String(entry)),
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
|
|
667
874
|
/**
|
|
668
875
|
* A code step's result is always `unmeasured` for routing quality. A red suite
|
|
669
876
|
* is evidence about the repository, not about the route that ran the command,
|
|
@@ -1036,6 +1243,12 @@ export function validateResultContract(input: ResultContractValidationInput): Re
|
|
|
1036
1243
|
}
|
|
1037
1244
|
case "verifier-report":
|
|
1038
1245
|
return validateVerifier(input.contract, input.output);
|
|
1246
|
+
case "council-report": {
|
|
1247
|
+
const report = parseCouncilReport(input.output);
|
|
1248
|
+
return report === null
|
|
1249
|
+
? failure(input.contract, "fail", "council-report is malformed")
|
|
1250
|
+
: success(input.contract, "unmeasured", { council: report });
|
|
1251
|
+
}
|
|
1039
1252
|
case "debugger-report":
|
|
1040
1253
|
return validateDebugger(input.contract, input.output);
|
|
1041
1254
|
case "research-report":
|
|
@@ -1044,6 +1257,18 @@ export function validateResultContract(input: ResultContractValidationInput): Re
|
|
|
1044
1257
|
return validateMutation(input.contract, input);
|
|
1045
1258
|
case "provenance-report":
|
|
1046
1259
|
return validateProvenance(input.contract, input.output);
|
|
1260
|
+
case "delegation-plan": {
|
|
1261
|
+
const plan = parseDelegationPlanResult(input.output);
|
|
1262
|
+
return plan === null
|
|
1263
|
+
? failure(
|
|
1264
|
+
input.contract,
|
|
1265
|
+
"fail",
|
|
1266
|
+
"delegation-plan must carry tasks with id, agent, description, depends_on, writes, and optional mode",
|
|
1267
|
+
)
|
|
1268
|
+
: success(input.contract, "unmeasured", plan, { delegationPlan: plan });
|
|
1269
|
+
}
|
|
1270
|
+
case "oracle-report":
|
|
1271
|
+
return validateOracle(input.contract, input.output);
|
|
1047
1272
|
case "external-delegation":
|
|
1048
1273
|
return success(input.contract, "unmeasured", { external: true });
|
|
1049
1274
|
case "artifact-report":
|
|
@@ -1120,6 +1345,8 @@ function resultContractShape(contract: ResultContract): string {
|
|
|
1120
1345
|
return '{"findings":[{"claim":"what you observed","path":"src/file.ts","line":1}],"needsSplit":false,"proposedSubtasks":[]} or {"findings":[],"needsSplit":true,"proposedSubtasks":[{"id":"inspect","task":"Inspect the boundary","dependencies":[],"expectedResultContract":"scout-report","requestedAuthority":"read-only"}]} or, if you cannot produce a citation you are sure of, at least {"findings":[{"claim":"what you observed"}]}, which is accepted as an ungrounded lead rather than losing the run';
|
|
1121
1346
|
case "verifier-report":
|
|
1122
1347
|
return '{"verdict":"pass","checks":[{"name":"npm run typecheck","passed":true,"evidence":"exit 0"}]}';
|
|
1348
|
+
case "council-report":
|
|
1349
|
+
return '{"members":[{"label":"alpha","runId":"run-1","round":1,"answer":"..."}],"synthesis":{"kind":"none"}}';
|
|
1123
1350
|
case "debugger-report":
|
|
1124
1351
|
return '{"diagnosis":"...","reproduction":"reproduced","evidence":["..."]}';
|
|
1125
1352
|
case "research-report":
|
|
@@ -1128,6 +1355,10 @@ function resultContractShape(contract: ResultContract): string {
|
|
|
1128
1355
|
return '{"mutatedPaths":["src/file.ts"],"validations":[{"name":"npm test","passed":true,"evidence":"exit 0"}],"commitMessage":"optional: the commit message for this change","summary":"optional: one line"}';
|
|
1129
1356
|
case "provenance-report":
|
|
1130
1357
|
return '{"confirmedFacts":["..."],"missingEvidence":["..."],"nextInspections":["..."]}';
|
|
1358
|
+
case "delegation-plan":
|
|
1359
|
+
return '{"tasks":[{"id":"build","agent":"coder","description":"Implement the change","depends_on":[],"writes":["src"],"mode":"parallel"}]}';
|
|
1360
|
+
case "oracle-report":
|
|
1361
|
+
return '{"verdict":"one line","challenge":"the strongest objection you can mount","changesMyMind":"the evidence that would reverse the verdict","citedDecisions":["decision key or task id you relied on"]}';
|
|
1131
1362
|
case "external-delegation":
|
|
1132
1363
|
return "any final text";
|
|
1133
1364
|
case "artifact-report":
|
|
@@ -1147,12 +1378,13 @@ export interface ResultContractRepairInput {
|
|
|
1147
1378
|
attempt: number;
|
|
1148
1379
|
/** `path:line` anchors from reads that succeeded in this run. */
|
|
1149
1380
|
anchors: ReadonlyArray<string>;
|
|
1381
|
+
/** True only when admission preauthorized a larger tool-call revision phase. */
|
|
1382
|
+
toolsAvailable?: boolean;
|
|
1150
1383
|
}
|
|
1151
1384
|
|
|
1152
1385
|
/**
|
|
1153
|
-
* One repair directive. It states the validator's reason
|
|
1154
|
-
*
|
|
1155
|
-
* terminal document from evidence it already has.
|
|
1386
|
+
* One repair directive. It states the validator's reason and exact shape. Tool
|
|
1387
|
+
* use remains closed unless dispatch preauthorized a larger revision phase.
|
|
1156
1388
|
*/
|
|
1157
1389
|
export function resultContractRepairMessage(input: ResultContractRepairInput): string {
|
|
1158
1390
|
const last = input.attempt >= RESULT_CONTRACT_REPAIR_LIMIT;
|
|
@@ -1161,7 +1393,9 @@ export function resultContractRepairMessage(input: ResultContractRepairInput): s
|
|
|
1161
1393
|
? "FINAL RESULT REQUIRED IN THIS RESPONSE. Your previous response still did not satisfy this run's result contract."
|
|
1162
1394
|
: "Your previous response did not satisfy this run's result contract.",
|
|
1163
1395
|
`Validator reason: ${input.reason}`,
|
|
1164
|
-
|
|
1396
|
+
input.toolsAvailable === true
|
|
1397
|
+
? "You may use the admitted tools to repair this validator failure, then emit the required terminal result."
|
|
1398
|
+
: "Tool use is over. Answer from the evidence you already gathered.",
|
|
1165
1399
|
`Emit exactly this shape and nothing else: ${resultContractShape(input.contract)}`,
|
|
1166
1400
|
"Do not add prose, code fences, or commentary around it. Do not describe work you intend to do next.",
|
|
1167
1401
|
];
|
|
@@ -1250,13 +1484,16 @@ export function parseResultContract(value: unknown, sourcePath: string): ResultC
|
|
|
1250
1484
|
const kinds = [
|
|
1251
1485
|
"scout-report",
|
|
1252
1486
|
"verifier-report",
|
|
1487
|
+
"council-report",
|
|
1253
1488
|
"debugger-report",
|
|
1254
1489
|
"research-report",
|
|
1255
1490
|
"mutation-report",
|
|
1256
1491
|
"provenance-report",
|
|
1492
|
+
"oracle-report",
|
|
1257
1493
|
"external-delegation",
|
|
1258
1494
|
"artifact-report",
|
|
1259
1495
|
"context-handbook",
|
|
1496
|
+
"delegation-plan",
|
|
1260
1497
|
] as const;
|
|
1261
1498
|
if (!(kinds as ReadonlyArray<string>).includes(record.kind) || !only("kind")) {
|
|
1262
1499
|
throw new Error(`agent recipe: ${sourcePath}: resultContract.kind is unsupported or has unknown keys`);
|
|
@@ -28,6 +28,10 @@ const HOT_RELOAD_FIELDS = new Set<string>([
|
|
|
28
28
|
"modelSelector",
|
|
29
29
|
"terminal.smoothStreaming",
|
|
30
30
|
"attribution.gitCommits",
|
|
31
|
+
// The watchdog registration reads its settings live on every trigger, so
|
|
32
|
+
// enabling it, retargeting it, or changing its cadence takes effect on the
|
|
33
|
+
// next turn boundary without a restart or a session-routing patch.
|
|
34
|
+
"watchdog",
|
|
31
35
|
]);
|
|
32
36
|
|
|
33
37
|
const NEXT_TURN_FIELDS = new Set<string>([
|
|
@@ -2,6 +2,7 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join, parse } from "node:path";
|
|
4
4
|
import type { ContextActivityPayload } from "../../core/bus-events.js";
|
|
5
|
+
import { createTomlFileReader, type TomlFileReader, tomlTableAt } from "../../core/toml.js";
|
|
5
6
|
import { classifyProjectPreload, type ProjectPreloadClass } from "../prompts/preload.js";
|
|
6
7
|
import { detectProjectType, type ProjectType } from "../session/workspace/project-type.js";
|
|
7
8
|
import {
|
|
@@ -15,7 +16,7 @@ import { type ClioMdSection, type ParsedClioMd, parseClioMd, serializeClioMd, tr
|
|
|
15
16
|
import { buildCodewikiCandidate, coordinateCodewikiWrite } from "./codewiki/coordinator.js";
|
|
16
17
|
import type { Codewiki } from "./codewiki/schema.js";
|
|
17
18
|
import type { Fingerprint } from "./fingerprint.js";
|
|
18
|
-
import {
|
|
19
|
+
import { type ProjectMetadata, readProjectMetadata } from "./project-metadata.js";
|
|
19
20
|
import { renderPromptContext } from "./prompt-context.js";
|
|
20
21
|
import type { SiblingContextFile } from "./sibling-files.js";
|
|
21
22
|
import {
|
|
@@ -49,6 +50,8 @@ export interface BootstrapIo {
|
|
|
49
50
|
export interface BootstrapGenerateInput {
|
|
50
51
|
cwd: string;
|
|
51
52
|
expectedProjectName?: string;
|
|
53
|
+
/** Operation-scoped parsed TOML documents shared by bootstrap consumers. */
|
|
54
|
+
tomlFiles?: TomlFileReader;
|
|
52
55
|
projectType: ProjectType;
|
|
53
56
|
siblingFiles: ReadonlyArray<SiblingContextFile>;
|
|
54
57
|
adoption: AdoptionScanResult;
|
|
@@ -211,8 +214,8 @@ function projectNameFrom(metadata: ProjectMetadata, cwd: string): string {
|
|
|
211
214
|
return titleFromPackageName(parse(cwd).base || "Project");
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
function projectName(cwd: string): string {
|
|
215
|
-
return projectNameFrom(readProjectMetadata(cwd), cwd);
|
|
217
|
+
function projectName(cwd: string, tomlFiles: TomlFileReader): string {
|
|
218
|
+
return projectNameFrom(readProjectMetadata(cwd, tomlFiles), cwd);
|
|
216
219
|
}
|
|
217
220
|
|
|
218
221
|
function projectTypeLabel(projectType: ProjectType): string {
|
|
@@ -270,8 +273,9 @@ function resolveDefaultIdentity(
|
|
|
270
273
|
cwd: string,
|
|
271
274
|
projectType: ProjectType,
|
|
272
275
|
files: ReadonlyArray<SiblingContextFile>,
|
|
276
|
+
tomlFiles: TomlFileReader,
|
|
273
277
|
): DeterministicIdentity {
|
|
274
|
-
const metadata = readProjectMetadata(cwd);
|
|
278
|
+
const metadata = readProjectMetadata(cwd, tomlFiles);
|
|
275
279
|
const name = projectNameFrom(metadata, cwd);
|
|
276
280
|
const context = allContextText(files);
|
|
277
281
|
if (/Clio owns the agent loop/i.test(context) && /pi-(?:ai|SDK)/i.test(context)) {
|
|
@@ -303,8 +307,8 @@ function resolveDefaultIdentity(
|
|
|
303
307
|
* which tells a reader nothing the directory name did not. A model sentence is
|
|
304
308
|
* strictly better than that, and only than that.
|
|
305
309
|
*/
|
|
306
|
-
function stabilizedIdentity(input: BootstrapGenerateInput, modelIdentity: unknown): string {
|
|
307
|
-
const deterministic = resolveDefaultIdentity(input.cwd, input.projectType, input.siblingFiles);
|
|
310
|
+
function stabilizedIdentity(input: BootstrapGenerateInput, modelIdentity: unknown, tomlFiles: TomlFileReader): string {
|
|
311
|
+
const deterministic = resolveDefaultIdentity(input.cwd, input.projectType, input.siblingFiles, tomlFiles);
|
|
308
312
|
const model = typeof modelIdentity === "string" ? modelIdentity.trim() : "";
|
|
309
313
|
if (deterministic.bare && model.length > 0) return model;
|
|
310
314
|
return deterministic.text;
|
|
@@ -504,23 +508,21 @@ function cmakeVerificationLines(cwd: string): string[] {
|
|
|
504
508
|
* other way. Tox wins when both are declared because it typically wraps the
|
|
505
509
|
* pytest run.
|
|
506
510
|
*/
|
|
507
|
-
function pythonVerificationLines(cwd: string): string[] {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
pyproject = readFileSync(join(cwd, "pyproject.toml"), "utf8");
|
|
511
|
-
} catch {
|
|
512
|
-
pyproject = "";
|
|
513
|
-
}
|
|
514
|
-
if (existsSync(join(cwd, "tox.ini")) || hasTomlTable(pyproject, "tool.tox")) {
|
|
511
|
+
function pythonVerificationLines(cwd: string, tomlFiles: TomlFileReader): string[] {
|
|
512
|
+
const pyproject = tomlFiles.read("pyproject.toml");
|
|
513
|
+
if (existsSync(join(cwd, "tox.ini")) || (pyproject !== null && tomlTableAt(pyproject, ["tool", "tox"]))) {
|
|
515
514
|
return ["Run `tox` before handoff."];
|
|
516
515
|
}
|
|
517
|
-
if (
|
|
516
|
+
if (
|
|
517
|
+
existsSync(join(cwd, "pytest.ini")) ||
|
|
518
|
+
(pyproject !== null && tomlTableAt(pyproject, ["tool", "pytest", "ini_options"]))
|
|
519
|
+
) {
|
|
518
520
|
return ["Run `pytest` before handoff."];
|
|
519
521
|
}
|
|
520
522
|
return [];
|
|
521
523
|
}
|
|
522
524
|
|
|
523
|
-
function verificationSection(cwd: string): ClioMdSection | null {
|
|
525
|
+
function verificationSection(cwd: string, tomlFiles: TomlFileReader): ClioMdSection | null {
|
|
524
526
|
const scripts = packageScripts(cwd);
|
|
525
527
|
const pm = packageManager(cwd);
|
|
526
528
|
const hasScript = (name: string) => typeof scripts[name] === "string";
|
|
@@ -557,7 +559,8 @@ function verificationSection(cwd: string): ClioMdSection | null {
|
|
|
557
559
|
// Cargo's commands are defined by the toolchain, not by each project, so
|
|
558
560
|
// naming them for any Cargo workspace carries the same confidence as a
|
|
559
561
|
// declared package script.
|
|
560
|
-
|
|
562
|
+
const cargo = tomlFiles.read("Cargo.toml");
|
|
563
|
+
if (cargo !== null && (tomlTableAt(cargo, ["package"]) || tomlTableAt(cargo, ["workspace"]))) {
|
|
561
564
|
lines.push("Run `cargo build` and `cargo test` before handoff.");
|
|
562
565
|
}
|
|
563
566
|
// Go's commands are defined by the toolchain, not by each project, so
|
|
@@ -566,7 +569,7 @@ function verificationSection(cwd: string): ClioMdSection | null {
|
|
|
566
569
|
if (existsSync(join(cwd, "go.mod"))) {
|
|
567
570
|
lines.push("Run `go build ./...` and `go test ./...` before handoff.");
|
|
568
571
|
}
|
|
569
|
-
lines.push(...pythonVerificationLines(cwd));
|
|
572
|
+
lines.push(...pythonVerificationLines(cwd, tomlFiles));
|
|
570
573
|
if (lines.length === 0) return null;
|
|
571
574
|
return { title: "Verification expectations", body: lines.join(" ") };
|
|
572
575
|
}
|
|
@@ -717,6 +720,7 @@ function stabilizeGeneratedOutput(
|
|
|
717
720
|
onModelSectionRetained?: () => void,
|
|
718
721
|
groundModelOutput = false,
|
|
719
722
|
): BootstrapStructuredOutput {
|
|
723
|
+
const tomlFiles = input.tomlFiles ?? createTomlFileReader(input.cwd);
|
|
720
724
|
const existing = input.existingClioMd;
|
|
721
725
|
const conventions: string[] = [];
|
|
722
726
|
for (const convention of existing?.conventions ?? []) pushUnique(conventions, convention);
|
|
@@ -727,7 +731,7 @@ function stabilizeGeneratedOutput(
|
|
|
727
731
|
for (const invariant of existing?.invariants ?? []) pushUnique(invariants, invariant);
|
|
728
732
|
for (const invariant of inferInvariants(input.siblingFiles)) pushUnique(invariants, invariant);
|
|
729
733
|
|
|
730
|
-
const verification = verificationSection(input.cwd);
|
|
734
|
+
const verification = verificationSection(input.cwd, tomlFiles);
|
|
731
735
|
const inferredSections = inferHeuristicSections(input);
|
|
732
736
|
const existingSections = existing?.sections ?? [];
|
|
733
737
|
const ordinarySections: ClioMdSection[] = [];
|
|
@@ -754,8 +758,8 @@ function stabilizeGeneratedOutput(
|
|
|
754
758
|
}
|
|
755
759
|
return {
|
|
756
760
|
...base,
|
|
757
|
-
projectName: existing?.projectName ?? projectName(input.cwd),
|
|
758
|
-
identity: existing?.identity ?? stabilizedIdentity(input, base.identity),
|
|
761
|
+
projectName: existing?.projectName ?? projectName(input.cwd, tomlFiles),
|
|
762
|
+
identity: existing?.identity ?? stabilizedIdentity(input, base.identity, tomlFiles),
|
|
759
763
|
conventions: conventions.slice(0, 6),
|
|
760
764
|
invariants: invariants.slice(0, 3),
|
|
761
765
|
sections: [...retainedOrdinarySections, ...(verification ? [verification] : [])],
|
|
@@ -819,9 +823,11 @@ function inferHeuristicSections(input: BootstrapGenerateInput): ClioMdSection[]
|
|
|
819
823
|
* unavailable or fails.
|
|
820
824
|
*/
|
|
821
825
|
function heuristicBootstrapOutputSync(input: BootstrapGenerateInput): BootstrapStructuredOutput {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
826
|
+
const tomlFiles = input.tomlFiles ?? createTomlFileReader(input.cwd);
|
|
827
|
+
const sharedInput = input.tomlFiles ? input : { ...input, tomlFiles };
|
|
828
|
+
return stabilizeGeneratedOutput(sharedInput, {
|
|
829
|
+
projectName: projectName(input.cwd, tomlFiles),
|
|
830
|
+
identity: resolveDefaultIdentity(input.cwd, input.projectType, input.siblingFiles, tomlFiles).text,
|
|
825
831
|
conventions: inferConventions(input.cwd, input.siblingFiles, input.codewiki),
|
|
826
832
|
invariants: inferInvariants(input.siblingFiles),
|
|
827
833
|
sections: inferHeuristicSections(input),
|
|
@@ -1229,6 +1235,7 @@ function codewikiEntryPoints(codewiki: Codewiki, limit = 6): string[] {
|
|
|
1229
1235
|
|
|
1230
1236
|
export async function runBootstrap(input: RunBootstrapInput = {}): Promise<RunBootstrapResult> {
|
|
1231
1237
|
const cwd = input.cwd ?? process.cwd();
|
|
1238
|
+
const tomlFiles = createTomlFileReader(cwd);
|
|
1232
1239
|
const projectType = detectProjectType(cwd);
|
|
1233
1240
|
progress(input, {
|
|
1234
1241
|
phase: "scan",
|
|
@@ -1327,7 +1334,8 @@ export async function runBootstrap(input: RunBootstrapInput = {}): Promise<RunBo
|
|
|
1327
1334
|
});
|
|
1328
1335
|
output = await (input.generate ?? heuristicBootstrapOutput)({
|
|
1329
1336
|
cwd,
|
|
1330
|
-
expectedProjectName: projectName(cwd),
|
|
1337
|
+
expectedProjectName: projectName(cwd, tomlFiles),
|
|
1338
|
+
tomlFiles,
|
|
1331
1339
|
projectType,
|
|
1332
1340
|
siblingFiles,
|
|
1333
1341
|
adoption,
|
|
@@ -1342,7 +1350,8 @@ export async function runBootstrap(input: RunBootstrapInput = {}): Promise<RunBo
|
|
|
1342
1350
|
output = stabilizeGeneratedOutput(
|
|
1343
1351
|
{
|
|
1344
1352
|
cwd,
|
|
1345
|
-
expectedProjectName: projectName(cwd),
|
|
1353
|
+
expectedProjectName: projectName(cwd, tomlFiles),
|
|
1354
|
+
tomlFiles,
|
|
1346
1355
|
projectType,
|
|
1347
1356
|
siblingFiles,
|
|
1348
1357
|
adoption,
|
|
@@ -1372,7 +1381,7 @@ export async function runBootstrap(input: RunBootstrapInput = {}): Promise<RunBo
|
|
|
1372
1381
|
} else {
|
|
1373
1382
|
output = existingParsed
|
|
1374
1383
|
? bootstrapOutputFromParsed(existingParsed)
|
|
1375
|
-
: await heuristicBootstrapOutput({ cwd, projectType, siblingFiles, adoption, codewiki });
|
|
1384
|
+
: await heuristicBootstrapOutput({ cwd, tomlFiles, projectType, siblingFiles, adoption, codewiki });
|
|
1376
1385
|
progress(input, {
|
|
1377
1386
|
phase: "generate",
|
|
1378
1387
|
status: "completed",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
20
20
|
import { join } from "node:path";
|
|
21
21
|
import { parse as parseYaml } from "yaml";
|
|
22
|
+
import { createTomlFileReader, type TomlFileReader, tomlTableAt } from "../../core/toml.js";
|
|
22
23
|
|
|
23
24
|
/** Longest description worth carrying into a one-sentence identity line. */
|
|
24
25
|
const MAX_DESCRIPTION_CHARS = 400;
|
|
@@ -39,7 +40,7 @@ interface MetadataFragment {
|
|
|
39
40
|
description?: string | null;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
type MetadataReader = (cwd: string) => { file: string; fragment: MetadataFragment } | null;
|
|
43
|
+
type MetadataReader = (cwd: string, tomlFiles: TomlFileReader) => { file: string; fragment: MetadataFragment } | null;
|
|
43
44
|
|
|
44
45
|
function readText(cwd: string, relative: string): string | null {
|
|
45
46
|
try {
|
|
@@ -83,54 +84,6 @@ function jsonRecord(cwd: string, relative: string): Record<string, unknown> | nu
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
/**
|
|
87
|
-
* One key from one TOML table, without a TOML parser.
|
|
88
|
-
*
|
|
89
|
-
* Deliberately narrow: it reads `key = "value"` (single-line, single- or
|
|
90
|
-
* double-quoted) from the named top-level table and understands nothing else.
|
|
91
|
-
* That is the entire shape `description` and `name` take in `pyproject.toml`
|
|
92
|
-
* and `Cargo.toml`. A multi-line or otherwise exotic value reads as absent,
|
|
93
|
-
* which costs a description and never produces a wrong one.
|
|
94
|
-
*/
|
|
95
|
-
function tomlTableValue(raw: string, table: string, key: string): string | null {
|
|
96
|
-
const lines = raw.split(/\r?\n/u);
|
|
97
|
-
let inTable = false;
|
|
98
|
-
for (const line of lines) {
|
|
99
|
-
const trimmed = line.trim();
|
|
100
|
-
if (trimmed.startsWith("[")) {
|
|
101
|
-
inTable = trimmed === `[${table}]`;
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
if (!inTable) continue;
|
|
105
|
-
const match = /^([A-Za-z0-9_-]+)\s*=\s*(.*)$/u.exec(trimmed);
|
|
106
|
-
if (!match || match[1] !== key) continue;
|
|
107
|
-
const value = (match[2] ?? "").trim();
|
|
108
|
-
const quoted = /^"((?:[^"\\]|\\.)*)"$/u.exec(value) ?? /^'([^']*)'$/u.exec(value);
|
|
109
|
-
if (!quoted) return null;
|
|
110
|
-
return (quoted[1] ?? "").replace(/\\"/gu, '"');
|
|
111
|
-
}
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Whether a TOML file declares the named top-level table, e.g. `tool.tox`.
|
|
117
|
-
* A trimmed line must start with `[table]` and carry nothing after it but an
|
|
118
|
-
* optional `# comment`, the only content TOML itself allows there. A raw
|
|
119
|
-
* `raw.includes("[table]")` check would also fire on a comment mentioning the
|
|
120
|
-
* table or a string value that happens to contain the same bracketed text;
|
|
121
|
-
* anchoring to the start of the line rules both out without a TOML parser,
|
|
122
|
-
* while still accepting the valid `[table] # comment` form that a plain
|
|
123
|
-
* equality check would have missed.
|
|
124
|
-
*/
|
|
125
|
-
export function hasTomlTable(raw: string, table: string): boolean {
|
|
126
|
-
const header = `[${table}]`;
|
|
127
|
-
return raw.split(/\r?\n/u).some((line) => {
|
|
128
|
-
const trimmed = line.trim();
|
|
129
|
-
if (!trimmed.startsWith(header)) return false;
|
|
130
|
-
return /^\s*(?:#.*)?$/u.test(trimmed.slice(header.length));
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
87
|
/** One `KEY = value` line from a Doxyfile or an INI-shaped config. */
|
|
135
88
|
function keyEqualsValue(raw: string, key: string): string | null {
|
|
136
89
|
const match = new RegExp(`^[ \\t]*${key}[ \\t]*(?:\\+?=)[ \\t]*(.+)$`, "mu").exec(raw);
|
|
@@ -182,27 +135,30 @@ const packageJson: MetadataReader = (cwd) => {
|
|
|
182
135
|
return { file: "package.json", fragment: { name: name(record.name), description: description(record.description) } };
|
|
183
136
|
};
|
|
184
137
|
|
|
185
|
-
const pyprojectToml: MetadataReader = (
|
|
186
|
-
const
|
|
187
|
-
if (
|
|
138
|
+
const pyprojectToml: MetadataReader = (_cwd, tomlFiles) => {
|
|
139
|
+
const document = tomlFiles.read("pyproject.toml");
|
|
140
|
+
if (document === null) return null;
|
|
141
|
+
const project = tomlTableAt(document, ["project"]);
|
|
142
|
+
const poetry = tomlTableAt(document, ["tool", "poetry"]);
|
|
188
143
|
// PEP 621 `[project]` first, then Poetry's older table.
|
|
189
|
-
const projectName = tomlTableValue(raw, "project", "name") ?? tomlTableValue(raw, "tool.poetry", "name");
|
|
190
|
-
const projectDescription =
|
|
191
|
-
tomlTableValue(raw, "project", "description") ?? tomlTableValue(raw, "tool.poetry", "description");
|
|
192
144
|
return {
|
|
193
145
|
file: "pyproject.toml",
|
|
194
|
-
fragment: {
|
|
146
|
+
fragment: {
|
|
147
|
+
name: name(project?.name) ?? name(poetry?.name),
|
|
148
|
+
description: description(project?.description) ?? description(poetry?.description),
|
|
149
|
+
},
|
|
195
150
|
};
|
|
196
151
|
};
|
|
197
152
|
|
|
198
|
-
const cargoToml: MetadataReader = (
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
153
|
+
const cargoToml: MetadataReader = (_cwd, tomlFiles) => {
|
|
154
|
+
const document = tomlFiles.read("Cargo.toml");
|
|
155
|
+
if (document === null) return null;
|
|
156
|
+
const packageTable = tomlTableAt(document, ["package"]);
|
|
201
157
|
return {
|
|
202
158
|
file: "Cargo.toml",
|
|
203
159
|
fragment: {
|
|
204
|
-
name: name(
|
|
205
|
-
description: description(
|
|
160
|
+
name: name(packageTable?.name),
|
|
161
|
+
description: description(packageTable?.description),
|
|
206
162
|
},
|
|
207
163
|
};
|
|
208
164
|
};
|
|
@@ -488,13 +444,13 @@ function readmeTitle(content: string): string | null {
|
|
|
488
444
|
* a CMake project that names itself but describes itself only in its README
|
|
489
445
|
* should get both, not one or neither.
|
|
490
446
|
*/
|
|
491
|
-
export function readProjectMetadata(cwd: string): ProjectMetadata {
|
|
447
|
+
export function readProjectMetadata(cwd: string, tomlFiles = createTomlFileReader(cwd)): ProjectMetadata {
|
|
492
448
|
const out: ProjectMetadata = { name: null, description: null, nameSource: null, descriptionSource: null };
|
|
493
449
|
for (const reader of METADATA_READERS) {
|
|
494
450
|
if (out.name !== null && out.description !== null) break;
|
|
495
451
|
let read: ReturnType<MetadataReader>;
|
|
496
452
|
try {
|
|
497
|
-
read = reader(cwd);
|
|
453
|
+
read = reader(cwd, tomlFiles);
|
|
498
454
|
} catch {
|
|
499
455
|
continue;
|
|
500
456
|
}
|