@opengsd/gsd-pi 1.1.1-dev.74e8dd1 → 1.1.1-dev.75048e7

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 (329) 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 +16 -0
  12. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
  13. package/dist/resources/extensions/gsd/auto-prompts.js +63 -22
  14. package/dist/resources/extensions/gsd/auto-recovery.js +3 -4
  15. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  16. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  17. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +18 -66
  18. package/dist/resources/extensions/gsd/auto-worktree.js +18 -5
  19. package/dist/resources/extensions/gsd/auto.js +9 -2
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +20 -14
  21. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +28 -13
  22. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +18 -29
  23. package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
  24. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +61 -0
  25. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  26. package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
  27. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
  28. package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
  29. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  30. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
  31. package/dist/resources/extensions/gsd/escalation.js +4 -4
  32. package/dist/resources/extensions/gsd/forensics.js +74 -2
  33. package/dist/resources/extensions/gsd/gsd-db.js +5 -2
  34. package/dist/resources/extensions/gsd/guided-flow.js +118 -175
  35. package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
  36. package/dist/resources/extensions/gsd/memory-store.js +4 -1
  37. package/dist/resources/extensions/gsd/milestone-closeout.js +3 -1
  38. package/dist/resources/extensions/gsd/pending-auto-start.js +0 -1
  39. package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
  40. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  41. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  42. package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
  43. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  44. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  47. package/dist/resources/extensions/gsd/prompts/run-uat.md +25 -21
  48. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  49. package/dist/resources/extensions/gsd/recovery-classification.js +20 -0
  50. package/dist/resources/extensions/gsd/rule-registry.js +428 -52
  51. package/dist/resources/extensions/gsd/state.js +2 -2
  52. package/dist/resources/extensions/gsd/templates/plan.md +3 -1
  53. package/dist/resources/extensions/gsd/tool-contract.js +5 -0
  54. package/dist/resources/extensions/gsd/tool-presentation-plan.js +30 -7
  55. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
  56. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
  57. package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
  58. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +132 -18
  59. package/dist/resources/extensions/gsd/unit-tool-contracts.js +169 -0
  60. package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
  61. package/dist/resources/extensions/gsd/verification-gate.js +72 -1
  62. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -75
  63. package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
  64. package/dist/rtk.d.ts +7 -1
  65. package/dist/rtk.js +27 -11
  66. package/dist/update-check.d.ts +15 -1
  67. package/dist/update-check.js +87 -12
  68. package/dist/update-cmd.d.ts +1 -0
  69. package/dist/update-cmd.js +53 -2
  70. package/dist/web/standalone/.next/BUILD_ID +1 -1
  71. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  72. package/dist/web/standalone/.next/build-manifest.json +2 -2
  73. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  74. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/index.html +1 -1
  92. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  99. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  100. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  102. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  103. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  104. package/package.json +4 -2
  105. package/packages/cloud-mcp-gateway/package.json +2 -2
  106. package/packages/contracts/package.json +1 -1
  107. package/packages/daemon/package.json +4 -4
  108. package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
  109. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  110. package/packages/gsd-agent-core/dist/agent-session.js +32 -0
  111. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  112. package/packages/gsd-agent-core/dist/index.d.ts +1 -0
  113. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  114. package/packages/gsd-agent-core/dist/index.js +1 -0
  115. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  116. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
  117. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
  118. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
  119. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
  120. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
  121. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  122. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  123. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  124. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
  125. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  126. package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
  127. package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
  128. package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
  129. package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
  130. package/packages/gsd-agent-core/package.json +6 -6
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +20 -0
  140. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
  143. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  144. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
  145. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
  146. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
  147. package/packages/gsd-agent-modes/package.json +7 -7
  148. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  149. package/packages/mcp-server/dist/remote-questions.js +23 -9
  150. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  151. package/packages/mcp-server/dist/workflow-tools.js +2 -2
  152. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  153. package/packages/mcp-server/package.json +3 -3
  154. package/packages/native/package.json +1 -1
  155. package/packages/pi-agent-core/dist/agent-loop.js +42 -3
  156. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  157. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  158. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  159. package/packages/pi-agent-core/dist/agent.js +2 -0
  160. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  161. package/packages/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
  162. package/packages/pi-agent-core/dist/harness/agent-harness.js +3 -1
  163. package/packages/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
  164. package/packages/pi-agent-core/dist/harness/types.d.ts +1 -0
  165. package/packages/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  166. package/packages/pi-agent-core/dist/harness/types.js.map +1 -1
  167. package/packages/pi-agent-core/dist/types.d.ts +6 -1
  168. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  169. package/packages/pi-agent-core/dist/types.js.map +1 -1
  170. package/packages/pi-agent-core/package.json +1 -1
  171. package/packages/pi-ai/dist/api-registry.d.ts +2 -0
  172. package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
  173. package/packages/pi-ai/dist/api-registry.js +23 -0
  174. package/packages/pi-ai/dist/api-registry.js.map +1 -1
  175. package/packages/pi-ai/dist/models.generated.d.ts +74 -23
  176. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  177. package/packages/pi-ai/dist/models.generated.js +82 -31
  178. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  179. package/packages/pi-ai/dist/stream.js +6 -6
  180. package/packages/pi-ai/dist/stream.js.map +1 -1
  181. package/packages/pi-ai/package.json +1 -1
  182. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +3 -0
  183. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
  187. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/tools/bash.js +2 -2
  189. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/tools/edit.js +3 -2
  192. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts +1 -0
  194. package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/tools/render-utils.js +6 -0
  196. package/packages/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/tools/write.js +3 -2
  199. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  200. package/packages/pi-coding-agent/package.json +7 -7
  201. package/packages/pi-tui/package.json +1 -1
  202. package/packages/rpc-client/package.json +2 -2
  203. package/pkg/package.json +1 -1
  204. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
  205. package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
  206. package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
  207. package/src/resources/extensions/browser-tools/index.ts +60 -9
  208. package/src/resources/extensions/browser-tools/package.json +5 -1
  209. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
  210. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
  211. package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
  212. package/src/resources/extensions/gsd/auto-dashboard.ts +82 -14
  213. package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
  214. package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
  215. package/src/resources/extensions/gsd/auto-prompts.ts +97 -15
  216. package/src/resources/extensions/gsd/auto-recovery.ts +3 -3
  217. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
  218. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  219. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +43 -74
  220. package/src/resources/extensions/gsd/auto-worktree.ts +23 -5
  221. package/src/resources/extensions/gsd/auto.ts +12 -2
  222. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +20 -14
  223. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +32 -13
  224. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +50 -54
  225. package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
  226. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +137 -0
  227. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
  228. package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
  229. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
  230. package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
  231. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  232. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
  233. package/src/resources/extensions/gsd/escalation.ts +4 -4
  234. package/src/resources/extensions/gsd/forensics.ts +99 -5
  235. package/src/resources/extensions/gsd/gsd-db.ts +5 -2
  236. package/src/resources/extensions/gsd/guided-flow.ts +214 -216
  237. package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
  238. package/src/resources/extensions/gsd/memory-store.ts +4 -1
  239. package/src/resources/extensions/gsd/milestone-closeout.ts +3 -1
  240. package/src/resources/extensions/gsd/pending-auto-start.ts +0 -2
  241. package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
  242. package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
  243. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  244. package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
  245. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  246. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  247. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  248. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  249. package/src/resources/extensions/gsd/prompts/run-uat.md +25 -21
  250. package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  251. package/src/resources/extensions/gsd/recovery-classification.ts +20 -0
  252. package/src/resources/extensions/gsd/rule-registry.ts +558 -58
  253. package/src/resources/extensions/gsd/rule-types.ts +2 -0
  254. package/src/resources/extensions/gsd/state.ts +2 -2
  255. package/src/resources/extensions/gsd/templates/plan.md +3 -1
  256. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +105 -4
  257. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
  258. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +10 -2
  259. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +4 -1
  260. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +12 -2
  261. package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
  262. package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +9 -15
  263. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +26 -16
  264. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +21 -0
  265. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
  266. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  267. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
  268. package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
  269. package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
  270. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +40 -1
  271. package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
  272. package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
  273. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
  274. package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
  275. package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
  276. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +31 -79
  277. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
  278. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
  279. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +5 -3
  280. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +40 -4
  281. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
  282. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
  283. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +8 -0
  284. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +16 -0
  285. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +72 -10
  286. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
  287. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
  288. package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
  289. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +63 -0
  290. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +10 -1
  291. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +9 -1
  292. package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
  293. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
  294. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
  295. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
  296. package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
  297. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +73 -1
  298. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
  299. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
  300. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +44 -0
  301. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
  302. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +4 -0
  303. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +36 -0
  304. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
  305. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +139 -0
  306. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -4
  307. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  308. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
  309. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
  310. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
  311. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
  312. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +410 -0
  313. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
  314. package/src/resources/extensions/gsd/tool-contract.ts +6 -0
  315. package/src/resources/extensions/gsd/tool-presentation-plan.ts +63 -7
  316. package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
  317. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
  318. package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
  319. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +163 -20
  320. package/src/resources/extensions/gsd/types.ts +69 -5
  321. package/src/resources/extensions/gsd/unit-tool-contracts.ts +186 -0
  322. package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
  323. package/src/resources/extensions/gsd/verification-gate.ts +87 -1
  324. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -75
  325. package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
  326. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +0 -246
  327. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +0 -218
  328. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → h4TGni4xJzlZjGkxaT6uU}/_buildManifest.js +0 -0
  329. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → h4TGni4xJzlZjGkxaT6uU}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { loadStoredEnvKeys } from './wizard.js';
