gsd-pi 2.79.0-dev.ece5fd8ba → 2.80.0-dev.4ea7d80e7

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 (681) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/github-sync/templates.js +90 -74
  3. package/dist/resources/extensions/gsd/auto/contracts.js +1 -0
  4. package/dist/resources/extensions/gsd/auto/custom-verify-retry-store.js +53 -0
  5. package/dist/resources/extensions/gsd/auto/loop.js +365 -522
  6. package/dist/resources/extensions/gsd/auto/orchestrator.js +146 -0
  7. package/dist/resources/extensions/gsd/auto/phases.js +55 -6
  8. package/dist/resources/extensions/gsd/auto/run-unit.js +19 -15
  9. package/dist/resources/extensions/gsd/auto/session.js +8 -0
  10. package/dist/resources/extensions/gsd/auto/workflow-custom-engine-dispatch-outcome.js +12 -0
  11. package/dist/resources/extensions/gsd/auto/workflow-custom-engine-iteration.js +24 -0
  12. package/dist/resources/extensions/gsd/auto/workflow-custom-engine-reconcile-outcome.js +33 -0
  13. package/dist/resources/extensions/gsd/auto/workflow-custom-engine-reconcile.js +26 -0
  14. package/dist/resources/extensions/gsd/auto/workflow-custom-engine-retry.js +49 -0
  15. package/dist/resources/extensions/gsd/auto/workflow-custom-engine-verify-outcome.js +25 -0
  16. package/dist/resources/extensions/gsd/auto/workflow-dispatch-claim.js +48 -0
  17. package/dist/resources/extensions/gsd/auto/workflow-dispatch-ledger.js +26 -0
  18. package/dist/resources/extensions/gsd/auto/workflow-iteration-completion.js +10 -0
  19. package/dist/resources/extensions/gsd/auto/workflow-journal-reporter.js +16 -0
  20. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +263 -0
  21. package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +36 -0
  22. package/dist/resources/extensions/gsd/auto/workflow-phase-reporter.js +9 -0
  23. package/dist/resources/extensions/gsd/auto/workflow-session-lock.js +35 -0
  24. package/dist/resources/extensions/gsd/auto/workflow-sidecar-iteration.js +24 -0
  25. package/dist/resources/extensions/gsd/auto/workflow-sidecar-queue.js +26 -0
  26. package/dist/resources/extensions/gsd/auto/workflow-turn-reporter.js +36 -0
  27. package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +44 -0
  28. package/dist/resources/extensions/gsd/auto/workflow-worker-heartbeat.js +15 -0
  29. package/dist/resources/extensions/gsd/auto-dashboard.js +54 -15
  30. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  31. package/dist/resources/extensions/gsd/auto-prompts.js +168 -3
  32. package/dist/resources/extensions/gsd/auto-recovery.js +198 -59
  33. package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -0
  34. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  35. package/dist/resources/extensions/gsd/auto-verification.js +2 -11
  36. package/dist/resources/extensions/gsd/auto-worktree.js +87 -38
  37. package/dist/resources/extensions/gsd/auto.js +168 -3
  38. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +24 -2
  39. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
  40. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +22 -6
  41. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  42. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  43. package/dist/resources/extensions/gsd/commands-ship.js +23 -46
  44. package/dist/resources/extensions/gsd/commands-workflow-templates.js +12 -7
  45. package/dist/resources/extensions/gsd/component-loader.js +5 -11
  46. package/dist/resources/extensions/gsd/custom-workflow-engine.js +25 -1
  47. package/dist/resources/extensions/gsd/db-adapter.js +47 -0
  48. package/dist/resources/extensions/gsd/db-base-schema.js +351 -0
  49. package/dist/resources/extensions/gsd/db-connection-cache.js +31 -0
  50. package/dist/resources/extensions/gsd/db-coordination-schema.js +104 -0
  51. package/dist/resources/extensions/gsd/db-decision-requirement-rows.js +71 -0
  52. package/dist/resources/extensions/gsd/db-gate-rows.js +16 -0
  53. package/dist/resources/extensions/gsd/db-lightweight-query-rows.js +29 -0
  54. package/dist/resources/extensions/gsd/db-memory-fts-schema.js +56 -0
  55. package/dist/resources/extensions/gsd/db-migration-backup.js +22 -0
  56. package/dist/resources/extensions/gsd/db-migration-steps.js +410 -0
  57. package/dist/resources/extensions/gsd/db-milestone-artifact-rows.js +35 -0
  58. package/dist/resources/extensions/gsd/db-open-state.js +32 -0
  59. package/dist/resources/extensions/gsd/db-provider.js +108 -0
  60. package/dist/resources/extensions/gsd/db-runtime-kv-schema.js +27 -0
  61. package/dist/resources/extensions/gsd/db-schema-metadata.js +23 -0
  62. package/dist/resources/extensions/gsd/db-task-slice-rows.js +86 -0
  63. package/dist/resources/extensions/gsd/db-transaction.js +63 -0
  64. package/dist/resources/extensions/gsd/db-verification-evidence-rows.js +3 -0
  65. package/dist/resources/extensions/gsd/db-verification-evidence-schema.js +19 -0
  66. package/dist/resources/extensions/gsd/escalation.js +2 -0
  67. package/dist/resources/extensions/gsd/graph.js +9 -3
  68. package/dist/resources/extensions/gsd/gsd-db.js +316 -1520
  69. package/dist/resources/extensions/gsd/guided-flow.js +2 -2
  70. package/dist/resources/extensions/gsd/legacy-telemetry.js +70 -0
  71. package/dist/resources/extensions/gsd/markdown-renderer.js +2 -0
  72. package/dist/resources/extensions/gsd/model-router.js +9 -6
  73. package/dist/resources/extensions/gsd/notification-widget.js +21 -3
  74. package/dist/resources/extensions/gsd/post-execution-checks.js +27 -6
  75. package/dist/resources/extensions/gsd/pr-evidence.js +117 -0
  76. package/dist/resources/extensions/gsd/pre-execution-checks.js +2 -0
  77. package/dist/resources/extensions/gsd/process-task-path.js +61 -0
  78. package/dist/resources/extensions/gsd/prompt-loader.js +9 -5
  79. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +32 -30
  80. package/dist/resources/extensions/gsd/prompts/complete-slice.md +23 -34
  81. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +50 -96
  82. package/dist/resources/extensions/gsd/prompts/discuss.md +81 -181
  83. package/dist/resources/extensions/gsd/prompts/execute-task.md +40 -67
  84. package/dist/resources/extensions/gsd/prompts/forensics.md +41 -84
  85. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +29 -39
  86. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +30 -65
  87. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +25 -52
  88. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +36 -36
  89. package/dist/resources/extensions/gsd/prompts/guided-research-project.md +20 -38
  90. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +45 -59
  91. package/dist/resources/extensions/gsd/prompts/plan-slice.md +25 -87
  92. package/dist/resources/extensions/gsd/prompts/queue.md +46 -53
  93. package/dist/resources/extensions/gsd/prompts/refine-slice.md +23 -23
  94. package/dist/resources/extensions/gsd/prompts/research-slice.md +23 -23
  95. package/dist/resources/extensions/gsd/prompts/rethink.md +10 -10
  96. package/dist/resources/extensions/gsd/prompts/system.md +65 -107
  97. package/dist/resources/extensions/gsd/prompts/triage-captures.md +15 -15
  98. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +24 -24
  99. package/dist/resources/extensions/gsd/prompts/worktree-merge.md +35 -35
  100. package/dist/resources/extensions/gsd/state.js +4 -0
  101. package/dist/resources/extensions/gsd/tools/complete-milestone.js +14 -9
  102. package/dist/resources/extensions/gsd/tools/complete-task.js +2 -0
  103. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +6 -1
  104. package/dist/resources/extensions/gsd/unit-context-composer.js +1 -1
  105. package/dist/resources/extensions/gsd/uok/kernel.js +8 -3
  106. package/dist/resources/extensions/gsd/uok/plan-v2.js +2 -0
  107. package/dist/resources/extensions/gsd/workflow-logger.js +13 -13
  108. package/dist/resources/extensions/gsd/workflow-manifest.js +2 -0
  109. package/dist/resources/extensions/gsd/workflow-projections.js +2 -0
  110. package/dist/resources/extensions/gsd/workflow-templates.js +9 -0
  111. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  112. package/dist/resources/extensions/shared/interview-ui.js +15 -4
  113. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  114. package/dist/web/standalone/.next/BUILD_ID +1 -1
  115. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  116. package/dist/web/standalone/.next/build-manifest.json +3 -3
  117. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  118. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  119. package/dist/web/standalone/.next/required-server-files.json +1 -1
  120. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  121. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  129. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  138. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  162. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  168. package/dist/web/standalone/.next/server/app/api/notifications/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  170. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  174. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  176. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  178. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  182. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  184. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  186. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  188. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  190. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  192. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  194. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  196. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  198. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  200. package/dist/web/standalone/.next/server/app/index.html +1 -1
  201. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  202. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  203. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  204. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  205. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  206. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  207. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  208. package/dist/web/standalone/.next/server/chunks/167.js +2 -0
  209. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  210. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  211. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  213. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  214. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  215. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  216. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  217. package/dist/web/standalone/package.json +1 -0
  218. package/dist/web/standalone/server.js +1 -1
  219. package/package.json +18 -7
  220. package/packages/contracts/dist/index.d.ts +3 -0
  221. package/packages/contracts/dist/index.d.ts.map +1 -0
  222. package/packages/contracts/dist/index.js +5 -0
  223. package/packages/contracts/dist/index.js.map +1 -0
  224. package/packages/contracts/dist/rpc.d.ts +549 -0
  225. package/packages/contracts/dist/rpc.d.ts.map +1 -0
  226. package/packages/contracts/dist/rpc.js +53 -0
  227. package/packages/contracts/dist/rpc.js.map +1 -0
  228. package/packages/contracts/dist/rpc.test.d.ts +2 -0
  229. package/packages/contracts/dist/rpc.test.d.ts.map +1 -0
  230. package/packages/contracts/dist/rpc.test.js +47 -0
  231. package/packages/contracts/dist/rpc.test.js.map +1 -0
  232. package/packages/contracts/dist/workflow.d.ts +180 -0
  233. package/packages/contracts/dist/workflow.d.ts.map +1 -0
  234. package/packages/contracts/dist/workflow.js +201 -0
  235. package/packages/contracts/dist/workflow.js.map +1 -0
  236. package/packages/contracts/package.json +39 -0
  237. package/packages/contracts/src/index.ts +5 -0
  238. package/packages/contracts/src/rpc.test.ts +72 -0
  239. package/packages/contracts/src/rpc.ts +286 -0
  240. package/packages/contracts/src/workflow.ts +213 -0
  241. package/packages/contracts/tsconfig.json +25 -0
  242. package/packages/daemon/package.json +3 -2
  243. package/packages/daemon/src/event-bridge.test.ts +2 -1
  244. package/packages/daemon/src/event-bridge.ts +1 -1
  245. package/packages/daemon/src/event-formatter.test.ts +1 -2
  246. package/packages/daemon/src/event-formatter.ts +1 -2
  247. package/packages/daemon/src/session-manager.ts +2 -2
  248. package/packages/daemon/src/types.ts +3 -18
  249. package/packages/mcp-server/dist/server.d.ts +13 -0
  250. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  251. package/packages/mcp-server/dist/server.js +77 -0
  252. package/packages/mcp-server/dist/server.js.map +1 -1
  253. package/packages/mcp-server/dist/session-manager.js +1 -1
  254. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  255. package/packages/mcp-server/dist/types.d.ts +3 -11
  256. package/packages/mcp-server/dist/types.d.ts.map +1 -1
  257. package/packages/mcp-server/dist/types.js.map +1 -1
  258. package/packages/mcp-server/dist/workflow-tools.d.ts +1 -1
  259. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  260. package/packages/mcp-server/dist/workflow-tools.js +2 -40
  261. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  262. package/packages/mcp-server/package.json +3 -2
  263. package/packages/mcp-server/src/mcp-server.test.ts +138 -0
  264. package/packages/mcp-server/src/server.ts +99 -1
  265. package/packages/mcp-server/src/session-manager.ts +2 -2
  266. package/packages/mcp-server/src/types.ts +7 -18
  267. package/packages/mcp-server/src/workflow-tools.ts +2 -40
  268. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  269. package/packages/native/package.json +1 -1
  270. package/packages/pi-agent-core/package.json +1 -1
  271. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  272. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  273. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  274. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  275. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  276. package/packages/pi-ai/dist/models/index.js +8 -0
  277. package/packages/pi-ai/dist/models/index.js.map +1 -1
  278. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  279. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  280. package/packages/pi-ai/dist/providers/fake.js +319 -0
  281. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  282. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  283. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  284. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  285. package/packages/pi-ai/package.json +1 -1
  286. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  287. package/packages/pi-ai/src/models/index.ts +9 -0
  288. package/packages/pi-ai/src/providers/fake.ts +376 -0
  289. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  290. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  291. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +74 -0
  292. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  293. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  294. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  295. package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
  296. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  297. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
  298. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  299. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  300. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  301. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  302. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
  303. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  304. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  305. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  306. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  307. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  308. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  309. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  310. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  311. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +67 -14
  312. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  313. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  314. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  315. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  316. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  317. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  318. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  319. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  320. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  321. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  322. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  323. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  324. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  325. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  326. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  327. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  328. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  329. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  330. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +27 -6
  331. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  332. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
  333. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  334. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +112 -18
  335. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  336. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  337. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +60 -1
  338. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  339. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
  340. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  341. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  342. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  343. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  344. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  345. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  346. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +12 -1
  347. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  348. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +54 -10
  349. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  350. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  352. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  354. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  355. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  356. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  357. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  358. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  359. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  360. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  361. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  362. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  363. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  364. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  365. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  366. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  367. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  368. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  369. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  370. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  371. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  372. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  373. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  374. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  375. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  376. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -512
  377. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  378. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js +3 -7
  379. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  380. package/packages/pi-coding-agent/package.json +2 -1
  381. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +87 -0
  382. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
  383. package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
  384. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  385. package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
  386. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  387. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  388. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +100 -16
  389. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  390. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  391. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  392. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  393. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  394. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +41 -9
  395. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +124 -18
  396. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
  397. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -1
  398. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  399. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  400. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +75 -9
  401. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  402. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  403. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  404. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  405. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  406. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  407. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  408. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +3 -336
  409. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  410. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  411. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  412. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  413. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  414. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  415. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  416. package/packages/pi-tui/dist/index.d.ts +1 -0
  417. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  418. package/packages/pi-tui/dist/index.js +2 -0
  419. package/packages/pi-tui/dist/index.js.map +1 -1
  420. package/packages/pi-tui/dist/style.d.ts +41 -0
  421. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  422. package/packages/pi-tui/dist/style.js +158 -0
  423. package/packages/pi-tui/dist/style.js.map +1 -0
  424. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  425. package/packages/pi-tui/dist/tui.js +1 -0
  426. package/packages/pi-tui/dist/tui.js.map +1 -1
  427. package/packages/pi-tui/package.json +1 -1
  428. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  429. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  430. package/packages/pi-tui/src/index.ts +9 -0
  431. package/packages/pi-tui/src/style.ts +225 -0
  432. package/packages/pi-tui/src/tui.ts +1 -0
  433. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  434. package/packages/rpc-client/README.md +3 -3
  435. package/packages/rpc-client/dist/index.d.ts +1 -1
  436. package/packages/rpc-client/dist/index.d.ts.map +1 -1
  437. package/packages/rpc-client/dist/index.js.map +1 -1
  438. package/packages/rpc-client/dist/rpc-client.d.ts +2 -6
  439. package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -1
  440. package/packages/rpc-client/dist/rpc-client.js.map +1 -1
  441. package/packages/rpc-client/dist/rpc-types.d.ts +1 -565
  442. package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -1
  443. package/packages/rpc-client/dist/rpc-types.js +3 -11
  444. package/packages/rpc-client/dist/rpc-types.js.map +1 -1
  445. package/packages/rpc-client/package.json +4 -1
  446. package/packages/rpc-client/src/index.ts +1 -1
  447. package/packages/rpc-client/src/rpc-client.ts +3 -6
  448. package/packages/rpc-client/src/rpc-types.ts +3 -398
  449. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  450. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  451. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  452. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  453. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  454. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  455. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  456. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  457. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  458. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  459. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  460. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  461. package/pkg/package.json +1 -1
  462. package/src/resources/extensions/github-sync/templates.ts +93 -88
  463. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  464. package/src/resources/extensions/github-sync/tests/templates.test.ts +10 -2
  465. package/src/resources/extensions/gsd/auto/contracts.ts +87 -0
  466. package/src/resources/extensions/gsd/auto/custom-verify-retry-store.ts +72 -0
  467. package/src/resources/extensions/gsd/auto/loop-deps.ts +10 -3
  468. package/src/resources/extensions/gsd/auto/loop.ts +416 -596
  469. package/src/resources/extensions/gsd/auto/orchestrator.ts +161 -0
  470. package/src/resources/extensions/gsd/auto/phases.ts +82 -8
  471. package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
  472. package/src/resources/extensions/gsd/auto/session.ts +11 -0
  473. package/src/resources/extensions/gsd/auto/workflow-custom-engine-dispatch-outcome.ts +28 -0
  474. package/src/resources/extensions/gsd/auto/workflow-custom-engine-iteration.ts +52 -0
  475. package/src/resources/extensions/gsd/auto/workflow-custom-engine-reconcile-outcome.ts +58 -0
  476. package/src/resources/extensions/gsd/auto/workflow-custom-engine-reconcile.ts +71 -0
  477. package/src/resources/extensions/gsd/auto/workflow-custom-engine-retry.ts +90 -0
  478. package/src/resources/extensions/gsd/auto/workflow-custom-engine-verify-outcome.ts +50 -0
  479. package/src/resources/extensions/gsd/auto/workflow-dispatch-claim.ts +97 -0
  480. package/src/resources/extensions/gsd/auto/workflow-dispatch-ledger.ts +45 -0
  481. package/src/resources/extensions/gsd/auto/workflow-iteration-completion.ts +26 -0
  482. package/src/resources/extensions/gsd/auto/workflow-journal-reporter.ts +33 -0
  483. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +520 -0
  484. package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +58 -0
  485. package/src/resources/extensions/gsd/auto/workflow-phase-reporter.ts +22 -0
  486. package/src/resources/extensions/gsd/auto/workflow-session-lock.ts +68 -0
  487. package/src/resources/extensions/gsd/auto/workflow-sidecar-iteration.ts +46 -0
  488. package/src/resources/extensions/gsd/auto/workflow-sidecar-queue.ts +46 -0
  489. package/src/resources/extensions/gsd/auto/workflow-turn-reporter.ts +68 -0
  490. package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +89 -0
  491. package/src/resources/extensions/gsd/auto/workflow-worker-heartbeat.ts +38 -0
  492. package/src/resources/extensions/gsd/auto-dashboard.ts +61 -8
  493. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  494. package/src/resources/extensions/gsd/auto-prompts.ts +170 -3
  495. package/src/resources/extensions/gsd/auto-recovery.ts +194 -56
  496. package/src/resources/extensions/gsd/auto-runtime-state.ts +7 -0
  497. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  498. package/src/resources/extensions/gsd/auto-verification.ts +5 -1
  499. package/src/resources/extensions/gsd/auto-worktree.ts +85 -36
  500. package/src/resources/extensions/gsd/auto.ts +179 -2
  501. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +30 -2
  502. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +11 -0
  503. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +30 -6
  504. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  505. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  506. package/src/resources/extensions/gsd/commands-ship.ts +24 -51
  507. package/src/resources/extensions/gsd/commands-workflow-templates.ts +13 -0
  508. package/src/resources/extensions/gsd/component-loader.ts +5 -11
  509. package/src/resources/extensions/gsd/custom-workflow-engine.ts +29 -0
  510. package/src/resources/extensions/gsd/db-adapter.ts +75 -0
  511. package/src/resources/extensions/gsd/db-base-schema.ts +383 -0
  512. package/src/resources/extensions/gsd/db-connection-cache.ts +45 -0
  513. package/src/resources/extensions/gsd/db-coordination-schema.ts +109 -0
  514. package/src/resources/extensions/gsd/db-decision-requirement-rows.ts +77 -0
  515. package/src/resources/extensions/gsd/db-gate-rows.ts +19 -0
  516. package/src/resources/extensions/gsd/db-lightweight-query-rows.ts +50 -0
  517. package/src/resources/extensions/gsd/db-memory-fts-schema.ts +66 -0
  518. package/src/resources/extensions/gsd/db-migration-backup.ts +34 -0
  519. package/src/resources/extensions/gsd/db-migration-steps.ts +451 -0
  520. package/src/resources/extensions/gsd/db-milestone-artifact-rows.ts +70 -0
  521. package/src/resources/extensions/gsd/db-open-state.ts +47 -0
  522. package/src/resources/extensions/gsd/db-provider.ts +148 -0
  523. package/src/resources/extensions/gsd/db-runtime-kv-schema.ts +30 -0
  524. package/src/resources/extensions/gsd/db-schema-metadata.ts +33 -0
  525. package/src/resources/extensions/gsd/db-task-slice-rows.ts +146 -0
  526. package/src/resources/extensions/gsd/db-transaction.ts +76 -0
  527. package/src/resources/extensions/gsd/db-verification-evidence-rows.ts +14 -0
  528. package/src/resources/extensions/gsd/db-verification-evidence-schema.ts +22 -0
  529. package/src/resources/extensions/gsd/escalation.ts +3 -1
  530. package/src/resources/extensions/gsd/graph.ts +12 -5
  531. package/src/resources/extensions/gsd/gsd-db.ts +379 -1660
  532. package/src/resources/extensions/gsd/guided-flow.ts +2 -2
  533. package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
  534. package/src/resources/extensions/gsd/legacy-telemetry.ts +99 -0
  535. package/src/resources/extensions/gsd/markdown-renderer.ts +4 -1
  536. package/src/resources/extensions/gsd/model-router.ts +10 -6
  537. package/src/resources/extensions/gsd/notification-widget.ts +25 -4
  538. package/src/resources/extensions/gsd/post-execution-checks.ts +35 -7
  539. package/src/resources/extensions/gsd/pr-evidence.ts +182 -0
  540. package/src/resources/extensions/gsd/pre-execution-checks.ts +4 -1
  541. package/src/resources/extensions/gsd/process-task-path.ts +81 -0
  542. package/src/resources/extensions/gsd/prompt-loader.ts +9 -5
  543. package/src/resources/extensions/gsd/prompts/complete-milestone.md +32 -30
  544. package/src/resources/extensions/gsd/prompts/complete-slice.md +23 -34
  545. package/src/resources/extensions/gsd/prompts/discuss-headless.md +50 -96
  546. package/src/resources/extensions/gsd/prompts/discuss.md +81 -181
  547. package/src/resources/extensions/gsd/prompts/execute-task.md +40 -67
  548. package/src/resources/extensions/gsd/prompts/forensics.md +41 -84
  549. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +29 -39
  550. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +30 -65
  551. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +25 -52
  552. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +36 -36
  553. package/src/resources/extensions/gsd/prompts/guided-research-project.md +20 -38
  554. package/src/resources/extensions/gsd/prompts/plan-milestone.md +45 -59
  555. package/src/resources/extensions/gsd/prompts/plan-slice.md +25 -87
  556. package/src/resources/extensions/gsd/prompts/queue.md +46 -53
  557. package/src/resources/extensions/gsd/prompts/refine-slice.md +23 -23
  558. package/src/resources/extensions/gsd/prompts/research-slice.md +23 -23
  559. package/src/resources/extensions/gsd/prompts/rethink.md +10 -10
  560. package/src/resources/extensions/gsd/prompts/system.md +65 -107
  561. package/src/resources/extensions/gsd/prompts/triage-captures.md +15 -15
  562. package/src/resources/extensions/gsd/prompts/validate-milestone.md +24 -24
  563. package/src/resources/extensions/gsd/prompts/worktree-merge.md +35 -35
  564. package/src/resources/extensions/gsd/state.ts +6 -3
  565. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +38 -0
  566. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +98 -0
  567. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +117 -0
  568. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +353 -0
  569. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +19 -0
  570. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
  571. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +39 -0
  572. package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +3 -0
  573. package/src/resources/extensions/gsd/tests/commands-eval-review.test.ts +2 -2
  574. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  575. package/src/resources/extensions/gsd/tests/commands-ship-eval-warn.test.ts +3 -2
  576. package/src/resources/extensions/gsd/tests/complete-milestone-prompt-rendering.test.ts +47 -0
  577. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +19 -5
  578. package/src/resources/extensions/gsd/tests/component-loader.test.ts +2 -9
  579. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +144 -0
  580. package/src/resources/extensions/gsd/tests/custom-verify-retry-store.test.ts +139 -0
  581. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +50 -0
  582. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +3 -3
  583. package/src/resources/extensions/gsd/tests/db-adapter.test.ts +82 -0
  584. package/src/resources/extensions/gsd/tests/db-base-schema.test.ts +62 -0
  585. package/src/resources/extensions/gsd/tests/db-connection-cache.test.ts +60 -0
  586. package/src/resources/extensions/gsd/tests/db-coordination-schema.test.ts +39 -0
  587. package/src/resources/extensions/gsd/tests/db-decision-requirement-rows.test.ts +135 -0
  588. package/src/resources/extensions/gsd/tests/db-gate-rows.test.ts +53 -0
  589. package/src/resources/extensions/gsd/tests/db-lightweight-query-rows.test.ts +45 -0
  590. package/src/resources/extensions/gsd/tests/db-memory-fts-schema.test.ts +86 -0
  591. package/src/resources/extensions/gsd/tests/db-migration-backup.test.ts +105 -0
  592. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  593. package/src/resources/extensions/gsd/tests/db-migration-steps.test.ts +159 -0
  594. package/src/resources/extensions/gsd/tests/db-milestone-artifact-rows.test.ts +53 -0
  595. package/src/resources/extensions/gsd/tests/db-open-state.test.ts +56 -0
  596. package/src/resources/extensions/gsd/tests/db-provider.test.ts +105 -0
  597. package/src/resources/extensions/gsd/tests/db-runtime-kv-schema.test.ts +37 -0
  598. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +115 -0
  599. package/src/resources/extensions/gsd/tests/db-task-slice-rows.test.ts +128 -0
  600. package/src/resources/extensions/gsd/tests/db-transaction.test.ts +110 -0
  601. package/src/resources/extensions/gsd/tests/db-verification-evidence-schema.test.ts +76 -0
  602. package/src/resources/extensions/gsd/tests/discuss-headless-rendering.test.ts +37 -0
  603. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +59 -0
  604. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  605. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  606. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  607. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  608. package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +36 -0
  609. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  610. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +139 -0
  611. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +43 -0
  612. package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +41 -0
  613. package/src/resources/extensions/gsd/tests/guided-discuss-requirements-prompt-rendering.test.ts +45 -0
  614. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  615. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +79 -0
  616. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +37 -0
  617. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +5 -3
  618. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +134 -0
  619. package/src/resources/extensions/gsd/tests/legacy-component-format-telemetry.test.ts +62 -0
  620. package/src/resources/extensions/gsd/tests/legacy-telemetry.test.ts +144 -0
  621. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +40 -16
  622. package/src/resources/extensions/gsd/tests/model-router.test.ts +33 -12
  623. package/src/resources/extensions/gsd/tests/notification-store.test.ts +8 -0
  624. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +40 -1
  625. package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +2 -0
  626. package/src/resources/extensions/gsd/tests/plan-milestone-rendering.test.ts +45 -0
  627. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +65 -16
  628. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +27 -0
  629. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +46 -0
  630. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  631. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  632. package/src/resources/extensions/gsd/tests/pr-evidence.test.ts +79 -0
  633. package/src/resources/extensions/gsd/tests/process-task-path.test.ts +51 -0
  634. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -1
  635. package/src/resources/extensions/gsd/tests/queue-prompt-rendering.test.ts +37 -0
  636. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
  637. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +32 -9
  638. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +6 -6
  639. package/src/resources/extensions/gsd/tests/uok-kernel-path.test.ts +12 -0
  640. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  641. package/src/resources/extensions/gsd/tests/workflow-custom-engine-dispatch-outcome.test.ts +55 -0
  642. package/src/resources/extensions/gsd/tests/workflow-custom-engine-iteration.test.ts +93 -0
  643. package/src/resources/extensions/gsd/tests/workflow-custom-engine-reconcile-outcome.test.ts +108 -0
  644. package/src/resources/extensions/gsd/tests/workflow-custom-engine-reconcile.test.ts +146 -0
  645. package/src/resources/extensions/gsd/tests/workflow-custom-engine-retry.test.ts +136 -0
  646. package/src/resources/extensions/gsd/tests/workflow-custom-engine-verify-outcome.test.ts +95 -0
  647. package/src/resources/extensions/gsd/tests/workflow-dispatch-claim.test.ts +158 -0
  648. package/src/resources/extensions/gsd/tests/workflow-dispatch-ledger.test.ts +82 -0
  649. package/src/resources/extensions/gsd/tests/workflow-iteration-completion.test.ts +44 -0
  650. package/src/resources/extensions/gsd/tests/workflow-journal-reporter.test.ts +49 -0
  651. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +607 -0
  652. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +20 -4
  653. package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +71 -0
  654. package/src/resources/extensions/gsd/tests/workflow-phase-reporter.test.ts +40 -0
  655. package/src/resources/extensions/gsd/tests/workflow-session-lock.test.ts +135 -0
  656. package/src/resources/extensions/gsd/tests/workflow-sidecar-iteration.test.ts +110 -0
  657. package/src/resources/extensions/gsd/tests/workflow-sidecar-queue.test.ts +116 -0
  658. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +21 -0
  659. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +32 -0
  660. package/src/resources/extensions/gsd/tests/workflow-turn-reporter.test.ts +87 -0
  661. package/src/resources/extensions/gsd/tests/workflow-unit-dispatch.test.ts +160 -0
  662. package/src/resources/extensions/gsd/tests/workflow-worker-heartbeat.test.ts +123 -0
  663. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  664. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +17 -33
  665. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  666. package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
  667. package/src/resources/extensions/gsd/tools/complete-task.ts +4 -1
  668. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +6 -1
  669. package/src/resources/extensions/gsd/unit-context-composer.ts +1 -1
  670. package/src/resources/extensions/gsd/uok/kernel.ts +10 -3
  671. package/src/resources/extensions/gsd/uok/plan-v2.ts +5 -1
  672. package/src/resources/extensions/gsd/workflow-logger.ts +13 -13
  673. package/src/resources/extensions/gsd/workflow-manifest.ts +6 -15
  674. package/src/resources/extensions/gsd/workflow-projections.ts +5 -1
  675. package/src/resources/extensions/gsd/workflow-templates.ts +11 -0
  676. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  677. package/src/resources/extensions/shared/interview-ui.ts +18 -5
  678. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +41 -0
  679. package/dist/web/standalone/.next/server/chunks/6336.js +0 -1
  680. /package/dist/web/standalone/.next/static/{TzEVJ1Lh8vbez4n4Q9TqQ → vIAZSyxIuvqNkCvXt9oqb}/_buildManifest.js +0 -0
  681. /package/dist/web/standalone/.next/static/{TzEVJ1Lh8vbez4n4Q9TqQ → vIAZSyxIuvqNkCvXt9oqb}/_ssgManifest.js +0 -0
