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
@@ -10,11 +10,10 @@
10
10
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
11
11
 
12
12
  import { randomUUID } from "node:crypto";
13
- import type { AutoSession, SidecarItem } from "./session.js";
13
+ import type { AutoSession } from "./session.js";
14
14
  import type { LoopDeps } from "./loop-deps.js";
15
15
  import {
16
16
  MAX_LOOP_ITERATIONS,
17
- type PhaseResult,
18
17
  type LoopState,
19
18
  type IterationContext,
20
19
  type IterationData,
@@ -24,7 +23,6 @@ import {
24
23
  runPreDispatch,
25
24
  runDispatch,
26
25
  runGuards,
27
- runUnitPhase,
28
26
  runFinalize,
29
27
  } from "./phases.js";
30
28
  import { debugLog } from "../debug-logger.js";
@@ -32,27 +30,70 @@ import { isInfrastructureError, isTransientCooldownError, getCooldownRetryAfterM
32
30
  import { ModelPolicyDispatchBlockedError } from "../auto-model-selection.js";
33
31
  import { resolveEngine } from "../engine-resolver.js";
34
32
  import { logWarning } from "../workflow-logger.js";
35
- import { gsdRoot } from "../paths.js";
36
- import { heartbeatAutoWorker } from "../db/auto-workers.js";
37
33
  import {
38
34
  recordDispatchClaim,
39
35
  markRunning as markDispatchRunning,
40
36
  markCompleted as markDispatchCompleted,
41
37
  markFailed as markDispatchFailed,
42
- markStuck as markDispatchStuck,
43
38
  getRecentForUnit as getRecentDispatchesForUnit,
44
39
  getRecentUnitKeysForProjectRoot,
45
40
  } from "../db/unit-dispatches.js";
46
41
  import { refreshMilestoneLease } from "../db/milestone-leases.js";
42
+ import { heartbeatAutoWorker } from "../db/auto-workers.js";
47
43
  import { getRuntimeKv, setRuntimeKv } from "../db/runtime-kv.js";
48
- import { atomicWriteSync } from "../atomic-write.js";
49
44
  import { resolveUokFlags } from "../uok/flags.js";
50
45
  import { scheduleSidecarQueue } from "../uok/execution-graph.js";
51
- import { ExecutionGraphScheduler } from "../uok/execution-graph.js";
52
- import type { UokGraphNode } from "../uok/contracts.js";
53
- import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
54
- import { join } from "node:path";
55
46
  import { normalizeRealPath } from "../paths.js";
47
+ import {
48
+ decideCooldownRecovery,
49
+ decideDispatchClaim,
50
+ decideEngineDispatch,
51
+ decideFinalizeResult,
52
+ decideInfrastructureError,
53
+ decideIterationErrorRecovery,
54
+ decideMemoryPressure,
55
+ decideModelPolicyBlocked,
56
+ decideMinRequestInterval,
57
+ decideWorkflowLoop,
58
+ formatDispatchExceptionSummary,
59
+ formatUnhandledDispatchErrorSummary,
60
+ resolveUnitRequestTimestamp,
61
+ shouldUseCustomEnginePath,
62
+ } from "./workflow-kernel.js";
63
+ import {
64
+ hydrateCustomVerifyRetryCounts,
65
+ saveCustomVerifyRetryCounts,
66
+ } from "./custom-verify-retry-store.js";
67
+ import {
68
+ settleDispatchCompleted,
69
+ settleDispatchFailed,
70
+ } from "./workflow-dispatch-ledger.js";
71
+ import { emitOpenUnitEndForUnit } from "../crash-recovery.js";
72
+ import { writeUnitRuntimeRecord } from "../unit-runtime.js";
73
+ import { openDispatchClaim } from "./workflow-dispatch-claim.js";
74
+ import { completeWorkflowIteration } from "./workflow-iteration-completion.js";
75
+ import { createWorkflowJournalReporter } from "./workflow-journal-reporter.js";
76
+ import { createWorkflowPhaseReporter } from "./workflow-phase-reporter.js";
77
+ import { createWorkflowTurnReporter } from "./workflow-turn-reporter.js";
78
+ import { validateWorkflowSessionLock } from "./workflow-session-lock.js";
79
+ import { dequeueSidecarItem } from "./workflow-sidecar-queue.js";
80
+ import { maintainWorkerHeartbeat } from "./workflow-worker-heartbeat.js";
81
+ import { measureMemoryPressure } from "./workflow-memory-pressure.js";
82
+ import { buildSidecarIterationData } from "./workflow-sidecar-iteration.js";
83
+ import {
84
+ createExecutionGraphUnitDispatchDeps,
85
+ runUnitPhaseViaContract,
86
+ type DispatchContract,
87
+ } from "./workflow-unit-dispatch.js";
88
+ import { handleCustomEngineDispatchOutcome } from "./workflow-custom-engine-dispatch-outcome.js";
89
+ import { buildCustomEngineIterationData } from "./workflow-custom-engine-iteration.js";
90
+ import { handleCustomEngineVerifyRetry } from "./workflow-custom-engine-retry.js";
91
+ import {
92
+ handleCustomEngineVerifyPause,
93
+ handleCustomEngineVerifyRetryOutcome,
94
+ } from "./workflow-custom-engine-verify-outcome.js";
95
+ import { handleCustomEngineReconcile } from "./workflow-custom-engine-reconcile.js";
96
+ import { handleCustomEngineReconcileOutcome } from "./workflow-custom-engine-reconcile-outcome.js";
56
97
 
57
98
  // ── Stuck detection persistence (#3704) ──────────────────────────────────
58
99
  // Phase C migration: stuck-state.json deleted in favor of DB-backed
@@ -98,132 +139,44 @@ function saveStuckState(s: AutoSession, state: LoopState): void {
98
139
  }
99
140
  }
100
141
 
101
- // ── Custom workflow verification retry persistence ───────────────────────
102
- // Custom workflows can request verification retries after a step runs. The
103
- // retry budget must survive an auto-mode restart or a failing verifier can
104
- // consume a fresh retry budget every session.
105
- function customVerifyRetryStateDir(s: Pick<AutoSession, "activeRunDir" | "basePath">): string {
106
- return s.activeRunDir ? join(s.activeRunDir, "runtime") : join(gsdRoot(s.basePath), "runtime");
142
+ function logDispatchLedgerWriteFailure(err: unknown): void {
143
+ debugLog("autoLoop", {
144
+ phase: "dispatch-ledger-write-failed",
145
+ error: err instanceof Error ? err.message : String(err),
146
+ });
107
147
  }
108
148
 
109
- function customVerifyRetryStatePath(s: Pick<AutoSession, "activeRunDir" | "basePath">): string {
110
- return join(customVerifyRetryStateDir(s), "custom-verify-retries.json");
149
+ function logDispatchClaimRejected(details: {
150
+ unitId: string;
151
+ reason: string;
152
+ existingId?: number;
153
+ existingWorker?: string;
154
+ }): void {
155
+ debugLog("autoLoop", {
156
+ phase: "dispatch-claim-rejected",
157
+ ...details,
158
+ });
111
159
  }
112
160
 
113
- function hydrateCustomVerifyRetryCounts(s: AutoSession): Map<string, number> {
114
- if (s.verificationRetryCount.size > 0) {
115
- return s.verificationRetryCount;
116
- }
117
-
118
- try {
119
- const raw = JSON.parse(readFileSync(customVerifyRetryStatePath(s), "utf-8"));
120
- const counts = raw && typeof raw === "object" && raw.counts && typeof raw.counts === "object"
121
- ? raw.counts as Record<string, unknown>
122
- : {};
123
- for (const [key, value] of Object.entries(counts)) {
124
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
125
- s.verificationRetryCount.set(key, Math.floor(value));
126
- }
127
- }
128
- } catch (err) {
129
- debugLog("autoLoop", { phase: "load-custom-verify-retries-failed", error: err instanceof Error ? err.message : String(err) });
130
- }
131
-
132
- return s.verificationRetryCount;
161
+ function logDispatchClaimFailed(err: unknown): void {
162
+ debugLog("autoLoop", {
163
+ phase: "dispatch-claim-failed",
164
+ error: err instanceof Error ? err.message : String(err),
165
+ });
133
166
  }
134
167
 
135
- function saveCustomVerifyRetryCounts(s: AutoSession): void {
136
- const retryCounts = s.verificationRetryCount;
137
- const filePath = customVerifyRetryStatePath(s);
138
-
139
- try {
140
- if (!retryCounts || retryCounts.size === 0) {
141
- unlinkSync(filePath);
142
- return;
143
- }
144
- mkdirSync(customVerifyRetryStateDir(s), { recursive: true });
145
- atomicWriteSync(filePath, JSON.stringify({
146
- counts: Object.fromEntries(retryCounts),
147
- updatedAt: new Date().toISOString(),
148
- }) + "\n");
149
- } catch (err) {
150
- const code = err && typeof err === "object" && "code" in err ? (err as { code?: string }).code : undefined;
151
- if (code !== "ENOENT") {
152
- debugLog("autoLoop", { phase: "save-custom-verify-retries-failed", error: err instanceof Error ? err.message : String(err) });
153
- }
154
- }
168
+ function logCustomVerifyRetryLoadFailure(err: unknown): void {
169
+ debugLog("autoLoop", {
170
+ phase: "load-custom-verify-retries-failed",
171
+ error: err instanceof Error ? err.message : String(err),
172
+ });
155
173
  }
156
174
 
157
- /**
158
- * Phase B helper: open a unit_dispatches row in 'claimed' state and
159
- * immediately transition it to 'running'. Returns a tri-state result so
160
- * callers can distinguish between a degraded ledger write and an explicit
161
- * already-active rejection from the partial unique index.
162
- *
163
- * Single-worker compatibility: this function is best-effort and never
164
- * throws. The auto-loop must continue to behave identically when the
165
- * ledger is degraded.
166
- */
167
- type DispatchClaimOutcome =
168
- | { kind: "opened"; dispatchId: number }
169
- | { kind: "skip"; reason: "already-active" | "stale-lease"; existingId?: number; existingWorker?: string }
170
- | { kind: "degraded" };
171
-
172
- function openDispatchClaim(
173
- s: AutoSession,
174
- flowId: string,
175
- turnId: string,
176
- iterData: IterationData,
177
- ): DispatchClaimOutcome {
178
- if (!s.workerId || s.milestoneLeaseToken === null) return { kind: "degraded" };
179
- const mid = iterData.mid;
180
- if (!mid) return { kind: "degraded" };
181
-
182
- const recent = getRecentDispatchesForUnit(iterData.unitId, 1);
183
- const attemptN = (recent[0]?.attempt_n ?? 0) + 1;
184
-
185
- let claim: ReturnType<typeof recordDispatchClaim>;
186
- try {
187
- claim = recordDispatchClaim({
188
- traceId: flowId,
189
- turnId,
190
- workerId: s.workerId,
191
- milestoneLeaseToken: s.milestoneLeaseToken,
192
- milestoneId: mid,
193
- sliceId: iterData.state.activeSlice?.id ?? null,
194
- taskId: iterData.state.activeTask?.id ?? null,
195
- unitType: iterData.unitType,
196
- unitId: iterData.unitId,
197
- attemptN,
198
- });
199
- if (!claim.ok) {
200
- debugLog("autoLoop", {
201
- phase: "dispatch-claim-rejected",
202
- unitId: iterData.unitId,
203
- reason: claim.error,
204
- existingId: "existingId" in claim ? claim.existingId : undefined,
205
- existingWorker: "existingWorker" in claim ? claim.existingWorker : undefined,
206
- });
207
- if (claim.error === "already_active") {
208
- return {
209
- kind: "skip",
210
- reason: "already-active",
211
- existingId: claim.existingId,
212
- existingWorker: claim.existingWorker,
213
- };
214
- }
215
- return { kind: "skip", reason: "stale-lease" };
216
- }
217
- markDispatchRunning(claim.dispatchId);
218
- return { kind: "opened", dispatchId: claim.dispatchId };
219
- } catch (err) {
220
- debugLog("autoLoop", {
221
- phase: "dispatch-claim-failed",
222
- error: err instanceof Error ? err.message : String(err),
223
- });
224
- return { kind: "degraded" };
225
- }
226
-
175
+ function logCustomVerifyRetrySaveFailure(err: unknown): void {
176
+ debugLog("autoLoop", {
177
+ phase: "save-custom-verify-retries-failed",
178
+ error: err instanceof Error ? err.message : String(err),
179
+ });
227
180
  }
228
181
 
229
182
  // ── Memory pressure monitoring (#3331) ──────────────────────────────────
@@ -231,108 +184,53 @@ function openDispatchClaim(
231
184
  // the OS OOM killer sends SIGKILL. The threshold is 90% of the V8 heap
232
185
  // limit (--max-old-space-size or default ~1.5-4GB depending on platform).
233
186
  const MEMORY_CHECK_INTERVAL = 5; // check every 5 iterations
234
- const MEMORY_PRESSURE_THRESHOLD = 0.85; // 85% of heap limit
235
187
  const MAX_CUSTOM_ENGINE_VERIFY_RETRIES = 3;
236
188
 
237
- type DispatchContract = "legacy-direct" | "uok-scheduler";
238
-
239
189
  interface AutoLoopOptions {
240
190
  dispatchContract?: DispatchContract;
241
191
  }
242
192
 
243
- function checkMemoryPressure(): { pressured: boolean; heapMB: number; limitMB: number; pct: number } {
244
- const mem = process.memoryUsage();
245
- // v8.getHeapStatistics() gives heap_size_limit but requires import
246
- // Use a conservative estimate: RSS > 3GB is danger zone on most systems
247
- const heapMB = Math.round(mem.heapUsed / 1024 / 1024);
248
- const rssMB = Math.round(mem.rss / 1024 / 1024);
249
- // Try to get the actual V8 heap limit
250
- let limitMB = 4096; // conservative default
251
- try {
252
- const v8 = require("node:v8");
253
- const stats = v8.getHeapStatistics();
254
- limitMB = Math.round(stats.heap_size_limit / 1024 / 1024);
255
- } catch { limitMB = 4096; /* v8 stats unavailable — use conservative default */ }
256
- const pct = heapMB / limitMB;
257
- return { pressured: pct > MEMORY_PRESSURE_THRESHOLD, heapMB, limitMB, pct };
258
- }
259
-
260
- function resolveDispatchNodeKind(
261
- unitType: string,
262
- sidecarItem?: SidecarItem,
263
- ): UokGraphNode["kind"] {
264
- if (sidecarItem?.kind === "hook") return "hook";
265
- if (sidecarItem?.kind === "triage") return "verification";
266
- if (sidecarItem?.kind === "quick-task") return "team-worker";
267
-
268
- if (unitType.startsWith("hook/")) return "hook";
269
- if (unitType === "reactive-execute") return "subagent";
270
- if (
271
- unitType === "gate-evaluate"
272
- || unitType === "validate-milestone"
273
- || unitType === "run-uat"
274
- || unitType === "complete-slice"
275
- ) {
276
- return "verification";
277
- }
278
- if (unitType === "replan-slice" || unitType === "reassess-roadmap") {
279
- return "reprocess";
280
- }
281
- return "unit";
282
- }
283
-
284
193
  async function enforceMinRequestInterval(s: AutoSession, prefs: IterationContext["prefs"]): Promise<void> {
285
194
  const minInterval = prefs?.min_request_interval_ms ?? 0;
286
- if (minInterval > 0 && s.lastRequestTimestamp > 0) {
287
- const elapsed = Date.now() - s.lastRequestTimestamp;
288
- if (elapsed < minInterval) {
289
- const waitMs = minInterval - elapsed;
290
- debugLog("autoLoop", { phase: "rate-limit-wait", waitMs });
291
- await new Promise<void>(r => setTimeout(r, waitMs));
292
- }
195
+ const decision = decideMinRequestInterval({
196
+ minIntervalMs: minInterval,
197
+ lastRequestTimestamp: s.lastRequestTimestamp,
198
+ nowMs: Date.now(),
199
+ });
200
+ if (decision.action === "wait") {
201
+ debugLog("autoLoop", { phase: "rate-limit-wait", waitMs: decision.waitMs });
202
+ await new Promise<void>(r => setTimeout(r, decision.waitMs));
293
203
  }
294
204
  }
295
205
 
296
- async function runUnitPhaseViaContract(
297
- dispatchContract: DispatchContract,
298
- ic: IterationContext,
299
- iterData: IterationData,
300
- loopState: LoopState,
301
- sidecarItem?: SidecarItem,
302
- ): Promise<PhaseResult<{ unitStartedAt?: number; requestDispatchedAt?: number }>> {
303
- if (dispatchContract === "legacy-direct") {
304
- return runUnitPhase(ic, iterData, loopState, sidecarItem);
305
- }
306
-
307
- const scheduler = new ExecutionGraphScheduler();
308
- let outcome: PhaseResult<{ unitStartedAt?: number; requestDispatchedAt?: number }> | null = null;
309
- const executeNode = async (): Promise<void> => {
310
- outcome = await runUnitPhase(ic, iterData, loopState, sidecarItem);
311
- };
312
- const kinds: UokGraphNode["kind"][] = [
313
- "unit",
314
- "hook",
315
- "subagent",
316
- "team-worker",
317
- "verification",
318
- "reprocess",
319
- ];
320
- for (const kind of kinds) scheduler.registerHandler(kind, executeNode);
321
-
322
- const nodeId = `dispatch:${ic.iteration}:${iterData.unitType}:${iterData.unitId}`;
323
- await scheduler.run([
324
- {
325
- id: nodeId,
326
- kind: resolveDispatchNodeKind(iterData.unitType, sidecarItem),
327
- dependsOn: [],
328
- metadata: {
329
- unitType: iterData.unitType,
330
- unitId: iterData.unitId,
206
+ function closeOutCrashedUnit(s: AutoSession, iterData: IterationData, err: unknown): void {
207
+ const summary = formatDispatchExceptionSummary({ error: err });
208
+ try {
209
+ emitOpenUnitEndForUnit(
210
+ s.basePath,
211
+ iterData.unitType,
212
+ iterData.unitId,
213
+ "cancelled",
214
+ {
215
+ message: summary,
216
+ category: "unit-exception",
217
+ isTransient: false,
331
218
  },
332
- },
333
- ], { parallel: false, maxWorkers: 1 });
334
-
335
- return outcome ?? { action: "break", reason: "scheduler-dispatch-missing-result" };
219
+ );
220
+ writeUnitRuntimeRecord(
221
+ s.basePath,
222
+ iterData.unitType,
223
+ iterData.unitId,
224
+ s.currentUnit?.startedAt ?? Date.now(),
225
+ {
226
+ phase: "crashed",
227
+ lastProgressAt: Date.now(),
228
+ lastProgressKind: "unit-exception",
229
+ },
230
+ );
231
+ } catch (closeoutErr) {
232
+ logWarning("dispatch", `unit crash closeout failed: ${closeoutErr instanceof Error ? closeoutErr.message : String(closeoutErr)}`);
233
+ }
336
234
  }
337
235
 
338
236
  /**
@@ -353,6 +251,7 @@ export async function autoLoop(
353
251
  debugLog("autoLoop", { phase: "enter" });
354
252
  let iteration = 0;
355
253
  const dispatchContract = options?.dispatchContract ?? "legacy-direct";
254
+ const unitDispatchDeps = createExecutionGraphUnitDispatchDeps();
356
255
  // Load persisted stuck state so counters survive session restarts (#3704)
357
256
  const persisted = loadStuckState(s);
358
257
  const loopState: LoopState = {
@@ -368,69 +267,71 @@ export async function autoLoop(
368
267
  iteration++;
369
268
  debugLog("autoLoop", { phase: "loop-top", iteration });
370
269
 
371
- // Phase B: heartbeat the worker registry + active milestone lease so
372
- // janitors and concurrent workers see a live process. Best-effort —
373
- // DB unavailability or stale state must not stop the loop.
374
- if (s.workerId) {
375
- try {
376
- heartbeatAutoWorker(s.workerId);
377
- if (s.currentMilestoneId && s.milestoneLeaseToken) {
378
- refreshMilestoneLease(s.workerId, s.currentMilestoneId, s.milestoneLeaseToken);
379
- }
380
- } catch (err) {
381
- debugLog("autoLoop", {
382
- phase: "heartbeat-failed",
383
- error: err instanceof Error ? err.message : String(err),
384
- });
385
- }
386
- }
270
+ maintainWorkerHeartbeat(s, {
271
+ heartbeatAutoWorker,
272
+ refreshMilestoneLease,
273
+ logHeartbeatFailure: err => debugLog("autoLoop", {
274
+ phase: "heartbeat-failed",
275
+ error: err instanceof Error ? err.message : String(err),
276
+ }),
277
+ });
387
278
 
388
279
  // ── Journal: per-iteration flow grouping ──
389
280
  const flowId = randomUUID();
390
281
  let seqCounter = 0;
391
282
  const nextSeq = () => ++seqCounter;
283
+ const journalReporter = createWorkflowJournalReporter({
284
+ emitJournalEvent: deps.emitJournalEvent,
285
+ flowId,
286
+ nextSeq,
287
+ });
392
288
  const turnId = randomUUID();
393
289
  s.currentTraceId = flowId;
394
290
  s.currentTurnId = turnId;
395
291
  const turnStartedAt = new Date().toISOString();
396
292
  let observedUnitType: string | undefined;
397
293
  let observedUnitId: string | undefined;
398
- let turnFinished = false;
294
+ const phaseReporter = createWorkflowPhaseReporter({
295
+ observer: deps.uokObserver,
296
+ });
297
+ const turnReporter = createWorkflowTurnReporter({
298
+ observer: deps.uokObserver,
299
+ traceId: flowId,
300
+ turnId,
301
+ iteration,
302
+ basePath: s.basePath,
303
+ startedAt: turnStartedAt,
304
+ clearCurrentTurn: () => {
305
+ s.currentTraceId = null;
306
+ s.currentTurnId = null;
307
+ },
308
+ });
399
309
  const finishTurn = (
400
310
  status: "completed" | "failed" | "paused" | "stopped" | "skipped" | "retry",
401
311
  failureClass: "none" | "unknown" | "manual-attention" | "timeout" | "execution" | "closeout" | "git" = "none",
402
312
  error?: string,
403
313
  ): void => {
404
- if (turnFinished) return;
405
- turnFinished = true;
406
- deps.uokObserver?.onTurnResult({
407
- traceId: flowId,
408
- turnId,
409
- iteration,
314
+ turnReporter.finish({
410
315
  unitType: observedUnitType,
411
316
  unitId: observedUnitId,
412
317
  status,
413
318
  failureClass,
414
- phaseResults: [],
415
319
  error,
416
- startedAt: turnStartedAt,
417
- finishedAt: new Date().toISOString(),
418
320
  });
419
- s.currentTraceId = null;
420
- s.currentTurnId = null;
421
321
  };
422
- deps.uokObserver?.onTurnStart({
423
- traceId: flowId,
424
- turnId,
322
+ turnReporter.start();
323
+
324
+ const iterationDecision = decideWorkflowLoop({
325
+ active: s.active,
425
326
  iteration,
426
- basePath: s.basePath,
427
- startedAt: turnStartedAt,
327
+ maxIterations: MAX_LOOP_ITERATIONS,
328
+ hasCommandContext: true,
329
+ sessionLockValid: true,
428
330
  });
429
-
430
- if (iteration > MAX_LOOP_ITERATIONS) {
331
+ if (iterationDecision.action === "stop" && iterationDecision.reason === "max-iterations") {
431
332
  debugLog("autoLoop", {
432
333
  phase: "exit",
433
- reason: "max-iterations",
334
+ reason: iterationDecision.reason,
434
335
  iteration,
435
336
  });
436
337
  await deps.stopAuto(
@@ -445,30 +346,45 @@ export async function autoLoop(
445
346
  // ── Memory pressure check (#3331) ──
446
347
  // Graceful shutdown before OOM killer sends SIGKILL.
447
348
  if (iteration % MEMORY_CHECK_INTERVAL === 0) {
448
- const mem = checkMemoryPressure();
349
+ const mem = measureMemoryPressure();
449
350
  debugLog("autoLoop", { phase: "memory-check", ...mem });
450
- if (mem.pressured) {
451
- logWarning("dispatch", `Memory pressure: ${mem.heapMB}MB / ${mem.limitMB}MB (${Math.round(mem.pct * 100)}%) — stopping auto-mode to prevent OOM kill`);
452
- await deps.stopAuto(
453
- ctx,
454
- pi,
455
- `Memory pressure: heap at ${mem.heapMB}MB / ${mem.limitMB}MB (${Math.round(mem.pct * 100)}%). ` +
456
- `Stopping gracefully to prevent OOM kill after ${iteration} iterations. ` +
457
- `Resume with /gsd auto to continue from where you left off.`,
458
- );
459
- finishTurn("stopped", "timeout", "memory-pressure");
351
+ const memoryDecision = decideMemoryPressure({ ...mem, iteration });
352
+ if (memoryDecision.action === "stop") {
353
+ logWarning("dispatch", memoryDecision.warningMessage);
354
+ await deps.stopAuto(ctx, pi, memoryDecision.stopMessage);
355
+ finishTurn("stopped", "timeout", memoryDecision.turnError);
460
356
  break;
461
357
  }
462
358
  }
463
359
 
464
- if (!s.cmdCtx) {
360
+ const commandContextDecision = decideWorkflowLoop({
361
+ active: s.active,
362
+ iteration,
363
+ maxIterations: MAX_LOOP_ITERATIONS,
364
+ hasCommandContext: Boolean(s.cmdCtx),
365
+ sessionLockValid: true,
366
+ });
367
+ if (commandContextDecision.action === "stop" && commandContextDecision.reason === "missing-command-context") {
465
368
  debugLog("autoLoop", { phase: "exit", reason: "no-cmdCtx" });
466
- finishTurn("stopped", "manual-attention", "missing-command-context");
369
+ finishTurn("stopped", "manual-attention", commandContextDecision.reason);
467
370
  break;
468
371
  }
469
372
 
470
373
  let dispatchId: number | null = null;
471
374
  let dispatchSettled = false;
375
+ const completeIteration = (): void => {
376
+ completeWorkflowIteration({
377
+ get consecutiveErrors() { return consecutiveErrors; },
378
+ set consecutiveErrors(value) { consecutiveErrors = value; },
379
+ get consecutiveCooldowns() { return consecutiveCooldowns; },
380
+ set consecutiveCooldowns(value) { consecutiveCooldowns = value; },
381
+ recentErrorMessages,
382
+ }, {
383
+ emitIterationEnd: () => journalReporter.emit("iteration-end", { iteration }),
384
+ saveStuckState: () => saveStuckState(s, loopState),
385
+ logIterationComplete: () => debugLog("autoLoop", { phase: "iteration-complete", iteration }),
386
+ });
387
+ };
472
388
 
473
389
  try {
474
390
  // ── Blanket try/catch: one bad iteration must not kill the session
@@ -476,47 +392,44 @@ export async function autoLoop(
476
392
  const uokFlags = resolveUokFlags(prefs);
477
393
 
478
394
  // ── Check sidecar queue before deriveState ──
479
- let sidecarItem: SidecarItem | undefined;
480
- if (s.sidecarQueue.length > 0) {
481
- if (uokFlags.executionGraph && s.sidecarQueue.length > 1) {
482
- try {
483
- s.sidecarQueue = await scheduleSidecarQueue(s.sidecarQueue);
484
- } catch (err) {
485
- logWarning("dispatch", `sidecar queue scheduling failed: ${err instanceof Error ? err.message : String(err)}`);
486
- }
487
- }
488
- sidecarItem = s.sidecarQueue.shift()!;
489
- debugLog("autoLoop", {
490
- phase: "sidecar-dequeue",
491
- kind: sidecarItem.kind,
492
- unitType: sidecarItem.unitType,
493
- unitId: sidecarItem.unitId,
494
- });
495
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "sidecar-dequeue", data: { kind: sidecarItem.kind, unitType: sidecarItem.unitType, unitId: sidecarItem.unitId } });
496
- }
395
+ // NOTE: Sidecar dequeue MUST run before validateWorkflowSessionLock so a
396
+ // queued item is popped (and the `sidecar-dequeue` journal event emitted)
397
+ // even when the session lock invalidates this iteration. Inverting this
398
+ // order silently drops queued items on lock-loss. Refs #5308.
399
+ const sidecarItem = await dequeueSidecarItem({
400
+ queue: s.sidecarQueue,
401
+ executionGraphEnabled: uokFlags.executionGraph,
402
+ scheduleQueue: scheduleSidecarQueue,
403
+ warnSchedulingFailure: message => logWarning("dispatch", `sidecar queue scheduling failed: ${message}`),
404
+ logDequeue: payload => debugLog("autoLoop", { phase: "sidecar-dequeue", ...payload }),
405
+ emitDequeue: payload => journalReporter.emit("sidecar-dequeue", payload),
406
+ });
497
407
 
498
- const sessionLockBase = deps.lockBase();
499
- if (sessionLockBase) {
500
- const lockStatus = deps.validateSessionLock(sessionLockBase);
501
- if (!lockStatus.valid) {
502
- debugLog("autoLoop", {
408
+ const sessionLockOutcome = validateWorkflowSessionLock({
409
+ active: s.active,
410
+ iteration,
411
+ maxIterations: MAX_LOOP_ITERATIONS,
412
+ deps: {
413
+ lockBase: deps.lockBase,
414
+ validateSessionLock: deps.validateSessionLock,
415
+ handleLostSessionLock: lockStatus => deps.handleLostSessionLock(ctx, lockStatus),
416
+ logInvalidSessionLock: details => debugLog("autoLoop", {
503
417
  phase: "session-lock-invalid",
504
- reason: lockStatus.failureReason ?? "unknown",
505
- existingPid: lockStatus.existingPid,
506
- expectedPid: lockStatus.expectedPid,
507
- });
508
- deps.handleLostSessionLock(ctx, lockStatus);
509
- debugLog("autoLoop", {
418
+ ...details,
419
+ }),
420
+ logSessionLockExit: details => debugLog("autoLoop", {
510
421
  phase: "exit",
511
- reason: "session-lock-lost",
512
- detail: lockStatus.failureReason ?? "unknown",
513
- });
514
- break;
515
- }
422
+ ...details,
423
+ }),
424
+ },
425
+ });
426
+ if (sessionLockOutcome.action === "stop" && sessionLockOutcome.reason === "session-lock-lost") {
427
+ finishTurn("stopped", "manual-attention", sessionLockOutcome.reason);
428
+ break;
516
429
  }
517
430
 
518
431
  const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
519
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
432
+ journalReporter.emit("iteration-start", { iteration });
520
433
  let iterData: IterationData;
521
434
 
522
435
  // ── Custom engine path ──────────────────────────────────────────────
@@ -527,7 +440,11 @@ export async function autoLoop(
527
440
  //
528
441
  // GSD_ENGINE_BYPASS=1 skips the engine layer entirely — falls through
529
442
  // to the dev path below.
530
- if (s.activeEngineId != null && s.activeEngineId !== "dev" && !sidecarItem && process.env.GSD_ENGINE_BYPASS !== "1") {
443
+ if (shouldUseCustomEnginePath({
444
+ activeEngineId: s.activeEngineId,
445
+ hasSidecarItem: Boolean(sidecarItem),
446
+ engineBypass: process.env.GSD_ENGINE_BYPASS === "1",
447
+ })) {
531
448
  debugLog("autoLoop", { phase: "custom-engine-derive", iteration, engineId: s.activeEngineId });
532
449
 
533
450
  const { engine, policy } = resolveEngine({
@@ -547,45 +464,48 @@ export async function autoLoop(
547
464
  isComplete: engineState.isComplete,
548
465
  });
549
466
  if (engineState.isComplete) {
467
+ finishTurn("completed");
550
468
  await deps.stopAuto(ctx, pi, "Workflow complete");
551
469
  break;
552
470
  }
553
471
 
554
472
  debugLog("autoLoop", { phase: "custom-engine-dispatch", iteration });
555
473
  const dispatch = await engine.resolveDispatch(engineState, { basePath: s.basePath });
556
-
557
- if (dispatch.action === "stop") {
558
- await deps.stopAuto(ctx, pi, dispatch.reason ?? "Engine stopped");
474
+ const engineDispatchDecision = decideEngineDispatch(dispatch.action === "stop"
475
+ ? { action: "stop", reason: dispatch.reason }
476
+ : { action: dispatch.action });
477
+ const dispatchFlow = await handleCustomEngineDispatchOutcome({
478
+ decision: engineDispatchDecision,
479
+ deps: {
480
+ stopAuto: reason => deps.stopAuto(ctx, pi, reason),
481
+ },
482
+ });
483
+ if (dispatchFlow.action === "break") {
484
+ finishTurn("stopped", "manual-attention", "custom-engine-dispatch-stop");
559
485
  break;
560
486
  }
561
- if (dispatch.action === "skip") {
487
+ if (dispatchFlow.action === "continue") {
488
+ finishTurn("skipped");
562
489
  continue;
563
490
  }
564
491
 
565
492
  // dispatch.action === "dispatch"
566
- const step = dispatch.step!;
567
- const gsdState = await deps.deriveState(s.canonicalProjectRoot);
568
- debugLog("autoLoop", {
569
- phase: "post-derive",
570
- site: "custom-engine-gsd-state",
493
+ if (dispatch.action !== "dispatch") {
494
+ finishTurn("skipped");
495
+ continue;
496
+ }
497
+ const step = dispatch.step;
498
+ iterData = await buildCustomEngineIterationData({
499
+ step,
571
500
  basePath: s.basePath,
572
501
  canonicalProjectRoot: s.canonicalProjectRoot,
573
- derivedPhase: gsdState.phase,
574
- activeUnit: gsdState.activeTask?.id ?? gsdState.activeSlice?.id ?? gsdState.activeMilestone?.id,
502
+ currentMilestoneId: s.currentMilestoneId,
503
+ deriveState: deps.deriveState,
504
+ logPostDerive: details => debugLog("autoLoop", {
505
+ phase: "post-derive",
506
+ ...details,
507
+ }),
575
508
  });
576
-
577
- iterData = {
578
- unitType: step.unitType,
579
- unitId: step.unitId,
580
- prompt: step.prompt,
581
- finalPrompt: step.prompt,
582
- pauseAfterUatDispatch: false,
583
- state: gsdState,
584
- mid: s.currentMilestoneId ?? "workflow",
585
- midTitle: "Workflow",
586
- isRetry: false,
587
- previousTier: undefined,
588
- };
589
509
  observedUnitType = iterData.unitType;
590
510
  observedUnitId = iterData.unitId;
591
511
 
@@ -594,7 +514,7 @@ export async function autoLoop(
594
514
 
595
515
  // ── Guards (shared with dev path) ──
596
516
  const guardsResult = await runGuards(ic, s.currentMilestoneId ?? "workflow");
597
- deps.uokObserver?.onPhaseResult("guard", guardsResult.action, {
517
+ phaseReporter.report("guard", guardsResult.action, {
598
518
  unitType: iterData.unitType,
599
519
  unitId: iterData.unitId,
600
520
  });
@@ -605,17 +525,28 @@ export async function autoLoop(
605
525
 
606
526
  // ── Unit execution (shared with dev path) ──
607
527
  await enforceMinRequestInterval(s, prefs);
608
- const unitPhaseResult = await runUnitPhaseViaContract(
609
- dispatchContract,
610
- ic,
611
- iterData,
612
- loopState,
613
- );
528
+ let unitPhaseResult: Awaited<ReturnType<typeof runUnitPhaseViaContract>>;
529
+ try {
530
+ unitPhaseResult = await runUnitPhaseViaContract(
531
+ dispatchContract,
532
+ ic,
533
+ iterData,
534
+ loopState,
535
+ undefined,
536
+ unitDispatchDeps,
537
+ );
538
+ } catch (err) {
539
+ if (err instanceof ModelPolicyDispatchBlockedError) {
540
+ throw err;
541
+ }
542
+ closeOutCrashedUnit(s, iterData, err);
543
+ throw err;
544
+ }
614
545
  if (unitPhaseResult.action === "next") {
615
- const requestTimestamp = unitPhaseResult.data.requestDispatchedAt ?? unitPhaseResult.data.unitStartedAt;
616
- if (typeof requestTimestamp === "number") s.lastRequestTimestamp = requestTimestamp;
546
+ const requestTimestamp = resolveUnitRequestTimestamp(unitPhaseResult.data);
547
+ if (requestTimestamp !== undefined) s.lastRequestTimestamp = requestTimestamp;
617
548
  }
618
- deps.uokObserver?.onPhaseResult("unit", unitPhaseResult.action, {
549
+ phaseReporter.report("unit", unitPhaseResult.action, {
619
550
  unitType: iterData.unitType,
620
551
  unitId: iterData.unitId,
621
552
  });
@@ -628,116 +559,93 @@ export async function autoLoop(
628
559
  debugLog("autoLoop", { phase: "custom-engine-verify", iteration, unitId: iterData.unitId });
629
560
  const verifyResult = await policy.verify(iterData.unitType, iterData.unitId, { basePath: s.basePath });
630
561
  if (verifyResult === "pause") {
631
- await deps.pauseAuto(ctx, pi);
632
- deps.uokObserver?.onPhaseResult("custom-engine", "pause", {
562
+ const verifyFlow = await handleCustomEngineVerifyPause({
633
563
  unitType: iterData.unitType,
634
564
  unitId: iterData.unitId,
565
+ deps: {
566
+ pauseAuto: () => deps.pauseAuto(ctx, pi),
567
+ stopAuto: reason => deps.stopAuto(ctx, pi, reason),
568
+ reportPause: details => phaseReporter.report("custom-engine", "pause", details),
569
+ finishTurn,
570
+ },
635
571
  });
636
- finishTurn("paused", "manual-attention", "custom-engine-verify-pause");
637
- break;
572
+ if (verifyFlow.action === "break") break;
638
573
  }
639
574
  if (verifyResult === "retry") {
640
- const recoveryKey = `${iterData.unitType}/${iterData.unitId}`;
641
- const retryCounts = hydrateCustomVerifyRetryCounts(s);
642
- const attempts = (retryCounts.get(recoveryKey) ?? 0) + 1;
643
- retryCounts.set(recoveryKey, attempts);
644
- saveCustomVerifyRetryCounts(s);
645
- debugLog("autoLoop", { phase: "custom-engine-verify-retry", iteration, unitId: iterData.unitId, attempts });
646
- deps.uokObserver?.onPhaseResult("custom-engine", "retry", {
575
+ const retryOutcome = await handleCustomEngineVerifyRetry({
576
+ session: s,
647
577
  unitType: iterData.unitType,
648
578
  unitId: iterData.unitId,
649
- attempts,
579
+ basePath: s.basePath,
580
+ iteration,
581
+ maxRetries: MAX_CUSTOM_ENGINE_VERIFY_RETRIES,
582
+ deps: {
583
+ hydrateRetryCounts: () => hydrateCustomVerifyRetryCounts(s, {
584
+ logFailure: logCustomVerifyRetryLoadFailure,
585
+ }),
586
+ saveRetryCounts: () => saveCustomVerifyRetryCounts(s, {
587
+ logFailure: logCustomVerifyRetrySaveFailure,
588
+ }),
589
+ recover: (unitType, unitId, options) => policy.recover(unitType, unitId, options),
590
+ logRetry: details => debugLog("autoLoop", {
591
+ phase: "custom-engine-verify-retry",
592
+ ...details,
593
+ }),
594
+ reportRetry: details => phaseReporter.report("custom-engine", "retry", details),
595
+ },
650
596
  });
651
- if (attempts > MAX_CUSTOM_ENGINE_VERIFY_RETRIES) {
652
- const recovery = await policy.recover(iterData.unitType, iterData.unitId, { basePath: s.basePath });
653
- if (recovery.outcome === "pause") {
654
- await deps.pauseAuto(ctx, pi);
655
- finishTurn("paused", "manual-attention", recovery.reason ?? "custom-engine-verify-retry-exhausted");
656
- break;
657
- }
658
- if (recovery.outcome === "skip") {
659
- await deps.stopAuto(
660
- ctx,
661
- pi,
662
- recovery.reason ??
663
- `Custom workflow verification for ${iterData.unitId} requested skip after retry exhaustion, but the custom engine cannot reconcile skipped steps.`,
664
- );
665
- finishTurn("stopped", "manual-attention", "custom-engine-verify-retry-exhausted");
666
- break;
667
- }
668
- const exhaustedReason =
669
- `Custom workflow verification for ${iterData.unitId} requested retry ${attempts} times without passing.`;
670
- await deps.stopAuto(
671
- ctx,
672
- pi,
673
- recovery.outcome === "stop" && recovery.reason ? recovery.reason : exhaustedReason,
674
- );
675
- finishTurn("stopped", "manual-attention", "custom-engine-verify-retry-exhausted");
676
- break;
677
- }
678
- finishTurn("retry");
597
+ const retryFlow = await handleCustomEngineVerifyRetryOutcome({
598
+ outcome: retryOutcome,
599
+ deps: {
600
+ pauseAuto: () => deps.pauseAuto(ctx, pi),
601
+ stopAuto: reason => deps.stopAuto(ctx, pi, reason),
602
+ reportPause: details => phaseReporter.report("custom-engine", "pause", details),
603
+ finishTurn,
604
+ },
605
+ });
606
+ if (retryFlow.action === "break") break;
679
607
  continue;
680
608
  }
681
609
 
682
610
  // Verification passed — mark step complete
683
- s.verificationRetryCount?.delete(`${iterData.unitType}/${iterData.unitId}`);
684
- saveCustomVerifyRetryCounts(s);
685
- debugLog("autoLoop", { phase: "custom-engine-reconcile", iteration, unitId: iterData.unitId });
686
- const reconcileResult = await engine.reconcile(engineState, {
687
- unitType: iterData.unitType,
688
- unitId: iterData.unitId,
689
- startedAt: s.currentUnit?.startedAt ?? Date.now(),
690
- finishedAt: Date.now(),
611
+ const reconcileOutcome = await handleCustomEngineReconcile({
612
+ session: s,
613
+ engineState,
614
+ iterData,
615
+ iteration,
616
+ deps: {
617
+ saveRetryCounts: () => saveCustomVerifyRetryCounts(s, {
618
+ logFailure: logCustomVerifyRetrySaveFailure,
619
+ }),
620
+ logReconcile: details => debugLog("autoLoop", {
621
+ phase: "custom-engine-reconcile",
622
+ ...details,
623
+ }),
624
+ reconcile: (state, completedStep) => engine.reconcile(state, completedStep),
625
+ now: () => Date.now(),
626
+ clearUnitTimeout: deps.clearUnitTimeout,
627
+ completeIteration,
628
+ },
691
629
  });
692
-
693
- deps.clearUnitTimeout();
694
- consecutiveErrors = 0;
695
- consecutiveCooldowns = 0;
696
- recentErrorMessages.length = 0;
697
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
698
- saveStuckState(s, loopState); // persist across session restarts (#3704)
699
- debugLog("autoLoop", { phase: "iteration-complete", iteration });
700
-
701
- if (reconcileResult.outcome === "milestone-complete") {
702
- await deps.stopAuto(ctx, pi, "Workflow complete");
703
- deps.uokObserver?.onPhaseResult("custom-engine", "milestone-complete", {
704
- unitType: iterData.unitType,
705
- unitId: iterData.unitId,
706
- });
707
- finishTurn("completed");
708
- break;
709
- }
710
- if (reconcileResult.outcome === "pause") {
711
- await deps.pauseAuto(ctx, pi);
712
- deps.uokObserver?.onPhaseResult("custom-engine", "pause", {
713
- unitType: iterData.unitType,
714
- unitId: iterData.unitId,
715
- });
716
- finishTurn("paused", "manual-attention");
717
- break;
718
- }
719
- if (reconcileResult.outcome === "stop") {
720
- await deps.stopAuto(ctx, pi, reconcileResult.reason ?? "Engine stopped");
721
- deps.uokObserver?.onPhaseResult("custom-engine", "stop", {
722
- unitType: iterData.unitType,
723
- unitId: iterData.unitId,
724
- reason: reconcileResult.reason,
725
- });
726
- finishTurn("stopped", "manual-attention", reconcileResult.reason);
727
- break;
728
- }
729
- deps.uokObserver?.onPhaseResult("custom-engine", "continue", {
630
+ const reconcileFlow = await handleCustomEngineReconcileOutcome({
631
+ outcome: reconcileOutcome,
730
632
  unitType: iterData.unitType,
731
633
  unitId: iterData.unitId,
634
+ deps: {
635
+ stopAuto: reason => deps.stopAuto(ctx, pi, reason),
636
+ pauseAuto: () => deps.pauseAuto(ctx, pi),
637
+ report: (action, details) => phaseReporter.report("custom-engine", action, details),
638
+ finishTurn,
639
+ },
732
640
  });
733
- finishTurn("completed");
641
+ if (reconcileFlow.action === "break") break;
734
642
  continue;
735
643
  }
736
644
 
737
645
  if (!sidecarItem) {
738
646
  // ── Phase 1: Pre-dispatch ─────────────────────────────────────────
739
647
  const preDispatchResult = await runPreDispatch(ic, loopState);
740
- deps.uokObserver?.onPhaseResult("pre-dispatch", preDispatchResult.action);
648
+ phaseReporter.report("pre-dispatch", preDispatchResult.action);
741
649
  if (preDispatchResult.action === "break") {
742
650
  finishTurn("stopped", "manual-attention", "pre-dispatch-break");
743
651
  break;
@@ -751,7 +659,7 @@ export async function autoLoop(
751
659
 
752
660
  // ── Phase 2: Guards ───────────────────────────────────────────────
753
661
  const guardsResult = await runGuards(ic, preData.mid);
754
- deps.uokObserver?.onPhaseResult("guard", guardsResult.action);
662
+ phaseReporter.report("guard", guardsResult.action);
755
663
  if (guardsResult.action === "break") {
756
664
  finishTurn("stopped", "manual-attention", "guard-break");
757
665
  break;
@@ -759,7 +667,7 @@ export async function autoLoop(
759
667
 
760
668
  // ── Phase 3: Dispatch ─────────────────────────────────────────────
761
669
  const dispatchResult = await runDispatch(ic, preData, loopState);
762
- deps.uokObserver?.onPhaseResult("dispatch", dispatchResult.action);
670
+ phaseReporter.report("dispatch", dispatchResult.action);
763
671
  if (dispatchResult.action === "break") {
764
672
  finishTurn("stopped", "manual-attention", "dispatch-break");
765
673
  break;
@@ -772,30 +680,19 @@ export async function autoLoop(
772
680
  observedUnitType = iterData.unitType;
773
681
  observedUnitId = iterData.unitId;
774
682
  } else {
775
- // ── Sidecar path: use values from the sidecar item directly ──
776
- const sidecarState = await deps.deriveState(s.canonicalProjectRoot);
777
- debugLog("autoLoop", {
778
- phase: "post-derive",
779
- site: "sidecar",
683
+ iterData = await buildSidecarIterationData({
684
+ sidecarItem,
780
685
  basePath: s.basePath,
781
686
  canonicalProjectRoot: s.canonicalProjectRoot,
782
- derivedPhase: sidecarState.phase,
783
- activeUnit: sidecarState.activeTask?.id ?? sidecarState.activeSlice?.id ?? sidecarState.activeMilestone?.id,
687
+ deriveState: deps.deriveState,
688
+ logPostDerive: details => debugLog("autoLoop", {
689
+ phase: "post-derive",
690
+ ...details,
691
+ }),
784
692
  });
785
- iterData = {
786
- unitType: sidecarItem.unitType,
787
- unitId: sidecarItem.unitId,
788
- prompt: sidecarItem.prompt,
789
- finalPrompt: sidecarItem.prompt,
790
- pauseAfterUatDispatch: false,
791
- state: sidecarState,
792
- mid: sidecarState.activeMilestone?.id,
793
- midTitle: sidecarState.activeMilestone?.title,
794
- isRetry: false, previousTier: undefined,
795
- };
796
693
  observedUnitType = iterData.unitType;
797
694
  observedUnitId = iterData.unitId;
798
- deps.uokObserver?.onPhaseResult("dispatch", "sidecar", {
695
+ phaseReporter.report("dispatch", "sidecar", {
799
696
  unitType: iterData.unitType,
800
697
  unitId: iterData.unitId,
801
698
  sidecarKind: sidecarItem.kind,
@@ -810,12 +707,25 @@ export async function autoLoop(
810
707
  // null when DB unavailable, no worker registered, or no active lease
811
708
  // — those degraded paths fall through to the existing single-worker
812
709
  // semantics with no ledger entry, preserving back-compat.
813
- const dispatchClaim = openDispatchClaim(s, flowId, turnId, iterData);
814
- if (dispatchClaim.kind === "skip") {
815
- finishTurn("skipped", "execution", dispatchClaim.reason);
710
+ const dispatchClaim = openDispatchClaim(s, flowId, turnId, iterData, {
711
+ getRecentDispatchesForUnit,
712
+ recordDispatchClaim,
713
+ markDispatchRunning,
714
+ logClaimRejected: logDispatchClaimRejected,
715
+ logClaimFailed: logDispatchClaimFailed,
716
+ });
717
+ const dispatchDecision = decideDispatchClaim(
718
+ dispatchClaim.kind === "opened"
719
+ ? { kind: "opened", dispatchId: dispatchClaim.dispatchId }
720
+ : dispatchClaim.kind === "skip"
721
+ ? { kind: "skip", reason: dispatchClaim.reason }
722
+ : { kind: "degraded" },
723
+ );
724
+ if (dispatchDecision.action === "skip") {
725
+ finishTurn("skipped", "execution", dispatchDecision.reason);
816
726
  continue;
817
727
  }
818
- dispatchId = dispatchClaim.kind === "opened" ? dispatchClaim.dispatchId : null;
728
+ dispatchId = dispatchDecision.dispatchId;
819
729
 
820
730
  let unitPhaseResult: Awaited<ReturnType<typeof runUnitPhaseViaContract>>;
821
731
  try {
@@ -825,40 +735,36 @@ export async function autoLoop(
825
735
  iterData,
826
736
  loopState,
827
737
  sidecarItem,
738
+ unitDispatchDeps,
828
739
  );
829
740
  } catch (err) {
830
741
  if (err instanceof ModelPolicyDispatchBlockedError) {
831
742
  throw err;
832
743
  }
833
- if (dispatchId !== null) {
834
- try {
835
- markDispatchFailed(dispatchId, {
836
- errorSummary: `exception:${err instanceof Error ? err.message : String(err)}`,
837
- });
838
- dispatchSettled = true;
839
- } catch (ledgerErr) {
840
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: ledgerErr instanceof Error ? ledgerErr.message : String(ledgerErr) });
841
- }
842
- }
744
+ closeOutCrashedUnit(s, iterData, err);
745
+ dispatchSettled = settleDispatchFailed(
746
+ dispatchId,
747
+ formatDispatchExceptionSummary({ error: err }),
748
+ {
749
+ markFailed: markDispatchFailed,
750
+ logWriteFailure: logDispatchLedgerWriteFailure,
751
+ },
752
+ ) || dispatchSettled;
843
753
  throw err;
844
754
  }
845
755
  if (unitPhaseResult.action === "next") {
846
- const requestTimestamp = unitPhaseResult.data.requestDispatchedAt ?? unitPhaseResult.data.unitStartedAt;
847
- if (typeof requestTimestamp === "number") s.lastRequestTimestamp = requestTimestamp;
756
+ const requestTimestamp = resolveUnitRequestTimestamp(unitPhaseResult.data);
757
+ if (requestTimestamp !== undefined) s.lastRequestTimestamp = requestTimestamp;
848
758
  }
849
- deps.uokObserver?.onPhaseResult("unit", unitPhaseResult.action, {
759
+ phaseReporter.report("unit", unitPhaseResult.action, {
850
760
  unitType: iterData.unitType,
851
761
  unitId: iterData.unitId,
852
762
  });
853
763
  if (unitPhaseResult.action === "break") {
854
- if (dispatchId !== null) {
855
- try {
856
- markDispatchFailed(dispatchId, { errorSummary: "unit-break" });
857
- dispatchSettled = true;
858
- } catch (err) {
859
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: err instanceof Error ? err.message : String(err) });
860
- }
861
- }
764
+ dispatchSettled = settleDispatchFailed(dispatchId, "unit-break", {
765
+ markFailed: markDispatchFailed,
766
+ logWriteFailure: logDispatchLedgerWriteFailure,
767
+ }) || dispatchSettled;
862
768
  finishTurn("stopped", "execution", "unit-break");
863
769
  break;
864
770
  }
@@ -869,84 +775,68 @@ export async function autoLoop(
869
775
  try {
870
776
  finalizeResult = await runFinalize(ic, iterData, loopState, sidecarItem);
871
777
  } catch (err) {
872
- if (dispatchId !== null) {
873
- try {
874
- markDispatchFailed(dispatchId, {
875
- errorSummary: `exception:${err instanceof Error ? err.message : String(err)}`,
876
- });
877
- dispatchSettled = true;
878
- } catch (ledgerErr) {
879
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: ledgerErr instanceof Error ? ledgerErr.message : String(ledgerErr) });
880
- }
881
- }
778
+ dispatchSettled = settleDispatchFailed(
779
+ dispatchId,
780
+ formatDispatchExceptionSummary({ error: err }),
781
+ {
782
+ markFailed: markDispatchFailed,
783
+ logWriteFailure: logDispatchLedgerWriteFailure,
784
+ },
785
+ ) || dispatchSettled;
882
786
  throw err;
883
787
  }
884
- deps.uokObserver?.onPhaseResult("finalize", finalizeResult.action, {
788
+ phaseReporter.report("finalize", finalizeResult.action, {
885
789
  unitType: iterData.unitType,
886
790
  unitId: iterData.unitId,
887
791
  });
888
- if (finalizeResult.action === "break") {
889
- const finalizeFailureClass = finalizeResult.reason === "git-closeout-failure"
890
- ? "git"
891
- : "closeout";
892
- if (dispatchId !== null) {
893
- try {
894
- markDispatchFailed(dispatchId, { errorSummary: `finalize-break:${finalizeResult.reason ?? "unknown"}` });
895
- dispatchSettled = true;
896
- } catch (err) {
897
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: err instanceof Error ? err.message : String(err) });
898
- }
899
- }
900
- finishTurn("stopped", finalizeFailureClass, "finalize-break");
792
+ const finalizeDecision = decideFinalizeResult(
793
+ finalizeResult.action === "break"
794
+ ? { action: "break", reason: finalizeResult.reason }
795
+ : finalizeResult.action === "continue"
796
+ ? { action: "continue" }
797
+ : { action: "next" },
798
+ );
799
+ if (finalizeDecision.action === "stop") {
800
+ dispatchSettled = settleDispatchFailed(dispatchId, finalizeDecision.ledgerErrorSummary, {
801
+ markFailed: markDispatchFailed,
802
+ logWriteFailure: logDispatchLedgerWriteFailure,
803
+ }) || dispatchSettled;
804
+ finishTurn("stopped", finalizeDecision.failureClass, finalizeDecision.turnError);
901
805
  break;
902
806
  }
903
- if (finalizeResult.action === "continue") {
904
- if (dispatchId !== null) {
905
- try {
906
- markDispatchFailed(dispatchId, { errorSummary: "finalize-retry" });
907
- dispatchSettled = true;
908
- } catch (err) {
909
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: err instanceof Error ? err.message : String(err) });
910
- }
911
- }
807
+ if (finalizeDecision.action === "retry") {
808
+ dispatchSettled = settleDispatchFailed(dispatchId, finalizeDecision.ledgerErrorSummary, {
809
+ markFailed: markDispatchFailed,
810
+ logWriteFailure: logDispatchLedgerWriteFailure,
811
+ }) || dispatchSettled;
912
812
  finishTurn("retry");
913
813
  continue;
914
814
  }
915
815
 
916
- if (dispatchId !== null) {
917
- try {
918
- markDispatchCompleted(dispatchId);
919
- dispatchSettled = true;
920
- } catch (err) {
921
- debugLog("autoLoop", { phase: "dispatch-ledger-write-failed", error: err instanceof Error ? err.message : String(err) });
922
- }
923
- }
924
- consecutiveErrors = 0; // Iteration completed successfully
925
- consecutiveCooldowns = 0;
926
- recentErrorMessages.length = 0;
927
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
928
- saveStuckState(s, loopState); // persist across session restarts (#4382)
929
- debugLog("autoLoop", { phase: "iteration-complete", iteration });
816
+ dispatchSettled = settleDispatchCompleted(dispatchId, {
817
+ markCompleted: markDispatchCompleted,
818
+ logWriteFailure: logDispatchLedgerWriteFailure,
819
+ }) || dispatchSettled;
820
+ completeIteration();
930
821
  finishTurn("completed");
931
822
  } catch (loopErr) {
932
823
  // ── Blanket catch: absorb unexpected exceptions, apply graduated recovery ──
933
824
  const msg = loopErr instanceof Error ? loopErr.message : String(loopErr);
934
825
  if (dispatchId !== null && !dispatchSettled && !(loopErr instanceof ModelPolicyDispatchBlockedError)) {
935
- try {
936
- markDispatchFailed(dispatchId, { errorSummary: `unhandled-error:${msg.slice(0, 200)}` });
937
- dispatchSettled = true;
938
- } catch (err) {
939
- debugLog("autoLoop", {
940
- phase: "dispatch-ledger-write-failed",
941
- error: err instanceof Error ? err.message : String(err),
942
- });
943
- }
826
+ dispatchSettled = settleDispatchFailed(
827
+ dispatchId,
828
+ formatUnhandledDispatchErrorSummary({ error: loopErr }),
829
+ {
830
+ markFailed: markDispatchFailed,
831
+ logWriteFailure: logDispatchLedgerWriteFailure,
832
+ },
833
+ ) || dispatchSettled;
944
834
  }
945
835
 
946
836
  // Always emit iteration-end on error so the journal records iteration
947
837
  // completion even on failure (#2344). Without this, errors in
948
838
  // runFinalize leave the journal incomplete, making diagnosis harder.
949
- deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration, error: msg } });
839
+ journalReporter.emit("iteration-end", { iteration, error: msg });
950
840
 
951
841
  // ── Pre-send model-policy block: not a retryable error (#4959 / #4850) ──
952
842
  // The model-policy gate runs before the prompt is sent. When every
@@ -957,6 +847,12 @@ export async function autoLoop(
957
847
  // instead, with the per-model deny reasons surfaced from the typed
958
848
  // error.
959
849
  if (loopErr instanceof ModelPolicyDispatchBlockedError) {
850
+ const policyDecision = decideModelPolicyBlocked({
851
+ unitType: loopErr.unitType,
852
+ unitId: loopErr.unitId,
853
+ errorMessage: msg,
854
+ reasons: loopErr.reasons,
855
+ });
960
856
  debugLog("autoLoop", {
961
857
  phase: "model-policy-blocked",
962
858
  iteration,
@@ -964,23 +860,8 @@ export async function autoLoop(
964
860
  unitId: loopErr.unitId,
965
861
  reasons: loopErr.reasons,
966
862
  });
967
- ctx.ui.notify(
968
- `Auto-mode paused: model-policy denied dispatch for ${loopErr.unitType}/${loopErr.unitId}. ${msg}`,
969
- "error",
970
- );
971
- deps.emitJournalEvent({
972
- ts: new Date().toISOString(),
973
- flowId,
974
- seq: nextSeq(),
975
- eventType: "unit-end",
976
- data: {
977
- unitType: loopErr.unitType,
978
- unitId: loopErr.unitId,
979
- status: "blocked",
980
- reason: "model-policy-dispatch-blocked",
981
- reasons: loopErr.reasons,
982
- },
983
- });
863
+ ctx.ui.notify(policyDecision.notifyMessage, "error");
864
+ journalReporter.emit("unit-end", policyDecision.journalData);
984
865
  // Carry the blocked unit identity into the turn-result observer:
985
866
  // the throw originated inside dispatch, so observedUnitType/Id were
986
867
  // not assigned by the success path at lines 453/631/647 — but the
@@ -988,7 +869,7 @@ export async function autoLoop(
988
869
  observedUnitType = loopErr.unitType;
989
870
  observedUnitId = loopErr.unitId;
990
871
  await deps.pauseAuto(ctx, pi);
991
- finishTurn("paused", "manual-attention", msg);
872
+ finishTurn(policyDecision.turnStatus, policyDecision.failureClass, msg);
992
873
  // Do NOT increment consecutiveErrors — the failure is configuration,
993
874
  // not a transient runtime fault.
994
875
  break;
@@ -999,22 +880,19 @@ export async function autoLoop(
999
880
  // LLM budget on guaranteed failures.
1000
881
  const infraCode = isInfrastructureError(loopErr);
1001
882
  if (infraCode) {
883
+ const infraDecision = decideInfrastructureError({
884
+ code: infraCode,
885
+ errorMessage: msg,
886
+ });
1002
887
  debugLog("autoLoop", {
1003
888
  phase: "infrastructure-error",
1004
889
  iteration,
1005
890
  code: infraCode,
1006
891
  error: msg,
1007
892
  });
1008
- ctx.ui.notify(
1009
- `Auto-mode stopped: infrastructure error ${infraCode} — ${msg}`,
1010
- "error",
1011
- );
1012
- await deps.stopAuto(
1013
- ctx,
1014
- pi,
1015
- `Infrastructure error (${infraCode}): not recoverable by retry`,
1016
- );
1017
- finishTurn("failed", "execution", msg);
893
+ ctx.ui.notify(infraDecision.notifyMessage, "error");
894
+ await deps.stopAuto(ctx, pi, infraDecision.stopMessage);
895
+ finishTurn(infraDecision.turnStatus, infraDecision.failureClass, msg);
1018
896
  break;
1019
897
  }
1020
898
 
@@ -1027,6 +905,12 @@ export async function autoLoop(
1027
905
  if (isTransientCooldownError(loopErr)) {
1028
906
  consecutiveCooldowns++;
1029
907
  const retryAfterMs = getCooldownRetryAfterMs(loopErr);
908
+ const cooldownDecision = decideCooldownRecovery({
909
+ consecutiveCooldowns,
910
+ maxCooldownRetries: MAX_COOLDOWN_RETRIES,
911
+ retryAfterMs,
912
+ fallbackWaitMs: COOLDOWN_FALLBACK_WAIT_MS,
913
+ });
1030
914
  debugLog("autoLoop", {
1031
915
  phase: "cooldown-wait",
1032
916
  iteration,
@@ -1035,27 +919,15 @@ export async function autoLoop(
1035
919
  error: msg,
1036
920
  });
1037
921
 
1038
- if (consecutiveCooldowns > MAX_COOLDOWN_RETRIES) {
1039
- ctx.ui.notify(
1040
- `Auto-mode stopped: ${consecutiveCooldowns} consecutive credential cooldowns — rate limit or quota may be persistently exhausted.`,
1041
- "error",
1042
- );
1043
- await deps.stopAuto(
1044
- ctx,
1045
- pi,
1046
- `${consecutiveCooldowns} consecutive credential cooldowns exceeded retry budget`,
1047
- );
922
+ if (cooldownDecision.action === "stop") {
923
+ ctx.ui.notify(cooldownDecision.notifyMessage, "error");
924
+ finishTurn("stopped", "timeout", msg);
925
+ await deps.stopAuto(ctx, pi, cooldownDecision.stopMessage);
1048
926
  break;
1049
927
  }
1050
928
 
1051
- const waitMs = (retryAfterMs !== undefined && retryAfterMs > 0 && retryAfterMs <= 60_000)
1052
- ? retryAfterMs + 500 // Use structured hint + small buffer
1053
- : COOLDOWN_FALLBACK_WAIT_MS;
1054
- ctx.ui.notify(
1055
- `Credentials in cooldown (${consecutiveCooldowns}/${MAX_COOLDOWN_RETRIES}) — waiting ${Math.round(waitMs / 1000)}s before retrying.`,
1056
- "warning",
1057
- );
1058
- await new Promise(resolve => setTimeout(resolve, waitMs));
929
+ ctx.ui.notify(cooldownDecision.notifyMessage, "warning");
930
+ await new Promise(resolve => setTimeout(resolve, cooldownDecision.waitMs));
1059
931
  finishTurn("retry", "timeout", msg);
1060
932
  continue; // Retry iteration without incrementing consecutiveErrors
1061
933
  }
@@ -1069,34 +941,24 @@ export async function autoLoop(
1069
941
  error: msg,
1070
942
  });
1071
943
 
1072
- if (consecutiveErrors >= 3) {
1073
- // 3+ consecutive: hard stop — something is fundamentally broken
1074
- const errorHistory = recentErrorMessages
1075
- .map((m, i) => ` ${i + 1}. ${m}`)
1076
- .join("\n");
1077
- ctx.ui.notify(
1078
- `Auto-mode stopped: ${consecutiveErrors} consecutive iteration failures:\n${errorHistory}`,
1079
- "error",
1080
- );
1081
- await deps.stopAuto(
1082
- ctx,
1083
- pi,
1084
- `${consecutiveErrors} consecutive iteration failures`,
1085
- );
1086
- finishTurn("failed", "execution", msg);
944
+ const errorDecision = decideIterationErrorRecovery({
945
+ consecutiveErrors,
946
+ recentErrorMessages,
947
+ currentErrorMessage: msg,
948
+ });
949
+ if (errorDecision.action === "stop") {
950
+ ctx.ui.notify(errorDecision.notifyMessage, "error");
951
+ await deps.stopAuto(ctx, pi, errorDecision.stopMessage);
952
+ finishTurn(errorDecision.turnStatus, "execution", msg);
1087
953
  break;
1088
- } else if (consecutiveErrors === 2) {
1089
- // 2nd consecutive: try invalidating caches + re-deriving state
1090
- ctx.ui.notify(
1091
- `Iteration error (attempt ${consecutiveErrors}): ${msg}. Invalidating caches and retrying.`,
1092
- "warning",
1093
- );
954
+ }
955
+ if (errorDecision.action === "invalidate-and-retry") {
956
+ ctx.ui.notify(errorDecision.notifyMessage, "warning");
1094
957
  deps.invalidateAllCaches();
1095
958
  } else {
1096
- // 1st error: log and retry — transient failures happen
1097
- ctx.ui.notify(`Iteration error: ${msg}. Retrying.`, "warning");
959
+ ctx.ui.notify(errorDecision.notifyMessage, "warning");
1098
960
  }
1099
- finishTurn("retry", "execution", msg);
961
+ finishTurn(errorDecision.turnStatus, "execution", msg);
1100
962
  }
1101
963
  }
1102
964