@opengsd/gsd-pi 1.1.1-dev.74e8dd1 → 1.1.1-dev.9bb7453

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 (259) hide show
  1. package/dist/cli.js +3 -2
  2. package/dist/help-text.js +10 -6
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +495 -0
  5. package/dist/resources/extensions/browser-tools/engine/selection.js +16 -0
  6. package/dist/resources/extensions/browser-tools/extension-manifest.json +2 -2
  7. package/dist/resources/extensions/browser-tools/index.js +57 -9
  8. package/dist/resources/extensions/browser-tools/package.json +5 -1
  9. package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
  10. package/dist/resources/extensions/gsd/auto-dashboard.js +77 -13
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +5 -0
  12. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
  13. package/dist/resources/extensions/gsd/auto-prompts.js +59 -22
  14. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  16. package/dist/resources/extensions/gsd/auto.js +9 -2
  17. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -4
  18. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -5
  19. package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
  20. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  21. package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
  22. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
  23. package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
  24. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  25. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
  26. package/dist/resources/extensions/gsd/escalation.js +4 -4
  27. package/dist/resources/extensions/gsd/forensics.js +74 -2
  28. package/dist/resources/extensions/gsd/gsd-db.js +5 -2
  29. package/dist/resources/extensions/gsd/guided-flow.js +29 -68
  30. package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
  31. package/dist/resources/extensions/gsd/memory-store.js +4 -1
  32. package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
  33. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  34. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  35. package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
  36. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  37. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  38. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  40. package/dist/resources/extensions/gsd/prompts/run-uat.md +40 -22
  41. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  42. package/dist/resources/extensions/gsd/rule-registry.js +428 -52
  43. package/dist/resources/extensions/gsd/state.js +2 -2
  44. package/dist/resources/extensions/gsd/templates/plan.md +3 -1
  45. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
  46. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
  47. package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
  48. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +51 -14
  49. package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
  50. package/dist/resources/extensions/gsd/verification-gate.js +72 -1
  51. package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
  52. package/dist/rtk.d.ts +7 -1
  53. package/dist/rtk.js +27 -11
  54. package/dist/update-check.d.ts +15 -1
  55. package/dist/update-check.js +87 -12
  56. package/dist/update-cmd.d.ts +1 -0
  57. package/dist/update-cmd.js +53 -2
  58. package/dist/web/standalone/.next/BUILD_ID +1 -1
  59. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  60. package/dist/web/standalone/.next/build-manifest.json +2 -2
  61. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  62. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/index.html +1 -1
  80. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  87. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  88. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  90. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  91. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  92. package/package.json +4 -2
  93. package/packages/cloud-mcp-gateway/package.json +2 -2
  94. package/packages/contracts/package.json +1 -1
  95. package/packages/daemon/package.json +4 -4
  96. package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
  97. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  98. package/packages/gsd-agent-core/dist/agent-session.js +32 -0
  99. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  100. package/packages/gsd-agent-core/dist/index.d.ts +1 -0
  101. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  102. package/packages/gsd-agent-core/dist/index.js +1 -0
  103. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  104. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
  105. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
  106. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
  107. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
  108. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
  109. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  110. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  111. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  112. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
  113. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  114. package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
  115. package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
  116. package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
  117. package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
  118. package/packages/gsd-agent-core/package.json +6 -6
  119. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
  120. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
  121. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
  122. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
  123. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  124. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +20 -0
  125. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  126. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  127. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
  128. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  129. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
  132. package/packages/gsd-agent-modes/package.json +7 -7
  133. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  134. package/packages/mcp-server/dist/remote-questions.js +23 -9
  135. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  136. package/packages/mcp-server/dist/workflow-tools.js +2 -2
  137. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  138. package/packages/mcp-server/package.json +3 -3
  139. package/packages/native/package.json +1 -1
  140. package/packages/pi-agent-core/dist/agent-loop.js +38 -0
  141. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  142. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  143. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  144. package/packages/pi-agent-core/dist/agent.js +2 -0
  145. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  146. package/packages/pi-agent-core/dist/types.d.ts +3 -0
  147. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  148. package/packages/pi-agent-core/dist/types.js.map +1 -1
  149. package/packages/pi-agent-core/package.json +1 -1
  150. package/packages/pi-ai/dist/api-registry.d.ts +2 -0
  151. package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
  152. package/packages/pi-ai/dist/api-registry.js +23 -0
  153. package/packages/pi-ai/dist/api-registry.js.map +1 -1
  154. package/packages/pi-ai/dist/models.generated.d.ts +68 -0
  155. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  156. package/packages/pi-ai/dist/models.generated.js +72 -4
  157. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  158. package/packages/pi-ai/dist/stream.js +6 -6
  159. package/packages/pi-ai/dist/stream.js.map +1 -1
  160. package/packages/pi-ai/package.json +1 -1
  161. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
  163. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  164. package/packages/pi-coding-agent/package.json +7 -7
  165. package/packages/pi-tui/package.json +1 -1
  166. package/packages/rpc-client/package.json +2 -2
  167. package/pkg/package.json +1 -1
  168. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
  169. package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
  170. package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
  171. package/src/resources/extensions/browser-tools/index.ts +60 -9
  172. package/src/resources/extensions/browser-tools/package.json +5 -1
  173. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
  174. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
  175. package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
  176. package/src/resources/extensions/gsd/auto-dashboard.ts +82 -14
  177. package/src/resources/extensions/gsd/auto-dispatch.ts +5 -0
  178. package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
  179. package/src/resources/extensions/gsd/auto-prompts.ts +93 -15
  180. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
  181. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  182. package/src/resources/extensions/gsd/auto.ts +12 -2
  183. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -4
  184. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +9 -5
  185. package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
  186. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
  187. package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
  188. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
  189. package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
  190. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  191. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
  192. package/src/resources/extensions/gsd/escalation.ts +4 -4
  193. package/src/resources/extensions/gsd/forensics.ts +99 -5
  194. package/src/resources/extensions/gsd/gsd-db.ts +5 -2
  195. package/src/resources/extensions/gsd/guided-flow.ts +90 -82
  196. package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
  197. package/src/resources/extensions/gsd/memory-store.ts +4 -1
  198. package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
  199. package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
  200. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  201. package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
  202. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  203. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  204. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  205. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  206. package/src/resources/extensions/gsd/prompts/run-uat.md +40 -22
  207. package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  208. package/src/resources/extensions/gsd/rule-registry.ts +558 -58
  209. package/src/resources/extensions/gsd/rule-types.ts +2 -0
  210. package/src/resources/extensions/gsd/state.ts +2 -2
  211. package/src/resources/extensions/gsd/templates/plan.md +3 -1
  212. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +105 -4
  213. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
  214. package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
  215. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
  216. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  217. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
  218. package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
  219. package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
  220. package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
  221. package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
  222. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
  223. package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
  224. package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
  225. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
  226. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
  227. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
  228. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
  229. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +66 -10
  230. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
  231. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
  232. package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
  233. package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
  234. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
  235. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
  236. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
  237. package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
  238. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +43 -1
  239. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
  240. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
  241. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
  242. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
  243. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +139 -0
  244. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  245. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
  246. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
  247. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
  248. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
  249. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +130 -0
  250. package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
  251. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
  252. package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
  253. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +63 -15
  254. package/src/resources/extensions/gsd/types.ts +69 -5
  255. package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
  256. package/src/resources/extensions/gsd/verification-gate.ts +87 -1
  257. package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
  258. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → jBtwT9v1u2lUA3UEOy_ZH}/_buildManifest.js +0 -0
  259. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → jBtwT9v1u2lUA3UEOy_ZH}/_ssgManifest.js +0 -0
