gsd-pi 2.78.0-dev.aeeb2ca00 → 2.78.1-dev.0fdacd524

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 (471) hide show
  1. package/README.md +8 -7
  2. package/dist/bundled-resource-path.d.ts +7 -0
  3. package/dist/bundled-resource-path.js +34 -2
  4. package/dist/claude-cli-check.js +77 -38
  5. package/dist/cli-policy.d.ts +13 -0
  6. package/dist/cli-policy.js +17 -0
  7. package/dist/cli.js +95 -55
  8. package/dist/headless-query.d.ts +22 -0
  9. package/dist/headless-query.js +43 -8
  10. package/dist/headless.d.ts +10 -0
  11. package/dist/headless.js +16 -1
  12. package/dist/loader.js +9 -13
  13. package/dist/onboarding.d.ts +10 -0
  14. package/dist/onboarding.js +2 -2
  15. package/dist/provider-migrations.d.ts +2 -2
  16. package/dist/provider-migrations.js +5 -2
  17. package/dist/resource-loader.d.ts +5 -2
  18. package/dist/resource-loader.js +30 -13
  19. package/dist/resources/.managed-resources-content-hash +1 -0
  20. package/dist/resources/extensions/claude-code-cli/readiness.js +90 -46
  21. package/dist/resources/extensions/google-search/index.js +2 -6
  22. package/dist/resources/extensions/gsd/auto/loop.js +23 -0
  23. package/dist/resources/extensions/gsd/auto/phases.js +5 -13
  24. package/dist/resources/extensions/gsd/auto/run-unit.js +26 -12
  25. package/dist/resources/extensions/gsd/auto/session.js +5 -6
  26. package/dist/resources/extensions/gsd/auto-dashboard.js +3 -2
  27. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +55 -21
  28. package/dist/resources/extensions/gsd/auto-dispatch.js +18 -6
  29. package/dist/resources/extensions/gsd/auto-prompts.js +69 -2
  30. package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
  31. package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
  32. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  33. package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
  34. package/dist/resources/extensions/gsd/auto-worktree.js +60 -13
  35. package/dist/resources/extensions/gsd/auto.js +39 -14
  36. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
  37. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
  38. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  39. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
  40. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +112 -31
  41. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
  42. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +22 -0
  43. package/dist/resources/extensions/gsd/bootstrap/system-context.js +45 -8
  44. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +121 -3
  45. package/dist/resources/extensions/gsd/commands/catalog.js +76 -5
  46. package/dist/resources/extensions/gsd/commands/handlers/core.js +23 -1
  47. package/dist/resources/extensions/gsd/commands/handlers/ops.js +8 -0
  48. package/dist/resources/extensions/gsd/commands-config.js +3 -2
  49. package/dist/resources/extensions/gsd/commands-extensions.js +46 -3
  50. package/dist/resources/extensions/gsd/commands-handlers.js +3 -2
  51. package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
  52. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
  53. package/dist/resources/extensions/gsd/commands-worktree.js +309 -0
  54. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  55. package/dist/resources/extensions/gsd/docs/preferences-reference.md +10 -0
  56. package/dist/resources/extensions/gsd/doctor-providers.js +2 -1
  57. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
  58. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  59. package/dist/resources/extensions/gsd/forensics.js +10 -8
  60. package/dist/resources/extensions/gsd/git-service.js +12 -5
  61. package/dist/resources/extensions/gsd/gsd-db.js +11 -2
  62. package/dist/resources/extensions/gsd/guided-flow.js +25 -24
  63. package/dist/resources/extensions/gsd/home-dir.js +16 -0
  64. package/dist/resources/extensions/gsd/key-manager.js +2 -1
  65. package/dist/resources/extensions/gsd/memory-store.js +66 -31
  66. package/dist/resources/extensions/gsd/migrate/command.js +3 -2
  67. package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
  68. package/dist/resources/extensions/gsd/model-router.js +114 -9
  69. package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
  70. package/dist/resources/extensions/gsd/preferences-models.js +91 -15
  71. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  72. package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
  73. package/dist/resources/extensions/gsd/preferences.js +5 -3
  74. package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
  75. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
  76. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -0
  77. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  78. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +2 -0
  79. package/dist/resources/extensions/gsd/prompts/plan-slice.md +10 -0
  80. package/dist/resources/extensions/gsd/prompts/refine-slice.md +10 -0
  81. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +2 -0
  82. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
  83. package/dist/resources/extensions/gsd/state.js +42 -0
  84. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  85. package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
  86. package/dist/resources/extensions/gsd/unit-context-manifest.js +29 -4
  87. package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
  88. package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
  89. package/dist/resources/extensions/gsd/worktree-command.js +26 -46
  90. package/dist/resources/extensions/gsd/worktree-manager.js +20 -1
  91. package/dist/resources/extensions/gsd/worktree-resolver.js +4 -13
  92. package/dist/resources/extensions/gsd/worktree-root.js +124 -0
  93. package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
  94. package/dist/resources/extensions/gsd/worktree.js +4 -115
  95. package/dist/resources/extensions/mcp-client/index.js +6 -9
  96. package/dist/resources/extensions/ollama/index.js +15 -2
  97. package/dist/resources/extensions/ollama/model-capabilities.js +31 -0
  98. package/dist/resources/extensions/ollama/ollama-client.js +40 -4
  99. package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
  100. package/dist/resources/extensions/subagent/index.js +324 -178
  101. package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
  102. package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  103. package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  104. package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  105. package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  106. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  107. package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  108. package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  109. package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  110. package/dist/resources/skills/lint/SKILL.md +4 -0
  111. package/dist/resources/skills/review/SKILL.md +4 -0
  112. package/dist/resources/skills/test/SKILL.md +3 -0
  113. package/dist/rtk-shared.d.ts +3 -0
  114. package/dist/rtk-shared.js +17 -0
  115. package/dist/rtk.d.ts +2 -5
  116. package/dist/rtk.js +3 -20
  117. package/dist/runtime-checks.d.ts +27 -0
  118. package/dist/runtime-checks.js +38 -0
  119. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  120. package/dist/web/standalone/.next/BUILD_ID +1 -1
  121. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  122. package/dist/web/standalone/.next/build-manifest.json +3 -3
  123. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  124. package/dist/web/standalone/.next/react-loadable-manifest.json +44 -4
  125. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  127. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  136. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/events/route.js +4 -2
  146. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/index.html +1 -1
  148. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  149. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  150. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  151. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  153. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  156. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  158. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  160. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  161. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  162. package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
  163. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +1 -0
  164. package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
  165. package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
  166. package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
  167. package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
  168. package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
  169. package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
  170. package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
  171. package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
  172. package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
  173. package/dist/web/standalone/.next/static/chunks/app/{page-5b113fd32bc2a1c3.js → page-9bf2e0c50fb2ca05.js} +1 -1
  174. package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +1 -0
  175. package/dist/web/standalone/package.json +2 -1
  176. package/dist/welcome-screen.js +27 -1
  177. package/dist/worktree-cli.d.ts +1 -0
  178. package/dist/worktree-cli.js +9 -3
  179. package/dist/worktree-status-banner.d.ts +1 -0
  180. package/dist/worktree-status-banner.js +132 -0
  181. package/package.json +1 -3
  182. package/packages/daemon/package.json +2 -2
  183. package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
  184. package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
  185. package/packages/mcp-server/dist/alias-telemetry.js +30 -0
  186. package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
  187. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  188. package/packages/mcp-server/dist/workflow-tools.js +74 -46
  189. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  190. package/packages/mcp-server/package.json +2 -2
  191. package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
  192. package/packages/mcp-server/src/alias-telemetry.ts +30 -0
  193. package/packages/mcp-server/src/workflow-tools.test.ts +78 -0
  194. package/packages/mcp-server/src/workflow-tools.ts +93 -58
  195. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  196. package/packages/native/package.json +1 -1
  197. package/packages/native/tsconfig.tsbuildinfo +1 -1
  198. package/packages/pi-agent-core/package.json +1 -1
  199. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  200. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
  201. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
  202. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
  203. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
  204. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  205. package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
  206. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  207. package/packages/pi-ai/dist/types.d.ts +13 -0
  208. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  209. package/packages/pi-ai/dist/types.js.map +1 -1
  210. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  211. package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
  212. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  213. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
  214. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  215. package/packages/pi-ai/package.json +1 -1
  216. package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
  217. package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
  218. package/packages/pi-ai/src/types.ts +13 -0
  219. package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
  220. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
  221. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  222. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
  224. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/core/messages.js +4 -0
  227. package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
  229. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -0
  231. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  232. package/packages/pi-coding-agent/dist/core/model-registry.js +18 -0
  233. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  234. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
  235. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
  237. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  238. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
  239. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
  240. package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
  241. package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
  242. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
  243. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
  244. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
  245. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
  246. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
  247. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  248. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
  249. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  250. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
  251. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
  252. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
  253. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
  254. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
  255. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
  256. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
  257. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
  258. package/packages/pi-coding-agent/package.json +1 -1
  259. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
  260. package/packages/pi-coding-agent/src/core/messages.ts +4 -0
  261. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
  262. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
  263. package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
  264. package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
  265. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
  266. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
  267. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
  268. package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
  269. package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
  270. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  271. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
  272. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  273. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
  274. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
  275. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
  276. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
  277. package/packages/pi-tui/package.json +1 -1
  278. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
  279. package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
  280. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  281. package/packages/rpc-client/package.json +1 -1
  282. package/pkg/package.json +1 -1
  283. package/src/resources/extensions/claude-code-cli/readiness.ts +92 -47
  284. package/src/resources/extensions/google-search/index.ts +2 -9
  285. package/src/resources/extensions/gsd/auto/loop.ts +24 -2
  286. package/src/resources/extensions/gsd/auto/phases.ts +6 -14
  287. package/src/resources/extensions/gsd/auto/run-unit.ts +26 -12
  288. package/src/resources/extensions/gsd/auto/session.ts +5 -6
  289. package/src/resources/extensions/gsd/auto/types.ts +1 -0
  290. package/src/resources/extensions/gsd/auto-dashboard.ts +3 -2
  291. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +60 -24
  292. package/src/resources/extensions/gsd/auto-dispatch.ts +18 -6
  293. package/src/resources/extensions/gsd/auto-prompts.ts +66 -2
  294. package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
  295. package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
  296. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  297. package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
  298. package/src/resources/extensions/gsd/auto-worktree.ts +82 -12
  299. package/src/resources/extensions/gsd/auto.ts +37 -10
  300. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
  301. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
  302. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  303. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
  304. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +121 -31
  305. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
  306. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +20 -0
  307. package/src/resources/extensions/gsd/bootstrap/system-context.ts +50 -8
  308. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +141 -11
  309. package/src/resources/extensions/gsd/commands/catalog.ts +82 -5
  310. package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -1
  311. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  312. package/src/resources/extensions/gsd/commands-config.ts +3 -2
  313. package/src/resources/extensions/gsd/commands-extensions.ts +43 -3
  314. package/src/resources/extensions/gsd/commands-handlers.ts +3 -2
  315. package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
  316. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
  317. package/src/resources/extensions/gsd/commands-worktree.ts +383 -0
  318. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  319. package/src/resources/extensions/gsd/docs/preferences-reference.md +10 -0
  320. package/src/resources/extensions/gsd/doctor-providers.ts +2 -1
  321. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
  322. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  323. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  324. package/src/resources/extensions/gsd/forensics.ts +12 -7
  325. package/src/resources/extensions/gsd/git-service.ts +13 -5
  326. package/src/resources/extensions/gsd/gsd-db.ts +12 -2
  327. package/src/resources/extensions/gsd/guided-flow.ts +27 -26
  328. package/src/resources/extensions/gsd/home-dir.ts +19 -0
  329. package/src/resources/extensions/gsd/journal.ts +4 -1
  330. package/src/resources/extensions/gsd/key-manager.ts +2 -1
  331. package/src/resources/extensions/gsd/memory-store.ts +81 -28
  332. package/src/resources/extensions/gsd/migrate/command.ts +3 -2
  333. package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
  334. package/src/resources/extensions/gsd/model-router.ts +172 -9
  335. package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
  336. package/src/resources/extensions/gsd/preferences-models.ts +101 -15
  337. package/src/resources/extensions/gsd/preferences-types.ts +6 -0
  338. package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
  339. package/src/resources/extensions/gsd/preferences.ts +16 -2
  340. package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
  341. package/src/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
  342. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -0
  343. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  344. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +2 -0
  345. package/src/resources/extensions/gsd/prompts/plan-slice.md +10 -0
  346. package/src/resources/extensions/gsd/prompts/refine-slice.md +10 -0
  347. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +2 -0
  348. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
  349. package/src/resources/extensions/gsd/state.ts +42 -0
  350. package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  351. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +179 -1
  352. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
  353. package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +24 -5
  354. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
  355. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
  356. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
  357. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +50 -27
  358. package/src/resources/extensions/gsd/tests/commands-extensions-version-compare.test.ts +58 -0
  359. package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +48 -0
  360. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
  361. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
  362. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
  363. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
  364. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
  365. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
  366. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
  367. package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
  368. package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
  369. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
  370. package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
  371. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
  372. package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
  373. package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
  374. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
  375. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
  376. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
  377. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
  378. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +25 -65
  379. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
  380. package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
  381. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
  382. package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
  383. package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
  384. package/src/resources/extensions/gsd/tests/home-dir.test.ts +52 -0
  385. package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
  386. package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
  387. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +72 -1
  388. package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
  389. package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
  390. package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
  391. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
  392. package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
  393. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +18 -1
  394. package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
  395. package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
  396. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
  397. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
  398. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
  399. package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
  400. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
  401. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  402. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
  403. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
  404. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +34 -0
  405. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
  406. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
  407. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
  408. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +17 -1
  409. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
  410. package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
  411. package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
  412. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +38 -3
  413. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
  414. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
  415. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
  416. package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +235 -0
  417. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +34 -33
  418. package/src/resources/extensions/gsd/tests/worktree.test.ts +8 -0
  419. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +131 -1
  420. package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
  421. package/src/resources/extensions/gsd/unit-context-manifest.ts +44 -12
  422. package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
  423. package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
  424. package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
  425. package/src/resources/extensions/gsd/worktree-command.ts +31 -44
  426. package/src/resources/extensions/gsd/worktree-manager.ts +40 -1
  427. package/src/resources/extensions/gsd/worktree-resolver.ts +4 -14
  428. package/src/resources/extensions/gsd/worktree-root.ts +144 -0
  429. package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
  430. package/src/resources/extensions/gsd/worktree.ts +8 -119
  431. package/src/resources/extensions/mcp-client/index.ts +6 -10
  432. package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
  433. package/src/resources/extensions/ollama/index.ts +16 -2
  434. package/src/resources/extensions/ollama/model-capabilities.ts +34 -0
  435. package/src/resources/extensions/ollama/ollama-client.ts +41 -4
  436. package/src/resources/extensions/ollama/tests/model-capabilities.test.ts +96 -0
  437. package/src/resources/extensions/ollama/tests/ollama-client-timeout-env.test.ts +147 -0
  438. package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
  439. package/src/resources/extensions/subagent/index.ts +165 -7
  440. package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
  441. package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  442. package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  443. package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  444. package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  445. package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  446. package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  447. package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  448. package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
  449. package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
  450. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
  451. package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  452. package/src/resources/skills/lint/SKILL.md +4 -0
  453. package/src/resources/skills/review/SKILL.md +4 -0
  454. package/src/resources/skills/test/SKILL.md +3 -0
  455. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
  456. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
  457. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
  458. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
  459. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
  460. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
  461. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
  462. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  463. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
  464. package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
  465. package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
  466. package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
  467. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
  468. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
  469. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
  470. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → 4iu6IYeYfxOq8OidlDqp6}/_buildManifest.js +0 -0
  471. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → 4iu6IYeYfxOq8OidlDqp6}/_ssgManifest.js +0 -0
