@iowarp/clio-coder 0.3.6 → 0.3.8
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 +59 -0
- package/README.md +22 -6
- package/dist/{acp-2BEHC4DL.js → acp-U67UHUK2.js} +14 -14
- package/dist/{agents-LNNFTM53.js → agents-YU6SGALZ.js} +42 -35
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-KXXFI2VS.js → auth-5ZPJOIVG.js} +32 -24
- package/dist/builtins-C6JMZVV6.js +17 -0
- package/dist/{chunk-E25LMLRW.js → chunk-26LEYJZH.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-2HEJ2F35.js} +5 -5
- package/dist/{chunk-PBTHKCPN.js → chunk-2HFZQUHL.js} +7 -7
- package/dist/{chunk-FO5ZOVUY.js → chunk-3BINW3FP.js} +5 -5
- package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
- package/dist/{chunk-CKXWIANG.js → chunk-4SPRNWDE.js} +18 -16
- package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
- package/dist/{chunk-XF5N4U5A.js → chunk-5DHKRSMQ.js} +9 -8
- package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
- package/dist/{chunk-EKY57CSP.js → chunk-5H3GB5BO.js} +68 -772
- package/dist/{chunk-6XXKFVSN.js → chunk-5Q2VVUKB.js} +4 -4
- package/dist/{chunk-DJVECN66.js → chunk-7RFXX52T.js} +295 -3705
- package/dist/{chunk-ZXF4XRKW.js → chunk-7RGZWPB6.js} +158 -8
- package/dist/{chunk-FYYLNIL5.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-XXQNGV4M.js → chunk-A3WNZD3P.js} +548 -72
- package/dist/{chunk-VEZEGCGW.js → chunk-B5CSFE7B.js} +23 -21
- package/dist/{chunk-2SFS6XQE.js → chunk-DGSYXYMX.js} +3 -2
- package/dist/chunk-DR52UMZW.js +21 -0
- package/dist/{chunk-ZWLZP4ZT.js → chunk-DYIM5TJT.js} +98 -12
- package/dist/{chunk-QKMUKYO7.js → chunk-E77JEWSD.js} +270 -125
- package/dist/{chunk-LYF7OHWH.js → chunk-EMYUUSFG.js} +12 -467
- package/dist/{chunk-4VP4KH3K.js → chunk-EQ63NRB7.js} +8 -8
- package/dist/chunk-FBVTI2TJ.js +518 -0
- package/dist/{chunk-R46L2BIR.js → chunk-FHJEP5SW.js} +19 -25
- package/dist/{chunk-WR67VIZY.js → chunk-GN57SG4G.js} +66 -8
- package/dist/{chunk-RD5U66HV.js → chunk-GPIEI3LY.js} +9 -9
- package/dist/{chunk-PCZJO5TI.js → chunk-GU2UIAFZ.js} +13 -178
- package/dist/chunk-GWS3VEIW.js +195 -0
- package/dist/chunk-H7IXIC72.js +103 -0
- package/dist/{chunk-3BPUFZDL.js → chunk-HLE42MG7.js} +3 -3
- package/dist/{chunk-24I7BN55.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-QNQHSOLF.js → chunk-IGWKHNIQ.js} +114 -55
- package/dist/chunk-IIZWH4XA.js +172 -0
- package/dist/{chunk-AD2SYQYC.js → chunk-IJ7RPIYJ.js} +124 -6
- package/dist/chunk-J3YUBZWY.js +382 -0
- package/dist/{chunk-5JGRAMKL.js → chunk-JOZYP4GM.js} +8 -6
- package/dist/{chunk-OH3TOQTB.js → chunk-K4XHGFR5.js} +751 -15
- package/dist/{chunk-EYPA3EGJ.js → chunk-KTYTFRMB.js} +178 -14
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/chunk-ME6CCNFO.js +108 -0
- package/dist/chunk-MXKJU4JB.js +1100 -0
- package/dist/{chunk-RY3LY4J5.js → chunk-N22QMJKY.js} +21 -18
- package/dist/chunk-NMPKI6XL.js +3006 -0
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-22NAGB7X.js → chunk-P43ETTHK.js} +5 -94
- package/dist/{chunk-NILBFAPG.js → chunk-PMDBGQSJ.js} +2 -2
- package/dist/chunk-PT7HYKEM.js +165 -0
- package/dist/chunk-RVG5JXAL.js +41 -0
- package/dist/{chunk-GEYXPTRF.js → chunk-RWSI4YD7.js} +2 -2
- package/dist/{chunk-4BPJXDWC.js → chunk-TANS5ZJS.js} +35 -19
- package/dist/{verifiers-NCBTHHN2.js → chunk-TB5666IT.js} +67 -324
- package/dist/{chunk-QM3F2GKX.js → chunk-TLQJPP24.js} +7927 -8088
- package/dist/{chunk-G7MUEIGA.js → chunk-TT36MB5S.js} +2 -1
- package/dist/chunk-TTHACPOM.js +961 -0
- package/dist/{chunk-6US73PDB.js → chunk-TYPGUK6W.js} +7 -7
- package/dist/{chunk-MFFY33HR.js → chunk-U6MBIEMB.js} +554 -209
- package/dist/chunk-VAWNZU7Z.js +242 -0
- package/dist/{chunk-IR4CFBFN.js → chunk-VCBR6CU7.js} +12 -12
- package/dist/{chunk-WHJYKASB.js → chunk-VHN4MY6O.js} +2 -2
- package/dist/{chunk-XYDYPRZI.js → chunk-VWZOAB7K.js} +10 -10
- package/dist/{chunk-ZRGEBJ4T.js → chunk-WLFILSD5.js} +48 -48
- package/dist/{chunk-K7T3E2SR.js → chunk-WNIJTQQK.js} +12 -11
- package/dist/{chunk-CYQKWTG3.js → chunk-WSB3FPX7.js} +68 -20
- package/dist/{chunk-KHSFENX2.js → chunk-WWCZ5F23.js} +116 -14
- package/dist/{chunk-XE2VEJHX.js → chunk-WXY7KU3G.js} +2 -2
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-KOHPCX4K.js → chunk-XWSF374K.js} +5 -5
- package/dist/{chunk-CJUB2JJ2.js → chunk-YS5VLNH5.js} +10 -10
- package/dist/{chunk-ZZMN5OM4.js → chunk-ZNLWCMVZ.js} +2 -2
- package/dist/{chunk-WHGPSPT5.js → chunk-ZVJ5BLO2.js} +2 -2
- package/dist/cli/index.js +33 -31
- package/dist/{clio-M2KGYUFZ.js → clio-QVTYJ57A.js} +10 -10
- package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-5TTYYX6G.js → components-ZFA3SAER.js} +9 -9
- package/dist/{config-XUUYQIWO.js → config-LW5IJFQN.js} +65 -55
- package/dist/{configure-IHJ7YOMV.js → configure-7XIZCOU4.js} +29 -24
- package/dist/{context-75MIWW3U.js → context-L3WL3X7K.js} +54 -43
- package/dist/{context-ZQ7SIFJV.js → context-N52ZA626.js} +30 -14
- package/dist/{context-74JLXAWD.js → context-Y6Y7QPR6.js} +12 -12
- package/dist/{context-clear-GYKWNUML.js → context-clear-MBQRLSDQ.js} +54 -43
- package/dist/{context-index-SSR5ECNE.js → context-index-HVMFQHK3.js} +5 -5
- package/dist/{context-working-set-UX5KEP4J.js → context-working-set-GS6DSO7F.js} +18 -18
- package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-22ZCNOM3.js} +367 -79
- package/dist/{docs-6FZSCG5B.js → docs-7LQ23DLM.js} +9 -9
- package/dist/{doctor-SVJ5BZCW.js → doctor-M7YEDGAE.js} +27 -23
- package/dist/{eval-CG6LLBLD.js → eval-BEC2WHDA.js} +73 -20
- package/dist/{evidence-ZYFIEN42.js → evidence-REJUMSKM.js} +70 -59
- package/dist/{evolve-QGEXEMDW.js → evolve-PY5ZBA5K.js} +50 -39
- package/dist/{extensions-ADGNCJJD.js → extensions-HVKU65YU.js} +7 -7
- package/dist/{fleet-S5R4ZOQY.js → fleet-7WZEWRFA.js} +236 -377
- package/dist/fleet-commands-UVHWM76J.js +70 -0
- package/dist/fleet-graph-6ULH7PES.js +125 -0
- package/dist/fleet-new-RDVJLHHH.js +48 -0
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-J53T6CCE.js} +5 -5
- package/dist/fleet-validate-72PC4SLA.js +79 -0
- package/dist/{init-5DRU55YR.js → init-OG3TPGQG.js} +69 -57
- package/dist/library-CNTMPLRF.js +217 -0
- package/dist/{memory-7YKKR6UC.js → memory-6IS7F275.js} +52 -41
- package/dist/{models-ZPOLRU2C.js → models-ENRJDA5W.js} +37 -31
- package/dist/{monitor-US5F5YGZ.js → monitor-XLDVO7TN.js} +79 -42
- package/dist/{orchestrator-E2AL4T5N.js → orchestrator-6KSPYRHA.js} +4126 -762
- package/dist/{paths-E7KYAQWE.js → paths-DBXMZMDU.js} +6 -6
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-KZ652EK6.js → reset-RZ4ER727.js} +12 -12
- package/dist/{run-SRNBKDWD.js → run-Y2CNK5RU.js} +89 -76
- package/dist/{share-CGZE33UP.js → share-A55GYP6Z.js} +37 -13
- package/dist/{skills-S2X4DLY5.js → skills-ALC5J6AT.js} +29 -13
- package/dist/{skills-eval-W2GGIC4R.js → skills-eval-JPBEBYQU.js} +60 -48
- package/dist/support-MIETYA5E.js +38 -0
- package/dist/{targets-54SWINWB.js → targets-VGNXIR3S.js} +46 -36
- package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WOBR64YA.js} +4 -4
- package/dist/{uninstall-BVLWXKBT.js → uninstall-ZJF5H5ZN.js} +9 -9
- package/dist/{upgrade-JKAR27XC.js → upgrade-FUSUAGHR.js} +31 -28
- package/dist/{usage-MSAWCLX4.js → usage-N4MKVHKD.js} +140 -68
- package/dist/verifiers-YAWOJ3H2.js +336 -0
- package/dist/{verify-X5HDROLA.js → verify-LTDHYBGY.js} +10 -9
- package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-6M7GHTBJ.js} +75 -62
- package/dist/worker/entry.js +67 -57
- package/docs/README.md +3 -2
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +11 -6
- package/docs/built-in-agents.md +26 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +83 -3
- package/docs/configuration-and-targets.md +86 -4
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +1 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +3 -3
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +74 -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 +228 -16
- package/docs/git-commit-provenance.md +2 -2
- package/docs/glossary.md +22 -2
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +2 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +56 -9
- package/docs/proactive-memory.md +1 -1
- 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 +2 -2
- 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 +1 -1
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +1 -2
- package/src/cli/argv.ts +5 -0
- package/src/cli/configure.ts +107 -23
- package/src/cli/doctor.ts +5 -1
- package/src/cli/evidence.ts +30 -25
- 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 +111 -0
- package/src/cli/fleet-validate.ts +30 -0
- package/src/cli/fleet.ts +173 -335
- package/src/cli/index.ts +3 -1
- package/src/cli/library.ts +190 -0
- package/src/cli/share.ts +13 -1
- package/src/cli/shared.ts +1 -0
- package/src/cli/targets.ts +4 -1
- package/src/cli/usage.ts +111 -19
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +29 -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/path-boundary.ts +100 -0
- package/src/domains/agents/builtins/architect.md +1 -0
- package/src/domains/agents/builtins/oracle.md +33 -0
- package/src/domains/agents/catalog.ts +13 -1
- package/src/domains/agents/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +304 -24
- package/src/domains/agents/index.ts +14 -0
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +360 -15
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/config/classify.ts +4 -0
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/active-route-planner.ts +14 -0
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- package/src/domains/dispatch/backoff.ts +2 -1
- 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 +48 -0
- package/src/domains/dispatch/delegation-plan.ts +164 -0
- package/src/domains/dispatch/execution-plan.ts +76 -5
- package/src/domains/dispatch/execution-role.ts +12 -2
- package/src/domains/dispatch/execution-scheduler.ts +183 -67
- package/src/domains/dispatch/extension.ts +412 -74
- 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 +791 -0
- package/src/domains/dispatch/gate-role-prompts.ts +47 -0
- package/src/domains/dispatch/host-verification.ts +178 -0
- package/src/domains/dispatch/index.ts +41 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +235 -0
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +9 -4
- package/src/domains/dispatch/state.ts +36 -3
- package/src/domains/dispatch/types.ts +64 -12
- package/src/domains/dispatch/validation.ts +69 -6
- package/src/domains/dispatch/write-boundary-enforcer.ts +45 -0
- package/src/domains/dispatch/write-boundary.ts +201 -22
- package/src/domains/eval/metrics/evidence.ts +79 -2
- package/src/domains/eval/runners/clio-run.ts +12 -2
- package/src/domains/evidence/build.ts +69 -11
- package/src/domains/evidence/index.ts +21 -0
- package/src/domains/evidence/provenance.ts +46 -11
- package/src/domains/evidence/trust-projection.ts +274 -0
- package/src/domains/evidence/trust-status.ts +155 -18
- package/src/domains/evidence/types.ts +4 -0
- package/src/domains/extensions/discovery.ts +88 -1
- package/src/domains/extensions/resources.ts +20 -8
- package/src/domains/extensions/state.ts +6 -2
- package/src/domains/extensions/types.ts +4 -1
- package/src/domains/lifecycle/doctor.ts +140 -1
- package/src/domains/middleware/index.ts +15 -0
- package/src/domains/middleware/watchdog.ts +281 -0
- package/src/domains/observability/contract.ts +3 -1
- package/src/domains/observability/cost.ts +12 -1
- 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/prompts/contract.ts +3 -5
- package/src/domains/providers/extension.ts +30 -2
- package/src/domains/resources/common-loader.ts +3 -0
- package/src/domains/resources/index.ts +20 -0
- package/src/domains/resources/library.ts +326 -0
- package/src/domains/resources/prompts/loader.ts +119 -14
- package/src/domains/resources/skills/marketplace.ts +37 -12
- package/src/domains/safety/policy-engine.ts +5 -5
- package/src/domains/safety/run-effects.ts +64 -1
- package/src/domains/safety/scope.ts +7 -12
- package/src/domains/session/handoff.ts +629 -0
- package/src/domains/share/archive.ts +67 -2
- package/src/engine/acp/server.ts +4 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/worker-runtime.ts +6 -3
- package/src/entry/orchestrator.ts +37 -0
- package/src/interactive/bus-notices.ts +26 -0
- package/src/interactive/chat-loop.ts +235 -1
- package/src/interactive/chat-renderer.ts +22 -0
- package/src/interactive/cost-overlay.ts +31 -3
- 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 +311 -17
- package/src/interactive/fleet-run-preview.ts +307 -0
- package/src/interactive/footer/notifications.ts +219 -0
- package/src/interactive/handoff-round.ts +56 -0
- package/src/interactive/interactive-application.ts +43 -1
- package/src/interactive/interactive-event-projection.ts +23 -1
- package/src/interactive/interactive-slash-runtime.ts +52 -2
- package/src/interactive/interactive-subscriptions.ts +14 -2
- package/src/interactive/oracle.ts +179 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +6 -0
- package/src/interactive/overlay-general-openers.ts +190 -1
- package/src/interactive/overlay-key-routing.ts +17 -1
- package/src/interactive/overlay-lifecycle.ts +41 -1
- package/src/interactive/overlay-permission-lifecycle.ts +10 -0
- package/src/interactive/overlay-resource-openers.ts +11 -3
- package/src/interactive/overlay-session-lifecycle.ts +234 -2
- 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 +221 -30
- package/src/interactive/overlays/side-question.ts +139 -0
- package/src/interactive/overlays/skills-hub.ts +401 -15
- package/src/interactive/side-question.ts +171 -0
- package/src/interactive/slash-commands.ts +439 -7
- package/src/interactive/slash-spec.ts +19 -6
- package/src/interactive/theme/tokens.ts +30 -0
- package/src/interactive/turn-middleware.ts +15 -1
- package/src/interactive/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +15 -3
- package/src/interactive/watchdog-run.ts +75 -0
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-share.ts +56 -1
- package/src/interactive/worker-stream.ts +15 -0
- package/src/tools/bootstrap.ts +3 -0
- package/src/tools/compete-worktrees.ts +13 -79
- package/src/tools/dispatch-admission.ts +251 -18
- package/src/tools/dispatch-arguments.ts +84 -1
- package/src/tools/dispatch-plan.ts +165 -6
- package/src/tools/dispatch-runner.ts +364 -23
- package/src/tools/dispatch-types.ts +20 -1
- package/src/tools/dispatch.ts +72 -2
- package/src/tools/monitor.ts +29 -0
- package/src/tools/profiles.ts +18 -4
- package/src/tools/task-worktree.ts +238 -0
- package/src/tools/verify/authoring.ts +61 -1
- package/src/tools/verify/scripts.ts +62 -0
- package/src/tools/worker-evidence.ts +21 -14
- package/src/worker/spec-contract.ts +3 -1
- package/dist/chunk-HC4CLZ2Y.js +0 -68
|
@@ -2,13 +2,16 @@ import { createReadStream, readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
3
3
|
import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import type { DispatchContract } from "../../domains/dispatch/contract.js";
|
|
5
|
-
import {
|
|
6
|
-
isReceiptIntegrity,
|
|
7
|
-
type ReceiptIntegrityResult,
|
|
8
|
-
verifyReceiptIntegrity,
|
|
9
|
-
} from "../../domains/dispatch/receipt-integrity.js";
|
|
5
|
+
import { isReceiptIntegrity, verifyReceiptIntegrity } from "../../domains/dispatch/receipt-integrity.js";
|
|
10
6
|
import type { RunEnvelope, RunReceipt } from "../../domains/dispatch/types.js";
|
|
11
7
|
import { evidenceDirectory, inspectEvidence, listEvidenceOverviews } from "../../domains/evidence/store.js";
|
|
8
|
+
import { formatTrustSummaryLine } from "../../domains/evidence/trust-projection.js";
|
|
9
|
+
import {
|
|
10
|
+
inspectRunReceiptTrustStatus,
|
|
11
|
+
type ReceiptIntegrityOutcome,
|
|
12
|
+
retiredReceiptIntegrity,
|
|
13
|
+
retiredReceiptIntegrityReason,
|
|
14
|
+
} from "../../domains/evidence/trust-status.js";
|
|
12
15
|
import type { EvidenceFinding, EvidenceOverview, EvidenceSource } from "../../domains/evidence/types.js";
|
|
13
16
|
import { type AccountabilitySummary, readAccountabilitySummary } from "../../domains/observability/index.js";
|
|
14
17
|
import type {
|
|
@@ -72,7 +75,7 @@ export interface ViewArtifact {
|
|
|
72
75
|
toolName?: string;
|
|
73
76
|
searchText?: readonly string[];
|
|
74
77
|
load(): Promise<ViewArtifactLoadResult>;
|
|
75
|
-
verify?(): Promise<
|
|
78
|
+
verify?(): Promise<ViewArtifactVerification>;
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
export interface ArtifactProvider {
|
|
@@ -108,7 +111,14 @@ const ACCOUNTABILITY_TOP_CAUSES = 8;
|
|
|
108
111
|
export const VIEW_ARTIFACT_LINE_CAP = 50_000;
|
|
109
112
|
const JSON_PRETTY_MAX_BYTES = 10 * 1024 * 1024;
|
|
110
113
|
|
|
111
|
-
export type ReceiptVerifyResult =
|
|
114
|
+
export type ReceiptVerifyResult = ReceiptIntegrityOutcome;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* What a verify action reports. `retired` marks a seal this build does not
|
|
118
|
+
* verify because its integrity version was retired: not a pass, and not the
|
|
119
|
+
* failure a tampered receipt reports.
|
|
120
|
+
*/
|
|
121
|
+
export type ViewArtifactVerification = { ok: true; detail: string } | { ok: false; detail: string; retired?: true };
|
|
112
122
|
|
|
113
123
|
const RECEIPT_REQUIRED_KEYS = [
|
|
114
124
|
"runId",
|
|
@@ -286,6 +296,21 @@ function validateToolStats(value: unknown): ReceiptVerifyResult {
|
|
|
286
296
|
|
|
287
297
|
type ReadLedgerResult = { ok: true; envelope: RunEnvelope } | { ok: false; reason: string };
|
|
288
298
|
|
|
299
|
+
/**
|
|
300
|
+
* The canonical trust line for a receipt the integrity verifier just accepted:
|
|
301
|
+
* the same file and ledger row, projected through the shared boundary so the
|
|
302
|
+
* receipt view spells its verdict exactly as the board and the CLI do.
|
|
303
|
+
*/
|
|
304
|
+
export function receiptTrustDetail(stateDir: string, runId: string): string {
|
|
305
|
+
try {
|
|
306
|
+
const receipt = JSON.parse(readFileSync(receiptFilePath(stateDir, runId), "utf8")) as RunReceipt;
|
|
307
|
+
const ledger = readRunEnvelope(stateDir, runId);
|
|
308
|
+
return formatTrustSummaryLine(inspectRunReceiptTrustStatus(receipt, ledger.ok ? ledger.envelope : null).status);
|
|
309
|
+
} catch {
|
|
310
|
+
return "integrity verified; trust status unreadable";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
289
314
|
function readRunEnvelope(stateDir: string, runId: string): ReadLedgerResult {
|
|
290
315
|
const runs = readRunLedger(stateDir);
|
|
291
316
|
for (const entry of runs) {
|
|
@@ -398,6 +423,10 @@ export function verifyReceiptFile(stateDir: string, runId: string): ReceiptVerif
|
|
|
398
423
|
if (!isNullableString(r.sessionId)) {
|
|
399
424
|
return { ok: false, reason: `sessionId invalid: ${String(r.sessionId)}` };
|
|
400
425
|
}
|
|
426
|
+
// A retired seal is diagnosed ahead of the shape check, which would
|
|
427
|
+
// otherwise report the older version as an invalid integrity block.
|
|
428
|
+
const retired = retiredReceiptIntegrity(r.integrity);
|
|
429
|
+
if (retired !== null) return { ok: false, reason: retiredReceiptIntegrityReason(retired), retired };
|
|
401
430
|
if (!isReceiptIntegrity(r.integrity)) {
|
|
402
431
|
return { ok: false, reason: "integrity invalid" };
|
|
403
432
|
}
|
|
@@ -673,8 +702,12 @@ export class ReceiptArtifactProvider implements ArtifactProvider {
|
|
|
673
702
|
load: () => loadJsonFileLines(path),
|
|
674
703
|
verify: async () => {
|
|
675
704
|
const result = verifyReceiptFile(this.deps.stateDir, env.id);
|
|
676
|
-
if (!result.ok)
|
|
677
|
-
|
|
705
|
+
if (!result.ok) {
|
|
706
|
+
return { ok: false, detail: result.reason, ...(result.retired !== undefined ? { retired: true } : {}) };
|
|
707
|
+
}
|
|
708
|
+
// "integrity verified" alone read as a verified result. The
|
|
709
|
+
// canonical line says what the seal proves and what it does not.
|
|
710
|
+
return { ok: true, detail: receiptTrustDetail(this.deps.stateDir, env.id) };
|
|
678
711
|
},
|
|
679
712
|
};
|
|
680
713
|
});
|
|
@@ -51,7 +51,9 @@ export type ViewVerificationState =
|
|
|
51
51
|
| { status: "idle" }
|
|
52
52
|
| { status: "running" }
|
|
53
53
|
| { status: "ok"; detail: string }
|
|
54
|
-
| { status: "fail"; detail: string }
|
|
54
|
+
| { status: "fail"; detail: string }
|
|
55
|
+
/** A seal this build does not verify because its version was retired: neither a pass nor a tampered-receipt failure. */
|
|
56
|
+
| { status: "retired"; detail: string };
|
|
55
57
|
|
|
56
58
|
export type ViewScrollAction =
|
|
57
59
|
| "line-up"
|
|
@@ -327,6 +329,7 @@ function verificationText(state: ViewVerificationState | undefined): string {
|
|
|
327
329
|
if (!state || state.status === "idle") return "";
|
|
328
330
|
if (state.status === "running") return `${GLYPH.running} verify running`;
|
|
329
331
|
if (state.status === "ok") return `${GLYPH.ok} verify ok ${state.detail}`;
|
|
332
|
+
if (state.status === "retired") return `${GLYPH.warn} verify retired ${state.detail}`;
|
|
330
333
|
return `${GLYPH.error} verify fail ${state.detail}`;
|
|
331
334
|
}
|
|
332
335
|
|
|
@@ -343,7 +346,14 @@ export function buildArtifactHeader(
|
|
|
343
346
|
if (size.length > 0) parts.push(theme.fg("dim", size));
|
|
344
347
|
const verify = verificationText(verification);
|
|
345
348
|
if (verify.length > 0) {
|
|
346
|
-
const token =
|
|
349
|
+
const token =
|
|
350
|
+
verification?.status === "ok"
|
|
351
|
+
? "success"
|
|
352
|
+
: verification?.status === "fail"
|
|
353
|
+
? "error"
|
|
354
|
+
: verification?.status === "retired"
|
|
355
|
+
? "warning"
|
|
356
|
+
: "info";
|
|
347
357
|
parts.push(theme.fg(token, verify));
|
|
348
358
|
}
|
|
349
359
|
return padAnsi(parts.join(" "), width, ELLIPSIS);
|
|
@@ -689,7 +699,9 @@ export class ViewOverlayView implements Component {
|
|
|
689
699
|
.then((result) => {
|
|
690
700
|
this.verifications.set(
|
|
691
701
|
key,
|
|
692
|
-
result.ok
|
|
702
|
+
result.ok
|
|
703
|
+
? { status: "ok", detail: result.detail }
|
|
704
|
+
: { status: result.retired === true ? "retired" : "fail", detail: result.detail },
|
|
693
705
|
);
|
|
694
706
|
})
|
|
695
707
|
.catch((err) => {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The watchdog's run: one read-only verifier dispatch, one transcript notice.
|
|
3
|
+
*
|
|
4
|
+
* The middleware registration decides when to fire and what the turn changed;
|
|
5
|
+
* this decides nothing. It dispatches through the ordinary path so admission,
|
|
6
|
+
* the receipt, and the Fleet Runs island treat a watchdog run exactly as they
|
|
7
|
+
* treat any other, reads the failed checks out of the `verifier-report`
|
|
8
|
+
* contract, and hands the operator one line. A passing report produces nothing
|
|
9
|
+
* at all.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { BusChannels } from "../core/bus-events.js";
|
|
13
|
+
import type { SafeEventBus } from "../core/event-bus.js";
|
|
14
|
+
import { parseVerifierResult } from "../domains/agents/index.js";
|
|
15
|
+
import type { DispatchContract, DispatchRequest } from "../domains/dispatch/contract.js";
|
|
16
|
+
import { type AgentRoleFactsResolver, requestExecutionRole } from "../domains/dispatch/execution-role.js";
|
|
17
|
+
import { WATCHDOG_TASK, type WatchdogTrigger, watchdogBriefing } from "../domains/middleware/index.js";
|
|
18
|
+
import { watchdogBlockersNotice } from "./bus-notices.js";
|
|
19
|
+
|
|
20
|
+
/** The builtin recipe the watchdog reviews with. */
|
|
21
|
+
export const WATCHDOG_AGENT_ID = "verifier";
|
|
22
|
+
|
|
23
|
+
export interface WatchdogRunDeps {
|
|
24
|
+
dispatch: DispatchContract;
|
|
25
|
+
bus?: SafeEventBus;
|
|
26
|
+
getAgentRoleFacts?: AgentRoleFactsResolver;
|
|
27
|
+
/** `watchdog.target` when the operator set one; the session's active target otherwise. */
|
|
28
|
+
target?: string | undefined;
|
|
29
|
+
/** One transcript line for the operator. Absent means the finding has nowhere to go and the run is skipped. */
|
|
30
|
+
emitNotice?: (text: string) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Dispatch one watchdog review and report what it found.
|
|
35
|
+
*
|
|
36
|
+
* Resolves when the run has settled, whatever the outcome. A watchdog that
|
|
37
|
+
* failed is not a turn that failed: the review is advisory, so a broken run
|
|
38
|
+
* stays silent rather than raising an alarm about itself in the middle of the
|
|
39
|
+
* operator's work.
|
|
40
|
+
*/
|
|
41
|
+
export async function runWatchdogReview(trigger: WatchdogTrigger, deps: WatchdogRunDeps): Promise<void> {
|
|
42
|
+
if (!deps.emitNotice) return;
|
|
43
|
+
const request: DispatchRequest = {
|
|
44
|
+
agentId: WATCHDOG_AGENT_ID,
|
|
45
|
+
task: WATCHDOG_TASK,
|
|
46
|
+
briefing: watchdogBriefing(trigger),
|
|
47
|
+
executionRole: requestExecutionRole({
|
|
48
|
+
agentId: WATCHDOG_AGENT_ID,
|
|
49
|
+
...(deps.getAgentRoleFacts ? { resolveFacts: deps.getAgentRoleFacts } : {}),
|
|
50
|
+
}),
|
|
51
|
+
requestOrigin: "internal",
|
|
52
|
+
// Narrowed below whatever the session holds. A reviewer that could write
|
|
53
|
+
// would be a second builder nobody admitted.
|
|
54
|
+
autonomy: "read-only",
|
|
55
|
+
...(deps.target ? { target: deps.target } : {}),
|
|
56
|
+
};
|
|
57
|
+
const progressBus = deps.dispatch.ownsProgressBus?.(deps.bus) === true ? undefined : deps.bus;
|
|
58
|
+
try {
|
|
59
|
+
const handle = await deps.dispatch.dispatch(request);
|
|
60
|
+
for await (const event of handle.events) {
|
|
61
|
+
const typed = event as { type?: string };
|
|
62
|
+
if (!typed.type || typed.type === "heartbeat") continue;
|
|
63
|
+
progressBus?.emit(BusChannels.DispatchProgress, { runId: handle.runId, agentId: WATCHDOG_AGENT_ID, event });
|
|
64
|
+
}
|
|
65
|
+
const receipt = await handle.finalPromise;
|
|
66
|
+
const report = parseVerifierResult(receipt.output?.text ?? null);
|
|
67
|
+
if (report === null) return;
|
|
68
|
+
const notice = watchdogBlockersNotice(report.checks);
|
|
69
|
+
if (notice === null) return;
|
|
70
|
+
deps.emitNotice(notice.text);
|
|
71
|
+
} catch {
|
|
72
|
+
// An advisory review that could not run says nothing. The receipt, if one
|
|
73
|
+
// was written at all, is in the dispatch board like every other run.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
import { existsSync, readFileSync } from "node:fs";
|
|
17
17
|
import { join } from "node:path";
|
|
18
18
|
import { clioStateDir } from "../core/xdg.js";
|
|
19
|
+
import type { RunEnvelope, RunReceipt } from "../domains/dispatch/types.js";
|
|
20
|
+
import { inspectRunReceiptTrustStatus } from "../domains/evidence/trust-status.js";
|
|
19
21
|
import { receiptFilePath } from "./view/artifacts.js";
|
|
20
22
|
import type { WorkerReceiptFacts, WorkerResultContract } from "./worker-stream.js";
|
|
21
23
|
|
|
@@ -86,12 +88,22 @@ export function workerReceiptFacts(receipt: Record<string, unknown>): WorkerRece
|
|
|
86
88
|
|
|
87
89
|
/** Read and project `<state>/receipts/<runId>.json` alone; null when it is absent or unreadable. */
|
|
88
90
|
function readReceiptFileFacts(runId: string, stateDir: string): WorkerReceiptFacts | null {
|
|
91
|
+
let parsed: unknown;
|
|
89
92
|
try {
|
|
90
|
-
|
|
91
|
-
return isRecord(parsed) ? workerReceiptFacts(parsed) : null;
|
|
93
|
+
parsed = JSON.parse(readFileSync(receiptFilePath(stateDir, runId), "utf8"));
|
|
92
94
|
} catch {
|
|
93
95
|
return null;
|
|
94
96
|
}
|
|
97
|
+
if (!isRecord(parsed)) return null;
|
|
98
|
+
const facts = workerReceiptFacts(parsed);
|
|
99
|
+
if (facts === null) return null;
|
|
100
|
+
// The trust verdict is an authenticated read-back: the receipt file
|
|
101
|
+
// against the ledger row it was sealed from. Without the row there is no
|
|
102
|
+
// authentication, and no verdict is better than a guessed one.
|
|
103
|
+
const row = findRunRow(runId, stateDir);
|
|
104
|
+
if (row === null) return facts;
|
|
105
|
+
const trust = inspectRunReceiptTrustStatus(parsed as unknown as RunReceipt, row as unknown as RunEnvelope).status;
|
|
106
|
+
return { ...facts, trust };
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
/** Read and project `<state>/receipts/<runId>.json`; null when it is absent or unreadable. */
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* Pure: no I/O, no bus, no session.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import type { CouncilReport } from "../domains/agents/result-contract.js";
|
|
14
15
|
import { WORKER_OUTPUT_MAX_BYTES } from "../domains/dispatch/event-pump.js";
|
|
15
16
|
import { truncateUtf8 } from "../tools/truncate-utf8.js";
|
|
16
17
|
import { type WorkerEntryState, workerAskedByModel } from "./worker-stream.js";
|
|
@@ -56,12 +57,66 @@ export const WORKER_SHARE_ORIGIN = "shared by the operator";
|
|
|
56
57
|
* produced no text is not worth a turn.
|
|
57
58
|
*/
|
|
58
59
|
export function formatWorkerShareNote(facts: WorkerShareFacts): string | null {
|
|
59
|
-
|
|
60
|
+
return shareNote(facts, facts.text);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The header every shared note carries, plus one bounded body under it. */
|
|
64
|
+
function shareNote(facts: WorkerShareFacts, body: string): string | null {
|
|
65
|
+
const text = body.trim();
|
|
60
66
|
if (text.length === 0) return null;
|
|
61
67
|
const header = `${WORKER_SHARE_NOTE_PREFIX} ${facts.agentId} · run ${facts.runId} · ${outcomeWord(facts.outcome)} · ${WORKER_SHARE_ORIGIN}`;
|
|
62
68
|
return `${header}\n${truncateUtf8(text, WORKER_SHARE_MAX_BYTES, SHARE_TRUNCATION_MARKER)}`;
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
/** One member's answer under its roster label, or its failure when it produced none. */
|
|
72
|
+
function councilMemberLine(member: CouncilReport["members"][number]): string {
|
|
73
|
+
if (member.failed !== undefined) return `[${member.label}] failed: ${member.failed.reason}`;
|
|
74
|
+
const verdict = member.verdict === undefined ? "" : ` (verdict ${member.verdict})`;
|
|
75
|
+
return `[${member.label}]${verdict} ${member.answer.trim()}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The synthesis line: what the council concluded, and how it concluded it. */
|
|
79
|
+
function councilSynthesisLines(synthesis: CouncilReport["synthesis"]): string[] {
|
|
80
|
+
if (synthesis.kind === "none") return ["[synthesis none] the members answered separately; no synthesis was run."];
|
|
81
|
+
const facts = [
|
|
82
|
+
...(synthesis.verdict !== undefined ? [`verdict ${synthesis.verdict}`] : []),
|
|
83
|
+
...(synthesis.tally !== undefined
|
|
84
|
+
? [
|
|
85
|
+
`tally ${Object.entries(synthesis.tally)
|
|
86
|
+
.map(([key, count]) => `${key}=${count}`)
|
|
87
|
+
.join(" ")}`,
|
|
88
|
+
]
|
|
89
|
+
: []),
|
|
90
|
+
...(synthesis.judgeRunId !== undefined ? [`judge run ${synthesis.judgeRunId}`] : []),
|
|
91
|
+
];
|
|
92
|
+
const head = `[synthesis ${synthesis.kind}]${facts.length > 0 ? ` ${facts.join(" · ")}` : ""}`;
|
|
93
|
+
const text = synthesis.text?.trim() ?? "";
|
|
94
|
+
return text.length > 0 ? [head, text] : [head];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A whole council as one operator note: every member's labelled answer, in the
|
|
99
|
+
* order the report seals them, then the synthesis. Sharing a synthesis run id
|
|
100
|
+
* means sharing what the council decided, and the members' answers are what it
|
|
101
|
+
* decided from, so they travel together in one bounded block rather than as
|
|
102
|
+
* several notes the main agent would have to reassemble.
|
|
103
|
+
*/
|
|
104
|
+
export function formatCouncilShareNote(facts: WorkerShareFacts, report: CouncilReport): string | null {
|
|
105
|
+
const finalRound = report.members.reduce((highest, member) => Math.max(highest, member.round), 1);
|
|
106
|
+
const body = [
|
|
107
|
+
...report.members.filter((member) => member.round === finalRound).map(councilMemberLine),
|
|
108
|
+
...councilSynthesisLines(report.synthesis),
|
|
109
|
+
].join("\n\n");
|
|
110
|
+
return shareNote(facts, body);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** One council member's answer, labelled with the roster label the operator watched it under. */
|
|
114
|
+
export function formatCouncilMemberShareNote(facts: WorkerShareFacts, label: string): string | null {
|
|
115
|
+
const text = facts.text.trim();
|
|
116
|
+
if (text.length === 0) return null;
|
|
117
|
+
return shareNote(facts, `[${label}] ${text}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
65
120
|
/**
|
|
66
121
|
* Whether operator text is a shared worker note. The header is the first line,
|
|
67
122
|
* so the chat loop can tell the honesty rail a worker result entered the turn
|
|
@@ -42,6 +42,7 @@ import type {
|
|
|
42
42
|
RunAbortedPayload,
|
|
43
43
|
} from "../core/bus-events.js";
|
|
44
44
|
import type { RunKind } from "../domains/dispatch/types.js";
|
|
45
|
+
import type { CanonicalTrustStatus } from "../domains/evidence/trust-status.js";
|
|
45
46
|
import type { WorkerRunOrigin, WorkerRunRuntime, WorkerRunRuntimeKind } from "../domains/session/index.js";
|
|
46
47
|
import { createWorkerProgressFold, type WorkerProgressFold, type WorkerProgressSnapshot } from "./worker-progress.js";
|
|
47
48
|
|
|
@@ -88,6 +89,13 @@ export interface WorkerReceiptSummary {
|
|
|
88
89
|
/** A receipt's projection: the summary plus the answer it sealed. */
|
|
89
90
|
export interface WorkerReceiptFacts extends WorkerReceiptSummary {
|
|
90
91
|
text?: string;
|
|
92
|
+
/**
|
|
93
|
+
* The canonical trust status of the persisted receipt, authenticated
|
|
94
|
+
* against the persisted ledger row it was sealed from. Present only when
|
|
95
|
+
* both could be read back, so a surface that shows it shows an
|
|
96
|
+
* authenticated verdict and never the sealing process's own claim.
|
|
97
|
+
*/
|
|
98
|
+
trust?: CanonicalTrustStatus;
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
/** Reads `receipts/<runId>.json` and projects it; null when it is absent or unreadable. */
|
|
@@ -113,6 +121,12 @@ export interface WorkerEntryState {
|
|
|
113
121
|
/** Every attempt of this assignment, oldest first. */
|
|
114
122
|
attempts: WorkerAttempt[];
|
|
115
123
|
pending: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Council provenance, present only on a run of a council group. `/share`
|
|
126
|
+
* reads it so a member's answer reaches the main agent under the roster label
|
|
127
|
+
* the operator watched it under, rather than as one unattributed voice.
|
|
128
|
+
*/
|
|
129
|
+
council?: { group: string; label: string; color?: string; round: number };
|
|
116
130
|
receipt?: WorkerReceiptSummary;
|
|
117
131
|
/** Agent origin: the tool call whose execution spawned this run. */
|
|
118
132
|
parentToolCallId?: string;
|
|
@@ -323,6 +337,7 @@ export function createWorkerStream(options: WorkerStreamOptions = {}): WorkerStr
|
|
|
323
337
|
progress: progress.snapshot(),
|
|
324
338
|
attempts: [{ runId, targetLabel: workerTargetLabel(runtime) }],
|
|
325
339
|
pending: true,
|
|
340
|
+
...(payload.council !== undefined ? { council: { ...payload.council } } : {}),
|
|
326
341
|
...(payload.parentToolCallId !== undefined ? { parentToolCallId: payload.parentToolCallId } : {}),
|
|
327
342
|
};
|
|
328
343
|
byAssignment.set(assignmentId, entry);
|
package/src/tools/bootstrap.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { WorkerRosters } from "../core/defaults.js";
|
|
1
2
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
2
3
|
import type { AgentSpec } from "../domains/agents/spec.js";
|
|
3
4
|
import type { DispatchContract } from "../domains/dispatch/contract.js";
|
|
@@ -22,6 +23,7 @@ export interface ToolBootstrapDeps extends CoreToolBootstrapDeps {
|
|
|
22
23
|
getAgentRoleFacts?: AgentRoleFactsResolver;
|
|
23
24
|
getAutonomy?: () => AutonomyLevel;
|
|
24
25
|
getCostCeilingUsd?: () => number;
|
|
26
|
+
getWorkerRosters?: () => WorkerRosters;
|
|
25
27
|
dispatchBackground?: DispatchBackgroundRegistry;
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -44,6 +46,7 @@ export function registerAllTools(registry: ToolRegistry, deps: ToolBootstrapDeps
|
|
|
44
46
|
...(deps.getAgentRoleFacts ? { getAgentRoleFacts: deps.getAgentRoleFacts } : {}),
|
|
45
47
|
...(deps.getAutonomy ? { getAutonomy: deps.getAutonomy } : {}),
|
|
46
48
|
...(deps.getCostCeilingUsd ? { getCostCeilingUsd: deps.getCostCeilingUsd } : {}),
|
|
49
|
+
...(deps.getWorkerRosters ? { getWorkerRosters: deps.getWorkerRosters } : {}),
|
|
47
50
|
...(deps.dispatchBackground ? { background: deps.dispatchBackground } : {}),
|
|
48
51
|
};
|
|
49
52
|
registry.register({
|
|
@@ -33,8 +33,15 @@ import {
|
|
|
33
33
|
writeFileSync,
|
|
34
34
|
} from "node:fs";
|
|
35
35
|
import { hostname } from "node:os";
|
|
36
|
-
import {
|
|
36
|
+
import { join, relative, resolve } from "node:path";
|
|
37
37
|
import type { RunKind } from "../domains/dispatch/types.js";
|
|
38
|
+
import {
|
|
39
|
+
commitWorktreePath,
|
|
40
|
+
isCanonicalWorktreePathInside,
|
|
41
|
+
mergeWorktreeBranch,
|
|
42
|
+
protectedPathsChangedByWorktreeBranch,
|
|
43
|
+
worktreeBranchDiffStat,
|
|
44
|
+
} from "./task-worktree.js";
|
|
38
45
|
|
|
39
46
|
export interface CandidateWorktree {
|
|
40
47
|
index: number;
|
|
@@ -300,20 +307,7 @@ function isCanonicalDirectoryAt(path: string): boolean {
|
|
|
300
307
|
* be mistaken for a child of `/group-a` as it can with textual startsWith.
|
|
301
308
|
*/
|
|
302
309
|
export function isCanonicalPathInside(parent: string, candidate: string): boolean {
|
|
303
|
-
|
|
304
|
-
let canonicalCandidate: string;
|
|
305
|
-
try {
|
|
306
|
-
canonicalParent = realpathSync(parent);
|
|
307
|
-
} catch {
|
|
308
|
-
canonicalParent = resolve(parent);
|
|
309
|
-
}
|
|
310
|
-
try {
|
|
311
|
-
canonicalCandidate = realpathSync(candidate);
|
|
312
|
-
} catch {
|
|
313
|
-
canonicalCandidate = resolve(candidate);
|
|
314
|
-
}
|
|
315
|
-
const rel = relative(canonicalParent, canonicalCandidate);
|
|
316
|
-
return rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
310
|
+
return isCanonicalWorktreePathInside(parent, candidate);
|
|
317
311
|
}
|
|
318
312
|
|
|
319
313
|
function manifestFromUnknown(value: unknown): CompeteGroupManifest | null {
|
|
@@ -557,29 +551,12 @@ export function createCandidateWorktree(
|
|
|
557
551
|
* builder changed nothing (an empty candidate is a legitimate ranking fact).
|
|
558
552
|
*/
|
|
559
553
|
export function commitCandidateWork(worktree: CandidateWorktree, message: string): boolean {
|
|
560
|
-
|
|
561
|
-
const staged = git(worktree.path, ["status", "--porcelain"]);
|
|
562
|
-
if (staged.length === 0) return false;
|
|
563
|
-
git(worktree.path, [
|
|
564
|
-
"-c",
|
|
565
|
-
`user.name=${COMPETE_COMMIT_IDENTITY}`,
|
|
566
|
-
"-c",
|
|
567
|
-
`user.email=${COMPETE_COMMIT_IDENTITY}@local`,
|
|
568
|
-
"commit",
|
|
569
|
-
"-m",
|
|
570
|
-
message,
|
|
571
|
-
"--no-verify",
|
|
572
|
-
]);
|
|
573
|
-
return true;
|
|
554
|
+
return commitWorktreePath(worktree.path, COMPETE_COMMIT_IDENTITY, message);
|
|
574
555
|
}
|
|
575
556
|
|
|
576
557
|
/** One-line stat summary of what a candidate branch changed relative to HEAD. */
|
|
577
558
|
export function candidateDiffStat(root: string, branch: string): string {
|
|
578
|
-
|
|
579
|
-
return git(root, ["diff", "--shortstat", `HEAD...${branch}`]) || "no changes";
|
|
580
|
-
} catch {
|
|
581
|
-
return "diff unavailable";
|
|
582
|
-
}
|
|
559
|
+
return worktreeBranchDiffStat(root, branch);
|
|
583
560
|
}
|
|
584
561
|
|
|
585
562
|
/**
|
|
@@ -592,31 +569,7 @@ export function protectedPathsChangedByCompeteBranch(
|
|
|
592
569
|
branch: string,
|
|
593
570
|
protectedPaths: ReadonlyArray<string>,
|
|
594
571
|
): string[] {
|
|
595
|
-
|
|
596
|
-
const protectedInside = protectedPaths
|
|
597
|
-
.map((path) => resolve(path))
|
|
598
|
-
.filter((path) => {
|
|
599
|
-
const rel = relative(canonical, path);
|
|
600
|
-
return rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
601
|
-
});
|
|
602
|
-
if (protectedInside.length === 0) return [];
|
|
603
|
-
const changed = git(canonical, ["diff", "--name-only", "-z", "--no-renames", `HEAD...${branch}`])
|
|
604
|
-
.split("\0")
|
|
605
|
-
.filter((path) => path.length > 0)
|
|
606
|
-
.map((path) => resolve(canonical, path));
|
|
607
|
-
const blocked = new Set<string>();
|
|
608
|
-
for (const candidate of changed) {
|
|
609
|
-
for (const protectedPath of protectedInside) {
|
|
610
|
-
const rel = relative(protectedPath, candidate);
|
|
611
|
-
if (
|
|
612
|
-
candidate === protectedPath ||
|
|
613
|
-
(rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel))
|
|
614
|
-
) {
|
|
615
|
-
blocked.add(protectedPath);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
return [...blocked].sort();
|
|
572
|
+
return protectedPathsChangedByWorktreeBranch(root, branch, protectedPaths);
|
|
620
573
|
}
|
|
621
574
|
|
|
622
575
|
function registeredWorktreePaths(root: string): string[] {
|
|
@@ -687,26 +640,7 @@ export function removeCandidateWorktree(
|
|
|
687
640
|
* the merge and reports failure so the operator decides.
|
|
688
641
|
*/
|
|
689
642
|
export function mergeWinnerBranch(root: string, branch: string): { ok: true } | { ok: false; reason: string } {
|
|
690
|
-
|
|
691
|
-
git(root, [
|
|
692
|
-
"-c",
|
|
693
|
-
`user.name=${COMPETE_COMMIT_IDENTITY}`,
|
|
694
|
-
"-c",
|
|
695
|
-
`user.email=${COMPETE_COMMIT_IDENTITY}@local`,
|
|
696
|
-
"merge",
|
|
697
|
-
"--no-edit",
|
|
698
|
-
"--no-verify",
|
|
699
|
-
branch,
|
|
700
|
-
]);
|
|
701
|
-
return { ok: true };
|
|
702
|
-
} catch (err) {
|
|
703
|
-
try {
|
|
704
|
-
git(root, ["merge", "--abort"]);
|
|
705
|
-
} catch {
|
|
706
|
-
// No merge in progress; nothing to abort.
|
|
707
|
-
}
|
|
708
|
-
return { ok: false, reason: err instanceof Error ? (err.message.split("\n")[0] ?? "merge failed") : String(err) };
|
|
709
|
-
}
|
|
643
|
+
return mergeWorktreeBranch(root, branch, COMPETE_COMMIT_IDENTITY);
|
|
710
644
|
}
|
|
711
645
|
|
|
712
646
|
/**
|