@@ -1,11 +1,16 @@
1
- /** browser-tools — pi extension: full browser interaction via Playwright. */
1
+ /** browser-tools — Pi Browser Automation Contract adapter. */
2
2
  import { importExtensionModule, type ExtensionAPI } from "@gsd/pi-coding-agent";
3
3
 
4
- let registrationPromise: Promise<void> | null = null;
4
+ import { closeManagedGsdBrowser, registerManagedGsdBrowserTools } from "./engine/managed-gsd-browser.js";
5
+ import { resolveBrowserEngineMode, type BrowserEngineMode } from "./engine/selection.js";
5
6
 
6
- async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
7
- if (!registrationPromise) {
8
- registrationPromise = (async () => {
7
+ let legacyRegistrationPromise: Promise<void> | null = null;
8
+ let managedRegistrationPromise: Promise<void> | null = null;
9
+ let registeredEngine: Exclude<BrowserEngineMode, "off"> | null = null;
10
+
11
+ async function registerLegacyBrowserTools(pi: ExtensionAPI): Promise<void> {
12
+ if (!legacyRegistrationPromise) {
13
+ legacyRegistrationPromise = (async () => {
9
14
  const [
10
15
  lifecycle,
11
16
  capture,
@@ -136,12 +141,55 @@ async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
136
141
  injectionDetection.registerInjectionDetectionTools(pi, deps);
137
142
  verify.registerVerifyTools(pi, deps);
138
143
  })().catch((error) => {
139
- registrationPromise = null;
144
+ legacyRegistrationPromise = null;
140
145
  throw error;
141
146
  });
142
147
  }
143
148
 
144
- return registrationPromise;
149
+ return legacyRegistrationPromise;
150
+ }
151
+
152
+ async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
153
+ const engine = resolveBrowserEngineMode();
154
+ if (engine === "off") return;
155
+ if (registeredEngine && registeredEngine !== engine) {
156
+ throw new Error(
157
+ `Browser tools already registered with GSD_BROWSER_ENGINE=${registeredEngine}. Restart GSD before switching to ${engine}.`,
158
+ );
159
+ }
160
+
161
+ let registration: Promise<void>;
162
+ if (engine === "legacy") {
163
+ registration = registerLegacyBrowserTools(pi);
164
+ } else if (!managedRegistrationPromise) {
165
+ managedRegistrationPromise = Promise.resolve()
166
+ .then(() => {
167
+ registerManagedGsdBrowserTools(pi);
168
+ })
169
+ .catch((error) => {
170
+ managedRegistrationPromise = null;
171
+ throw error;
172
+ });
173
+ registration = managedRegistrationPromise;
174
+ } else {
175
+ registration = managedRegistrationPromise;
176
+ }
177
+
178
+ registeredEngine = engine;
179
+ try {
180
+ await registration;
181
+ } catch (error) {
182
+ if (registeredEngine === engine) registeredEngine = null;
183
+ throw error;
184
+ }
185
+ }
186
+
187
+ async function closeActiveBrowserEngines(): Promise<void> {
188
+ await closeManagedGsdBrowser();
189
+ if (legacyRegistrationPromise) {
190
+ const { closeBrowser } = await importExtensionModule<typeof import("./lifecycle.js")>(import.meta.url, "./lifecycle.js");
191
+ await closeBrowser();
192
+ }
145
193
  }
146
194
 
147
195
  export default function (pi: ExtensionAPI) {
@@ -157,7 +205,10 @@ export default function (pi: ExtensionAPI) {
157
205
  });
158
206
 
159
207
  pi.on("session_shutdown", async () => {
160
- const { closeBrowser } = await importExtensionModule<typeof import("./lifecycle.js")>(import.meta.url, "./lifecycle.js");
161
- await closeBrowser();
208
+ await closeActiveBrowserEngines();
209
+ });
210
+
211
+ pi.on("session_switch", async () => {
212
+ await closeActiveBrowserEngines();
162
213
  });
163
214
  }
@@ -4,16 +4,20 @@
4
4
  "version": "1.0.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "test": "node --test tests/*.test.mjs"
7
+ "test": "node --import ../gsd/tests/resolve-ts.mjs --experimental-strip-types --test tests/*.test.mjs"
8
8
  },
9
9
  "pi": {
10
10
  "extensions": ["./index.ts"]
11
11
  },
12
12
  "peerDependencies": {
13
+ "@opengsd/gsd-browser": ">=0.1.27",
13
14
  "playwright": ">=1.40.0",
14
15
  "sharp": ">=0.33.0"
15
16
  },
16
17
  "peerDependenciesMeta": {
18
+ "@opengsd/gsd-browser": {
19
+ "optional": true
20
+ },
17
21
  "playwright": {
18
22
  "optional": true
19
23
  },
@@ -0,0 +1,35 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { createRequire } from "node:module";
4
+ import { dirname } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const require = createRequire(import.meta.url);
9
+ const jiti = require("jiti")(__dirname, { interopDefault: true, debug: false });
10
+
11
+ const { resolveBrowserEngineMode } = jiti("../engine/selection.ts");
12
+
13
+ describe("resolveBrowserEngineMode", () => {
14
+ it("defaults to gsd-browser", () => {
15
+ assert.equal(resolveBrowserEngineMode({}), "gsd-browser");
16
+ });
17
+
18
+ it("accepts the explicit engine modes", () => {
19
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "gsd-browser" }), "gsd-browser");
20
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "legacy" }), "legacy");
21
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "off" }), "off");
22
+ });
23
+
24
+ it("accepts compatibility aliases", () => {
25
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "playwright" }), "legacy");
26
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "false" }), "off");
27
+ });
28
+
29
+ it("rejects unknown engine modes", () => {
30
+ assert.throws(
31
+ () => resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "surprise" }),
32
+ /Expected "gsd-browser", "legacy", or "off"/,
33
+ );
34
+ });
35
+ });
@@ -0,0 +1,33 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ const {
5
+ MANAGED_GSD_BROWSER_TOOL_NAMES,
6
+ registerManagedGsdBrowserTools,
7
+ } = await import("../engine/managed-gsd-browser.ts");
8
+
9
+ describe("registerManagedGsdBrowserTools", () => {
10
+ it("registers the curated Pi browser contract", () => {
11
+ const tools = [];
12
+ registerManagedGsdBrowserTools({
13
+ registerTool(tool) {
14
+ tools.push(tool);
15
+ },
16
+ });
17
+
18
+ assert.deepEqual(tools.map((tool) => tool.name), [...MANAGED_GSD_BROWSER_TOOL_NAMES]);
19
+ assert.equal(new Set(tools.map((tool) => tool.name)).size, tools.length);
20
+ });
21
+
22
+ it("keeps screenshots marked as image-producing evidence", () => {
23
+ const tools = [];
24
+ registerManagedGsdBrowserTools({
25
+ registerTool(tool) {
26
+ tools.push(tool);
27
+ },
28
+ });
29
+
30
+ const screenshot = tools.find((tool) => tool.name === "browser_screenshot");
31
+ assert.equal(screenshot?.compatibility?.producesImages, true);
32
+ });
33
+ });
@@ -277,7 +277,6 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
277
277
  this.status.activeUnit = { unitType: decision.unitType, unitId: decision.unitId };
278
278
  this.status.phase = "running";
279
279
  this.lastAdvanceKey = nextKey;
280
- this.lastFinalizedUnitKey = null;
281
280
  this.bumpTransition();
282
281
 
283
282
  await this.deps.runtime.journalTransition({
@@ -13,10 +13,11 @@ import type {
13
13
  ExtensionCommandContext,
14
14
  ReadonlyFooterDataProvider,
15
15
  Theme,
16
+ ThemeColor,
16
17
  } from "@gsd/pi-coding-agent";
17
18
  import type { GSDState } from "./types.js";
18
19
  import { getActiveHook } from "./post-unit-hooks.js";
19
- import { getLedger } from "./metrics.js";
20
+ import { getLedger, getProjectTotals } from "./metrics.js";
20
21
  import { getErrorMessage } from "./error-utils.js";
21
22
  import { nativeIsRepo } from "./native-git-bridge.js";
22
23
  import {
@@ -304,6 +305,40 @@ export function shouldRenderRoadmapProgress(
304
305
  return !!progress && progress.total > 0;
305
306
  }
306
307
 
308
+ function widgetGridLabel(theme: Theme, text: string, color: ThemeColor = "borderAccent"): string {
309
+ return theme.fg(color, theme.bold(text.toUpperCase()));
310
+ }
311
+
312
+ function widgetGridColumn(content: string, width: number): string {
313
+ return padRightVisible(truncateToWidth(content, width, "…"), width);
314
+ }
315
+
316
+ function widgetGridColumns(theme: Theme, width: number, parts: string[]): string {
317
+ if (parts.length === 0) return "";
318
+ const gap = theme.fg("dim", " │ ");
319
+ const gapWidth = visibleWidth(gap) * (parts.length - 1);
320
+ const available = Math.max(parts.length * 8, width - gapWidth);
321
+ const base = Math.floor(available / parts.length);
322
+ let remaining = available - base * parts.length;
323
+ const columns = parts.map((part) => {
324
+ const columnWidth = base + (remaining > 0 ? 1 : 0);
325
+ remaining--;
326
+ return widgetGridColumn(part, columnWidth);
327
+ });
328
+ return truncateToWidth(columns.join(gap), width, "…");
329
+ }
330
+
331
+ function formatSmallWidgetSpend(): string {
332
+ const ledger = getLedger();
333
+ if (!ledger || ledger.units.length === 0) return "--";
334
+
335
+ const totals = getProjectTotals(ledger.units);
336
+ const parts: string[] = [];
337
+ if (totals.tokens.total > 0) parts.push(formatWidgetTokens(totals.tokens.total));
338
+ if (totals.cost > 0) parts.push(`$${totals.cost.toFixed(2)}`);
339
+ return parts.length > 0 ? parts.join(" · ") : "--";
340
+ }
341
+
307
342
  // ─── ETA Estimation ──────────────────────────────────────────────────────────
308
343
 
309
344
  /**
@@ -836,29 +871,62 @@ export function updateProgressWidget(
836
871
  return lines;
837
872
  }
838
873
 
839
- // ── Mode: small — header + active work progress ───────────────
874
+ // ── Mode: small — dense horizontal grid ───────────────────────
840
875
  if (widgetMode === "small") {
841
- lines.push("");
842
-
843
- // Action line
844
- const target = task ? `${task.id}: ${task.title}` : unitId;
845
- const actionLeft = `${pad}${theme.fg("accent", "▸")} ${theme.fg("accent", verb)} ${theme.fg("text", target)}`;
846
- lines.push(rightAlign(actionLeft, theme.fg("dim", phaseLabel), width));
876
+ lines.length = 0;
877
+ lines.push(...ui.bar());
847
878
 
848
- // Progress bar
849
879
  const roadmapSlices = mid ? getRoadmapSlicesSync() : null;
880
+ const unitLabel = unitId || [mid?.id, slice?.id, task?.id].filter(Boolean).join("/");
881
+ const statusParts = [
882
+ spinner,
883
+ theme.fg("success", modeTag),
884
+ theme.fg(stateColor, activeState),
885
+ ];
886
+ if (runtimeSignal?.summary) {
887
+ statusParts.push(theme.fg(healthColor, healthSummary));
888
+ } else if (healthLevel !== "green") {
889
+ statusParts.push(`${theme.fg(healthColor, healthIcon)} ${theme.fg(healthColor, healthSummary)}`);
890
+ }
891
+
892
+ const timeValue = [elapsed, etaShort].filter(Boolean).join(" · ") || "--";
893
+ const rowOne = widgetGridColumns(theme, width, [
894
+ `${widgetGridLabel(theme, "status", "border")} ${statusParts.join(" ")}`,
895
+ `${widgetGridLabel(theme, "unit")} ${theme.fg("text", unitLabel || "--")}`,
896
+ `${widgetGridLabel(theme, "spend", "border")} ${theme.fg("dim", formatSmallWidgetSpend())}`,
897
+ `${widgetGridLabel(theme, "time")} ${theme.fg("dim", timeValue)}`,
898
+ ]);
899
+
900
+ const target = task
901
+ ? `${task.id}: ${task.title}`
902
+ : slice
903
+ ? `${slice.id}: ${slice.title}`
904
+ : unitId;
905
+
906
+ let taskValue = task?.id ?? "--";
907
+ let sliceValue = slice?.id ?? "--";
850
908
  if (shouldRenderRoadmapProgress(roadmapSlices)) {
851
909
  const { done, total, activeSliceTasks } = roadmapSlices;
852
- const barWidth = Math.max(6, Math.min(18, Math.floor(width * 0.25)));
910
+ const barWidth = Math.max(4, Math.min(14, Math.floor(width * 0.12)));
853
911
  const bar = renderProgressBar(theme, done, total, barWidth);
854
- let meta = `${theme.fg("text", `${done}`)}${theme.fg("dim", `/${total} slices`)}`;
912
+ sliceValue = `${bar} ${theme.fg("text", `${done}/${total}`)}`;
855
913
  if (activeSliceTasks && activeSliceTasks.total > 0) {
856
- const tn = Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
857
- meta += `${theme.fg("dim", " · task ")}${theme.fg("accent", `${tn}`)}${theme.fg("dim", `/${activeSliceTasks.total}`)}`;
914
+ const taskNum = isHook
915
+ ? Math.max(activeSliceTasks.done, 1)
916
+ : Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
917
+ taskValue = `${theme.fg("accent", `${taskNum}`)}${theme.fg("dim", `/${activeSliceTasks.total}`)}`;
858
918
  }
859
- lines.push(`${pad}${bar} ${meta}`);
860
919
  }
861
920
 
921
+ const rowTwo = widgetGridColumns(theme, width, [
922
+ `${widgetGridLabel(theme, "phase", "border")} ${theme.fg("dim", unitType)}`,
923
+ `${widgetGridLabel(theme, "work")} ${theme.fg("text", target || "--")}`,
924
+ `${widgetGridLabel(theme, "task", "border")} ${taskValue}`,
925
+ `${widgetGridLabel(theme, "slice")} ${sliceValue}`,
926
+ ]);
927
+
928
+ lines.push(rowOne);
929
+ lines.push(rowTwo);
862
930
  lines.push(...ui.bar());
863
931
  cachedLines = lines;
864
932
  cachedWidth = width;
@@ -622,6 +622,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
622
622
  structuredQuestionsAvailable,
623
623
  { headless: !!process.env.GSD_HEADLESS },
624
624
  ),
625
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
625
626
  };
626
627
  },
627
628
  },
@@ -772,6 +773,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
772
773
  structuredQuestionsAvailable,
773
774
  { headless: !!process.env.GSD_HEADLESS },
774
775
  ),
776
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
775
777
  };
776
778
  },
777
779
  },
@@ -805,6 +807,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
805
807
  unitType: "discuss-project",
806
808
  unitId: "PROJECT",
807
809
  prompt: await buildDiscussProjectPrompt(basePath, structuredQuestionsAvailable),
810
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
808
811
  };
809
812
  },
810
813
  },
@@ -826,6 +829,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
826
829
  unitType: "discuss-requirements",
827
830
  unitId: "REQUIREMENTS",
828
831
  prompt: await buildDiscussRequirementsPrompt(basePath, structuredQuestionsAvailable),
832
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
829
833
  };
830
834
  },
831
835
  },
@@ -940,6 +944,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
940
944
  structuredQuestionsAvailable,
941
945
  { headless: !!process.env.GSD_HEADLESS },
942
946
  ),
947
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
943
948
  };
944
949
  },
945
950
  },
@@ -55,8 +55,10 @@ import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js";
55
55
  import { consumeSignal } from "./session-status-io.js";
56
56
  import {
57
57
  checkPostUnitHooks,
58
+ consumeHookFailure,
58
59
  isRetryPending,
59
60
  consumeRetryTrigger,
61
+ consumeGateBlock,
60
62
  persistHookState,
61
63
  resolveHookArtifactPath,
62
64
  } from "./post-unit-hooks.js";
@@ -2227,11 +2229,11 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2227
2229
  // ── Post-unit hooks ──
2228
2230
  if (s.currentUnit && !s.stepMode) {
2229
2231
  const hookUnit = checkPostUnitHooks(s.currentUnit.type, s.currentUnit.id, s.basePath);
2232
+ persistHookState(s.basePath);
2230
2233
  if (hookUnit) {
2231
2234
  if (s.currentUnit) {
2232
2235
  await closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
2233
2236
  }
2234
- persistHookState(s.basePath);
2235
2237
 
2236
2238
  return enqueueSidecar(
2237
2239
  s, ctx,
@@ -2240,12 +2242,23 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2240
2242
  );
2241
2243
  }
2242
2244
 
2245
+ const hookFailure = consumeHookFailure();
2246
+ if (hookFailure) {
2247
+ ctx.ui.notify(
2248
+ `Post-unit hook ${hookFailure.hookName} failed for ${hookFailure.unitId}: ${hookFailure.reason}. Pausing auto-mode.`,
2249
+ "warning",
2250
+ );
2251
+ await pauseAuto(ctx, pi);
2252
+ return "stopped";
2253
+ }
2254
+
2243
2255
  // Check if a hook requested a retry of the trigger unit
2244
2256
  if (isRetryPending()) {
2245
2257
  const trigger = consumeRetryTrigger();
2246
2258
  if (trigger) {
2259
+ persistHookState(s.basePath);
2247
2260
  ctx.ui.notify(
2248
- `Hook requested retry of ${trigger.unitType} ${trigger.unitId} — resetting task state.`,
2261
+ `Hook requested retry of ${trigger.unitType} ${trigger.unitId} — resetting trigger unit state.`,
2249
2262
  "info",
2250
2263
  );
2251
2264
 
@@ -2299,6 +2312,19 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2299
2312
  // Fall through to normal dispatch — deriveState will re-derive the unit
2300
2313
  }
2301
2314
  }
2315
+
2316
+ const gateBlock = consumeGateBlock();
2317
+ if (gateBlock) {
2318
+ persistHookState(s.basePath);
2319
+ const verdict = gateBlock.verdict ? ` verdict=${gateBlock.verdict};` : "";
2320
+ const artifact = gateBlock.artifact ? ` artifact=${gateBlock.artifact};` : "";
2321
+ const message =
2322
+ `Post-unit gate "${gateBlock.hookName}" blocked ${gateBlock.triggerUnitType} ${gateBlock.triggerUnitId}:` +
2323
+ `${verdict}${artifact} ${gateBlock.reason}. Run /gsd status to inspect, then /gsd auto after recovery.`;
2324
+ ctx.ui.notify(message, "warning");
2325
+ await pauseAuto(ctx, pi);
2326
+ return "stopped";
2327
+ }
2302
2328
  }
2303
2329
 
2304
2330
  // ── Fast-path stop detection (#3487) ──
@@ -45,6 +45,7 @@ import { classifyProject, type ProjectClassification } from "./detection.js";
45
45
  import { hasBrowserRequiredText } from "./browser-evidence.js";
46
46
  import { debugLog } from "./debug-logger.js";
47
47
  import { buildSkillActivationBlock, buildSkillDiscoveryVars } from "./skill-activation.js";
48
+ import { findMilestoneIds } from "./milestone-ids.js";
48
49
 
49
50
  export { buildSkillActivationBlock, buildSkillDiscoveryVars };
50
51
 
@@ -1428,7 +1429,7 @@ export async function checkNeedsRunUat(
1428
1429
  // If the UAT file already contains a verdict, UAT has been run — skip
1429
1430
  if (hasVerdict(uatContent)) continue;
1430
1431
  // Also check the ASSESSMENT file — the run-uat prompt writes the verdict
1431
- // there (via gsd_summary_save artifact_type:"ASSESSMENT"), not into the
1432
+ // there (via gsd_uat_result_save), not into the
1432
1433
  // UAT spec file. Without this check the unit re-dispatches indefinitely.
1433
1434
  const assessmentFile = resolveSliceFile(base, mid, sid, "ASSESSMENT");
1434
1435
  if (assessmentFile) {
@@ -1482,21 +1483,84 @@ export async function checkNeedsRunUat(
1482
1483
  * as a seed when present. The discussion agent interviews the user, writes
1483
1484
  * a full CONTEXT.md, and the phase transitions to pre-planning automatically.
1484
1485
  */
1486
+ export interface DiscussMilestonePromptOptions {
1487
+ headless?: boolean;
1488
+ commitInstruction?: string;
1489
+ fastPathInstruction?: string;
1490
+ includeDraftSeed?: boolean;
1491
+ includeContextMode?: boolean;
1492
+ }
1493
+
1494
+ export async function buildDiscussMilestoneInlinedContext(mid: string, base: string): Promise<string> {
1495
+ const inlined: string[] = [];
1496
+
1497
+ const roadmapInline = await inlineFileOptional(
1498
+ resolveMilestoneFile(base, mid, "ROADMAP"),
1499
+ relMilestoneFile(base, mid, "ROADMAP"),
1500
+ "Milestone Roadmap",
1501
+ );
1502
+ if (roadmapInline) inlined.push(roadmapInline);
1503
+
1504
+ const contextInline = await inlineFileOptional(
1505
+ resolveMilestoneFile(base, mid, "CONTEXT"),
1506
+ relMilestoneFile(base, mid, "CONTEXT"),
1507
+ "Milestone Context",
1508
+ );
1509
+ if (contextInline) inlined.push(contextInline);
1510
+
1511
+ const researchInline = await inlineFileOptional(
1512
+ resolveMilestoneFile(base, mid, "RESEARCH"),
1513
+ relMilestoneFile(base, mid, "RESEARCH"),
1514
+ "Milestone Research",
1515
+ );
1516
+ if (researchInline) inlined.push(researchInline);
1517
+
1518
+ const decisionsPath = resolveGsdRootFile(base, "DECISIONS");
1519
+ if (existsSync(decisionsPath)) {
1520
+ const decisionsContent = await loadFile(decisionsPath);
1521
+ if (decisionsContent) {
1522
+ inlined.push(`### Decisions Register\nSource: \`${relGsdRootFile("DECISIONS")}\`\n\n${decisionsContent.trim()}`);
1523
+ }
1524
+ }
1525
+
1526
+ const milestoneIds = findMilestoneIds(base);
1527
+ const currentIndex = milestoneIds.indexOf(mid);
1528
+ const priorMilestoneIds = currentIndex >= 0 ? milestoneIds.slice(0, currentIndex) : milestoneIds;
1529
+ for (const priorMid of priorMilestoneIds) {
1530
+ const summaryInline = await inlineFileOptional(
1531
+ resolveMilestoneFile(base, priorMid, "SUMMARY"),
1532
+ relMilestoneFile(base, priorMid, "SUMMARY"),
1533
+ `${priorMid} Prior Milestone Summary`,
1534
+ );
1535
+ if (summaryInline) inlined.push(summaryInline);
1536
+ }
1537
+
1538
+ return inlined.length > 0
1539
+ ? `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`
1540
+ : "## Inlined Context\n\n_(no milestone context files found yet — go in blind and ask broad questions)_";
1541
+ }
1542
+
1485
1543
  export async function buildDiscussMilestonePrompt(
1486
1544
  mid: string,
1487
1545
  midTitle: string,
1488
1546
  base: string,
1489
1547
  structuredQuestionsAvailable = "false",
1490
- { headless = false }: { headless?: boolean } = {},
1548
+ {
1549
+ headless = false,
1550
+ commitInstruction = "Do not commit planning artifacts — .gsd/ is managed externally.",
1551
+ fastPathInstruction = "",
1552
+ includeDraftSeed = true,
1553
+ includeContextMode = true,
1554
+ }: DiscussMilestonePromptOptions = {},
1491
1555
  ): Promise<string> {
1492
- const discussTemplates = inlineTemplate("context", "Context");
1556
+ const contextTemplate = inlineTemplate("context", "Context");
1493
1557
 
1494
1558
  if (headless) {
1495
1559
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
1496
1560
  const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
1497
1561
  return loadPrompt("discuss-headless", {
1498
1562
  seedContext: roadmapContent ?? "",
1499
- inlinedTemplates: discussTemplates,
1563
+ inlinedTemplates: contextTemplate,
1500
1564
  workingDirectory: base,
1501
1565
  milestoneId: mid,
1502
1566
  contextPath: relMilestoneFile(base, mid, "CONTEXT"),
@@ -1505,7 +1569,9 @@ export async function buildDiscussMilestonePrompt(
1505
1569
  });
1506
1570
  }
1507
1571
 
1508
- const contextModeInstructions = renderContextModeForPrompt("discuss-milestone", base);
1572
+ const rawInlinedContext = await buildDiscussMilestoneInlinedContext(mid, base);
1573
+ const cappedInlinedContext = capPreamble(rawInlinedContext);
1574
+ const discussTemplates = [cappedInlinedContext, contextTemplate].join("\n\n---\n\n");
1509
1575
 
1510
1576
  const basePrompt = loadPrompt("guided-discuss-milestone", {
1511
1577
  workingDirectory: base,
@@ -1513,20 +1579,22 @@ export async function buildDiscussMilestonePrompt(
1513
1579
  milestoneTitle: midTitle,
1514
1580
  inlinedTemplates: discussTemplates,
1515
1581
  structuredQuestionsAvailable,
1516
- commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
1517
- fastPathInstruction: "",
1582
+ commitInstruction,
1583
+ fastPathInstruction,
1518
1584
  });
1519
- const promptWithContextMode = prependContextModeToBlock("discuss-milestone", base, basePrompt);
1585
+ const promptWithContextMode = includeContextMode
1586
+ ? prependContextModeToBlock("discuss-milestone", base, basePrompt)
1587
+ : basePrompt;
1520
1588
 
1521
1589
  // If a CONTEXT-DRAFT.md exists, append it as seed material
1522
1590
  const draftPath = resolveMilestoneFile(base, mid, "CONTEXT-DRAFT");
1523
1591
  const draftContent = draftPath ? await loadFile(draftPath) : null;
1524
1592
 
1525
- if (draftContent) {
1593
+ if (includeDraftSeed && draftContent) {
1526
1594
  return `${promptWithContextMode}\n\n## Prior Discussion (Draft Seed)\n\nThe following draft was captured from a prior multi-milestone discussion. Use it as seed material — the user has already provided this context. Start with a brief reflection on what the draft covers, then probe for any gaps or open questions before writing the full CONTEXT.md.\n\n${draftContent}`;
1527
1595
  }
1528
1596
 
1529
- return contextModeInstructions ? promptWithContextMode : basePrompt;
1597
+ return promptWithContextMode;
1530
1598
  }
1531
1599
 
1532
1600
  /**
@@ -2918,13 +2986,23 @@ export async function buildValidateMilestonePrompt(
2918
2986
  if (isDbAvailable()) {
2919
2987
  const milestone = getMilestone(mid);
2920
2988
  if (milestone) {
2989
+ const escapeCell = (value: string) =>
2990
+ value.replace(/[\\|]/g, (char) => `\\${char}`).replace(/\r?\n/g, " ");
2921
2991
  const classes: string[] = [];
2922
- if (milestone.verification_contract) classes.push(`- **Contract:** ${milestone.verification_contract}`);
2923
- if (milestone.verification_integration) classes.push(`- **Integration:** ${milestone.verification_integration}`);
2924
- if (milestone.verification_operational) classes.push(`- **Operational:** ${milestone.verification_operational}`);
2925
- if (milestone.verification_uat) classes.push(`- **UAT:** ${milestone.verification_uat}`);
2992
+ if (milestone.verification_contract) classes.push(`| Contract | ${escapeCell(milestone.verification_contract)} |`);
2993
+ if (milestone.verification_integration) classes.push(`| Integration | ${escapeCell(milestone.verification_integration)} |`);
2994
+ if (milestone.verification_operational) classes.push(`| Operational | ${escapeCell(milestone.verification_operational)} |`);
2995
+ if (milestone.verification_uat) classes.push(`| UAT | ${escapeCell(milestone.verification_uat)} |`);
2926
2996
  if (classes.length > 0) {
2927
- const verificationClasses = `### Verification Classes (from planning)\n\nThese verification tiers were defined during milestone planning. Each non-empty class must be checked for evidence during validation.\n\n${classes.join("\n")}`;
2997
+ const verificationClasses = [
2998
+ "### Verification Classes (from planning)",
2999
+ "",
3000
+ "These verification tiers were defined during milestone planning. Every row in this table must appear in `verificationClasses` with the same canonical class name.",
3001
+ "",
3002
+ "| Class | Planned Check |",
3003
+ "| --- | --- |",
3004
+ ...classes,
3005
+ ].join("\n");
2928
3006
  inlined.push(verificationClasses);
2929
3007
  trackPromptContext(contextTelemetry, "verification-classes", "inline", verificationClasses);
2930
3008
  }
@@ -42,6 +42,10 @@ export function isAutoPaused(): boolean {
42
42
  return autoSession.paused;
43
43
  }
44
44
 
45
+ export function isAutoCompletionStopInProgress(): boolean {
46
+ return autoSession.completionStopInProgress;
47
+ }
48
+
45
49
  export function markToolStart(toolCallId: string, toolName?: string): void {
46
50
  markTrackedToolStart(toolCallId, autoSession.active, toolName);
47
51
  }
@@ -94,7 +94,7 @@ export function clearInFlightTools(): void {
94
94
  * from the tool handler. When these errors occur, retrying the same unit will
95
95
  * produce the same failure, so the retry loop must be broken.
96
96
  */
97
- const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}'(?: after property value)?(?: in JSON)?|Unexpected end of JSON|Unexpected token.*in JSON|does not provide an export named|Named export .* not found|Cannot find module|ERR_MODULE_NOT_FOUND|ERR_MODULE_NOT_EXPORTED|ERR_PACKAGE_PATH_NOT_EXPORTED/i;
97
+ const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Input validation error|Invalid arguments for tool|MCP error -32602|No such tool available|Expected ',' or '\}'(?: after property value)?(?: in JSON)?|Unexpected end of JSON|Unexpected token.*in JSON|does not provide an export named|Named export .* not found|Cannot find module|ERR_MODULE_NOT_FOUND|ERR_MODULE_NOT_EXPORTED|ERR_PACKAGE_PATH_NOT_EXPORTED/i;
98
98
  const DETERMINISTIC_POLICY_ERROR_RE = /(?:^|\b)(?:HARD BLOCK:|Blocked: \/gsd queue is a planning tool|Direct writes to \.gsd\/STATE\.md and \.gsd\/gsd\.db are blocked|This is a mechanical gate)/i;
99
99
 
100
100
  /**
@@ -2311,10 +2311,20 @@ export function createWiredAutoOrchestrationModule(
2311
2311
  async reconcileBeforeDispatch() {
2312
2312
  const activeBasePath = getLiveDispatchBasePath();
2313
2313
  const result = await reconcileBeforeDispatch(activeBasePath);
2314
- if (result.blockers.length > 0) {
2314
+ // Failure-path summaries written by gsd_summary_save create
2315
+ // artifact-db-status-divergence blockers for tasks that are still
2316
+ // pending (gsd_task_complete never ran). These tasks can still be
2317
+ // dispatched and the drift self-heals once they complete successfully.
2318
+ const hardBlockers = result.blockers.filter(
2319
+ (b) =>
2320
+ !b.includes("has SUMMARY artifact while DB status is") &&
2321
+ !b.includes("has SUMMARY on disk while DB status is") &&
2322
+ !b.includes("has task SUMMARY artifacts but no DB tasks"),
2323
+ );
2324
+ if (hardBlockers.length > 0) {
2315
2325
  return {
2316
2326
  ok: false,
2317
- reason: result.blockers[0],
2327
+ reason: hardBlockers[0],
2318
2328
  stateSnapshot: result.stateSnapshot,
2319
2329
  };
2320
2330
  }