zob-harness 0.1.0

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.
Files changed (356) hide show
  1. package/.pi/adapters/registry.json +103 -0
  2. package/.pi/agents/architecture-cartographer.md +53 -0
  3. package/.pi/agents/chief-vision.md +39 -0
  4. package/.pi/agents/clarifier.md +58 -0
  5. package/.pi/agents/context-steward.md +52 -0
  6. package/.pi/agents/doc-steward.md +34 -0
  7. package/.pi/agents/explore.md +49 -0
  8. package/.pi/agents/factory.md +41 -0
  9. package/.pi/agents/implementer.md +44 -0
  10. package/.pi/agents/librarian.md +32 -0
  11. package/.pi/agents/oracle-merge.md +50 -0
  12. package/.pi/agents/oracle.md +55 -0
  13. package/.pi/agents/pattern-miner.md +53 -0
  14. package/.pi/agents/planner.md +39 -0
  15. package/.pi/agents/project-dna-golden-evaluator.md +32 -0
  16. package/.pi/agents/project-dna-ontology-steward.md +30 -0
  17. package/.pi/agents/project-dna-oracle.md +56 -0
  18. package/.pi/agents/project-dna-orchestrator.md +60 -0
  19. package/.pi/agents/project-dna-query-steward.md +38 -0
  20. package/.pi/agents/project-dna-safety-preflight.md +54 -0
  21. package/.pi/agents/project-dna-test-linker.md +27 -0
  22. package/.pi/agents/qa.md +38 -0
  23. package/.pi/agents/refactor-cartographer.md +28 -0
  24. package/.pi/agents/refactor-mover.md +31 -0
  25. package/.pi/agents/refactor-oracle.md +49 -0
  26. package/.pi/agents/repo-scout.md +60 -0
  27. package/.pi/agents/sample-architect.md +48 -0
  28. package/.pi/agents/specifier.md +57 -0
  29. package/.pi/agents/symbol-range-curator.md +41 -0
  30. package/.pi/agents/synthesis.md +52 -0
  31. package/.pi/agents/temp-agent-creator.md +35 -0
  32. package/.pi/autonomy-policy.json +67 -0
  33. package/.pi/budget-policy.json +54 -0
  34. package/.pi/capabilities/zob-public-runtime-capabilities.json +1700 -0
  35. package/.pi/chains/explore-plan-oracle.json +78 -0
  36. package/.pi/chains/explore-spec-clarify-plan-oracle.json +64 -0
  37. package/.pi/chains/explore-spec-plan-oracle.json +53 -0
  38. package/.pi/chains/spec-clarify-plan-oracle.json +53 -0
  39. package/.pi/chains/spec-factory-oracle.json +42 -0
  40. package/.pi/chains/spec-plan-oracle.json +42 -0
  41. package/.pi/compute-profiles/defaults.json +19 -0
  42. package/.pi/compute-profiles/overrides.json +13 -0
  43. package/.pi/compute-profiles/risk-rules.json +16 -0
  44. package/.pi/daemon-policy.json +80 -0
  45. package/.pi/damage-control-rules.json +45 -0
  46. package/.pi/extensions/zob-child-safety/index.ts +212 -0
  47. package/.pi/extensions/zob-harness/AGENTS.md +28 -0
  48. package/.pi/extensions/zob-harness/index.ts +391 -0
  49. package/.pi/extensions/zob-harness/src/AGENTS.md +25 -0
  50. package/.pi/extensions/zob-harness/src/agents.ts +82 -0
  51. package/.pi/extensions/zob-harness/src/autonomous-runtime.ts +2912 -0
  52. package/.pi/extensions/zob-harness/src/autonomy-readiness.ts +778 -0
  53. package/.pi/extensions/zob-harness/src/budget-policy.ts +308 -0
  54. package/.pi/extensions/zob-harness/src/capabilities.ts +249 -0
  55. package/.pi/extensions/zob-harness/src/child-runner.ts +249 -0
  56. package/.pi/extensions/zob-harness/src/chronicle.ts +262 -0
  57. package/.pi/extensions/zob-harness/src/compute-profile.ts +602 -0
  58. package/.pi/extensions/zob-harness/src/compute-workflow-shape.ts +168 -0
  59. package/.pi/extensions/zob-harness/src/coms-v2/AGENTS.md +16 -0
  60. package/.pi/extensions/zob-harness/src/coms-v2/envelope.ts +121 -0
  61. package/.pi/extensions/zob-harness/src/coms-v2/identity.ts +53 -0
  62. package/.pi/extensions/zob-harness/src/coms-v2/ledger-bridge.ts +67 -0
  63. package/.pi/extensions/zob-harness/src/coms-v2/local-transport.ts +147 -0
  64. package/.pi/extensions/zob-harness/src/coms-v2/pending-replies.ts +80 -0
  65. package/.pi/extensions/zob-harness/src/coms-v2/policy.ts +125 -0
  66. package/.pi/extensions/zob-harness/src/coms-v2/presence.ts +55 -0
  67. package/.pi/extensions/zob-harness/src/coms-v2/registry.ts +113 -0
  68. package/.pi/extensions/zob-harness/src/coms-v2/response-capture.ts +50 -0
  69. package/.pi/extensions/zob-harness/src/coms-v2/transcript-capture.ts +164 -0
  70. package/.pi/extensions/zob-harness/src/coms-v2/types.ts +149 -0
  71. package/.pi/extensions/zob-harness/src/coms-v2/zpeer-profile.ts +140 -0
  72. package/.pi/extensions/zob-harness/src/coms-v2/zpeer.ts +452 -0
  73. package/.pi/extensions/zob-harness/src/constants.ts +108 -0
  74. package/.pi/extensions/zob-harness/src/context-gbrain.ts +465 -0
  75. package/.pi/extensions/zob-harness/src/daemon-policy.ts +223 -0
  76. package/.pi/extensions/zob-harness/src/daemon-readiness.ts +134 -0
  77. package/.pi/extensions/zob-harness/src/daemon-runtime.ts +393 -0
  78. package/.pi/extensions/zob-harness/src/factory/AGENTS.md +24 -0
  79. package/.pi/extensions/zob-harness/src/factory/agentic-plan.ts +65 -0
  80. package/.pi/extensions/zob-harness/src/factory/quarantine.ts +319 -0
  81. package/.pi/extensions/zob-harness/src/factory/run.ts +520 -0
  82. package/.pi/extensions/zob-harness/src/factory/validation.ts +454 -0
  83. package/.pi/extensions/zob-harness/src/factory-selector.ts +318 -0
  84. package/.pi/extensions/zob-harness/src/full-autonomy-test.ts +226 -0
  85. package/.pi/extensions/zob-harness/src/git-ops.ts +868 -0
  86. package/.pi/extensions/zob-harness/src/goal-room.ts +178 -0
  87. package/.pi/extensions/zob-harness/src/goal-runtime.ts +1569 -0
  88. package/.pi/extensions/zob-harness/src/goal-todo-imports.ts +111 -0
  89. package/.pi/extensions/zob-harness/src/goal-todo-types.ts +231 -0
  90. package/.pi/extensions/zob-harness/src/goal-todos.ts +1410 -0
  91. package/.pi/extensions/zob-harness/src/goal.ts +152 -0
  92. package/.pi/extensions/zob-harness/src/governed-requests.ts +436 -0
  93. package/.pi/extensions/zob-harness/src/interactive-autonomy.ts +595 -0
  94. package/.pi/extensions/zob-harness/src/launch-apply.ts +313 -0
  95. package/.pi/extensions/zob-harness/src/merge-queue.ts +290 -0
  96. package/.pi/extensions/zob-harness/src/mission-control.ts +573 -0
  97. package/.pi/extensions/zob-harness/src/model-availability.ts +52 -0
  98. package/.pi/extensions/zob-harness/src/model-routing.ts +429 -0
  99. package/.pi/extensions/zob-harness/src/orchestration/AGENTS.md +23 -0
  100. package/.pi/extensions/zob-harness/src/orchestration/adaptive-delegation.ts +547 -0
  101. package/.pi/extensions/zob-harness/src/orchestration/adaptive-workflow.ts +585 -0
  102. package/.pi/extensions/zob-harness/src/orchestration/lead-plan.ts +192 -0
  103. package/.pi/extensions/zob-harness/src/orchestration/plan.ts +168 -0
  104. package/.pi/extensions/zob-harness/src/orchestration/room.ts +346 -0
  105. package/.pi/extensions/zob-harness/src/orchestration/run.ts +134 -0
  106. package/.pi/extensions/zob-harness/src/orchestration/supervised-readonly.ts +1147 -0
  107. package/.pi/extensions/zob-harness/src/orchestration/widget-readers.ts +132 -0
  108. package/.pi/extensions/zob-harness/src/output-contracts.ts +656 -0
  109. package/.pi/extensions/zob-harness/src/project-dna.ts +533 -0
  110. package/.pi/extensions/zob-harness/src/promotion/AGENTS.md +24 -0
  111. package/.pi/extensions/zob-harness/src/promotion/candidate.ts +336 -0
  112. package/.pi/extensions/zob-harness/src/promotion/coms.ts +127 -0
  113. package/.pi/extensions/zob-harness/src/promotion/documentation.ts +142 -0
  114. package/.pi/extensions/zob-harness/src/promotion/factory.ts +107 -0
  115. package/.pi/extensions/zob-harness/src/promotion/ledger.ts +2 -0
  116. package/.pi/extensions/zob-harness/src/promotion/temp-agent.ts +151 -0
  117. package/.pi/extensions/zob-harness/src/promotion/types.ts +149 -0
  118. package/.pi/extensions/zob-harness/src/promotion/validate.ts +6 -0
  119. package/.pi/extensions/zob-harness/src/promotion/write-lane.ts +162 -0
  120. package/.pi/extensions/zob-harness/src/prompt-packs.ts +239 -0
  121. package/.pi/extensions/zob-harness/src/queue.ts +386 -0
  122. package/.pi/extensions/zob-harness/src/rules.ts +225 -0
  123. package/.pi/extensions/zob-harness/src/runtime/AGENTS.md +26 -0
  124. package/.pi/extensions/zob-harness/src/runtime/adaptive-zmode.ts +116 -0
  125. package/.pi/extensions/zob-harness/src/runtime/auto-compaction.ts +715 -0
  126. package/.pi/extensions/zob-harness/src/runtime/commands.ts +1315 -0
  127. package/.pi/extensions/zob-harness/src/runtime/compaction-policy.ts +516 -0
  128. package/.pi/extensions/zob-harness/src/runtime/delegation-click-markers.ts +141 -0
  129. package/.pi/extensions/zob-harness/src/runtime/delegation-feed.ts +415 -0
  130. package/.pi/extensions/zob-harness/src/runtime/delegation-markdown.ts +97 -0
  131. package/.pi/extensions/zob-harness/src/runtime/delegation-monitor.ts +553 -0
  132. package/.pi/extensions/zob-harness/src/runtime/delegation-mouse.ts +205 -0
  133. package/.pi/extensions/zob-harness/src/runtime/delegation-overlay.ts +434 -0
  134. package/.pi/extensions/zob-harness/src/runtime/events.ts +736 -0
  135. package/.pi/extensions/zob-harness/src/runtime/goal-todo-overlay.ts +214 -0
  136. package/.pi/extensions/zob-harness/src/runtime/mode-intent.ts +144 -0
  137. package/.pi/extensions/zob-harness/src/runtime/plan-capture.ts +270 -0
  138. package/.pi/extensions/zob-harness/src/runtime/state.ts +403 -0
  139. package/.pi/extensions/zob-harness/src/runtime/tools-autonomous.ts +117 -0
  140. package/.pi/extensions/zob-harness/src/runtime/tools-compute.ts +136 -0
  141. package/.pi/extensions/zob-harness/src/runtime/tools-coms.ts +365 -0
  142. package/.pi/extensions/zob-harness/src/runtime/tools-context.ts +70 -0
  143. package/.pi/extensions/zob-harness/src/runtime/tools-delegation.ts +1854 -0
  144. package/.pi/extensions/zob-harness/src/runtime/tools-factory.ts +810 -0
  145. package/.pi/extensions/zob-harness/src/runtime/tools-goal-room.ts +46 -0
  146. package/.pi/extensions/zob-harness/src/runtime/tools-governed-requests.ts +38 -0
  147. package/.pi/extensions/zob-harness/src/runtime/tools-merge-queue.ts +61 -0
  148. package/.pi/extensions/zob-harness/src/runtime/tools-mission-control.ts +77 -0
  149. package/.pi/extensions/zob-harness/src/runtime/tools-orchestration.ts +106 -0
  150. package/.pi/extensions/zob-harness/src/runtime/tools-project-dna.ts +123 -0
  151. package/.pi/extensions/zob-harness/src/runtime/tools-worker-pool.ts +93 -0
  152. package/.pi/extensions/zob-harness/src/runtime/tools-workspace-claims.ts +62 -0
  153. package/.pi/extensions/zob-harness/src/runtime/tools-zcommit.ts +147 -0
  154. package/.pi/extensions/zob-harness/src/runtime/widget.ts +353 -0
  155. package/.pi/extensions/zob-harness/src/runtime/zobHarness.ts +60 -0
  156. package/.pi/extensions/zob-harness/src/safety.ts +338 -0
  157. package/.pi/extensions/zob-harness/src/sandbox.ts +1508 -0
  158. package/.pi/extensions/zob-harness/src/schemas-project-dna.ts +47 -0
  159. package/.pi/extensions/zob-harness/src/schemas.ts +695 -0
  160. package/.pi/extensions/zob-harness/src/telemetry.ts +373 -0
  161. package/.pi/extensions/zob-harness/src/topology/AGENTS.md +22 -0
  162. package/.pi/extensions/zob-harness/src/topology/chains.ts +236 -0
  163. package/.pi/extensions/zob-harness/src/topology/coms.ts +211 -0
  164. package/.pi/extensions/zob-harness/src/topology/orchestration-profiles.ts +204 -0
  165. package/.pi/extensions/zob-harness/src/topology/teams.ts +113 -0
  166. package/.pi/extensions/zob-harness/src/types/core.ts +47 -0
  167. package/.pi/extensions/zob-harness/src/types.ts +939 -0
  168. package/.pi/extensions/zob-harness/src/utils/AGENTS.md +22 -0
  169. package/.pi/extensions/zob-harness/src/utils/formatting.ts +34 -0
  170. package/.pi/extensions/zob-harness/src/utils/hashing.ts +11 -0
  171. package/.pi/extensions/zob-harness/src/utils/json.ts +28 -0
  172. package/.pi/extensions/zob-harness/src/utils/paths.ts +54 -0
  173. package/.pi/extensions/zob-harness/src/utils/records.ts +25 -0
  174. package/.pi/extensions/zob-harness/src/utils/resources.ts +38 -0
  175. package/.pi/extensions/zob-harness/src/worker-pool.ts +672 -0
  176. package/.pi/extensions/zob-harness/src/workspace-claims.ts +297 -0
  177. package/.pi/extensions/zob-switch/index.ts +180 -0
  178. package/.pi/factories/budget-preflight-dry-run/batch-manifest.json +59 -0
  179. package/.pi/factories/budget-preflight-dry-run/factory.json +94 -0
  180. package/.pi/factories/budget-preflight-dry-run/pilot-manifest.json +50 -0
  181. package/.pi/factories/budget-preflight-dry-run/smoke-manifest.json +43 -0
  182. package/.pi/factories/code-review-matrix/batch-manifest.json +61 -0
  183. package/.pi/factories/code-review-matrix/factory.json +163 -0
  184. package/.pi/factories/code-review-matrix/pilot-manifest.json +41 -0
  185. package/.pi/factories/code-review-matrix/smoke-manifest.json +35 -0
  186. package/.pi/factories/factory-forge/batch-manifest.json +56 -0
  187. package/.pi/factories/factory-forge/factory.json +84 -0
  188. package/.pi/factories/factory-forge/pilot-manifest.json +32 -0
  189. package/.pi/factories/factory-forge/smoke-manifest.json +19 -0
  190. package/.pi/factories/opencode-pattern-canonizer/batch-manifest.json +54 -0
  191. package/.pi/factories/opencode-pattern-canonizer/factory.json +86 -0
  192. package/.pi/factories/opencode-pattern-canonizer/pilot-manifest.json +39 -0
  193. package/.pi/factories/opencode-pattern-canonizer/smoke-manifest.json +26 -0
  194. package/.pi/factories/project-dna/README.md +182 -0
  195. package/.pi/factories/project-dna/batch-manifest.json +37 -0
  196. package/.pi/factories/project-dna/example-project-dna-manifest-v2.json +80 -0
  197. package/.pi/factories/project-dna/example-project-dna-manifest.json +58 -0
  198. package/.pi/factories/project-dna/factory.json +131 -0
  199. package/.pi/factories/project-dna/golden-cases-smoke.json +62 -0
  200. package/.pi/factories/project-dna/pi-agentic-ontology.json +88 -0
  201. package/.pi/factories/project-dna/pilot-manifest.json +32 -0
  202. package/.pi/factories/project-dna/schemas/benchmark-suite.schema.json +27 -0
  203. package/.pi/factories/project-dna/schemas/code-knowledge-graph.schema.json +97 -0
  204. package/.pi/factories/project-dna/schemas/context-pack.schema.json +43 -0
  205. package/.pi/factories/project-dna/schemas/golden-case.schema.json +36 -0
  206. package/.pi/factories/project-dna/schemas/manifest-v2.schema.json +128 -0
  207. package/.pi/factories/project-dna/schemas/manifest.schema.json +77 -0
  208. package/.pi/factories/project-dna/schemas/ontology.schema.json +45 -0
  209. package/.pi/factories/project-dna/schemas/project-fingerprint.schema.json +28 -0
  210. package/.pi/factories/project-dna/schemas/query-steward-report.schema.json +52 -0
  211. package/.pi/factories/project-dna/smoke-manifest.json +27 -0
  212. package/.pi/factories/roadmap-smoke-lots/batch-manifest.json +49 -0
  213. package/.pi/factories/roadmap-smoke-lots/factory.json +89 -0
  214. package/.pi/factories/roadmap-smoke-lots/pilot-manifest.json +50 -0
  215. package/.pi/factories/roadmap-smoke-lots/smoke-manifest.json +35 -0
  216. package/.pi/git-policy.json +120 -0
  217. package/.pi/mission-control/zob_coms_transport.json +64 -0
  218. package/.pi/model-catalog.example.json +345 -0
  219. package/.pi/model-economy.example.json +196 -0
  220. package/.pi/model-routing.json +86 -0
  221. package/.pi/orchestrations/adaptive-chief-vision.json +193 -0
  222. package/.pi/orchestrations/ceo-feature-build.json +182 -0
  223. package/.pi/orchestrations/readonly-dynamic-smoke.json +75 -0
  224. package/.pi/output-contracts/agent-event.v1.json +19 -0
  225. package/.pi/output-contracts/base.v1.json +24 -0
  226. package/.pi/output-contracts/brain-lookup.v1.json +21 -0
  227. package/.pi/output-contracts/clarification.v1.json +21 -0
  228. package/.pi/output-contracts/context-pack.v1.json +20 -0
  229. package/.pi/output-contracts/context-request.v1.json +21 -0
  230. package/.pi/output-contracts/context-steward.v1.json +19 -0
  231. package/.pi/output-contracts/context-writeback-proposal.v1.json +18 -0
  232. package/.pi/output-contracts/delegation-request.v1.json +21 -0
  233. package/.pi/output-contracts/explore.v1.json +52 -0
  234. package/.pi/output-contracts/factory.v1.json +48 -0
  235. package/.pi/output-contracts/guidance-steward.v1.json +18 -0
  236. package/.pi/output-contracts/implement.v1.json +40 -0
  237. package/.pi/output-contracts/launch-authorization.v1.json +21 -0
  238. package/.pi/output-contracts/lead-plan.v1.json +22 -0
  239. package/.pi/output-contracts/mission-readiness.v1.json +20 -0
  240. package/.pi/output-contracts/oracle-merge.v1.json +44 -0
  241. package/.pi/output-contracts/oracle-request.v1.json +20 -0
  242. package/.pi/output-contracts/oracle.v1.json +44 -0
  243. package/.pi/output-contracts/orchestration-profile.v1.json +22 -0
  244. package/.pi/output-contracts/plan.v1.json +48 -0
  245. package/.pi/output-contracts/prompt-pack.v1.json +20 -0
  246. package/.pi/output-contracts/qa.v1.json +40 -0
  247. package/.pi/output-contracts/research.v1.json +36 -0
  248. package/.pi/output-contracts/spec.v1.json +22 -0
  249. package/.pi/output-contracts/synthesis.v1.json +44 -0
  250. package/.pi/output-contracts/temp-agent-card.v1.json +23 -0
  251. package/.pi/output-contracts/todo-child-result.v1.json +20 -0
  252. package/.pi/output-contracts/todo-child-result.v2.json +22 -0
  253. package/.pi/output-contracts/todo-claim-validation.v1.json +22 -0
  254. package/.pi/output-contracts/todo-split-request.v1.json +20 -0
  255. package/.pi/prompts/adaptive-workflow.md +63 -0
  256. package/.pi/prompts/autonomous-runtime.md +15 -0
  257. package/.pi/prompts/benchmark-contender.md +15 -0
  258. package/.pi/prompts/benchmark-judge.md +19 -0
  259. package/.pi/prompts/clarify-spec.md +20 -0
  260. package/.pi/prompts/compute-plan.md +36 -0
  261. package/.pi/prompts/compute-preview.md +42 -0
  262. package/.pi/prompts/contract.md +29 -0
  263. package/.pi/prompts/explore.md +13 -0
  264. package/.pi/prompts/factory-run.md +36 -0
  265. package/.pi/prompts/factory.md +20 -0
  266. package/.pi/prompts/implement.md +27 -0
  267. package/.pi/prompts/model-catalog.md +68 -0
  268. package/.pi/prompts/model-economy.md +64 -0
  269. package/.pi/prompts/oracle-merge.md +18 -0
  270. package/.pi/prompts/oracle.md +13 -0
  271. package/.pi/prompts/orchestrator.md +48 -0
  272. package/.pi/prompts/parallel-review.md +21 -0
  273. package/.pi/prompts/plan.md +21 -0
  274. package/.pi/prompts/project-dna.md +90 -0
  275. package/.pi/prompts/refactor-oracle.md +23 -0
  276. package/.pi/prompts/refactor-slice.md +24 -0
  277. package/.pi/prompts/research.md +20 -0
  278. package/.pi/prompts/spec.md +19 -0
  279. package/.pi/prompts/synthesis.md +18 -0
  280. package/.pi/rules/always.md +38 -0
  281. package/.pi/rules/docs.md +32 -0
  282. package/.pi/rules/factory.md +44 -0
  283. package/.pi/rules/oracle.md +34 -0
  284. package/.pi/rules/orchestration.md +44 -0
  285. package/.pi/rules/project.md +34 -0
  286. package/.pi/rules/prompts.md +43 -0
  287. package/.pi/rules/runtime.md +43 -0
  288. package/.pi/rules/sandbox.md +43 -0
  289. package/.pi/settings.json +28 -0
  290. package/.pi/skills/zob-agentic-access/SKILL.md +20 -0
  291. package/.pi/skills/zob-autonomous-runtime/SKILL.md +41 -0
  292. package/.pi/skills/zob-commit/SKILL.md +79 -0
  293. package/.pi/skills/zob-compaction-policy/SKILL.md +92 -0
  294. package/.pi/skills/zob-compute-profile/SKILL.md +108 -0
  295. package/.pi/skills/zob-coms-safety/SKILL.md +54 -0
  296. package/.pi/skills/zob-coms-v2-live/SKILL.md +47 -0
  297. package/.pi/skills/zob-delegation-routing/SKILL.md +82 -0
  298. package/.pi/skills/zob-factory/SKILL.md +28 -0
  299. package/.pi/skills/zob-goal-todo-tree/SKILL.md +279 -0
  300. package/.pi/skills/zob-harness/SKILL.md +68 -0
  301. package/.pi/skills/zob-mission-control-coms/SKILL.md +39 -0
  302. package/.pi/skills/zob-oracle/SKILL.md +21 -0
  303. package/.pi/skills/zob-owner-pool-drill-writer/SKILL.md +244 -0
  304. package/.pi/skills/zob-owner-pool-launcher/SKILL.md +261 -0
  305. package/.pi/skills/zob-project-dna/SKILL.md +275 -0
  306. package/.pi/skills/zob-sandbox/SKILL.md +29 -0
  307. package/.pi/skills/zob-spec/SKILL.md +25 -0
  308. package/.pi/skills/zob-split-refactor/SKILL.md +39 -0
  309. package/.pi/skills/zob-tool-router/SKILL.md +104 -0
  310. package/.pi/teams/zob-core.json +122 -0
  311. package/AGENTS.md +89 -0
  312. package/CONTRIBUTING.md +56 -0
  313. package/LICENSE +21 -0
  314. package/README.md +360 -0
  315. package/SECURITY.md +35 -0
  316. package/SOURCE_INDEX.md +46 -0
  317. package/package.json +135 -0
  318. package/scripts/README.md +57 -0
  319. package/scripts/autonomy/mission-readiness-secret-smoke.mjs +90 -0
  320. package/scripts/compute-profile/plan-workflow.mjs +85 -0
  321. package/scripts/compute-profile/preview.mjs +242 -0
  322. package/scripts/compute-profile/regression-smoke.mjs +38 -0
  323. package/scripts/compute-profile/summarize.mjs +72 -0
  324. package/scripts/compute-profile/validate-policy.mjs +50 -0
  325. package/scripts/compute-profile/validate-preview.mjs +95 -0
  326. package/scripts/compute-profile/validate-workflow.mjs +58 -0
  327. package/scripts/git-ops/commit-policy-smoke.mjs +221 -0
  328. package/scripts/goal-todo/child-goal-ref-smoke.mjs +252 -0
  329. package/scripts/harness-switch/static-smoke.mjs +43 -0
  330. package/scripts/model-catalog/validate-economy.mjs +223 -0
  331. package/scripts/model-catalog/validate.mjs +199 -0
  332. package/scripts/package-surface/validate-script-refs.mjs +190 -0
  333. package/scripts/path-policy/validate-smoke.mjs +103 -0
  334. package/scripts/project-dna/bench-smoke.mjs +217 -0
  335. package/scripts/project-dna/build-capsules.mjs +207 -0
  336. package/scripts/project-dna/build-sample-spec.mjs +140 -0
  337. package/scripts/project-dna/emit-golden-cases.mjs +75 -0
  338. package/scripts/project-dna/emit-ontology.mjs +75 -0
  339. package/scripts/project-dna/generate-sample.mjs +302 -0
  340. package/scripts/project-dna/oracle-review-smoke.mjs +157 -0
  341. package/scripts/project-dna/plan-workflow.mjs +289 -0
  342. package/scripts/project-dna/query-context.mjs +276 -0
  343. package/scripts/project-dna/query-steward.mjs +149 -0
  344. package/scripts/project-dna/scan.mjs +553 -0
  345. package/scripts/project-dna/validate-5of5.mjs +159 -0
  346. package/scripts/project-dna/validate-golden-cases.mjs +78 -0
  347. package/scripts/project-dna/validate-ontology.mjs +97 -0
  348. package/scripts/project-dna/validate-sample-project.mjs +105 -0
  349. package/scripts/project-dna/validate-scaffold.mjs +383 -0
  350. package/scripts/project-dna/validate-scan-artifacts.mjs +187 -0
  351. package/scripts/project-dna/validate-workflow.mjs +166 -0
  352. package/scripts/start-pi.sh +4 -0
  353. package/scripts/worker-pool/static-smoke.mjs +54 -0
  354. package/scripts/zpeer-local-e2e-smoke.mjs +395 -0
  355. package/scripts/zpeer-static-smoke.mjs +129 -0
  356. package/tsconfig.json +12 -0
