pi-crew 0.9.50 → 0.9.52

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 (609) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/README.md +216 -20
  3. package/dist/build-meta.json +2222 -1761
  4. package/dist/index.mjs +18636 -17603
  5. package/docs/actions-reference.md +431 -1
  6. package/package.json +19 -9
  7. package/schema.json +41 -0
  8. package/scripts/check-bundle-size.mjs +38 -0
  9. package/scripts/test-runner.mjs +11 -1
  10. package/skills/distill-software/SKILL.md +24 -1
  11. package/dist/index.mjs.map +0 -7
  12. package/docs/FEATURE_INTAKE.md +0 -126
  13. package/docs/HARNESS.md +0 -87
  14. package/docs/HARNESS_BACKLOG.md +0 -89
  15. package/docs/PROJECT_REVIEW.md +0 -270
  16. package/docs/PROJECT_REVIEW_FIXES.md +0 -342
  17. package/docs/PROJECT_REVIEW_ROUND4.md +0 -156
  18. package/docs/PROJECT_REVIEW_ROUND5.md +0 -86
  19. package/docs/REVIEW-FINDINGS-2026-06 +0 -357
  20. package/docs/REVIEW-FINDINGS-2026-07-CORE.md +0 -139
  21. package/docs/REVIEW-FINDINGS-2026-07.md +0 -125
  22. package/docs/TEST_MATRIX.md +0 -51
  23. package/docs/archive/README.md +0 -91
  24. package/docs/archive/followup-review-round3-2026-05-12.md +0 -342
  25. package/docs/archive/followup-review-round4-2026-05-13.md +0 -107
  26. package/docs/archive/pi-crew-bugs.md +0 -961
  27. package/docs/archive/pi-crew-test-final.md +0 -120
  28. package/docs/archive/pi-crew-test-results.md +0 -260
  29. package/docs/archive/pi-crew-test-round2.md +0 -136
  30. package/docs/archive/pi-crew-test-round4.md +0 -100
  31. package/docs/archive/pi-crew-test-round5.md +0 -70
  32. package/docs/archive/pi-crew-test-round6.md +0 -110
  33. package/docs/archive/pi-crew-v0.5.10-audit-fix-plan.md +0 -60
  34. package/docs/archive/pi-crew-v0.5.11-audit-fix-plan.md +0 -92
  35. package/docs/archive/pi-crew-v0.5.12-audit-fix-plan.md +0 -76
  36. package/docs/archive/pi-crew-v0.5.13-audit-fix-plan.md +0 -75
  37. package/docs/archive/pi-crew-v0.5.14-audit-fix-plan.md +0 -75
  38. package/docs/archive/pi-crew-v0.5.16-audit-fix-plan.md +0 -35
  39. package/docs/archive/pi-crew-v0.5.17-audit-fix-plan.md +0 -80
  40. package/docs/archive/pi-crew-v0.5.5-audit-fix-plan.md +0 -133
  41. package/docs/archive/pi-crew-v0.5.9-audit-fix-plan.md +0 -88
  42. package/docs/archive/pi-mono-opportunities.md +0 -969
  43. package/docs/archive/pi-mono-review.md +0 -291
  44. package/docs/archive/pi-subagent4-comparison.md +0 -261
  45. package/docs/archive/pi-subagents3-deep-analysis.md +0 -508
  46. package/docs/bugs/bug-021-notification-badge-counter-misleading.md +0 -293
  47. package/docs/bugs/bug-023-chain-windows-path-resolution.md +0 -106
  48. package/docs/bugs/bug-quota-display-truncation.md +0 -223
  49. package/docs/bugs/cross-session-notification-leakage.md +0 -82
  50. package/docs/code-review-2026-05-11.md +0 -592
  51. package/docs/coding-agent-optimization.md +0 -268
  52. package/docs/comparison-pi-subagents-vs-pi-crew.md +0 -303
  53. package/docs/decisions/0001-durable-state.md +0 -41
  54. package/docs/decisions/0002-child-process-for-async.md +0 -42
  55. package/docs/decisions/0003-depth-guard.md +0 -36
  56. package/docs/decisions/0004-execfile-over-exec.md +0 -34
  57. package/docs/decisions/0005-no-parameter-properties.md +0 -49
  58. package/docs/decisions/0006-publish-bundled-esm.md +0 -63
  59. package/docs/decisions/0007-active-run-binary-index.md +0 -54
  60. package/docs/decisions/0008-child-pi-warm-pool.md +0 -61
  61. package/docs/decisions/2026-07-21-broker-phase4-default-on.md +0 -77
  62. package/docs/decisions/2026-07-21-broker-windows-perms.md +0 -91
  63. package/docs/decisions/2026-07-22-broker-phase4-gated-on.md +0 -99
  64. package/docs/decisions/2026-07-24-oidc-trusted-publishing.md +0 -112
  65. package/docs/decisions/README.md +0 -26
  66. package/docs/deep-review-report.md +0 -384
  67. package/docs/distillation/cybersecurity-patterns.md +0 -294
  68. package/docs/feature-analysis-subagent4.md +0 -305
  69. package/docs/fix-plan-disabletools-exit-null.md +0 -219
  70. package/docs/fixes/BATCH_A_H1_H2.md +0 -86
  71. package/docs/fixes/bug-006-foreground-cancel-concurrent.md +0 -78
  72. package/docs/fixes/bug-007-async-notifier-stale-ctx.md +0 -112
  73. package/docs/fixes/bug-008-child-process-silent-timeout.md +0 -100
  74. package/docs/fixes/bug-009-executor-yield-limit-needs-attention.md +0 -75
  75. package/docs/fixes/bug-010-child-process-api-key-filtered.md +0 -109
  76. package/docs/fixes/bug-011-spawn-pi-enoent.md +0 -92
  77. package/docs/fixes/bug-012-essential-env-stripped.md +0 -89
  78. package/docs/fixes/bug-013-background-runner-death.md +0 -84
  79. package/docs/fixes/bug-014-infinite-retry-loop-needs-attention.md +0 -82
  80. package/docs/fixes/bug-015-background-runner-sigterm.md +0 -65
  81. package/docs/fixes/bug-017-background-runner-session-shutdown.md +0 -66
  82. package/docs/fixes/bug-017-background-runner-sigkill-double-fork.md +0 -28
  83. package/docs/fixes/bug-018-child-pi-worker-stdin-hang.md +0 -61
  84. package/docs/fixes/bug-019-phantom-runs-temp-workspace.md +0 -52
  85. package/docs/fixes/bug-020-infinite-retry-loop-needs-attention.md +0 -47
  86. package/docs/fixes/v0.9.10/locks-fix-verify.md +0 -3
  87. package/docs/fixes/v0.9.10/smoke-test.md +0 -12
  88. package/docs/followup-plan-2026-05-12.md +0 -463
  89. package/docs/followup-review-2026-05-12.md +0 -297
  90. package/docs/goals.md +0 -59
  91. package/docs/implementation-plan-top3.md +0 -333
  92. package/docs/issue-29-analysis.md +0 -189
  93. package/docs/live-mailbox-runtime.md +0 -36
  94. package/docs/migration/atomic-write-v2-migration.md +0 -299
  95. package/docs/next-upgrade-roadmap.md +0 -808
  96. package/docs/oh-my-pi-research.md +0 -509
  97. package/docs/optimization-plan-2026-07.md +0 -366
  98. package/docs/optimization-plan.md +0 -644
  99. package/docs/patterns/command-agent-skill.md +0 -71
  100. package/docs/perf/baseline-2026-05.md +0 -113
  101. package/docs/perf/final-report-2026-05.md +0 -206
  102. package/docs/perf/optimization-plan-2026-07-verified.md +0 -396
  103. package/docs/perf/performance-audit-report-2026-07.md +0 -948
  104. package/docs/perf/performance-review-2026-07.md +0 -287
  105. package/docs/perf/sprint-1-report.md +0 -71
  106. package/docs/perf/sprint-2-report.md +0 -81
  107. package/docs/perf/sprint-2.5-report.md +0 -53
  108. package/docs/perf/sprint-3-report.md +0 -36
  109. package/docs/perf/sprint-4-report.md +0 -47
  110. package/docs/perf/sprint-5-report.md +0 -51
  111. package/docs/perf/sprint-6-report.md +0 -94
  112. package/docs/perf/sprint-7-report.md +0 -74
  113. package/docs/perf/upgrade-plan-2026-05.md +0 -147
  114. package/docs/performance-optimization-execution-plan.md +0 -520
  115. package/docs/phase4-triage.md +0 -58
  116. package/docs/pi-crew-investigation-report.md +0 -411
  117. package/docs/product/README.md +0 -31
  118. package/docs/product/platform.md +0 -27
  119. package/docs/product/runtime-safety.md +0 -37
  120. package/docs/product/team-run.md +0 -39
  121. package/docs/product/team-tool.md +0 -37
  122. package/docs/runtime-analysis-child-vs-live.md +0 -171
  123. package/docs/runtime-migration-in-process-analysis.md +0 -250
  124. package/docs/skills/REFERENCE.md +0 -138
  125. package/docs/stories/README.md +0 -32
  126. package/docs/stories/US-DEPS-major-upgrade.md +0 -62
  127. package/docs/stories/backlog.md +0 -36
  128. package/docs/superpowers/plans/2026-06-09-fallow-patterns-adoption.md +0 -1270
  129. package/docs/templates/decision.md +0 -27
  130. package/docs/templates/story.md +0 -44
  131. package/docs/templates/validation-report.md +0 -32
  132. package/docs/ui-optimization-plan.md +0 -447
  133. package/src/adapters/claude-adapter.ts +0 -23
  134. package/src/adapters/codex-adapter.ts +0 -21
  135. package/src/adapters/cursor-adapter.ts +0 -17
  136. package/src/adapters/export-util.ts +0 -143
  137. package/src/adapters/index.ts +0 -15
  138. package/src/adapters/registry.ts +0 -18
  139. package/src/adapters/types.ts +0 -23
  140. package/src/agents/agent-config.ts +0 -194
  141. package/src/agents/agent-search.ts +0 -98
  142. package/src/agents/agent-serializer.ts +0 -38
  143. package/src/agents/discover-agents.ts +0 -623
  144. package/src/benchmark/benchmark-runner.ts +0 -313
  145. package/src/benchmark/feedback-loop.ts +0 -73
  146. package/src/config/config.ts +0 -1269
  147. package/src/config/defaults.ts +0 -193
  148. package/src/config/drift-detector.ts +0 -290
  149. package/src/config/markers.ts +0 -330
  150. package/src/config/resilient-parser.ts +0 -117
  151. package/src/config/role-tools.ts +0 -118
  152. package/src/config/suggestions.ts +0 -75
  153. package/src/config/types.ts +0 -278
  154. package/src/errors.ts +0 -194
  155. package/src/extension/action-suggestions.ts +0 -117
  156. package/src/extension/async-notifier.ts +0 -179
  157. package/src/extension/autonomous-policy.ts +0 -209
  158. package/src/extension/command-completions.ts +0 -120
  159. package/src/extension/context-status-injection.ts +0 -185
  160. package/src/extension/crew-autocomplete.ts +0 -131
  161. package/src/extension/crew-cleanup.ts +0 -168
  162. package/src/extension/crew-input-router.ts +0 -93
  163. package/src/extension/crew-shortcuts.ts +0 -87
  164. package/src/extension/crew-vibes/cat-frames.ts +0 -18
  165. package/src/extension/crew-vibes/config.ts +0 -195
  166. package/src/extension/crew-vibes/figures.ts +0 -101
  167. package/src/extension/crew-vibes/font-detect.ts +0 -71
  168. package/src/extension/crew-vibes/footer.ts +0 -292
  169. package/src/extension/crew-vibes/index.ts +0 -432
  170. package/src/extension/crew-vibes/provider-usage.ts +0 -396
  171. package/src/extension/crew-vibes/render.ts +0 -206
  172. package/src/extension/crew-vibes/speed.ts +0 -286
  173. package/src/extension/cross-extension-rpc.ts +0 -350
  174. package/src/extension/help.ts +0 -62
  175. package/src/extension/import-index.ts +0 -74
  176. package/src/extension/knowledge-injection.ts +0 -402
  177. package/src/extension/management.ts +0 -571
  178. package/src/extension/message-renderers.ts +0 -108
  179. package/src/extension/notification-router.ts +0 -152
  180. package/src/extension/notification-sink.ts +0 -54
  181. package/src/extension/pi-api.ts +0 -56
  182. package/src/extension/plan-orchestrate.ts +0 -302
  183. package/src/extension/project-init.ts +0 -170
  184. package/src/extension/register.ts +0 -113
  185. package/src/extension/registration/artifact-cleanup.ts +0 -20
  186. package/src/extension/registration/command-registration.ts +0 -58
  187. package/src/extension/registration/command-utils.ts +0 -58
  188. package/src/extension/registration/commands.ts +0 -1228
  189. package/src/extension/registration/compaction-guard.ts +0 -333
  190. package/src/extension/registration/context-builder.ts +0 -135
  191. package/src/extension/registration/crash-recovery-cache.ts +0 -54
  192. package/src/extension/registration/foreground-run-controller.ts +0 -238
  193. package/src/extension/registration/hook-registration.ts +0 -119
  194. package/src/extension/registration/lazy-configurers.ts +0 -124
  195. package/src/extension/registration/lifecycle-handlers.ts +0 -923
  196. package/src/extension/registration/lifecycle.ts +0 -259
  197. package/src/extension/registration/observability.ts +0 -322
  198. package/src/extension/registration/registration-types.ts +0 -158
  199. package/src/extension/registration/runtime-cleanup.ts +0 -230
  200. package/src/extension/registration/subagent-helpers.ts +0 -125
  201. package/src/extension/registration/subagent-manager-setup.ts +0 -330
  202. package/src/extension/registration/subagent-tools.ts +0 -456
  203. package/src/extension/registration/team-tool.ts +0 -220
  204. package/src/extension/registration/tool-registration.ts +0 -50
  205. package/src/extension/registration/ui.ts +0 -172
  206. package/src/extension/registration/viewers.ts +0 -121
  207. package/src/extension/registration/wire-cross-extension.ts +0 -37
  208. package/src/extension/result-watcher.ts +0 -139
  209. package/src/extension/rpc-hmac.ts +0 -256
  210. package/src/extension/run-bundle-schema.ts +0 -104
  211. package/src/extension/run-export.ts +0 -97
  212. package/src/extension/run-import.ts +0 -168
  213. package/src/extension/run-index.ts +0 -131
  214. package/src/extension/run-maintenance.ts +0 -190
  215. package/src/extension/session-summary.ts +0 -18
  216. package/src/extension/team-manager-command.ts +0 -153
  217. package/src/extension/team-onboard.ts +0 -177
  218. package/src/extension/team-recommendation.ts +0 -288
  219. package/src/extension/team-tool/anchor.ts +0 -172
  220. package/src/extension/team-tool/api.ts +0 -1244
  221. package/src/extension/team-tool/auto-summarize.ts +0 -142
  222. package/src/extension/team-tool/cache-control.ts +0 -19
  223. package/src/extension/team-tool/cancel.ts +0 -337
  224. package/src/extension/team-tool/chain-dispatch.ts +0 -95
  225. package/src/extension/team-tool/chain-executor.ts +0 -379
  226. package/src/extension/team-tool/config-patch.ts +0 -50
  227. package/src/extension/team-tool/context.ts +0 -149
  228. package/src/extension/team-tool/destructive-gate.ts +0 -52
  229. package/src/extension/team-tool/doctor.ts +0 -431
  230. package/src/extension/team-tool/explain.ts +0 -282
  231. package/src/extension/team-tool/failure-patterns.ts +0 -118
  232. package/src/extension/team-tool/goal-wrap.ts +0 -330
  233. package/src/extension/team-tool/goal.ts +0 -556
  234. package/src/extension/team-tool/handle-schedule.ts +0 -332
  235. package/src/extension/team-tool/handle-settings.ts +0 -520
  236. package/src/extension/team-tool/health-monitor.ts +0 -481
  237. package/src/extension/team-tool/inspect.ts +0 -88
  238. package/src/extension/team-tool/intent-policy.ts +0 -45
  239. package/src/extension/team-tool/lifecycle-actions.ts +0 -615
  240. package/src/extension/team-tool/orchestrate.ts +0 -98
  241. package/src/extension/team-tool/parallel-dispatch.ts +0 -183
  242. package/src/extension/team-tool/plan.ts +0 -50
  243. package/src/extension/team-tool/respond.ts +0 -150
  244. package/src/extension/team-tool/run-not-found.ts +0 -49
  245. package/src/extension/team-tool/run.ts +0 -1035
  246. package/src/extension/team-tool/status.ts +0 -260
  247. package/src/extension/team-tool/workflow-manage.ts +0 -261
  248. package/src/extension/team-tool-types.ts +0 -26
  249. package/src/extension/team-tool.ts +0 -1116
  250. package/src/extension/tool-result.ts +0 -16
  251. package/src/extension/validate-resources.ts +0 -108
  252. package/src/hooks/registry.ts +0 -200
  253. package/src/hooks/types.ts +0 -69
  254. package/src/i18n.ts +0 -212
  255. package/src/observability/correlation.ts +0 -50
  256. package/src/observability/event-bus.ts +0 -86
  257. package/src/observability/event-to-metric.ts +0 -170
  258. package/src/observability/exporters/adapter.ts +0 -27
  259. package/src/observability/exporters/otlp-exporter.ts +0 -356
  260. package/src/observability/exporters/prometheus-exporter.ts +0 -54
  261. package/src/observability/metric-registry.ts +0 -100
  262. package/src/observability/metric-retention.ts +0 -64
  263. package/src/observability/metric-sink.ts +0 -99
  264. package/src/observability/metrics-primitives.ts +0 -219
  265. package/src/plugins/plugin-define.ts +0 -6
  266. package/src/plugins/plugin-registry.ts +0 -32
  267. package/src/plugins/plugins/index.ts +0 -3
  268. package/src/plugins/plugins/nextjs.ts +0 -19
  269. package/src/plugins/plugins/vite.ts +0 -10
  270. package/src/plugins/plugins/vitest.ts +0 -9
  271. package/src/prompt/prompt-runtime.ts +0 -380
  272. package/src/runtime/adaptive-plan.ts +0 -563
  273. package/src/runtime/agent-control.ts +0 -215
  274. package/src/runtime/agent-memory.ts +0 -81
  275. package/src/runtime/agent-observability.ts +0 -122
  276. package/src/runtime/anchor-manager.ts +0 -475
  277. package/src/runtime/async-marker.ts +0 -32
  278. package/src/runtime/async-runner.ts +0 -381
  279. package/src/runtime/attention-events.ts +0 -28
  280. package/src/runtime/auto-summarize.ts +0 -344
  281. package/src/runtime/background-runner.ts +0 -837
  282. package/src/runtime/batch-barrier.ts +0 -147
  283. package/src/runtime/broker-issuer.ts +0 -37
  284. package/src/runtime/cancellation-token.ts +0 -99
  285. package/src/runtime/cancellation.ts +0 -99
  286. package/src/runtime/capability-inventory.ts +0 -137
  287. package/src/runtime/chain-parser.ts +0 -237
  288. package/src/runtime/chain-runner.ts +0 -606
  289. package/src/runtime/checkpoint.ts +0 -291
  290. package/src/runtime/child-pi-constants.ts +0 -42
  291. package/src/runtime/child-pi-kill.ts +0 -180
  292. package/src/runtime/child-pi-pool.ts +0 -68
  293. package/src/runtime/child-pi-spawn.ts +0 -287
  294. package/src/runtime/child-pi-steering.ts +0 -128
  295. package/src/runtime/child-pi-streams.ts +0 -296
  296. package/src/runtime/child-pi-transcript.ts +0 -169
  297. package/src/runtime/child-pi.ts +0 -1105
  298. package/src/runtime/coalesce-tasks.ts +0 -268
  299. package/src/runtime/code-summary.ts +0 -292
  300. package/src/runtime/command-trace.ts +0 -105
  301. package/src/runtime/compact-pipeline.ts +0 -56
  302. package/src/runtime/compact-stages/ansi-strip-stage.ts +0 -25
  303. package/src/runtime/compact-stages/blank-collapse-stage.ts +0 -31
  304. package/src/runtime/compact-stages/deduplicate-stage.ts +0 -34
  305. package/src/runtime/compact-stages/head-snap-stage.ts +0 -57
  306. package/src/runtime/compact-stages/index.ts +0 -23
  307. package/src/runtime/compact-stages/tail-capture-stage.ts +0 -77
  308. package/src/runtime/compact-stages/truncation-stage.ts +0 -74
  309. package/src/runtime/compaction-summary.ts +0 -278
  310. package/src/runtime/completion-guard.ts +0 -207
  311. package/src/runtime/concurrency.ts +0 -58
  312. package/src/runtime/crash-classification.ts +0 -229
  313. package/src/runtime/crash-recovery.ts +0 -577
  314. package/src/runtime/crew-agent-records.ts +0 -593
  315. package/src/runtime/crew-agent-runtime.ts +0 -62
  316. package/src/runtime/crew-broker-child.ts +0 -88
  317. package/src/runtime/crew-broker-client.ts +0 -673
  318. package/src/runtime/crew-broker-tokens.ts +0 -84
  319. package/src/runtime/crew-broker.ts +0 -1276
  320. package/src/runtime/crew-hooks.ts +0 -227
  321. package/src/runtime/cross-extension-rpc.ts +0 -143
  322. package/src/runtime/custom-tools/irc-tool.ts +0 -282
  323. package/src/runtime/custom-tools/submit-result-tool.ts +0 -102
  324. package/src/runtime/deadletter.ts +0 -47
  325. package/src/runtime/delivery-coordinator.ts +0 -211
  326. package/src/runtime/delta-conflict.ts +0 -348
  327. package/src/runtime/deterministic-ast.ts +0 -161
  328. package/src/runtime/diagnostic-export.ts +0 -171
  329. package/src/runtime/direct-run.ts +0 -45
  330. package/src/runtime/dwf-state-store.ts +0 -102
  331. package/src/runtime/dynamic-workflow-context.ts +0 -1016
  332. package/src/runtime/dynamic-workflow-runner.ts +0 -310
  333. package/src/runtime/effectiveness.ts +0 -90
  334. package/src/runtime/errors/crew-errors.ts +0 -162
  335. package/src/runtime/event-stream-bridge.ts +0 -98
  336. package/src/runtime/foreground-control.ts +0 -193
  337. package/src/runtime/foreground-watchdog.ts +0 -132
  338. package/src/runtime/global-worker-cap.ts +0 -96
  339. package/src/runtime/goal-achievement.ts +0 -148
  340. package/src/runtime/goal-evaluator.ts +0 -361
  341. package/src/runtime/goal-loop-runner.ts +0 -828
  342. package/src/runtime/goal-state-store.ts +0 -219
  343. package/src/runtime/green-contract.ts +0 -55
  344. package/src/runtime/group-join.ts +0 -267
  345. package/src/runtime/handoff-manager.ts +0 -598
  346. package/src/runtime/heartbeat-gradient.ts +0 -36
  347. package/src/runtime/heartbeat-watcher.ts +0 -239
  348. package/src/runtime/hidden-handoff.ts +0 -407
  349. package/src/runtime/important-line-classifier.ts +0 -140
  350. package/src/runtime/intercom-bridge.ts +0 -187
  351. package/src/runtime/iteration-hooks.ts +0 -305
  352. package/src/runtime/live-agent-control.ts +0 -136
  353. package/src/runtime/live-agent-manager.ts +0 -704
  354. package/src/runtime/live-control-realtime.ts +0 -56
  355. package/src/runtime/live-extension-bridge.ts +0 -148
  356. package/src/runtime/live-irc.ts +0 -97
  357. package/src/runtime/live-session-health.ts +0 -108
  358. package/src/runtime/live-session-runtime.ts +0 -1139
  359. package/src/runtime/loop-gates.ts +0 -128
  360. package/src/runtime/manifest-cache.ts +0 -375
  361. package/src/runtime/mcp-proxy.ts +0 -105
  362. package/src/runtime/metric-parser.ts +0 -36
  363. package/src/runtime/model-fallback.ts +0 -409
  364. package/src/runtime/model-resolver.ts +0 -126
  365. package/src/runtime/model-scope.ts +0 -158
  366. package/src/runtime/orphan-worker-registry.ts +0 -465
  367. package/src/runtime/output-validator.ts +0 -202
  368. package/src/runtime/overflow-recovery.ts +0 -206
  369. package/src/runtime/parallel-research.ts +0 -68
  370. package/src/runtime/parallel-utils.ts +0 -160
  371. package/src/runtime/parent-guard.ts +0 -134
  372. package/src/runtime/path-overlap.ts +0 -150
  373. package/src/runtime/peer-dep.ts +0 -292
  374. package/src/runtime/per-write-validator.ts +0 -181
  375. package/src/runtime/phase-progress.ts +0 -217
  376. package/src/runtime/phase-tracker.ts +0 -385
  377. package/src/runtime/pi-args.ts +0 -655
  378. package/src/runtime/pi-json-output.ts +0 -170
  379. package/src/runtime/pi-spawn.ts +0 -273
  380. package/src/runtime/pipeline-runner.ts +0 -523
  381. package/src/runtime/plan-templates.ts +0 -201
  382. package/src/runtime/policy-engine.ts +0 -115
  383. package/src/runtime/post-checks.ts +0 -142
  384. package/src/runtime/post-exit-stdio-guard.ts +0 -104
  385. package/src/runtime/process-lifecycle.ts +0 -491
  386. package/src/runtime/process-status.ts +0 -154
  387. package/src/runtime/progress-event-coalescer.ts +0 -44
  388. package/src/runtime/progress-tracker.ts +0 -124
  389. package/src/runtime/prose-compressor.ts +0 -162
  390. package/src/runtime/recovery-recipes.ts +0 -203
  391. package/src/runtime/replace.ts +0 -570
  392. package/src/runtime/resilient-edit.ts +0 -153
  393. package/src/runtime/result-extractor.ts +0 -213
  394. package/src/runtime/retry-executor.ts +0 -100
  395. package/src/runtime/retry-runner.ts +0 -336
  396. package/src/runtime/role-permission.ts +0 -59
  397. package/src/runtime/run-coalesced-task-group.ts +0 -308
  398. package/src/runtime/run-drift.ts +0 -219
  399. package/src/runtime/run-tracker.ts +0 -116
  400. package/src/runtime/runtime-policy.ts +0 -29
  401. package/src/runtime/runtime-resolver.ts +0 -172
  402. package/src/runtime/runtime-warmup.ts +0 -160
  403. package/src/runtime/scheduler.ts +0 -353
  404. package/src/runtime/semaphore.ts +0 -141
  405. package/src/runtime/sensitive-paths.ts +0 -93
  406. package/src/runtime/session-resources.ts +0 -25
  407. package/src/runtime/session-snapshot.ts +0 -59
  408. package/src/runtime/session-usage.ts +0 -79
  409. package/src/runtime/settings-store.ts +0 -155
  410. package/src/runtime/sidechain-output.ts +0 -35
  411. package/src/runtime/single-agent-compose.ts +0 -84
  412. package/src/runtime/skill-effectiveness.ts +0 -524
  413. package/src/runtime/skill-instructions.ts +0 -391
  414. package/src/runtime/stale-reconciler.ts +0 -680
  415. package/src/runtime/stream-preview.ts +0 -184
  416. package/src/runtime/streaming-output.ts +0 -50
  417. package/src/runtime/subagent-manager.ts +0 -561
  418. package/src/runtime/subprocess-tool-registry.ts +0 -70
  419. package/src/runtime/supervisor-contact.ts +0 -64
  420. package/src/runtime/task-display.ts +0 -52
  421. package/src/runtime/task-graph-scheduler.ts +0 -227
  422. package/src/runtime/task-graph.ts +0 -290
  423. package/src/runtime/task-health.ts +0 -83
  424. package/src/runtime/task-id.ts +0 -161
  425. package/src/runtime/task-output-context.ts +0 -602
  426. package/src/runtime/task-packet.ts +0 -268
  427. package/src/runtime/task-quality.ts +0 -199
  428. package/src/runtime/task-runner/capabilities.ts +0 -78
  429. package/src/runtime/task-runner/context-retrieval.ts +0 -163
  430. package/src/runtime/task-runner/live-executor.ts +0 -227
  431. package/src/runtime/task-runner/output-splitter.ts +0 -152
  432. package/src/runtime/task-runner/progress.ts +0 -157
  433. package/src/runtime/task-runner/prompt-builder.ts +0 -278
  434. package/src/runtime/task-runner/prompt-pipeline.ts +0 -88
  435. package/src/runtime/task-runner/result-utils.ts +0 -16
  436. package/src/runtime/task-runner/retrieval-orchestrator.ts +0 -310
  437. package/src/runtime/task-runner/run-projection.ts +0 -125
  438. package/src/runtime/task-runner/state-helpers.ts +0 -176
  439. package/src/runtime/task-runner/tail-read.ts +0 -34
  440. package/src/runtime/task-runner.ts +0 -1420
  441. package/src/runtime/team-runner-artifacts.ts +0 -13
  442. package/src/runtime/team-runner.ts +0 -1993
  443. package/src/runtime/tool-output-pruner.ts +0 -333
  444. package/src/runtime/tool-progress.ts +0 -278
  445. package/src/runtime/usage-tracker.ts +0 -73
  446. package/src/runtime/verification-gates.ts +0 -571
  447. package/src/runtime/verification-integrity.ts +0 -105
  448. package/src/runtime/verification-worktree.ts +0 -186
  449. package/src/runtime/worker-heartbeat.ts +0 -25
  450. package/src/runtime/worker-startup.ts +0 -83
  451. package/src/runtime/workflow-state.ts +0 -195
  452. package/src/runtime/workspace-lock.ts +0 -443
  453. package/src/runtime/workspace-tree.ts +0 -312
  454. package/src/runtime/yield-handler.ts +0 -224
  455. package/src/runtime/zombie-scanner.ts +0 -298
  456. package/src/schema/config-schema.ts +0 -327
  457. package/src/schema/team-tool-schema.ts +0 -415
  458. package/src/schema/validation-types.ts +0 -159
  459. package/src/skills/discover-skills.ts +0 -197
  460. package/src/skills/skill-templates.ts +0 -456
  461. package/src/skills/validate.ts +0 -285
  462. package/src/state/active-run-registry.ts +0 -439
  463. package/src/state/artifact-store.ts +0 -176
  464. package/src/state/atomic-write.ts +0 -903
  465. package/src/state/blob-store.ts +0 -308
  466. package/src/state/contracts.ts +0 -158
  467. package/src/state/crew-init.ts +0 -269
  468. package/src/state/decision-ledger.ts +0 -345
  469. package/src/state/event-log-rotation.ts +0 -342
  470. package/src/state/event-log.ts +0 -1311
  471. package/src/state/event-reconstructor.ts +0 -229
  472. package/src/state/gitignore-manager.ts +0 -46
  473. package/src/state/health-store.ts +0 -79
  474. package/src/state/hook-instinct-bridge.ts +0 -94
  475. package/src/state/hook-integrations.ts +0 -51
  476. package/src/state/instinct-store.ts +0 -275
  477. package/src/state/jsonl-writer.ts +0 -107
  478. package/src/state/locks.ts +0 -562
  479. package/src/state/mailbox.ts +0 -916
  480. package/src/state/observation-store.ts +0 -176
  481. package/src/state/run-cache.ts +0 -193
  482. package/src/state/run-graph.ts +0 -183
  483. package/src/state/run-metrics.ts +0 -165
  484. package/src/state/schedule.ts +0 -166
  485. package/src/state/session-state-map.ts +0 -51
  486. package/src/state/state-store.ts +0 -958
  487. package/src/state/task-claims.ts +0 -61
  488. package/src/state/tiered-eval.ts +0 -480
  489. package/src/state/types-eval.ts +0 -58
  490. package/src/state/types.ts +0 -440
  491. package/src/state/usage.ts +0 -138
  492. package/src/state/worker-atomic-writer.ts +0 -194
  493. package/src/subagents/async-entry.ts +0 -1
  494. package/src/subagents/index.ts +0 -3
  495. package/src/subagents/live/control.ts +0 -1
  496. package/src/subagents/live/manager.ts +0 -1
  497. package/src/subagents/live/realtime.ts +0 -1
  498. package/src/subagents/live/session-runtime.ts +0 -1
  499. package/src/subagents/manager.ts +0 -1
  500. package/src/subagents/spawn.ts +0 -1
  501. package/src/teams/discover-teams.ts +0 -187
  502. package/src/teams/team-config.ts +0 -27
  503. package/src/teams/team-serializer.ts +0 -38
  504. package/src/tools/safe-bash-extension.ts +0 -54
  505. package/src/tools/safe-bash.ts +0 -505
  506. package/src/types/diff.d.ts +0 -18
  507. package/src/types/new-api-types.ts +0 -35
  508. package/src/ui/agent-management-overlay.ts +0 -160
  509. package/src/ui/card-colors.ts +0 -139
  510. package/src/ui/crew-footer.ts +0 -102
  511. package/src/ui/crew-select-list.ts +0 -111
  512. package/src/ui/dashboard-panes/agents-pane.ts +0 -164
  513. package/src/ui/dashboard-panes/cancellation-pane.ts +0 -66
  514. package/src/ui/dashboard-panes/capability-pane.ts +0 -77
  515. package/src/ui/dashboard-panes/health-pane.ts +0 -31
  516. package/src/ui/dashboard-panes/mailbox-pane.ts +0 -35
  517. package/src/ui/dashboard-panes/metrics-pane.ts +0 -37
  518. package/src/ui/dashboard-panes/progress-pane.ts +0 -38
  519. package/src/ui/dashboard-panes/transcript-pane.ts +0 -10
  520. package/src/ui/deploy-bundled-themes.ts +0 -71
  521. package/src/ui/dwf-phase-display.ts +0 -151
  522. package/src/ui/dynamic-border.ts +0 -35
  523. package/src/ui/heartbeat-aggregator.ts +0 -74
  524. package/src/ui/key-utils.ts +0 -42
  525. package/src/ui/keybinding-map.ts +0 -248
  526. package/src/ui/layout-primitives.ts +0 -106
  527. package/src/ui/live-conversation-overlay.ts +0 -183
  528. package/src/ui/live-duration.ts +0 -55
  529. package/src/ui/live-run-sidebar.ts +0 -301
  530. package/src/ui/loaders.ts +0 -176
  531. package/src/ui/mascot.ts +0 -436
  532. package/src/ui/overlays/agent-picker-overlay.ts +0 -65
  533. package/src/ui/overlays/confirm-overlay.ts +0 -60
  534. package/src/ui/overlays/help-overlay.ts +0 -177
  535. package/src/ui/overlays/mailbox-compose-overlay.ts +0 -181
  536. package/src/ui/overlays/mailbox-compose-preview.ts +0 -78
  537. package/src/ui/overlays/mailbox-detail-overlay.ts +0 -154
  538. package/src/ui/pi-ui-compat.ts +0 -68
  539. package/src/ui/powerbar-publisher.ts +0 -459
  540. package/src/ui/render-coalescer.ts +0 -101
  541. package/src/ui/render-diff.ts +0 -160
  542. package/src/ui/render-scheduler.ts +0 -224
  543. package/src/ui/run-action-dispatcher.ts +0 -182
  544. package/src/ui/run-dashboard.ts +0 -913
  545. package/src/ui/run-event-bus.ts +0 -381
  546. package/src/ui/run-snapshot-cache.ts +0 -1057
  547. package/src/ui/settings-overlay.ts +0 -1067
  548. package/src/ui/snapshot-types.ts +0 -90
  549. package/src/ui/spinner.ts +0 -17
  550. package/src/ui/status-colors.ts +0 -143
  551. package/src/ui/syntax-highlight.ts +0 -136
  552. package/src/ui/terminal-status.ts +0 -269
  553. package/src/ui/theme-adapter.ts +0 -271
  554. package/src/ui/theme-discovery.ts +0 -199
  555. package/src/ui/tool-progress-formatter.ts +0 -93
  556. package/src/ui/tool-render.ts +0 -453
  557. package/src/ui/tool-renderers/brief-mode.ts +0 -225
  558. package/src/ui/tool-renderers/index.ts +0 -726
  559. package/src/ui/transcript-cache.ts +0 -128
  560. package/src/ui/transcript-entries.ts +0 -256
  561. package/src/ui/transcript-viewer.ts +0 -424
  562. package/src/ui/widget/index.ts +0 -357
  563. package/src/ui/widget/widget-formatters.ts +0 -182
  564. package/src/ui/widget/widget-model.ts +0 -110
  565. package/src/ui/widget/widget-renderer.ts +0 -182
  566. package/src/ui/widget/widget-types.ts +0 -36
  567. package/src/utils/bm25-search.ts +0 -235
  568. package/src/utils/completion-dedupe.ts +0 -63
  569. package/src/utils/conflict-detect.ts +0 -668
  570. package/src/utils/env-allowlist.ts +0 -30
  571. package/src/utils/env-filter.ts +0 -155
  572. package/src/utils/file-coalescer.ts +0 -98
  573. package/src/utils/fingerprint.ts +0 -180
  574. package/src/utils/frontmatter.ts +0 -70
  575. package/src/utils/fs-watch.ts +0 -37
  576. package/src/utils/gh-protocol.ts +0 -556
  577. package/src/utils/git.ts +0 -260
  578. package/src/utils/guards.ts +0 -107
  579. package/src/utils/ids.ts +0 -24
  580. package/src/utils/incremental-reader.ts +0 -220
  581. package/src/utils/internal-error.ts +0 -10
  582. package/src/utils/names.ts +0 -36
  583. package/src/utils/ndjson.ts +0 -115
  584. package/src/utils/paths.ts +0 -227
  585. package/src/utils/project-detector.ts +0 -160
  586. package/src/utils/redaction.ts +0 -347
  587. package/src/utils/resolve-shell.ts +0 -36
  588. package/src/utils/run-watcher-registry.ts +0 -152
  589. package/src/utils/safe-paths.ts +0 -383
  590. package/src/utils/scan-cache.ts +0 -144
  591. package/src/utils/session-utils.ts +0 -110
  592. package/src/utils/sleep.ts +0 -54
  593. package/src/utils/socket-path.ts +0 -127
  594. package/src/utils/sse-parser.ts +0 -129
  595. package/src/utils/task-name-generator.ts +0 -337
  596. package/src/utils/timings.ts +0 -33
  597. package/src/utils/token-counter.ts +0 -200
  598. package/src/utils/visual.ts +0 -207
  599. package/src/workflows/cost-estimator.ts +0 -34
  600. package/src/workflows/discover-workflows.ts +0 -290
  601. package/src/workflows/intermediate-store.ts +0 -166
  602. package/src/workflows/preflight-validator.ts +0 -168
  603. package/src/workflows/topology-analyzer.ts +0 -203
  604. package/src/workflows/validate-workflow.ts +0 -50
  605. package/src/workflows/workflow-config.ts +0 -70
  606. package/src/workflows/workflow-serializer.ts +0 -32
  607. package/src/worktree/branch-freshness.ts +0 -112
  608. package/src/worktree/cleanup.ts +0 -341
  609. package/src/worktree/worktree-manager.ts +0 -1200
