gsd-pi 2.79.0-dev.ece5fd8ba → 2.80.0-dev.0aaa4fc17

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