@@ -9,24 +9,39 @@
9
9
  import { randomUUID } from "node:crypto";
10
10
  import { MAX_LOOP_ITERATIONS, } from "./types.js";
11
11
  import { _clearCurrentResolve } from "./resolve.js";
12
- import { runPreDispatch, runDispatch, runGuards, runUnitPhase, runFinalize, } from "./phases.js";
12
+ import { runPreDispatch, runDispatch, runGuards, runFinalize, } from "./phases.js";
13
13
  import { debugLog } from "../debug-logger.js";
14
14
  import { isInfrastructureError, isTransientCooldownError, getCooldownRetryAfterMs, COOLDOWN_FALLBACK_WAIT_MS, MAX_COOLDOWN_RETRIES } from "./infra-errors.js";
15
15
  import { ModelPolicyDispatchBlockedError } from "../auto-model-selection.js";
16
16
  import { resolveEngine } from "../engine-resolver.js";
17
17
  import { logWarning } from "../workflow-logger.js";
18
- import { gsdRoot } from "../paths.js";
19
- import { heartbeatAutoWorker } from "../db/auto-workers.js";
20
18
  import { recordDispatchClaim, markRunning as markDispatchRunning, markCompleted as markDispatchCompleted, markFailed as markDispatchFailed, getRecentForUnit as getRecentDispatchesForUnit, getRecentUnitKeysForProjectRoot, } from "../db/unit-dispatches.js";
