@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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { createHash, randomBytes } from "node:crypto";
|
|
13
13
|
import { type Dirent, existsSync, readdirSync, readFileSync } from "node:fs";
|
|
14
14
|
import { isAbsolute, relative, resolve as resolvePath, sep } from "node:path";
|
|
15
|
-
import { BusChannels, type DispatchCompletedPayload } from "../../core/bus-events.js";
|
|
15
|
+
import { BusChannels, type DispatchCompletedPayload, type DispatchRunIdentity } from "../../core/bus-events.js";
|
|
16
16
|
import { DEFAULT_SETTINGS, type DelegationToolGovernance } from "../../core/defaults.js";
|
|
17
17
|
import type { DomainBundle, DomainContext, DomainExtension } from "../../core/domain-loader.js";
|
|
18
18
|
import { GUARDRAIL_DEFAULTS } from "../../core/guardrails.js";
|
|
@@ -40,6 +40,12 @@ import { WORKER_RUNTIME_MEDIATES_CLIO_DISPATCH } from "../../engine/worker-runti
|
|
|
40
40
|
import { toolPromptHintsForNames } from "../../tools/builtin-tool-catalog.js";
|
|
41
41
|
import { networkToolsDisabled } from "../../tools/network-policy.js";
|
|
42
42
|
import { applyToolProfile, assertToolProfileEnforceable, type ToolProfileName } from "../../tools/profiles.js";
|
|
43
|
+
import {
|
|
44
|
+
applyTaskWorktree,
|
|
45
|
+
cleanupTaskWorktree,
|
|
46
|
+
createTaskWorktree,
|
|
47
|
+
gitCheckoutRoot,
|
|
48
|
+
} from "../../tools/task-worktree.js";
|
|
43
49
|
import { truncateUtf8 } from "../../tools/truncate-utf8.js";
|
|
44
50
|
import {
|
|
45
51
|
serializeWorkerRuntimeDescriptor,
|
|
@@ -117,6 +123,7 @@ import { AssignmentRegistry, applyActiveRouteSelection, asAssignmentId } from ".
|
|
|
117
123
|
import type { AssignmentAttemptStartEvent } from "./assignment-events.js";
|
|
118
124
|
import { reconcileOrphanAssignments } from "./assignment-reconcile.js";
|
|
119
125
|
import {
|
|
126
|
+
assignmentProcessOwnerAlive,
|
|
120
127
|
cancelStoredAssignment,
|
|
121
128
|
failQueuedAssignment,
|
|
122
129
|
getStoredAssignment,
|
|
@@ -138,6 +145,7 @@ import { type BatchState, createBatch, onRunComplete, snapshotBatch } from "./ba
|
|
|
138
145
|
import { type RunToolBudgetEnvelope, resolveToolBudgetEnvelope } from "./budget-envelope.js";
|
|
139
146
|
import { assessCapabilityMismatch, type CapabilityMismatch } from "./capability-match.js";
|
|
140
147
|
import { capacityLeaseUsage, createNodeLeaseUsageReader } from "./capacity-lease.js";
|
|
148
|
+
import { acquireCheckoutWriterLease, type CheckoutWriterLease } from "./checkout-writer-lease.js";
|
|
141
149
|
import type {
|
|
142
150
|
DispatchAdmissionObserver,
|
|
143
151
|
DispatchContract,
|
|
@@ -159,6 +167,8 @@ import {
|
|
|
159
167
|
import { routeFactVerdict } from "./fleet-preflight.js";
|
|
160
168
|
import { competeStanceLiner, isBoundedGateRolePrompt } from "./gate-role-prompts.js";
|
|
161
169
|
import { classifyHeartbeat, DEFAULT_HEARTBEAT_SPEC, type HeartbeatSpec, type HeartbeatStatus } from "./heartbeat.js";
|
|
170
|
+
import { hostVerificationRejection, runHostVerification } from "./host-verification.js";
|
|
171
|
+
import { renderDispatchIntentRequirements } from "./intent-requirements.js";
|
|
162
172
|
import { adaptJointRouteInput } from "./joint-route-adapter.js";
|
|
163
173
|
import {
|
|
164
174
|
configuredJointNodes,
|
|
@@ -173,6 +183,12 @@ import {
|
|
|
173
183
|
resultContractWasDue,
|
|
174
184
|
runStatusForOutcome,
|
|
175
185
|
} from "./outcome.js";
|
|
186
|
+
import {
|
|
187
|
+
type DispatchPathScope,
|
|
188
|
+
declaredScopeReplacementDiagnostic,
|
|
189
|
+
declaredScopeReplacementNotice,
|
|
190
|
+
resolveDispatchPathScope,
|
|
191
|
+
} from "./path-scope.js";
|
|
176
192
|
import { deriveEnvelopePhaseDurations, deriveRunPhaseDurations, recordRunTimingBestEffort } from "./phase-timing.js";
|
|
177
193
|
import { createFleetPlacementPreviewResolver, createFleetPlacementResolver } from "./placement.js";
|
|
178
194
|
import {
|
|
@@ -209,7 +225,7 @@ import { createRouteObserver, type RouteObservationHandle, type RouteObserver }
|
|
|
209
225
|
import { reduceRouteQuality } from "./route-quality.js";
|
|
210
226
|
import { defaultRoutingIntent } from "./routing-intent.js";
|
|
211
227
|
import { detectRunIdentity } from "./run-identity.js";
|
|
212
|
-
import { type Ledger, openLedger } from "./state.js";
|
|
228
|
+
import { type Ledger, newRunId, openLedger } from "./state.js";
|
|
213
229
|
import {
|
|
214
230
|
countToolCalls,
|
|
215
231
|
hasPotentiallyMutatingAttempt,
|
|
@@ -606,6 +622,14 @@ function recordWorkerRunEffect(recorder: RunEffectsRecorder, event: Record<strin
|
|
|
606
622
|
*/
|
|
607
623
|
const DISPATCH_DRAIN_GRACE_MS = 2000;
|
|
608
624
|
|
|
625
|
+
/**
|
|
626
|
+
* How many finalized runs keep their observed write targets in memory. The
|
|
627
|
+
* write boundary reads a run's record while closing the window it ran in, which
|
|
628
|
+
* is the same wave, so a few hundred runs of headroom is far more than the
|
|
629
|
+
* lookup ever reaches back for.
|
|
630
|
+
*/
|
|
631
|
+
const RUN_WRITE_TARGET_HISTORY = 512;
|
|
632
|
+
|
|
609
633
|
async function awaitEventDrain(drained: Promise<void>, graceMs = DISPATCH_DRAIN_GRACE_MS): Promise<boolean> {
|
|
610
634
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
611
635
|
const grace = new Promise<false>((resolve) => {
|
|
@@ -743,9 +767,14 @@ function pickOrchestratorScope(safety: SafetyContract): ScopeSpec {
|
|
|
743
767
|
return safety.scopes.workspace;
|
|
744
768
|
}
|
|
745
769
|
|
|
746
|
-
function pickWorkerScope(
|
|
770
|
+
function pickWorkerScope(
|
|
771
|
+
safety: SafetyContract,
|
|
772
|
+
requestedActions: ReadonlyArray<ActionClass>,
|
|
773
|
+
pathScope: DispatchPathScope,
|
|
774
|
+
): ScopeSpec {
|
|
747
775
|
if (requestedActions.every((action) => action === "read")) return safety.scopes.readonly;
|
|
748
|
-
return safety.scopes.workspace;
|
|
776
|
+
if (pathScope.writeBoundaries.length === 0) return safety.scopes.workspace;
|
|
777
|
+
return { ...safety.scopes.workspace, allowedWriteRoots: pathScope.writeBoundaries };
|
|
749
778
|
}
|
|
750
779
|
|
|
751
780
|
function deriveRequestedActions(tools: ReadonlyArray<ToolName>, safety: SafetyContract): ReadonlyArray<ActionClass> {
|
|
@@ -985,6 +1014,25 @@ function hasPersonaOverride(req: DispatchRequest): boolean {
|
|
|
985
1014
|
return typeof req.systemPrompt === "string" && req.systemPrompt.trim().length > 0;
|
|
986
1015
|
}
|
|
987
1016
|
|
|
1017
|
+
/**
|
|
1018
|
+
* Whether this request's system prompt is a caller-authored persona rather
|
|
1019
|
+
* than one of the coordinator's own bounded gate-role prompts.
|
|
1020
|
+
*
|
|
1021
|
+
* A reviewer, a compete judge, and a council synthesis all carry a system
|
|
1022
|
+
* prompt the coordinator wrote, pinned by {@link isBoundedGateRolePrompt} to
|
|
1023
|
+
* one exact text under one gate role and read-only autonomy. That is the
|
|
1024
|
+
* topology speaking, not an operator reshaping a recipe, so it is not the
|
|
1025
|
+
* thing the shadow and internal audiences are protected from. The ACP
|
|
1026
|
+
* delegation path already draws the line here; without the same line, a
|
|
1027
|
+
* council that seats the builtin read-only `researcher` (a shadow recipe, and
|
|
1028
|
+
* the agent every unnamed council seats) refused `--synthesis judge` outright,
|
|
1029
|
+
* before a single member ran.
|
|
1030
|
+
*/
|
|
1031
|
+
function hasCallerPersonaOverride(req: DispatchRequest): boolean {
|
|
1032
|
+
if (!hasPersonaOverride(req)) return false;
|
|
1033
|
+
return !isBoundedGateRolePrompt({ role: req.gate?.role, autonomy: req.autonomy, systemPrompt: req.systemPrompt });
|
|
1034
|
+
}
|
|
1035
|
+
|
|
988
1036
|
function personaOverrideFor(req: DispatchRequest, staticCompositionHash: string | null): RunPersonaOverride | null {
|
|
989
1037
|
if (!hasPersonaOverride(req) || staticCompositionHash === null) return null;
|
|
990
1038
|
return { promptHash: staticCompositionHash };
|
|
@@ -995,9 +1043,8 @@ function personaOverrideFor(req: DispatchRequest, staticCompositionHash: string
|
|
|
995
1043
|
* flows through dynamic messages, never through the system prompt. The system
|
|
996
1044
|
* prompt itself is stable per recipe and tool surface with one exception: the
|
|
997
1045
|
* operator-editable layer (`additionalFragments`) folds in path-scoped project
|
|
998
|
-
* rules selected
|
|
999
|
-
*
|
|
1000
|
-
* runs of one recipe when a rule's glob matches one task and not the other.
|
|
1046
|
+
* rules selected from the canonical request scope, so `staticCompositionHash`
|
|
1047
|
+
* can differ between two runs of one recipe when their scoped rules differ.
|
|
1001
1048
|
* A local model's worker prefix cache misses on that flip; the trade was made
|
|
1002
1049
|
* deliberately in #96 to keep rules scoped rather than shipped wholesale.
|
|
1003
1050
|
*/
|
|
@@ -1066,35 +1113,6 @@ function workerProjectContextIncludesVerification(body: string): boolean {
|
|
|
1066
1113
|
return body.includes("\nVerification expectations:\n");
|
|
1067
1114
|
}
|
|
1068
1115
|
|
|
1069
|
-
/**
|
|
1070
|
-
* Path-like tokens in free text: a slash-separated segment, or a bare
|
|
1071
|
-
* `name.ext` token. The model-facing dispatch tool has no structured
|
|
1072
|
-
* path/file field, so a worker's task text is the only working-context
|
|
1073
|
-
* signal available at compile time; this is best-effort recall, not a
|
|
1074
|
-
* parser. A false-positive token is harmless because `selectActiveRules`
|
|
1075
|
-
* only activates a rule when the token matches that rule's own glob.
|
|
1076
|
-
*/
|
|
1077
|
-
const PATH_TOKEN_RE = /(?:[\w.-]+\/)+[\w.-]+|\b[\w-]+\.[A-Za-z0-9]{1,8}\b/g;
|
|
1078
|
-
|
|
1079
|
-
/**
|
|
1080
|
-
* Best-effort working-context paths for a dispatched worker: `writeRoots`
|
|
1081
|
-
* when the caller set them (the precise signal, for internal/programmatic
|
|
1082
|
-
* callers), plus path-like tokens recalled from the task and briefing text.
|
|
1083
|
-
* Feeds the same `selectActiveRules` a session uses, so a worker whose task
|
|
1084
|
-
* touches a ruled path reads that rule instead of never hearing about it.
|
|
1085
|
-
*/
|
|
1086
|
-
function workerWorkingContextPaths(req: DispatchRequest): string[] {
|
|
1087
|
-
const out = new Set<string>();
|
|
1088
|
-
for (const root of req.writeRoots ?? []) out.add(root);
|
|
1089
|
-
const text = `${req.task}\n${req.briefing ?? ""}`;
|
|
1090
|
-
for (const match of text.matchAll(PATH_TOKEN_RE)) {
|
|
1091
|
-
const token = match[0];
|
|
1092
|
-
if (token.startsWith("http://") || token.startsWith("https://")) continue;
|
|
1093
|
-
out.add(token);
|
|
1094
|
-
}
|
|
1095
|
-
return [...out];
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
1116
|
export function buildDynamicPromptMessages(
|
|
1099
1117
|
req: DispatchRequest,
|
|
1100
1118
|
dynamicContext: WorkerDynamicContext = {},
|
|
@@ -1118,6 +1136,10 @@ export function buildDynamicPromptMessages(
|
|
|
1118
1136
|
const body = `Safety posture: autonomy ${autonomy}. ${workerSafetyOneLiner(autonomy, permission)} Worker permission routing: ${permission}.`;
|
|
1119
1137
|
messages.push({ id: "dispatch-safety-posture", body, contentHash: sha256(body) });
|
|
1120
1138
|
}
|
|
1139
|
+
const requirements = renderDispatchIntentRequirements(req.intent);
|
|
1140
|
+
if (requirements !== null) {
|
|
1141
|
+
messages.push({ id: "dispatch-intent-requirements", body: requirements, contentHash: sha256(requirements) });
|
|
1142
|
+
}
|
|
1121
1143
|
if (req.competeStance !== undefined) {
|
|
1122
1144
|
const body = competeStanceLiner(req.competeStance);
|
|
1123
1145
|
messages.push({ id: "dispatch-compete-stance", body, contentHash: sha256(body) });
|
|
@@ -1242,6 +1264,7 @@ interface DispatchAdmissionStage {
|
|
|
1242
1264
|
|
|
1243
1265
|
interface DispatchWorkerSpecInput {
|
|
1244
1266
|
req: DispatchRequest;
|
|
1267
|
+
pathScope: DispatchPathScope;
|
|
1245
1268
|
target: ResolvedTarget;
|
|
1246
1269
|
admission: DispatchAdmissionStage;
|
|
1247
1270
|
recipe?: AgentRecipe | null;
|
|
@@ -1261,6 +1284,7 @@ interface DispatchWorkerSpecInput {
|
|
|
1261
1284
|
|
|
1262
1285
|
interface DispatchLifecycleStage {
|
|
1263
1286
|
recipe: AgentRecipe | null;
|
|
1287
|
+
pathScope: DispatchPathScope;
|
|
1264
1288
|
admission: DispatchAdmissionStage;
|
|
1265
1289
|
target: ResolvedTarget;
|
|
1266
1290
|
cwd: string;
|
|
@@ -1296,6 +1320,7 @@ interface DispatchLifecycleStage {
|
|
|
1296
1320
|
|
|
1297
1321
|
interface AcpDelegationLifecycleStage {
|
|
1298
1322
|
admission: DispatchAdmissionStage;
|
|
1323
|
+
pathScope: DispatchPathScope;
|
|
1299
1324
|
agentConfig: ReturnType<ConfigContract["get"]>["delegation"]["agents"][number];
|
|
1300
1325
|
cwd: string;
|
|
1301
1326
|
systemPrompt: string;
|
|
@@ -1480,10 +1505,6 @@ function explicitToolCapability(
|
|
|
1480
1505
|
return typeof fromKnowledgeBase === "boolean" ? fromKnowledgeBase : null;
|
|
1481
1506
|
}
|
|
1482
1507
|
|
|
1483
|
-
function writeConfinedRequest(req: DispatchRequest): boolean {
|
|
1484
|
-
return req.writeRoots !== undefined && req.writeRoots.length > 0;
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
1508
|
function effectiveToolNames(
|
|
1488
1509
|
allowedTools: ReadonlyArray<ToolName>,
|
|
1489
1510
|
target: ResolvedTarget,
|
|
@@ -1709,6 +1730,7 @@ function resolveDispatchAdmissionStage(
|
|
|
1709
1730
|
req: DispatchRequest,
|
|
1710
1731
|
recipe: AgentRecipe,
|
|
1711
1732
|
safety: SafetyContract,
|
|
1733
|
+
pathScope: DispatchPathScope,
|
|
1712
1734
|
): DispatchAdmissionStage {
|
|
1713
1735
|
const recipeTools = recipe.tools;
|
|
1714
1736
|
const candidateTools = recipeTools && recipeTools.length > 0 ? (Array.from(recipeTools) as ToolName[]) : [];
|
|
@@ -1722,7 +1744,7 @@ function resolveDispatchAdmissionStage(
|
|
|
1722
1744
|
}
|
|
1723
1745
|
const requestedActions = deriveRequestedActions(allowedTools, safety);
|
|
1724
1746
|
const orchScope = pickOrchestratorScope(safety);
|
|
1725
|
-
const workerScope = pickWorkerScope(safety, requestedActions);
|
|
1747
|
+
const workerScope = pickWorkerScope(safety, requestedActions, pathScope);
|
|
1726
1748
|
const verdict = admit(
|
|
1727
1749
|
{
|
|
1728
1750
|
requestedScope: workerScope,
|
|
@@ -1747,7 +1769,7 @@ function resolveDelegationAdmissionStage(req: DispatchRequest, safety: SafetyCon
|
|
|
1747
1769
|
const allowedTools = applyToolProfile([], req.toolProfile);
|
|
1748
1770
|
const requestedActions = deriveRequestedActions(allowedTools, safety);
|
|
1749
1771
|
const orchScope = pickOrchestratorScope(safety);
|
|
1750
|
-
const workerScope = pickWorkerScope(safety, requestedActions);
|
|
1772
|
+
const workerScope = pickWorkerScope(safety, requestedActions, resolveDispatchPathScope(req));
|
|
1751
1773
|
const verdict = admit(
|
|
1752
1774
|
{
|
|
1753
1775
|
requestedScope: workerScope,
|
|
@@ -1825,9 +1847,17 @@ function buildDispatchWorkerSpec(input: DispatchWorkerSpecInput, config?: Config
|
|
|
1825
1847
|
// The worker repairs against exactly the contract the orchestrator will seal.
|
|
1826
1848
|
// A gate role that overrides the recipe contract gets no worker-side repair,
|
|
1827
1849
|
// for the same reason it gets no recipe validation.
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1850
|
+
//
|
|
1851
|
+
// The request's own override is the contract whether or not the seated
|
|
1852
|
+
// recipe declares one, because that is the resolution the seal already uses.
|
|
1853
|
+
// Requiring a recipe contract here meant a caller-supplied override against a
|
|
1854
|
+
// recipe with none was sealed but never sent, so the worker spent no repair
|
|
1855
|
+
// round on a contract it was never told about and the run failed on a shape
|
|
1856
|
+
// nothing had asked it for.
|
|
1857
|
+
const workerResultContract = appliesRecipeResultContract(input.req.gate?.role)
|
|
1858
|
+
? (input.req.resultContractOverride ?? input.recipe?.resultContract)
|
|
1859
|
+
: undefined;
|
|
1860
|
+
if (workerResultContract) spec.resultContract = workerResultContract;
|
|
1831
1861
|
const product = input.req.product ?? input.recipe?.product;
|
|
1832
1862
|
if (product) spec.product = product;
|
|
1833
1863
|
// The orchestrator's tool decision is the one the run was admitted under and
|
|
@@ -1878,14 +1908,9 @@ function buildDispatchWorkerSpec(input: DispatchWorkerSpecInput, config?: Config
|
|
|
1878
1908
|
if (escalation) spec.escalation = { timeoutMs: escalation.timeoutMs, fallback: escalation.fallback };
|
|
1879
1909
|
}
|
|
1880
1910
|
assertRuntimeCanHonorWorkerPermissionMode(input.target.runtime, spec.onPermission);
|
|
1881
|
-
// Carry write
|
|
1882
|
-
//
|
|
1883
|
-
|
|
1884
|
-
// reaches the worker absolute, matching the validation contract.
|
|
1885
|
-
if (input.req.writeRoots !== undefined && input.req.writeRoots.length > 0) {
|
|
1886
|
-
const jobCwd = input.req.cwd !== undefined && input.req.cwd.length > 0 ? input.req.cwd : process.cwd();
|
|
1887
|
-
spec.writeRoots = input.req.writeRoots.map((root) => resolvePath(jobCwd, root));
|
|
1888
|
-
}
|
|
1911
|
+
// Carry canonical write boundaries to the worker safety seam. Exact files
|
|
1912
|
+
// remain exact and trailing-slash entries remain subtrees after resolution.
|
|
1913
|
+
if (input.pathScope.writeBoundaries.length > 0) spec.writeRoots = [...input.pathScope.writeBoundaries];
|
|
1889
1914
|
assertWriteRootsEnforceable(input.target.runtime, spec.writeRoots);
|
|
1890
1915
|
// Carry the tool profile so external CLI runtimes that cannot mediate
|
|
1891
1916
|
// per-tool calls can refuse a narrowing profile they would otherwise ignore.
|
|
@@ -2380,6 +2405,26 @@ export function createDispatchBundle(
|
|
|
2380
2405
|
const active = new Map<string, ActiveRun>();
|
|
2381
2406
|
const targetCooldowns = new Map<string, { until: number; reason: string }>();
|
|
2382
2407
|
const ownedReservations = new Set<string>();
|
|
2408
|
+
/**
|
|
2409
|
+
* Paths each finalized run's own tool calls aimed a successful mutation at,
|
|
2410
|
+
* newest last. `null` records a run whose write record is unusable, which is
|
|
2411
|
+
* a different answer from an empty list and has to survive as one.
|
|
2412
|
+
*
|
|
2413
|
+
* Held in memory rather than sealed into the receipt: the only reader is the
|
|
2414
|
+
* write boundary closing a window seconds later in this process, and a
|
|
2415
|
+
* per-run path list is run-shaped evidence the receipt schema does not carry.
|
|
2416
|
+
* Bounded because a long session finalizes many runs and none of this is
|
|
2417
|
+
* needed once its window has closed.
|
|
2418
|
+
*/
|
|
2419
|
+
const runWriteTargets = new Map<string, ReadonlyArray<string> | null>();
|
|
2420
|
+
const recordRunWriteTargets = (runId: string, paths: ReadonlyArray<string> | null): void => {
|
|
2421
|
+
runWriteTargets.set(runId, paths);
|
|
2422
|
+
while (runWriteTargets.size > RUN_WRITE_TARGET_HISTORY) {
|
|
2423
|
+
const oldest = runWriteTargets.keys().next();
|
|
2424
|
+
if (oldest.done === true) break;
|
|
2425
|
+
runWriteTargets.delete(oldest.value);
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2383
2428
|
const capacityAdmission = createCapacityAdmissionController({
|
|
2384
2429
|
limits: () => {
|
|
2385
2430
|
const settings = getEffectiveSettings();
|
|
@@ -3161,6 +3206,27 @@ export function createDispatchBundle(
|
|
|
3161
3206
|
targetCooldowns.set(key, { until: now() + cooldownMs, reason: failureClass });
|
|
3162
3207
|
}
|
|
3163
3208
|
|
|
3209
|
+
function publishDispatchPathScope(req: DispatchRequest, pathScope: DispatchPathScope): void {
|
|
3210
|
+
const replacementDiagnostic = declaredScopeReplacementDiagnostic(pathScope);
|
|
3211
|
+
if (replacementDiagnostic !== null) {
|
|
3212
|
+
reportDispatchDiagnostic("typed scope replacement", new Error(replacementDiagnostic));
|
|
3213
|
+
}
|
|
3214
|
+
// Only the replacement case reaches the transcript. A dispatch that
|
|
3215
|
+
// declared no intent is the ordinary case rather than an anomaly, and one
|
|
3216
|
+
// receipt in ninety-nine carries an intent key today, so noticing it would
|
|
3217
|
+
// warn on almost every dispatch and teach the operator to skip the channel.
|
|
3218
|
+
// Its provenance is not lost: the receipt seals `pathProvenance`, and the
|
|
3219
|
+
// approval artifact renders every inferred entry in full before a
|
|
3220
|
+
// supervised dispatch runs, which is where an operator can still act on it.
|
|
3221
|
+
const notice = declaredScopeReplacementNotice(pathScope);
|
|
3222
|
+
if (notice !== null) {
|
|
3223
|
+
context.bus.emit(BusChannels.DispatchScopeNotice, {
|
|
3224
|
+
...notice,
|
|
3225
|
+
agentId: req.agentId,
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3164
3230
|
async function resolveLifecycle(req: DispatchRequest, settings: EffectiveSettings): Promise<DispatchLifecycleStage> {
|
|
3165
3231
|
const recipe = agents.get(req.agentId);
|
|
3166
3232
|
if (!recipe) {
|
|
@@ -3172,7 +3238,7 @@ export function createDispatchBundle(
|
|
|
3172
3238
|
`dispatch: agent '${req.agentId}' is a ${spec.audience} agent reserved for Clio internal orchestration`,
|
|
3173
3239
|
);
|
|
3174
3240
|
}
|
|
3175
|
-
if (
|
|
3241
|
+
if (hasCallerPersonaOverride(req) && (spec.audience === "shadow" || spec.audience === "internal")) {
|
|
3176
3242
|
throw new Error(`dispatch: persona overrides are not allowed for ${spec.audience} agent '${req.agentId}'`);
|
|
3177
3243
|
}
|
|
3178
3244
|
// A read-only recipe pointed at a mutating task is knowable here and costs
|
|
@@ -3194,7 +3260,9 @@ export function createDispatchBundle(
|
|
|
3194
3260
|
specs: agents.listSpecs(),
|
|
3195
3261
|
});
|
|
3196
3262
|
if (capabilityMismatch?.verdict === "refuse") throw new Error(capabilityMismatch.detail);
|
|
3197
|
-
const
|
|
3263
|
+
const pathScope = resolveDispatchPathScope(req);
|
|
3264
|
+
publishDispatchPathScope(req, pathScope);
|
|
3265
|
+
const admission = resolveDispatchAdmissionStage(req, recipe, safety, pathScope);
|
|
3198
3266
|
const targets = readWorkerTargets(settings);
|
|
3199
3267
|
const target = resolveDispatchTarget(
|
|
3200
3268
|
req,
|
|
@@ -3213,7 +3281,7 @@ export function createDispatchBundle(
|
|
|
3213
3281
|
const sessionAutonomy = settings?.autonomy ?? "auto-edit";
|
|
3214
3282
|
const effectiveAutonomy = clampWorkerAutonomy(sessionAutonomy, req.autonomy);
|
|
3215
3283
|
const effectiveTools = withLedgerToolNarrowing(
|
|
3216
|
-
effectiveToolNames(admission.allowedTools, target,
|
|
3284
|
+
effectiveToolNames(admission.allowedTools, target, pathScope.writeBoundaries.length > 0, deniedToolNames(req)),
|
|
3217
3285
|
req,
|
|
3218
3286
|
);
|
|
3219
3287
|
assertPostRuntimeToolCompatibility(req.agentId, spec, effectiveTools, target);
|
|
@@ -3242,7 +3310,7 @@ export function createDispatchBundle(
|
|
|
3242
3310
|
dynamic: false,
|
|
3243
3311
|
},
|
|
3244
3312
|
cwd,
|
|
3245
|
-
workingContextPaths:
|
|
3313
|
+
workingContextPaths: pathScope.workingContextPaths,
|
|
3246
3314
|
});
|
|
3247
3315
|
const systemPrompt = compiledWorkerPrompt.systemPrompt;
|
|
3248
3316
|
const budgetEnvelope = resolveEffectiveWorkerBudget({
|
|
@@ -3286,6 +3354,7 @@ export function createDispatchBundle(
|
|
|
3286
3354
|
const limitations = runtimeLimitations(runtimeKind, target.runtime.id);
|
|
3287
3355
|
return {
|
|
3288
3356
|
recipe,
|
|
3357
|
+
pathScope,
|
|
3289
3358
|
admission: effectiveAdmission,
|
|
3290
3359
|
target,
|
|
3291
3360
|
cwd,
|
|
@@ -3323,12 +3392,7 @@ export function createDispatchBundle(
|
|
|
3323
3392
|
): AcpDelegationLifecycleStage {
|
|
3324
3393
|
const agentId = req.delegationAgentId;
|
|
3325
3394
|
if (!agentId) throw new Error("dispatch: missing delegationAgentId");
|
|
3326
|
-
|
|
3327
|
-
role: req.gate?.role,
|
|
3328
|
-
autonomy: req.autonomy,
|
|
3329
|
-
systemPrompt: req.systemPrompt,
|
|
3330
|
-
});
|
|
3331
|
-
if (hasPersonaOverride(req) && !boundedGateRolePrompt) {
|
|
3395
|
+
if (hasCallerPersonaOverride(req)) {
|
|
3332
3396
|
throw new Error(`dispatch: persona overrides are not allowed for ACP delegation agent '${agentId}'`);
|
|
3333
3397
|
}
|
|
3334
3398
|
if (req.agentId && maybeAgents) {
|
|
@@ -3357,6 +3421,7 @@ export function createDispatchBundle(
|
|
|
3357
3421
|
);
|
|
3358
3422
|
}
|
|
3359
3423
|
const cwd = req.cwd ?? process.cwd();
|
|
3424
|
+
const pathScope = resolveDispatchPathScope(req);
|
|
3360
3425
|
const personaBody = workerPersonaBody(req, null, []);
|
|
3361
3426
|
// ACP owns an unknown external tool inventory, so it receives the raw
|
|
3362
3427
|
// bounded persona rather than a native Clio schema-harness claim.
|
|
@@ -3383,6 +3448,7 @@ export function createDispatchBundle(
|
|
|
3383
3448
|
const personaOverride = personaOverrideFor(req, staticCompositionHash);
|
|
3384
3449
|
return {
|
|
3385
3450
|
admission,
|
|
3451
|
+
pathScope,
|
|
3386
3452
|
agentConfig: configured,
|
|
3387
3453
|
cwd,
|
|
3388
3454
|
systemPrompt,
|
|
@@ -3418,6 +3484,7 @@ export function createDispatchBundle(
|
|
|
3418
3484
|
finalPromise: Promise<RunReceipt>;
|
|
3419
3485
|
}> {
|
|
3420
3486
|
const lifecycle = resolveAcpDelegationLifecycle(req, settings);
|
|
3487
|
+
publishDispatchPathScope(req, lifecycle.pathScope);
|
|
3421
3488
|
timing.decisionCompletedAt = new Date(now()).toISOString();
|
|
3422
3489
|
const targetId = `delegation:${lifecycle.agentConfig.id}`;
|
|
3423
3490
|
const runtimeId = "acp";
|
|
@@ -3610,10 +3677,12 @@ export function createDispatchBundle(
|
|
|
3610
3677
|
let identity!: ReturnType<typeof detectRunIdentity>;
|
|
3611
3678
|
try {
|
|
3612
3679
|
envelope = ledgerRef.create({
|
|
3680
|
+
...(req.runIdHint !== undefined ? { id: req.runIdHint } : {}),
|
|
3613
3681
|
agentId: req.agentId,
|
|
3614
3682
|
executionRole: withAttemptRole(req.executionRole, req.lineage?.attempt ?? 0),
|
|
3615
3683
|
requestOrigin: lifecycle.requestOrigin,
|
|
3616
3684
|
task: req.task,
|
|
3685
|
+
...(req.intent !== undefined ? { intent: structuredClone(req.intent) } : {}),
|
|
3617
3686
|
targetId,
|
|
3618
3687
|
wireModelId,
|
|
3619
3688
|
runtimeId,
|
|
@@ -3645,6 +3714,7 @@ export function createDispatchBundle(
|
|
|
3645
3714
|
...(lifecycle.pipeline ? { pipeline: lifecycle.pipeline } : {}),
|
|
3646
3715
|
...(lifecycle.briefing ? { briefing: lifecycle.briefing } : {}),
|
|
3647
3716
|
...(req.gate !== undefined ? { gate: req.gate } : {}),
|
|
3717
|
+
...(req.council !== undefined ? { council: req.council } : {}),
|
|
3648
3718
|
...(req.plan !== undefined ? { plan: req.plan } : {}),
|
|
3649
3719
|
...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
|
|
3650
3720
|
});
|
|
@@ -3787,6 +3857,8 @@ export function createDispatchBundle(
|
|
|
3787
3857
|
executionRole: envelope.executionRole,
|
|
3788
3858
|
requestOrigin: lifecycle.requestOrigin,
|
|
3789
3859
|
task: req.task,
|
|
3860
|
+
...(req.intent !== undefined ? { intent: structuredClone(req.intent) } : {}),
|
|
3861
|
+
pathScope: structuredClone(lifecycle.pathScope.provenance),
|
|
3790
3862
|
targetId,
|
|
3791
3863
|
wireModelId,
|
|
3792
3864
|
runtimeId,
|
|
@@ -3801,6 +3873,7 @@ export function createDispatchBundle(
|
|
|
3801
3873
|
...(lifecycle.pipeline ? { pipeline: lifecycle.pipeline } : {}),
|
|
3802
3874
|
...(lifecycle.briefing ? { briefing: lifecycle.briefing } : {}),
|
|
3803
3875
|
...(req.gate !== undefined ? { gate: req.gate } : {}),
|
|
3876
|
+
...(req.council !== undefined ? { council: req.council } : {}),
|
|
3804
3877
|
...(req.plan !== undefined ? { plan: req.plan } : {}),
|
|
3805
3878
|
...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
|
|
3806
3879
|
projectContext: lifecycle.projectContext,
|
|
@@ -3913,6 +3986,7 @@ export function createDispatchBundle(
|
|
|
3913
3986
|
durationMs,
|
|
3914
3987
|
exitCode: receipt.exitCode,
|
|
3915
3988
|
toolActivity: receipt.toolActivity ?? null,
|
|
3989
|
+
...(receipt.hostVerification !== undefined ? { hostVerification: receipt.hostVerification.status } : {}),
|
|
3916
3990
|
...(receipt.skillActivations && receipt.skillActivations.length > 0
|
|
3917
3991
|
? { skillActivations: [...receipt.skillActivations] }
|
|
3918
3992
|
: {}),
|
|
@@ -4262,6 +4336,7 @@ export function createDispatchBundle(
|
|
|
4262
4336
|
const spec = buildDispatchWorkerSpec(
|
|
4263
4337
|
{
|
|
4264
4338
|
req,
|
|
4339
|
+
pathScope: lifecycle.pathScope,
|
|
4265
4340
|
target: lifecycle.target,
|
|
4266
4341
|
admission: lifecycle.admission,
|
|
4267
4342
|
recipe: lifecycle.recipe,
|
|
@@ -4444,7 +4519,8 @@ export function createDispatchBundle(
|
|
|
4444
4519
|
if (
|
|
4445
4520
|
event.type === "clio_run_outcome" &&
|
|
4446
4521
|
isRunOutcomeCode(event.payload?.outcomeCode) &&
|
|
4447
|
-
event.payload.outcomeCode !== "worker_final_output_missing"
|
|
4522
|
+
event.payload.outcomeCode !== "worker_final_output_missing" &&
|
|
4523
|
+
event.payload.outcomeCode !== "host_verification_rejected"
|
|
4448
4524
|
) {
|
|
4449
4525
|
if (acceptsOutcomeCodeEvents) {
|
|
4450
4526
|
trustedOutcomeCodes.add(event.payload.outcomeCode);
|
|
@@ -4604,11 +4680,13 @@ export function createDispatchBundle(
|
|
|
4604
4680
|
let identity!: ReturnType<typeof detectRunIdentity>;
|
|
4605
4681
|
try {
|
|
4606
4682
|
envelope = ledgerRef.create({
|
|
4683
|
+
...(req.runIdHint !== undefined ? { id: req.runIdHint } : {}),
|
|
4607
4684
|
agentId: req.agentId,
|
|
4608
4685
|
executionRole: withAttemptRole(req.executionRole, req.lineage?.attempt ?? 0),
|
|
4609
4686
|
agentAudience: lifecycle.agentAudience,
|
|
4610
4687
|
requestOrigin: lifecycle.requestOrigin,
|
|
4611
4688
|
task: req.task,
|
|
4689
|
+
...(req.intent !== undefined ? { intent: structuredClone(req.intent) } : {}),
|
|
4612
4690
|
budget: lifecycle.budgetEnvelope,
|
|
4613
4691
|
targetId: lifecycle.target.target.id,
|
|
4614
4692
|
wireModelId: lifecycle.target.wireModelId,
|
|
@@ -4658,6 +4736,7 @@ export function createDispatchBundle(
|
|
|
4658
4736
|
...(lifecycle.pipeline ? { pipeline: lifecycle.pipeline } : {}),
|
|
4659
4737
|
...(lifecycle.briefing ? { briefing: lifecycle.briefing } : {}),
|
|
4660
4738
|
...(req.gate !== undefined ? { gate: req.gate } : {}),
|
|
4739
|
+
...(req.council !== undefined ? { council: req.council } : {}),
|
|
4661
4740
|
...(req.plan !== undefined ? { plan: req.plan } : {}),
|
|
4662
4741
|
...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
|
|
4663
4742
|
...(heartbeatAt ? { heartbeatAt: heartbeatIso(heartbeatAt.current) } : {}),
|
|
@@ -4678,6 +4757,7 @@ export function createDispatchBundle(
|
|
|
4678
4757
|
budget: lifecycle.budgetEnvelope,
|
|
4679
4758
|
...(placement !== undefined && placement !== null ? { node: placement.node.id } : {}),
|
|
4680
4759
|
...(req.gate !== undefined ? { gate: { role: req.gate.role, cycle: req.gate.cycle } } : {}),
|
|
4760
|
+
...(req.council !== undefined ? { council: req.council } : {}),
|
|
4681
4761
|
...(req.reroutes !== undefined && req.reroutes.length > 0 ? { rerouteCount: req.reroutes.length } : {}),
|
|
4682
4762
|
...(lifecycle.target.modelCapabilities !== undefined &&
|
|
4683
4763
|
lifecycle.target.modelCapabilities !== null &&
|
|
@@ -4829,6 +4909,15 @@ export function createDispatchBundle(
|
|
|
4829
4909
|
const tokenCount =
|
|
4830
4910
|
tokenMeter.inputTokens + tokenMeter.outputTokens + tokenMeter.cacheReadTokens + tokenMeter.cacheWriteTokens;
|
|
4831
4911
|
const protectedArtifacts = protectedArtifactReceiptSummary(spec.protectedArtifactState);
|
|
4912
|
+
const fleetGate = (() => {
|
|
4913
|
+
if (req.fleetGateReceipt === undefined) return null;
|
|
4914
|
+
try {
|
|
4915
|
+
const content = readFileSync(resolvePath(lifecycle.cwd, req.fleetGateReceipt.path));
|
|
4916
|
+
return { path: req.fleetGateReceipt.path, pathHash: createHash("sha256").update(content).digest("hex") };
|
|
4917
|
+
} catch {
|
|
4918
|
+
return { path: req.fleetGateReceipt.path, pathHash: createHash("sha256").update("").digest("hex") };
|
|
4919
|
+
}
|
|
4920
|
+
})();
|
|
4832
4921
|
// Sealed from the stored board, never from anything the worker said
|
|
4833
4922
|
// about itself. Absent when the run had no ledger.
|
|
4834
4923
|
const ledgerContribution = (() => {
|
|
@@ -4864,6 +4953,8 @@ export function createDispatchBundle(
|
|
|
4864
4953
|
outcome,
|
|
4865
4954
|
outcomeCode,
|
|
4866
4955
|
lineage,
|
|
4956
|
+
...(req.intent !== undefined ? { intent: structuredClone(req.intent) } : {}),
|
|
4957
|
+
pathScope: structuredClone(lifecycle.pathScope.provenance),
|
|
4867
4958
|
identity,
|
|
4868
4959
|
node: placement?.node ?? LOCAL_RUN_NODE,
|
|
4869
4960
|
...receiptAttestationFields(worker.attestation?.() ?? null),
|
|
@@ -4874,7 +4965,9 @@ export function createDispatchBundle(
|
|
|
4874
4965
|
...(lifecycle.briefing ? { briefing: lifecycle.briefing } : {}),
|
|
4875
4966
|
...(steering.length > 0 ? { steering: steering.map((entry) => ({ ...entry })) } : {}),
|
|
4876
4967
|
...(req.gate !== undefined ? { gate: req.gate } : {}),
|
|
4968
|
+
...(req.council !== undefined ? { council: req.council } : {}),
|
|
4877
4969
|
...(req.plan !== undefined ? { plan: req.plan } : {}),
|
|
4970
|
+
...(fleetGate !== null ? { fleetGate } : {}),
|
|
4878
4971
|
...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
|
|
4879
4972
|
projectContext: lifecycle.projectContext,
|
|
4880
4973
|
rulesApplied: lifecycle.rulesApplied,
|
|
@@ -5004,6 +5097,7 @@ export function createDispatchBundle(
|
|
|
5004
5097
|
durationMs,
|
|
5005
5098
|
exitCode: receipt.exitCode,
|
|
5006
5099
|
toolActivity: receipt.toolActivity ?? null,
|
|
5100
|
+
...(receipt.hostVerification !== undefined ? { hostVerification: receipt.hostVerification.status } : {}),
|
|
5007
5101
|
...(receipt.skillActivations && receipt.skillActivations.length > 0
|
|
5008
5102
|
? { skillActivations: [...receipt.skillActivations] }
|
|
5009
5103
|
: {}),
|
|
@@ -5102,7 +5196,7 @@ export function createDispatchBundle(
|
|
|
5102
5196
|
const capturedOutput = outputCapture.snapshot();
|
|
5103
5197
|
const observedRunEffects = runEffects.snapshot();
|
|
5104
5198
|
const appliedResultContract = appliesRecipeResultContract(req.gate?.role)
|
|
5105
|
-
? (lifecycle.recipe?.resultContract ?? null)
|
|
5199
|
+
? (req.resultContractOverride ?? lifecycle.recipe?.resultContract ?? null)
|
|
5106
5200
|
: null;
|
|
5107
5201
|
const resultContract = validateRecipeResult({
|
|
5108
5202
|
contract: appliedResultContract,
|
|
@@ -5166,13 +5260,40 @@ export function createDispatchBundle(
|
|
|
5166
5260
|
finalDetail = [finalDetail, describeUngroundedValidations(validationGrounding)].filter(Boolean).join("; ");
|
|
5167
5261
|
}
|
|
5168
5262
|
const hasTerminalArtifact =
|
|
5169
|
-
lifecycle.recipe?.resultContract
|
|
5263
|
+
(req.resultContractOverride ?? lifecycle.recipe?.resultContract)?.kind === "architect-plan" &&
|
|
5264
|
+
resultValidation?.conformance === "pass";
|
|
5170
5265
|
if (finalOutcome === "succeeded" && !hasDurableFinalOutput(capturedOutput) && !hasTerminalArtifact) {
|
|
5171
5266
|
finalOutcome = "failed";
|
|
5172
5267
|
outcomeCode = "worker_final_output_missing";
|
|
5173
5268
|
finalDetail = WORKER_FINAL_OUTPUT_MISSING_DETAIL;
|
|
5174
5269
|
failureMessage = finalDetail;
|
|
5175
5270
|
}
|
|
5271
|
+
const hostVerification = await runHostVerification({
|
|
5272
|
+
runId: envelope.id,
|
|
5273
|
+
request: req,
|
|
5274
|
+
workerSuccessful: finalOutcome === "succeeded",
|
|
5275
|
+
onDiagnostic: (error) => reportDispatchDiagnostic(`write host verification memo for ${envelope.id}`, error),
|
|
5276
|
+
});
|
|
5277
|
+
const hostRejection = hostVerificationRejection(hostVerification);
|
|
5278
|
+
if (hostRejection !== null && finalOutcome === "succeeded") {
|
|
5279
|
+
finalOutcome = "failed";
|
|
5280
|
+
outcomeCode = hostRejection.outcomeCode;
|
|
5281
|
+
finalDetail = hostRejection.detail;
|
|
5282
|
+
failureMessage = finalDetail;
|
|
5283
|
+
}
|
|
5284
|
+
let worktreeReceipt: RunReceiptDraft["worktree"];
|
|
5285
|
+
if (req.taskWorktree !== undefined && finalOutcome === "succeeded") {
|
|
5286
|
+
worktreeReceipt = applyTaskWorktree({
|
|
5287
|
+
worktree: req.taskWorktree,
|
|
5288
|
+
apply: req.apply ?? "merge",
|
|
5289
|
+
protectedPaths: getProtectedArtifactState().artifacts.map((artifact) => artifact.path),
|
|
5290
|
+
});
|
|
5291
|
+
if (worktreeReceipt.reason !== undefined) {
|
|
5292
|
+
finalOutcome = "failed";
|
|
5293
|
+
finalDetail = worktreeReceipt.reason;
|
|
5294
|
+
failureMessage = finalDetail;
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5176
5297
|
const status = runStatusForOutcome(finalOutcome);
|
|
5177
5298
|
const failureClass = classifyFailure(evidence, result, finalOutcome, outcomeCode);
|
|
5178
5299
|
const receiptDraft = buildReceiptDraft(
|
|
@@ -5186,6 +5307,8 @@ export function createDispatchBundle(
|
|
|
5186
5307
|
sealedResultContractFact,
|
|
5187
5308
|
validationGrounding === null ? null : { ...validationGrounding, ungrounded: [...validationGrounding.ungrounded] },
|
|
5188
5309
|
);
|
|
5310
|
+
if (hostVerification !== undefined) receiptDraft.hostVerification = hostVerification;
|
|
5311
|
+
if (worktreeReceipt !== undefined) receiptDraft.worktree = worktreeReceipt;
|
|
5189
5312
|
const ledgerPatch: Partial<RunEnvelope> = {
|
|
5190
5313
|
status,
|
|
5191
5314
|
outcome: finalOutcome,
|
|
@@ -5215,7 +5338,33 @@ export function createDispatchBundle(
|
|
|
5215
5338
|
}
|
|
5216
5339
|
ledgerRef.update(envelope.id, ledgerPatch);
|
|
5217
5340
|
const receipt = ledgerRef.recordReceipt(envelope.id, sealRouteDecision(receiptDraft, routeObservation.decision));
|
|
5341
|
+
// Two independent ways a run's write record can fail to be a closed
|
|
5342
|
+
// list, and either one files it as `null` rather than as an empty
|
|
5343
|
+
// one. The difference decides whether the write boundary may clear a
|
|
5344
|
+
// path it did not see this run touch.
|
|
5345
|
+
//
|
|
5346
|
+
// The telemetry axis is whether the events were seen at all: a
|
|
5347
|
+
// subprocess runtime publishes no tool events, and a partial trail
|
|
5348
|
+
// has its holes exactly where an unobserved write would sit.
|
|
5349
|
+
//
|
|
5350
|
+
// The opacity axis is whether the events that were seen say enough.
|
|
5351
|
+
// A run that called bash, verify, dispatch, or steer to a clean exit
|
|
5352
|
+
// could have written through a channel no argument names, so nothing
|
|
5353
|
+
// in the checkout can be cleared on the strength of its absence from
|
|
5354
|
+
// the set. That run's window keeps today's blame-the-whole-diff
|
|
5355
|
+
// behaviour; a run that never touched such a tool keeps the
|
|
5356
|
+
// protection.
|
|
5357
|
+
const writeRecordUsable =
|
|
5358
|
+
receipt.safety?.toolTelemetry?.coverage === "complete" && observedRunEffects.writeRecordComplete;
|
|
5359
|
+
recordRunWriteTargets(envelope.id, writeRecordUsable ? [...observedRunEffects.mutatedPaths] : null);
|
|
5218
5360
|
await ledgerRef.persist();
|
|
5361
|
+
if (worktreeReceipt?.applied === true && req.taskWorktree !== undefined) {
|
|
5362
|
+
try {
|
|
5363
|
+
cleanupTaskWorktree(req.taskWorktree, true);
|
|
5364
|
+
} catch (cleanupError) {
|
|
5365
|
+
reportDispatchDiagnostic(`clean applied task worktree ${req.taskWorktree.runId}`, cleanupError);
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5219
5368
|
active.delete(envelope.id);
|
|
5220
5369
|
recordTargetOutcome(
|
|
5221
5370
|
lifecycle.target.target.id,
|
|
@@ -5303,10 +5452,58 @@ export function createDispatchBundle(
|
|
|
5303
5452
|
events: AsyncIterableIterator<unknown>;
|
|
5304
5453
|
finalPromise: Promise<RunReceipt>;
|
|
5305
5454
|
}> {
|
|
5455
|
+
let prepared = req;
|
|
5456
|
+
let createdWorktree: DispatchRequest["taskWorktree"];
|
|
5457
|
+
let writerLease: CheckoutWriterLease | undefined;
|
|
5458
|
+
const writerRecipe = agents.get(req.agentId);
|
|
5459
|
+
if (writerRecipe !== null && normalizeAgentSpec(writerRecipe).capabilityClass === "workspace-edit") {
|
|
5460
|
+
const checkout = req.taskWorktree?.root ?? gitCheckoutRoot(req.cwd ?? process.cwd());
|
|
5461
|
+
if (checkout !== null) writerLease = acquireCheckoutWriterLease({ checkout });
|
|
5462
|
+
}
|
|
5463
|
+
if (req.worktree === true && req.taskWorktree === undefined) {
|
|
5464
|
+
const requestedCwd = resolvePath(req.cwd ?? process.cwd());
|
|
5465
|
+
const root = gitCheckoutRoot(requestedCwd);
|
|
5466
|
+
if (root === null) throw new Error("worktree_non_git_checkout");
|
|
5467
|
+
if (req.taskWorktreeDestination !== undefined && root !== req.taskWorktreeDestination) {
|
|
5468
|
+
writerLease?.release();
|
|
5469
|
+
throw new Error("dispatch: worktree merge destination differs from the approved execution snapshot");
|
|
5470
|
+
}
|
|
5471
|
+
const runId = newRunId();
|
|
5472
|
+
let taskWorktree: NonNullable<DispatchRequest["taskWorktree"]>;
|
|
5473
|
+
try {
|
|
5474
|
+
taskWorktree = createTaskWorktree(root, runId);
|
|
5475
|
+
} catch (error) {
|
|
5476
|
+
writerLease?.release();
|
|
5477
|
+
throw error;
|
|
5478
|
+
}
|
|
5479
|
+
createdWorktree = taskWorktree;
|
|
5480
|
+
const cwdRelative = relative(root, requestedCwd);
|
|
5481
|
+
const workerCwd = resolvePath(taskWorktree.path, cwdRelative);
|
|
5482
|
+
const writeRoots = req.writeRoots?.map((entry) => {
|
|
5483
|
+
const rel = relative(root, entry);
|
|
5484
|
+
return rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel) ? entry : resolvePath(taskWorktree.path, rel);
|
|
5485
|
+
});
|
|
5486
|
+
prepared = {
|
|
5487
|
+
...req,
|
|
5488
|
+
runIdHint: runId,
|
|
5489
|
+
taskWorktree,
|
|
5490
|
+
cwd: workerCwd,
|
|
5491
|
+
protectedArtifactRemap: { sourceRoot: root, workerRoot: taskWorktree.path },
|
|
5492
|
+
...(writeRoots === undefined ? {} : { writeRoots }),
|
|
5493
|
+
};
|
|
5494
|
+
}
|
|
5306
5495
|
let handle: Awaited<ReturnType<typeof dispatchAttempt>>;
|
|
5307
5496
|
try {
|
|
5308
|
-
handle = await dispatchAttempt(
|
|
5497
|
+
handle = await dispatchAttempt(prepared, observer);
|
|
5309
5498
|
} catch (error) {
|
|
5499
|
+
writerLease?.release();
|
|
5500
|
+
if (createdWorktree !== undefined) {
|
|
5501
|
+
try {
|
|
5502
|
+
cleanupTaskWorktree(createdWorktree, true);
|
|
5503
|
+
} catch (cleanupError) {
|
|
5504
|
+
reportDispatchDiagnostic(`clean task worktree ${createdWorktree.runId} after admission failure`, cleanupError);
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5310
5507
|
if (req.reservation !== undefined && req.lineage === undefined) {
|
|
5311
5508
|
rollbackDispatchReservation(req.reservation.ownerId, now());
|
|
5312
5509
|
}
|
|
@@ -5314,7 +5511,7 @@ export function createDispatchBundle(
|
|
|
5314
5511
|
}
|
|
5315
5512
|
// Requests carrying lineage are internal attempts (retry or nested
|
|
5316
5513
|
// orchestration) and retain the per-attempt handle contract.
|
|
5317
|
-
if (req.lineage) return handle;
|
|
5514
|
+
if (req.lineage) return { ...handle, finalPromise: handle.finalPromise.finally(() => writerLease?.release()) };
|
|
5318
5515
|
|
|
5319
5516
|
const assignment = assignments.open(handle.runId, assignmentPolicyFor(handle.effectiveRequest));
|
|
5320
5517
|
persistAssignment(registerAssignment(assignment.id), `${assignment.id}:open`);
|
|
@@ -5327,9 +5524,10 @@ export function createDispatchBundle(
|
|
|
5327
5524
|
const reservation = req.reservation;
|
|
5328
5525
|
const terminal =
|
|
5329
5526
|
reservation === undefined
|
|
5330
|
-
? assignment.terminal
|
|
5527
|
+
? assignment.terminal.finally(() => writerLease?.release())
|
|
5331
5528
|
: assignment.terminal.finally(() => {
|
|
5332
5529
|
releaseDispatchReservationMember(reservation.ownerId, reservation.memberId, now());
|
|
5530
|
+
writerLease?.release();
|
|
5333
5531
|
});
|
|
5334
5532
|
return { runId: handle.runId, events: assignment.events, finalPromise: terminal };
|
|
5335
5533
|
}
|
|
@@ -5393,10 +5591,11 @@ export function createDispatchBundle(
|
|
|
5393
5591
|
`dispatch: agent '${req.agentId}' is a ${agentSpec.audience} agent reserved for Clio internal orchestration`,
|
|
5394
5592
|
);
|
|
5395
5593
|
}
|
|
5396
|
-
if (
|
|
5594
|
+
if (hasCallerPersonaOverride(req) && (agentSpec.audience === "shadow" || agentSpec.audience === "internal")) {
|
|
5397
5595
|
throw new Error(`dispatch: persona overrides are not allowed for ${agentSpec.audience} agent '${req.agentId}'`);
|
|
5398
5596
|
}
|
|
5399
|
-
const
|
|
5597
|
+
const pathScope = resolveDispatchPathScope(req);
|
|
5598
|
+
const admission = resolveDispatchAdmissionStage(req, recipe, safety, pathScope);
|
|
5400
5599
|
const targets = readWorkerTargets(settings);
|
|
5401
5600
|
const target = resolveDispatchTarget(
|
|
5402
5601
|
req,
|
|
@@ -5409,14 +5608,14 @@ export function createDispatchBundle(
|
|
|
5409
5608
|
);
|
|
5410
5609
|
enforceCapabilityGate(target.target.id, target.modelCapabilities, req.requiredCapabilities);
|
|
5411
5610
|
const effectiveTools = withLedgerToolNarrowing(
|
|
5412
|
-
effectiveToolNames(admission.allowedTools, target,
|
|
5611
|
+
effectiveToolNames(admission.allowedTools, target, pathScope.writeBoundaries.length > 0, deniedToolNames(req)),
|
|
5413
5612
|
req,
|
|
5414
5613
|
);
|
|
5415
5614
|
assertPostRuntimeToolCompatibility(req.agentId, agentSpec, effectiveTools, target);
|
|
5416
5615
|
assertRuntimeCanHonorWorkerPermissionMode(target.runtime, settings?.workers.onPermission ?? "deny");
|
|
5417
5616
|
assertWorkerBudgetEnforceable(target.runtime, agentSpec.budget !== null || req.budget !== undefined);
|
|
5418
5617
|
assertResponseSchemaEnforceable(target.runtime, target.modelCapabilities, req.responseSchema, effectiveTools.length);
|
|
5419
|
-
assertWriteRootsEnforceable(target.runtime,
|
|
5618
|
+
assertWriteRootsEnforceable(target.runtime, pathScope.writeBoundaries);
|
|
5420
5619
|
assertProtectedArtifactsEnforceable(
|
|
5421
5620
|
target.runtime.id,
|
|
5422
5621
|
target.runtime.kind !== "subprocess",
|
|
@@ -5713,6 +5912,137 @@ export function createDispatchBundle(
|
|
|
5713
5912
|
};
|
|
5714
5913
|
}
|
|
5715
5914
|
|
|
5915
|
+
async function sealCouncilSynthesis(input: {
|
|
5916
|
+
group: string;
|
|
5917
|
+
round: number;
|
|
5918
|
+
kind: "none" | "vote" | "judge";
|
|
5919
|
+
text: string;
|
|
5920
|
+
subjects: ReadonlyArray<{ runId: string; digest: string | null }>;
|
|
5921
|
+
template: RunReceipt;
|
|
5922
|
+
}): Promise<RunReceipt> {
|
|
5923
|
+
const l = requireLedger();
|
|
5924
|
+
const runId = newRunId();
|
|
5925
|
+
const task = `Council ${input.kind} synthesis`;
|
|
5926
|
+
// The synthesis is the council's own verdict, filed under the origin the
|
|
5927
|
+
// council was asked from: an internal origin would keep it out of the
|
|
5928
|
+
// transcript worker fold, and with it out of /share.
|
|
5929
|
+
const origin = input.template.requestOrigin ?? "agent";
|
|
5930
|
+
const gate = { role: "synthesis" as const, group: input.group, cycle: input.round, subjects: [...input.subjects] };
|
|
5931
|
+
const council = { group: input.group, label: "synthesis", round: input.round };
|
|
5932
|
+
const created = l.create({
|
|
5933
|
+
id: runId,
|
|
5934
|
+
agentId: "council-synthesis",
|
|
5935
|
+
executionRole: "judge",
|
|
5936
|
+
requestOrigin: origin,
|
|
5937
|
+
task,
|
|
5938
|
+
targetId: input.template.targetId,
|
|
5939
|
+
wireModelId: input.template.wireModelId,
|
|
5940
|
+
runtimeId: input.template.runtimeId,
|
|
5941
|
+
runtimeKind: input.template.runtimeKind,
|
|
5942
|
+
sessionId: input.template.sessionId,
|
|
5943
|
+
cwd: input.template.reproducibility?.cwd ?? process.cwd(),
|
|
5944
|
+
});
|
|
5945
|
+
const endedAt = new Date().toISOString();
|
|
5946
|
+
l.update(runId, {
|
|
5947
|
+
status: "completed",
|
|
5948
|
+
outcome: "succeeded",
|
|
5949
|
+
endedAt,
|
|
5950
|
+
exitCode: 0,
|
|
5951
|
+
gate,
|
|
5952
|
+
council,
|
|
5953
|
+
...(input.template.plan !== undefined ? { plan: input.template.plan } : {}),
|
|
5954
|
+
});
|
|
5955
|
+
const receipt = l.recordReceipt(runId, {
|
|
5956
|
+
runId,
|
|
5957
|
+
agentId: "council-synthesis",
|
|
5958
|
+
executionRole: "judge",
|
|
5959
|
+
requestOrigin: origin,
|
|
5960
|
+
task,
|
|
5961
|
+
targetId: input.template.targetId,
|
|
5962
|
+
wireModelId: input.template.wireModelId,
|
|
5963
|
+
runtimeId: input.template.runtimeId,
|
|
5964
|
+
runtimeKind: input.template.runtimeKind,
|
|
5965
|
+
startedAt: created.startedAt,
|
|
5966
|
+
endedAt,
|
|
5967
|
+
outcome: "succeeded",
|
|
5968
|
+
outcomeDetail: null,
|
|
5969
|
+
exitCode: 0,
|
|
5970
|
+
tokenCount: 0,
|
|
5971
|
+
inputTokenCount: 0,
|
|
5972
|
+
outputTokenCount: 0,
|
|
5973
|
+
reasoningTokenCount: 0,
|
|
5974
|
+
cacheReadTokenCount: 0,
|
|
5975
|
+
cacheWriteTokenCount: 0,
|
|
5976
|
+
output: { state: "final", text: input.text, bytes: Buffer.byteLength(input.text, "utf8"), truncated: false },
|
|
5977
|
+
costUsd: 0,
|
|
5978
|
+
costProvenance: "unknown",
|
|
5979
|
+
compiledPromptHash: null,
|
|
5980
|
+
staticCompositionHash: null,
|
|
5981
|
+
clioVersion: readClioVersion(),
|
|
5982
|
+
piMonoVersion: readPiMonoVersion(),
|
|
5983
|
+
platform: process.platform,
|
|
5984
|
+
nodeVersion: process.version,
|
|
5985
|
+
toolCalls: 0,
|
|
5986
|
+
toolStats: [],
|
|
5987
|
+
verification: { state: "unverified", basis: "no-validation-tool" },
|
|
5988
|
+
routingIntent: defaultRoutingIntent({}),
|
|
5989
|
+
quality: createRunReceiptQuality({ runtimeEnforceable: false, enforcementPassed: null, resultContract: null }),
|
|
5990
|
+
reproducibility: collectReproducibility(input.template.reproducibility?.cwd ?? process.cwd(), null),
|
|
5991
|
+
sessionId: input.template.sessionId,
|
|
5992
|
+
gate,
|
|
5993
|
+
council,
|
|
5994
|
+
...(input.template.plan !== undefined ? { plan: input.template.plan } : {}),
|
|
5995
|
+
});
|
|
5996
|
+
await l.persist();
|
|
5997
|
+
// The synthesis never ran as a worker, so nothing else publishes its
|
|
5998
|
+
// lifecycle. The Fleet Runs board and /share are built from these
|
|
5999
|
+
// events, and a council whose synthesis they cannot see is a council
|
|
6000
|
+
// whose verdict the operator cannot read or share.
|
|
6001
|
+
const identity: DispatchRunIdentity = {
|
|
6002
|
+
runId,
|
|
6003
|
+
agentId: "council-synthesis",
|
|
6004
|
+
task,
|
|
6005
|
+
requestOrigin: origin,
|
|
6006
|
+
targetId: receipt.targetId,
|
|
6007
|
+
wireModelId: receipt.wireModelId,
|
|
6008
|
+
runtimeId: receipt.runtimeId,
|
|
6009
|
+
runtimeKind: receipt.runtimeKind,
|
|
6010
|
+
gate: { role: gate.role, cycle: gate.cycle },
|
|
6011
|
+
council,
|
|
6012
|
+
};
|
|
6013
|
+
context.bus.emit(BusChannels.DispatchEnqueued, { ...identity, requestOrigin: origin });
|
|
6014
|
+
context.bus.emit(BusChannels.DispatchStarted, {
|
|
6015
|
+
...identity,
|
|
6016
|
+
requestOrigin: origin,
|
|
6017
|
+
pid: null,
|
|
6018
|
+
assignmentId: runId,
|
|
6019
|
+
attempt: 0,
|
|
6020
|
+
});
|
|
6021
|
+
context.bus.emit(BusChannels.DispatchCompleted, {
|
|
6022
|
+
...identity,
|
|
6023
|
+
requestOrigin: origin,
|
|
6024
|
+
outcome: "succeeded",
|
|
6025
|
+
outcomeCode: null,
|
|
6026
|
+
outcomeDetail: null,
|
|
6027
|
+
lineage: input.template.lineage ?? { parentRunId: null, rootRunId: runId, attempt: 0, depth: 0 },
|
|
6028
|
+
tokenCount: 0,
|
|
6029
|
+
inputTokenCount: 0,
|
|
6030
|
+
outputTokenCount: 0,
|
|
6031
|
+
cacheReadTokenCount: 0,
|
|
6032
|
+
cacheWriteTokenCount: 0,
|
|
6033
|
+
reasoningTokenCount: 0,
|
|
6034
|
+
staticShellHash: null,
|
|
6035
|
+
sessionShellHash: null,
|
|
6036
|
+
dynamicHash: null,
|
|
6037
|
+
costUsd: 0,
|
|
6038
|
+
costProvenance: "unknown",
|
|
6039
|
+
durationMs: 0,
|
|
6040
|
+
exitCode: 0,
|
|
6041
|
+
toolActivity: null,
|
|
6042
|
+
});
|
|
6043
|
+
return receipt;
|
|
6044
|
+
}
|
|
6045
|
+
|
|
5716
6046
|
const extension: DomainExtension = {
|
|
5717
6047
|
async start() {
|
|
5718
6048
|
// No in-memory executor survives a process restart, so every active
|
|
@@ -5743,6 +6073,7 @@ export function createDispatchBundle(
|
|
|
5743
6073
|
const reconciledLedger = ledger;
|
|
5744
6074
|
const reconciled = await reconcileOrphanAssignments({
|
|
5745
6075
|
listRunning: () => listStoredAssignments().filter((record) => record.status === "running"),
|
|
6076
|
+
ownerAlive: assignmentProcessOwnerAlive,
|
|
5746
6077
|
lookupAttempt: (runId) => {
|
|
5747
6078
|
const envelope = reconciledLedger?.get(runId) ?? null;
|
|
5748
6079
|
if (!envelope) return null;
|
|
@@ -5752,7 +6083,8 @@ export function createDispatchBundle(
|
|
|
5752
6083
|
succeeded: envelope.outcome === "succeeded",
|
|
5753
6084
|
};
|
|
5754
6085
|
},
|
|
5755
|
-
settle: (assignmentId, terminalRunId, status) =>
|
|
6086
|
+
settle: (assignmentId, terminalRunId, status, owner) =>
|
|
6087
|
+
settleStoredAssignment(assignmentId, terminalRunId, status, owner),
|
|
5756
6088
|
});
|
|
5757
6089
|
if ((reconciled.recovered > 0 || reconciled.abandoned > 0) && process.env.CLIO_CODER_INTERACTIVE !== "1") {
|
|
5758
6090
|
process.stderr.write(
|
|
@@ -5947,6 +6279,7 @@ export function createDispatchBundle(
|
|
|
5947
6279
|
protectedArtifactState: () => getProtectedArtifactState(),
|
|
5948
6280
|
dispatch,
|
|
5949
6281
|
dispatchBatch,
|
|
6282
|
+
sealCouncilSynthesis,
|
|
5950
6283
|
listRuns(status) {
|
|
5951
6284
|
const l = requireLedger();
|
|
5952
6285
|
return status ? l.list({ status }) : l.list();
|
|
@@ -5955,6 +6288,11 @@ export function createDispatchBundle(
|
|
|
5955
6288
|
if (!ledger) return null;
|
|
5956
6289
|
return ledger.get(runId);
|
|
5957
6290
|
},
|
|
6291
|
+
observedRunWrites(runId) {
|
|
6292
|
+
// A run this process never finalized, or finalized long enough ago to
|
|
6293
|
+
// have been evicted, is unknown rather than silent, so both answer null.
|
|
6294
|
+
return runWriteTargets.get(runId) ?? null;
|
|
6295
|
+
},
|
|
5958
6296
|
assignments: {
|
|
5959
6297
|
get: (id) => assignments.get(id),
|
|
5960
6298
|
getStored: (id) => getStoredAssignment(id),
|