7
7
  import { migratePiCredentials } from './pi-migration.js';
8
8
  import { shouldRunOnboarding, runOnboarding } from './onboarding.js';
9
9
  import chalk from 'chalk';
10
- import { checkForUpdates } from './update-check.js';
10
+ import { checkForGsdBrowserUpdates, checkForUpdates } from './update-check.js';
11
11
  import { shouldBypassManagedResourceMismatchGate } from './cli-policy.js';
12
12
  import { shouldRedirectAutoToHeadless } from './cli-auto-routing.js';
13
13
  import { printHelp, printSubcommandHelp } from './help-text.js';
@@ -207,7 +207,7 @@ function ensureRtkBootstrap() {
207
207
  // actually upgrade out of the broken state. See shouldBypassManagedResourceMismatchGate.
208
208
  if (shouldBypassManagedResourceMismatchGate(cliFlags.messages[0])) {
209
209
  const { runUpdate } = await import('./update-cmd.js');
210
- await runUpdate();
210
+ await runUpdate({ target: cliFlags.messages[1] });
211
211
  process.exit(0);
212
212
  }
213
213
  // ---------------------------------------------------------------------------
@@ -550,6 +550,7 @@ if (!isPrintMode && shouldRunOnboarding(authStorage, settingsManager.getDefaultP
550
550
  // available (using cached data or a background fetch) without blocking the TUI.
551
551
  if (!isPrintMode) {
552
552
  checkForUpdates().catch(() => { });
553
+ checkForGsdBrowserUpdates().catch(() => { });
553
554
  }
554
555
  // Warn if terminal is too narrow for readable output
555
556
  if (!isPrintMode && process.stdout.columns && process.stdout.columns < 40) {
package/dist/help-text.js CHANGED
@@ -19,18 +19,22 @@ const SUBCOMMAND_HELP = {
19
19
  'and other OpenAI-compatible endpoints), see docs/providers.md.',
20
20
  ].join('\n'),
21
21
  update: [
22
- 'Usage: gsd update',
22
+ 'Usage: gsd update [browser]',
23
23
  '',
24
- 'Update GSD to the latest version.',
24
+ 'Update GSD to the latest version, or update browser automation only.',
25
25
  '',
26
- 'Equivalent to: npm install -g @opengsd/gsd-pi@latest',
26
+ 'Examples:',
27
+ ' gsd update',
28
+ ' gsd update browser',
27
29
  ].join('\n'),
28
30
  upgrade: [
29
- 'Usage: gsd upgrade',
31
+ 'Usage: gsd upgrade [browser]',
30
32
  '',
31
- 'Upgrade GSD to the latest @opengsd package.',
33
+ 'Alias for update. Upgrade GSD, or upgrade browser automation only.',
32
34
  '',
33
- 'Equivalent to: npm install -g @opengsd/gsd-pi@latest',
35
+ 'Examples:',
36
+ ' gsd upgrade',
37
+ ' gsd upgrade browser',
34
38
  ].join('\n'),
35
39
  sessions: [
36
40
  'Usage: gsd sessions',
@@ -1 +1 @@
1
- 5f6f7839b5420a75
1
+ 5403bc93a34728ed
@@ -0,0 +1,495 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import { Type } from "@sinclair/typebox";
4
+ import { resolveGsdBrowserMcpLaunchConfig } from "../../shared/gsd-browser-cli.js";
5
+ import { buildMcpChildEnv } from "../../mcp-client/manager.js";
6
+ const connections = new Map();
7
+ const pendingConnections = new Map();
8
+ const DEFAULT_MAX_LINES = 2_000;
9
+ const DEFAULT_MAX_BYTES = 50 * 1024;
10
+ const AssertionCheck = Type.Object({
11
+ kind: Type.String({ description: "Assertion kind, e.g. url_contains, text_visible, selector_visible, no_console_errors, no_failed_requests." }),
12
+ selector: Type.Optional(Type.String()),
13
+ text: Type.Optional(Type.String()),
14
+ value: Type.Optional(Type.String()),
15
+ checked: Type.Optional(Type.Boolean()),
16
+ sinceActionId: Type.Optional(Type.Number()),
17
+ }, { additionalProperties: true });
18
+ const BatchStep = Type.Object({
19
+ action: Type.String({ description: "Step action, e.g. navigate, click, type, wait_for, assert, click_ref, fill_ref." }),
20
+ selector: Type.Optional(Type.String()),
21
+ text: Type.Optional(Type.String()),
22
+ url: Type.Optional(Type.String()),
23
+ key: Type.Optional(Type.String()),
24
+ condition: Type.Optional(Type.String()),
25
+ value: Type.Optional(Type.String()),
26
+ threshold: Type.Optional(Type.String()),
27
+ timeout: Type.Optional(Type.Number()),
28
+ clearFirst: Type.Optional(Type.Boolean()),
29
+ submit: Type.Optional(Type.Boolean()),
30
+ ref: Type.Optional(Type.String()),
31
+ checks: Type.Optional(Type.Array(AssertionCheck)),
32
+ }, { additionalProperties: true });
33
+ export const MANAGED_GSD_BROWSER_TOOL_NAMES = [
34
+ "browser_navigate",
35
+ "browser_click",
36
+ "browser_type",
37
+ "browser_fill_form",
38
+ "browser_click_ref",
39
+ "browser_fill_ref",
40
+ "browser_wait_for",
41
+ "browser_assert",
42
+ "browser_verify",
43
+ "browser_screenshot",
44
+ "browser_snapshot_refs",
45
+ "browser_find",
46
+ "browser_get_console_logs",
47
+ "browser_get_network_logs",
48
+ "browser_evaluate",
49
+ "browser_reload",
50
+ "browser_batch",
51
+ "browser_act",
52
+ ];
53
+ const MANAGED_BROWSER_TOOLS = [
54
+ {
55
+ name: "browser_navigate",
56
+ label: "Browser Navigate",
57
+ description: "Navigate the managed gsd-browser session to a URL and return page state. Use for local web app verification and UAT evidence.",
58
+ parameters: Type.Object({
59
+ url: Type.String({ description: "URL to navigate to, e.g. http://localhost:3000." }),
60
+ screenshot: Type.Optional(Type.Boolean({ description: "Capture screenshot evidence when supported." })),
61
+ }, { additionalProperties: true }),
62
+ },
63
+ {
64
+ name: "browser_click",
65
+ label: "Browser Click",
66
+ description: "Click an element in the managed gsd-browser session by selector or coordinates.",
67
+ parameters: Type.Object({
68
+ selector: Type.Optional(Type.String({ description: "CSS selector to click." })),
69
+ x: Type.Optional(Type.Number({ description: "X coordinate to click." })),
70
+ y: Type.Optional(Type.Number({ description: "Y coordinate to click." })),
71
+ }, { additionalProperties: true }),
72
+ },
73
+ {
74
+ name: "browser_type",
75
+ label: "Browser Type",
76
+ description: "Type or fill text into an input in the managed gsd-browser session.",
77
+ parameters: Type.Object({
78
+ selector: Type.Optional(Type.String({ description: "CSS selector of the input to type into." })),
79
+ text: Type.String({ description: "Text to enter." }),
80
+ clearFirst: Type.Optional(Type.Boolean({ description: "Clear existing text first." })),
81
+ submit: Type.Optional(Type.Boolean({ description: "Press Enter after typing." })),
82
+ slowly: Type.Optional(Type.Boolean({ description: "Type character by character." })),
83
+ }, { additionalProperties: true }),
84
+ },
85
+ {
86
+ name: "browser_fill_form",
87
+ label: "Browser Fill Form",
88
+ description: "Fill a form in the managed gsd-browser session using field labels, names, placeholders, or aria labels.",
89
+ parameters: Type.Object({
90
+ selector: Type.Optional(Type.String({ description: "CSS selector targeting the form." })),
91
+ values: Type.Record(Type.String(), Type.String(), { description: "Field identifier to value mapping." }),
92
+ submit: Type.Optional(Type.Boolean({ description: "Submit the form after filling." })),
93
+ }, { additionalProperties: true }),
94
+ },
95
+ {
96
+ name: "browser_click_ref",
97
+ label: "Browser Click Ref",
98
+ description: "Click a versioned ref from the latest gsd-browser snapshot.",
99
+ parameters: Type.Object({
100
+ ref: Type.String({ description: "Versioned ref, e.g. @v3:e2." }),
101
+ }, { additionalProperties: true }),
102
+ },
103
+ {
104
+ name: "browser_fill_ref",
105
+ label: "Browser Fill Ref",
106
+ description: "Fill text into an input-like versioned ref from the latest gsd-browser snapshot.",
107
+ parameters: Type.Object({
108
+ ref: Type.String({ description: "Versioned ref, e.g. @v3:e1." }),
109
+ text: Type.String({ description: "Text to enter." }),
110
+ clearFirst: Type.Optional(Type.Boolean({ description: "Clear existing text first." })),
111
+ submit: Type.Optional(Type.Boolean({ description: "Press Enter after filling." })),
112
+ slowly: Type.Optional(Type.Boolean({ description: "Type character by character." })),
113
+ }, { additionalProperties: true }),
114
+ },
115
+ {
116
+ name: "browser_wait_for",
117
+ label: "Browser Wait For",
118
+ description: "Wait for a browser condition such as network idle, selector visibility, text visibility, or URL change.",
119
+ parameters: Type.Object({
120
+ condition: Type.String({ description: "Condition, e.g. network_idle, selector_visible, text_visible, url_contains." }),
121
+ value: Type.Optional(Type.String({ description: "Selector, text, URL substring, or delay value depending on condition." })),
122
+ threshold: Type.Optional(Type.String({ description: "Threshold expression for count-based conditions." })),
123
+ timeout: Type.Optional(Type.Number({ description: "Maximum milliseconds to wait." })),
124
+ }, { additionalProperties: true }),
125
+ },
126
+ {
127
+ name: "browser_assert",
128
+ label: "Browser Assert",
129
+ description: "Run explicit browser assertions and return structured PASS/FAIL evidence.",
130
+ promptGuidelines: [
131
+ "Prefer browser_assert for final browser verification instead of inferring success from summaries.",
132
+ "Use checks for URL, text, selector state, value, and browser diagnostics whenever those signals are available.",
133
+ ],
134
+ parameters: Type.Object({
135
+ checks: Type.Array(AssertionCheck),
136
+ }, { additionalProperties: true }),
137
+ },
138
+ {
139
+ name: "browser_verify",
140
+ label: "Browser Verify",
141
+ description: "Run a structured browser verification flow and return evidence from the managed gsd-browser session.",
142
+ parameters: Type.Object({
143
+ url: Type.String({ description: "URL to verify." }),
144
+ checks: Type.Array(Type.Object({
145
+ description: Type.String({ description: "What this check verifies." }),
146
+ selector: Type.Optional(Type.String()),
147
+ expectedText: Type.Optional(Type.String()),
148
+ expectedVisible: Type.Optional(Type.Boolean()),
149
+ screenshot: Type.Optional(Type.Boolean()),
150
+ }, { additionalProperties: true })),
151
+ timeout: Type.Optional(Type.Number({ description: "Navigation timeout in milliseconds." })),
152
+ }, { additionalProperties: true }),
153
+ },
154
+ {
155
+ name: "browser_screenshot",
156
+ label: "Browser Screenshot",
157
+ description: "Capture browser screenshot evidence from the managed gsd-browser session.",
158
+ compatibility: { producesImages: true },
159
+ parameters: Type.Object({
160
+ fullPage: Type.Optional(Type.Boolean({ description: "Capture the full scrollable page." })),
161
+ selector: Type.Optional(Type.String({ description: "CSS selector to crop." })),
162
+ quality: Type.Optional(Type.Number({ description: "JPEG quality when supported." })),
163
+ }, { additionalProperties: true }),
164
+ },
165
+ {
166
+ name: "browser_snapshot_refs",
167
+ mcpTools: ["browser_snapshot", "browser_snapshot_refs"],
168
+ label: "Browser Snapshot Refs",
169
+ description: "Capture a compact gsd-browser snapshot with versioned refs for reliable interaction.",
170
+ parameters: Type.Object({
171
+ selector: Type.Optional(Type.String({ description: "Optional CSS selector scope." })),
172
+ interactiveOnly: Type.Optional(Type.Boolean({ description: "Compatibility flag; use mode for gsd-browser filtering." })),
173
+ limit: Type.Optional(Type.Number({ description: "Maximum elements to include." })),
174
+ mode: Type.Optional(Type.String({ description: "Snapshot mode: interactive, form, dialog, navigation, errors, headings, visible_only." })),
175
+ }, { additionalProperties: true }),
176
+ },
177
+ {
178
+ name: "browser_find",
179
+ mcpTools: ["browser_find_element", "browser_find"],
180
+ label: "Browser Find",
181
+ description: "Find elements by text, role, or selector in the managed gsd-browser session.",
182
+ parameters: Type.Object({
183
+ text: Type.Optional(Type.String({ description: "Visible text to find." })),
184
+ role: Type.Optional(Type.String({ description: "ARIA role to filter by." })),
185
+ selector: Type.Optional(Type.String({ description: "CSS selector to scope or match." })),
186
+ limit: Type.Optional(Type.Number({ description: "Maximum results to return." })),
187
+ }, { additionalProperties: true }),
188
+ },
189
+ {
190
+ name: "browser_get_console_logs",
191
+ mcpTools: ["browser_console", "browser_get_console_logs"],
192
+ label: "Browser Console Logs",
193
+ description: "Return buffered console logs and JavaScript errors from the managed gsd-browser session.",
194
+ parameters: Type.Object({
195
+ clear: Type.Optional(Type.Boolean({ description: "Clear the buffer after reading logs." })),
196
+ }, { additionalProperties: true }),
197
+ },
198
+ {
199
+ name: "browser_get_network_logs",
200
+ mcpTools: ["browser_network", "browser_get_network_logs"],
201
+ label: "Browser Network Logs",
202
+ description: "Return buffered network requests and responses from the managed gsd-browser session.",
203
+ parameters: Type.Object({
204
+ clear: Type.Optional(Type.Boolean({ description: "Clear the buffer after reading logs." })),
205
+ filter: Type.Optional(Type.String({ description: "Filter, e.g. all, errors, fetch-xhr." })),
206
+ }, { additionalProperties: true }),
207
+ },
208
+ {
209
+ name: "browser_evaluate",
210
+ mcpTools: ["browser_eval", "browser_evaluate"],
211
+ label: "Browser Evaluate",
212
+ description: "Evaluate a JavaScript expression in the managed gsd-browser page context.",
213
+ parameters: Type.Object({
214
+ expression: Type.String({ description: "JavaScript expression to evaluate." }),
215
+ }, { additionalProperties: true }),
216
+ },
217
+ {
218
+ name: "browser_reload",
219
+ label: "Browser Reload",
220
+ description: "Reload the current page in the managed gsd-browser session.",
221
+ parameters: Type.Object({}, { additionalProperties: true }),
222
+ },
223
+ {
224
+ name: "browser_batch",
225
+ label: "Browser Batch",
226
+ description: "Execute multiple explicit browser steps through the managed gsd-browser session in one call.",
227
+ promptGuidelines: [
228
+ "Use browser_batch for obvious low-risk sequences like navigate, snapshot, click, type, wait, assert.",
229
+ "Keep browser_batch steps explicit; do not use it as a speculative planner.",
230
+ ],
231
+ parameters: Type.Object({
232
+ steps: Type.Array(BatchStep),
233
+ stopOnFailure: Type.Optional(Type.Boolean({ description: "Stop after the first failing step." })),
234
+ finalSummaryOnly: Type.Optional(Type.Boolean({ description: "Return only the compact final summary." })),
235
+ }, { additionalProperties: true }),
236
+ },
237
+ {
238
+ name: "browser_act",
239
+ label: "Browser Act",
240
+ description: "Execute a semantic browser action through gsd-browser, such as primary_cta, submit_form, or close_dialog.",
241
+ parameters: Type.Object({
242
+ intent: Type.String({ description: "Semantic intent, e.g. submit_form, close_dialog, primary_cta, search_field, accept_cookies." }),
243
+ scope: Type.Optional(Type.String({ description: "CSS selector to narrow the search area." })),
244
+ }, { additionalProperties: true }),
245
+ },
246
+ ];
247
+ function resolveProjectRoot(ctx) {
248
+ return ctx?.cwd || process.cwd();
249
+ }
250
+ function resolveManagedSessionSuffix(ctx) {
251
+ const explicit = process.env.GSD_BROWSER_SESSION_SUFFIX?.trim() || process.env.GSD_BROWSER_SESSION_ID?.trim();
252
+ if (explicit)
253
+ return explicit;
254
+ try {
255
+ const sessionId = ctx?.sessionManager?.getSessionId?.();
256
+ if (sessionId)
257
+ return `pi-${sessionId.slice(0, 12)}`;
258
+ }
259
+ catch {
260
+ // Fall back to pid below when session metadata is unavailable.
261
+ }
262
+ return `pi-${process.pid}`;
263
+ }
264
+ function buildConnectionKey(launch) {
265
+ return JSON.stringify({
266
+ command: launch.command,
267
+ args: launch.args,
268
+ cwd: launch.cwd,
269
+ env: launch.env ?? {},
270
+ });
271
+ }
272
+ async function connectManagedGsdBrowser(launch, signal) {
273
+ const client = new Client({ name: "gsd-pi-browser-tools", version: "1.0.0" });
274
+ const transport = new StdioClientTransport({
275
+ command: launch.command,
276
+ args: launch.args,
277
+ env: buildMcpChildEnv(launch.env),
278
+ cwd: launch.cwd,
279
+ stderr: "pipe",
280
+ });
281
+ try {
282
+ await client.connect(transport, { signal, timeout: 30000 });
283
+ return { client, transport, launch };
284
+ }
285
+ catch (error) {
286
+ try {
287
+ await transport.close();
288
+ }
289
+ catch {
290
+ // Best-effort cleanup after a failed or aborted connection attempt.
291
+ }
292
+ try {
293
+ await client.close();
294
+ }
295
+ catch {
296
+ // Best-effort cleanup after a failed or aborted connection attempt.
297
+ }
298
+ throw error;
299
+ }
300
+ }
301
+ async function getOrConnectManagedGsdBrowser(ctx, signal) {
302
+ const launch = resolveGsdBrowserMcpLaunchConfig(resolveProjectRoot(ctx), process.env, {
303
+ sessionSuffix: resolveManagedSessionSuffix(ctx),
304
+ });
305
+ const key = buildConnectionKey(launch);
306
+ const existing = connections.get(key);
307
+ if (existing)
308
+ return existing;
309
+ const pending = pendingConnections.get(key);
310
+ if (pending)
311
+ return pending;
312
+ const connectionPromise = connectManagedGsdBrowser(launch, signal);
313
+ pendingConnections.set(key, connectionPromise);
314
+ try {
315
+ const connection = await connectionPromise;
316
+ connections.set(key, connection);
317
+ return connection;
318
+ }
319
+ finally {
320
+ pendingConnections.delete(key);
321
+ }
322
+ }
323
+ function isUnknownMcpToolError(error) {
324
+ const message = error instanceof Error ? error.message : String(error);
325
+ return /unknown tool|tool .*not found|tool not found|not registered|does not exist/i.test(message);
326
+ }
327
+ function normalizeManagedArgs(piToolName, args) {
328
+ if (piToolName === "browser_snapshot_refs") {
329
+ const { interactiveOnly: _interactiveOnly, ...snapshotArgs } = args;
330
+ return snapshotArgs;
331
+ }
332
+ return args;
333
+ }
334
+ function serializeMcpContent(contentItems) {
335
+ const imageItems = [];
336
+ const textParts = [];
337
+ for (const item of contentItems) {
338
+ if (item.type === "text") {
339
+ textParts.push(item.text ?? "");
340
+ continue;
341
+ }
342
+ if (item.type === "image" && typeof item.data === "string" && typeof item.mimeType === "string") {
343
+ imageItems.push({ type: "image", data: item.data, mimeType: item.mimeType });
344
+ textParts.push(`[image evidence: ${item.mimeType}]`);
345
+ continue;
346
+ }
347
+ textParts.push(JSON.stringify(item));
348
+ }
349
+ const rawText = textParts.filter((part) => part.length > 0).join("\n");
350
+ const truncation = truncateHeadText(rawText, { maxLines: DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES });
351
+ let finalText = truncation.content;
352
+ if (truncation.truncated) {
353
+ finalText += `\n\n[Output truncated: ${truncation.outputLines}/${truncation.totalLines} lines (${formatByteSize(truncation.outputBytes)} of ${formatByteSize(truncation.totalBytes)})]`;
354
+ }
355
+ let content;
356
+ if (finalText) {
357
+ content = [{ type: "text", text: finalText }, ...imageItems];
358
+ }
359
+ else if (imageItems.length > 0) {
360
+ content = imageItems;
361
+ }
362
+ else {
363
+ content = [{ type: "text", text: "gsd-browser returned no content." }];
364
+ }
365
+ return {
366
+ content,
367
+ truncated: truncation.truncated,
368
+ outputLines: truncation.outputLines,
369
+ outputBytes: truncation.outputBytes,
370
+ };
371
+ }
372
+ function formatByteSize(bytes) {
373
+ if (bytes < 1024)
374
+ return `${bytes} B`;
375
+ if (bytes < 1024 * 1024)
376
+ return `${(bytes / 1024).toFixed(1)} KB`;
377
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
378
+ }
379
+ function truncateHeadText(text, options) {
380
+ const totalBytes = Buffer.byteLength(text, "utf-8");
381
+ const allLines = text.split(/\r?\n/);
382
+ const totalLines = text.length === 0 ? 0 : allLines.length;
383
+ let content = allLines.slice(0, options.maxLines).join("\n");
384
+ while (Buffer.byteLength(content, "utf-8") > options.maxBytes && content.length > 0) {
385
+ content = content.slice(0, Math.max(0, content.length - 1024));
386
+ }
387
+ const outputBytes = Buffer.byteLength(content, "utf-8");
388
+ const outputLines = content.length === 0 ? 0 : content.split(/\r?\n/).length;
389
+ return {
390
+ content,
391
+ truncated: outputLines < totalLines || outputBytes < totalBytes,
392
+ outputLines,
393
+ totalLines,
394
+ outputBytes,
395
+ totalBytes,
396
+ };
397
+ }
398
+ async function callManagedGsdBrowserTool(piToolName, mcpTools, args, options) {
399
+ const connection = await getOrConnectManagedGsdBrowser(options.ctx, options.signal);
400
+ const normalizedArgs = normalizeManagedArgs(piToolName, args);
401
+ let lastError;
402
+ for (const mcpTool of mcpTools) {
403
+ try {
404
+ const result = await connection.client.callTool({ name: mcpTool, arguments: normalizedArgs }, undefined, { signal: options.signal, timeout: 60000 });
405
+ const contentItems = Array.isArray(result.content) ? result.content : [];
406
+ const serialized = serializeMcpContent(contentItems);
407
+ return {
408
+ content: serialized.content,
409
+ details: {
410
+ engine: "gsd-browser",
411
+ server: connection.launch.serverName,
412
+ tool: piToolName,
413
+ mcpTool,
414
+ sessionName: connection.launch.sessionName,
415
+ projectRoot: connection.launch.projectRoot,
416
+ truncated: serialized.truncated,
417
+ outputLines: serialized.outputLines,
418
+ outputBytes: serialized.outputBytes,
419
+ structuredContent: result.structuredContent,
420
+ mcpIsError: Boolean(result.isError),
421
+ },
422
+ isError: Boolean(result.isError),
423
+ };
424
+ }
425
+ catch (error) {
426
+ lastError = error;
427
+ if (!isUnknownMcpToolError(error))
428
+ break;
429
+ }
430
+ }
431
+ throw lastError;
432
+ }
433
+ function formatManagedBrowserError(toolName, error) {
434
+ const message = error instanceof Error ? error.message : String(error);
435
+ return [
436
+ `gsd-browser engine or tool unavailable for ${toolName}: ${message}`,
437
+ "",
438
+ "GSD browser automation now uses the managed gsd-browser engine by default.",
439
+ "Run /gsd doctor or reinstall dependencies so @opengsd/gsd-browser is available.",
440
+ "Set GSD_BROWSER_ENGINE=legacy only when you intentionally need the Playwright compatibility engine.",
441
+ ].join("\n");
442
+ }
443
+ export function registerManagedGsdBrowserTools(pi) {
444
+ for (const tool of MANAGED_BROWSER_TOOLS) {
445
+ pi.registerTool({
446
+ name: tool.name,
447
+ label: tool.label,
448
+ description: tool.description,
449
+ ...(tool.promptGuidelines ? { promptGuidelines: tool.promptGuidelines } : {}),
450
+ ...(tool.compatibility ? { compatibility: tool.compatibility } : {}),
451
+ parameters: tool.parameters,
452
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
453
+ try {
454
+ return await callManagedGsdBrowserTool(tool.name, tool.mcpTools ?? [tool.name], params, { signal, ctx });
455
+ }
456
+ catch (error) {
457
+ const message = formatManagedBrowserError(tool.name, error);
458
+ return {
459
+ content: [{ type: "text", text: message }],
460
+ details: {
461
+ engine: "gsd-browser",
462
+ server: "gsd-browser",
463
+ tool: tool.name,
464
+ mcpTool: tool.mcpTools?.[0] ?? tool.name,
465
+ error: error instanceof Error ? error.message : String(error),
466
+ },
467
+ isError: true,
468
+ };
469
+ }
470
+ },
471
+ });
472
+ }
473
+ }
474
+ export async function closeManagedGsdBrowser() {
475
+ const closing = Array.from(connections.entries()).map(async ([key, connection]) => {
476
+ try {
477
+ await connection.client.close();
478
+ }
479
+ catch {
480
+ // Best-effort cleanup.
481
+ }
482
+ try {
483
+ await connection.transport.close();
484
+ }
485
+ catch {
486
+ // Best-effort cleanup.
487
+ }
488
+ connections.delete(key);
489
+ });
490
+ await Promise.allSettled(closing);
491
+ pendingConnections.clear();
492
+ }
493
+ export async function _resetManagedGsdBrowserForTest() {
494
+ await closeManagedGsdBrowser();
495
+ }
@@ -0,0 +1,16 @@
1
+ const DEFAULT_BROWSER_ENGINE = "gsd-browser";
2
+ export function resolveBrowserEngineMode(env = process.env) {
3
+ const raw = env.GSD_BROWSER_ENGINE?.trim();
4
+ if (!raw)
5
+ return DEFAULT_BROWSER_ENGINE;
6
+ const normalized = raw.toLowerCase();
7
+ if (normalized === "gsd-browser" || normalized === "gsd_browser" || normalized === "gsdbrowser") {
8
+ return "gsd-browser";
9
+ }
10
+ if (normalized === "legacy" || normalized === "playwright")
11
+ return "legacy";
12
+ if (normalized === "off" || normalized === "none" || normalized === "disabled" || normalized === "0" || normalized === "false") {
13
+ return "off";
14
+ }
15
+ throw new Error(`Invalid GSD_BROWSER_ENGINE="${raw}". Expected "gsd-browser", "legacy", or "off".`);
16
+ }
@@ -2,7 +2,7 @@
2
2
  "id": "browser-tools",
3
3
  "name": "Browser Tools",
4
4
  "version": "1.0.0",
5
- "description": "Playwright-based web automation, screenshots, and analysis",
5
+ "description": "GSD browser automation contract adapter backed by the managed gsd-browser engine",
6
6
  "tier": "bundled",
7
7
  "requires": { "platform": ">=2.29.0" },
8
8
  "provides": {
@@ -32,6 +32,6 @@
32
32
  "hooks": ["session_start", "session_shutdown"]
33
33
  },
34
34
  "dependencies": {
35
- "runtime": ["playwright"]
35
+ "runtime": ["@opengsd/gsd-browser"]
36
36
  }
37
37
  }
@@ -1,9 +1,13 @@
1
- /** browser-tools — pi extension: full browser interaction via Playwright. */
1
+ /** browser-tools — Pi Browser Automation Contract adapter. */
2
2
  import { importExtensionModule } from "@gsd/pi-coding-agent";
3
- let registrationPromise = null;
4
- async function registerBrowserTools(pi) {
5
- if (!registrationPromise) {
6
- registrationPromise = (async () => {
3
+ import { closeManagedGsdBrowser, registerManagedGsdBrowserTools } from "./engine/managed-gsd-browser.js";
4
+ import { resolveBrowserEngineMode } from "./engine/selection.js";
5
+ let legacyRegistrationPromise = null;
6
+ let managedRegistrationPromise = null;
7
+ let registeredEngine = null;
8
+ async function registerLegacyBrowserTools(pi) {
9
+ if (!legacyRegistrationPromise) {
10
+ legacyRegistrationPromise = (async () => {
7
11
  const [lifecycle, capture, settle, refs, utils, navigation, screenshot, interaction, inspection, session, assertions, refTools, wait, pages, forms, intent, pdf, statePersistence, networkMock, device, extract, visualDiff, zoom, codegen, actionCache, injectionDetection, verify,] = await Promise.all([
8
12
  importExtensionModule(import.meta.url, "./lifecycle.js"),
9
13
  importExtensionModule(import.meta.url, "./capture.js"),
@@ -102,11 +106,53 @@ async function registerBrowserTools(pi) {
102
106
  injectionDetection.registerInjectionDetectionTools(pi, deps);
103
107
  verify.registerVerifyTools(pi, deps);
104
108
  })().catch((error) => {
105
- registrationPromise = null;
109
+ legacyRegistrationPromise = null;
106
110
  throw error;
107
111
  });
108
112
  }
109
- return registrationPromise;
113
+ return legacyRegistrationPromise;
114
+ }
115
+ async function registerBrowserTools(pi) {
116
+ const engine = resolveBrowserEngineMode();
117
+ if (engine === "off")
118
+ return;
119
+ if (registeredEngine && registeredEngine !== engine) {
120
+ throw new Error(`Browser tools already registered with GSD_BROWSER_ENGINE=${registeredEngine}. Restart GSD before switching to ${engine}.`);
121
+ }
122
+ let registration;
123
+ if (engine === "legacy") {
124
+ registration = registerLegacyBrowserTools(pi);
125
+ }
126
+ else if (!managedRegistrationPromise) {
127
+ managedRegistrationPromise = Promise.resolve()
128
+ .then(() => {
129
+ registerManagedGsdBrowserTools(pi);
130
+ })
131
+ .catch((error) => {
132
+ managedRegistrationPromise = null;
133
+ throw error;
134
+ });
135
+ registration = managedRegistrationPromise;
136
+ }
137
+ else {
138
+ registration = managedRegistrationPromise;
139
+ }
140
+ registeredEngine = engine;
141
+ try {
142
+ await registration;
143
+ }
144
+ catch (error) {
145
+ if (registeredEngine === engine)
146
+ registeredEngine = null;
147
+ throw error;
148
+ }
149
+ }
150
+ async function closeActiveBrowserEngines() {
151
+ await closeManagedGsdBrowser();
152
+ if (legacyRegistrationPromise) {
153
+ const { closeBrowser } = await importExtensionModule(import.meta.url, "./lifecycle.js");
154
+ await closeBrowser();
155
+ }
110
156
  }
111
157
  export default function (pi) {
112
158
  pi.on("session_start", async (_event, ctx) => {
@@ -119,7 +165,9 @@ export default function (pi) {
119
165
  await registerBrowserTools(pi);
120
166
  });
121
167
  pi.on("session_shutdown", async () => {
122
- const { closeBrowser } = await importExtensionModule(import.meta.url, "./lifecycle.js");
123
- await closeBrowser();
168
+ await closeActiveBrowserEngines();
169
+ });
170
+ pi.on("session_switch", async () => {
171
+ await closeActiveBrowserEngines();
124
172
  });
125
173
  }