21
19
  import { refreshMilestoneLease } from "../db/milestone-leases.js";
20
+ import { heartbeatAutoWorker } from "../db/auto-workers.js";
22
21
  import { getRuntimeKv, setRuntimeKv } from "../db/runtime-kv.js";
23
- import { atomicWriteSync } from "../atomic-write.js";
24
22
  import { resolveUokFlags } from "../uok/flags.js";
25
23
  import { scheduleSidecarQueue } from "../uok/execution-graph.js";
26
- import { ExecutionGraphScheduler } from "../uok/execution-graph.js";
27
- import { readFileSync, mkdirSync, unlinkSync } from "node:fs";
28
- import { join } from "node:path";
29
24
  import { normalizeRealPath } from "../paths.js";
25
+ import { decideCooldownRecovery, decideDispatchClaim, decideEngineDispatch, decideFinalizeResult, decideInfrastructureError, decideIterationErrorRecovery, decideMemoryPressure, decideModelPolicyBlocked, decideMinRequestInterval, decideWorkflowLoop, formatDispatchExceptionSummary, formatUnhandledDispatchErrorSummary, resolveUnitRequestTimestamp, shouldUseCustomEnginePath, } from "./workflow-kernel.js";
26
+ import { hydrateCustomVerifyRetryCounts, saveCustomVerifyRetryCounts, } from "./custom-verify-retry-store.js";
27
+ import { settleDispatchCompleted, settleDispatchFailed, } from "./workflow-dispatch-ledger.js";
28
+ import { openDispatchClaim } from "./workflow-dispatch-claim.js";
29
+ import { completeWorkflowIteration } from "./workflow-iteration-completion.js";
30
+ import { createWorkflowJournalReporter } from "./workflow-journal-reporter.js";
31
+ import { createWorkflowPhaseReporter } from "./workflow-phase-reporter.js";
32
+ import { createWorkflowTurnReporter } from "./workflow-turn-reporter.js";
33
+ import { validateWorkflowSessionLock } from "./workflow-session-lock.js";
34
+ import { dequeueSidecarItem } from "./workflow-sidecar-queue.js";
35
+ import { maintainWorkerHeartbeat } from "./workflow-worker-heartbeat.js";
36
+ import { measureMemoryPressure } from "./workflow-memory-pressure.js";
37
+ import { buildSidecarIterationData } from "./workflow-sidecar-iteration.js";
38
+ import { createExecutionGraphUnitDispatchDeps, runUnitPhaseViaContract, } from "./workflow-unit-dispatch.js";
39
+ import { handleCustomEngineDispatchOutcome } from "./workflow-custom-engine-dispatch-outcome.js";
40
+ import { buildCustomEngineIterationData } from "./workflow-custom-engine-iteration.js";
41
+ import { handleCustomEngineVerifyRetry } from "./workflow-custom-engine-retry.js";
42
+ import { handleCustomEngineVerifyPause, handleCustomEngineVerifyRetryOutcome, } from "./workflow-custom-engine-verify-outcome.js";
43
+ import { handleCustomEngineReconcile } from "./workflow-custom-engine-reconcile.js";
44
+ import { handleCustomEngineReconcileOutcome } from "./workflow-custom-engine-reconcile-outcome.js";
30
45
  // ── Stuck detection persistence (#3704) ──────────────────────────────────