@@ -32,6 +32,16 @@ export interface HeadlessOptions {
32
32
  resumeSession?: string;
33
33
  bare?: boolean;
34
34
  }
35
+ /**
36
+ * Commands classified as multi-turn in headless mode: they involve multiple
37
+ * question rounds, codebase scanning, and artifact writing before the workflow
38
+ * completes (#3547). Multi-turn commands suppress single-execution-complete
39
+ * exit and disable the default 5-minute timeout.
40
+ *
41
+ * Exported so the regression test can exercise the real classifier rather
42
+ * than grepping the source for identifier names.
43
+ */
44
+ export declare function isMultiTurnHeadlessCommand(command: string): boolean;
35
45
  export interface ResumeSessionResult {
36
46
  session?: SessionInfo;
37
47
  error?: string;
package/dist/headless.js CHANGED
@@ -21,6 +21,21 @@ import { isTerminalNotification, isBlockedNotification, isMilestoneReadyNotifica
21
21
  import { VALID_OUTPUT_FORMATS } from './headless-types.js';
22
22
  import { handleExtensionUIRequest, formatProgress, formatThinkingLine, formatTextStart, formatTextEnd, formatThinkingStart, formatThinkingEnd, startSupervisedStdinReader, } from './headless-ui.js';
23
23
  import { loadContext, bootstrapGsdProject, } from './headless-context.js';
24
+ /**
25
+ * Commands classified as multi-turn in headless mode: they involve multiple
26
+ * question rounds, codebase scanning, and artifact writing before the workflow
27
+ * completes (#3547). Multi-turn commands suppress single-execution-complete
28
+ * exit and disable the default 5-minute timeout.
29
+ *
30
+ * Exported so the regression test can exercise the real classifier rather
31
+ * than grepping the source for identifier names.
32
+ */
33
+ export function isMultiTurnHeadlessCommand(command) {
34
+ return (command === 'auto' ||
35
+ command === 'next' ||
36
+ command === 'discuss' ||
37
+ command === 'plan');
38
+ }
24
39
  /**
25
40
  * Resolve a session prefix to a single session.
26
41
  * Exact id match is preferred over prefix match.
@@ -186,7 +201,7 @@ async function runHeadlessOnce(options, restartCount) {
186
201
  const isAutoMode = options.command === 'auto';
187
202
  // discuss and plan are multi-turn: they involve multiple question rounds,
188
203
  // codebase scanning, and artifact writing before the workflow completes (#3547).
189
- const isMultiTurnCommand = options.command === 'auto' || options.command === 'next' || options.command === 'discuss' || options.command === 'plan';
204
+ const isMultiTurnCommand = isMultiTurnHeadlessCommand(options.command);
190
205
  if (isAutoMode && options.timeout === 300_000) {
191
206
  options.timeout = 0;
192
207
  }
package/dist/loader.js CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  // GSD Startup Loader
3
- // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
4
3
  import { fileURLToPath } from 'url';
5
4
  import { dirname, resolve, join, relative, delimiter } from 'path';
6
5
  import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, symlinkSync, cpSync } from 'fs';
@@ -32,14 +31,14 @@ if (firstArg === '--help' || firstArg === '-h') {
32
31
  // package.json (already parsed above) and verifies git is available.
33
32
  // ---------------------------------------------------------------------------
34
33
  {
35
- const MIN_NODE_MAJOR = 22;
34
+ const { MIN_NODE_MAJOR, checkNodeVersion, requireGit } = await import('./runtime-checks.js');
36
35
  const red = '\x1b[31m';
37
36
  const bold = '\x1b[1m';
38
37
  const dim = '\x1b[2m';
39
38
  const reset = '\x1b[0m';
40
39
  // -- Node version --
41
- const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
42
- if (nodeMajor < MIN_NODE_MAJOR) {
40
+ const nodeCheck = checkNodeVersion(process.versions.node, MIN_NODE_MAJOR);
41
+ if (!nodeCheck.ok) {
43
42
  process.stderr.write(`\n${red}${bold}Error:${reset} GSD requires Node.js >= ${MIN_NODE_MAJOR}.0.0\n` +
44
43
  ` You are running Node.js ${process.versions.node}\n\n` +
45
44
  `${dim}Install a supported version:${reset}\n` +
@@ -49,11 +48,9 @@ if (firstArg === '--help' || firstArg === '-h') {
49
48
  process.exit(1);
50
49
  }
51
50
  // -- git --
52
- try {
53
- const { execFileSync } = await import('child_process');
54
- execFileSync('git', ['--version'], { stdio: 'ignore' });
55
- }
56
- catch {
51
+ const { execFileSync } = await import('child_process');
52
+ const gitOk = requireGit((cmd, args) => execFileSync(cmd, args, { stdio: 'ignore' }));
53
+ if (!gitOk) {
57
54
  process.stderr.write(`\n${red}${bold}Error:${reset} GSD requires git but it was not found on PATH.\n\n` +
58
55
  `${dim}Install git:${reset}\n` +
59
56
  ` https://git-scm.com/downloads\n\n`);
@@ -61,8 +58,9 @@ if (firstArg === '--help' || firstArg === '-h') {
61
58
  }
62
59
  }
63
60
  import { agentDir, appRoot } from './app-paths.js';
64
- import { applyRtkProcessEnv } from './rtk.js';
61
+ import { applyRtkProcessEnv } from './rtk-shared.js';
65
62
  import { serializeBundledExtensionPaths } from './bundled-extension-paths.js';
63
+ import { resolveBundledResourcesDirFromPackageRoot } from './bundled-resource-path.js';
66
64
  import { discoverExtensionEntryPaths } from './extension-discovery.js';
67
65
  import { loadRegistry, readManifestFromEntryPath, isExtensionEnabled } from './extension-registry.js';
68
66
  import { renderLogo } from './logo.js';
@@ -121,9 +119,7 @@ process.env.GSD_BIN_PATH = process.argv[1];
121
119
  // GSD_WORKFLOW_PATH — absolute path to bundled GSD-WORKFLOW.md, used by patched gsd extension
122
120
  // when dispatching workflow prompts. Prefers dist/resources/ (stable, set at build time)
123
121
  // over src/resources/ (live working tree) — see resource-loader.ts for rationale.
124
- const distRes = join(gsdRoot, 'dist', 'resources');
125
- const srcRes = join(gsdRoot, 'src', 'resources');
126
- const resourcesDir = existsSync(distRes) ? distRes : srcRes;
122
+ const resourcesDir = resolveBundledResourcesDirFromPackageRoot(gsdRoot);
127
123
  process.env.GSD_WORKFLOW_PATH = join(resourcesDir, 'GSD-WORKFLOW.md');
128
124
  // GSD_BUNDLED_EXTENSION_PATHS — dynamically discovered bundled extension entry points.
129
125
  // Uses the shared discoverExtensionEntryPaths() to scan the bundled resources
@@ -24,6 +24,16 @@ interface RunOnboardingOptions {
24
24
  /** Show logo + intro banner. Disable when onboarding is launched inside an active TUI session. */
25
25
  showIntro?: boolean;
26
26
  }
27
+ export declare const OTHER_PROVIDERS: ({
28
+ value: string;
29
+ label: string;
30
+ hint: string;
31
+ } | {
32
+ value: string;
33
+ label: string;
34
+ hint?: undefined;
35
+ })[];
36
+ export declare function detectNativeProviderFromBaseUrl(baseUrl: string): 'minimax' | 'minimax-cn' | null;
27
37
  /**
28
38
  * Determine if the onboarding wizard should run.
29
39
  *
@@ -54,7 +54,7 @@ const API_KEY_PREFIXES = {
54
54
  anthropic: ['sk-ant-'],
55
55
  openai: ['sk-'],
56
56
  };
57
- const OTHER_PROVIDERS = [
57
+ export const OTHER_PROVIDERS = [
58
58
  { value: 'google', label: 'Google (Gemini)', hint: 'aistudio.google.com/app/apikey' },
59
59
  { value: 'groq', label: 'Groq', hint: 'console.groq.com/keys' },
60
60
  { value: 'xai', label: 'xAI (Grok)', hint: 'console.x.ai' },
@@ -148,7 +148,7 @@ function persistDefaultModel(modelId) {
148
148
  // Non-fatal: startup fallback logic will still run.
149
149
  }
150
150
  }
151
- function detectNativeProviderFromBaseUrl(baseUrl) {
151
+ export function detectNativeProviderFromBaseUrl(baseUrl) {
152
152
  try {
153
153
  const hostname = new URL(baseUrl).hostname.toLowerCase();
154
154
  if (hostname === 'api.minimax.io' || hostname.endsWith('.minimax.io')) {
@@ -1,7 +1,7 @@
1
1
  import type { AuthStorage } from "@gsd/pi-coding-agent";
2
2
  type AnthropicMigrationDeps = {
3
3
  authStorage: Pick<AuthStorage, "getCredentialsForProvider">;
4
- isClaudeCodeReady: boolean;
4
+ isClaudeCodeReady: boolean | (() => boolean);
5
5
  defaultProvider: string | undefined;
6
6
  env?: NodeJS.ProcessEnv;
7
7
  };
@@ -11,7 +11,7 @@ type MigrationModel = {
11
11
  };
12
12
  type AnthropicDefaultMigrationDeps = {
13
13
  authStorage: Pick<AuthStorage, "getCredentialsForProvider">;
14
- isClaudeCodeReady: boolean;
14
+ isClaudeCodeReady: boolean | (() => boolean);
15
15
  settingsManager: {
16
16
  getDefaultProvider(): string | undefined;
17
17
  getDefaultModel(): string | undefined;
@@ -5,10 +5,13 @@ export function hasDirectAnthropicApiKey(authStorage, env = process.env) {
5
5
  return authStorage.getCredentialsForProvider("anthropic").some((credential) => credential?.type === "api_key" && typeof credential?.key === "string" && credential.key.trim().length > 0);
6
6
  }
7
7
  export function shouldMigrateAnthropicToClaudeCode({ authStorage, isClaudeCodeReady, defaultProvider, env = process.env, }) {
8
- if (!isClaudeCodeReady || defaultProvider !== "anthropic") {
8
+ if (defaultProvider !== "anthropic") {
9
9
  return false;
10
10
  }
11
- return !hasDirectAnthropicApiKey(authStorage, env);
11
+ if (hasDirectAnthropicApiKey(authStorage, env)) {
12
+ return false;
13
+ }
14
+ return typeof isClaudeCodeReady === "function" ? isClaudeCodeReady() : isClaudeCodeReady;
12
15
  }
13
16
  export function migrateAnthropicDefaultToClaudeCode({ authStorage, isClaudeCodeReady, settingsManager, modelRegistry, env = process.env, }) {
14
17
  const defaultProvider = settingsManager.getDefaultProvider();
@@ -1,4 +1,4 @@
1
- import { DefaultResourceLoader } from '@gsd/pi-coding-agent';
1
+ import type { DefaultResourceLoader as DefaultResourceLoaderType } from '@gsd/pi-coding-agent';
2
2
  export { discoverExtensionEntryPaths } from './extension-discovery.js';
3
3
  export declare function getExtensionKey(entryPath: string, extensionsDir: string): string;
4
4
  export declare function readManagedResourceVersion(agentDir: string): string | null;
@@ -63,4 +63,7 @@ export declare function mergedFingerprint(hoisted: string, internal: string): st
63
63
  */
64
64
  export declare function initResources(agentDir: string, skillsDir?: string): void;
65
65
  export declare function hasStaleCompiledExtensionSiblings(extensionsDir: string, sourceDir?: string): boolean;
66
- export declare function buildResourceLoader(agentDir: string): DefaultResourceLoader;
66
+ interface BuildResourceLoaderOptions {
67
+ additionalExtensionPaths?: string[];
68
+ }
69
+ export declare function buildResourceLoader(agentDir: string, options?: BuildResourceLoaderOptions): Promise<DefaultResourceLoaderType>;
@@ -1,4 +1,3 @@
1
- import { DefaultResourceLoader, sortExtensionPaths } from '@gsd/pi-coding-agent';
2
1
  import { createHash } from 'node:crypto';
3
2
  import { homedir } from 'node:os';
4
3
  import { chmodSync, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, openSync, closeSync, readFileSync, readlinkSync, readdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
@@ -7,6 +6,11 @@ import { fileURLToPath } from 'node:url';
7
6
  import { compareSemver } from './update-check.js';
8
7
  import { discoverExtensionEntryPaths } from './extension-discovery.js';
9
8
  import { loadRegistry, readManifestFromEntryPath, isExtensionEnabled, ensureRegistryEntries } from './extension-registry.js';
9
+ import { resolveBundledResourcesDirFromPackageRoot } from './bundled-resource-path.js';
10
+ let piCodingAgentModulePromise;
11
+ function loadPiCodingAgentModule() {
12
+ return (piCodingAgentModulePromise ??= import('@gsd/pi-coding-agent'));
13
+ }
10
14
  // Resolve resources directory — prefer dist/resources/ (stable, set at build time)
11
15
  // over src/resources/ (live working tree, changes with git branch).
12
16
  //
@@ -16,16 +20,10 @@ import { loadRegistry, readManifestFromEntryPath, isExtensionEnabled, ensureRegi
16
20
  // dist/resources/ is populated by the build step (`npm run copy-resources`) and
17
21
  // reflects the built state, not the currently checked-out branch.
18
22
  const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
19
- const distResources = join(packageRoot, 'dist', 'resources');
20
- const srcResources = join(packageRoot, 'src', 'resources');
21
- // Use dist/resources only if it has the full expected structure.
22
- // A partial build (tsc without copy-resources) creates dist/resources/extensions/
23
- // but not agents/ or skills/, causing initResources to sync from an incomplete source.
24
- const resourcesDir = (existsSync(distResources) && existsSync(join(distResources, 'agents')))
25
- ? distResources
26
- : srcResources;
23
+ const resourcesDir = resolveBundledResourcesDirFromPackageRoot(packageRoot);
27
24
  const bundledExtensionsDir = join(resourcesDir, 'extensions');
28
25
  const resourceVersionManifestName = 'managed-resources.json';
26
+ const resourceFingerprintFileName = '.managed-resources-content-hash';
29
27
  export { discoverExtensionEntryPaths } from './extension-discovery.js';
30
28
  export function getExtensionKey(entryPath, extensionsDir) {
31
29
  const relPath = relative(extensionsDir, entryPath);
@@ -77,7 +75,7 @@ function writeManagedResourceManifest(agentDir) {
77
75
  const manifest = {
78
76
  gsdVersion: getBundledGsdVersion(),
79
77
  syncedAt: Date.now(),
80
- contentHash: computeResourceFingerprint(),
78
+ contentHash: getCurrentResourceFingerprint(),
81
79
  installedExtensionRootFiles,
82
80
  installedExtensionDirs,
83
81
  };
@@ -125,10 +123,24 @@ export function computeResourceFingerprint(rootDir = resourcesDir) {
125
123
  entries.sort();
126
124
  return createHash('sha256').update(entries.join('\n')).digest('hex').slice(0, 16);
127
125
  }
126
+ function getCurrentResourceFingerprint() {
127
+ try {
128
+ const precomputed = readFileSync(join(resourcesDir, resourceFingerprintFileName), 'utf-8').trim();
129
+ if (/^[a-f0-9]{16}$/i.test(precomputed)) {
130
+ return precomputed;
131
+ }
132
+ }
133
+ catch {
134
+ // Source-tree and partial-build workflows may not have a precomputed hash.
135
+ }
136
+ return computeResourceFingerprint();
137
+ }
128
138
  function collectFileEntries(dir, root, out) {
129
139
  if (!existsSync(dir))
130
140
  return;
131
141
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
142
+ if (entry.name === resourceFingerprintFileName)
143
+ continue;
132
144
  const fullPath = join(dir, entry.name);
133
145
  if (entry.isDirectory()) {
134
146
  collectFileEntries(fullPath, root, out);
@@ -548,7 +560,7 @@ export function initResources(agentDir, skillsDir = join(homedir(), '.agents', '
548
560
  // hotfixes within a release). The content hash catches those at ~1ms cost.
549
561
  if (manifest && manifest.gsdVersion === currentVersion) {
550
562
  // Version matches — check content fingerprint for same-version staleness.
551
- const currentHash = computeResourceFingerprint();
563
+ const currentHash = getCurrentResourceFingerprint();
552
564
  const hasStaleExtensionFiles = hasStaleCompiledExtensionSiblings(extensionsDir, bundledExtensionsDir);
553
565
  if (manifest.contentHash && manifest.contentHash === currentHash && !hasStaleExtensionFiles) {
554
566
  return;
@@ -736,7 +748,8 @@ function getBundledExtensionKeys() {
736
748
  }
737
749
  return _bundledExtensionKeys;
738
750
  }
739
- export function buildResourceLoader(agentDir) {
751
+ export async function buildResourceLoader(agentDir, options = {}) {
752
+ const { DefaultResourceLoader, sortExtensionPaths } = await loadPiCodingAgentModule();
740
753
  const registry = loadRegistry();
741
754
  const piAgentDir = join(homedir(), '.pi', 'agent');
742
755
  const piExtensionsDir = join(piAgentDir, 'extensions');
@@ -749,9 +762,13 @@ export function buildResourceLoader(agentDir) {
749
762
  return true;
750
763
  return isExtensionEnabled(registry, manifest.id);
751
764
  });
765
+ const additionalExtensionPaths = [
766
+ ...piExtensionPaths,
767
+ ...(options.additionalExtensionPaths ?? []),
768
+ ];
752
769
  return new DefaultResourceLoader({
753
770
  agentDir,
754
- additionalExtensionPaths: piExtensionPaths,
771
+ additionalExtensionPaths,
755
772
  bundledExtensionKeys: bundledKeys,
756
773
  extensionPathsTransform: (paths) => {
757
774
  // 1. Filter community extensions through the GSD registry
@@ -0,0 +1 @@
1
+ 4ec0444c08b7f2ca
@@ -6,10 +6,29 @@
6
6
  * model-availability check.
7
7
  *
8
8
  * Auth verification runs `claude auth status --json` and inspects the
9
- * `loggedIn` field, falling back to a text heuristic when the JSON shape
10
- * is unavailable (older Claude CLI builds).
9
+ * `loggedIn` field, falling back to plain `claude auth status` and a text
10
+ * heuristic when the JSON shape is unavailable (older Claude CLI builds).
11
+ *
12
+ * Set GSD_CLAUDE_DEBUG=1 to print the probe's binary selection and auth
13
+ * outputs to stderr — useful when diagnosing platform-specific detection
14
+ * failures (Issue #4997).
11
15
  */
12
16
  import { execFileSync } from "node:child_process";
17
+ /**
18
+ * Spawn the Claude CLI without triggering Node's DEP0190.
19
+ *
20
+ * Passing `args` together with `shell: true` is deprecated in Node 22+
21
+ * because the args are concatenated into the command string without
22
+ * escaping. On Windows we still need a shell to resolve `.cmd` shims, so
23
+ * we invoke `cmd /c <command> <args...>` explicitly. On POSIX we don't
24
+ * need a shell at all.
25
+ */
26
+ function spawnClaude(command, args, opts) {
27
+ if (process.platform === "win32") {
28
+ return execFileSync("cmd", ["/c", command, ...args], opts);
29
+ }
30
+ return execFileSync(command, args, opts);
31
+ }
13
32
  /**
14
33
  * Candidate executable names for the Claude Code CLI.
15
34
  *
@@ -24,42 +43,44 @@ const CLAUDE_COMMAND = process.platform === "win32" ? "claude.cmd" : "claude";
24
43
  * installed" results in readiness checks.
25
44
  */
26
45
  const CLAUDE_COMMAND_CANDIDATES = process.platform === "win32" ? [CLAUDE_COMMAND, "claude.exe", "claude"] : [CLAUDE_COMMAND];
27
- // Codes treated as "this candidate didn't run — try the next one" rather than
28
- // fatal failures. ENOENT/EINVAL cover the original Windows .cmd shim cases.
29
- // ETIMEDOUT and EAGAIN cover slow-spawn cases where cmd.exe wrapping plus
30
- // the Claude CLI startup path together exceed the per-attempt timeout
31
- // (Issue #4997 regression on Windows + Node 25).
32
- const SOFT_FAIL_CODES = new Set(["ENOENT", "EINVAL", "ETIMEDOUT", "EAGAIN"]);
33
46
  // Keep the version probe snappy — `claude --version` is a quick path.
34
47
  const VERSION_TIMEOUT_MS = 5_000;
35
48
  // Auth status can be much slower on Windows because the spawn goes through
36
49
  // cmd.exe → claude.cmd → node → Claude CLI. 15s leaves headroom on cold spawns
37
50
  // without making startup feel hung when the CLI is genuinely missing.
38
51
  const AUTH_TIMEOUT_MS = 15_000;
52
+ function debugLog(...parts) {
53
+ if (process.env.GSD_CLAUDE_DEBUG) {
54
+ process.stderr.write(`[claude-readiness] ${parts.map((p) => (typeof p === "string" ? p : JSON.stringify(p))).join(" ")}\n`);
55
+ }
56
+ }
39
57
  /**
40
- * Run the requested Claude CLI command against each supported executable name.
41
- * Returns the first successful output buffer and rethrows hard failures.
58
+ * Find the first candidate that responds to `--version`. Returns the
59
+ * candidate name on success, null if none worked.
60
+ *
61
+ * On Windows with `cmd /c`, a missing candidate surfaces as a
62
+ * non-zero exit from cmd.exe rather than ENOENT — so we cannot rely on
63
+ * the error code to decide "try next". Treat any failure as "try next"
64
+ * for the version probe; the only thing that matters for binary
65
+ * detection is whether *some* candidate produces a `claude --version`
66
+ * line.
42
67
  */
43
- function execClaude(args, timeoutMs) {
44
- let lastError;
68
+ function findWorkingCommand() {
45
69
  for (const command of CLAUDE_COMMAND_CANDIDATES) {
46
70
  try {
47
- return execFileSync(command, args, {
48
- timeout: timeoutMs,
71
+ spawnClaude(command, ["--version"], {
72
+ timeout: VERSION_TIMEOUT_MS,
49
73
  stdio: "pipe",
50
- shell: process.platform === "win32",
51
74
  });
75
+ debugLog("version probe ok via", command);
76
+ return command;
52
77
  }
53
78
  catch (error) {
54
- lastError = error;
55
- const code = error?.code;
56
- if (code && SOFT_FAIL_CODES.has(code)) {
57
- continue;
58
- }
59
- throw error;
79
+ debugLog("version probe failed for", command, "code=", error?.code);
80
+ continue;
60
81
  }
61
82
  }
62
- throw lastError ?? new Error(`Claude CLI executable not found (tried: ${CLAUDE_COMMAND_CANDIDATES.join(", ")})`);
83
+ return null;
63
84
  }
64
85
  /**
65
86
  * Decide auth state from `claude auth status` output.
@@ -71,6 +92,8 @@ function execClaude(args, timeoutMs) {
71
92
  */
72
93
  function parseAuthStatus(output) {
73
94
  const trimmed = output.trim();
95
+ if (!trimmed)
96
+ return null;
74
97
  if (trimmed.startsWith("{")) {
75
98
  try {
76
99
  const parsed = JSON.parse(trimmed);
@@ -86,8 +109,39 @@ function parseAuthStatus(output) {
86
109
  if (/not logged in|no credentials|unauthenticated|not authenticated/.test(lower)) {
87
110
  return false;
88
111
  }
89
- // Exit-0 with non-error output and no negative markers — treat as authed.
90
- return true;
112
+ if (/logged in|authenticated|signed in|email|subscription/.test(lower)) {
113
+ return true;
114
+ }
115
+ return null;
116
+ }
117
+ function probeAuth(command) {
118
+ // Try --json first (newer CLIs).
119
+ try {
120
+ const out = spawnClaude(command, ["auth", "status", "--json"], {
121
+ timeout: AUTH_TIMEOUT_MS,
122
+ stdio: "pipe",
123
+ }).toString();
124
+ debugLog("auth status --json output:", out.slice(0, 200));
125
+ const parsed = parseAuthStatus(out);
126
+ if (parsed !== null)
127
+ return parsed;
128
+ }
129
+ catch (error) {
130
+ debugLog("auth status --json threw:", error.message?.slice(0, 200));
131
+ }
132
+ // Fallback: plain `auth status` (older CLIs that don't accept --json).
133
+ try {
134
+ const out = spawnClaude(command, ["auth", "status"], {
135
+ timeout: AUTH_TIMEOUT_MS,
136
+ stdio: "pipe",
137
+ }).toString();
138
+ debugLog("auth status output:", out.slice(0, 200));
139
+ return parseAuthStatus(out);
140
+ }
141
+ catch (error) {
142
+ debugLog("auth status threw:", error.message?.slice(0, 200));
143
+ return null;
144
+ }
91
145
  }
92
146
  let cachedBinaryPresent = null;
93
147
  let cachedAuthed = null;
@@ -104,33 +158,23 @@ function refreshCache() {
104
158
  }
105
159
  // Set timestamp first to prevent re-entrant checks during the same window
106
160
  lastCheckMs = now;
107
- // Check binary presence
108
- try {
109
- execClaude(["--version"], VERSION_TIMEOUT_MS);
110
- cachedBinaryPresent = true;
111
- }
112
- catch {
161
+ const command = findWorkingCommand();
162
+ if (!command) {
113
163
  cachedBinaryPresent = false;
114
164
  cachedAuthed = false;
115
165
  return;
116
166
  }
117
- // Request JSON explicitly so older CLI builds that defaulted to text and
118
- // newer builds that default to JSON produce a consistent shape.
119
- try {
120
- const output = execClaude(["auth", "status", "--json"], AUTH_TIMEOUT_MS).toString();
121
- cachedAuthed = parseAuthStatus(output);
122
- }
123
- catch (error) {
124
- const code = error?.code;
125
- // Spawn-shape failures (timeout, transient) shouldn't be treated as
126
- // "definitely not authed" — leave the previous value if we have one,
127
- // otherwise default to false. The version probe already established
128
- // the binary works, so a flaky auth probe is more likely transient.
129
- if (code && SOFT_FAIL_CODES.has(code) && cachedAuthed !== null) {
130
- return;
131
- }
132
- cachedAuthed = false;
167
+ cachedBinaryPresent = true;
168
+ const authed = probeAuth(command);
169
+ if (authed === null) {
170
+ // Couldn't determine auth state from CLI output. Don't clobber a
171
+ // previously known-good cache; otherwise default to false so we don't
172
+ // silently route requests to an unauthenticated CLI.
173
+ if (cachedAuthed === null)
174
+ cachedAuthed = false;
175
+ return;
133
176
  }
177
+ cachedAuthed = authed;
134
178
  }
135
179
  /**
136
180
  * Whether the `claude` binary is installed (regardless of auth state).
@@ -1,7 +1,3 @@
1
- export default function (pi) {
2
- pi.on("session_start", async (_event, ctx) => {
3
- ctx.ui.notify("google_search is being extracted to @gsd-extensions/google-search " +
4
- "(not yet published to npm). This stub will be replaced once the " +
5
- "package is available. No action needed for now.", "warning");
6
- });
1
+ export default function (_pi) {
2
+ // Deprecation notice intentionally suppressed until @gsd-extensions/google-search ships.
7
3
  }
@@ -163,6 +163,17 @@ function resolveDispatchNodeKind(unitType, sidecarItem) {
163
163
  }
164
164
  return "unit";
165
165
  }
166
+ async function enforceMinRequestInterval(s, prefs) {
167
+ const minInterval = prefs?.min_request_interval_ms ?? 0;
168
+ if (minInterval > 0 && s.lastRequestTimestamp > 0) {
169
+ const elapsed = Date.now() - s.lastRequestTimestamp;
170
+ if (elapsed < minInterval) {
171
+ const waitMs = minInterval - elapsed;
172
+ debugLog("autoLoop", { phase: "rate-limit-wait", waitMs });
173
+ await new Promise(r => setTimeout(r, waitMs));
174
+ }
175
+ }
176
+ }
166
177
  async function runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, sidecarItem) {
167
178
  if (dispatchContract === "legacy-direct") {
168
179
  return runUnitPhase(ic, iterData, loopState, sidecarItem);
@@ -392,7 +403,13 @@ export async function autoLoop(ctx, pi, s, deps, options) {
392
403
  break;
393
404
  }
394
405
  // ── Unit execution (shared with dev path) ──
406
+ await enforceMinRequestInterval(s, prefs);
395
407
  const unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState);
408
+ if (unitPhaseResult.action === "next") {
409
+ const requestTimestamp = unitPhaseResult.data.requestDispatchedAt ?? unitPhaseResult.data.unitStartedAt;
410
+ if (typeof requestTimestamp === "number")
411
+ s.lastRequestTimestamp = requestTimestamp;
412
+ }
396
413
  deps.uokObserver?.onPhaseResult("unit", unitPhaseResult.action, {
397
414
  unitType: iterData.unitType,
398
415
  unitId: iterData.unitId,
@@ -555,7 +572,13 @@ export async function autoLoop(ctx, pi, s, deps, options) {
555
572
  sidecarKind: sidecarItem.kind,
556
573
  });
557
574
  }
575
+ await enforceMinRequestInterval(s, prefs);
558
576
  const unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, sidecarItem);
577
+ if (unitPhaseResult.action === "next") {
578
+ const requestTimestamp = unitPhaseResult.data.requestDispatchedAt ?? unitPhaseResult.data.unitStartedAt;
579
+ if (typeof requestTimestamp === "number")
580
+ s.lastRequestTimestamp = requestTimestamp;
581
+ }
559
582
  deps.uokObserver?.onPhaseResult("unit", unitPhaseResult.action, {
560
583
  unitType: iterData.unitType,
561
584
  unitId: iterData.unitId,
@@ -11,6 +11,7 @@ import { MAX_RECOVERY_CHARS, BUDGET_THRESHOLDS, MAX_FINALIZE_TIMEOUTS, } from ".
11
11
  import { detectStuck } from "./detect-stuck.js";
12
12
  import { runUnit } from "./run-unit.js";
13
13
  import { debugLog } from "../debug-logger.js";
14
+ import { resolveWorktreeProjectRoot } from "../worktree-root.js";
14
15
  import { PROJECT_FILES, hasProjectFileInAncestor } from "../detection.js";
15
16
  import { MergeConflictError } from "../git-service.js";
16
17
  import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
@@ -48,11 +49,7 @@ export function resetSessionTimeoutState() {
48
49
  * Exported for testing as _resolveReportBasePath.
49
50
  */
50
51
  export function _resolveReportBasePath(s) {
51
- // Strip /.gsd/worktrees/ suffix when basePath is itself a worktree path and
52
- // originalBasePath is falsy — prevents reports landing in the worktree (#3729).
53
- const resolved = s.originalBasePath || s.basePath;
54
- const markerIdx = resolved.indexOf("/.gsd/worktrees/");
55
- return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
52
+ return resolveWorktreeProjectRoot(s.basePath, s.originalBasePath);
56
53
  }
57
54
  /**
58
55
  * Resolve the authoritative project base for dispatch guards.
@@ -60,12 +57,7 @@ export function _resolveReportBasePath(s) {
60
57
  * unit is running inside an auto worktree.
61
58
  */
62
59
  export function _resolveDispatchGuardBasePath(s) {
63
- // Strip /.gsd/worktrees/ suffix when basePath is itself a worktree path and
64
- // originalBasePath is falsy — prevents guard checks running against the
65
- // worktree instead of the project root (#3729).
66
- const resolved = s.originalBasePath || s.basePath;
67
- const markerIdx = resolved.indexOf("/.gsd/worktrees/");
68
- return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
60
+ return resolveWorktreeProjectRoot(s.basePath, s.originalBasePath);
69
61
  }
70
62
  const PLAN_V2_GATE_PHASES = new Set([
71
63
  "executing",
@@ -1365,7 +1357,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1365
1357
  ctx.ui.notify(`${unitType} ${unitId} completed with 0 tool calls — context exhaustion, will retry`, "warning");
1366
1358
  // Fall through to next iteration where dispatch will re-derive
1367
1359
  // and re-dispatch this unit.
1368
- return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt } };
1360
+ return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt, requestDispatchedAt: unitResult.requestDispatchedAt } };
1369
1361
  }
1370
1362
  }
1371
1363
  }
@@ -1418,7 +1410,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1418
1410
  }
1419
1411
  s.checkpointSha = null;
1420
1412
  }
1421
- return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt } };
1413
+ return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt, requestDispatchedAt: unitResult.requestDispatchedAt } };
1422
1414
  }
1423
1415
  // ─── runFinalize ──────────────────────────────────────────────────────────────
1424
1416
  /**