@@ -0,0 +1,1854 @@
1
+ import { appendFileSync, mkdirSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
4
+ import { Text } from "@earendil-works/pi-tui";
5
+
6
+ import type { AgentScope, ChildResult, ChildStopCondition, ChildThinkingLevel, DelegationDetails, DelegationFailureKind } from "../types.js";
7
+ import { AwaitDelegationRunParams, DelegateParams, DelegateTaskParams, DelegationCatalogParams, DelegationRunParams } from "../schemas.js";
8
+ import { discoverAgents, formatAgentList } from "../agents.js";
9
+ import { applyTodoSplitRequest, extractTodoClaimFromText, extractTodoClaimValidationFromText, extractTodoSplitRequestFromText, isActionableTodoClaimValidation, isActionableTodoSplitRequest, linkGoalTodoDelegation, recordGoalTodoClaimValidationResult, requestGoalTodoClaimValidation, resolveGoalTodoReference, returnGoalTodoClaim, type GoalTodoNode } from "../goal-todos.js";
10
+ import { isFailed, mapWithConcurrency, runChildAgent, validateChildThinkingOverride } from "../child-runner.js";
11
+ import { classifyChildStopCondition, classifyDelegationChronicleCompletion, outputHasEvidenceMarker } from "../chronicle.js";
12
+ import { validateExplicitModelOverride } from "../model-availability.js";
13
+ import { applyChildGates, getOutputContractDefinitions, inferOutputContract, listOutputContracts, validateOutputContractId } from "../output-contracts.js";
14
+ import { captureZcommitChildDirtySnapshot, diffZcommitChildDirtySnapshots, type ZcommitChildChangedPathRef } from "../git-ops.js";
15
+ import {
16
+ parseToolList,
17
+ resolveChildCwd,
18
+ validateAllowedPathPolicy,
19
+ validateDelegationWriteScope,
20
+ validateDelegateTaskWriteScope,
21
+ validateForbiddenPathPolicy,
22
+ validateSixPartContract,
23
+ validateToolList,
24
+ } from "../safety.js";
25
+ import { usageEmpty, writeDelegationTelemetrySummary } from "../telemetry.js";
26
+ import { capOutput, formatChildResultText } from "../utils/formatting.js";
27
+ import { sha256 } from "../utils/hashing.js";
28
+ import { newRunId } from "../utils/paths.js";
29
+ import {
30
+ delegationDurationMs,
31
+ delegationSignalBadge,
32
+ delegationSignalColor,
33
+ extractDelegationSignalBadge,
34
+ finishDelegationRun,
35
+ formatDelegationModelLabel,
36
+ formatDelegationSignalBadge,
37
+ formatDuration,
38
+ hasActiveDelegations,
39
+ startDelegationRun,
40
+ statusIcon,
41
+ updateDelegationRun,
42
+ type DelegationRunMode,
43
+ } from "./delegation-monitor.js";
44
+ import { delegateViewLink } from "./delegation-mouse.js";
45
+ import type { BackgroundDelegationRuntimeRun, HarnessRuntimeState } from "./state.js";
46
+ import { strictGoalErrors, strictGoalSpecErrors } from "./state.js";
47
+ import { renderHarnessWidget } from "./widget.js";
48
+
49
+ function appendLedgerFile(repoRoot: string, entry: Record<string, unknown>): void {
50
+ const dir = join(repoRoot, ".pi", "logs", "runs");
51
+ mkdirSync(dir, { recursive: true });
52
+ const day = new Date().toISOString().slice(0, 10);
53
+ appendFileSync(join(dir, `${day}.jsonl`), `${JSON.stringify({ ...entry, timestamp: new Date().toISOString() })}\n`, "utf8");
54
+ }
55
+
56
+ function asDelegationDetails(value: unknown): DelegationDetails | undefined {
57
+ if (!value || typeof value !== "object") return undefined;
58
+ const details = value as Partial<DelegationDetails>;
59
+ if ((details.mode !== "single" && details.mode !== "parallel" && details.mode !== "chain") || !Array.isArray(details.results)) return undefined;
60
+ return { mode: details.mode, results: details.results, agents: Array.isArray(details.agents) ? details.agents : [] };
61
+ }
62
+
63
+ function delegationLedgerMeta(source: "delegate_agent" | "delegate_task", parentToolCallId: string | undefined, delegationMode: DelegationRunMode, index?: number): Record<string, unknown> {
64
+ return { source, parentToolCallId, delegationMode, index };
65
+ }
66
+
67
+ function delegationCallLabel(args: { agent?: string; task?: string; tasks?: Array<{ agent: string }>; chain?: Array<{ agent: string }> }): string {
68
+ if (args.agent && args.task) return `single → ${args.agent}`;
69
+ if (args.tasks && args.tasks.length > 0) return `parallel ×${args.tasks.length} → ${args.tasks.map((task) => task.agent).join(", ")}`;
70
+ if (args.chain && args.chain.length > 0) return `chain ×${args.chain.length} → ${args.chain.map((step) => step.agent).join(" → ")}`;
71
+ return "invalid parameters";
72
+ }
73
+
74
+ function classifyConfigOrPreflight(errors: string[]): DelegationFailureKind {
75
+ return errors.some((error) => /unknown agent|unknown output contract|available:/i.test(error)) ? "config" : "preflight";
76
+ }
77
+
78
+ function toolsEnableWrites(tools: string[] | undefined): boolean {
79
+ return Boolean(tools?.some((tool) => tool === "edit" || tool === "write"));
80
+ }
81
+
82
+ function captureChildDirtyDelta(repoRoot: string, pathPolicy: { allowedPaths?: string[]; forbiddenPaths?: string[] }, before: ReturnType<typeof captureZcommitChildDirtySnapshot> | undefined): ZcommitChildChangedPathRef[] {
83
+ if (!before) return [];
84
+ const after = captureZcommitChildDirtySnapshot(repoRoot, pathPolicy);
85
+ return diffZcommitChildDirtySnapshots(before, after);
86
+ }
87
+
88
+ function classifyChildFailure(result: ChildResult): DelegationFailureKind | undefined {
89
+ if (result.stopReason === "aborted") return "aborted";
90
+ if (result.exitCode !== 0 || result.stopReason === "error") return "child_runtime";
91
+ if (result.gatePassed === false) return "output_gate";
92
+ return undefined;
93
+ }
94
+
95
+ function delegateTaskPreflightHelp(errors: string[]): string {
96
+ const base = `Delegation preflight failed (no child launched):\n- ${errors.join("\n- ")}`;
97
+ const aliasHint = errors.some((error) => /delegate_task field|Conflicting delegate_task fields/i.test(error))
98
+ ? "\n\nHow to fix structured fields: use canonical JSON keys expected_outcome, must_do, must_not_do, original_user_ask, allowed_paths, forbidden_paths, required_tools, output_contract, run_in_background, child_goal, load_skills; safe aliases such as expectedOutcome, mustDo, mustNotDo/must_not/mustNot, originalUserAsk, allowedPaths, forbiddenPaths, requiredTools, outputContract, runInBackground, childGoal, loadSkills are accepted only when they do not conflict."
99
+ : "";
100
+ const missingOriginalUserAsk = errors.some((error) => /ORIGINAL_USER_ASK\/original_user_ask is required/i.test(error));
101
+ if (!missingOriginalUserAsk) return `${base}${aliasHint}`;
102
+ return `${base}${aliasHint}\n\nHow to fix: retry delegate_task with top-level original_user_ask set to the original human request. Putting it only in context or task text is not enough for write-enabled delegations with edit/write tools.`;
103
+ }
104
+
105
+ function childFailureMessage(kind: DelegationFailureKind | undefined, gateErrors: string[] | undefined, errorMessage: string | undefined): string | undefined {
106
+ if (kind === "output_gate") return `Output contract gate failed; format repair may be enough. Gate errors: ${(gateErrors ?? []).join("; ") || "unknown"}`;
107
+ if (kind === "child_runtime") return errorMessage ?? "Child runtime failed before a valid gated output was produced";
108
+ if (kind === "aborted") return "Child agent aborted";
109
+ return errorMessage;
110
+ }
111
+
112
+ type AgenticClaimValidationInput = {
113
+ mode?: "off" | "oracle_then_auto_accept";
114
+ oracle_agent?: string;
115
+ auto_accept_on_pass?: boolean;
116
+ output_contract?: string;
117
+ };
118
+
119
+ type ChildGoalInput = {
120
+ enabled?: boolean;
121
+ objective?: string;
122
+ todo_id?: string;
123
+ parent_todo_id?: string;
124
+ todo_path?: string;
125
+ delegation_depth?: number;
126
+ request_id?: string;
127
+ oracle_required?: boolean;
128
+ max_turns?: number;
129
+ max_tokens?: number;
130
+ completion_policy?: "return_claim" | "oracle_before_complete";
131
+ agentic_validation?: AgenticClaimValidationInput;
132
+ };
133
+
134
+ type DelegateTaskAliasInput = {
135
+ agent: string;
136
+ task: string;
137
+ expected_outcome?: string;
138
+ expectedOutcome?: string;
139
+ required_tools?: string[];
140
+ requiredTools?: string[];
141
+ must_do?: string[];
142
+ mustDo?: string[];
143
+ must_not_do?: string[];
144
+ mustNotDo?: string[];
145
+ must_not?: string[];
146
+ mustNot?: string[];
147
+ context: string;
148
+ original_user_ask?: string;
149
+ originalUserAsk?: string;
150
+ allowed_paths?: string[];
151
+ allowedPaths?: string[];
152
+ forbidden_paths?: string[];
153
+ forbiddenPaths?: string[];
154
+ output_contract?: string;
155
+ outputContract?: string;
156
+ child_goal?: ChildGoalInput;
157
+ childGoal?: ChildGoalInput;
158
+ run_in_background?: boolean;
159
+ runInBackground?: boolean;
160
+ load_skills?: string[];
161
+ loadSkills?: string[];
162
+ cwd?: string;
163
+ scope?: AgentScope;
164
+ model?: string;
165
+ thinking?: ChildThinkingLevel;
166
+ };
167
+
168
+ type DelegateTaskCanonicalInput = Omit<DelegateTaskAliasInput,
169
+ | "expectedOutcome"
170
+ | "requiredTools"
171
+ | "mustDo"
172
+ | "mustNotDo"
173
+ | "must_not"
174
+ | "mustNot"
175
+ | "originalUserAsk"
176
+ | "allowedPaths"
177
+ | "forbiddenPaths"
178
+ | "outputContract"
179
+ | "childGoal"
180
+ | "runInBackground"
181
+ | "loadSkills"
182
+ > & {
183
+ expected_outcome: string;
184
+ must_do: string[];
185
+ must_not_do: string[];
186
+ };
187
+
188
+ function deepEqual(left: unknown, right: unknown): boolean {
189
+ if (Object.is(left, right)) return true;
190
+ if (Array.isArray(left) || Array.isArray(right)) {
191
+ return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((item, index) => deepEqual(item, right[index]));
192
+ }
193
+ if (!left || !right || typeof left !== "object" || typeof right !== "object") return false;
194
+ const leftRecord = left as Record<string, unknown>;
195
+ const rightRecord = right as Record<string, unknown>;
196
+ const leftKeys = Object.keys(leftRecord).sort();
197
+ const rightKeys = Object.keys(rightRecord).sort();
198
+ return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && deepEqual(leftRecord[key], rightRecord[key]));
199
+ }
200
+
201
+ function normalizeDelegateTaskParams(input: DelegateTaskAliasInput): { params: DelegateTaskCanonicalInput; errors: string[] } {
202
+ const errors: string[] = [];
203
+ const knownKeys = new Set<keyof DelegateTaskAliasInput>(["agent", "task", "expected_outcome", "expectedOutcome", "required_tools", "requiredTools", "must_do", "mustDo", "must_not_do", "mustNotDo", "must_not", "mustNot", "context", "original_user_ask", "originalUserAsk", "allowed_paths", "allowedPaths", "forbidden_paths", "forbiddenPaths", "output_contract", "outputContract", "child_goal", "childGoal", "run_in_background", "runInBackground", "load_skills", "loadSkills", "cwd", "scope", "model", "thinking"]);
204
+ for (const key of Object.keys(input)) {
205
+ if (!knownKeys.has(key as keyof DelegateTaskAliasInput)) errors.push(`Unknown delegate_task field '${key}'; use canonical JSON keys or the documented safe aliases only`);
206
+ }
207
+ const pick = <T>(canonical: keyof DelegateTaskAliasInput, aliases: Array<keyof DelegateTaskAliasInput>, required = false): T | undefined => {
208
+ const entries = [canonical, ...aliases]
209
+ .map((key) => ({ key, value: input[key] }))
210
+ .filter((entry) => entry.value !== undefined);
211
+ if (entries.length === 0) {
212
+ if (required) errors.push(`Missing required delegate_task field '${String(canonical)}' after alias normalization`);
213
+ return undefined;
214
+ }
215
+ const first = entries[0];
216
+ for (const entry of entries.slice(1)) {
217
+ if (!deepEqual(first.value, entry.value)) errors.push(`Conflicting delegate_task fields '${String(first.key)}' and '${String(entry.key)}'; use canonical '${String(canonical)}' or matching aliases only`);
218
+ }
219
+ return first.value as T;
220
+ };
221
+
222
+ return {
223
+ params: {
224
+ agent: input.agent,
225
+ task: input.task,
226
+ expected_outcome: pick<string>("expected_outcome", ["expectedOutcome"], true) ?? "",
227
+ required_tools: pick<string[]>("required_tools", ["requiredTools"]),
228
+ must_do: pick<string[]>("must_do", ["mustDo"], true) ?? [],
229
+ must_not_do: pick<string[]>("must_not_do", ["mustNotDo", "must_not", "mustNot"], true) ?? [],
230
+ context: input.context,
231
+ original_user_ask: pick<string>("original_user_ask", ["originalUserAsk"]),
232
+ allowed_paths: pick<string[]>("allowed_paths", ["allowedPaths"]),
233
+ forbidden_paths: pick<string[]>("forbidden_paths", ["forbiddenPaths"]),
234
+ output_contract: pick<string>("output_contract", ["outputContract"]),
235
+ child_goal: pick<ChildGoalInput>("child_goal", ["childGoal"]),
236
+ run_in_background: pick<boolean>("run_in_background", ["runInBackground"]),
237
+ load_skills: pick<string[]>("load_skills", ["loadSkills"]),
238
+ cwd: input.cwd,
239
+ scope: input.scope,
240
+ model: input.model,
241
+ thinking: input.thinking,
242
+ },
243
+ errors,
244
+ };
245
+ }
246
+
247
+ function childGoalGuidance(childGoal: ChildGoalInput | undefined, parentGoalId: string | undefined, runId: string): string[] {
248
+ if (!childGoal || childGoal.enabled === false || !childGoal.objective?.trim()) return [];
249
+ const childGoalId = parentGoalId ? `child:${parentGoalId}:${runId}` : `child:${runId}`;
250
+ return [
251
+ "ZOB CHILD GOAL:",
252
+ `- CHILD_GOAL_ID: ${childGoalId}`,
253
+ `- OBJECTIVE: ${childGoal.objective.trim()}`,
254
+ childGoal.todo_id ? `- TODO_ID: ${childGoal.todo_id}` : undefined,
255
+ childGoal.parent_todo_id ? `- PARENT_TODO_ID: ${childGoal.parent_todo_id}` : undefined,
256
+ childGoal.todo_path ? `- TODO_PATH: ${childGoal.todo_path}` : undefined,
257
+ childGoal.delegation_depth !== undefined ? `- DELEGATION_DEPTH: ${childGoal.delegation_depth}` : undefined,
258
+ childGoal.request_id ? `- REQUEST_ID: ${childGoal.request_id}` : undefined,
259
+ `- ORACLE_REQUIRED: ${childGoal.oracle_required !== false}`,
260
+ `- MAX_TURNS: ${childGoal.max_turns ?? "parent-managed"}`,
261
+ `- MAX_TOKENS: ${childGoal.max_tokens ?? "parent-managed"}`,
262
+ `- COMPLETION_POLICY: ${childGoal.completion_policy ?? "return_claim"}`,
263
+ childGoal.agentic_validation?.mode ? `- AGENTIC_VALIDATION: ${childGoal.agentic_validation.mode}` : undefined,
264
+ childGoal.agentic_validation?.mode === "oracle_then_auto_accept" ? `- ORACLE_VALIDATION_AGENT: ${childGoal.agentic_validation.oracle_agent ?? "oracle"}` : undefined,
265
+ "- Do not claim global goal completion. Return a child completion claim for parent/oracle review.",
266
+ "- Required child exit field: child_goal_status: ready_for_oracle | incomplete | blocked",
267
+ "- Required child claim fields when ready_for_oracle: evidence_refs, validation_commands, risks, no_ship.",
268
+ "- Child no_ship is advisory/readiness evidence: parent/oracle decides review_no_ship and the runtime computes hard_no_ship/effective_no_ship.",
269
+ childGoal.todo_id ? "- This is a TODO-linked child goal. Do not mark the parent TODO done directly; return TODO_CHILD_RESULT.v2 (v1 remains accepted) fields for parent acceptance." : undefined,
270
+ childGoal.todo_id ? "- TODO_CHILD_RESULT.v2 should include acceptance_blockers and target_readiness: ready_for_parent_acceptance | needs_parent_review | blocked." : undefined,
271
+ childGoal.todo_id ? "- Do not run multiple write-capable workers on this same leaf TODO. If parallel work is needed, ask the parent to split the TODO into subtodos/XDEF leaves and delegate separate leaves/workspaces." : undefined,
272
+ childGoal.todo_id ? "- If explicitly operating under compute high/xhigh/max and this TODO is too broad for your scope/context, return TODO_SPLIT_REQUEST.v1 instead of forcing a poor completion; parent will decide/apply any split." : undefined,
273
+ childGoal.todo_id ? "- TODO_SPLIT_REQUEST.v1 must include deliverable_delivered: yes, todo_id, reason, recommended_action, proposed_subtodos, risk_level, validation_plan, evidence, risks_blockers, no_ship, compliance, and FINAL_MARKER: TODO_SPLIT_REQUEST_END." : undefined,
274
+ childGoal.agentic_validation?.mode === "oracle_then_auto_accept" ? "- After your claim returns, the parent runtime may launch an oracle validation child and auto-accept only on PASS/no_ship=false." : undefined,
275
+ "- Parent/oracle decides acceptance; child must not call update_goal for the parent goal.",
276
+ "",
277
+ ].filter((line): line is string => typeof line === "string");
278
+ }
279
+
280
+ function appendChildGoalToTask(task: string, childGoal: ChildGoalInput | undefined, parentGoalId: string | undefined, runId: string): string {
281
+ const guidance = childGoalGuidance(childGoal, parentGoalId, runId);
282
+ return guidance.length > 0 ? `${guidance.join("\n")}\n${task}` : task;
283
+ }
284
+
285
+ function resolveChildGoalTodoRef(state: HarnessRuntimeState, childGoal: ChildGoalInput | undefined): { childGoal: ChildGoalInput | undefined; errors: string[]; node?: GoalTodoNode } {
286
+ if (!childGoal) return { childGoal, errors: [] };
287
+ const requestedRef = childGoal.todo_id ?? childGoal.todo_path;
288
+ if (!requestedRef) return { childGoal, errors: [] };
289
+ const goalId = state.runtimeGoal?.goalId;
290
+ const resolution = resolveGoalTodoReference(state.goalTodos, goalId, requestedRef, childGoal.todo_id ? "child_goal.todo_id" : "child_goal.todo_path", { requireDelegatable: true });
291
+ const errors = [...resolution.errors];
292
+ if (childGoal.delegation_depth !== undefined && childGoal.delegation_depth > state.goalTodos.policy.maxDelegationDepth) errors.push(`child_goal.delegation_depth exceeds maxDelegationDepth=${state.goalTodos.policy.maxDelegationDepth}`);
293
+ if (!resolution.node || errors.length > 0) return { childGoal: { ...childGoal, todo_id: undefined }, errors };
294
+ return {
295
+ childGoal: {
296
+ ...childGoal,
297
+ todo_id: resolution.node.id,
298
+ todo_path: childGoal.todo_path ?? resolution.node.path,
299
+ },
300
+ errors,
301
+ node: resolution.node,
302
+ };
303
+ }
304
+
305
+ function linkChildGoalTodoDelegationIfReady(pi: ExtensionAPI, state: HarnessRuntimeState, childGoal: ChildGoalInput | undefined, runId: string, agent?: string): void {
306
+ const goalId = state.runtimeGoal?.goalId;
307
+ if (!goalId || !childGoal?.todo_id) return;
308
+ if (!state.goalTodos.nodes.some((node) => node.goalId === goalId && node.id === childGoal.todo_id)) return;
309
+ linkGoalTodoDelegation(pi, state, goalId, childGoal.todo_id, { runId, agent, requestId: childGoal.request_id, delegationDepth: childGoal.delegation_depth }, "delegation");
310
+ }
311
+
312
+ function retargetTodoSplitRequestResult(result: ChildResult, childGoal: ChildGoalInput | undefined, repoRoot: string): void {
313
+ const todoId = childGoal?.todo_id;
314
+ if (!todoId) return;
315
+ const splitRequest = extractTodoSplitRequestFromText(result.output || result.stderr || "");
316
+ if (!isActionableTodoSplitRequest(splitRequest, todoId)) return;
317
+ const previousContract = result.outputContract;
318
+ const previousGatePassed = result.gatePassed;
319
+ const previousGateErrors = result.gateErrors;
320
+ result.outputContract = "todo-split-request.v1";
321
+ result.gatePassed = undefined;
322
+ result.gateErrors = undefined;
323
+ applyChildGates(result, { repoRoot });
324
+ if (result.gatePassed !== true) {
325
+ result.outputContract = previousContract;
326
+ result.gatePassed = previousGatePassed;
327
+ result.gateErrors = previousGateErrors;
328
+ }
329
+ }
330
+
331
+ function recordTodoClaimFromChildResult(pi: ExtensionAPI, state: HarnessRuntimeState, childGoal: ChildGoalInput | undefined, result: ChildResult, meta: { runId?: string; outputHash?: string } = {}): { goalId?: string; todoId?: string; claimHash?: string; validReadyClaim: boolean; node?: GoalTodoNode; splitApplied?: boolean } {
332
+ const goalId = state.runtimeGoal?.goalId;
333
+ const todoId = childGoal?.todo_id;
334
+ if (!goalId || !todoId) return { validReadyClaim: false };
335
+ const text = result.output || result.stderr || "";
336
+ const splitRequest = extractTodoSplitRequestFromText(text);
337
+ if (isActionableTodoSplitRequest(splitRequest, todoId) && result.gatePassed === true && result.outputContract === "todo-split-request.v1") {
338
+ try {
339
+ applyTodoSplitRequest(pi, state, goalId, todoId, splitRequest, "delegation");
340
+ return { goalId, todoId, validReadyClaim: false, splitApplied: true };
341
+ } catch (error) {
342
+ const node = returnGoalTodoClaim(pi, state, goalId, todoId, {
343
+ claimText: `TODO_SPLIT_REQUEST handling failed: ${error instanceof Error ? error.message : String(error)}`,
344
+ evidenceRefs: [],
345
+ validationCommands: [],
346
+ noShip: true,
347
+ runId: meta.runId,
348
+ outputHash: meta.outputHash,
349
+ outputContract: result.outputContract,
350
+ gatePassed: result.gatePassed,
351
+ childChangedPaths: result.childChangedPaths ?? [],
352
+ }, "delegation");
353
+ return { goalId, todoId, claimHash: node?.claim?.claimHash, validReadyClaim: false, node };
354
+ }
355
+ }
356
+ const claim = extractTodoClaimFromText(text);
357
+ const validReadyClaim = !isFailed(result)
358
+ && claim.todoId === todoId
359
+ && claim.childGoalStatus === "ready_for_oracle"
360
+ && claim.statusClaim === "done"
361
+ && claim.hasFinalMarker;
362
+ const node = returnGoalTodoClaim(pi, state, goalId, todoId, {
363
+ claimText: text || "child returned no TODO_CHILD_RESULT.v1/v2 claim",
364
+ evidenceRefs: claim.evidenceRefs,
365
+ validationCommands: claim.validationCommands,
366
+ noShip: validReadyClaim ? claim.noShip === true : true,
367
+ runId: meta.runId,
368
+ outputHash: meta.outputHash,
369
+ outputContract: result.outputContract,
370
+ gatePassed: result.gatePassed,
371
+ childGoalStatus: claim.childGoalStatus,
372
+ statusClaim: claim.statusClaim,
373
+ targetReadiness: claim.targetReadiness,
374
+ acceptanceBlockers: claim.acceptanceBlockers,
375
+ childChangedPaths: result.childChangedPaths ?? [],
376
+ }, "delegation");
377
+ return { goalId, todoId, claimHash: node?.claim?.claimHash, validReadyClaim, node };
378
+ }
379
+
380
+ function shouldRunAgenticClaimValidation(childGoal: ChildGoalInput | undefined, claimRecord: { validReadyClaim: boolean; node?: GoalTodoNode; splitApplied?: boolean }): boolean {
381
+ const settings = childGoal?.agentic_validation;
382
+ if (!settings || settings.mode !== "oracle_then_auto_accept") return false;
383
+ if (claimRecord.splitApplied || !claimRecord.validReadyClaim || !claimRecord.node?.claim) return false;
384
+ if (claimRecord.node.claim.noShip === true) return false;
385
+ return true;
386
+ }
387
+
388
+ function formatTodoClaimValidationTask(node: GoalTodoNode, childGoal: ChildGoalInput | undefined): string {
389
+ const claim = node.claim;
390
+ const validationCommands = node.validationCommands.length > 0 ? node.validationCommands.join("\n- ") : "none";
391
+ const evidenceRefs = node.evidenceRefs.length > 0 ? node.evidenceRefs.join("\n- ") : "none";
392
+ const criteria = node.acceptanceCriteria.length > 0 ? node.acceptanceCriteria.join("\n- ") : "none provided";
393
+ const allowedPaths = childGoal?.todo_id ? "Use only repo-local evidence refs, acceptance criteria, and bounded validation commands relevant to this TODO." : "Use bounded repo-local evidence only.";
394
+ return [
395
+ `1. TASK: Validate returned delegated /goal TODO claim ${node.id} without mutating parent-owned TODO state.`,
396
+ "2. EXPECTED OUTCOME: A strict todo-claim-validation.v1 verdict that parent runtime can parse; PASS/no_ship=false/accept_claim only if evidence proves the TODO claim is safe to accept.",
397
+ "3. REQUIRED TOOLS: read, grep, find, ls, bash",
398
+ "4. MUST DO:",
399
+ `- Verify TODO title: ${node.title}`,
400
+ `- Verify acceptance criteria:\n- ${criteria}`,
401
+ `- Inspect evidence_refs from the claim:\n- ${evidenceRefs}`,
402
+ `- Check validation_commands from the claim when safe/non-destructive:\n- ${validationCommands}`,
403
+ "- Treat missing evidence, unsafe commands, failed checks, mismatched todo_id/claim_hash, or unresolved blockers as WARN/FAIL with no_ship=true unless clearly non-blocking.",
404
+ "- Return recommended_action=accept_claim only with verdict=PASS, no_ship=false, confidence HIGH or MEDIUM, and no blocking issues.",
405
+ "- Return exactly the todo-claim-validation.v1 fields and final marker.",
406
+ "5. MUST NOT DO:",
407
+ "- Do not edit files, commit, call parent goal/TODO tools, or mark the TODO done.",
408
+ "- Do not inspect secrets, environment files, keys, or credential stores.",
409
+ "- Do not accept on assertion alone; require concrete repo evidence or command results.",
410
+ "6. CONTEXT:",
411
+ `- goal_id: ${node.goalId}`,
412
+ `- todo_id: ${node.id}`,
413
+ `- todo_path: ${node.path}`,
414
+ `- owner/status: ${node.owner}/${node.status}`,
415
+ `- claim_hash: ${claim?.claimHash ?? "missing"}`,
416
+ `- claim_output_hash: ${claim?.outputHash ?? "none"}`,
417
+ `- claim_output_contract: ${claim?.outputContract ?? "unknown"}`,
418
+ `- claim_gate_passed: ${claim?.gatePassed === true}`,
419
+ `- child_goal_status: ${claim?.childGoalStatus ?? "unknown"}`,
420
+ `- status_claim: ${claim?.statusClaim ?? "unknown"}`,
421
+ `- target_readiness: ${claim?.targetReadiness ?? "unknown"}`,
422
+ `- acceptance_blockers: ${(claim?.acceptanceBlockers ?? []).join("; ") || "none"}`,
423
+ `- claim_no_ship: ${claim?.noShip === true}`,
424
+ `- ${allowedPaths}`,
425
+ "OUTPUT_CONTRACT: todo-claim-validation.v1",
426
+ ...finalFormatGuidance("todo-claim-validation.v1"),
427
+ ].join("\n");
428
+ }
429
+
430
+ async function runAgenticTodoClaimValidation(input: {
431
+ ctx: ExtensionContext;
432
+ pi: ExtensionAPI;
433
+ state: HarnessRuntimeState;
434
+ childGoal: ChildGoalInput | undefined;
435
+ claimRecord: { goalId?: string; todoId?: string; node?: GoalTodoNode; claimHash?: string };
436
+ parentRunId: string;
437
+ appendDelegationLedger: (entry: Record<string, unknown>) => void;
438
+ signal?: AbortSignal;
439
+ modelOverride?: string;
440
+ allowedPaths?: string[];
441
+ forbiddenPaths?: string[];
442
+ }): Promise<void> {
443
+ const { ctx, pi, state, childGoal, claimRecord, parentRunId, appendDelegationLedger, signal, modelOverride, allowedPaths, forbiddenPaths } = input;
444
+ const goalId = claimRecord.goalId;
445
+ const todoId = claimRecord.todoId;
446
+ const node = claimRecord.node;
447
+ if (!goalId || !todoId || !node?.claim) return;
448
+ const settings = childGoal?.agentic_validation;
449
+ const agentName = settings?.oracle_agent ?? "oracle";
450
+ const agents = discoverAgents(ctx.cwd, "both");
451
+ const agent = agents.find((candidate) => candidate.name.toLowerCase() === agentName.toLowerCase());
452
+ const validationRunId = newRunId("todo_claim_validation");
453
+ try {
454
+ requestGoalTodoClaimValidation(pi, state, goalId, todoId, { runId: validationRunId, agent: agentName }, "delegation");
455
+ } catch (error) {
456
+ appendDelegationLedger({ event: "todo_claim_validation_request_failed", parentRunId, runId: validationRunId, goalId, todoId, errorHash: sha256(error instanceof Error ? error.message : String(error)), at: new Date().toISOString() });
457
+ return;
458
+ }
459
+ appendDelegationLedger({ event: "todo_claim_validation_start", parentRunId, runId: validationRunId, goalId, todoId, agent: agentName, claimHash: node.claim.claimHash, at: new Date().toISOString() });
460
+ let result: ChildResult;
461
+ if (!agent) {
462
+ result = { agent: agentName, task: "todo_claim_validation", exitCode: 1, output: "", stderr: "", usage: usageEmpty(), failureKind: "config", errorMessage: "Configuration blocked; no child launched: unknown oracle validation agent" };
463
+ } else {
464
+ result = await runChildAgent(
465
+ ctx,
466
+ agent,
467
+ formatTodoClaimValidationTask(node, childGoal),
468
+ ctx.cwd,
469
+ signal,
470
+ modelOverride,
471
+ undefined,
472
+ undefined,
473
+ { allowedPaths, forbiddenPaths },
474
+ );
475
+ result.outputContract = settings?.output_contract ?? "todo-claim-validation.v1";
476
+ applyChildGates(result, { repoRoot: ctx.cwd });
477
+ result.failureKind = classifyChildFailure(result);
478
+ }
479
+ const outputHash = result.output ? sha256(result.output) : undefined;
480
+ let parsed = extractTodoClaimValidationFromText(result.output || result.stderr || "");
481
+ if (!isActionableTodoClaimValidation(parsed, todoId, node.claim.claimHash) || isFailed(result)) {
482
+ parsed = {
483
+ todoId,
484
+ claimHash: node.claim.claimHash,
485
+ verdict: "FAIL",
486
+ recommendedAction: "block",
487
+ evidenceRefs: [],
488
+ validationCommands: [],
489
+ blockingIssues: [result.gateErrors?.join("; ") || result.errorMessage || "oracle validation did not return an actionable PASS/WARN/FAIL claim validation"],
490
+ noShip: true,
491
+ confidence: "LOW",
492
+ hasFinalMarker: false,
493
+ };
494
+ }
495
+ try {
496
+ recordGoalTodoClaimValidationResult(pi, state, goalId, todoId, {
497
+ result: parsed,
498
+ runId: validationRunId,
499
+ agent: agentName,
500
+ outputHash,
501
+ autoAccept: settings?.auto_accept_on_pass !== false,
502
+ repoRoot: ctx.cwd,
503
+ }, "delegation");
504
+ } catch (error) {
505
+ appendDelegationLedger({ event: "todo_claim_validation_record_failed", parentRunId, runId: validationRunId, goalId, todoId, errorHash: sha256(error instanceof Error ? error.message : String(error)), at: new Date().toISOString() });
506
+ }
507
+ appendDelegationLedger({
508
+ event: "todo_claim_validation_end",
509
+ parentRunId,
510
+ runId: validationRunId,
511
+ goalId,
512
+ todoId,
513
+ agent: agentName,
514
+ claimHash: node.claim.claimHash,
515
+ outputHash,
516
+ gatePassed: result.gatePassed,
517
+ verdict: parsed.verdict,
518
+ noShip: parsed.noShip,
519
+ recommendedAction: parsed.recommendedAction,
520
+ autoAcceptRequested: settings?.auto_accept_on_pass !== false,
521
+ status: parsed.verdict === "PASS" && parsed.noShip !== true ? "passed" : "blocked",
522
+ at: new Date().toISOString(),
523
+ });
524
+ }
525
+
526
+ function finalFormatGuidance(outputContract: string): string[] {
527
+ if (outputContract === "implement.v1") return [
528
+ "FINAL FORMAT (implement.v1):",
529
+ "- gap_verdict: SUFFICIENT or GAP, with no-change evidence or exact missing behavior",
530
+ "- changed_files: paths changed, or no-change evidence",
531
+ "- verification_commands: exact commands run",
532
+ "- results: exact command outcomes",
533
+ "- evidence: concise proof",
534
+ "- risks/blockers: unresolved risks",
535
+ "- compliance: forbidden zones respected, no commits",
536
+ "- final line must be exactly: deliverable_delivered: yes",
537
+ ];
538
+ if (outputContract === "qa.v1") return [
539
+ "FINAL FORMAT (qa.v1):",
540
+ "- verdict: PASS / FAIL / WARN / INCONCLUSIVE",
541
+ "- commands: exact command, cwd, and exit code",
542
+ "- important output: stdout/stderr excerpts or artifact evidence",
543
+ "- reproduction: steps to reproduce",
544
+ "- evidence: concrete verification evidence",
545
+ "- risks/blockers: unresolved risks",
546
+ "- compliance: read-only QA; forbidden zones respected; no commits",
547
+ "- final line must be exactly: deliverable_delivered: yes",
548
+ ];
549
+ if (outputContract === "todo-child-result.v1" || outputContract === "todo-child-result.v2") return [
550
+ `FINAL FORMAT (${outputContract}):`,
551
+ "- deliverable_delivered: yes/no",
552
+ "- todo_id: exact parent TODO id",
553
+ "- child_goal_status: ready_for_oracle | incomplete | blocked",
554
+ "- status_claim: done | incomplete | blocked",
555
+ "- evidence_refs: safe repo-relative evidence refs",
556
+ "- validation_commands: commands run, or none",
557
+ "- risks_blockers: unresolved risks",
558
+ ...(outputContract === "todo-child-result.v2" ? ["- acceptance_blockers: blockers parent must resolve before accepting, or none", "- target_readiness: ready_for_parent_acceptance | needs_parent_review | blocked"] : []),
559
+ "- no_ship: true/false (advisory/readiness evidence for parent review; deliverable_delivered=yes still returns a claim)",
560
+ "- subtodo_delta_proposals: metadata-only subtodo proposals, or none",
561
+ "- compliance: parent-owned claim only; no parent completion",
562
+ `- final line must be exactly: FINAL_MARKER: ${outputContract === "todo-child-result.v2" ? "TODO_CHILD_RESULT_V2_END" : "TODO_CHILD_RESULT_END"}`,
563
+ ];
564
+ if (outputContract === "todo-claim-validation.v1") return [
565
+ "FINAL FORMAT (todo-claim-validation.v1):",
566
+ "- deliverable_delivered: yes",
567
+ "- todo_id: exact parent TODO id",
568
+ "- claim_hash: exact returned claim hash under review",
569
+ "- verdict: PASS | WARN | FAIL",
570
+ "- recommended_action: accept_claim | needs_review | reject_claim | block",
571
+ "- evidence_refs: safe repo-relative evidence refs inspected, or none",
572
+ "- validation_commands: commands run/checked, or none",
573
+ "- blocking_issues: blockers, or none",
574
+ "- no_ship: true/false",
575
+ "- confidence: LOW | MEDIUM | HIGH",
576
+ "- evidence: concise oracle proof; no raw secrets",
577
+ "- risks_blockers: unresolved risks",
578
+ "- compliance: oracle validation only; no parent TODO mutation by child",
579
+ "- final line must be exactly: FINAL_MARKER: TODO_CLAIM_VALIDATION_END",
580
+ ];
581
+ if (outputContract === "todo-split-request.v1") return [
582
+ "FINAL FORMAT (todo-split-request.v1):",
583
+ "- deliverable_delivered: yes",
584
+ "- todo_id: exact parent TODO id",
585
+ "- reason: why the TODO exceeds this child scope/context",
586
+ "- recommended_action: split | replan | factory | needs_user | blocked",
587
+ "- proposed_subtodos: metadata-only bullet list of bounded child TODO titles",
588
+ "- risk_level: low | medium | high",
589
+ "- validation_plan: commands/evidence each child should provide, or none",
590
+ "- evidence: why this split/replan request is justified",
591
+ "- risks_blockers: unresolved risks",
592
+ "- no_ship: true/false",
593
+ "- compliance: parent-owned split request only; no child dispatch or parent TODO mutation",
594
+ "- final line must be exactly: FINAL_MARKER: TODO_SPLIT_REQUEST_END",
595
+ ];
596
+ if (["delegation-request.v1", "oracle-request.v1", "context-request.v1"].includes(outputContract)) {
597
+ const requestType = outputContract === "oracle-request.v1" ? "ORACLE_REQUEST.v1" : outputContract === "context-request.v1" ? "CONTEXT_REQUEST.v1" : "DELEGATION_REQUEST.v1";
598
+ const finalMarker = requestType.replace(".v1", "_END");
599
+ return [
600
+ `FINAL FORMAT (${outputContract}):`,
601
+ "- deliverable_delivered: yes",
602
+ `- request_type: ${requestType}`,
603
+ "- request_id: path-safe unique id",
604
+ "- goal_id: parent goal id if known",
605
+ "- todo_id: parent TODO id if known",
606
+ "- requested_by: your team role id",
607
+ "- requested_action: concise action label only; no raw prompt/body",
608
+ "- priority: low | normal | high | critical",
609
+ "- risk_level: low | medium | high",
610
+ "- body_hash: sha256 hash of the transient request body; do not include raw body/text/prompt/output/content",
611
+ "- agent: target agent role/name if delegation-request.v1, else none",
612
+ "- required_tools: comma-separated tool names or none",
613
+ "- allowed_paths: repo-relative-only paths or none (use reports/... snapshot/context_ref refs for external context)",
614
+ "- forbidden_paths: deny-only paths or none",
615
+ "- context_scope_id: context scope id if context-request.v1, else none",
616
+ "- evidence_refs: safe repo-relative evidence refs or none",
617
+ "- artifact_refs: safe repo-relative artifact refs or none",
618
+ "- no_ship: true/false",
619
+ "- evidence: hash-only/request evidence summary; no secrets/raw bodies",
620
+ "- risks_blockers: unresolved risks",
621
+ "- compliance: governed request only; no child direct dispatch, no parent TODO mutation, parent/governor decides",
622
+ `- final line must be exactly: FINAL_MARKER: ${finalMarker}`,
623
+ ];
624
+ }
625
+ return [
626
+ "FINAL FORMAT:",
627
+ "- result",
628
+ "- evidence",
629
+ "- risks/blockers",
630
+ "- compliance line",
631
+ "- final line must be exactly: deliverable_delivered: yes",
632
+ ];
633
+ }
634
+
635
+ function hydrateDelegationRunsFromDetails(source: "delegate_agent" | "delegate_task", details: DelegationDetails | undefined, state: HarnessRuntimeState, toolCallId: string | undefined): void {
636
+ if (!details?.results?.length) return;
637
+ const nowMs = Date.now();
638
+ for (const [index, result] of details.results.entries()) {
639
+ if (!result.ledgerRunId) continue;
640
+ const existing = state.delegations.runs.find((run) => run.id === result.ledgerRunId);
641
+ if (!existing) {
642
+ startDelegationRun(state.delegations, {
643
+ id: result.ledgerRunId,
644
+ parentToolCallId: toolCallId ?? result.ledgerRunId,
645
+ source,
646
+ mode: details.mode,
647
+ index,
648
+ agent: result.agent,
649
+ task: result.task || "restored from delegate tool result",
650
+ startedAtMs: nowMs - Math.max(1, index + 1),
651
+ });
652
+ }
653
+ finishDelegationRun(state.delegations, result.ledgerRunId, {
654
+ parentToolCallId: toolCallId ?? existing?.parentToolCallId ?? result.ledgerRunId,
655
+ source,
656
+ mode: details.mode,
657
+ index,
658
+ agent: result.agent,
659
+ model: result.model,
660
+ status: result.stopReason === "aborted" ? "aborted" : isFailed(result) ? "failed" : "complete",
661
+ endedAtMs: existing?.endedAtMs ?? nowMs,
662
+ outputPreview: result.output,
663
+ stderrPreview: result.stderr,
664
+ sessionPath: result.sessionPath,
665
+ exitCode: result.exitCode,
666
+ gatePassed: result.gatePassed,
667
+ gateErrors: result.gateErrors,
668
+ failureKind: result.failureKind,
669
+ stopReason: result.stopReason,
670
+ stopCondition: result.stopCondition,
671
+ errorMessage: result.errorMessage,
672
+ usage: result.usage,
673
+ });
674
+ }
675
+ }
676
+
677
+ function agentSourcePath(repoRoot: string, filePath: string, source: "project" | "user"): string | undefined {
678
+ if (source !== "project") return undefined;
679
+ const path = relative(repoRoot, filePath).replace(/\\/g, "/");
680
+ if (!path || path.startsWith("../") || path === "..") return undefined;
681
+ return path;
682
+ }
683
+
684
+ function buildDelegationCatalog(repoRoot: string, scope: AgentScope, includeContractRequirements = false): Record<string, unknown> {
685
+ const agents = discoverAgents(repoRoot, scope).map((agent) => ({
686
+ name: agent.name,
687
+ description: agent.description,
688
+ source: agent.source,
689
+ sourcePath: agentSourcePath(repoRoot, agent.filePath, agent.source),
690
+ tools: agent.tools ?? [],
691
+ model: agent.model,
692
+ thinking: agent.thinking,
693
+ inferredOutputContract: inferOutputContract(agent.name),
694
+ })).sort((left, right) => left.name.localeCompare(right.name));
695
+ const agentNames = new Set(agents.map((agent) => agent.name));
696
+ const contractDefinitions = getOutputContractDefinitions();
697
+ const validOutputContracts = includeContractRequirements
698
+ ? contractDefinitions
699
+ : listOutputContracts();
700
+ const commonRouting = [
701
+ { need: "read-only exploration / facts / file mapping", agent: "explore", outputContract: "explore.v1" },
702
+ { need: "implementation plan / TDD sequence / handoff", agent: "planner", outputContract: "plan.v1" },
703
+ { need: "source edit or bounded implementation", agent: "implementer", outputContract: "implement.v1" },
704
+ { need: "skeptical audit / PASS-WARN-FAIL / no_ship", agent: "oracle", outputContract: "oracle.v1" },
705
+ { need: "verification run / reproduction / QA verdict", agent: "qa", outputContract: "qa.v1" },
706
+ { need: "sourced research / reusable context", agent: "librarian", outputContract: "research.v1" },
707
+ { need: "specification from fuzzy request", agent: "specifier", outputContract: "spec.v1" },
708
+ { need: "clarification gate before planning", agent: "clarifier", outputContract: "clarification.v1" },
709
+ { need: "factory design", agent: "factory", outputContract: "factory.v1" },
710
+ ].filter((hint) => agentNames.has(hint.agent));
711
+
712
+ return {
713
+ schema: "zob.delegation-catalog.v1",
714
+ scope,
715
+ agents,
716
+ validOutputContracts,
717
+ commonRouting,
718
+ usageGuidance: {
719
+ chooseAgentFirst: true,
720
+ normallyOmitOutputContract: true,
721
+ outputContractInference: "delegate_task infers output_contract from agent when omitted; delegate_agent always infers from agent.",
722
+ doNotInventOutputContracts: true,
723
+ ifUncertain: "Call zob_delegation_catalog before the first delegation or when agent/contract routing is ambiguous.",
724
+ auditRouting: "For an audit/verdict/no_ship review, use oracle with oracle.v1; do not use planner plus a made-up audit.v1.",
725
+ delegateTaskCanonicalFields: ["expected_outcome", "required_tools", "must_do", "must_not_do", "original_user_ask", "allowed_paths", "forbidden_paths", "output_contract", "run_in_background", "child_goal", "load_skills"],
726
+ delegateTaskSafeAliases: { expected_outcome: ["expectedOutcome"], required_tools: ["requiredTools"], must_do: ["mustDo"], must_not_do: ["mustNotDo", "must_not", "mustNot"], original_user_ask: ["originalUserAsk"], allowed_paths: ["allowedPaths"], forbidden_paths: ["forbiddenPaths"], output_contract: ["outputContract"], run_in_background: ["runInBackground"], child_goal: ["childGoal"], load_skills: ["loadSkills"] },
727
+ },
728
+ noExecution: true,
729
+ childDispatchAllowed: false,
730
+ networkAccessed: false,
731
+ bodyStored: false,
732
+ promptBodiesStored: false,
733
+ outputBodiesStored: false,
734
+ generatedAt: new Date().toISOString(),
735
+ };
736
+ }
737
+
738
+ function formatDelegationCatalogSummary(catalog: Record<string, unknown>): string {
739
+ const agents = Array.isArray(catalog.agents) ? catalog.agents as Array<Record<string, unknown>> : [];
740
+ const contracts = Array.isArray(catalog.validOutputContracts) ? catalog.validOutputContracts : [];
741
+ const lines = [
742
+ `zob_delegation_catalog: ${agents.length} agents, ${contracts.length} output contracts`,
743
+ "Rule: normally omit delegate_task.output_contract; the harness infers it from agent.",
744
+ "delegate_task canonical JSON keys: expected_outcome, required_tools, must_do, must_not_do, original_user_ask, allowed_paths, forbidden_paths, output_contract, run_in_background, child_goal, load_skills.",
745
+ "Safe aliases are accepted only when non-conflicting: expectedOutcome, requiredTools, mustDo, mustNotDo/must_not/mustNot, originalUserAsk, allowedPaths, forbiddenPaths, outputContract, runInBackground, childGoal, loadSkills.",
746
+ "If you need an audit/verdict/no_ship review, route to oracle/oracle.v1, not planner/audit.v1.",
747
+ "",
748
+ "Agents:",
749
+ ...agents.map((agent) => `- ${agent.name} -> ${agent.inferredOutputContract} tools=${Array.isArray(agent.tools) ? agent.tools.join(",") || "default" : "default"}: ${agent.description ?? ""}`),
750
+ "",
751
+ `Valid output contracts: ${contracts.map((contract) => typeof contract === "string" ? contract : (contract as Record<string, unknown>).id).filter(Boolean).join(", ")}`,
752
+ ];
753
+ return lines.join("\n");
754
+ }
755
+
756
+ function renderDelegationToolResultText(source: "delegate_agent" | "delegate_task", details: DelegationDetails | undefined, state: HarnessRuntimeState, toolCallId: string | undefined, isPartial: boolean, expanded: boolean, theme: { fg: (color: any, text: string) => string; bold: (text: string) => string }): string {
757
+ hydrateDelegationRunsFromDetails(source, details, state, toolCallId);
758
+ const nowMs = Date.now();
759
+ const monitoredRuns = toolCallId ? state.delegations.runs.filter((run) => run.parentToolCallId === toolCallId) : [];
760
+ const results = details?.results ?? [];
761
+ const okCount = results.filter((result) => !isFailed(result)).length;
762
+ const failCount = results.filter((result) => isFailed(result)).length;
763
+ const blockedCount = results.filter((result) => result.failureKind === "preflight" || result.failureKind === "config").length;
764
+ const gateCount = results.filter((result) => result.failureKind === "output_gate").length;
765
+ const runtimeCount = results.filter((result) => result.failureKind === "child_runtime" || result.failureKind === "aborted").length;
766
+ const runningCount = monitoredRuns.filter((run) => run.status === "queued" || run.status === "running").length;
767
+ const mode = details?.mode ?? (source === "delegate_task" ? "single" : "single");
768
+ const lines = [
769
+ `${theme.fg("toolTitle", theme.bold(source))} ${theme.fg("accent", mode)} ${isPartial ? theme.fg("warning", "running") : theme.fg(failCount > 0 ? "error" : "success", `${okCount}/${results.length || monitoredRuns.length || 1} ok`)}`,
770
+ `${theme.fg("dim", "running")} ${theme.fg(runningCount > 0 ? "warning" : "dim", String(runningCount))} ${theme.fg("dim", "blocked")} ${theme.fg(blockedCount > 0 ? "warning" : "dim", String(blockedCount))} ${theme.fg("dim", "gate")} ${theme.fg(gateCount > 0 ? "warning" : "dim", String(gateCount))} ${theme.fg("dim", "runtime")} ${theme.fg(runtimeCount > 0 ? "error" : "dim", String(runtimeCount))} ${theme.fg("dim", "details")} ${theme.fg("muted", "/zstatus delegations")}`,
771
+ ];
772
+
773
+ if (monitoredRuns.length > 0) {
774
+ const maxRows = expanded ? 12 : 5;
775
+ const visibleRuns = monitoredRuns.slice(0, maxRows);
776
+ const hasMore = monitoredRuns.length > maxRows;
777
+ for (const [index, run] of visibleRuns.entries()) {
778
+ const color = run.status === "complete" ? "success" : run.status === "running" || run.status === "queued" || run.failureKind === "preflight" || run.failureKind === "config" || run.failureKind === "output_gate" ? "warning" : "error";
779
+ const prefix = index === visibleRuns.length - 1 && !hasMore ? "└─" : "├─";
780
+ const viewHint = delegateViewLink(run.id);
781
+ const kind = run.failureKind ? ` ${run.failureKind}` : "";
782
+ const badge = delegationSignalBadge(run);
783
+ const badgeText = formatDelegationSignalBadge(badge);
784
+ const modelLabel = formatDelegationModelLabel(run);
785
+ const modelSuffix = modelLabel ? ` ${theme.fg("muted", `(${modelLabel})`)}` : "";
786
+ lines.push(`${theme.fg("dim", prefix)} ${theme.fg(color, `${statusIcon(run.status)} ${run.agent}${kind}`)}${badgeText ? ` ${theme.fg(delegationSignalColor(badge), badgeText)}` : ""}${modelSuffix} ${theme.fg("dim", formatDuration(delegationDurationMs(run, nowMs)))} ${theme.fg("muted", viewHint)}`);
787
+ if (expanded && (run.errorMessage || run.stopReason || run.gateErrors?.length)) lines.push(` ${theme.fg("dim", run.errorMessage ?? run.gateErrors?.join("; ") ?? run.stopReason ?? "")}`);
788
+ }
789
+ if (hasMore) lines.push(theme.fg("dim", `└─ … ${monitoredRuns.length - maxRows} more child run(s)`));
790
+ return lines.join("\n");
791
+ }
792
+
793
+ const maxRows = expanded ? 10 : 4;
794
+ const visibleResults = results.slice(0, maxRows);
795
+ const hasMore = results.length > maxRows;
796
+ for (const [index, result] of visibleResults.entries()) {
797
+ const failed = isFailed(result);
798
+ const color = failed ? (result.failureKind === "preflight" || result.failureKind === "config" || result.failureKind === "output_gate" ? "warning" : "error") : "success";
799
+ const prefix = index === visibleResults.length - 1 && !hasMore ? "└─" : "├─";
800
+ const viewHint = result.ledgerRunId ? delegateViewLink(result.ledgerRunId) : "[view]";
801
+ const kind = result.failureKind ? ` ${result.failureKind}` : "";
802
+ const badge = extractDelegationSignalBadge(result.output);
803
+ const badgeText = formatDelegationSignalBadge(badge);
804
+ const modelLabel = formatDelegationModelLabel(result);
805
+ const modelSuffix = modelLabel ? ` ${theme.fg("muted", `(${modelLabel})`)}` : "";
806
+ lines.push(`${theme.fg("dim", prefix)} ${theme.fg(color, `${failed ? "✗" : "✓"} ${result.agent}${kind}`)}${badgeText ? ` ${theme.fg(delegationSignalColor(badge), badgeText)}` : ""}${modelSuffix} ${theme.fg("dim", result.ledgerRunId ?? "")} ${theme.fg("muted", viewHint)}`);
807
+ if (expanded && result.output.trim()) lines.push(` ${theme.fg("muted", result.output.split("\n")[0] ?? "")}`);
808
+ }
809
+ if (hasMore) lines.push(theme.fg("dim", `└─ … ${results.length - maxRows} more result(s)`));
810
+ return lines.join("\n");
811
+ }
812
+
813
+ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeState): void {
814
+ pi.registerTool({
815
+ name: "zob_delegation_catalog",
816
+ label: "ZOB Delegation Catalog",
817
+ description: "Read-only live catalog of available ZOB specialist agents, their tools/descriptions, inferred output contracts, valid output contract ids, and routing hints. No child dispatch, no execution, no network.",
818
+ promptSnippet: "Inspect available delegation agents and output contracts before choosing delegate_agent/delegate_task",
819
+ promptGuidelines: [
820
+ "Use this before the first delegation when agent or output_contract routing is uncertain.",
821
+ "Choose the agent by desired deliverable; normally omit delegate_task.output_contract so the harness infers it from the agent.",
822
+ "Do not invent output contract ids; use only validOutputContracts returned by this catalog.",
823
+ ],
824
+ parameters: DelegationCatalogParams,
825
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
826
+ const scope = params.scope ?? "project";
827
+ const catalog = buildDelegationCatalog(ctx.cwd, scope, params.include_contract_requirements === true);
828
+ return { content: [{ type: "text", text: formatDelegationCatalogSummary(catalog) }], details: catalog };
829
+ },
830
+ });
831
+
832
+ pi.registerTool({
833
+ name: "delegate_agent",
834
+ label: "Delegate Agent",
835
+ description: [
836
+ "Delegate a focused task to ZOB specialist Pi child agents.",
837
+ "Modes: single (agent+task), parallel (tasks[]), chain (chain[] with {previous}).",
838
+ "Use this for explore/plan/oracle/research slices before broad implementation.",
839
+ "Every delegated task should use the six-part TASK/EXPECTED OUTCOME/TOOLS/MUST DO/MUST NOT/CONTEXT contract.",
840
+ ].join(" "),
841
+ promptSnippet: "Delegate focused work to project specialist agents with isolated child Pi contexts",
842
+ promptGuidelines: [
843
+ "If agent routing is uncertain, call zob_delegation_catalog before the first delegation.",
844
+ "Use delegate_agent for broad discovery, external research, skeptical review, or independent QA before making risky edits.",
845
+ "When using delegate_agent, give each child a bounded six-part contract and a concrete final output shape.",
846
+ "If effective tools include edit/write, provide non-empty repo-relative-only allowed_paths; use repo-local reports/... snapshot/context_ref refs for external context.",
847
+ ],
848
+ parameters: DelegateParams,
849
+ renderCall(args, theme) {
850
+ return new Text(`${theme.fg("toolTitle", theme.bold("delegate_agent"))} ${theme.fg("accent", delegationCallLabel(args))}`, 0, 0);
851
+ },
852
+ renderResult(result, { expanded, isPartial }, theme, context) {
853
+ const details = asDelegationDetails(result.details);
854
+ const toolCallId = (context as { toolCallId?: string }).toolCallId;
855
+ return new Text(renderDelegationToolResultText("delegate_agent", details, state, toolCallId, isPartial, expanded, theme), 0, 0);
856
+ },
857
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
858
+ const scope = params.scope ?? "project";
859
+ const agents = discoverAgents(ctx.cwd, scope);
860
+ const byName = new Map(agents.map((agent) => [agent.name.toLowerCase(), agent]));
861
+ const makeDetails = (mode: DelegationDetails["mode"], results: ChildResult[]): DelegationDetails => ({
862
+ mode,
863
+ results,
864
+ agents: agents.map((agent) => agent.name),
865
+ });
866
+
867
+ const modes = Number(Boolean(params.agent && params.task)) + Number((params.tasks?.length ?? 0) > 0) + Number((params.chain?.length ?? 0) > 0);
868
+ if (modes !== 1) {
869
+ return {
870
+ content: [{ type: "text", text: `Provide exactly one mode. Available agents:\n${formatAgentList(agents)}` }],
871
+ details: makeDetails("single", []),
872
+ };
873
+ }
874
+
875
+ const appendDelegationLedger = (entry: Record<string, unknown>): void => {
876
+ appendLedgerFile(ctx.cwd, entry);
877
+ pi.appendEntry("zob-delegation", entry);
878
+ };
879
+
880
+ const renderDelegationMonitor = (): void => {
881
+ if (ctx.hasUI) renderHarnessWidget(pi, state, ctx);
882
+ };
883
+ let monitorTicker: NodeJS.Timeout | undefined;
884
+ const startMonitorTicker = (): void => {
885
+ if (!ctx.hasUI || monitorTicker) return;
886
+ monitorTicker = setInterval(() => {
887
+ if (hasActiveDelegations(state.delegations)) renderHarnessWidget(pi, state, ctx);
888
+ }, 1000);
889
+ monitorTicker.unref();
890
+ };
891
+ const stopMonitorTicker = (): void => {
892
+ if (monitorTicker) clearInterval(monitorTicker);
893
+ monitorTicker = undefined;
894
+ renderDelegationMonitor();
895
+ };
896
+
897
+ const runOne = async (item: { agent: string; task: string; cwd?: string; thinking?: ChildThinkingLevel; child_goal?: ChildGoalInput }, monitor: { mode: DelegationRunMode; index?: number }, update?: (result: ChildResult) => void): Promise<ChildResult> => {
898
+ const runId = newRunId("delegate");
899
+ const childGoalResolution = resolveChildGoalTodoRef(state, item.child_goal ?? params.child_goal);
900
+ const effectiveChildGoal = childGoalResolution.childGoal;
901
+ const taskText = appendChildGoalToTask(item.task, effectiveChildGoal, state.runtimeGoal?.goalId, runId);
902
+ const startedAtMs = Date.now();
903
+ const startedAt = new Date(startedAtMs).toISOString();
904
+ const requestedTools = parseToolList(params.tools);
905
+ const effectiveThinking = item.thinking ?? params.thinking;
906
+ startDelegationRun(state.delegations, {
907
+ id: runId,
908
+ parentToolCallId: toolCallId,
909
+ source: "delegate_agent",
910
+ mode: monitor.mode,
911
+ index: monitor.index,
912
+ agent: item.agent,
913
+ task: taskText,
914
+ startedAtMs,
915
+ });
916
+ renderDelegationMonitor();
917
+ const agent = byName.get(item.agent.toLowerCase());
918
+ if (!agent) {
919
+ const result: ChildResult = {
920
+ agent: item.agent,
921
+ task: taskText,
922
+ exitCode: 1,
923
+ output: `Unknown agent '${item.agent}'. Available: ${agents.map((a) => a.name).join(", ") || "none"}`,
924
+ stderr: "",
925
+ ledgerRunId: runId,
926
+ gatePassed: false,
927
+ gateErrors: ["unknown agent"],
928
+ failureKind: "config",
929
+ usage: usageEmpty(),
930
+ };
931
+ const endedAtMs = Date.now();
932
+ const endedAt = new Date(endedAtMs).toISOString();
933
+ appendDelegationLedger({
934
+ event: "config_failed",
935
+ runId,
936
+ mode: state.activeMode,
937
+ ...delegationLedgerMeta("delegate_agent", toolCallId, monitor.mode, monitor.index),
938
+ agent: item.agent,
939
+ taskHash: sha256(taskText),
940
+ cwd: resolveChildCwd(ctx.cwd, item.cwd).cwd,
941
+ tools: requestedTools ?? [],
942
+ errors: ["unknown agent"],
943
+ failureKind: result.failureKind,
944
+ latencyMs: endedAtMs - startedAtMs,
945
+ endedAt,
946
+ });
947
+ writeDelegationTelemetrySummary(ctx.cwd, {
948
+ runId,
949
+ source: "delegate_agent",
950
+ mode: state.activeMode,
951
+ agent: item.agent,
952
+ model: params.model,
953
+ cwd: resolveChildCwd(ctx.cwd, item.cwd).cwd,
954
+ tools: requestedTools ?? [],
955
+ taskHash: sha256(taskText),
956
+ outputContract: inferOutputContract(item.agent),
957
+ status: "unknown_agent",
958
+ gatePassed: false,
959
+ gateErrors: ["unknown agent"],
960
+ failureKind: result.failureKind,
961
+ usage: result.usage,
962
+ latencyMs: endedAtMs - startedAtMs,
963
+ startedAt,
964
+ endedAt,
965
+ });
966
+ finishDelegationRun(state.delegations, runId, {
967
+ status: "preflight_failed",
968
+ endedAtMs,
969
+ outputPreview: result.output,
970
+ stderrPreview: result.stderr,
971
+ exitCode: result.exitCode,
972
+ gatePassed: false,
973
+ gateErrors: ["unknown agent"],
974
+ failureKind: result.failureKind,
975
+ errorMessage: "Configuration blocked; no child launched: unknown agent",
976
+ model: params.model,
977
+ });
978
+ recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result);
979
+ renderDelegationMonitor();
980
+ return result;
981
+ }
982
+
983
+ updateDelegationRun(state.delegations, runId, { agent: agent.name });
984
+ const cwdResult = resolveChildCwd(ctx.cwd, item.cwd);
985
+ const effectiveTools = requestedTools ?? agent.tools ?? [];
986
+ const preflightErrors = [
987
+ ...strictGoalErrors(state),
988
+ ...strictGoalSpecErrors(state, { kind: "delegate_write", taskText, requiredTools: effectiveTools }),
989
+ ...childGoalResolution.errors,
990
+ ...validateSixPartContract(taskText),
991
+ ...validateToolList(agent, requestedTools),
992
+ ...cwdResult.errors,
993
+ ...validateAllowedPathPolicy(params.allowed_paths, "allowed_paths", ctx.cwd),
994
+ ...validateForbiddenPathPolicy(params.forbidden_paths, "forbidden_paths", ctx.cwd),
995
+ ...validateDelegationWriteScope("delegate_agent", effectiveTools, params.allowed_paths),
996
+ ...validateExplicitModelOverride(ctx.cwd, params.model).errors,
997
+ ...validateChildThinkingOverride(effectiveThinking, item.thinking ? "tasks[].thinking" : "thinking"),
998
+ ];
999
+ if (preflightErrors.length > 0) {
1000
+ const result: ChildResult = {
1001
+ agent: agent.name,
1002
+ task: taskText,
1003
+ exitCode: 1,
1004
+ output: `Delegation preflight failed (no child launched):\n- ${preflightErrors.join("\n- ")}`,
1005
+ stderr: "",
1006
+ ledgerRunId: runId,
1007
+ contractErrors: preflightErrors,
1008
+ gatePassed: false,
1009
+ gateErrors: preflightErrors,
1010
+ failureKind: classifyConfigOrPreflight(preflightErrors),
1011
+ usage: usageEmpty(),
1012
+ };
1013
+ const endedAtMs = Date.now();
1014
+ const endedAt = new Date(endedAtMs).toISOString();
1015
+ appendDelegationLedger({
1016
+ event: "preflight_failed",
1017
+ runId,
1018
+ mode: state.activeMode,
1019
+ ...delegationLedgerMeta("delegate_agent", toolCallId, monitor.mode, monitor.index),
1020
+ agent: agent.name,
1021
+ taskHash: sha256(taskText),
1022
+ cwd: cwdResult.cwd,
1023
+ tools: requestedTools ?? agent.tools ?? [],
1024
+ errors: preflightErrors,
1025
+ failureKind: result.failureKind,
1026
+ latencyMs: endedAtMs - startedAtMs,
1027
+ });
1028
+ writeDelegationTelemetrySummary(ctx.cwd, {
1029
+ runId,
1030
+ source: "delegate_agent",
1031
+ mode: state.activeMode,
1032
+ agent: agent.name,
1033
+ model: params.model ?? agent.model,
1034
+ cwd: cwdResult.cwd,
1035
+ tools: requestedTools ?? agent.tools ?? [],
1036
+ taskHash: sha256(taskText),
1037
+ outputContract: inferOutputContract(agent.name),
1038
+ status: "failed_preflight",
1039
+ gatePassed: false,
1040
+ gateErrors: preflightErrors,
1041
+ failureKind: result.failureKind,
1042
+ usage: result.usage,
1043
+ latencyMs: endedAtMs - startedAtMs,
1044
+ startedAt,
1045
+ endedAt,
1046
+ });
1047
+ finishDelegationRun(state.delegations, runId, {
1048
+ status: "preflight_failed",
1049
+ endedAtMs,
1050
+ outputPreview: result.output,
1051
+ stderrPreview: result.stderr,
1052
+ exitCode: result.exitCode,
1053
+ gatePassed: false,
1054
+ gateErrors: preflightErrors,
1055
+ failureKind: result.failureKind,
1056
+ errorMessage: `${result.failureKind === "config" ? "Configuration blocked" : "Preflight blocked"}; no child launched: ${preflightErrors.join("; ")}`,
1057
+ model: params.model ?? agent.model,
1058
+ });
1059
+ recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result);
1060
+ renderDelegationMonitor();
1061
+ return result;
1062
+ }
1063
+
1064
+ linkChildGoalTodoDelegationIfReady(pi, state, effectiveChildGoal, runId, agent.name);
1065
+ renderDelegationMonitor();
1066
+ const outputContract = inferOutputContract(agent.name);
1067
+ appendDelegationLedger({
1068
+ event: "start",
1069
+ runId,
1070
+ mode: state.activeMode,
1071
+ ...delegationLedgerMeta("delegate_agent", toolCallId, monitor.mode, monitor.index),
1072
+ agent: agent.name,
1073
+ model: params.model ?? agent.model,
1074
+ cwd: cwdResult.cwd,
1075
+ tools: requestedTools ?? agent.tools ?? [],
1076
+ taskHash: sha256(taskText),
1077
+ originalUserAskHash: state.activeGoal ? sha256(state.activeGoal.originalUserAsk) : undefined,
1078
+ outputContract,
1079
+ startedAt,
1080
+ });
1081
+
1082
+ const effectiveChildTools = requestedTools ?? agent.tools ?? [];
1083
+ const childPathPolicy = { allowedPaths: params.allowed_paths, forbiddenPaths: params.forbidden_paths };
1084
+ const beforeChildDirty = toolsEnableWrites(effectiveChildTools) ? captureZcommitChildDirtySnapshot(ctx.cwd, childPathPolicy) : undefined;
1085
+ const result = await runChildAgent(ctx, agent, taskText, cwdResult.cwd, signal, params.model, requestedTools?.join(","), (partial) => {
1086
+ updateDelegationRun(state.delegations, runId, {
1087
+ status: partial.stopReason === "aborted" ? "aborted" : "running",
1088
+ agent: partial.agent,
1089
+ model: partial.model,
1090
+ outputPreview: partial.output,
1091
+ stderrPreview: partial.stderr,
1092
+ sessionPath: partial.sessionPath,
1093
+ stopReason: partial.stopReason,
1094
+ errorMessage: partial.errorMessage,
1095
+ usage: partial.usage,
1096
+ });
1097
+ renderDelegationMonitor();
1098
+ update?.(partial);
1099
+ }, childPathPolicy, effectiveThinking);
1100
+ result.childChangedPaths = captureChildDirtyDelta(ctx.cwd, childPathPolicy, beforeChildDirty);
1101
+ result.ledgerRunId = runId;
1102
+ result.outputContract = outputContract;
1103
+ result.contractErrors = [];
1104
+ applyChildGates(result, { repoRoot: ctx.cwd });
1105
+ retargetTodoSplitRequestResult(result, effectiveChildGoal, ctx.cwd);
1106
+ result.failureKind = classifyChildFailure(result);
1107
+ const outputHash = result.output ? sha256(result.output) : undefined;
1108
+ const claimRecord = recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result, { runId, outputHash });
1109
+ const endedAtMs = Date.now();
1110
+ const endedAt = new Date(endedAtMs).toISOString();
1111
+ const status = isFailed(result) ? "incomplete_or_failed" : "complete";
1112
+ const assistantTurnSeen = result.usage.turns > 0 || result.output.trim().length > 0;
1113
+ const evidenceChecked = result.gatePassed === true && outputHasEvidenceMarker(result.output);
1114
+ result.stopCondition = classifyChildStopCondition({
1115
+ status,
1116
+ agent: agent.name,
1117
+ outputContract: result.outputContract,
1118
+ output: result.output,
1119
+ assistantTurnSeen,
1120
+ outputHash,
1121
+ outputCaptured: Boolean(outputHash),
1122
+ outputValidated: result.gatePassed === true,
1123
+ evidenceChecked,
1124
+ }).stopCondition as ChildStopCondition;
1125
+ appendDelegationLedger({
1126
+ event: "end",
1127
+ runId,
1128
+ mode: state.activeMode,
1129
+ ...delegationLedgerMeta("delegate_agent", toolCallId, monitor.mode, monitor.index),
1130
+ agent: agent.name,
1131
+ model: result.model,
1132
+ cwd: cwdResult.cwd,
1133
+ tools: requestedTools ?? agent.tools ?? [],
1134
+ taskHash: sha256(taskText),
1135
+ sessionPath: result.sessionPath,
1136
+ exitCode: result.exitCode,
1137
+ stopReason: result.stopReason,
1138
+ stopCondition: result.stopCondition,
1139
+ status,
1140
+ outputContract: result.outputContract,
1141
+ gatePassed: result.gatePassed,
1142
+ gateErrors: result.gateErrors ?? [],
1143
+ failureKind: result.failureKind,
1144
+ outputHash,
1145
+ childChangedPathRefs: result.childChangedPaths,
1146
+ childChangedPathHashes: result.childChangedPaths.map((ref) => ref.pathHash),
1147
+ chronicle: classifyDelegationChronicleCompletion({
1148
+ runId,
1149
+ source: "delegate_agent",
1150
+ mode: state.activeMode,
1151
+ agent: agent.name,
1152
+ cwd: cwdResult.cwd,
1153
+ tools: requestedTools ?? agent.tools ?? [],
1154
+ taskHash: sha256(taskText),
1155
+ outputHash,
1156
+ outputContract: result.outputContract,
1157
+ status,
1158
+ stopCondition: result.stopCondition,
1159
+ gatePassed: result.gatePassed,
1160
+ gateErrors: result.gateErrors ?? [],
1161
+ assistantTurnSeen,
1162
+ outputCaptured: Boolean(outputHash),
1163
+ outputValidated: result.gatePassed === true,
1164
+ evidenceChecked,
1165
+ usage: result.usage,
1166
+ latencyMs: endedAtMs - startedAtMs,
1167
+ startedAt,
1168
+ endedAt,
1169
+ sessionPath: result.sessionPath,
1170
+ }),
1171
+ usage: result.usage,
1172
+ latencyMs: endedAtMs - startedAtMs,
1173
+ endedAt,
1174
+ });
1175
+ writeDelegationTelemetrySummary(ctx.cwd, {
1176
+ runId,
1177
+ source: "delegate_agent",
1178
+ mode: state.activeMode,
1179
+ agent: agent.name,
1180
+ model: result.model,
1181
+ cwd: cwdResult.cwd,
1182
+ tools: requestedTools ?? agent.tools ?? [],
1183
+ taskHash: sha256(taskText),
1184
+ outputHash,
1185
+ outputContract: result.outputContract,
1186
+ status,
1187
+ stopCondition: result.stopCondition,
1188
+ gatePassed: result.gatePassed,
1189
+ gateErrors: result.gateErrors ?? [],
1190
+ failureKind: result.failureKind,
1191
+ assistantTurnSeen,
1192
+ outputCaptured: Boolean(outputHash),
1193
+ outputValidated: result.gatePassed === true,
1194
+ evidenceChecked,
1195
+ usage: result.usage,
1196
+ latencyMs: endedAtMs - startedAtMs,
1197
+ startedAt,
1198
+ endedAt,
1199
+ sessionPath: result.sessionPath,
1200
+ });
1201
+ finishDelegationRun(state.delegations, runId, {
1202
+ status: result.stopReason === "aborted" ? "aborted" : isFailed(result) ? "failed" : "complete",
1203
+ endedAtMs,
1204
+ outputPreview: result.output,
1205
+ stderrPreview: result.stderr,
1206
+ sessionPath: result.sessionPath,
1207
+ exitCode: result.exitCode,
1208
+ gatePassed: result.gatePassed,
1209
+ gateErrors: result.gateErrors ?? [],
1210
+ failureKind: result.failureKind,
1211
+ stopReason: result.stopReason,
1212
+ stopCondition: result.stopCondition,
1213
+ errorMessage: childFailureMessage(result.failureKind, result.gateErrors, result.errorMessage),
1214
+ childChangedPaths: result.childChangedPaths,
1215
+ usage: result.usage,
1216
+ model: result.model,
1217
+ });
1218
+ if (shouldRunAgenticClaimValidation(effectiveChildGoal, claimRecord)) {
1219
+ await runAgenticTodoClaimValidation({ ctx, pi, state, childGoal: effectiveChildGoal, claimRecord, parentRunId: runId, appendDelegationLedger, signal, modelOverride: params.model, allowedPaths: params.allowed_paths, forbiddenPaths: params.forbidden_paths });
1220
+ }
1221
+ renderDelegationMonitor();
1222
+ return result;
1223
+ };
1224
+
1225
+ if (params.agent && params.task) {
1226
+ startMonitorTicker();
1227
+ try {
1228
+ const result = await runOne({ agent: params.agent, task: params.task, thinking: params.thinking, child_goal: params.child_goal }, { mode: "single", index: 0 }, (partial) => {
1229
+ onUpdate?.({ content: [{ type: "text", text: partial.output || partial.stderr || "running..." }], details: makeDetails("single", [partial]) });
1230
+ });
1231
+ return {
1232
+ content: [{ type: "text", text: isFailed(result) ? `Agent failed or incomplete:\n\n${formatChildResultText(result)}` : result.output || "(no output)" }],
1233
+ details: makeDetails("single", [result]),
1234
+ };
1235
+ } finally {
1236
+ stopMonitorTicker();
1237
+ }
1238
+ }
1239
+
1240
+ if (params.tasks && params.tasks.length > 0) {
1241
+ if (params.tasks.length > 8) {
1242
+ return { content: [{ type: "text", text: "Too many parallel tasks. Max is 8." }], details: makeDetails("parallel", []) };
1243
+ }
1244
+ const partials: ChildResult[] = [];
1245
+ startMonitorTicker();
1246
+ try {
1247
+ const results = await mapWithConcurrency(params.tasks, 4, async (task, index) => {
1248
+ const result = await runOne(task, { mode: "parallel", index }, (partial) => {
1249
+ partials[index] = partial;
1250
+ onUpdate?.({ content: [{ type: "text", text: `Parallel delegation running: ${partials.filter(Boolean).length}/${params.tasks?.length ?? 0} updated` }], details: makeDetails("parallel", partials.filter(Boolean)) });
1251
+ });
1252
+ partials[index] = result;
1253
+ return result;
1254
+ });
1255
+ const successCount = results.filter((result) => !isFailed(result)).length;
1256
+ const summaries = results.map((result) => `### ${result.agent} — ${isFailed(result) ? "FAILED/INCOMPLETE" : "OK"}\n\n${capOutput(formatChildResultText(result))}`);
1257
+ return {
1258
+ content: [{ type: "text", text: `Parallel delegation: ${successCount}/${results.length} succeeded\n\n${summaries.join("\n\n---\n\n")}` }],
1259
+ details: makeDetails("parallel", results),
1260
+ };
1261
+ } finally {
1262
+ stopMonitorTicker();
1263
+ }
1264
+ }
1265
+
1266
+ if (params.chain && params.chain.length > 0) {
1267
+ const results: ChildResult[] = [];
1268
+ let previous = "";
1269
+ startMonitorTicker();
1270
+ try {
1271
+ for (const [index, step] of params.chain.entries()) {
1272
+ const task = step.task.replace(/\{previous\}/g, previous);
1273
+ const result = await runOne({ ...step, task }, { mode: "chain", index }, (partial) => {
1274
+ onUpdate?.({ content: [{ type: "text", text: `Chain step ${index + 1}/${params.chain?.length ?? 0}: ${partial.agent}` }], details: makeDetails("chain", [...results, partial]) });
1275
+ });
1276
+ results.push(result);
1277
+ if (isFailed(result)) {
1278
+ return {
1279
+ content: [{ type: "text", text: `Chain stopped at step ${index + 1} (${result.agent}):\n\n${formatChildResultText(result)}` }],
1280
+ details: makeDetails("chain", results),
1281
+ };
1282
+ }
1283
+ previous = result.output;
1284
+ }
1285
+ return { content: [{ type: "text", text: previous || "(no output)" }], details: makeDetails("chain", results) };
1286
+ } finally {
1287
+ stopMonitorTicker();
1288
+ }
1289
+ }
1290
+
1291
+ return { content: [{ type: "text", text: "Invalid delegation parameters." }], details: makeDetails("single", []) };
1292
+ },
1293
+ });
1294
+
1295
+ pi.registerTool({
1296
+ name: "delegate_task",
1297
+ label: "Delegate Task",
1298
+ description: [
1299
+ "Strict single-task delegation API for ZOB specialist agents.",
1300
+ "Requires the six-part contract fields as structured parameters, validates tools/cwd/paths, logs a ledger entry, and gates child output.",
1301
+ "run_in_background starts active-session background execution and returns a run id; get_delegation_run/await_delegation_run inspect it without starting an always-on daemon.",
1302
+ ].join(" "),
1303
+ promptSnippet: "Delegate one atomic task with a mandatory six-part ZOB contract",
1304
+ promptGuidelines: [
1305
+ "If agent/output_contract routing is uncertain, call zob_delegation_catalog before the first delegation.",
1306
+ "Use delegate_task when you need strict preflight rather than a freeform delegated prompt.",
1307
+ "Normally omit output_contract; the harness infers it from the selected agent. Do not invent output contract ids.",
1308
+ "Normally omit required_tools; the harness infers the selected agent's declared tools. Only set required_tools to intentionally narrow tools.",
1309
+ "If effective tools include edit/write, set top-level original_user_ask to the original human request; context or task text does not satisfy the strict write preflight gate.",
1310
+ "Use canonical JSON keys expected_outcome, must_do, must_not_do, context, original_user_ask, allowed_paths, forbidden_paths; safe aliases are accepted only when non-conflicting.",
1311
+ "Accepted aliases: expectedOutcome, mustDo, mustNotDo/must_not/mustNot, originalUserAsk, allowedPaths, forbiddenPaths, requiredTools, outputContract, runInBackground, childGoal, loadSkills.",
1312
+ "Always set expected_outcome, must_do, must_not_do, context, repo-relative-only allowed_paths, and deny-only forbidden_paths when known. Use reports/... snapshot/context_ref refs instead of external allowed_paths.",
1313
+ "For implementer/QA, require the exact output-contract headings and final line marker so format repair does not look like a failed subagent.",
1314
+ ],
1315
+ parameters: DelegateTaskParams,
1316
+ renderCall(args, theme) {
1317
+ return new Text(`${theme.fg("toolTitle", theme.bold("delegate_task"))} ${theme.fg("accent", `single → ${args.agent}`)}`, 0, 0);
1318
+ },
1319
+ renderResult(result, { expanded, isPartial }, theme, context) {
1320
+ const details = asDelegationDetails(result.details);
1321
+ const toolCallId = (context as { toolCallId?: string }).toolCallId;
1322
+ return new Text(renderDelegationToolResultText("delegate_task", details, state, toolCallId, isPartial, expanded, theme), 0, 0);
1323
+ },
1324
+ async execute(toolCallId, rawParams, signal, onUpdate, ctx) {
1325
+ const normalized = normalizeDelegateTaskParams(rawParams as DelegateTaskAliasInput);
1326
+ const params = normalized.params;
1327
+ const scope = params.scope ?? "project";
1328
+ const agents = discoverAgents(ctx.cwd, scope);
1329
+ const agent = agents.find((candidate) => candidate.name.toLowerCase() === params.agent.toLowerCase());
1330
+ const runId = newRunId("task");
1331
+ const childGoalResolution = resolveChildGoalTodoRef(state, params.child_goal);
1332
+ const effectiveChildGoal = childGoalResolution.childGoal;
1333
+ const startedAtMs = Date.now();
1334
+ const startedAt = new Date(startedAtMs).toISOString();
1335
+ const appendDelegationLedger = (entry: Record<string, unknown>): void => {
1336
+ appendLedgerFile(ctx.cwd, entry);
1337
+ pi.appendEntry("zob-delegation", entry);
1338
+ };
1339
+ const renderDelegationMonitor = (): void => {
1340
+ if (ctx.hasUI) renderHarnessWidget(pi, state, ctx);
1341
+ };
1342
+ const notifyBackgroundDelegationSettled = (status: "complete" | "failed", entry: Record<string, unknown>): void => {
1343
+ const hashLabel = typeof entry.outputHash === "string" ? ` outputHash=${entry.outputHash}` : typeof entry.errorHash === "string" ? ` errorHash=${entry.errorHash}` : "";
1344
+ // Background settlement is evidence/UI metadata only. Do not call
1345
+ // pi.sendMessage(triggerTurn=true) from an async background promise:
1346
+ // it can race active tool-call turns and corrupt provider function-call
1347
+ // state. Parent agents must inspect explicitly with get_delegation_run.
1348
+ if (ctx.hasUI) ctx.ui.notify(`delegate_task background ${status}: ${runId}${hashLabel}. Inspect with get_delegation_run.`, status === "complete" ? "info" : "warning");
1349
+ };
1350
+ let monitorTicker: NodeJS.Timeout | undefined;
1351
+ const startMonitorTicker = (): void => {
1352
+ if (!ctx.hasUI || monitorTicker) return;
1353
+ monitorTicker = setInterval(() => {
1354
+ if (hasActiveDelegations(state.delegations)) renderHarnessWidget(pi, state, ctx);
1355
+ }, 1000);
1356
+ monitorTicker.unref();
1357
+ };
1358
+ const stopMonitorTicker = (): void => {
1359
+ if (monitorTicker) clearInterval(monitorTicker);
1360
+ monitorTicker = undefined;
1361
+ renderDelegationMonitor();
1362
+ };
1363
+ startDelegationRun(state.delegations, {
1364
+ id: runId,
1365
+ parentToolCallId: toolCallId,
1366
+ source: "delegate_task",
1367
+ mode: "single",
1368
+ index: 0,
1369
+ agent: params.agent,
1370
+ task: params.task,
1371
+ startedAtMs,
1372
+ });
1373
+ renderDelegationMonitor();
1374
+ startMonitorTicker();
1375
+
1376
+ try {
1377
+ if (!agent) {
1378
+ const result: ChildResult = {
1379
+ agent: params.agent,
1380
+ task: params.task,
1381
+ exitCode: 1,
1382
+ output: `Unknown agent '${params.agent}'. Available: ${agents.map((candidate) => candidate.name).join(", ") || "none"}`,
1383
+ stderr: "",
1384
+ ledgerRunId: runId,
1385
+ gatePassed: false,
1386
+ gateErrors: ["unknown agent"],
1387
+ failureKind: "config",
1388
+ usage: usageEmpty(),
1389
+ };
1390
+ const endedAtMs = Date.now();
1391
+ const endedAt = new Date(endedAtMs).toISOString();
1392
+ appendDelegationLedger({
1393
+ event: "config_failed",
1394
+ runId,
1395
+ mode: state.activeMode,
1396
+ ...delegationLedgerMeta("delegate_task", toolCallId, "single", 0),
1397
+ agent: params.agent,
1398
+ taskHash: sha256(params.task),
1399
+ cwd: resolveChildCwd(ctx.cwd, params.cwd).cwd,
1400
+ tools: params.required_tools ?? [],
1401
+ errors: ["unknown agent"],
1402
+ failureKind: result.failureKind,
1403
+ latencyMs: endedAtMs - startedAtMs,
1404
+ endedAt,
1405
+ });
1406
+ writeDelegationTelemetrySummary(ctx.cwd, {
1407
+ runId,
1408
+ source: "delegate_task",
1409
+ mode: state.activeMode,
1410
+ agent: params.agent,
1411
+ model: params.model,
1412
+ cwd: resolveChildCwd(ctx.cwd, params.cwd).cwd,
1413
+ tools: params.required_tools ?? [],
1414
+ taskHash: sha256(params.task),
1415
+ outputContract: params.output_contract ?? inferOutputContract(params.agent),
1416
+ status: "unknown_agent",
1417
+ gatePassed: false,
1418
+ gateErrors: ["unknown agent"],
1419
+ failureKind: result.failureKind,
1420
+ usage: result.usage,
1421
+ latencyMs: endedAtMs - startedAtMs,
1422
+ startedAt,
1423
+ endedAt,
1424
+ });
1425
+ finishDelegationRun(state.delegations, runId, {
1426
+ status: "preflight_failed",
1427
+ endedAtMs,
1428
+ outputPreview: result.output,
1429
+ stderrPreview: result.stderr,
1430
+ exitCode: result.exitCode,
1431
+ gatePassed: false,
1432
+ gateErrors: ["unknown agent"],
1433
+ failureKind: result.failureKind,
1434
+ errorMessage: "Configuration blocked; no child launched: unknown agent",
1435
+ model: params.model,
1436
+ });
1437
+ recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result);
1438
+ stopMonitorTicker();
1439
+ return { content: [{ type: "text", text: formatChildResultText(result) }], details: { mode: "single", results: [result], agents: agents.map((candidate) => candidate.name) } };
1440
+ }
1441
+
1442
+ updateDelegationRun(state.delegations, runId, { agent: agent.name });
1443
+ const requestedOutputContract = params.output_contract ?? inferOutputContract(agent.name);
1444
+ const effectiveTools = params.required_tools?.length ? params.required_tools : agent.tools ?? [];
1445
+
1446
+ const structuredTask = [
1447
+ `ORIGINAL_USER_ASK: ${params.original_user_ask ?? state.activeGoal?.originalUserAsk ?? "Not set"}`,
1448
+ params.output_contract ? `OUTPUT_CONTRACT: ${params.output_contract}` : undefined,
1449
+ params.allowed_paths?.length ? `ALLOWED_PATHS: ${params.allowed_paths.join(", ")}` : undefined,
1450
+ params.forbidden_paths?.length ? `FORBIDDEN_PATHS: ${params.forbidden_paths.join(", ")}` : undefined,
1451
+ ...childGoalGuidance(effectiveChildGoal, state.runtimeGoal?.goalId, runId),
1452
+ "",
1453
+ `1. TASK: ${params.task}`,
1454
+ `2. EXPECTED OUTCOME: ${params.expected_outcome}`,
1455
+ `3. REQUIRED TOOLS: ${effectiveTools.join(", ") || "agent default"}`,
1456
+ `4. MUST DO:\n${params.must_do.map((item) => ` - ${item}`).join("\n")}`,
1457
+ `5. MUST NOT DO:\n${params.must_not_do.map((item) => ` - ${item}`).join("\n")}`,
1458
+ `6. CONTEXT: ${params.context}`,
1459
+ "",
1460
+ ...finalFormatGuidance(requestedOutputContract),
1461
+ ]
1462
+ .filter((part): part is string => typeof part === "string")
1463
+ .join("\n");
1464
+
1465
+ const cwdResult = resolveChildCwd(ctx.cwd, params.cwd);
1466
+ const preflightErrors = [
1467
+ ...normalized.errors,
1468
+ ...strictGoalErrors(state),
1469
+ ...strictGoalSpecErrors(state, { kind: "delegate_write", originalUserAsk: params.original_user_ask, taskText: structuredTask, requiredTools: effectiveTools }),
1470
+ ...childGoalResolution.errors,
1471
+ ...validateSixPartContract(structuredTask),
1472
+ ...validateToolList(agent, params.required_tools),
1473
+ ...validateOutputContractId(params.output_contract),
1474
+ ...cwdResult.errors,
1475
+ ...validateAllowedPathPolicy(params.allowed_paths, "allowed_paths", ctx.cwd),
1476
+ ...validateForbiddenPathPolicy(params.forbidden_paths, "forbidden_paths", ctx.cwd),
1477
+ ...validateDelegateTaskWriteScope(effectiveTools, params.allowed_paths),
1478
+ ...validateExplicitModelOverride(ctx.cwd, params.model).errors,
1479
+ ...validateChildThinkingOverride(params.thinking),
1480
+ ];
1481
+ if ((params.load_skills?.length ?? 0) > 0) preflightErrors.push("load_skills is reserved for a future explicit skill-loading gate; use [] for P0");
1482
+
1483
+ if (preflightErrors.length > 0) {
1484
+ const result: ChildResult = {
1485
+ agent: agent.name,
1486
+ task: structuredTask,
1487
+ exitCode: 1,
1488
+ output: delegateTaskPreflightHelp(preflightErrors),
1489
+ stderr: "",
1490
+ ledgerRunId: runId,
1491
+ contractErrors: preflightErrors,
1492
+ gatePassed: false,
1493
+ gateErrors: preflightErrors,
1494
+ failureKind: classifyConfigOrPreflight(preflightErrors),
1495
+ usage: usageEmpty(),
1496
+ };
1497
+ const endedAtMs = Date.now();
1498
+ const endedAt = new Date(endedAtMs).toISOString();
1499
+ appendDelegationLedger({
1500
+ event: "preflight_failed",
1501
+ runId,
1502
+ mode: state.activeMode,
1503
+ ...delegationLedgerMeta("delegate_task", toolCallId, "single", 0),
1504
+ agent: agent.name,
1505
+ taskHash: sha256(structuredTask),
1506
+ cwd: cwdResult.cwd,
1507
+ tools: effectiveTools,
1508
+ errors: preflightErrors,
1509
+ failureKind: result.failureKind,
1510
+ latencyMs: endedAtMs - startedAtMs,
1511
+ });
1512
+ writeDelegationTelemetrySummary(ctx.cwd, {
1513
+ runId,
1514
+ source: "delegate_task",
1515
+ mode: state.activeMode,
1516
+ agent: agent.name,
1517
+ model: params.model ?? agent.model,
1518
+ cwd: cwdResult.cwd,
1519
+ tools: effectiveTools,
1520
+ taskHash: sha256(structuredTask),
1521
+ outputContract: params.output_contract ?? inferOutputContract(agent.name),
1522
+ status: "failed_preflight",
1523
+ gatePassed: false,
1524
+ gateErrors: preflightErrors,
1525
+ failureKind: result.failureKind,
1526
+ usage: result.usage,
1527
+ latencyMs: endedAtMs - startedAtMs,
1528
+ startedAt,
1529
+ endedAt,
1530
+ });
1531
+ finishDelegationRun(state.delegations, runId, {
1532
+ status: "preflight_failed",
1533
+ endedAtMs,
1534
+ outputPreview: result.output,
1535
+ stderrPreview: result.stderr,
1536
+ exitCode: result.exitCode,
1537
+ gatePassed: false,
1538
+ gateErrors: preflightErrors,
1539
+ failureKind: result.failureKind,
1540
+ errorMessage: delegateTaskPreflightHelp(preflightErrors).replace(/\n/g, " "),
1541
+ model: params.model ?? agent.model,
1542
+ });
1543
+ recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result);
1544
+ stopMonitorTicker();
1545
+ return { content: [{ type: "text", text: formatChildResultText(result) }], details: { mode: "single", results: [result], agents: agents.map((candidate) => candidate.name) } };
1546
+ }
1547
+
1548
+ const runDelegateTaskChild = async (childSignal: AbortSignal | undefined, emitToolUpdates: boolean): Promise<ChildResult> => {
1549
+ const outputContract = requestedOutputContract;
1550
+ linkChildGoalTodoDelegationIfReady(pi, state, effectiveChildGoal, runId, agent.name);
1551
+ renderDelegationMonitor();
1552
+ appendDelegationLedger({
1553
+ event: "start",
1554
+ runId,
1555
+ mode: state.activeMode,
1556
+ ...delegationLedgerMeta("delegate_task", toolCallId, "single", 0),
1557
+ agent: agent.name,
1558
+ model: params.model ?? agent.model,
1559
+ cwd: cwdResult.cwd,
1560
+ tools: effectiveTools,
1561
+ taskHash: sha256(structuredTask),
1562
+ originalUserAskHash: sha256(params.original_user_ask ?? state.activeGoal?.originalUserAsk ?? ""),
1563
+ outputContract,
1564
+ startedAt,
1565
+ });
1566
+
1567
+ const childPathPolicy = { allowedPaths: params.allowed_paths, forbiddenPaths: params.forbidden_paths };
1568
+ const beforeChildDirty = toolsEnableWrites(effectiveTools) ? captureZcommitChildDirtySnapshot(ctx.cwd, childPathPolicy) : undefined;
1569
+ const result = await runChildAgent(ctx, agent, structuredTask, cwdResult.cwd, childSignal, params.model, effectiveTools.join(","), (partial) => {
1570
+ updateDelegationRun(state.delegations, runId, {
1571
+ status: partial.stopReason === "aborted" ? "aborted" : "running",
1572
+ agent: partial.agent,
1573
+ model: partial.model,
1574
+ outputPreview: partial.output,
1575
+ stderrPreview: partial.stderr,
1576
+ sessionPath: partial.sessionPath,
1577
+ stopReason: partial.stopReason,
1578
+ errorMessage: partial.errorMessage,
1579
+ usage: partial.usage,
1580
+ });
1581
+ renderDelegationMonitor();
1582
+ if (emitToolUpdates) onUpdate?.({ content: [{ type: "text", text: partial.output || partial.stderr || "running..." }], details: { mode: "single", results: [partial], agents: agents.map((candidate) => candidate.name) } });
1583
+ }, childPathPolicy, params.thinking);
1584
+ result.childChangedPaths = captureChildDirtyDelta(ctx.cwd, childPathPolicy, beforeChildDirty);
1585
+ result.ledgerRunId = runId;
1586
+ result.outputContract = outputContract;
1587
+ result.contractErrors = [];
1588
+ applyChildGates(result, { repoRoot: ctx.cwd });
1589
+ retargetTodoSplitRequestResult(result, effectiveChildGoal, ctx.cwd);
1590
+ result.failureKind = classifyChildFailure(result);
1591
+ const outputHash = result.output ? sha256(result.output) : undefined;
1592
+ const claimRecord = recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result, { runId, outputHash });
1593
+ const endedAtMs = Date.now();
1594
+ const endedAt = new Date(endedAtMs).toISOString();
1595
+ const status = isFailed(result) ? "incomplete_or_failed" : "complete";
1596
+ const assistantTurnSeen = result.usage.turns > 0 || result.output.trim().length > 0;
1597
+ const evidenceChecked = result.gatePassed === true && outputHasEvidenceMarker(result.output);
1598
+ result.stopCondition = classifyChildStopCondition({
1599
+ status,
1600
+ agent: agent.name,
1601
+ outputContract: result.outputContract,
1602
+ output: result.output,
1603
+ assistantTurnSeen,
1604
+ outputHash,
1605
+ outputCaptured: Boolean(outputHash),
1606
+ outputValidated: result.gatePassed === true,
1607
+ evidenceChecked,
1608
+ }).stopCondition as ChildStopCondition;
1609
+
1610
+ appendDelegationLedger({
1611
+ event: "end",
1612
+ runId,
1613
+ mode: state.activeMode,
1614
+ ...delegationLedgerMeta("delegate_task", toolCallId, "single", 0),
1615
+ agent: agent.name,
1616
+ model: result.model,
1617
+ cwd: cwdResult.cwd,
1618
+ tools: effectiveTools,
1619
+ taskHash: sha256(structuredTask),
1620
+ sessionPath: result.sessionPath,
1621
+ exitCode: result.exitCode,
1622
+ stopReason: result.stopReason,
1623
+ stopCondition: result.stopCondition,
1624
+ status,
1625
+ outputContract: result.outputContract,
1626
+ gatePassed: result.gatePassed,
1627
+ gateErrors: result.gateErrors ?? [],
1628
+ failureKind: result.failureKind,
1629
+ outputHash,
1630
+ childChangedPathRefs: result.childChangedPaths,
1631
+ childChangedPathHashes: result.childChangedPaths.map((ref) => ref.pathHash),
1632
+ chronicle: classifyDelegationChronicleCompletion({
1633
+ runId,
1634
+ source: "delegate_task",
1635
+ mode: state.activeMode,
1636
+ agent: agent.name,
1637
+ cwd: cwdResult.cwd,
1638
+ tools: effectiveTools,
1639
+ taskHash: sha256(structuredTask),
1640
+ outputHash,
1641
+ outputContract: result.outputContract,
1642
+ status,
1643
+ stopCondition: result.stopCondition,
1644
+ gatePassed: result.gatePassed,
1645
+ gateErrors: result.gateErrors ?? [],
1646
+ assistantTurnSeen,
1647
+ outputCaptured: Boolean(outputHash),
1648
+ outputValidated: result.gatePassed === true,
1649
+ evidenceChecked,
1650
+ usage: result.usage,
1651
+ latencyMs: endedAtMs - startedAtMs,
1652
+ startedAt,
1653
+ endedAt,
1654
+ sessionPath: result.sessionPath,
1655
+ }),
1656
+ usage: result.usage,
1657
+ latencyMs: endedAtMs - startedAtMs,
1658
+ endedAt,
1659
+ });
1660
+ writeDelegationTelemetrySummary(ctx.cwd, {
1661
+ runId,
1662
+ source: "delegate_task",
1663
+ mode: state.activeMode,
1664
+ agent: agent.name,
1665
+ model: result.model,
1666
+ cwd: cwdResult.cwd,
1667
+ tools: effectiveTools,
1668
+ taskHash: sha256(structuredTask),
1669
+ outputHash,
1670
+ outputContract: result.outputContract,
1671
+ status,
1672
+ stopCondition: result.stopCondition,
1673
+ gatePassed: result.gatePassed,
1674
+ gateErrors: result.gateErrors ?? [],
1675
+ failureKind: result.failureKind,
1676
+ assistantTurnSeen,
1677
+ outputCaptured: Boolean(outputHash),
1678
+ outputValidated: result.gatePassed === true,
1679
+ evidenceChecked,
1680
+ usage: result.usage,
1681
+ latencyMs: endedAtMs - startedAtMs,
1682
+ startedAt,
1683
+ endedAt,
1684
+ sessionPath: result.sessionPath,
1685
+ });
1686
+ finishDelegationRun(state.delegations, runId, {
1687
+ status: result.stopReason === "aborted" ? "aborted" : isFailed(result) ? "failed" : "complete",
1688
+ endedAtMs,
1689
+ outputPreview: result.output,
1690
+ stderrPreview: result.stderr,
1691
+ sessionPath: result.sessionPath,
1692
+ exitCode: result.exitCode,
1693
+ gatePassed: result.gatePassed,
1694
+ gateErrors: result.gateErrors ?? [],
1695
+ failureKind: result.failureKind,
1696
+ stopReason: result.stopReason,
1697
+ stopCondition: result.stopCondition,
1698
+ errorMessage: childFailureMessage(result.failureKind, result.gateErrors, result.errorMessage),
1699
+ childChangedPaths: result.childChangedPaths,
1700
+ usage: result.usage,
1701
+ model: result.model,
1702
+ });
1703
+ if (shouldRunAgenticClaimValidation(effectiveChildGoal, claimRecord)) {
1704
+ await runAgenticTodoClaimValidation({ ctx, pi, state, childGoal: effectiveChildGoal, claimRecord, parentRunId: runId, appendDelegationLedger, signal: childSignal, modelOverride: params.model, allowedPaths: params.allowed_paths, forbiddenPaths: params.forbidden_paths });
1705
+ }
1706
+ stopMonitorTicker();
1707
+
1708
+ return result;
1709
+ };
1710
+
1711
+ if (params.run_in_background) {
1712
+ const backgroundController = new AbortController();
1713
+ const backgroundPromise = runDelegateTaskChild(backgroundController.signal, false);
1714
+ const backgroundRun: BackgroundDelegationRuntimeRun = { runId, startedAtMs, promise: backgroundPromise, abortController: backgroundController };
1715
+ state.backgroundDelegations.set(runId, backgroundRun);
1716
+ backgroundPromise
1717
+ .then((result) => {
1718
+ backgroundRun.result = result;
1719
+ const settledStatus = isFailed(result) ? "failed" : "complete";
1720
+ const settledEntry = {
1721
+ event: "background_settled",
1722
+ runId,
1723
+ mode: state.activeMode,
1724
+ ...delegationLedgerMeta("delegate_task", toolCallId, "single", 0),
1725
+ agent: agent.name,
1726
+ status: settledStatus,
1727
+ outputHash: result.output ? sha256(result.output) : undefined,
1728
+ errorHash: settledStatus === "failed" ? sha256(childFailureMessage(result.failureKind, result.gateErrors, result.errorMessage) ?? result.stderr ?? result.output ?? "failed") : undefined,
1729
+ outputContract: result.outputContract,
1730
+ gatePassed: result.gatePassed,
1731
+ failureKind: result.failureKind,
1732
+ bodyStored: false,
1733
+ promptBodiesStored: false,
1734
+ outputBodiesStored: false,
1735
+ endedAt: new Date().toISOString(),
1736
+ };
1737
+ appendDelegationLedger(settledEntry);
1738
+ notifyBackgroundDelegationSettled(settledStatus, settledEntry);
1739
+ renderDelegationMonitor();
1740
+ return result;
1741
+ })
1742
+ .catch((error: unknown) => {
1743
+ const message = error instanceof Error ? error.message : String(error);
1744
+ backgroundRun.error = message;
1745
+ finishDelegationRun(state.delegations, runId, { status: "failed", endedAtMs: Date.now(), errorMessage: message, gatePassed: false, gateErrors: [message], failureKind: "child_runtime", model: params.model ?? agent.model });
1746
+ const settledEntry = {
1747
+ event: "background_settled",
1748
+ runId,
1749
+ mode: state.activeMode,
1750
+ ...delegationLedgerMeta("delegate_task", toolCallId, "single", 0),
1751
+ agent: agent.name,
1752
+ status: "failed",
1753
+ errorHash: sha256(message),
1754
+ failureKind: "child_runtime",
1755
+ bodyStored: false,
1756
+ promptBodiesStored: false,
1757
+ outputBodiesStored: false,
1758
+ endedAt: new Date().toISOString(),
1759
+ };
1760
+ appendDelegationLedger(settledEntry);
1761
+ notifyBackgroundDelegationSettled("failed", settledEntry);
1762
+ renderDelegationMonitor();
1763
+ });
1764
+ return {
1765
+ content: [{ type: "text", text: `delegate_task background started: ${runId}` }],
1766
+ details: { mode: "single", background: true, runId, status: "running", results: [], agents: agents.map((candidate) => candidate.name) },
1767
+ };
1768
+ }
1769
+
1770
+ const result = await runDelegateTaskChild(signal, true);
1771
+ return {
1772
+ content: [{ type: "text", text: isFailed(result) ? `Task failed or incomplete:
1773
+
1774
+ ${formatChildResultText(result)}` : result.output || "(no output)" }],
1775
+ details: { mode: "single", results: [result], agents: agents.map((candidate) => candidate.name) },
1776
+ };
1777
+ } finally {
1778
+ stopMonitorTicker();
1779
+ }
1780
+ },
1781
+ });
1782
+
1783
+
1784
+ pi.registerTool({
1785
+ name: "get_delegation_run",
1786
+ label: "Get Delegation Run",
1787
+ description: "Inspect an active-session delegation run, including background delegate_task runs. Metadata/output preview only; no daemon polling.",
1788
+ promptSnippet: "Inspect a background delegation run before deciding next TODO action.",
1789
+ parameters: DelegationRunParams,
1790
+ async execute(_toolCallId, params) {
1791
+ const run = state.delegations.runs.find((candidate) => candidate.id === params.run_id);
1792
+ const background = state.backgroundDelegations.get(params.run_id);
1793
+ const status = run?.status ?? (background?.result ? "complete" : background?.error ? "failed" : background ? "running" : "not_found");
1794
+ return {
1795
+ content: [{ type: "text", text: `get_delegation_run ${params.run_id}: ${status}` }],
1796
+ details: { schema: "zob.delegation-run-status.v1", runId: params.run_id, status, run, background: background ? { startedAtMs: background.startedAtMs, complete: Boolean(background.result), error: background.error } : undefined, result: background?.result },
1797
+ };
1798
+ },
1799
+ });
1800
+
1801
+ pi.registerTool({
1802
+ name: "await_delegation_run",
1803
+ label: "Await Delegation Run",
1804
+ description: "Bounded passive wait for an active-session background delegate_task run. brief keeps the short cap; long_idle allows a longer bounded idle. Does not start a daemon, continuous loop, or wakeup.",
1805
+ promptSnippet: "Idle briefly or with long_idle while waiting for a background child when no other TODO is actionable.",
1806
+ parameters: AwaitDelegationRunParams,
1807
+ async execute(_toolCallId, params) {
1808
+ const reply = (text: string, details: Record<string, unknown>) => ({ content: [{ type: "text" as const, text }], details });
1809
+ const waitMode = params.wait_mode === "long_idle" ? "long_idle" : "brief";
1810
+ const maxTimeoutMs = waitMode === "long_idle" ? 300_000 : 30_000;
1811
+ const requestedTimeoutMs = Math.floor(params.timeout_ms ?? 5_000);
1812
+ const timeoutMs = Math.max(25, Math.min(maxTimeoutMs, Number.isFinite(requestedTimeoutMs) ? requestedTimeoutMs : 5_000));
1813
+ const includeResult = params.include_result !== false;
1814
+ const awaitMeta = { waitMode, timeoutMs, maxTimeoutMs };
1815
+ const compactResult = (result: ChildResult) => ({
1816
+ agent: result.agent,
1817
+ exitCode: result.exitCode,
1818
+ status: isFailed(result) ? "failed" : "complete",
1819
+ outputHash: result.output ? sha256(result.output) : undefined,
1820
+ outputContract: result.outputContract,
1821
+ gatePassed: result.gatePassed,
1822
+ gateErrors: result.gateErrors ?? [],
1823
+ failureKind: result.failureKind,
1824
+ stopReason: result.stopReason,
1825
+ stopCondition: result.stopCondition,
1826
+ sessionPath: result.sessionPath,
1827
+ });
1828
+ const resultDetails = (result: ChildResult) => includeResult ? { result } : { resultSummary: compactResult(result), resultIncluded: false };
1829
+ const background = state.backgroundDelegations.get(params.run_id);
1830
+ const existingRun = state.delegations.runs.find((candidate) => candidate.id === params.run_id);
1831
+ if (!background) {
1832
+ const status = existingRun?.status ?? "not_found";
1833
+ return reply(`await_delegation_run ${params.run_id}: ${status}`, { schema: "zob.delegation-await.v1", runId: params.run_id, status, timedOut: false, ...awaitMeta, run: existingRun });
1834
+ }
1835
+ if (background.result) return reply(`await_delegation_run complete: ${params.run_id}`, { schema: "zob.delegation-await.v1", runId: params.run_id, status: "complete", timedOut: false, ...awaitMeta, ...resultDetails(background.result) });
1836
+ if (background.error) return reply(`await_delegation_run failed: ${params.run_id}`, { schema: "zob.delegation-await.v1", runId: params.run_id, status: "failed", timedOut: false, ...awaitMeta, error: background.error });
1837
+ let timedOut = false;
1838
+ try {
1839
+ const result = await Promise.race([
1840
+ background.promise,
1841
+ new Promise<undefined>((resolve) => setTimeout(() => { timedOut = true; resolve(undefined); }, timeoutMs)),
1842
+ ]);
1843
+ if (timedOut || !result) return reply(`await_delegation_run timeout: ${params.run_id}`, { schema: "zob.delegation-await.v1", runId: params.run_id, status: "running", timedOut: true, ...awaitMeta, run: state.delegations.runs.find((candidate) => candidate.id === params.run_id) });
1844
+ background.result = result;
1845
+ return reply(`await_delegation_run complete: ${params.run_id}`, { schema: "zob.delegation-await.v1", runId: params.run_id, status: "complete", timedOut: false, ...awaitMeta, ...resultDetails(result) });
1846
+ } catch (error) {
1847
+ const message = error instanceof Error ? error.message : String(error);
1848
+ background.error = message;
1849
+ return reply(`await_delegation_run failed: ${params.run_id}`, { schema: "zob.delegation-await.v1", runId: params.run_id, status: "failed", timedOut: false, ...awaitMeta, error: message });
1850
+ }
1851
+ },
1852
+ });
1853
+
1854
+ }