31
46
  // Phase C migration: stuck-state.json deleted in favor of DB-backed
32
47
  // equivalents. recentUnits is rebuilt from unit_dispatches (Phase B
@@ -70,200 +85,54 @@ function saveStuckState(s, state) {
70
85
  debugLog("autoLoop", { phase: "save-stuck-state-failed", error: err instanceof Error ? err.message : String(err) });
71
86
  }
72
87
  }
73
- // ── Custom workflow verification retry persistence ───────────────────────
74
- // Custom workflows can request verification retries after a step runs. The
75
- // retry budget must survive an auto-mode restart or a failing verifier can
76
- // consume a fresh retry budget every session.
77
- function customVerifyRetryStateDir(s) {
78
- return s.activeRunDir ? join(s.activeRunDir, "runtime") : join(gsdRoot(s.basePath), "runtime");
88
+ function logDispatchLedgerWriteFailure(err) {
89
+ debugLog("autoLoop", {
90
+ phase: "dispatch-ledger-write-failed",
91
+ error: err instanceof Error ? err.message : String(err),
92
+ });
79
93
  }
80
- function customVerifyRetryStatePath(s) {
81
- return join(customVerifyRetryStateDir(s), "custom-verify-retries.json");
94
+ function logDispatchClaimRejected(details) {
95
+ debugLog("autoLoop", {
96
+ phase: "dispatch-claim-rejected",
97
+ ...details,
98
+ });
82
99
  }
83
- function hydrateCustomVerifyRetryCounts(s) {
84
- if (s.verificationRetryCount.size > 0) {
85
- return s.verificationRetryCount;
86
- }
87
- try {
88
- const raw = JSON.parse(readFileSync(customVerifyRetryStatePath(s), "utf-8"));
89
- const counts = raw && typeof raw === "object" && raw.counts && typeof raw.counts === "object"
90
- ? raw.counts
91
- : {};
92
- for (const [key, value] of Object.entries(counts)) {
93
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
94
- s.verificationRetryCount.set(key, Math.floor(value));
95
- }
96
- }
97
- }
98
- catch (err) {
99
- debugLog("autoLoop", { phase: "load-custom-verify-retries-failed", error: err instanceof Error ? err.message : String(err) });
100
- }
101
- return s.verificationRetryCount;
100
+ function logDispatchClaimFailed(err) {
101
+ debugLog("autoLoop", {
102
+ phase: "dispatch-claim-failed",
103
+ error: err instanceof Error ? err.message : String(err),
104
+ });
102
105
  }
103
- function saveCustomVerifyRetryCounts(s) {
104
- const retryCounts = s.verificationRetryCount;
105
- const filePath = customVerifyRetryStatePath(s);
106
- try {
107
- if (!retryCounts || retryCounts.size === 0) {
108
- unlinkSync(filePath);
109
- return;
110
- }
111
- mkdirSync(customVerifyRetryStateDir(s), { recursive: true });
112
- atomicWriteSync(filePath, JSON.stringify({
113
- counts: Object.fromEntries(retryCounts),
114
- updatedAt: new Date().toISOString(),
115
- }) + "\n");
116
- }
117
- catch (err) {
118
- const code = err && typeof err === "object" && "code" in err ? err.code : undefined;
119
- if (code !== "ENOENT") {
120
- debugLog("autoLoop", { phase: "save-custom-verify-retries-failed", error: err instanceof Error ? err.message : String(err) });
121
- }
122
- }
106
+ function logCustomVerifyRetryLoadFailure(err) {
107
+ debugLog("autoLoop", {
108
+ phase: "load-custom-verify-retries-failed",
109
+ error: err instanceof Error ? err.message : String(err),
110
+ });
123
111
  }
124
- function openDispatchClaim(s, flowId, turnId, iterData) {
125
- if (!s.workerId || s.milestoneLeaseToken === null)
126
- return { kind: "degraded" };
127
- const mid = iterData.mid;
128
- if (!mid)
129
- return { kind: "degraded" };
130
- const recent = getRecentDispatchesForUnit(iterData.unitId, 1);
131
- const attemptN = (recent[0]?.attempt_n ?? 0) + 1;
132
- let claim;
133
- try {
134
- claim = recordDispatchClaim({
135
- traceId: flowId,
136
- turnId,
137
- workerId: s.workerId,
138
- milestoneLeaseToken: s.milestoneLeaseToken,
139
- milestoneId: mid,
140
- sliceId: iterData.state.activeSlice?.id ?? null,
141
- taskId: iterData.state.activeTask?.id ?? null,
142
- unitType: iterData.unitType,
143
- unitId: iterData.unitId,
144
- attemptN,
145
- });
146
- if (!claim.ok) {
147
- debugLog("autoLoop", {
148
- phase: "dispatch-claim-rejected",
149
- unitId: iterData.unitId,
150
- reason: claim.error,
151
- existingId: "existingId" in claim ? claim.existingId : undefined,
152
- existingWorker: "existingWorker" in claim ? claim.existingWorker : undefined,
153
- });
154
- if (claim.error === "already_active") {
155
- return {
156
- kind: "skip",
157
- reason: "already-active",
158
- existingId: claim.existingId,
159
- existingWorker: claim.existingWorker,
160
- };
161
- }
162
- return { kind: "skip", reason: "stale-lease" };
163
- }
164
- markDispatchRunning(claim.dispatchId);
165
- return { kind: "opened", dispatchId: claim.dispatchId };
166
- }
167
- catch (err) {
168
- debugLog("autoLoop", {
169
- phase: "dispatch-claim-failed",
170
- error: err instanceof Error ? err.message : String(err),
171
- });
172
- return { kind: "degraded" };
173
- }
112
+ function logCustomVerifyRetrySaveFailure(err) {
113
+ debugLog("autoLoop", {
114
+ phase: "save-custom-verify-retries-failed",
115
+ error: err instanceof Error ? err.message : String(err),
116
+ });
174
117
  }
175
118
  // ── Memory pressure monitoring (#3331) ──────────────────────────────────
176
119
  // Check heap usage every N iterations and trigger graceful shutdown before
177
120
  // the OS OOM killer sends SIGKILL. The threshold is 90% of the V8 heap
178
121
  // limit (--max-old-space-size or default ~1.5-4GB depending on platform).
179
122
  const MEMORY_CHECK_INTERVAL = 5; // check every 5 iterations
180
- const MEMORY_PRESSURE_THRESHOLD = 0.85; // 85% of heap limit
181
123
  const MAX_CUSTOM_ENGINE_VERIFY_RETRIES = 3;
182
- function checkMemoryPressure() {
183
- const mem = process.memoryUsage();
184
- // v8.getHeapStatistics() gives heap_size_limit but requires import
185
- // Use a conservative estimate: RSS > 3GB is danger zone on most systems
186
- const heapMB = Math.round(mem.heapUsed / 1024 / 1024);
187
- const rssMB = Math.round(mem.rss / 1024 / 1024);
188
- // Try to get the actual V8 heap limit
189
- let limitMB = 4096; // conservative default
190
- try {
191
- const v8 = require("node:v8");
192
- const stats = v8.getHeapStatistics();
193
- limitMB = Math.round(stats.heap_size_limit / 1024 / 1024);
194
- }
195
- catch {
196
- limitMB = 4096; /* v8 stats unavailable — use conservative default */
197
- }
198
- const pct = heapMB / limitMB;
199
- return { pressured: pct > MEMORY_PRESSURE_THRESHOLD, heapMB, limitMB, pct };
200
- }
201
- function resolveDispatchNodeKind(unitType, sidecarItem) {
202
- if (sidecarItem?.kind === "hook")
203
- return "hook";
204
- if (sidecarItem?.kind === "triage")
205
- return "verification";
206
- if (sidecarItem?.kind === "quick-task")
207
- return "team-worker";
208
- if (unitType.startsWith("hook/"))
209
- return "hook";
210
- if (unitType === "reactive-execute")
211
- return "subagent";
212
- if (unitType === "gate-evaluate"
213
- || unitType === "validate-milestone"
214
- || unitType === "run-uat"
215
- || unitType === "complete-slice") {
216
- return "verification";
217
- }
218
- if (unitType === "replan-slice" || unitType === "reassess-roadmap") {
219
- return "reprocess";
220
- }
221
- return "unit";
222
- }
223
124
  async function enforceMinRequestInterval(s, prefs) {
224
125
  const minInterval = prefs?.min_request_interval_ms ?? 0;
225
- if (minInterval > 0 && s.lastRequestTimestamp > 0) {
226
- const elapsed = Date.now() - s.lastRequestTimestamp;
227
- if (elapsed < minInterval) {
228
- const waitMs = minInterval - elapsed;
229
- debugLog("autoLoop", { phase: "rate-limit-wait", waitMs });
230
- await new Promise(r => setTimeout(r, waitMs));
231
- }
126
+ const decision = decideMinRequestInterval({
127
+ minIntervalMs: minInterval,
128
+ lastRequestTimestamp: s.lastRequestTimestamp,
129
+ nowMs: Date.now(),
130
+ });
131
+ if (decision.action === "wait") {
132
+ debugLog("autoLoop", { phase: "rate-limit-wait", waitMs: decision.waitMs });
133
+ await new Promise(r => setTimeout(r, decision.waitMs));
232
134
  }
233
135
  }
234
- async function runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, sidecarItem) {
235
- if (dispatchContract === "legacy-direct") {
236
- return runUnitPhase(ic, iterData, loopState, sidecarItem);
237
- }
238
- const scheduler = new ExecutionGraphScheduler();
239
- let outcome = null;
240
- const executeNode = async () => {
241
- outcome = await runUnitPhase(ic, iterData, loopState, sidecarItem);
242
- };
243
- const kinds = [
244
- "unit",
245
- "hook",
246
- "subagent",
247
- "team-worker",
248
- "verification",
249
- "reprocess",
250
- ];
251
- for (const kind of kinds)
252
- scheduler.registerHandler(kind, executeNode);
253
- const nodeId = `dispatch:${ic.iteration}:${iterData.unitType}:${iterData.unitId}`;
254
- await scheduler.run([
255
- {
256
- id: nodeId,
257
- kind: resolveDispatchNodeKind(iterData.unitType, sidecarItem),
258
- dependsOn: [],
259
- metadata: {
260
- unitType: iterData.unitType,
261
- unitId: iterData.unitId,
262
- },
263
- },
264
- ], { parallel: false, maxWorkers: 1 });
265
- return outcome ?? { action: "break", reason: "scheduler-dispatch-missing-result" };
266
- }
267
136
  /**
268
137
  * Main auto-mode execution loop. Iterates: derive → dispatch → guards →
269
138
  * runUnit → finalize → repeat. Exits when s.active becomes false or a
@@ -276,6 +145,7 @@ export async function autoLoop(ctx, pi, s, deps, options) {
276
145
  debugLog("autoLoop", { phase: "enter" });
277
146
  let iteration = 0;
278
147
  const dispatchContract = options?.dispatchContract ?? "legacy-direct";
148
+ const unitDispatchDeps = createExecutionGraphUnitDispatchDeps();
279
149
  // Load persisted stuck state so counters survive session restarts (#3704)
280
150
  const persisted = loadStuckState(s);
281
151
  const loopState = {
@@ -289,65 +159,65 @@ export async function autoLoop(ctx, pi, s, deps, options) {
289
159
  while (s.active) {
290
160
  iteration++;
291
161
  debugLog("autoLoop", { phase: "loop-top", iteration });
292
- // Phase B: heartbeat the worker registry + active milestone lease so
293
- // janitors and concurrent workers see a live process. Best-effort —
294
- // DB unavailability or stale state must not stop the loop.
295
- if (s.workerId) {
296
- try {
297
- heartbeatAutoWorker(s.workerId);
298
- if (s.currentMilestoneId && s.milestoneLeaseToken) {
299
- refreshMilestoneLease(s.workerId, s.currentMilestoneId, s.milestoneLeaseToken);
300
- }
301
- }
302
- catch (err) {
303
- debugLog("autoLoop", {
304
- phase: "heartbeat-failed",
305
- error: err instanceof Error ? err.message : String(err),
306
- });
307
- }
308
- }
162
+ maintainWorkerHeartbeat(s, {
163
+ heartbeatAutoWorker,
164
+ refreshMilestoneLease,
165
+ logHeartbeatFailure: err => debugLog("autoLoop", {
166
+ phase: "heartbeat-failed",
167
+ error: err instanceof Error ? err.message : String(err),
168
+ }),
169
+ });
309
170
  // ── Journal: per-iteration flow grouping ──
310
171
  const flowId = randomUUID();
311
172
  let seqCounter = 0;
312
173
  const nextSeq = () => ++seqCounter;
174
+ const journalReporter = createWorkflowJournalReporter({
175
+ emitJournalEvent: deps.emitJournalEvent,
176
+ flowId,
177
+ nextSeq,
178
+ });
313
179
  const turnId = randomUUID();
314
180
  s.currentTraceId = flowId;
315
181
  s.currentTurnId = turnId;
316
182
  const turnStartedAt = new Date().toISOString();
317
183
  let observedUnitType;
318
184
  let observedUnitId;
319
- let turnFinished = false;
185
+ const phaseReporter = createWorkflowPhaseReporter({
186
+ observer: deps.uokObserver,
187
+ });
188
+ const turnReporter = createWorkflowTurnReporter({
189
+ observer: deps.uokObserver,
190
+ traceId: flowId,
191
+ turnId,
192
+ iteration,
193
+ basePath: s.basePath,
194
+ startedAt: turnStartedAt,
195
+ clearCurrentTurn: () => {
196
+ s.currentTraceId = null;
197
+ s.currentTurnId = null;
198
+ },
199
+ });
320
200
  const finishTurn = (status, failureClass = "none", error) => {
321
- if (turnFinished)
322
- return;
323
- turnFinished = true;
324
- deps.uokObserver?.onTurnResult({
325
- traceId: flowId,
326
- turnId,
327
- iteration,
201
+ turnReporter.finish({
328
202
  unitType: observedUnitType,
329
203
  unitId: observedUnitId,
330
204
  status,
331
205
  failureClass,
332
- phaseResults: [],
333
206
  error,
334
- startedAt: turnStartedAt,
335
- finishedAt: new Date().toISOString(),
336
207
  });
337
- s.currentTraceId = null;
338
- s.currentTurnId = null;
339
208
  };
340
- deps.uokObserver?.onTurnStart({
341
- traceId: flowId,
342
- turnId,
209
+ turnReporter.start();
210
+ const iterationDecision = decideWorkflowLoop({
211
+ active: s.active,
343
212
  iteration,
344
- basePath: s.basePath,
345
- startedAt: turnStartedAt,
213
+ maxIterations: MAX_LOOP_ITERATIONS,
214
+ hasCommandContext: true,
215
+ sessionLockValid: true,
346
216
  });
347
- if (iteration > MAX_LOOP_ITERATIONS) {
217
+ if (iterationDecision.action === "stop" && iterationDecision.reason === "max-iterations") {
348
218
  debugLog("autoLoop", {
349
219
  phase: "exit",
350
- reason: "max-iterations",
220
+ reason: iterationDecision.reason,
351
221
  iteration,
352
222
  });
353
223
  await deps.stopAuto(ctx, pi, `Safety: loop exceeded ${MAX_LOOP_ITERATIONS} iterations — possible runaway`);
@@ -357,69 +227,84 @@ export async function autoLoop(ctx, pi, s, deps, options) {
357
227
  // ── Memory pressure check (#3331) ──
358
228
  // Graceful shutdown before OOM killer sends SIGKILL.
359
229
  if (iteration % MEMORY_CHECK_INTERVAL === 0) {
360
- const mem = checkMemoryPressure();
230
+ const mem = measureMemoryPressure();
361
231
  debugLog("autoLoop", { phase: "memory-check", ...mem });
362
- if (mem.pressured) {
363
- logWarning("dispatch", `Memory pressure: ${mem.heapMB}MB / ${mem.limitMB}MB (${Math.round(mem.pct * 100)}%) — stopping auto-mode to prevent OOM kill`);
364
- await deps.stopAuto(ctx, pi, `Memory pressure: heap at ${mem.heapMB}MB / ${mem.limitMB}MB (${Math.round(mem.pct * 100)}%). ` +
365
- `Stopping gracefully to prevent OOM kill after ${iteration} iterations. ` +
366
- `Resume with /gsd auto to continue from where you left off.`);
367
- finishTurn("stopped", "timeout", "memory-pressure");
232
+ const memoryDecision = decideMemoryPressure({ ...mem, iteration });
233
+ if (memoryDecision.action === "stop") {
234
+ logWarning("dispatch", memoryDecision.warningMessage);
235
+ await deps.stopAuto(ctx, pi, memoryDecision.stopMessage);
236
+ finishTurn("stopped", "timeout", memoryDecision.turnError);
368
237
  break;
369
238
  }
370
239
  }
371
- if (!s.cmdCtx) {
240
+ const commandContextDecision = decideWorkflowLoop({
241
+ active: s.active,
242
+ iteration,
243
+ maxIterations: MAX_LOOP_ITERATIONS,
244
+ hasCommandContext: Boolean(s.cmdCtx),
245
+ sessionLockValid: true,
246
+ });
247
+ if (commandContextDecision.action === "stop" && commandContextDecision.reason === "missing-command-context") {
372
248
  debugLog("autoLoop", { phase: "exit", reason: "no-cmdCtx" });
373
- finishTurn("stopped", "manual-attention", "missing-command-context");
249
+ finishTurn("stopped", "manual-attention", commandContextDecision.reason);
374
250
  break;
375
251
  }
376
252
  let dispatchId = null;
377
253
  let dispatchSettled = false;
254
+ const completeIteration = () => {
255
+ completeWorkflowIteration({
256
+ get consecutiveErrors() { return consecutiveErrors; },
257
+ set consecutiveErrors(value) { consecutiveErrors = value; },
258
+ get consecutiveCooldowns() { return consecutiveCooldowns; },
259
+ set consecutiveCooldowns(value) { consecutiveCooldowns = value; },
260
+ recentErrorMessages,
261
+ }, {
262
+ emitIterationEnd: () => journalReporter.emit("iteration-end", { iteration }),
263
+ saveStuckState: () => saveStuckState(s, loopState),
264
+ logIterationComplete: () => debugLog("autoLoop", { phase: "iteration-complete", iteration }),
265
+ });
266
+ };
378
267
  try {
379
268
  // ── Blanket try/catch: one bad iteration must not kill the session
380
269
  const prefs = deps.loadEffectiveGSDPreferences()?.preferences;
381
270
  const uokFlags = resolveUokFlags(prefs);
382
271
  // ── Check sidecar queue before deriveState ──
383
- let sidecarItem;
384
- if (s.sidecarQueue.length > 0) {
385
- if (uokFlags.executionGraph && s.sidecarQueue.length > 1) {
386
- try {
387
- s.sidecarQueue = await scheduleSidecarQueue(s.sidecarQueue);
388
- }
389
- catch (err) {
390
- logWarning("dispatch", `sidecar queue scheduling failed: ${err instanceof Error ? err.message : String(err)}`);
391
- }
392
- }
393
- sidecarItem = s.sidecarQueue.shift();
394
- debugLog("autoLoop", {
395
- phase: "sidecar-dequeue",
396
- kind: sidecarItem.kind,
397
- unitType: sidecarItem.unitType,
398
- unitId: sidecarItem.unitId,
399
- });
400
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "sidecar-dequeue", data: { kind: sidecarItem.kind, unitType: sidecarItem.unitType, unitId: sidecarItem.unitId } });
401
- }
402
- const sessionLockBase = deps.lockBase();
403
- if (sessionLockBase) {
404
- const lockStatus = deps.validateSessionLock(sessionLockBase);
405
- if (!lockStatus.valid) {
406
- debugLog("autoLoop", {
272
+ // NOTE: Sidecar dequeue MUST run before validateWorkflowSessionLock so a
273
+ // queued item is popped (and the `sidecar-dequeue` journal event emitted)
274
+ // even when the session lock invalidates this iteration. Inverting this
275
+ // order silently drops queued items on lock-loss. Refs #5308.
276
+ const sidecarItem = await dequeueSidecarItem({
277
+ queue: s.sidecarQueue,
278
+ executionGraphEnabled: uokFlags.executionGraph,
279
+ scheduleQueue: scheduleSidecarQueue,
280
+ warnSchedulingFailure: message => logWarning("dispatch", `sidecar queue scheduling failed: ${message}`),
281
+ logDequeue: payload => debugLog("autoLoop", { phase: "sidecar-dequeue", ...payload }),
282
+ emitDequeue: payload => journalReporter.emit("sidecar-dequeue", payload),
283
+ });
284
+ const sessionLockOutcome = validateWorkflowSessionLock({
285
+ active: s.active,
286
+ iteration,
287
+ maxIterations: MAX_LOOP_ITERATIONS,
288
+ deps: {
289
+ lockBase: deps.lockBase,
290
+ validateSessionLock: deps.validateSessionLock,
291
+ handleLostSessionLock: lockStatus => deps.handleLostSessionLock(ctx, lockStatus),
292
+ logInvalidSessionLock: details => debugLog("autoLoop", {
407
293
  phase: "session-lock-invalid",
408
- reason: lockStatus.failureReason ?? "unknown",
409
- existingPid: lockStatus.existingPid,
410
- expectedPid: lockStatus.expectedPid,
411
- });
412
- deps.handleLostSessionLock(ctx, lockStatus);
413
- debugLog("autoLoop", {
294
+ ...details,
295
+ }),
296
+ logSessionLockExit: details => debugLog("autoLoop", {
414
297
  phase: "exit",
415
- reason: "session-lock-lost",
416
- detail: lockStatus.failureReason ?? "unknown",
417
- });
418
- break;
419
- }
298
+ ...details,
299
+ }),
300
+ },
301
+ });
302
+ if (sessionLockOutcome.action === "stop" && sessionLockOutcome.reason === "session-lock-lost") {
303
+ finishTurn("stopped", "manual-attention", sessionLockOutcome.reason);
304
+ break;
420
305
  }
421
306
  const ic = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
422
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
307
+ journalReporter.emit("iteration-start", { iteration });
423
308
  let iterData;
424
309
  // ── Custom engine path ──────────────────────────────────────────────
425
310
  // When activeEngineId is a non-dev value, bypass runPreDispatch and
@@ -429,7 +314,11 @@ export async function autoLoop(ctx, pi, s, deps, options) {
429
314
  //
430
315
  // GSD_ENGINE_BYPASS=1 skips the engine layer entirely — falls through
431
316
  // to the dev path below.
432
- if (s.activeEngineId != null && s.activeEngineId !== "dev" && !sidecarItem && process.env.GSD_ENGINE_BYPASS !== "1") {
317
+ if (shouldUseCustomEnginePath({
318
+ activeEngineId: s.activeEngineId,
319
+ hasSidecarItem: Boolean(sidecarItem),
320
+ engineBypass: process.env.GSD_ENGINE_BYPASS === "1",
321
+ })) {
433
322
  debugLog("autoLoop", { phase: "custom-engine-derive", iteration, engineId: s.activeEngineId });
434
323
  const { engine, policy } = resolveEngine({
435
324
  activeEngineId: s.activeEngineId,
@@ -447,48 +336,53 @@ export async function autoLoop(ctx, pi, s, deps, options) {
447
336
  isComplete: engineState.isComplete,
448
337
  });
449
338
  if (engineState.isComplete) {
339
+ finishTurn("completed");
450
340
  await deps.stopAuto(ctx, pi, "Workflow complete");
451
341
  break;
452
342
  }
453
343
  debugLog("autoLoop", { phase: "custom-engine-dispatch", iteration });
454
344
  const dispatch = await engine.resolveDispatch(engineState, { basePath: s.basePath });
455
- if (dispatch.action === "stop") {
456
- await deps.stopAuto(ctx, pi, dispatch.reason ?? "Engine stopped");
345
+ const engineDispatchDecision = decideEngineDispatch(dispatch.action === "stop"
346
+ ? { action: "stop", reason: dispatch.reason }
347
+ : { action: dispatch.action });
348
+ const dispatchFlow = await handleCustomEngineDispatchOutcome({
349
+ decision: engineDispatchDecision,
350
+ deps: {
351
+ stopAuto: reason => deps.stopAuto(ctx, pi, reason),
352
+ },
353
+ });
354
+ if (dispatchFlow.action === "break") {
355
+ finishTurn("stopped", "manual-attention", "custom-engine-dispatch-stop");
457
356
  break;
458
357
  }
459
- if (dispatch.action === "skip") {
358
+ if (dispatchFlow.action === "continue") {
359
+ finishTurn("skipped");
460
360
  continue;
461
361
  }
462
362
  // dispatch.action === "dispatch"
363
+ if (dispatch.action !== "dispatch") {
364
+ finishTurn("skipped");
365
+ continue;
366
+ }
463
367
  const step = dispatch.step;
464
- const gsdState = await deps.deriveState(s.canonicalProjectRoot);
465
- debugLog("autoLoop", {
466
- phase: "post-derive",
467
- site: "custom-engine-gsd-state",
368
+ iterData = await buildCustomEngineIterationData({
369
+ step,
468
370
  basePath: s.basePath,
469
371
  canonicalProjectRoot: s.canonicalProjectRoot,
470
- derivedPhase: gsdState.phase,
471
- activeUnit: gsdState.activeTask?.id ?? gsdState.activeSlice?.id ?? gsdState.activeMilestone?.id,
372
+ currentMilestoneId: s.currentMilestoneId,
373
+ deriveState: deps.deriveState,
374
+ logPostDerive: details => debugLog("autoLoop", {
375
+ phase: "post-derive",
376
+ ...details,
377
+ }),
472
378
  });
473
- iterData = {
474
- unitType: step.unitType,
475
- unitId: step.unitId,
476
- prompt: step.prompt,
477
- finalPrompt: step.prompt,
478
- pauseAfterUatDispatch: false,
479
- state: gsdState,
480
- mid: s.currentMilestoneId ?? "workflow",
481
- midTitle: "Workflow",
482
- isRetry: false,
483
- previousTier: undefined,
484
- };
485
379
  observedUnitType = iterData.unitType;
486
380
  observedUnitId = iterData.unitId;
487
381
  // ── Progress widget (mirrors dev path in runDispatch) ──
488
382
  deps.updateProgressWidget(ctx, iterData.unitType, iterData.unitId, iterData.state);
489
383
  // ── Guards (shared with dev path) ──
490
384
  const guardsResult = await runGuards(ic, s.currentMilestoneId ?? "workflow");
491
- deps.uokObserver?.onPhaseResult("guard", guardsResult.action, {
385
+ phaseReporter.report("guard", guardsResult.action, {
492
386
  unitType: iterData.unitType,
493
387
  unitId: iterData.unitId,
494
388
  });
@@ -498,13 +392,13 @@ export async function autoLoop(ctx, pi, s, deps, options) {
498
392
  }
499
393
  // ── Unit execution (shared with dev path) ──
500
394
  await enforceMinRequestInterval(s, prefs);
501
- const unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState);
395
+ const unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, undefined, unitDispatchDeps);
502
396
  if (unitPhaseResult.action === "next") {
503
- const requestTimestamp = unitPhaseResult.data.requestDispatchedAt ?? unitPhaseResult.data.unitStartedAt;
504
- if (typeof requestTimestamp === "number")
397
+ const requestTimestamp = resolveUnitRequestTimestamp(unitPhaseResult.data);
398
+ if (requestTimestamp !== undefined)
505
399
  s.lastRequestTimestamp = requestTimestamp;
506
400
  }
507
- deps.uokObserver?.onPhaseResult("unit", unitPhaseResult.action, {
401
+ phaseReporter.report("unit", unitPhaseResult.action, {
508
402
  unitType: iterData.unitType,
509
403
  unitId: iterData.unitId,
510
404
  });
@@ -516,103 +410,94 @@ export async function autoLoop(ctx, pi, s, deps, options) {
516
410
  debugLog("autoLoop", { phase: "custom-engine-verify", iteration, unitId: iterData.unitId });
517
411
  const verifyResult = await policy.verify(iterData.unitType, iterData.unitId, { basePath: s.basePath });
518
412
  if (verifyResult === "pause") {
519
- await deps.pauseAuto(ctx, pi);
520
- deps.uokObserver?.onPhaseResult("custom-engine", "pause", {
413
+ const verifyFlow = await handleCustomEngineVerifyPause({
521
414
  unitType: iterData.unitType,
522
415
  unitId: iterData.unitId,
416
+ deps: {
417
+ pauseAuto: () => deps.pauseAuto(ctx, pi),
418
+ stopAuto: reason => deps.stopAuto(ctx, pi, reason),
419
+ reportPause: details => phaseReporter.report("custom-engine", "pause", details),
420
+ finishTurn,
421
+ },
523
422
  });
524
- finishTurn("paused", "manual-attention", "custom-engine-verify-pause");
525
- break;
423
+ if (verifyFlow.action === "break")
424
+ break;
526
425
  }
527
426
  if (verifyResult === "retry") {
528
- const recoveryKey = `${iterData.unitType}/${iterData.unitId}`;
529
- const retryCounts = hydrateCustomVerifyRetryCounts(s);
530
- const attempts = (retryCounts.get(recoveryKey) ?? 0) + 1;
531
- retryCounts.set(recoveryKey, attempts);
532
- saveCustomVerifyRetryCounts(s);
533
- debugLog("autoLoop", { phase: "custom-engine-verify-retry", iteration, unitId: iterData.unitId, attempts });
534
- deps.uokObserver?.onPhaseResult("custom-engine", "retry", {
427
+ const retryOutcome = await handleCustomEngineVerifyRetry({
428
+ session: s,
535
429
  unitType: iterData.unitType,
536
430
  unitId: iterData.unitId,
537
- attempts,
431
+ basePath: s.basePath,
432
+ iteration,
433
+ maxRetries: MAX_CUSTOM_ENGINE_VERIFY_RETRIES,
434
+ deps: {
435
+ hydrateRetryCounts: () => hydrateCustomVerifyRetryCounts(s, {
436
+ logFailure: logCustomVerifyRetryLoadFailure,
437
+ }),
438
+ saveRetryCounts: () => saveCustomVerifyRetryCounts(s, {
439
+ logFailure: logCustomVerifyRetrySaveFailure,
440
+ }),
441
+ recover: (unitType, unitId, options) => policy.recover(unitType, unitId, options),
442
+ logRetry: details => debugLog("autoLoop", {
443
+ phase: "custom-engine-verify-retry",
444
+ ...details,
445
+ }),
446
+ reportRetry: details => phaseReporter.report("custom-engine", "retry", details),
447
+ },
448
+ });
449
+ const retryFlow = await handleCustomEngineVerifyRetryOutcome({
450
+ outcome: retryOutcome,
451
+ deps: {
452
+ pauseAuto: () => deps.pauseAuto(ctx, pi),
453
+ stopAuto: reason => deps.stopAuto(ctx, pi, reason),
454
+ reportPause: details => phaseReporter.report("custom-engine", "pause", details),
455
+ finishTurn,
456
+ },
538
457
  });
539
- if (attempts > MAX_CUSTOM_ENGINE_VERIFY_RETRIES) {
540
- const recovery = await policy.recover(iterData.unitType, iterData.unitId, { basePath: s.basePath });
541
- if (recovery.outcome === "pause") {
542
- await deps.pauseAuto(ctx, pi);
543
- finishTurn("paused", "manual-attention", recovery.reason ?? "custom-engine-verify-retry-exhausted");
544
- break;
545
- }
546
- if (recovery.outcome === "skip") {
547
- await deps.stopAuto(ctx, pi, recovery.reason ??
548
- `Custom workflow verification for ${iterData.unitId} requested skip after retry exhaustion, but the custom engine cannot reconcile skipped steps.`);
549
- finishTurn("stopped", "manual-attention", "custom-engine-verify-retry-exhausted");
550
- break;
551
- }
552
- const exhaustedReason = `Custom workflow verification for ${iterData.unitId} requested retry ${attempts} times without passing.`;
553
- await deps.stopAuto(ctx, pi, recovery.outcome === "stop" && recovery.reason ? recovery.reason : exhaustedReason);
554
- finishTurn("stopped", "manual-attention", "custom-engine-verify-retry-exhausted");
458
+ if (retryFlow.action === "break")
555
459
  break;
556
- }
557
- finishTurn("retry");
558
460
  continue;
559
461
  }
560
462
  // Verification passed — mark step complete
561
- s.verificationRetryCount?.delete(`${iterData.unitType}/${iterData.unitId}`);
562
- saveCustomVerifyRetryCounts(s);
563
- debugLog("autoLoop", { phase: "custom-engine-reconcile", iteration, unitId: iterData.unitId });
564
- const reconcileResult = await engine.reconcile(engineState, {
565
- unitType: iterData.unitType,
566
- unitId: iterData.unitId,
567
- startedAt: s.currentUnit?.startedAt ?? Date.now(),
568
- finishedAt: Date.now(),
463
+ const reconcileOutcome = await handleCustomEngineReconcile({
464
+ session: s,
465
+ engineState,
466
+ iterData,
467
+ iteration,
468
+ deps: {
469
+ saveRetryCounts: () => saveCustomVerifyRetryCounts(s, {
470
+ logFailure: logCustomVerifyRetrySaveFailure,
471
+ }),
472
+ logReconcile: details => debugLog("autoLoop", {
473
+ phase: "custom-engine-reconcile",
474
+ ...details,
475
+ }),
476
+ reconcile: (state, completedStep) => engine.reconcile(state, completedStep),
477
+ now: () => Date.now(),
478
+ clearUnitTimeout: deps.clearUnitTimeout,
479
+ completeIteration,
480
+ },
569
481
  });
570
- deps.clearUnitTimeout();
571
- consecutiveErrors = 0;
572
- consecutiveCooldowns = 0;
573
- recentErrorMessages.length = 0;
574
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
575
- saveStuckState(s, loopState); // persist across session restarts (#3704)
576
- debugLog("autoLoop", { phase: "iteration-complete", iteration });
577
- if (reconcileResult.outcome === "milestone-complete") {
578
- await deps.stopAuto(ctx, pi, "Workflow complete");
579
- deps.uokObserver?.onPhaseResult("custom-engine", "milestone-complete", {
580
- unitType: iterData.unitType,
581
- unitId: iterData.unitId,
582
- });
583
- finishTurn("completed");
584
- break;
585
- }
586
- if (reconcileResult.outcome === "pause") {
587
- await deps.pauseAuto(ctx, pi);
588
- deps.uokObserver?.onPhaseResult("custom-engine", "pause", {
589
- unitType: iterData.unitType,
590
- unitId: iterData.unitId,
591
- });
592
- finishTurn("paused", "manual-attention");
593
- break;
594
- }
595
- if (reconcileResult.outcome === "stop") {
596
- await deps.stopAuto(ctx, pi, reconcileResult.reason ?? "Engine stopped");
597
- deps.uokObserver?.onPhaseResult("custom-engine", "stop", {
598
- unitType: iterData.unitType,
599
- unitId: iterData.unitId,
600
- reason: reconcileResult.reason,
601
- });
602
- finishTurn("stopped", "manual-attention", reconcileResult.reason);
603
- break;
604
- }
605
- deps.uokObserver?.onPhaseResult("custom-engine", "continue", {
482
+ const reconcileFlow = await handleCustomEngineReconcileOutcome({
483
+ outcome: reconcileOutcome,
606
484
  unitType: iterData.unitType,
607
485
  unitId: iterData.unitId,
486
+ deps: {
487
+ stopAuto: reason => deps.stopAuto(ctx, pi, reason),
488
+ pauseAuto: () => deps.pauseAuto(ctx, pi),
489
+ report: (action, details) => phaseReporter.report("custom-engine", action, details),
490
+ finishTurn,
491
+ },
608
492
  });
609
- finishTurn("completed");
493
+ if (reconcileFlow.action === "break")
494
+ break;
610
495
  continue;
611
496
  }
612
497
  if (!sidecarItem) {
613
498
  // ── Phase 1: Pre-dispatch ─────────────────────────────────────────
614
499
  const preDispatchResult = await runPreDispatch(ic, loopState);
615
- deps.uokObserver?.onPhaseResult("pre-dispatch", preDispatchResult.action);
500
+ phaseReporter.report("pre-dispatch", preDispatchResult.action);
616
501
  if (preDispatchResult.action === "break") {
617
502
  finishTurn("stopped", "manual-attention", "pre-dispatch-break");
618
503
  break;
@@ -624,14 +509,14 @@ export async function autoLoop(ctx, pi, s, deps, options) {
624
509
  const preData = preDispatchResult.data;
625
510
  // ── Phase 2: Guards ───────────────────────────────────────────────
626
511
  const guardsResult = await runGuards(ic, preData.mid);
627
- deps.uokObserver?.onPhaseResult("guard", guardsResult.action);
512
+ phaseReporter.report("guard", guardsResult.action);
628
513
  if (guardsResult.action === "break") {
629
514
  finishTurn("stopped", "manual-attention", "guard-break");
630
515
  break;
631
516
  }
632
517
  // ── Phase 3: Dispatch ─────────────────────────────────────────────
633
518
  const dispatchResult = await runDispatch(ic, preData, loopState);
634
- deps.uokObserver?.onPhaseResult("dispatch", dispatchResult.action);
519
+ phaseReporter.report("dispatch", dispatchResult.action);
635
520
  if (dispatchResult.action === "break") {
636
521
  finishTurn("stopped", "manual-attention", "dispatch-break");
637
522
  break;
@@ -645,30 +530,19 @@ export async function autoLoop(ctx, pi, s, deps, options) {
645
530
  observedUnitId = iterData.unitId;
646
531
  }
647
532
  else {
648
- // ── Sidecar path: use values from the sidecar item directly ──
649
- const sidecarState = await deps.deriveState(s.canonicalProjectRoot);
650
- debugLog("autoLoop", {
651
- phase: "post-derive",
652
- site: "sidecar",
533
+ iterData = await buildSidecarIterationData({
534
+ sidecarItem,
653
535
  basePath: s.basePath,
654
536
  canonicalProjectRoot: s.canonicalProjectRoot,
655
- derivedPhase: sidecarState.phase,
656
- activeUnit: sidecarState.activeTask?.id ?? sidecarState.activeSlice?.id ?? sidecarState.activeMilestone?.id,
537
+ deriveState: deps.deriveState,
538
+ logPostDerive: details => debugLog("autoLoop", {
539
+ phase: "post-derive",
540
+ ...details,
541
+ }),
657
542
  });
658
- iterData = {
659
- unitType: sidecarItem.unitType,
660
- unitId: sidecarItem.unitId,
661
- prompt: sidecarItem.prompt,
662
- finalPrompt: sidecarItem.prompt,
663
- pauseAfterUatDispatch: false,
664
- state: sidecarState,
665
- mid: sidecarState.activeMilestone?.id,
666
- midTitle: sidecarState.activeMilestone?.title,
667
- isRetry: false, previousTier: undefined,
668
- };
669
543
  observedUnitType = iterData.unitType;
670
544
  observedUnitId = iterData.unitId;
671
- deps.uokObserver?.onPhaseResult("dispatch", "sidecar", {
545
+ phaseReporter.report("dispatch", "sidecar", {
672
546
  unitType: iterData.unitType,
673
547
  unitId: iterData.unitId,
674
548
  sidecarKind: sidecarItem.kind,
@@ -681,52 +555,51 @@ export async function autoLoop(ctx, pi, s, deps, options) {
681
555
  // null when DB unavailable, no worker registered, or no active lease
682
556
  // — those degraded paths fall through to the existing single-worker
683
557
  // semantics with no ledger entry, preserving back-compat.
684
- const dispatchClaim = openDispatchClaim(s, flowId, turnId, iterData);
685
- if (dispatchClaim.kind === "skip") {
686
- finishTurn("skipped", "execution", dispatchClaim.reason);
558
+ const dispatchClaim = openDispatchClaim(s, flowId, turnId, iterData, {
559
+ getRecentDispatchesForUnit,
560
+ recordDispatchClaim,
561
+ markDispatchRunning,
562
+ logClaimRejected: logDispatchClaimRejected,
563
+ logClaimFailed: logDispatchClaimFailed,
564
+ });
565
+ const dispatchDecision = decideDispatchClaim(dispatchClaim.kind === "opened"
566
+ ? { kind: "opened", dispatchId: dispatchClaim.dispatchId }
567
+ : dispatchClaim.kind === "skip"
568
+ ? { kind: "skip", reason: dispatchClaim.reason }
569
+ : { kind: "degraded" });
570
+ if (dispatchDecision.action === "skip") {
571
+ finishTurn("skipped", "execution", dispatchDecision.reason);
687
572
  continue;
688
573
  }
689
- dispatchId = dispatchClaim.kind === "opened" ? dispatchClaim.dispatchId : null;
574
+ dispatchId = dispatchDecision.dispatchId;
690
575
  let unitPhaseResult;
691
576
  try {
692
- unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, sidecarItem);
577
+ unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, sidecarItem, unitDispatchDeps);
693
578
  }
694
579
  catch (err) {
695
580
  if (err instanceof ModelPolicyDispatchBlockedError) {
696
581
  throw err;
697
582
  }
698
- if (dispatchId !== null) {
699
- try {
700
- markDispatchFailed(dispatchId, {
701
- errorSummary: `exception:${err instanceof Error ? err.message : String(err)}`,
702
- });
703
- dispatchSettled = true;
704
- }
705
- catch (ledgerErr) {
706
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: ledgerErr instanceof Error ? ledgerErr.message : String(ledgerErr) });
707
- }
708
- }
583
+ dispatchSettled = settleDispatchFailed(dispatchId, formatDispatchExceptionSummary({ error: err }), {
584
+ markFailed: markDispatchFailed,
585
+ logWriteFailure: logDispatchLedgerWriteFailure,
586
+ }) || dispatchSettled;
709
587
  throw err;
710
588
  }
711
589
  if (unitPhaseResult.action === "next") {
712
- const requestTimestamp = unitPhaseResult.data.requestDispatchedAt ?? unitPhaseResult.data.unitStartedAt;
713
- if (typeof requestTimestamp === "number")
590
+ const requestTimestamp = resolveUnitRequestTimestamp(unitPhaseResult.data);
591
+ if (requestTimestamp !== undefined)
714
592
  s.lastRequestTimestamp = requestTimestamp;
715
593
  }
716
- deps.uokObserver?.onPhaseResult("unit", unitPhaseResult.action, {
594
+ phaseReporter.report("unit", unitPhaseResult.action, {
717
595
  unitType: iterData.unitType,
718
596
  unitId: iterData.unitId,
719
597
  });
720
598
  if (unitPhaseResult.action === "break") {
721
- if (dispatchId !== null) {
722
- try {
723
- markDispatchFailed(dispatchId, { errorSummary: "unit-break" });
724
- dispatchSettled = true;
725
- }
726
- catch (err) {
727
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: err instanceof Error ? err.message : String(err) });
728
- }
729
- }
599
+ dispatchSettled = settleDispatchFailed(dispatchId, "unit-break", {
600
+ markFailed: markDispatchFailed,
601
+ logWriteFailure: logDispatchLedgerWriteFailure,
602
+ }) || dispatchSettled;
730
603
  finishTurn("stopped", "execution", "unit-break");
731
604
  break;
732
605
  }
@@ -736,88 +609,57 @@ export async function autoLoop(ctx, pi, s, deps, options) {
736
609
  finalizeResult = await runFinalize(ic, iterData, loopState, sidecarItem);
737
610
  }
738
611
  catch (err) {
739
- if (dispatchId !== null) {
740
- try {
741
- markDispatchFailed(dispatchId, {
742
- errorSummary: `exception:${err instanceof Error ? err.message : String(err)}`,
743
- });
744
- dispatchSettled = true;
745
- }
746
- catch (ledgerErr) {
747
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: ledgerErr instanceof Error ? ledgerErr.message : String(ledgerErr) });
748
- }
749
- }
612
+ dispatchSettled = settleDispatchFailed(dispatchId, formatDispatchExceptionSummary({ error: err }), {
613
+ markFailed: markDispatchFailed,
614
+ logWriteFailure: logDispatchLedgerWriteFailure,
615
+ }) || dispatchSettled;
750
616
  throw err;
751
617
  }
752
- deps.uokObserver?.onPhaseResult("finalize", finalizeResult.action, {
618
+ phaseReporter.report("finalize", finalizeResult.action, {
753
619
  unitType: iterData.unitType,
754
620
  unitId: iterData.unitId,
755
621
  });
756
- if (finalizeResult.action === "break") {
757
- const finalizeFailureClass = finalizeResult.reason === "git-closeout-failure"
758
- ? "git"
759
- : "closeout";
760
- if (dispatchId !== null) {
761
- try {
762
- markDispatchFailed(dispatchId, { errorSummary: `finalize-break:${finalizeResult.reason ?? "unknown"}` });
763
- dispatchSettled = true;
764
- }
765
- catch (err) {
766
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: err instanceof Error ? err.message : String(err) });
767
- }
768
- }
769
- finishTurn("stopped", finalizeFailureClass, "finalize-break");
622
+ const finalizeDecision = decideFinalizeResult(finalizeResult.action === "break"
623
+ ? { action: "break", reason: finalizeResult.reason }
624
+ : finalizeResult.action === "continue"
625
+ ? { action: "continue" }
626
+ : { action: "next" });
627
+ if (finalizeDecision.action === "stop") {
628
+ dispatchSettled = settleDispatchFailed(dispatchId, finalizeDecision.ledgerErrorSummary, {
629
+ markFailed: markDispatchFailed,
630
+ logWriteFailure: logDispatchLedgerWriteFailure,
631
+ }) || dispatchSettled;
632
+ finishTurn("stopped", finalizeDecision.failureClass, finalizeDecision.turnError);
770
633
  break;
771
634
  }
772
- if (finalizeResult.action === "continue") {
773
- if (dispatchId !== null) {
774
- try {
775
- markDispatchFailed(dispatchId, { errorSummary: "finalize-retry" });
776
- dispatchSettled = true;
777
- }
778
- catch (err) {
779
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: err instanceof Error ? err.message : String(err) });
780
- }
781
- }
635
+ if (finalizeDecision.action === "retry") {
636
+ dispatchSettled = settleDispatchFailed(dispatchId, finalizeDecision.ledgerErrorSummary, {
637
+ markFailed: markDispatchFailed,
638
+ logWriteFailure: logDispatchLedgerWriteFailure,
639
+ }) || dispatchSettled;
782
640
  finishTurn("retry");
783
641
  continue;
784
642
  }
785
- if (dispatchId !== null) {
786
- try {
787
- markDispatchCompleted(dispatchId);
788
- dispatchSettled = true;
789
- }
790
- catch (err) {
791
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: err instanceof Error ? err.message : String(err) });
792
- }
793
- }
794
- consecutiveErrors = 0; // Iteration completed successfully
795
- consecutiveCooldowns = 0;
796
- recentErrorMessages.length = 0;
797
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
798
- saveStuckState(s, loopState); // persist across session restarts (#4382)
799
- debugLog("autoLoop", { phase: "iteration-complete", iteration });
643
+ dispatchSettled = settleDispatchCompleted(dispatchId, {
644
+ markCompleted: markDispatchCompleted,
645
+ logWriteFailure: logDispatchLedgerWriteFailure,
646
+ }) || dispatchSettled;
647
+ completeIteration();
800
648
  finishTurn("completed");
801
649
  }
802
650
  catch (loopErr) {
803
651
  // ── Blanket catch: absorb unexpected exceptions, apply graduated recovery ──
804
652
  const msg = loopErr instanceof Error ? loopErr.message : String(loopErr);
805
653
  if (dispatchId !== null && !dispatchSettled && !(loopErr instanceof ModelPolicyDispatchBlockedError)) {
806
- try {
807
- markDispatchFailed(dispatchId, { errorSummary: `unhandled-error:${msg.slice(0, 200)}` });
808
- dispatchSettled = true;
809
- }
810
- catch (err) {
811
- debugLog("autoLoop", {
812
- phase: "dispatch-ledger-write-failed",
813
- error: err instanceof Error ? err.message : String(err),
814
- });
815
- }
654
+ dispatchSettled = settleDispatchFailed(dispatchId, formatUnhandledDispatchErrorSummary({ error: loopErr }), {
655
+ markFailed: markDispatchFailed,
656
+ logWriteFailure: logDispatchLedgerWriteFailure,
657
+ }) || dispatchSettled;
816
658
  }
817
659
  // Always emit iteration-end on error so the journal records iteration
818
660
  // completion even on failure (#2344). Without this, errors in
819
661
  // runFinalize leave the journal incomplete, making diagnosis harder.
820
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration, error: msg } });
662
+ journalReporter.emit("iteration-end", { iteration, error: msg });
821
663
  // ── Pre-send model-policy block: not a retryable error (#4959 / #4850) ──
822
664
  // The model-policy gate runs before the prompt is sent. When every
823
665
  // candidate model is denied (cross-provider disabled + flat-rate
@@ -827,6 +669,12 @@ export async function autoLoop(ctx, pi, s, deps, options) {
827
669
  // instead, with the per-model deny reasons surfaced from the typed
828
670
  // error.
829
671
  if (loopErr instanceof ModelPolicyDispatchBlockedError) {
672
+ const policyDecision = decideModelPolicyBlocked({
673
+ unitType: loopErr.unitType,
674
+ unitId: loopErr.unitId,
675
+ errorMessage: msg,
676
+ reasons: loopErr.reasons,
677
+ });
830
678
  debugLog("autoLoop", {
831
679
  phase: "model-policy-blocked",
832
680
  iteration,
@@ -834,20 +682,8 @@ export async function autoLoop(ctx, pi, s, deps, options) {
834
682
  unitId: loopErr.unitId,
835
683
  reasons: loopErr.reasons,
836
684
  });
837
- ctx.ui.notify(`Auto-mode paused: model-policy denied dispatch for ${loopErr.unitType}/${loopErr.unitId}. ${msg}`, "error");
838
- deps.emitJournalEvent({
839
- ts: new Date().toISOString(),
840
- flowId,
841
- seq: nextSeq(),
842
- eventType: "unit-end",
843
- data: {
844
- unitType: loopErr.unitType,
845
- unitId: loopErr.unitId,
846
- status: "blocked",
847
- reason: "model-policy-dispatch-blocked",
848
- reasons: loopErr.reasons,
849
- },
850
- });
685
+ ctx.ui.notify(policyDecision.notifyMessage, "error");
686
+ journalReporter.emit("unit-end", policyDecision.journalData);
851
687
  // Carry the blocked unit identity into the turn-result observer:
852
688
  // the throw originated inside dispatch, so observedUnitType/Id were
853
689
  // not assigned by the success path at lines 453/631/647 — but the
@@ -855,7 +691,7 @@ export async function autoLoop(ctx, pi, s, deps, options) {
855
691
  observedUnitType = loopErr.unitType;
856
692
  observedUnitId = loopErr.unitId;
857
693
  await deps.pauseAuto(ctx, pi);
858
- finishTurn("paused", "manual-attention", msg);
694
+ finishTurn(policyDecision.turnStatus, policyDecision.failureClass, msg);
859
695
  // Do NOT increment consecutiveErrors — the failure is configuration,
860
696
  // not a transient runtime fault.
861
697
  break;
@@ -865,15 +701,19 @@ export async function autoLoop(ctx, pi, s, deps, options) {
865
701
  // LLM budget on guaranteed failures.
866
702
  const infraCode = isInfrastructureError(loopErr);
867
703
  if (infraCode) {
704
+ const infraDecision = decideInfrastructureError({
705
+ code: infraCode,
706
+ errorMessage: msg,
707
+ });
868
708
  debugLog("autoLoop", {
869
709
  phase: "infrastructure-error",
870
710
  iteration,
871
711
  code: infraCode,
872
712
  error: msg,
873
713
  });
874
- ctx.ui.notify(`Auto-mode stopped: infrastructure error ${infraCode} — ${msg}`, "error");
875
- await deps.stopAuto(ctx, pi, `Infrastructure error (${infraCode}): not recoverable by retry`);
876
- finishTurn("failed", "execution", msg);
714
+ ctx.ui.notify(infraDecision.notifyMessage, "error");
715
+ await deps.stopAuto(ctx, pi, infraDecision.stopMessage);
716
+ finishTurn(infraDecision.turnStatus, infraDecision.failureClass, msg);
877
717
  break;
878
718
  }
879
719
  // ── Credential cooldown: wait and retry with bounded budget ──
@@ -885,6 +725,12 @@ export async function autoLoop(ctx, pi, s, deps, options) {
885
725
  if (isTransientCooldownError(loopErr)) {
886
726
  consecutiveCooldowns++;
887
727
  const retryAfterMs = getCooldownRetryAfterMs(loopErr);
728
+ const cooldownDecision = decideCooldownRecovery({
729
+ consecutiveCooldowns,
730
+ maxCooldownRetries: MAX_COOLDOWN_RETRIES,
731
+ retryAfterMs,
732
+ fallbackWaitMs: COOLDOWN_FALLBACK_WAIT_MS,
733
+ });
888
734
  debugLog("autoLoop", {
889
735
  phase: "cooldown-wait",
890
736
  iteration,
@@ -892,16 +738,14 @@ export async function autoLoop(ctx, pi, s, deps, options) {
892
738
  retryAfterMs,
893
739
  error: msg,
894
740
  });
895
- if (consecutiveCooldowns > MAX_COOLDOWN_RETRIES) {
896
- ctx.ui.notify(`Auto-mode stopped: ${consecutiveCooldowns} consecutive credential cooldowns — rate limit or quota may be persistently exhausted.`, "error");
897
- await deps.stopAuto(ctx, pi, `${consecutiveCooldowns} consecutive credential cooldowns exceeded retry budget`);
741
+ if (cooldownDecision.action === "stop") {
742
+ ctx.ui.notify(cooldownDecision.notifyMessage, "error");
743
+ finishTurn("stopped", "timeout", msg);
744
+ await deps.stopAuto(ctx, pi, cooldownDecision.stopMessage);
898
745
  break;
899
746
  }
900
- const waitMs = (retryAfterMs !== undefined && retryAfterMs > 0 && retryAfterMs <= 60_000)
901
- ? retryAfterMs + 500 // Use structured hint + small buffer
902
- : COOLDOWN_FALLBACK_WAIT_MS;
903
- ctx.ui.notify(`Credentials in cooldown (${consecutiveCooldowns}/${MAX_COOLDOWN_RETRIES}) — waiting ${Math.round(waitMs / 1000)}s before retrying.`, "warning");
904
- await new Promise(resolve => setTimeout(resolve, waitMs));
747
+ ctx.ui.notify(cooldownDecision.notifyMessage, "warning");
748
+ await new Promise(resolve => setTimeout(resolve, cooldownDecision.waitMs));
905
749
  finishTurn("retry", "timeout", msg);
906
750
  continue; // Retry iteration without incrementing consecutiveErrors
907
751
  }
@@ -913,26 +757,25 @@ export async function autoLoop(ctx, pi, s, deps, options) {
913
757
  consecutiveErrors,
914
758
  error: msg,
915
759
  });
916
- if (consecutiveErrors >= 3) {
917
- // 3+ consecutive: hard stop — something is fundamentally broken
918
- const errorHistory = recentErrorMessages
919
- .map((m, i) => ` ${i + 1}. ${m}`)
920
- .join("\n");
921
- ctx.ui.notify(`Auto-mode stopped: ${consecutiveErrors} consecutive iteration failures:\n${errorHistory}`, "error");
922
- await deps.stopAuto(ctx, pi, `${consecutiveErrors} consecutive iteration failures`);
923
- finishTurn("failed", "execution", msg);
760
+ const errorDecision = decideIterationErrorRecovery({
761
+ consecutiveErrors,
762
+ recentErrorMessages,
763
+ currentErrorMessage: msg,
764
+ });
765
+ if (errorDecision.action === "stop") {
766
+ ctx.ui.notify(errorDecision.notifyMessage, "error");
767
+ await deps.stopAuto(ctx, pi, errorDecision.stopMessage);
768
+ finishTurn(errorDecision.turnStatus, "execution", msg);
924
769
  break;
925
770
  }
926
- else if (consecutiveErrors === 2) {
927
- // 2nd consecutive: try invalidating caches + re-deriving state
928
- ctx.ui.notify(`Iteration error (attempt ${consecutiveErrors}): ${msg}. Invalidating caches and retrying.`, "warning");
771
+ if (errorDecision.action === "invalidate-and-retry") {
772
+ ctx.ui.notify(errorDecision.notifyMessage, "warning");
929
773
  deps.invalidateAllCaches();
930
774
  }
931
775
  else {
932
- // 1st error: log and retry — transient failures happen
933
- ctx.ui.notify(`Iteration error: ${msg}. Retrying.`, "warning");
776
+ ctx.ui.notify(errorDecision.notifyMessage, "warning");
934
777
  }
935
- finishTurn("retry", "execution", msg);
778
+ finishTurn(errorDecision.turnStatus, "execution", msg);
936
779
  }
937
780
  }
938
781
  _clearCurrentResolve();