@@ -1,1016 +0,0 @@
1
- /**
2
- * dynamic-workflow-context.ts — WorkflowCtx facade for dynamic-workflow scripts (P2).
3
- *
4
- * Spec: research-findings/goal-workflow/00-SPEC.md §3.2
5
- * Plan: 07-PLAN.md v3 P2 + §0b G4 + §0c C4/C5/C7.
6
- *
7
- * The `ctx` object passed to a `.dwf.ts` script's `export default async function(ctx)`.
8
- * Capability-locked: exposes ONLY the documented methods (no raw manifest/process/require).
9
- * The script host (dynamic-workflow-runner.ts) loads the script via jiti in plain module
10
- * scope with a FROZEN WorkflowCtx. v1 has NO vm sandbox (review H-2): the script CAN
11
- * reach `process`/`require`/`import` directly — the frozen ctx is a contract surface,
12
- * not a security boundary. `.dwf.ts` = postinstall-equivalent trust. isolated-vm v1.5.
13
- *
14
- * `agent()` resolution (§0b G4): 4-tier precedence
15
- * 1. opts.agent (explicit name) — bypasses team lookup
16
- * 2. team.roles.find(r => r.name === role)?.agent → allAgents lookup
17
- * 3. allAgents(discoverAgents(cwd)).find(a => a.name === role) (role name == agent name)
18
- * 4. synthesize minimal AgentConfig (source:"dynamic", systemPrompt:"You are {role}.")
19
- *
20
- * Isolation (§0b G3 / report 05 §C.4): worker output → artifact file; `agent()` returns
21
- * structured data + writes a side artifact. The script holds results in JS vars; only
22
- * `setResult()` reaches the main context.
23
- */
24
-
25
- import { randomBytes } from "node:crypto";
26
- import type { TSchema } from "@sinclair/typebox";
27
- import type { AgentConfig } from "../agents/agent-config.ts";
28
- import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
29
- import { writeArtifact } from "../state/artifact-store.ts";
30
- import { appendEvent } from "../state/event-log.ts";
31
- import { appendMailboxMessage, readMailbox } from "../state/mailbox.ts";
32
- import type { TeamRunManifest } from "../state/types.ts";
33
- import type { TeamConfig } from "../teams/team-config.ts";
34
- import { logInternalError } from "../utils/internal-error.ts";
35
- import { cleanupAgentWorktreeAsync, prepareAgentWorktreeAsync } from "../worktree/worktree-manager.ts";
36
- import { runChildPi } from "./child-pi.ts";
37
- import type { DwfCheckpointState } from "./dwf-state-store.ts";
38
- import { withWorkerSlot } from "./global-worker-cap.ts";
39
- import { mapConcurrent } from "./parallel-utils.ts";
40
- import { parsePiJsonOutput } from "./pi-json-output.ts";
41
- import { renderPlanTemplate } from "./plan-templates.ts";
42
- import { extractStructuredResult } from "./result-extractor.ts";
43
- import { executeWithRetry } from "./retry-executor.ts";
44
- import { Semaphore } from "./semaphore.ts";
45
-
46
- export interface AgentCallOpts {
47
- prompt: string;
48
- /** Role name (resolved via G4 4-tier chain) OR explicit agent name. */
49
- role?: string;
50
- /** Explicit agent name — bypasses team-role lookup (tier 1). */
51
- agent?: string;
52
- description?: string;
53
- model?: string;
54
- skill?: string[] | false;
55
- maxTurns?: number;
56
- graceTurns?: number;
57
- /** Dependency artifact paths injected into the agent prompt. */
58
- inputs?: string[];
59
- /** Disable ALL tools for this call (Pi `--no-tools`, §0c C6). Use for pure-judgment /
60
- * verdict steps where the agent must answer directly without exploring, e.g.
61
- * `ctx.review()`'s JSON-verdict call. Without this, role-based tools (read/grep/bash)
62
- * apply and the model may loop exploring instead of answering. */
63
- disableTools?: boolean;
64
- /** Override the resolved agent's system prompt. Use when the call needs a different
65
- * persona/output-format than the role's defined agent — e.g. `ctx.review()` needs a
66
- * JSON-verdict judge, but the user's reviewer.md agent is a markdown code-reviewer.
67
- * When set, the resolved agent's systemPrompt is replaced entirely. */
68
- systemPrompt?: string;
69
- /** Round-13 P0-3: optional TypeBox schema. When set, the call's output is validated
70
- * against the schema after extraction. Validation failure yields ok:false with a
71
- * structured `error` and undefined `structured` field. Forward-compatible: when
72
- * undefined, behavior is identical to the regex-based extractor. */
73
- schema?: TSchema;
74
- /** round-17 P2-4: spawn this agent in an isolated git worktree.
75
- * Useful when parallel agents modify files concurrently (avoids conflicts). The
76
- * worktree is created from HEAD, the agent runs there, and on completion the
77
- * diff is captured as an artifact before cleanup. Default false.
78
- * If worktree creation fails (no git repo, dirty leader), the agent runs in the
79
- * normal cwd and a warning is logged via ctx.log(). Backward compatible —
80
- * omitting it is identical to `false`. */
81
- worktree?: boolean;
82
- }
83
-
84
- export interface AgentResult {
85
- ok: boolean;
86
- text: string;
87
- structured?: unknown;
88
- usage?: { input?: number; output?: number; cost?: number; turns?: number };
89
- runId?: string;
90
- taskId?: string;
91
- artifactPath?: string;
92
- error?: string;
93
- durationMs?: number;
94
- }
95
-
96
- /** round-14 P1-2: per-workflow token budget. Frozen read-only surface exposed as ctx.budget. */
97
- export interface WorkflowBudget {
98
- /** Configured budget, or null when unbounded. */
99
- total: number | null;
100
- /** Tokens consumed so far (accumulated from each ctx.agent() run's usage). */
101
- spent(): number;
102
- /** Tokens remaining; Infinity when total is null. */
103
- remaining(): number;
104
- }
105
-
106
- export interface WorkflowCtx {
107
- cwd: string;
108
- runId: string;
109
- goal?: string;
110
- /** Spawn one agent, await result. Concurrency enforced by ctx.semaphore. */
111
- agent(opts: AgentCallOpts): Promise<AgentResult>;
112
- /** Bounded fan-out preserving order (wraps mapConcurrent). */
113
- fanOut<T>(items: T[], limit: number, fn: (item: T, i: number) => Promise<AgentResult>): Promise<AgentResult[]>;
114
- /** Pipeline: sequential per-item stages, parallel across items (bounded by
115
- * ctx.semaphore). Each item passes through all stages in order; different
116
- * items may run concurrently. A failed stage yields `null` for that item
117
- * (logged via ctx.log) and other items continue. Aborts propagate.
118
- * round-16 (P2-1). */
119
- pipeline<TItem, TResult = unknown>(
120
- items: TItem[],
121
- ...stages: Array<(previous: TResult, original: TItem, index: number) => Promise<TResult> | TResult>
122
- ): Promise<(TResult | null)[]>;
123
- /** Run a reviewer agent over an artifact; parse {outcome, feedback}. §3.2. */
124
- review(
125
- taskId: string,
126
- reviewerRole?: string,
127
- opts?: {
128
- content?: string;
129
- artifactPath?: string;
130
- disableTools?: boolean;
131
- },
132
- ): Promise<{
133
- outcome: "accept" | "reject" | "changes_requested";
134
- feedback: string;
135
- }>;
136
- /** Re-run a task with feedback (wraps executeWithRetry). */
137
- retry(taskId: string, opts?: { feedback?: string }): Promise<AgentResult>;
138
- /** Send a mailbox message to another agent/leader. */
139
- mail(
140
- to: string,
141
- body: string,
142
- opts?: {
143
- kind?: string;
144
- taskId?: string;
145
- replyTo?: string;
146
- replyDeadline?: number;
147
- },
148
- ): string;
149
- /** Block until N mailbox replies arrive or deadline. ~10 LOC net-new (report 05 §G.4). */
150
- gatherReplies(messageIds: string[], deadlineMs: number): Promise<unknown[]>;
151
- /** Render a built-in plan template (full-implementation / standard-review). */
152
- renderTemplate(name: string, vars: Record<string, string>): unknown;
153
- /** Persistent variables (revived intermediate-store). */
154
- vars: Record<string, unknown>;
155
- /** Mark the final result. ONLY this artifact reaches the main context. */
156
- setResult(artifactPath: string, meta?: Record<string, unknown>): void;
157
- /** Mark the start of a named workflow phase. Emits a `dwf.phase_started` event
158
- * (and a `dwf.phase_completed` for the previous phase, if any) to the run's
159
- * events.jsonl. Idempotent on the same title — calling twice with the same
160
- * title is a no-op. Phase titles are in-memory only; the events log is the
161
- * durable source of truth for phase boundaries. */
162
- phase(title: string): void;
163
- /** round-14 P1-3: append a workflow-level log line. Persists to events.jsonl
164
- * as a `dwf.log` event and keeps a bounded in-memory copy (capped at 1000). */
165
- log(message: unknown): void;
166
- /** round-14 P1-2: per-workflow token budget. ctx.agent() auto-rejects with
167
- * ok:false once exhausted. */
168
- budget: WorkflowBudget;
169
- /** round-14 P1-5: typed workflow arguments. Reads the value passed via
170
- * MakeWorkflowCtxOptions.args (sourced from manifest.args). Defaults to {}
171
- * when unset. */
172
- args<T = unknown>(): T;
173
- semaphore: Semaphore;
174
- /** Abort signal (cancel/stop). */
175
- signal: AbortSignal;
176
- }
177
-
178
- export interface MakeWorkflowCtxOptions {
179
- concurrency?: number;
180
- signal: AbortSignal;
181
- team?: TeamConfig;
182
- modelOverride?: string;
183
- /** round-14 P1-2: per-workflow token budget. null/undefined = unbounded. */
184
- tokenBudget?: number | null;
185
- /** round-14 P1-5: typed workflow arguments (sourced from manifest.args). Defaults to {}. */
186
- args?: unknown;
187
- /** round-18 P2-3: checkpoint state to hydrate ctx with on resume. When provided,
188
- * the ctx starts with the resumed vars/phases/logs/spent/agentCount instead of
189
- * empty defaults. Omit (or undefined) for a fresh run — backward compatible. */
190
- resumedState?: DwfCheckpointState;
191
- /** round-18 P2-3: callback invoked after each `ctx.agent()` call completes
192
- * (success OR fail). The runner wires this to `DwfStore.save()` so a crash after
193
- * an agent call leaves a durable checkpoint. Best-effort — failures are swallowed
194
- * so checkpointing can never crash the workflow. */
195
- onCheckpoint?: (state: DwfCheckpointState) => void;
196
- }
197
-
198
- /**
199
- * Resolve a role/agent name to a full AgentConfig (§0b G4 4-tier precedence).
200
- * Module-local — NOT promoted to a shared module (keeps P2 isolated from the
201
- * load-bearing team-runner path).
202
- */
203
- export function resolveAgentForRole(
204
- roleName: string | undefined,
205
- opts: { explicitAgent?: string; team?: TeamConfig; cwd: string },
206
- ): AgentConfig {
207
- const cwd = opts.cwd;
208
- // Tier 1: explicit agent name.
209
- if (opts.explicitAgent) {
210
- const found = allAgents(discoverAgents(cwd)).find((a) => a.name === opts.explicitAgent);
211
- if (found) return found;
212
- // Fall through to synthesize if the named agent doesn't exist (P2-friendly).
213
- }
214
- // Tier 2: team.roles[].agent lookup.
215
- if (opts.team) {
216
- const role = opts.team.roles.find((r) => r.name === roleName);
217
- if (role) {
218
- const byAgentName = allAgents(discoverAgents(cwd)).find((a) => a.name === role.agent);
219
- if (byAgentName) return byAgentName;
220
- }
221
- }
222
- // Tier 3: discoverAgents by role name (role name == agent name).
223
- if (roleName) {
224
- const byRoleName = allAgents(discoverAgents(cwd)).find((a) => a.name === roleName);
225
- if (byRoleName) return byRoleName;
226
- }
227
- // Tier 4: synthesize a minimal AgentConfig.
228
- const name = opts.explicitAgent ?? roleName ?? "executor";
229
- return synthesizeAgentConfig(name);
230
- }
231
-
232
- /** Synthesize a minimal AgentConfig (§0c C7: source:"dynamic", not "synthetic"). */
233
- export function synthesizeAgentConfig(name: string, model?: string): AgentConfig {
234
- return {
235
- name,
236
- description: `Synthesized agent for dynamic workflow (${name}).`,
237
- source: "dynamic",
238
- filePath: `<dynamic-workflow>`,
239
- systemPrompt: `You are ${name}, an agent in a dynamic pi-crew workflow. Use the provided tools (read, grep, find, ls, bash) to investigate the target and produce concrete written findings. Do not return an empty response — always output substantive content for your task.`,
240
- model,
241
- // Round-N fix: give synthesized agents the STANDARD file-investigation toolkit
242
- // (matches real built-in agents). Previously tools:[] relied on pi-args default
243
- // behavior, which left most synthesized agents (explorer/analyst/critic/executor)
244
- // unable to read the codebase → 10/11 agents returned empty/!ok in distill-dwf runs.
245
- tools: ["read", "grep", "find", "ls", "bash"],
246
- inheritProjectContext: false,
247
- inheritSkills: false,
248
- };
249
- }
250
-
251
- /** Build the WorkflowCtx facade. Capability-locked: only documented methods exposed. */
252
- export function makeWorkflowCtx(manifest: TeamRunManifest, opts: MakeWorkflowCtxOptions): WorkflowCtx {
253
- const concurrency = Math.max(1, opts.concurrency ?? 4);
254
- const semaphore = new Semaphore(concurrency);
255
- let finalResult: { artifactPath: string; meta?: Record<string, unknown> } | undefined;
256
- // round-18 P2-3: agent invocation counter. Hydrated from a resumed checkpoint so a
257
- // resumed run keeps an accurate count; incremented in agent()'s finally block.
258
- let agentCount = opts.resumedState ? opts.resumedState.agentCount : 0;
259
- // round-12 P0-1: in-memory phase state, exposed via non-enumerable getter like __finalResult.
260
- // The events log is the durable source of truth for phase boundaries.
261
- // round-18 P2-3: hydrate phaseState from a resumed checkpoint (backward compatible when unset).
262
- const phaseState: { currentPhase: string | undefined; phases: string[] } = opts.resumedState
263
- ? {
264
- currentPhase: opts.resumedState.currentPhase,
265
- phases: [...opts.resumedState.phases],
266
- }
267
- : { currentPhase: undefined, phases: [] };
268
- let phaseCapWarned = false;
269
- // round-14 P1-2/P1-3/P1-5: closure-scoped runtime state shared by budget/log/args.
270
- // Mirrors the pi-dynamic-workflows RuntimeState pattern (workflow.ts:state).
271
- // round-18 P2-3: hydrate spent/logs from a resumed checkpoint (backward compatible when unset).
272
- const wfState: { spent: number; logs: string[]; args: unknown } = {
273
- spent: opts.resumedState?.spent ?? 0,
274
- logs: opts.resumedState ? [...opts.resumedState.logs].slice(0, 1000) : [],
275
- args: opts.args ?? {},
276
- };
277
- // PERS-1: per-agent-call idempotency cache. Hydrated from resumedState so a DWF
278
- // resume skips already-completed agent calls (avoids duplicating artifacts/mailbox/tokens).
279
- // Uses Record (not Map) for JSON serialization safety.
280
- const completedAgentCalls: Record<string, { text: string; usage?: { input: number; output: number } }> =
281
- opts.resumedState?.completedAgentCalls ?? {};
282
- // round-14 P1-2: frozen budget surface. The closures read wfState.spent so the
283
- // object stays live after Object.freeze(ctx). total is a snapshot primitive.
284
- const budget = Object.freeze({
285
- total: opts.tokenBudget ?? null,
286
- spent: () => wfState.spent,
287
- remaining: () => (opts.tokenBudget == null ? Infinity : Math.max(0, opts.tokenBudget - wfState.spent)),
288
- } satisfies WorkflowBudget);
289
-
290
- const ctx: WorkflowCtx = {
291
- cwd: manifest.cwd,
292
- runId: manifest.runId,
293
- goal: manifest.goal,
294
- signal: opts.signal,
295
- semaphore,
296
- async agent(call: AgentCallOpts): Promise<AgentResult> {
297
- await semaphore.acquire();
298
- const started = Date.now();
299
- // round-17 P2-4: declared before the try so the finally can clean it up
300
- // regardless of which return/throw path is taken.
301
- let worktreePath: string | undefined;
302
- let worktreeBranch: string | undefined;
303
- // BDG-2: declared before the try so the catch block can un-reserve on failure.
304
- const ESTIMATE = 4096;
305
- let reserved = false;
306
- try {
307
- // PERS-1: per-agent-call idempotency. Compute a deterministic call ID
308
- // from the call arguments and check if this call was already completed
309
- // (e.g. during a previous run before a crash). If so, return the cached
310
- // result without re-spawning the agent.
311
- const callId = JSON.stringify({
312
- role: call.role,
313
- agent: call.agent,
314
- prompt: call.prompt,
315
- model: call.model,
316
- schema: call.schema ? "yes" : "no",
317
- });
318
- const cached = completedAgentCalls[callId];
319
- if (cached) {
320
- return {
321
- ok: true,
322
- text: cached.text,
323
- usage: cached.usage,
324
- durationMs: 0,
325
- };
326
- }
327
-
328
- // BDG-2: reserve-then-adjust budget. Before spawning, estimate the cost and
329
- // reserve it by adding to wfState.spent. This prevents N concurrent calls from
330
- // all seeing the same remaining budget and overspending.
331
- if (budget.total !== null && budget.remaining() < ESTIMATE) {
332
- return {
333
- ok: false,
334
- text: "",
335
- error: "workflow token budget exhausted",
336
- durationMs: 0,
337
- };
338
- }
339
- // Reserve the estimate before spawning.
340
- wfState.spent += ESTIMATE;
341
- reserved = true;
342
-
343
- const agentConfig = resolveAgentForRole(call.role, {
344
- explicitAgent: call.agent,
345
- team: opts.team,
346
- cwd: manifest.cwd,
347
- });
348
- // §0c C6: per-call disableTools override. When set, force Pi `--no-tools` so the
349
- // agent answers directly without exploring. Applied AFTER role resolution so it
350
- // wins over any role-defined tools.
351
- let effectiveAgent = call.disableTools === true ? { ...agentConfig, disableTools: true, tools: [] } : agentConfig;
352
- // Per-call systemPrompt override (replaces the resolved agent's persona/output-format).
353
- // Used by ctx.review() to force a JSON-verdict judge instead of the role's markdown reviewer.
354
- // Round-13 P0-3: when a schema is provided, append a JSON-output instruction so
355
- // the model returns parseable JSON instead of prose. Schema name is intentionally
356
- // generic — we don't reveal TypeBox internal types.
357
- //
358
- // Smoke-test fix: when BOTH schema AND an explicit call.systemPrompt are set,
359
- // the call.systemPrompt is the caller's intended persona (e.g. a JSON-verdict
360
- // judge). It MUST be used as the base for the JSON instruction — otherwise the
361
- // role's persona leaks through and the model returns prose, failing schema
362
- // validation. Previously call.systemPrompt was silently dropped when a schema
363
- // was present, which confused models into returning text like "hello".
364
- if (call.schema !== undefined) {
365
- const base = call.systemPrompt ?? effectiveAgent.systemPrompt;
366
- effectiveAgent = {
367
- ...effectiveAgent,
368
- systemPrompt: composeSchemaSystemPrompt(base, call.schema),
369
- };
370
- } else if (call.systemPrompt !== undefined) {
371
- effectiveAgent = {
372
- ...effectiveAgent,
373
- systemPrompt: call.systemPrompt,
374
- };
375
- }
376
- const task = composeAgentTask(call);
377
-
378
- // round-17 P2-4: worktree isolation per agent. When requested, spawn the
379
- // agent in an isolated git worktree so parallel file-modifying agents
380
- // don't clobber each other. Falls back to the normal cwd (with a warning)
381
- // when worktree creation is unavailable (no git repo, dirty leader).
382
- let agentCwd = manifest.cwd;
383
- if (call.worktree === true) {
384
- const wt = await prepareAgentWorktreeAsync(manifest, `dwf-agent-${Date.now()}-${randomBytes(4).toString("hex")}`);
385
- if (wt?.worktreePath) {
386
- agentCwd = wt.cwd;
387
- worktreePath = wt.worktreePath;
388
- worktreeBranch = wt.branch;
389
- ctx.log(`worktree: agent isolated at ${wt.worktreePath}`);
390
- } else {
391
- ctx.log("worktree: creation unavailable — falling back to normal cwd");
392
- }
393
- }
394
-
395
- const childResult = await withWorkerSlot(() =>
396
- runChildPi({
397
- cwd: agentCwd,
398
- task,
399
- agent: effectiveAgent,
400
- model: call.model ?? opts.modelOverride ?? agentConfig.model,
401
- skillPaths: undefined, // skills resolved via agent config + team-role plumbing
402
- maxTurns: call.maxTurns,
403
- graceTurns: call.graceTurns,
404
- signal: opts.signal,
405
- artifactsRoot: manifest.artifactsRoot,
406
- runId: manifest.runId,
407
- role: call.role ?? call.agent,
408
- }),
409
- );
410
- if (childResult.exitCode !== 0 || childResult.error) {
411
- // BDG-2: un-reserve the estimate on spawn failure.
412
- wfState.spent -= ESTIMATE;
413
- reserved = false;
414
- return {
415
- ok: false,
416
- text: "",
417
- error: childResult.error ?? `exit ${childResult.exitCode}`,
418
- durationMs: Date.now() - started,
419
- };
420
- }
421
- const parsed = parsePiJsonOutput(childResult.stdout);
422
- // round-14 P1-2: accumulate this run's token usage into the workflow budget.
423
- // BDG-2: adjust the reserve — subtract the estimate, add the actual usage.
424
- // This correctly reduces spent when actualUsage < ESTIMATE.
425
- wfState.spent += (parsed.usage?.input ?? 0) + (parsed.usage?.output ?? 0) - ESTIMATE;
426
- reserved = false;
427
- let text = childResult.rawFinalText || parsed.finalText || "";
428
- // Round-11 test fix: parsePiJsonOutput only extracts text from pi event stream
429
- // ({type:"message_end", message:{role:"assistant", content:[...]}}). When the
430
- // agent emits plain JSON, plain text, or a different format, finalText is empty.
431
- // Fallback to a more permissive extraction that handles multiple output shapes.
432
- if (!text.trim()) {
433
- text = extractTextFallback(childResult.stdout);
434
- }
435
- // Round-13 P0-3: schema validation post-extraction. The schema option is
436
- // additive — when undefined the call site is unchanged. With a schema,
437
- // extracted.error means the worker output didn't match expected shape and
438
- // the script should treat the result as failed (ok:false, error set).
439
- const extracted = extractStructuredResult(text, call.schema);
440
- // Write a side artifact for audit/isolation (§0b G3).
441
- const rel = `wf/${Date.now()}-${randomBytes(4).toString("hex")}.md`;
442
- const artifact = writeArtifact(manifest.artifactsRoot, {
443
- kind: "result",
444
- relativePath: rel,
445
- content: text,
446
- producer: "dynamic-workflow",
447
- });
448
- if (call.schema !== undefined && !extracted.structured) {
449
- // BDG-2: un-reserve was already done above (reserved = false after adjust).
450
- return {
451
- ok: false,
452
- text,
453
- usage: parsed.usage,
454
- artifactPath: artifact.path,
455
- error: extracted.error ?? "structured output does not match schema",
456
- durationMs: Date.now() - started,
457
- };
458
- }
459
- // PERS-1: cache the successful result for idempotency on resume.
460
- completedAgentCalls[callId] = {
461
- text,
462
- usage: { input: parsed.usage?.input ?? 0, output: parsed.usage?.output ?? 0 },
463
- };
464
- return {
465
- ok: true,
466
- text,
467
- structured: extracted.structured ? extracted.data : undefined,
468
- usage: parsed.usage,
469
- artifactPath: artifact.path,
470
- durationMs: Date.now() - started,
471
- };
472
- } catch (error) {
473
- // BDG-2: un-reserve the estimate on failure (only if still reserved).
474
- if (reserved) wfState.spent -= ESTIMATE;
475
- logInternalError("dynamic-workflow-context.agent", error, `runId=${manifest.runId}`);
476
- return {
477
- ok: false,
478
- text: "",
479
- error: error instanceof Error ? error.message : String(error),
480
- durationMs: Date.now() - started,
481
- };
482
- } finally {
483
- // round-17 P2-4: clean up the worktree after the agent completes (success
484
- // OR failure). Captures the diff as an artifact before removal. Best-effort
485
- // — a leak must never crash the workflow.
486
- if (worktreePath) {
487
- try {
488
- await cleanupAgentWorktreeAsync(manifest, worktreePath, worktreeBranch);
489
- } catch (cleanupError) {
490
- logInternalError("dynamic-workflow-context.worktree-cleanup", cleanupError, `worktreePath=${worktreePath}`);
491
- }
492
- }
493
- // round-18 P2-3: checkpoint AFTER the agent completes (success or fail) so a
494
- // crash between agent calls leaves durable state to resume from. The counter is
495
- // incremented here (after the call) so the checkpoint reflects the call that ran.
496
- agentCount++;
497
- if (opts.onCheckpoint) {
498
- try {
499
- opts.onCheckpoint({
500
- runId: manifest.runId,
501
- vars: ctx.vars,
502
- phases: phaseState.phases,
503
- currentPhase: phaseState.currentPhase,
504
- logs: wfState.logs.slice(0, 1000),
505
- spent: wfState.spent,
506
- agentCount,
507
- completedAgentCalls,
508
- updatedAt: new Date().toISOString(),
509
- });
510
- } catch (checkpointError) {
511
- logInternalError("dynamic-workflow-context.checkpoint", checkpointError, `runId=${manifest.runId}`);
512
- }
513
- }
514
- semaphore.release();
515
- }
516
- },
517
- async fanOut<T>(items: T[], limit: number, fn: (item: T, i: number) => Promise<AgentResult>): Promise<AgentResult[]> {
518
- return mapConcurrent(items, Math.max(1, limit), fn);
519
- },
520
- async pipeline<TItem, TResult = unknown>(
521
- items: TItem[],
522
- ...stages: Array<(previous: TResult, original: TItem, index: number) => Promise<TResult> | TResult>
523
- ): Promise<(TResult | null)[]> {
524
- if (!Array.isArray(items)) {
525
- throw new TypeError("pipeline() expects an array as the first argument");
526
- }
527
- if (stages.length === 0 || stages.some((s) => typeof s !== "function")) {
528
- throw new TypeError("pipeline() stages must be functions");
529
- }
530
- if (items.length === 0) return [];
531
- // Parallel across items, bounded by the workflow concurrency (mirrors fanOut).
532
- // Per-item stages run sequentially. A failed stage yields null for that item
533
- // (logged via ctx.log) and the remaining items continue. Aborts propagate.
534
- return mapConcurrent(items, concurrency, async (item, index): Promise<TResult | null> => {
535
- let value: unknown = item;
536
- for (const stage of stages) {
537
- try {
538
- value = await stage(value as TResult, item, index);
539
- } catch (error) {
540
- if (opts.signal.aborted) throw error;
541
- ctx.log(`pipeline[${index}] failed: ${error instanceof Error ? error.message : String(error)}`);
542
- return null;
543
- }
544
- }
545
- return value as TResult;
546
- });
547
- },
548
- async review(
549
- taskId: string,
550
- reviewerRole = "reviewer",
551
- reviewOpts?: {
552
- content?: string;
553
- artifactPath?: string;
554
- disableTools?: boolean;
555
- },
556
- ): Promise<{
557
- outcome: "accept" | "reject" | "changes_requested";
558
- feedback: string;
559
- }> {
560
- // review() is a VERDICT step: it must produce a parseable JSON {outcome, feedback}, not a
561
- // free-form markdown review. The resolved reviewer agent (e.g. ~/.pi/agent/agents/reviewer.md)
562
- // has tools (read/grep/bash) + a markdown-output system prompt. Without disableTools, the
563
- // reviewer explores the repo looking for the task's work, loops, and gets killed (exit 143)
564
- // before producing JSON — leaving text="" and the fallback verdict. Default: disableTools so
565
- // the reviewer judges the provided content (or taskId context) directly.
566
- const disableTools = reviewOpts?.disableTools !== false; // default true
567
- const workContext = reviewOpts?.content
568
- ? `\n\nWork to review:\n"""\n${reviewOpts.content}\n"""`
569
- : reviewOpts?.artifactPath
570
- ? `\n\nRead the work from artifact: ${reviewOpts.artifactPath}`
571
- : "";
572
- const res = await ctx.agent({
573
- role: reviewerRole,
574
- prompt: `You are reviewing the work for task '${taskId}'.${workContext}\n\nEvaluate the work and respond with ONLY a single JSON object, no prose, no markdown:\n{"outcome":"accept|reject|changes_requested","feedback":"<one-paragraph explanation>"}\n\n- "accept": work is complete and correct.\n- "reject": work is fundamentally wrong.\n- "changes_requested": work needs revision (explain what in feedback).`,
575
- maxTurns: 3,
576
- disableTools,
577
- systemPrompt:
578
- 'You are a JSON verdict judge. You output ONLY a single JSON object with keys "outcome" (one of accept/reject/changes_requested) and "feedback" (a concise explanation). Never output prose, markdown, or code fences. Begin your response with { and end with }.',
579
- });
580
- const extracted = res.structured as { outcome?: string; feedback?: string } | undefined;
581
- if (extracted && typeof extracted.outcome === "string" && typeof extracted.feedback === "string") {
582
- const outcome =
583
- extracted.outcome === "accept" || extracted.outcome === "reject" || extracted.outcome === "changes_requested"
584
- ? extracted.outcome
585
- : "changes_requested";
586
- return { outcome, feedback: extracted.feedback };
587
- }
588
- // Fallback (round-11 runtime): many models (e.g. MiniMax-M3) ignore JSON-output
589
- // instructions and produce a prose review instead. Rather than report an
590
- // unparseable verdict, run a tiny judge call that converts the prose review into a
591
- // JSON verdict. This guarantees ctx.review() always returns a structured verdict
592
- // regardless of the reviewer's output format. Skipped when the reviewer produced
593
- // no text at all (genuine failure).
594
- if (res.text.trim()) {
595
- const judge = await ctx.agent({
596
- role: reviewerRole,
597
- prompt: `Convert the following code review into a verdict JSON. Read the review and decide the outcome.\n\nREVIEW:\n"""\n${res.text.slice(0, 4000)}\n"""\n\nRespond with ONLY a JSON object:\n{"outcome":"accept|reject|changes_requested","feedback":"<concise summary>"}\n- accept: review found no real issues.\n- reject: review found critical/fundamental problems.\n- changes_requested: review found issues that need fixing.`,
598
- maxTurns: 1,
599
- disableTools: true,
600
- systemPrompt:
601
- "You output ONLY a single JSON object with keys outcome and feedback. Begin with { and end with }. Never output prose.",
602
- });
603
- const judged = judge.structured as { outcome?: string; feedback?: string } | undefined;
604
- if (judged && typeof judged.outcome === "string" && typeof judged.feedback === "string") {
605
- const outcome =
606
- judged.outcome === "accept" || judged.outcome === "reject" || judged.outcome === "changes_requested"
607
- ? judged.outcome
608
- : "changes_requested";
609
- return { outcome, feedback: judged.feedback };
610
- }
611
- }
612
- // Tier-3 sentiment fallback (round-11): when neither the reviewer nor the judge
613
- // produced JSON (common with MiniMax-M3, GLM, which ignore JSON-output
614
- // instructions), classify the outcome from the REVIEWER's prose sentiment. We use
615
- // the reviewer's text (not the judge's terse output) because the original review is
616
- // the richest sentiment signal. This keeps outcome ACCURATE (accept vs reject vs
617
- // changes_requested) even when no JSON is ever produced — without it, outcome was
618
- // always the hardcoded 'changes_requested' default (e.g. correct code was
619
- // misclassified as needing changes).
620
- if (res.text.trim()) {
621
- return {
622
- outcome: classifyReviewOutcome(res.text),
623
- feedback: res.text,
624
- };
625
- }
626
- return {
627
- outcome: "changes_requested",
628
- feedback: res.text || "(reviewer produced no parseable verdict)",
629
- };
630
- },
631
- async retry(taskId: string, retryOpts?: { feedback?: string }): Promise<AgentResult> {
632
- return executeWithRetry(
633
- async () =>
634
- ctx.agent({
635
- role: "executor",
636
- prompt: `Re-do task '${taskId}'.${retryOpts?.feedback ? ` Feedback: ${retryOpts.feedback}` : ""}`,
637
- }),
638
- {
639
- maxAttempts: 3,
640
- backoffMs: 0,
641
- jitterRatio: 0,
642
- exponentialFactor: 1,
643
- },
644
- );
645
- },
646
- mail(
647
- to: string,
648
- body: string,
649
- mailOpts?: {
650
- kind?: string;
651
- taskId?: string;
652
- replyTo?: string;
653
- replyDeadline?: number;
654
- },
655
- ): string {
656
- const msg = appendMailboxMessage(manifest, {
657
- direction: "outbox",
658
- from: "dynamic-workflow",
659
- to,
660
- body,
661
- kind: (mailOpts?.kind as never) ?? "message",
662
- taskId: mailOpts?.taskId,
663
- replyTo: mailOpts?.replyTo,
664
- replyDeadline: mailOpts?.replyDeadline,
665
- });
666
- return msg.id;
667
- },
668
- async gatherReplies(messageIds: string[], deadlineMs: number): Promise<unknown[]> {
669
- const deadline = Date.now() + deadlineMs;
670
- while (Date.now() < deadline) {
671
- const inbox = readMailbox(manifest, "inbox");
672
- const got = inbox.filter((m) => m.replyTo && messageIds.includes(m.replyTo));
673
- if (got.length >= messageIds.length) return got;
674
- await new Promise((r) => setTimeout(r, 500));
675
- if (opts.signal.aborted) return inbox.filter((m) => m.replyTo && messageIds.includes(m.replyTo));
676
- }
677
- return readMailbox(manifest, "inbox").filter((m) => m.replyTo && messageIds.includes(m.replyTo));
678
- },
679
- renderTemplate(name: string, vars: Record<string, string>): unknown {
680
- return renderPlanTemplate(name, vars);
681
- },
682
- vars: opts.resumedState ? { ...opts.resumedState.vars } : ({} as Record<string, unknown>),
683
- setResult(artifactPath: string, meta?: Record<string, unknown>): void {
684
- finalResult = { artifactPath, meta };
685
- },
686
- phase(title: string): void {
687
- if (typeof title !== "string" || title.length === 0) {
688
- throw new TypeError("ctx.phase(title) requires a non-empty string title.");
689
- }
690
- // Idempotency: same phase title → no event, no state change.
691
- if (title === phaseState.currentPhase) return;
692
- // Close out the previous open phase BEFORE the new one opens.
693
- if (phaseState.currentPhase !== undefined) {
694
- appendEvent(manifest.eventsPath, {
695
- type: "dwf.phase_completed",
696
- runId: manifest.runId,
697
- data: { phase: phaseState.currentPhase },
698
- });
699
- }
700
- phaseState.currentPhase = title;
701
- // Dedup append with hard cap to bound memory; events still flow.
702
- if (!phaseState.phases.includes(title)) {
703
- if (phaseState.phases.length < 100) {
704
- phaseState.phases.push(title);
705
- } else if (!phaseCapWarned) {
706
- phaseCapWarned = true;
707
- logInternalError(
708
- "dynamic-workflow-context.phase-cap",
709
- new Error(
710
- "Phase list cap of 100 reached; further phases still emit events but are not added to the in-memory phases[] list. Use the events log as the durable source of truth.",
711
- ),
712
- `runId=${manifest.runId}`,
713
- );
714
- }
715
- }
716
- appendEvent(manifest.eventsPath, {
717
- type: "dwf.phase_started",
718
- runId: manifest.runId,
719
- data: { phase: title },
720
- });
721
- },
722
- budget,
723
- log(message: unknown): void {
724
- // round-14 P1-3: stringify non-strings, keep a bounded in-memory copy, and
725
- // always emit a dwf.log event (the events log is the durable source of truth).
726
- const text = typeof message === "string" ? message : JSON.stringify(message);
727
- if (wfState.logs.length < 1000) {
728
- wfState.logs.push(text);
729
- }
730
- appendEvent(manifest.eventsPath, {
731
- type: "dwf.log",
732
- runId: manifest.runId,
733
- data: { message: text },
734
- });
735
- },
736
- args<T = unknown>(): T {
737
- // round-14 P1-5: typed workflow args sourced from manifest (via opts.args).
738
- return wfState.args as T;
739
- },
740
- };
741
-
742
- // Attach the final-result slot via a non-enumerable getter so the runner can read it
743
- // without exposing a mutation surface on the ctx the script sees.
744
- Object.defineProperty(ctx, "__finalResult", {
745
- get: () => finalResult,
746
- enumerable: false,
747
- });
748
- // round-12 P0-1: phase state is read-only from the runner; the script can only mutate
749
- // it via ctx.phase(title), which is the documented public surface.
750
- Object.defineProperty(ctx, "__phaseState", {
751
- get: () => phaseState,
752
- enumerable: false,
753
- });
754
- // round-14 P1-3: in-memory log buffer is read-only from the runner; the script can only
755
- // append via ctx.log(message). The events log remains the durable source of truth.
756
- Object.defineProperty(ctx, "__logs", {
757
- get: () => wfState.logs,
758
- enumerable: false,
759
- });
760
- // round-18 P2-3: agent invocation counter is read-only from the runner. The script can
761
- // only advance it via ctx.agent() (incremented in agent()'s finally). Exposed so
762
- // getWorkflowCheckpoint() can report an accurate count.
763
- Object.defineProperty(ctx, "__agentCount", {
764
- get: () => agentCount,
765
- enumerable: false,
766
- });
767
- // PERS-1: completedAgentCalls is read-only from the runner; the agent() method
768
- // is the only writer. Exposed so getWorkflowCheckpoint() can include it.
769
- Object.defineProperty(ctx, "__completedAgentCalls", {
770
- get: () => completedAgentCalls,
771
- enumerable: false,
772
- });
773
- return ctx;
774
- }
775
-
776
- /** Read the final result set by the script (runner-only; not part of the public ctx surface). */
777
- export function getWorkflowFinalResult(ctx: WorkflowCtx): { artifactPath: string; meta?: Record<string, unknown> } | undefined {
778
- return (
779
- ctx as unknown as {
780
- __finalResult?: {
781
- artifactPath: string;
782
- meta?: Record<string, unknown>;
783
- };
784
- }
785
- ).__finalResult;
786
- }
787
-
788
- /** Read the in-memory phase state set by the script (runner-only; not part of the public ctx surface). */
789
- export function getWorkflowPhaseState(ctx: WorkflowCtx): { currentPhase: string | undefined; phases: string[] } | undefined {
790
- return (
791
- ctx as unknown as {
792
- __phaseState?: {
793
- currentPhase: string | undefined;
794
- phases: string[];
795
- };
796
- }
797
- ).__phaseState;
798
- }
799
-
800
- /** Read the in-memory log buffer appended by ctx.log() (runner-only; not part of the public ctx surface).
801
- * Capped at 1000 entries — the events log (dwf.log) is the durable source of truth. */
802
- export function getWorkflowLogs(ctx: WorkflowCtx): string[] | undefined {
803
- return (ctx as unknown as { __logs?: string[] }).__logs;
804
- }
805
-
806
- /** round-18 P2-3: snapshot the current DWF checkpoint state (runner-only; not part of the public
807
- * ctx surface). Mirrors getWorkflowFinalResult/getWorkflowPhaseState. The runner relies on the
808
- * `onCheckpoint` callback for accurate per-agent-call checkpoints (it captures the closure value
809
- * at call time); this helper is a best-effort snapshot for inspection/debugging. */
810
- export function getWorkflowCheckpoint(ctx: WorkflowCtx): DwfCheckpointState {
811
- const phaseState = getWorkflowPhaseState(ctx);
812
- const logs = getWorkflowLogs(ctx);
813
- return {
814
- runId: ctx.runId,
815
- vars: ctx.vars,
816
- phases: phaseState?.phases ?? [],
817
- currentPhase: phaseState?.currentPhase,
818
- logs: logs ?? [],
819
- spent: ctx.budget.spent(),
820
- agentCount: (ctx as unknown as { __agentCount?: number }).__agentCount ?? 0,
821
- completedAgentCalls:
822
- (ctx as unknown as { __completedAgentCalls?: Record<string, { text: string; usage?: { input: number; output: number } }> })
823
- .__completedAgentCalls ?? {},
824
- updatedAt: new Date().toISOString(),
825
- };
826
- }
827
-
828
- /** Compose the agent task: prompt + optional dependency-input context block. */
829
- function composeAgentTask(call: AgentCallOpts): string {
830
- let base = call.prompt;
831
- if (call.inputs?.length) {
832
- const block = call.inputs.map((p) => `- ${p}`).join("\n");
833
- base = `${base}\n\n## Inputs (artifact paths)\n${block}`;
834
- }
835
- // Round-13 P0-3: when a schema is requested, append a JSON-output directive.
836
- // The directive lives at the END of the prompt so it wins over any conflicting
837
- // persona instruction in the agent's system prompt.
838
- if (call.schema !== undefined) {
839
- base = `${base}\n\n## Output format\nRespond with ONLY a single JSON object that matches the schema described in your instructions. Begin your response with { and end with }. Do not wrap the JSON in a code fence. Do not add any prose before or after the JSON.`;
840
- }
841
- return base;
842
- }
843
-
844
- /**
845
- * Round-13 P0-3: compose a system-prompt suffix that asks the agent to output a
846
- * structured JSON object matching the schema's required shape. We don't expose
847
- * the TypeBox internal type — we describe the SHAPE so the model can match it.
848
- */
849
- function composeSchemaSystemPrompt(base: string | undefined, schema: TSchema): string {
850
- const shape = describeSchemaShape(schema, 0);
851
- const intro = "You are a structured-output assistant. ";
852
- const instruction = `When responding, output ONLY a single JSON object matching this shape (no prose, no markdown fences, no commentary): ${shape}. Begin your response with { and end with }.`;
853
- if (typeof base === "string" && base.length > 0) {
854
- return `${base}\n\n${intro}${instruction}`;
855
- }
856
- return `${intro}${instruction}`;
857
- }
858
-
859
- /**
860
- * Walk a TypeBox schema recursively and produce a human-readable shape description.
861
- * Depth-limited to avoid runaway expansion on deeply nested schemas.
862
- */
863
- function describeSchemaShape(schema: unknown, depth: number): string {
864
- if (depth > 4) return "{...}";
865
- if (!schema || typeof schema !== "object") return "any";
866
- const obj = schema as Record<string, unknown>;
867
- // TypeBox: every schema has a `type` discriminator or a `kind` field.
868
- const kind = obj.kind as string | undefined;
869
- const type = obj.type as string | undefined;
870
- if (kind === "object" || type === "object") {
871
- const properties = obj.properties;
872
- if (!properties || typeof properties !== "object") return "{}";
873
- const required = Array.isArray(obj.required) ? new Set(obj.required as string[]) : new Set<string>();
874
- const props = Object.entries(properties as Record<string, unknown>)
875
- .map(([key, sub]) => {
876
- const mark = required.has(key) ? "" : "?";
877
- return `"${key}"${mark}: ${describeSchemaShape(sub, depth + 1)}`;
878
- })
879
- .join(", ");
880
- return `{${props}}`;
881
- }
882
- if (kind === "array" || type === "array") {
883
- const items = obj.items;
884
- return `[${describeSchemaShape(items, depth + 1)}]`;
885
- }
886
- if (type === "string") return "string";
887
- if (type === "number" || type === "integer") return "number";
888
- if (type === "boolean") return "boolean";
889
- if (type === "null") return "null";
890
- // Union/Enum fallbacks.
891
- if (Array.isArray(obj.anyOf)) return obj.anyOf.map((s) => describeSchemaShape(s, depth + 1)).join(" | ");
892
- if (Array.isArray(obj.oneOf)) return obj.oneOf.map((s) => describeSchemaShape(s, depth + 1)).join(" | ");
893
- if (Array.isArray(obj.enum)) return obj.enum.map((v) => JSON.stringify(v)).join(" | ");
894
- return "any";
895
- }
896
-
897
- /**
898
- * Classify a review outcome from prose when no JSON was produced (round-11 tier-3/4 fallback).
899
- * Scans the reviewer's prose for sentiment signals to decide accept / reject / changes_requested.
900
- * This keeps the outcome ACCURATE for models that ignore JSON-output instructions.
901
- *
902
- * Decision order: reject (critical issues) → accept (explicit approval) → changes_requested (default).
903
- * reject is checked first because a review can mention both "correctly" (describing existing code)
904
- * AND "critical bug" (the verdict) — the verdict signal must win.
905
- */
906
- export function classifyReviewOutcome(prose: string): "accept" | "reject" | "changes_requested" {
907
- const text = prose.toLowerCase();
908
- // Strong negative signals → reject. These indicate fundamental/critical problems.
909
- const rejectSignals = [
910
- "\breject\b",
911
- "fundamentally",
912
- "completely broken",
913
- "totally broken",
914
- "critical bug",
915
- "critical issue",
916
- "critical flaw",
917
- "security vulnerability",
918
- "does not work",
919
- "doesn't work",
920
- "will not work",
921
- "fails to",
922
- "unacceptable",
923
- "must not be merged",
924
- "do not merge",
925
- "wrong approach",
926
- "logically incorrect",
927
- "incorrectly implements",
928
- "returns the opposite",
929
- "subtraction instead of addition",
930
- "opposite of its intended",
931
- ];
932
- // Acceptance signals → accept. These indicate explicit approval with no real issues.
933
- const acceptSignals = [
934
- "\baccept\b",
935
- "looks good",
936
- "well done",
937
- "no issues",
938
- "no real issues",
939
- "no problems",
940
- "no concerns",
941
- "nothing to change",
942
- "ready to merge",
943
- "lgtm",
944
- "ship it",
945
- "correctly implements",
946
- "correctly returns",
947
- "works as expected",
948
- "works correctly",
949
- "no bugs",
950
- "no defects",
951
- "meets all requirements",
952
- "all requirements met",
953
- "passes all",
954
- "is correct",
955
- "are correct",
956
- "no changes needed",
957
- "no changes required",
958
- "no further changes",
959
- "nothing more to",
960
- "complete and correct",
961
- "sound implementation",
962
- ];
963
- const hasReject = rejectSignals.some((sig) => new RegExp(sig).test(text));
964
- const hasAccept = acceptSignals.some((sig) => new RegExp(sig).test(text));
965
- if (hasReject) return "reject";
966
- if (hasAccept) return "accept";
967
- return "changes_requested";
968
- }
969
-
970
- /**
971
- * Round-11 test fix: permissive text extraction for ctx.agent().
972
- * parsePiJsonOutput only handles the canonical pi event stream. When the child emits
973
- * a different shape, finalText is empty. This fallback walks the JSON tree looking
974
- * for any text-shaped string at any depth, then returns the longest one (typically
975
- * the final assistant response).
976
- */
977
- export function extractTextFallback(stdout: string): string {
978
- const trimmed = stdout.trim();
979
- if (!trimmed) return "";
980
- const candidates: string[] = [];
981
- const collect = (value: unknown): void => {
982
- if (typeof value === "string") {
983
- const t = value.trim();
984
- // Skip very short strings and JSON-ish strings
985
- if (t.length >= 2 && !t.startsWith("{") && !t.startsWith("[") && !/^[\d.]+$/.test(t)) {
986
- candidates.push(t);
987
- }
988
- } else if (Array.isArray(value)) {
989
- for (const item of value) collect(item);
990
- } else if (value && typeof value === "object") {
991
- for (const v of Object.values(value as Record<string, unknown>)) collect(v);
992
- }
993
- };
994
- // 1. Try parsing each line as JSON, walk tree
995
- for (const line of trimmed.split("\n")) {
996
- const lineTrim = line.trim();
997
- if (!lineTrim.startsWith("{")) continue;
998
- try {
999
- const obj = JSON.parse(lineTrim);
1000
- collect(obj);
1001
- } catch {
1002
- /* skip */
1003
- }
1004
- }
1005
- // 2. If nothing from JSON, try plain text (longest non-empty line that's not JSON)
1006
- if (candidates.length === 0) {
1007
- for (const line of trimmed.split("\n")) {
1008
- const l = line.trim();
1009
- if (l.length >= 3 && !l.startsWith("{") && !l.startsWith("[") && !l.startsWith("=")) candidates.push(l);
1010
- }
1011
- }
1012
- // 3. Return the longest candidate (typically the final answer)
1013
- if (candidates.length === 0) return "";
1014
- candidates.sort((a, b) => b.length - a.length);
1015
- return candidates[0];
1016
- }