@iowarp/clio-coder 0.3.4 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -2
- package/CONTRIBUTING.md +6 -6
- package/README.md +16 -5
- package/dist/{acp-S5R4RR5B.js → acp-SK4MD6MM.js} +11 -11
- package/dist/{agents-P6DMMVZY.js → agents-2FN2K6ME.js} +33 -26
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-2XCZLPKS.js → auth-QIYZWM5I.js} +15 -15
- package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
- package/dist/{chunk-VAWWTKDP.js → chunk-3HAPLH5M.js} +11 -11
- package/dist/{chunk-YCWGATWI.js → chunk-465YSENW.js} +3 -3
- package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
- package/dist/{chunk-UZHIZC5S.js → chunk-4DWFMQDR.js} +61 -76
- package/dist/{chunk-ZWMF7253.js → chunk-5C3AQNDW.js} +328 -9
- package/dist/{chunk-22NAGB7X.js → chunk-5C77SEEY.js} +5 -94
- package/dist/{chunk-WPQLXFOZ.js → chunk-5FR74PWO.js} +3 -2
- package/dist/{chunk-35MKKU5R.js → chunk-5UJ6ECTS.js} +18 -10
- package/dist/{chunk-BRXQQJFP.js → chunk-6M7VS3J3.js} +571 -50
- package/dist/{chunk-QQK64KLB.js → chunk-6TUKSZVF.js} +141 -23
- package/dist/{chunk-N4CZJQRK.js → chunk-AB4XIIVB.js} +8 -6
- package/dist/{chunk-KRPY7NTG.js → chunk-BMWK7ZIZ.js} +14 -20
- package/dist/{chunk-4BPJXDWC.js → chunk-C4JBQ5SR.js} +30 -14
- package/dist/{chunk-ZYKPLLNQ.js → chunk-CEYBNUGC.js} +821 -83
- package/dist/{chunk-VEZEGCGW.js → chunk-D4MDIG46.js} +20 -18
- package/dist/chunk-DJNLUABN.js +843 -0
- package/dist/{chunk-BP4OYD6A.js → chunk-DMD2AGVS.js} +21 -2
- package/dist/{chunk-KOHPCX4K.js → chunk-DOOEX22V.js} +2 -2
- package/dist/chunk-DQA7QLMD.js +123 -0
- package/dist/chunk-DR52UMZW.js +21 -0
- package/dist/{chunk-3HZ5RWN2.js → chunk-EBEFWSGL.js} +9 -7
- package/dist/{chunk-EDRHSCIE.js → chunk-EELBMBT6.js} +128 -13
- package/dist/{chunk-HV5X7OR2.js → chunk-EOOQZZDE.js} +16 -14
- package/dist/{chunk-WR67VIZY.js → chunk-FOT2FX5J.js} +63 -5
- package/dist/{chunk-BPGS2WCQ.js → chunk-GEYXPTRF.js} +2 -1
- package/dist/{chunk-FYYLNIL5.js → chunk-GH5622CP.js} +2 -2
- package/dist/{chunk-BEY543CS.js → chunk-GOXNB3AO.js} +5 -2
- package/dist/chunk-GWS3VEIW.js +195 -0
- package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
- package/dist/chunk-HWUFFB6L.js +83 -0
- package/dist/{chunk-X6COSD2O.js → chunk-J7PIKKWC.js} +8 -436
- package/dist/{chunk-NILBFAPG.js → chunk-JNXPYBB4.js} +2 -2
- package/dist/{chunk-4VP4KH3K.js → chunk-JRIO5UD2.js} +4 -4
- package/dist/{chunk-K6WL7QZT.js → chunk-JTSEDYVQ.js} +7 -7
- package/dist/{chunk-QKMUKYO7.js → chunk-KCMKRQX4.js} +236 -84
- package/dist/chunk-KZ2H5X4G.js +1026 -0
- package/dist/{chunk-A2GZF7DC.js → chunk-LADCF22A.js} +13 -13
- package/dist/chunk-LCGCVYZ4.js +57 -0
- package/dist/chunk-M4AKACEO.js +382 -0
- package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
- package/dist/{chunk-4JUF2NNX.js → chunk-MXI6J5JF.js} +7 -7
- package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
- package/dist/{chunk-TTNYS3EA.js → chunk-OB5HIGJY.js} +1 -1
- package/dist/{chunk-7RXG6QRZ.js → chunk-OBMAI2DP.js} +61 -840
- package/dist/{chunk-5M54SPOL.js → chunk-ODFEOB4F.js} +161 -5
- package/dist/chunk-PD3MESLB.js +242 -0
- package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
- package/dist/{chunk-VMNQ6OZA.js → chunk-QCTRSGHQ.js} +963 -786
- package/dist/chunk-RVG5JXAL.js +41 -0
- package/dist/{chunk-RD5U66HV.js → chunk-SROCI7ZU.js} +7 -7
- package/dist/{chunk-MFFY33HR.js → chunk-THKY7CD7.js} +466 -205
- package/dist/{chunk-34475P3I.js → chunk-TSHXZTOQ.js} +5 -4
- package/dist/{chunk-PCZJO5TI.js → chunk-UFQ3F4FW.js} +13 -178
- package/dist/{chunk-AD2SYQYC.js → chunk-UHXRNZ2J.js} +121 -3
- package/dist/chunk-UND3GU2L.js +103 -0
- package/dist/{chunk-QQL5RT5M.js → chunk-UUANF5CR.js} +2323 -2114
- package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
- package/dist/chunk-UVDSQ6LW.js +472 -0
- package/dist/{chunk-QWU7ZBO7.js → chunk-VQNODYQ4.js} +215 -56
- package/dist/chunk-VREKEFLL.js +37 -0
- package/dist/{chunk-2TZWSW76.js → chunk-WHGPSPT5.js} +2 -2
- package/dist/{chunk-TW3WDMVS.js → chunk-WHJYKASB.js} +2 -2
- package/dist/{chunk-MEQ45TQ4.js → chunk-WJHBC77E.js} +21 -7
- package/dist/{chunk-HXG4IURW.js → chunk-X2KV5FXT.js} +2 -2
- package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
- package/dist/{chunk-2LZI5CAG.js → chunk-XEGB6BCN.js} +228 -36
- package/dist/{chunk-E25LMLRW.js → chunk-YD734TPH.js} +2 -2
- package/dist/{verifiers-4UUM6TEE.js → chunk-YTYFXUI3.js} +121 -372
- package/dist/{chunk-3JLKSKD7.js → chunk-ZGH7FGS5.js} +17 -7
- package/dist/{chunk-VSNATDE6.js → chunk-ZZMN5OM4.js} +2 -2
- package/dist/cli/index.js +34 -32
- package/dist/{clio-J5JIOIDS.js → clio-WBVQEBKO.js} +7 -7
- package/dist/{code-nav-AXCXSBHX.js → code-nav-FGGFIE7L.js} +7 -7
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-KELWS457.js → components-F7OEATSO.js} +5 -5
- package/dist/{config-OEBMIN2U.js → config-TRBL3RCF.js} +48 -41
- package/dist/{configure-PUQOSIXQ.js → configure-OLCVPHNM.js} +17 -17
- package/dist/{context-URSXPBCK.js → context-MJIJ6GOX.js} +12 -12
- package/dist/{context-EKDCKUUZ.js → context-WFPKQSM6.js} +26 -9
- package/dist/{context-MGSE4Z2T.js → context-XEWE3MOJ.js} +44 -37
- package/dist/{context-clear-KDAJRNUK.js → context-clear-KNOS2JPB.js} +44 -37
- package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
- package/dist/{context-working-set-SBKMPPI2.js → context-working-set-EUXAZI6N.js} +14 -13
- package/dist/{dispatch-runner-MSWN72NK.js → dispatch-runner-B7MTOVKL.js} +321 -60
- package/dist/{docs-2C2LTVT2.js → docs-FLJTIDSE.js} +5 -5
- package/dist/{doctor-7BSE27PJ.js → doctor-RN4YKO2X.js} +15 -15
- package/dist/{eval-IZGDOO4H.js → eval-RUBJVSNQ.js} +52 -236
- package/dist/{evidence-SR7WXB5B.js → evidence-JZNBUOQZ.js} +39 -33
- package/dist/{evolve-K7VE2CBX.js → evolve-FJVC4KKI.js} +39 -33
- package/dist/{extensions-QVDOHDGJ.js → extensions-IQL36S7K.js} +5 -5
- package/dist/{fleet-7XMJNQNF.js → fleet-BDKYJFCP.js} +243 -370
- package/dist/fleet-commands-ZFIWZSB3.js +70 -0
- package/dist/fleet-graph-Y6HPXIVF.js +125 -0
- package/dist/fleet-new-RDVJLHHH.js +48 -0
- package/dist/{fleet-preflight-AQNAH644.js → fleet-preflight-BHSNPBMH.js} +2 -2
- package/dist/fleet-validate-BIYREGIK.js +79 -0
- package/dist/{init-JGNPAYXT.js → init-LQUB5COQ.js} +57 -48
- package/dist/library-NJAHIGG4.js +217 -0
- package/dist/memory-OG6HOYKM.js +472 -0
- package/dist/{models-ZMMLFJNN.js → models-5ZG5XY7J.js} +23 -22
- package/dist/{monitor-2F3T5KHP.js → monitor-TJ7AMTGB.js} +69 -35
- package/dist/{orchestrator-ORHT43JB.js → orchestrator-WZYB54DM.js} +4868 -1189
- package/dist/{paths-UXLN5YYZ.js → paths-XUC7GS6E.js} +5 -5
- package/dist/{reset-NXGTYNUO.js → reset-PXQT45IY.js} +8 -8
- package/dist/{run-RF4WJGMT.js → run-FQ74YF62.js} +82 -62
- package/dist/{share-UT3W6E4M.js → share-FW7SVCL3.js} +34 -10
- package/dist/{skills-PSACKC5Q.js → skills-7E7IRB3R.js} +25 -9
- package/dist/{skills-eval-WJSI55RZ.js → skills-eval-LI75W6OK.js} +43 -35
- package/dist/{targets-PIIRAOYS.js → targets-4CIFKCTW.js} +27 -24
- package/dist/{terminal-lease-ULWXWNVY.js → terminal-lease-WUZY7ZV5.js} +5 -4
- package/dist/{uninstall-FZCQCDKC.js → uninstall-7FV7IP4E.js} +5 -5
- package/dist/{upgrade-346TZ6AV.js → upgrade-K2HVIVMQ.js} +21 -20
- package/dist/{usage-6KKXR32N.js → usage-GTZELZQX.js} +159 -59
- package/dist/verifiers-RLAHT27O.js +336 -0
- package/dist/{verify-X5HDROLA.js → verify-BX3BRKH5.js} +7 -6
- package/dist/{wiki-generate-7STOCIFZ.js → wiki-generate-ASIFASCN.js} +58 -48
- package/dist/worker/entry.js +98 -84
- package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
- package/docs/README.md +4 -3
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-placement.md +1 -2
- package/docs/artifact-versions.md +10 -6
- package/docs/built-in-agents.md +26 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +90 -8
- package/docs/configuration-and-targets.md +90 -2
- package/docs/context-engine.md +4 -2
- package/docs/context-working-set.md +4 -4
- package/docs/development-pipeline.md +1 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +4 -4
- package/docs/documentation-guide.md +4 -4
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +4 -45
- package/docs/evidence-and-memory.md +70 -10
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +4 -1
- package/docs/extensions-and-sharing.md +6 -2
- package/docs/fleet-demo-runbook.md +2 -2
- package/docs/fleet-dispatch.md +224 -11
- package/docs/git-commit-provenance.md +2 -2
- package/docs/glossary.md +1 -1
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +20 -2
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +55 -8
- package/docs/proactive-memory.md +26 -16
- package/docs/prompt-envelope-and-tools.md +4 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +83 -65
- package/docs/resource-library.md +59 -0
- package/docs/safety-model.md +29 -7
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +37 -1
- package/docs/skills-marketplace.md +16 -3
- package/docs/tool-usage.md +14 -7
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +38 -4
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +7 -4
- package/src/cli/agents.ts +2 -3
- package/src/cli/argv.ts +14 -1
- package/src/cli/fleet-commands.ts +37 -0
- package/src/cli/fleet-graph.ts +102 -0
- package/src/cli/fleet-new.ts +36 -0
- package/src/cli/fleet-preflight.ts +121 -0
- package/src/cli/fleet-validate.ts +30 -0
- package/src/cli/fleet.ts +188 -335
- package/src/cli/index.ts +4 -2
- package/src/cli/library.ts +190 -0
- package/src/cli/memory.ts +272 -10
- package/src/cli/modes/json-stream.ts +2 -2
- package/src/cli/modes/print.ts +12 -1
- package/src/cli/run.ts +22 -2
- package/src/cli/share.ts +13 -1
- package/src/cli/targets.ts +12 -3
- package/src/cli/usage.ts +160 -20
- package/src/core/bus-events.ts +7 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +130 -0
- package/src/core/defaults.ts +81 -0
- package/src/core/response-model-id.ts +134 -0
- package/src/core/toml.ts +62 -0
- package/src/core/workspace-files.ts +0 -1
- package/src/domains/agents/builtins/architect.md +2 -1
- package/src/domains/agents/builtins/oracle.md +33 -0
- package/src/domains/agents/catalog.ts +18 -5
- package/src/domains/agents/fleet-contract.ts +278 -16
- package/src/domains/agents/index.ts +14 -0
- package/src/domains/agents/recipe.ts +54 -14
- package/src/domains/agents/result-contract.ts +242 -5
- package/src/domains/config/classify.ts +4 -0
- package/src/domains/context/bootstrap.ts +36 -27
- package/src/domains/context/project-metadata.ts +19 -63
- package/src/domains/context/prompt-context.ts +8 -0
- package/src/domains/context/working-set/policies/index.ts +3 -4
- package/src/domains/dispatch/active-route-planner.ts +14 -0
- package/src/domains/dispatch/backoff.ts +2 -1
- package/src/domains/dispatch/budget-envelope.ts +396 -0
- package/src/domains/dispatch/capability-match.ts +1 -0
- package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
- package/src/domains/dispatch/contract.ts +36 -0
- package/src/domains/dispatch/delegation-plan.ts +167 -0
- package/src/domains/dispatch/execution-plan.ts +76 -5
- package/src/domains/dispatch/execution-role.ts +3 -1
- package/src/domains/dispatch/execution-scheduler.ts +183 -67
- package/src/domains/dispatch/extension.ts +339 -36
- package/src/domains/dispatch/fleet-gate.ts +14 -0
- package/src/domains/dispatch/fleet-plan.ts +63 -3
- package/src/domains/dispatch/fleet-run.ts +737 -0
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/host-verification.ts +178 -0
- package/src/domains/dispatch/index.ts +38 -0
- package/src/domains/dispatch/intent.ts +159 -0
- package/src/domains/dispatch/orphan-recovery.ts +1 -0
- package/src/domains/dispatch/receipt-integrity.ts +12 -4
- package/src/domains/dispatch/state.ts +37 -3
- package/src/domains/dispatch/types.ts +61 -9
- package/src/domains/dispatch/validation.ts +80 -6
- package/src/domains/dispatch/worker-spawn.ts +14 -3
- package/src/domains/eval/metrics/evidence.ts +0 -116
- package/src/domains/eval/metrics/invariants.ts +1 -1
- package/src/domains/eval/runners/clio-run.ts +1 -10
- package/src/domains/eval/runners/external-command.ts +2 -29
- package/src/domains/eval/schema/suite.ts +0 -7
- package/src/domains/eval/suites/run.ts +1 -7
- package/src/domains/evidence/trust-status.ts +10 -1
- package/src/domains/memory/index.ts +22 -0
- package/src/domains/memory/operations.ts +58 -1
- package/src/domains/memory/promotion.ts +281 -0
- package/src/domains/memory/prompt-section.ts +25 -5
- package/src/domains/memory/proposal.ts +51 -7
- package/src/domains/memory/task-bank.ts +3 -2
- package/src/domains/memory/task-memory-handoff.ts +181 -24
- package/src/domains/memory/task-memory-policy.ts +3 -1
- package/src/domains/memory/types.ts +37 -0
- package/src/domains/memory/validate.ts +178 -0
- package/src/domains/middleware/index.ts +15 -0
- package/src/domains/middleware/memory-intervention.ts +35 -25
- package/src/domains/middleware/runtime.ts +6 -0
- package/src/domains/middleware/skills-reminder.ts +19 -4
- package/src/domains/middleware/stalled-turn.ts +43 -1
- package/src/domains/middleware/types.ts +10 -0
- package/src/domains/middleware/watchdog.ts +281 -0
- package/src/domains/observability/contract.ts +9 -2
- package/src/domains/observability/cost.ts +31 -4
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/observability/index.ts +10 -0
- package/src/domains/observability/out-of-turn-usage.ts +223 -0
- package/src/domains/providers/index.ts +3 -0
- package/src/domains/providers/model-discovery.ts +9 -0
- package/src/domains/providers/runtime-resolution.ts +38 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
- package/src/domains/providers/types/context-window-slots.ts +18 -0
- package/src/domains/providers/types/runtime-descriptor.ts +3 -1
- package/src/domains/resources/index.ts +20 -0
- package/src/domains/resources/library.ts +326 -0
- package/src/domains/resources/skills/marketplace.ts +37 -12
- package/src/domains/safety/call-target.ts +211 -14
- package/src/domains/safety/decision-presentation.ts +268 -0
- package/src/domains/safety/redaction.ts +73 -0
- package/src/domains/session/context-ledger.ts +10 -1
- package/src/domains/session/decision-board.ts +4 -0
- package/src/domains/session/entries.ts +3 -0
- package/src/domains/session/handoff.ts +629 -0
- package/src/domains/session/history.ts +68 -19
- package/src/domains/session/usage.ts +24 -7
- package/src/domains/share/archive.ts +67 -2
- package/src/engine/acp/event-mapper.ts +7 -0
- package/src/engine/acp/server.ts +29 -2
- package/src/engine/apis/lmstudio.ts +25 -4
- package/src/engine/apis/openai-completions.ts +147 -22
- package/src/engine/claude/sdk-runtime.ts +8 -2
- package/src/engine/claude/tool-safety.ts +13 -0
- package/src/engine/loop-guard.ts +27 -3
- package/src/engine/worker-events.ts +4 -3
- package/src/engine/worker-runtime.ts +59 -54
- package/src/entry/orchestrator.ts +55 -1
- package/src/interactive/bus-notices.ts +26 -0
- package/src/interactive/chat-loop-messages.ts +22 -0
- package/src/interactive/chat-loop.ts +248 -1
- package/src/interactive/chat-renderer.ts +41 -3
- package/src/interactive/clio-editor.ts +44 -7
- package/src/interactive/context-overlay.ts +43 -5
- package/src/interactive/cost-overlay.ts +70 -11
- package/src/interactive/council-dispatch.ts +30 -0
- package/src/interactive/council-grid.ts +213 -0
- package/src/interactive/council.ts +99 -0
- package/src/interactive/dispatch-board.ts +471 -50
- package/src/interactive/fleet-run-preview.ts +307 -0
- package/src/interactive/footer/notifications.ts +219 -0
- package/src/interactive/footer/widgets.ts +13 -0
- package/src/interactive/handoff-round.ts +56 -0
- package/src/interactive/interactive-application.ts +49 -2
- package/src/interactive/interactive-event-projection.ts +9 -1
- package/src/interactive/interactive-input-runtime.ts +11 -1
- package/src/interactive/interactive-presentation.ts +11 -1
- package/src/interactive/interactive-slash-runtime.ts +52 -2
- package/src/interactive/interactive-subscriptions.ts +14 -2
- package/src/interactive/memory-overlay.ts +89 -4
- package/src/interactive/oracle.ts +179 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +7 -1
- package/src/interactive/overlay-frame.ts +5 -2
- package/src/interactive/overlay-general-openers.ts +230 -2
- package/src/interactive/overlay-key-routing.ts +58 -2
- package/src/interactive/overlay-lifecycle.ts +52 -5
- package/src/interactive/overlay-permission-lifecycle.ts +33 -8
- package/src/interactive/overlay-resource-openers.ts +11 -3
- package/src/interactive/overlay-session-lifecycle.ts +234 -2
- package/src/interactive/overlay-transitions.ts +11 -0
- package/src/interactive/overlays/ask-user.ts +74 -30
- package/src/interactive/overlays/decisions.ts +3 -1
- package/src/interactive/overlays/fleet-run-approval.ts +208 -0
- package/src/interactive/overlays/handoff-review.ts +185 -0
- package/src/interactive/overlays/library-install-confirm.ts +151 -0
- package/src/interactive/overlays/list-overlay.ts +168 -2
- package/src/interactive/overlays/settings.ts +101 -4
- package/src/interactive/overlays/side-question.ts +139 -0
- package/src/interactive/overlays/skills-hub.ts +401 -15
- package/src/interactive/permission-hint.ts +35 -0
- package/src/interactive/permission-overlay.ts +95 -45
- package/src/interactive/renderers/tool-execution.ts +19 -49
- package/src/interactive/session-last-turn.ts +8 -1
- package/src/interactive/session-usage-reseed.ts +36 -10
- package/src/interactive/side-question.ts +171 -0
- package/src/interactive/slash-commands.ts +434 -7
- package/src/interactive/slash-spec.ts +19 -6
- package/src/interactive/status/summary.ts +5 -0
- package/src/interactive/status/types.ts +5 -0
- package/src/interactive/terminal-lease.ts +1 -0
- package/src/interactive/theme/tokens.ts +30 -0
- package/src/interactive/turn-context.ts +96 -23
- package/src/interactive/turn-middleware.ts +16 -1
- package/src/interactive/turn-runtime.ts +37 -8
- package/src/interactive/turn-state.ts +3 -0
- package/src/interactive/watchdog-run.ts +75 -0
- package/src/interactive/worker-progress.ts +440 -0
- package/src/interactive/worker-share.ts +56 -1
- package/src/interactive/worker-stream.ts +58 -110
- package/src/tools/agent-tools.ts +28 -3
- package/src/tools/ask-user.ts +21 -1
- package/src/tools/bootstrap.ts +3 -0
- package/src/tools/compete-worktrees.ts +13 -79
- package/src/tools/context/index.ts +2 -2
- package/src/tools/dispatch-admission.ts +242 -8
- package/src/tools/dispatch-arguments.ts +65 -1
- package/src/tools/dispatch-event-text.ts +19 -0
- package/src/tools/dispatch-plan.ts +136 -6
- package/src/tools/dispatch-runner.ts +319 -13
- package/src/tools/dispatch-types.ts +20 -1
- package/src/tools/dispatch.ts +96 -3
- package/src/tools/monitor.ts +31 -0
- package/src/tools/profiles.ts +18 -4
- package/src/tools/registry.ts +15 -5
- package/src/tools/result-disposition.ts +156 -0
- package/src/tools/result-shaping.ts +59 -1
- package/src/tools/task-worktree.ts +238 -0
- package/src/tools/verify/authoring.ts +116 -55
- package/src/tools/verify/scripts.ts +62 -0
- package/src/tools/worker-evidence.ts +21 -1
- package/src/worker/spec-contract.ts +44 -3
- package/dist/chunk-EFADSJET.js +0 -18
- package/dist/chunk-HC4CLZ2Y.js +0 -68
- package/dist/memory-4ALKDJ4Q.js +0 -246
- package/src/domains/eval/metrics/chaos-stream.ts +0 -93
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
3
|
+
import { existsSync, mkdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
|
|
6
|
+
export type TaskWorktreeApply = "merge" | "preserve";
|
|
7
|
+
|
|
8
|
+
export interface TaskWorktree {
|
|
9
|
+
root: string;
|
|
10
|
+
runId: string;
|
|
11
|
+
path: string;
|
|
12
|
+
branch: string;
|
|
13
|
+
base: string;
|
|
14
|
+
ownerToken: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TaskWorktreeReceipt {
|
|
18
|
+
path: string;
|
|
19
|
+
branch: string;
|
|
20
|
+
diffHash: string;
|
|
21
|
+
apply: TaskWorktreeApply;
|
|
22
|
+
applied: boolean;
|
|
23
|
+
reason?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const SAFE_RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
27
|
+
const OWNER_FILE_SUFFIX = ".task-owner.json";
|
|
28
|
+
const COMMIT_IDENTITY = "clio-task";
|
|
29
|
+
|
|
30
|
+
function gitBytes(root: string, args: string[]): Buffer {
|
|
31
|
+
return execFileSync("git", ["-C", root, ...args], {
|
|
32
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
33
|
+
timeout: 30_000,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function git(root: string, args: string[]): string {
|
|
38
|
+
return gitBytes(root, args).toString("utf8").trim();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isCanonicalWorktreePathInside(parent: string, candidate: string): boolean {
|
|
42
|
+
let canonicalParent: string;
|
|
43
|
+
let canonicalCandidate: string;
|
|
44
|
+
try {
|
|
45
|
+
canonicalParent = realpathSync(parent);
|
|
46
|
+
} catch {
|
|
47
|
+
canonicalParent = resolve(parent);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
canonicalCandidate = realpathSync(candidate);
|
|
51
|
+
} catch {
|
|
52
|
+
canonicalCandidate = resolve(candidate);
|
|
53
|
+
}
|
|
54
|
+
const rel = relative(canonicalParent, canonicalCandidate);
|
|
55
|
+
return rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function commitWorktreePath(path: string, identity: string, message: string): boolean {
|
|
59
|
+
git(path, ["add", "-A"]);
|
|
60
|
+
if (git(path, ["status", "--porcelain"]).length === 0) return false;
|
|
61
|
+
git(path, [
|
|
62
|
+
"-c",
|
|
63
|
+
`user.name=${identity}`,
|
|
64
|
+
"-c",
|
|
65
|
+
`user.email=${identity}@local`,
|
|
66
|
+
"commit",
|
|
67
|
+
"-m",
|
|
68
|
+
message,
|
|
69
|
+
"--no-verify",
|
|
70
|
+
]);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function worktreeBranchDiffStat(root: string, branch: string): string {
|
|
75
|
+
try {
|
|
76
|
+
return git(root, ["diff", "--shortstat", `HEAD...${branch}`]) || "no changes";
|
|
77
|
+
} catch {
|
|
78
|
+
return "diff unavailable";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function protectedPathsChangedByWorktreeBranch(
|
|
83
|
+
root: string,
|
|
84
|
+
branch: string,
|
|
85
|
+
protectedPaths: ReadonlyArray<string>,
|
|
86
|
+
): string[] {
|
|
87
|
+
const canonical = realpathSync(root);
|
|
88
|
+
const protectedInside = protectedPaths
|
|
89
|
+
.map((path) => resolve(path))
|
|
90
|
+
.filter((path) => {
|
|
91
|
+
const rel = relative(canonical, path);
|
|
92
|
+
return rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
93
|
+
});
|
|
94
|
+
if (protectedInside.length === 0) return [];
|
|
95
|
+
const changed = git(canonical, ["diff", "--name-only", "-z", "--no-renames", `HEAD...${branch}`])
|
|
96
|
+
.split("\0")
|
|
97
|
+
.filter((path) => path.length > 0)
|
|
98
|
+
.map((path) => resolve(canonical, path));
|
|
99
|
+
const blocked = new Set<string>();
|
|
100
|
+
for (const candidate of changed) {
|
|
101
|
+
for (const protectedPath of protectedInside) {
|
|
102
|
+
const rel = relative(protectedPath, candidate);
|
|
103
|
+
if (
|
|
104
|
+
candidate === protectedPath ||
|
|
105
|
+
(rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel))
|
|
106
|
+
) {
|
|
107
|
+
blocked.add(protectedPath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [...blocked].sort();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function mergeWorktreeBranch(
|
|
115
|
+
root: string,
|
|
116
|
+
branch: string,
|
|
117
|
+
identity: string,
|
|
118
|
+
): { ok: true } | { ok: false; reason: string } {
|
|
119
|
+
try {
|
|
120
|
+
git(root, [
|
|
121
|
+
"-c",
|
|
122
|
+
`user.name=${identity}`,
|
|
123
|
+
"-c",
|
|
124
|
+
`user.email=${identity}@local`,
|
|
125
|
+
"merge",
|
|
126
|
+
"--no-edit",
|
|
127
|
+
"--no-verify",
|
|
128
|
+
branch,
|
|
129
|
+
]);
|
|
130
|
+
return { ok: true };
|
|
131
|
+
} catch (error) {
|
|
132
|
+
try {
|
|
133
|
+
git(root, ["merge", "--abort"]);
|
|
134
|
+
} catch {
|
|
135
|
+
// No merge remains to abort.
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
ok: false,
|
|
139
|
+
reason: error instanceof Error ? (error.message.split("\n")[0] ?? "merge failed") : String(error),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function validateRunId(runId: string): void {
|
|
145
|
+
if (!SAFE_RUN_ID.test(runId) || runId === "." || runId === "..")
|
|
146
|
+
throw new Error(`invalid task worktree run id '${runId}'`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function gitCheckoutRoot(cwd: string): string | null {
|
|
150
|
+
try {
|
|
151
|
+
return realpathSync(git(cwd, ["rev-parse", "--show-toplevel"]));
|
|
152
|
+
} catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function createTaskWorktree(root: string, runId: string, base?: string): TaskWorktree {
|
|
158
|
+
validateRunId(runId);
|
|
159
|
+
const canonical = realpathSync(root);
|
|
160
|
+
const resolvedBase = base ?? git(canonical, ["rev-parse", "HEAD"]);
|
|
161
|
+
const parent = join(canonical, ".clio-coder", "worktrees");
|
|
162
|
+
const path = join(parent, runId);
|
|
163
|
+
const branch = `clio/task/${runId}`;
|
|
164
|
+
mkdirSync(parent, { recursive: true });
|
|
165
|
+
if (!isCanonicalWorktreePathInside(parent, path))
|
|
166
|
+
throw new Error(`task worktree path escapes its parent for run ${runId}`);
|
|
167
|
+
git(canonical, ["worktree", "add", "-b", branch, path, resolvedBase]);
|
|
168
|
+
const ownerToken = randomBytes(16).toString("hex");
|
|
169
|
+
writeFileSync(
|
|
170
|
+
`${path}${OWNER_FILE_SUFFIX}`,
|
|
171
|
+
`${JSON.stringify({ version: 1, kind: "clio-task-worktree", root: canonical, runId, branch, base: resolvedBase, ownerToken }, null, 2)}\n`,
|
|
172
|
+
{ encoding: "utf8", flag: "wx" },
|
|
173
|
+
);
|
|
174
|
+
return { root: canonical, runId, path, branch, base: resolvedBase, ownerToken };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function assertOwnership(worktree: TaskWorktree): void {
|
|
178
|
+
const expected = join(worktree.root, ".clio-coder", "worktrees", worktree.runId);
|
|
179
|
+
if (
|
|
180
|
+
resolve(worktree.path) !== resolve(expected) ||
|
|
181
|
+
!isCanonicalWorktreePathInside(join(worktree.root, ".clio-coder", "worktrees"), worktree.path)
|
|
182
|
+
) {
|
|
183
|
+
throw new Error(`task worktree ${worktree.runId} has an invalid ownership path`);
|
|
184
|
+
}
|
|
185
|
+
if (!existsSync(`${worktree.path}${OWNER_FILE_SUFFIX}`))
|
|
186
|
+
throw new Error(`task worktree ${worktree.runId} has no ownership file`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function commitTaskWorktree(worktree: TaskWorktree, message = `Clio task ${worktree.runId}`): boolean {
|
|
190
|
+
assertOwnership(worktree);
|
|
191
|
+
return commitWorktreePath(worktree.path, COMMIT_IDENTITY, message);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function taskWorktreeDiffHash(worktree: Pick<TaskWorktree, "root" | "base" | "branch">): string {
|
|
195
|
+
const bytes = gitBytes(worktree.root, ["diff", `${worktree.base}..${worktree.branch}`]);
|
|
196
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function protectedPathsChangedByTaskBranch(
|
|
200
|
+
worktree: Pick<TaskWorktree, "root" | "branch">,
|
|
201
|
+
protectedPaths: ReadonlyArray<string>,
|
|
202
|
+
): string[] {
|
|
203
|
+
return protectedPathsChangedByWorktreeBranch(worktree.root, worktree.branch, protectedPaths);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function applyTaskWorktree(input: {
|
|
207
|
+
worktree: TaskWorktree;
|
|
208
|
+
apply: TaskWorktreeApply;
|
|
209
|
+
protectedPaths?: ReadonlyArray<string>;
|
|
210
|
+
}): TaskWorktreeReceipt {
|
|
211
|
+
assertOwnership(input.worktree);
|
|
212
|
+
commitTaskWorktree(input.worktree);
|
|
213
|
+
const receipt: TaskWorktreeReceipt = {
|
|
214
|
+
path: input.worktree.path,
|
|
215
|
+
branch: input.worktree.branch,
|
|
216
|
+
diffHash: taskWorktreeDiffHash(input.worktree),
|
|
217
|
+
apply: input.apply,
|
|
218
|
+
applied: false,
|
|
219
|
+
};
|
|
220
|
+
if (input.apply === "preserve") return receipt;
|
|
221
|
+
const protectedChanges = protectedPathsChangedByTaskBranch(input.worktree, input.protectedPaths ?? []);
|
|
222
|
+
if (protectedChanges.length > 0) return { ...receipt, reason: "protected_artifact_changed" };
|
|
223
|
+
const merged = mergeWorktreeBranch(input.worktree.root, input.worktree.branch, COMMIT_IDENTITY);
|
|
224
|
+
if (!merged.ok) return { ...receipt, reason: "worktree_merge_conflict" };
|
|
225
|
+
return { ...receipt, applied: true };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function cleanupTaskWorktree(worktree: TaskWorktree, deleteBranch: boolean): void {
|
|
229
|
+
assertOwnership(worktree);
|
|
230
|
+
try {
|
|
231
|
+
git(worktree.root, ["worktree", "remove", "--force", worktree.path]);
|
|
232
|
+
} catch {
|
|
233
|
+
if (existsSync(worktree.path)) rmSync(worktree.path, { recursive: true, force: true });
|
|
234
|
+
git(worktree.root, ["worktree", "prune"]);
|
|
235
|
+
}
|
|
236
|
+
if (deleteBranch) git(worktree.root, ["branch", "-D", worktree.branch]);
|
|
237
|
+
rmSync(`${worktree.path}${OWNER_FILE_SUFFIX}`, { force: true });
|
|
238
|
+
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
import { isMap, isSeq, parseDocument, stringify, type YAMLMap } from "yaml";
|
|
14
14
|
import { resolveSafeCwd, SAFE_EXEC_DEFAULT_TIMEOUT_MS } from "../../core/safe-exec.js";
|
|
15
|
+
import { parseTomlDocument, tomlTableAt } from "../../core/toml.js";
|
|
15
16
|
import { isVerificationScriptName } from "../../core/verification-scripts.js";
|
|
16
17
|
import { compareCodepoints } from "../../domains/evidence/ordering.js";
|
|
17
18
|
import type { ToolResult } from "../registry.js";
|
|
@@ -23,7 +24,7 @@ import {
|
|
|
23
24
|
parseProjectVerifierCatalogText,
|
|
24
25
|
} from "./catalog.js";
|
|
25
26
|
import { verifyTool } from "./index.js";
|
|
26
|
-
import { discoverDeclaredChecksAtRoot } from "./scripts.js";
|
|
27
|
+
import { discoverDeclaredChecksAtRoot, discoverDeclaredProjectEntriesAtRoot } from "./scripts.js";
|
|
27
28
|
|
|
28
29
|
export type VerifierProposalAuthority = "project-declared" | "toolchain-defined";
|
|
29
30
|
|
|
@@ -33,6 +34,8 @@ export type VerifierSignalKind =
|
|
|
33
34
|
| "cargo"
|
|
34
35
|
| "cmake-preset"
|
|
35
36
|
| "python-runner"
|
|
37
|
+
| "just-recipe"
|
|
38
|
+
| "make-target"
|
|
36
39
|
| "go-module"
|
|
37
40
|
| "validation-contract"
|
|
38
41
|
| "manual-entry";
|
|
@@ -148,6 +151,12 @@ export interface VerifierAuthoringWorkflowOptions {
|
|
|
148
151
|
runCheck?: (id: string) => Promise<ToolResult>;
|
|
149
152
|
}
|
|
150
153
|
|
|
154
|
+
export interface DeclaredProjectCommand {
|
|
155
|
+
id: string;
|
|
156
|
+
command: string[];
|
|
157
|
+
provenance: VerifierProvenance;
|
|
158
|
+
}
|
|
159
|
+
|
|
151
160
|
interface RawProposal {
|
|
152
161
|
preferredId: string;
|
|
153
162
|
description: string;
|
|
@@ -288,8 +297,13 @@ function cargoProposals(workspaceRoot: string, diagnostics: string[]): RawPropos
|
|
|
288
297
|
diagnostics.push(`${relative}: ${text.message}; Cargo discovery skipped.`);
|
|
289
298
|
return [];
|
|
290
299
|
}
|
|
291
|
-
const
|
|
292
|
-
|
|
300
|
+
const document = parseTomlDocument(text);
|
|
301
|
+
if (document === null) {
|
|
302
|
+
diagnostics.push(`${relative}: invalid TOML; Cargo discovery skipped.`);
|
|
303
|
+
return [];
|
|
304
|
+
}
|
|
305
|
+
const workspace = tomlTableAt(document, ["workspace"]) !== null;
|
|
306
|
+
const packageManifest = tomlTableAt(document, ["package"]) !== null;
|
|
293
307
|
if (!workspace && !packageManifest) {
|
|
294
308
|
diagnostics.push(`${relative}: no [package] or [workspace] declaration was found; Cargo discovery skipped.`);
|
|
295
309
|
return [];
|
|
@@ -364,73 +378,68 @@ function cmakeProposals(workspaceRoot: string, diagnostics: string[]): RawPropos
|
|
|
364
378
|
return proposals;
|
|
365
379
|
}
|
|
366
380
|
|
|
367
|
-
function tomlSections(text: string): Map<string, string[]> {
|
|
368
|
-
const sections = new Map<string, string[]>();
|
|
369
|
-
let active = "";
|
|
370
|
-
for (const line of text.split(/\r?\n/u)) {
|
|
371
|
-
const header = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/u.exec(line);
|
|
372
|
-
if (header?.[1] !== undefined) {
|
|
373
|
-
active = header[1].trim();
|
|
374
|
-
if (!sections.has(active)) sections.set(active, []);
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
if (active.length > 0) sections.get(active)?.push(line);
|
|
378
|
-
}
|
|
379
|
-
return sections;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function declaredScriptName(line: string): string | null {
|
|
383
|
-
const match = /^\s*(?:"([^"]+)"|'([^']+)'|([A-Za-z0-9_.:-]+))\s*=\s*(?:"[^"]+"|'[^']+')\s*(?:#.*)?$/u.exec(line);
|
|
384
|
-
return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
381
|
function pythonProposals(workspaceRoot: string, diagnostics: string[]): RawProposal[] {
|
|
388
382
|
const proposals: RawProposal[] = [];
|
|
389
383
|
const pyprojectPath = "pyproject.toml";
|
|
390
384
|
const pyproject = regularFileText(path.join(workspaceRoot, pyprojectPath), workspaceRoot);
|
|
391
385
|
if (pyproject instanceof Error) diagnostics.push(`${pyprojectPath}: ${pyproject.message}; Python discovery skipped.`);
|
|
392
386
|
if (typeof pyproject === "string") {
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
[
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
detail: `[${section}]`,
|
|
411
|
-
authority: "toolchain-defined",
|
|
412
|
-
},
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
for (const section of ["project.scripts", "tool.poetry.scripts"]) {
|
|
416
|
-
for (const line of sections.get(section) ?? []) {
|
|
417
|
-
const name = declaredScriptName(line);
|
|
418
|
-
if (name === null || !isVerificationScriptName(name)) continue;
|
|
387
|
+
const document = parseTomlDocument(pyproject);
|
|
388
|
+
if (document === null) {
|
|
389
|
+
diagnostics.push(`${pyprojectPath}: invalid TOML; Python discovery skipped.`);
|
|
390
|
+
} else {
|
|
391
|
+
for (const [path, section, module, id, description, tags] of [
|
|
392
|
+
[
|
|
393
|
+
["tool", "pytest", "ini_options"],
|
|
394
|
+
"tool.pytest.ini_options",
|
|
395
|
+
"pytest",
|
|
396
|
+
"python-pytest",
|
|
397
|
+
"Run the declared pytest suite",
|
|
398
|
+
["python", "test"],
|
|
399
|
+
],
|
|
400
|
+
[["tool", "tox"], "tool.tox", "tox", "python-tox", "Run the declared tox environments", ["python", "test"]],
|
|
401
|
+
[["tool", "nox"], "tool.nox", "nox", "python-nox", "Run the declared nox sessions", ["python", "test"]],
|
|
402
|
+
] as const) {
|
|
403
|
+
if (tomlTableAt(document, path) === null) continue;
|
|
419
404
|
proposals.push({
|
|
420
|
-
preferredId:
|
|
421
|
-
description
|
|
422
|
-
command: [
|
|
405
|
+
preferredId: id,
|
|
406
|
+
description,
|
|
407
|
+
command: ["python", "-m", module],
|
|
423
408
|
cwd: ".",
|
|
424
409
|
timeoutMs: SAFE_EXEC_DEFAULT_TIMEOUT_MS,
|
|
425
|
-
tags: [
|
|
410
|
+
tags: [...tags],
|
|
426
411
|
provenance: {
|
|
427
412
|
kind: "python-runner",
|
|
428
413
|
path: pyprojectPath,
|
|
429
|
-
detail: `[${section}]
|
|
430
|
-
authority: "
|
|
414
|
+
detail: `[${section}]`,
|
|
415
|
+
authority: "toolchain-defined",
|
|
431
416
|
},
|
|
432
417
|
});
|
|
433
418
|
}
|
|
419
|
+
for (const [path, section] of [
|
|
420
|
+
[["project", "scripts"], "project.scripts"],
|
|
421
|
+
[["tool", "poetry", "scripts"], "tool.poetry.scripts"],
|
|
422
|
+
] as const) {
|
|
423
|
+
const scripts = tomlTableAt(document, path);
|
|
424
|
+
if (scripts === null) continue;
|
|
425
|
+
for (const [name, target] of Object.entries(scripts)) {
|
|
426
|
+
if (typeof target !== "string" || !isVerificationScriptName(name)) continue;
|
|
427
|
+
proposals.push({
|
|
428
|
+
preferredId: `python-${slug(name, "check")}`,
|
|
429
|
+
description: `Run declared Python entry point '${name}'`,
|
|
430
|
+
command: [name],
|
|
431
|
+
cwd: ".",
|
|
432
|
+
timeoutMs: SAFE_EXEC_DEFAULT_TIMEOUT_MS,
|
|
433
|
+
tags: [slug(name.split(/[:.-]/u)[0] ?? "python", "python"), "python"],
|
|
434
|
+
provenance: {
|
|
435
|
+
kind: "python-runner",
|
|
436
|
+
path: pyprojectPath,
|
|
437
|
+
detail: `[${section}] entry '${name}'`,
|
|
438
|
+
authority: "project-declared",
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
434
443
|
}
|
|
435
444
|
}
|
|
436
445
|
|
|
@@ -465,6 +474,58 @@ function pythonProposals(workspaceRoot: string, diagnostics: string[]): RawPropo
|
|
|
465
474
|
return proposals;
|
|
466
475
|
}
|
|
467
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Discover exact invocation vectors from project declarations for fleet command
|
|
479
|
+
* authoring. This shares the verifier authoring readers and provenance model,
|
|
480
|
+
* while retaining entries that are not verification-family names.
|
|
481
|
+
*/
|
|
482
|
+
export function discoverDeclaredProjectCommands(workspaceRoot = process.cwd()): DeclaredProjectCommand[] {
|
|
483
|
+
const commands: DeclaredProjectCommand[] = [];
|
|
484
|
+
const add = (id: string, command: string[], pathValue: string, detail: string, kind: VerifierSignalKind): void => {
|
|
485
|
+
commands.push({
|
|
486
|
+
id,
|
|
487
|
+
command,
|
|
488
|
+
provenance: { kind, path: pathValue, detail, authority: "project-declared" },
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
for (const entry of discoverDeclaredProjectEntriesAtRoot(workspaceRoot)) {
|
|
492
|
+
add(entry.id, entry.command, entry.path, entry.detail, entry.kind);
|
|
493
|
+
}
|
|
494
|
+
const pyprojectText = regularFileText(path.join(workspaceRoot, "pyproject.toml"), workspaceRoot);
|
|
495
|
+
if (typeof pyprojectText === "string") {
|
|
496
|
+
const document = parseTomlDocument(pyprojectText);
|
|
497
|
+
if (document !== null) {
|
|
498
|
+
for (const [tablePath, label] of [
|
|
499
|
+
[["project", "scripts"], "project.scripts"],
|
|
500
|
+
[["tool", "poetry", "scripts"], "tool.poetry.scripts"],
|
|
501
|
+
] as const) {
|
|
502
|
+
const scripts = tomlTableAt(document, tablePath);
|
|
503
|
+
if (scripts === null) continue;
|
|
504
|
+
for (const [name, target] of Object.entries(scripts).sort(([left], [right]) => compareCodepoints(left, right))) {
|
|
505
|
+
if (typeof target === "string") add(name, [name], "pyproject.toml", `[${label}] entry '${name}'`, "python-runner");
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
for (const [tablePath, module, id, label] of [
|
|
509
|
+
[["tool", "pytest", "ini_options"], "pytest", "pytest", "tool.pytest.ini_options"],
|
|
510
|
+
[["tool", "tox"], "tox", "tox", "tool.tox"],
|
|
511
|
+
[["tool", "nox"], "nox", "nox", "tool.nox"],
|
|
512
|
+
] as const) {
|
|
513
|
+
if (tomlTableAt(document, tablePath) !== null)
|
|
514
|
+
add(id, ["python", "-m", module], "pyproject.toml", `[${label}]`, "python-runner");
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
const seen = new Set<string>();
|
|
519
|
+
return commands.filter((entry) => {
|
|
520
|
+
let id = slug(entry.id, "command");
|
|
521
|
+
let suffix = 2;
|
|
522
|
+
while (seen.has(id)) id = `${slug(entry.id, "command")}-${suffix++}`;
|
|
523
|
+
seen.add(id);
|
|
524
|
+
entry.id = id;
|
|
525
|
+
return true;
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
468
529
|
function goProposals(workspaceRoot: string, diagnostics: string[]): RawProposal[] {
|
|
469
530
|
const relative = "go.mod";
|
|
470
531
|
const text = regularFileText(path.join(workspaceRoot, relative), workspaceRoot);
|
|
@@ -21,6 +21,14 @@ import {
|
|
|
21
21
|
|
|
22
22
|
export type DeclaredCheckDiscoveryResult = { ok: true; sources: DeclaredCheckSource[] } | { ok: false; reason: string };
|
|
23
23
|
|
|
24
|
+
export interface DeclaredProjectEntry {
|
|
25
|
+
id: string;
|
|
26
|
+
command: string[];
|
|
27
|
+
path: string;
|
|
28
|
+
detail: string;
|
|
29
|
+
kind: "package-script" | "just-recipe" | "make-target";
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
function repositoryRelativeCwd(workspaceRoot: string, resolved: string): string {
|
|
25
33
|
const relative = path.relative(workspaceRoot, resolved);
|
|
26
34
|
return relative.length === 0 ? "." : relative.split(path.sep).join("/");
|
|
@@ -43,6 +51,60 @@ function packageCheckSource(packageRoot: string, workspaceRoot: string): Declare
|
|
|
43
51
|
return { kind: "package.json", path: packagePath, checks };
|
|
44
52
|
}
|
|
45
53
|
|
|
54
|
+
/** Discover every exact project-declared entry without promoting it to a verifier check. */
|
|
55
|
+
export function discoverDeclaredProjectEntriesAtRoot(workspaceRoot: string): DeclaredProjectEntry[] {
|
|
56
|
+
const entries: DeclaredProjectEntry[] = [];
|
|
57
|
+
const packagePath = path.join(workspaceRoot, "package.json");
|
|
58
|
+
if (existsSync(packagePath)) {
|
|
59
|
+
const pkg = parsePackageJson(packagePath);
|
|
60
|
+
if (pkg.ok) {
|
|
61
|
+
for (const name of Object.keys(pkg.scripts).sort()) {
|
|
62
|
+
if (typeof pkg.scripts[name] !== "string") continue;
|
|
63
|
+
entries.push({
|
|
64
|
+
id: name,
|
|
65
|
+
command: ["npm", "run", name],
|
|
66
|
+
path: "package.json",
|
|
67
|
+
detail: `package.json script '${name}'`,
|
|
68
|
+
kind: "package-script",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const relative of ["justfile", "Justfile"] as const) {
|
|
74
|
+
const filePath = path.join(workspaceRoot, relative);
|
|
75
|
+
if (!existsSync(filePath)) continue;
|
|
76
|
+
const text = readFileSync(filePath, "utf8");
|
|
77
|
+
for (const match of text.matchAll(/^([A-Za-z0-9][A-Za-z0-9_-]*)\s*(?:[^:=\n]*)?:\s*(?:#.*)?$/gmu)) {
|
|
78
|
+
const name = match[1];
|
|
79
|
+
if (name === undefined || name.startsWith("_")) continue;
|
|
80
|
+
entries.push({
|
|
81
|
+
id: name,
|
|
82
|
+
command: ["just", name],
|
|
83
|
+
path: relative,
|
|
84
|
+
detail: `just recipe '${name}'`,
|
|
85
|
+
kind: "just-recipe",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
const makePath = path.join(workspaceRoot, "Makefile");
|
|
91
|
+
if (existsSync(makePath)) {
|
|
92
|
+
const text = readFileSync(makePath, "utf8");
|
|
93
|
+
for (const match of text.matchAll(/^([A-Za-z0-9][A-Za-z0-9_.-]*)\s*:(?![=])[^\n]*$/gmu)) {
|
|
94
|
+
const name = match[1];
|
|
95
|
+
if (name === undefined || name.startsWith(".")) continue;
|
|
96
|
+
entries.push({
|
|
97
|
+
id: name,
|
|
98
|
+
command: ["make", name],
|
|
99
|
+
path: "Makefile",
|
|
100
|
+
detail: `Makefile target '${name}'`,
|
|
101
|
+
kind: "make-target",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return entries;
|
|
106
|
+
}
|
|
107
|
+
|
|
46
108
|
function providerCollision(sources: ReadonlyArray<DeclaredCheckSource>): string | null {
|
|
47
109
|
const seen = new Map<string, DeclaredCheck>();
|
|
48
110
|
for (const source of sources) {
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { mentionsWorkerToolCallCap } from "../core/guardrails.js";
|
|
2
|
+
import {
|
|
3
|
+
formatBudgetPolicy,
|
|
4
|
+
formatBudgetReasons,
|
|
5
|
+
formatBudgetRequest,
|
|
6
|
+
formatEffectiveBudget,
|
|
7
|
+
} from "../domains/dispatch/budget-envelope.js";
|
|
2
8
|
import type { ReceiptIntegrityResult } from "../domains/dispatch/receipt-integrity.js";
|
|
3
9
|
import type { RunReceipt, RunReceiptVerification } from "../domains/dispatch/types.js";
|
|
4
10
|
import { adaptRunReceiptTrustStatus, type CanonicalTrustStatus } from "../domains/evidence/trust-status.js";
|
|
11
|
+
import { receiptResponseModelIdObservationLabel } from "./dispatch-event-text.js";
|
|
5
12
|
|
|
6
13
|
/** Matches a source citation the parent can independently spot-check. */
|
|
7
14
|
const SOURCE_CITATION_PATTERN = /([\w./~-]+):(\d+)/g;
|
|
@@ -120,7 +127,7 @@ export function receiptEvidenceLabels(
|
|
|
120
127
|
verification: RunReceiptVerification,
|
|
121
128
|
integrity: ReceiptIntegrityResult,
|
|
122
129
|
): string[] {
|
|
123
|
-
const status = adaptRunReceiptTrustStatus({ ...receipt, verification }, { integrity });
|
|
130
|
+
const status = adaptRunReceiptTrustStatus({ ...receipt, verification: verification }, { integrity });
|
|
124
131
|
const canonical = canonicalTrustLabel(status);
|
|
125
132
|
if (!integrity.ok) {
|
|
126
133
|
return [canonical, `receipt_integrity=FAILED reason=${JSON.stringify(integrity.reason)}`];
|
|
@@ -136,10 +143,23 @@ export function receiptEvidenceLabels(
|
|
|
136
143
|
sections.length > 0 ? ` sections:${[...sections].sort().join(",")}` : ""
|
|
137
144
|
}${receipt.projectContext.contentHash !== undefined ? ` sha256:${receipt.projectContext.contentHash}` : ""}`;
|
|
138
145
|
}
|
|
146
|
+
const responseModelIdObservation = receiptResponseModelIdObservationLabel(receipt);
|
|
147
|
+
const budget =
|
|
148
|
+
receipt.budget === undefined
|
|
149
|
+
? []
|
|
150
|
+
: [
|
|
151
|
+
`budget_recipe_policy=${JSON.stringify(formatBudgetPolicy(receipt.budget))}`,
|
|
152
|
+
`budget_requested=${JSON.stringify(formatBudgetRequest(receipt.budget))}`,
|
|
153
|
+
`budget_effective=${JSON.stringify(formatEffectiveBudget(receipt.budget))}`,
|
|
154
|
+
`budget_reason=${JSON.stringify(formatBudgetReasons(receipt.budget))}`,
|
|
155
|
+
];
|
|
139
156
|
return [
|
|
140
157
|
canonical,
|
|
141
158
|
`receipt_integrity=verified/v${receipt.integrity.version}/${receipt.integrity.algorithm}`,
|
|
142
159
|
`evidence_verification=${verification.state}/${verification.basis}`,
|
|
160
|
+
`host_verification=${receipt.hostVerification?.status ?? "not_requested"}`,
|
|
161
|
+
...(responseModelIdObservation ? [responseModelIdObservation] : []),
|
|
162
|
+
...budget,
|
|
143
163
|
...receiptAdmissionLabels(receipt),
|
|
144
164
|
...receiptActivityLabels(receipt, status),
|
|
145
165
|
briefing,
|
|
@@ -48,6 +48,11 @@ export interface WorkerProtectedArtifactState {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/** Concrete dispatch-time budget after recipe policy and operator clamping. */
|
|
51
|
+
export interface WorkerBudgetPhase {
|
|
52
|
+
toolCalls: number;
|
|
53
|
+
readReserve: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
51
56
|
export interface WorkerBudget {
|
|
52
57
|
/** Agent phase boundary before synthesis or bounded termination. */
|
|
53
58
|
toolCalls: number;
|
|
@@ -57,6 +62,8 @@ export interface WorkerBudget {
|
|
|
57
62
|
synthesis: boolean;
|
|
58
63
|
/** Independent operator-owned attempt ceiling; recipes cannot widen it. */
|
|
59
64
|
hardCap: number;
|
|
65
|
+
/** Optional ceiling that a bounded result-contract revision may activate. */
|
|
66
|
+
revision?: WorkerBudgetPhase;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
interface WorkerSpecFields {
|
|
@@ -218,6 +225,7 @@ const TOOL_PROFILE_NAMES = [
|
|
|
218
225
|
"minimal-local",
|
|
219
226
|
"science-local",
|
|
220
227
|
"full-agent",
|
|
228
|
+
"council-read-only",
|
|
221
229
|
] as const satisfies ReadonlyArray<ToolProfileName>;
|
|
222
230
|
const WORKER_PRODUCTS = ["orientation"] as const satisfies ReadonlyArray<AgentProduct>;
|
|
223
231
|
const TARGET_LIFECYCLES = ["user-managed", "clio-managed"] as const;
|
|
@@ -416,10 +424,16 @@ function validateAllowedTools(value: unknown): void {
|
|
|
416
424
|
|
|
417
425
|
function validateWorkerBudget(value: unknown): void {
|
|
418
426
|
const budget = readRecord(value, "WorkerSpec.budget");
|
|
419
|
-
const expected = ["hardCap", "readReserve", "synthesis", "toolCalls"];
|
|
427
|
+
const expected = ["hardCap", "readReserve", "revision", "synthesis", "toolCalls"];
|
|
420
428
|
const actual = Object.keys(budget).sort();
|
|
421
|
-
if (
|
|
422
|
-
|
|
429
|
+
if (
|
|
430
|
+
actual.some((key) => !expected.includes(key)) ||
|
|
431
|
+
actual.some((key) => key === "revision" && budget.revision === undefined)
|
|
432
|
+
) {
|
|
433
|
+
throw new Error(`WorkerSpec.budget may contain only: ${expected.join(", ")}`);
|
|
434
|
+
}
|
|
435
|
+
for (const required of ["hardCap", "readReserve", "synthesis", "toolCalls"] as const) {
|
|
436
|
+
if (!Object.hasOwn(budget, required)) throw new Error(`WorkerSpec.budget.${required} is required`);
|
|
423
437
|
}
|
|
424
438
|
for (const key of ["toolCalls", "readReserve", "hardCap"] as const) {
|
|
425
439
|
const value = budget[key];
|
|
@@ -436,6 +450,33 @@ function validateWorkerBudget(value: unknown): void {
|
|
|
436
450
|
throw new Error("WorkerSpec.budget.readReserve must be an integer in [0, toolCalls)");
|
|
437
451
|
}
|
|
438
452
|
if (typeof budget.synthesis !== "boolean") throw new Error("WorkerSpec.budget.synthesis must be a boolean");
|
|
453
|
+
if (budget.revision !== undefined) {
|
|
454
|
+
const revision = readRecord(budget.revision, "WorkerSpec.budget.revision");
|
|
455
|
+
const revisionKeys = Object.keys(revision).sort();
|
|
456
|
+
if (revisionKeys.length !== 2 || revisionKeys[0] !== "readReserve" || revisionKeys[1] !== "toolCalls") {
|
|
457
|
+
throw new Error("WorkerSpec.budget.revision must contain exactly: readReserve, toolCalls");
|
|
458
|
+
}
|
|
459
|
+
for (const key of ["toolCalls", "readReserve"] as const) {
|
|
460
|
+
if (typeof revision[key] !== "number" || !Number.isSafeInteger(revision[key])) {
|
|
461
|
+
throw new Error(`WorkerSpec.budget.revision.${key} must be a safe integer`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if ((revision.toolCalls as number) < (budget.toolCalls as number)) {
|
|
465
|
+
throw new Error("WorkerSpec.budget.revision.toolCalls must not be smaller than WorkerSpec.budget.toolCalls");
|
|
466
|
+
}
|
|
467
|
+
if (
|
|
468
|
+
(revision.toolCalls as number) === (budget.toolCalls as number) &&
|
|
469
|
+
(revision.readReserve as number) <= (budget.readReserve as number)
|
|
470
|
+
) {
|
|
471
|
+
throw new Error("WorkerSpec.budget.revision must increase toolCalls or readReserve");
|
|
472
|
+
}
|
|
473
|
+
if ((revision.toolCalls as number) > (budget.hardCap as number)) {
|
|
474
|
+
throw new Error("WorkerSpec.budget.revision.toolCalls must not exceed WorkerSpec.budget.hardCap");
|
|
475
|
+
}
|
|
476
|
+
if ((revision.readReserve as number) < 0 || (revision.readReserve as number) >= (revision.toolCalls as number)) {
|
|
477
|
+
throw new Error("WorkerSpec.budget.revision.readReserve must be an integer in [0, toolCalls)");
|
|
478
|
+
}
|
|
479
|
+
}
|
|
439
480
|
}
|
|
440
481
|
|
|
441
482
|
function validateRuntimeCapabilityDecision(value: unknown, source: string): void {
|