@opengsd/gsd-pi 1.2.0-dev.955e4da0 → 1.2.0-dev.fb12b103

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 (378) hide show
  1. package/dist/cli-style.d.ts +17 -0
  2. package/dist/cli-style.js +28 -0
  3. package/dist/cli.js +1 -1
  4. package/dist/headless-events.d.ts +4 -2
  5. package/dist/headless-events.js +7 -29
  6. package/dist/models-resolver.d.ts +3 -13
  7. package/dist/models-resolver.js +3 -22
  8. package/dist/resource-loader.js +2 -14
  9. package/dist/resources/.managed-resources-content-hash +1 -1
  10. package/dist/resources/extensions/async-jobs/async-bash-tool.js +30 -64
  11. package/dist/resources/extensions/async-jobs/await-tool.js +80 -12
  12. package/dist/resources/extensions/async-jobs/index.js +65 -0
  13. package/dist/resources/extensions/async-jobs/job-manager.js +12 -1
  14. package/dist/resources/extensions/bg-shell/bg-shell-command.js +6 -6
  15. package/dist/resources/extensions/bg-shell/bg-shell-tool.js +10 -7
  16. package/dist/resources/extensions/bg-shell/overlay.js +9 -6
  17. package/dist/resources/extensions/bg-shell/process-manager.js +54 -25
  18. package/dist/resources/extensions/bg-shell/readiness-detector.js +11 -0
  19. package/dist/resources/extensions/bg-shell/utilities.js +3 -0
  20. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +209 -88
  21. package/dist/resources/extensions/browser-tools/engine/selection.js +73 -5
  22. package/dist/resources/extensions/browser-tools/index.js +69 -12
  23. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +30 -4
  24. package/dist/resources/extensions/gsd/auto/orchestrator.js +7 -5
  25. package/dist/resources/extensions/gsd/auto-dispatch.js +12 -1
  26. package/dist/resources/extensions/gsd/auto-model-selection.js +25 -6
  27. package/dist/resources/extensions/gsd/auto-post-unit.js +11 -2
  28. package/dist/resources/extensions/gsd/auto-prompts.js +15 -10
  29. package/dist/resources/extensions/gsd/auto-start.js +15 -10
  30. package/dist/resources/extensions/gsd/auto-tool-tracking.js +18 -0
  31. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +7 -16
  32. package/dist/resources/extensions/gsd/auto-worktree.js +30 -90
  33. package/dist/resources/extensions/gsd/auto.js +4 -13
  34. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
  35. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +23 -6
  36. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
  37. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +122 -20
  38. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +6 -2
  39. package/dist/resources/extensions/gsd/branch-patterns.js +2 -0
  40. package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +83 -0
  41. package/dist/resources/extensions/gsd/browser-evidence.js +8 -2
  42. package/dist/resources/extensions/gsd/captures.js +4 -6
  43. package/dist/resources/extensions/gsd/constants.js +0 -2
  44. package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
  45. package/dist/resources/extensions/gsd/doctor-environment.js +2 -6
  46. package/dist/resources/extensions/gsd/doctor-format.js +9 -6
  47. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +13 -15
  48. package/dist/resources/extensions/gsd/error-classifier.js +9 -0
  49. package/dist/resources/extensions/gsd/exec-sandbox.js +30 -10
  50. package/dist/resources/extensions/gsd/guidance.js +98 -0
  51. package/dist/resources/extensions/gsd/guided-flow.js +17 -2
  52. package/dist/resources/extensions/gsd/mcp-filter.js +2 -19
  53. package/dist/resources/extensions/gsd/mcp-tool-name.js +5 -13
  54. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +1 -1
  55. package/dist/resources/extensions/gsd/migrate/safety.js +4 -1
  56. package/dist/resources/extensions/gsd/notification-store.js +11 -4
  57. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +6 -4
  58. package/dist/resources/extensions/gsd/paths.js +27 -0
  59. package/dist/resources/extensions/gsd/pre-execution-checks.js +91 -3
  60. package/dist/resources/extensions/gsd/preferences-models.js +14 -48
  61. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  62. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  63. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  64. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/run-uat.md +1 -1
  66. package/dist/resources/extensions/gsd/prompts/system.md +5 -2
  67. package/dist/resources/extensions/gsd/provider-error-guidance.js +1 -5
  68. package/dist/resources/extensions/gsd/provider-switch-observer.js +1 -1
  69. package/dist/resources/extensions/gsd/publication.js +87 -0
  70. package/dist/resources/extensions/gsd/recovery-classification.js +37 -94
  71. package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
  72. package/dist/resources/extensions/gsd/state.js +1 -20
  73. package/dist/resources/extensions/gsd/stop-notice.js +57 -0
  74. package/dist/resources/extensions/gsd/tool-surface-readiness.js +56 -0
  75. package/dist/resources/extensions/gsd/tools/exec-tool.js +9 -7
  76. package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
  77. package/dist/resources/extensions/gsd/uat-policy.js +2 -1
  78. package/dist/resources/extensions/gsd/unit-closeout.js +138 -0
  79. package/dist/resources/extensions/gsd/unit-context-composer.js +74 -1
  80. package/dist/resources/extensions/gsd/unit-context-manifest.js +4 -27
  81. package/dist/resources/extensions/gsd/unit-registry.js +337 -0
  82. package/dist/resources/extensions/gsd/unit-tool-contracts.js +9 -182
  83. package/dist/resources/extensions/gsd/web-app-uat.js +45 -8
  84. package/dist/resources/extensions/gsd/workflow-tool-surface.js +1 -1
  85. package/dist/resources/extensions/gsd/worktree-git-recovery.js +15 -9
  86. package/dist/resources/extensions/gsd/worktree-root.js +11 -0
  87. package/dist/resources/extensions/gsd/worktree-session-state.js +4 -5
  88. package/dist/resources/extensions/search-the-web/native-search.js +5 -3
  89. package/dist/resources/extensions/shared/browser-contract.js +59 -0
  90. package/dist/resources/extensions/shared/gsd-browser-cli.js +96 -5
  91. package/dist/resources/shared/package.json +3 -0
  92. package/dist/resources/skills/create-skill/references/executable-code.md +1 -1
  93. package/dist/resources/skills/create-skill/workflows/add-reference.md +8 -3
  94. package/dist/resources/skills/create-skill/workflows/add-script.md +4 -2
  95. package/dist/resources/skills/create-skill/workflows/add-template.md +3 -1
  96. package/dist/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  97. package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  98. package/dist/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  99. package/dist/resources/skills/spike-wrap-up/SKILL.md +9 -9
  100. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  101. package/dist/web/standalone/.next/BUILD_ID +1 -1
  102. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  103. package/dist/web/standalone/.next/build-manifest.json +3 -3
  104. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  105. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  106. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  107. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  115. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  124. package/dist/web/standalone/.next/server/app/index.html +1 -1
  125. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  132. package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
  133. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  134. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  137. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  138. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  139. package/dist/web/standalone/.next/static/chunks/{796.cf859a427a2cb2ac.js → 796.e0bdc932325d7e03.js} +1 -1
  140. package/dist/web/standalone/.next/static/chunks/{webpack-fbea77b5f9953368.js → webpack-f0285ce91d4ec9ef.js} +1 -1
  141. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  142. package/dist/web/standalone/node_modules/postcss/lib/container.js +18 -26
  143. package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +14 -47
  144. package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
  145. package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
  146. package/dist/web/standalone/node_modules/postcss/lib/input.js +29 -54
  147. package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +37 -47
  148. package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +9 -26
  149. package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +55 -57
  150. package/dist/web/standalone/node_modules/postcss/lib/node.js +31 -99
  151. package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
  152. package/dist/web/standalone/node_modules/postcss/lib/parser.js +9 -10
  153. package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
  154. package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +11 -30
  155. package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
  156. package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
  157. package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
  158. package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +28 -69
  159. package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +2 -6
  160. package/dist/web/standalone/node_modules/postcss/package.json +48 -48
  161. package/dist/web/standalone/package.json +1 -1
  162. package/dist/worktree-cli.js +3 -6
  163. package/dist/worktree-status-banner.js +7 -15
  164. package/package.json +1 -1
  165. package/packages/cloud-mcp-gateway/package.json +2 -2
  166. package/packages/contracts/dist/rpc.d.ts +1 -0
  167. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  168. package/packages/contracts/dist/rpc.js.map +1 -1
  169. package/packages/contracts/dist/workflow.d.ts +4 -0
  170. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  171. package/packages/contracts/dist/workflow.js.map +1 -1
  172. package/packages/contracts/package.json +1 -1
  173. package/packages/daemon/package.json +4 -4
  174. package/packages/gsd-agent-core/package.json +5 -5
  175. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +5 -0
  176. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  177. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
  178. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  179. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  180. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +7 -0
  181. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  182. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  183. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +8 -1
  184. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  186. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +11 -1
  187. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  188. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  189. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +4 -4
  190. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  191. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  192. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +3 -1
  193. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  194. package/packages/gsd-agent-modes/package.json +7 -7
  195. package/packages/mcp-server/dist/cli.js +6 -3
  196. package/packages/mcp-server/dist/cli.js.map +1 -1
  197. package/packages/mcp-server/dist/workflow-tools.d.ts +8 -0
  198. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  199. package/packages/mcp-server/dist/workflow-tools.js +17 -1
  200. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  201. package/packages/mcp-server/package.json +3 -3
  202. package/packages/native/package.json +1 -1
  203. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts +1 -0
  204. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  205. package/packages/pi-agent-core/dist/harness/env/nodejs.js +34 -3
  206. package/packages/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  207. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  208. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  209. package/packages/pi-agent-core/dist/index.js +3 -0
  210. package/packages/pi-agent-core/dist/index.js.map +1 -1
  211. package/packages/pi-agent-core/package.json +1 -1
  212. package/packages/pi-ai/dist/models.generated.d.ts +94 -382
  213. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  214. package/packages/pi-ai/dist/models.generated.js +149 -422
  215. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  216. package/packages/pi-ai/package.json +1 -1
  217. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +2 -2
  218. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  219. package/packages/pi-coding-agent/dist/core/auth-storage.js +19 -13
  220. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  221. package/packages/pi-coding-agent/dist/core/provider-readiness.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/core/provider-readiness.js +13 -6
  223. package/packages/pi-coding-agent/dist/core/provider-readiness.js.map +1 -1
  224. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +11 -0
  225. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/core/tools/bash.js +53 -11
  227. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  229. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  230. package/packages/pi-coding-agent/dist/index.js +1 -1
  231. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  232. package/packages/pi-coding-agent/dist/utils/shell.d.ts +28 -2
  233. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  234. package/packages/pi-coding-agent/dist/utils/shell.js +56 -10
  235. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  236. package/packages/pi-coding-agent/package.json +7 -7
  237. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  238. package/packages/pi-tui/dist/tui.js +9 -0
  239. package/packages/pi-tui/dist/tui.js.map +1 -1
  240. package/packages/pi-tui/package.json +2 -2
  241. package/packages/rpc-client/package.json +2 -2
  242. package/pkg/package.json +1 -1
  243. package/src/resources/extensions/async-jobs/async-bash-cancel.test.ts +360 -0
  244. package/src/resources/extensions/async-jobs/async-bash-tool.ts +33 -56
  245. package/src/resources/extensions/async-jobs/await-tool.test.ts +139 -0
  246. package/src/resources/extensions/async-jobs/await-tool.ts +82 -12
  247. package/src/resources/extensions/async-jobs/index.ts +79 -0
  248. package/src/resources/extensions/async-jobs/job-manager.ts +21 -1
  249. package/src/resources/extensions/bg-shell/bg-shell-command.ts +6 -6
  250. package/src/resources/extensions/bg-shell/bg-shell-tool.ts +10 -6
  251. package/src/resources/extensions/bg-shell/overlay.ts +9 -5
  252. package/src/resources/extensions/bg-shell/process-manager.ts +50 -25
  253. package/src/resources/extensions/bg-shell/readiness-detector.ts +12 -0
  254. package/src/resources/extensions/bg-shell/tests/lifecycle-and-utilities.test.ts +48 -1
  255. package/src/resources/extensions/bg-shell/utilities.ts +3 -0
  256. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +265 -98
  257. package/src/resources/extensions/browser-tools/engine/selection.ts +90 -4
  258. package/src/resources/extensions/browser-tools/index.ts +71 -13
  259. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +83 -13
  260. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +29 -1
  261. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +136 -0
  262. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +34 -4
  263. package/src/resources/extensions/gsd/auto/orchestrator.ts +7 -5
  264. package/src/resources/extensions/gsd/auto-dispatch.ts +12 -0
  265. package/src/resources/extensions/gsd/auto-model-selection.ts +25 -5
  266. package/src/resources/extensions/gsd/auto-post-unit.ts +13 -2
  267. package/src/resources/extensions/gsd/auto-prompts.ts +40 -26
  268. package/src/resources/extensions/gsd/auto-start.ts +15 -10
  269. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  270. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +10 -17
  271. package/src/resources/extensions/gsd/auto-worktree.ts +30 -93
  272. package/src/resources/extensions/gsd/auto.ts +8 -15
  273. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
  274. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +23 -6
  275. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
  276. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +151 -15
  277. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +6 -2
  278. package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
  279. package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +108 -0
  280. package/src/resources/extensions/gsd/browser-evidence.ts +18 -2
  281. package/src/resources/extensions/gsd/captures.ts +4 -6
  282. package/src/resources/extensions/gsd/constants.ts +0 -3
  283. package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
  284. package/src/resources/extensions/gsd/doctor-environment.ts +2 -7
  285. package/src/resources/extensions/gsd/doctor-format.ts +12 -7
  286. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +13 -15
  287. package/src/resources/extensions/gsd/error-classifier.ts +11 -0
  288. package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
  289. package/src/resources/extensions/gsd/guidance.ts +139 -0
  290. package/src/resources/extensions/gsd/guided-flow.ts +16 -2
  291. package/src/resources/extensions/gsd/mcp-filter.ts +2 -23
  292. package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
  293. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
  294. package/src/resources/extensions/gsd/migrate/safety.ts +4 -1
  295. package/src/resources/extensions/gsd/notification-store.ts +26 -3
  296. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +6 -4
  297. package/src/resources/extensions/gsd/paths.ts +33 -0
  298. package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
  299. package/src/resources/extensions/gsd/preferences-models.ts +12 -47
  300. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  301. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  302. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  303. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  304. package/src/resources/extensions/gsd/prompts/run-uat.md +1 -1
  305. package/src/resources/extensions/gsd/prompts/system.md +5 -2
  306. package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
  307. package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
  308. package/src/resources/extensions/gsd/publication.ts +122 -0
  309. package/src/resources/extensions/gsd/recovery-classification.ts +42 -96
  310. package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
  311. package/src/resources/extensions/gsd/state.ts +4 -21
  312. package/src/resources/extensions/gsd/stop-notice.ts +75 -0
  313. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +22 -0
  314. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +16 -19
  315. package/src/resources/extensions/gsd/tests/browser-automation-contract-fixture.ts +39 -0
  316. package/src/resources/extensions/gsd/tests/browser-contract.test.ts +44 -0
  317. package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +144 -0
  318. package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
  319. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +22 -0
  320. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
  321. package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +303 -0
  322. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +2 -1
  323. package/src/resources/extensions/gsd/tests/dynamic-bash-no-cap.test.ts +132 -0
  324. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +193 -0
  325. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +29 -1
  326. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +35 -1
  327. package/src/resources/extensions/gsd/tests/guidance.test.ts +125 -0
  328. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +53 -11
  329. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +73 -58
  330. package/src/resources/extensions/gsd/tests/integration/gsd-integration-fixture.ts +80 -0
  331. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +3 -1
  332. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +32 -1
  333. package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
  334. package/src/resources/extensions/gsd/tests/oauth-api-model-routing.test.ts +167 -0
  335. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
  336. package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
  337. package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
  338. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +157 -0
  339. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
  340. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
  341. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +76 -0
  342. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
  343. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
  344. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +24 -29
  345. package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
  346. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +67 -2
  347. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
  348. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +44 -1
  349. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  350. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
  351. package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
  352. package/src/resources/extensions/gsd/tools/exec-tool.ts +8 -7
  353. package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
  354. package/src/resources/extensions/gsd/uat-policy.ts +2 -1
  355. package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
  356. package/src/resources/extensions/gsd/unit-context-composer.ts +111 -1
  357. package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
  358. package/src/resources/extensions/gsd/unit-registry.ts +412 -0
  359. package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
  360. package/src/resources/extensions/gsd/web-app-uat.ts +51 -8
  361. package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
  362. package/src/resources/extensions/gsd/worktree-git-recovery.ts +15 -9
  363. package/src/resources/extensions/gsd/worktree-root.ts +12 -0
  364. package/src/resources/extensions/gsd/worktree-session-state.ts +3 -5
  365. package/src/resources/extensions/search-the-web/native-search.ts +5 -3
  366. package/src/resources/extensions/shared/browser-contract.ts +66 -0
  367. package/src/resources/extensions/shared/gsd-browser-cli.ts +119 -5
  368. package/src/resources/shared/package.json +3 -0
  369. package/src/resources/skills/create-skill/references/executable-code.md +1 -1
  370. package/src/resources/skills/create-skill/workflows/add-reference.md +8 -3
  371. package/src/resources/skills/create-skill/workflows/add-script.md +4 -2
  372. package/src/resources/skills/create-skill/workflows/add-template.md +3 -1
  373. package/src/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  374. package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  375. package/src/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  376. package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
  377. /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → mU4QIDlpVHDdjDpeEKh5W}/_buildManifest.js +0 -0
  378. /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → mU4QIDlpVHDdjDpeEKh5W}/_ssgManifest.js +0 -0
@@ -11,6 +11,11 @@ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, realpathSync
11
11
  import { join } from "node:path";
12
12
  import { tmpdir } from "node:os";
13
13
  import { execSync } from "node:child_process";
14
+ import {
15
+ commitAll,
16
+ createGsdIntegrationProject,
17
+ writeGsdMilestoneContext,
18
+ } from "./gsd-integration-fixture.ts";
14
19
 
15
20
  import {
16
21
  createAutoWorktree,
@@ -32,17 +37,12 @@ function run(command: string, cwd: string): string {
32
37
  }
33
38
 
34
39
  function createTempRepo(): string {
35
- const dir = realpathSync(mkdtempSync(join(tmpdir(), "auto-wt-test-")));
36
- run("git init", dir);
37
- run("git config user.email test@test.com", dir);
38
- run("git config user.name Test", dir);
39
- // Create initial commit on main
40
- writeFileSync(join(dir, "README.md"), "# test\n");
41
- run("git add .", dir);
42
- run("git commit -m init", dir);
43
- // Ensure branch is called main
44
- run("git branch -M main", dir);
45
- return dir;
40
+ return createGsdIntegrationProject("auto-wt-test-").root;
41
+ }
42
+
43
+ function commitMilestoneContext(repo: string, milestoneId: string): void {
44
+ writeGsdMilestoneContext(repo, milestoneId);
45
+ commitAll(repo, "add milestone");
46
46
  }
47
47
 
48
48
  describe("auto-worktree lifecycle", () => {
@@ -59,13 +59,7 @@ describe("auto-worktree lifecycle", () => {
59
59
 
60
60
  test("create → detect → teardown", () => {
61
61
  tempDir = createTempRepo();
62
-
63
- // Create .gsd/milestones/M003 with a dummy file (simulates planning artifacts)
64
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
65
- mkdirSync(msDir, { recursive: true });
66
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
67
- run("git add .", tempDir);
68
- run("git commit -m \"add milestone\"", tempDir);
62
+ commitMilestoneContext(tempDir, "M003");
69
63
 
70
64
  // ─── createAutoWorktree ──────────────────────────────────────────
71
65
  const wtPath = createAutoWorktree(tempDir, "M003");
@@ -112,11 +106,7 @@ describe("auto-worktree lifecycle", () => {
112
106
 
113
107
  test("re-entry: create again, exit without teardown, re-enter", () => {
114
108
  tempDir = createTempRepo();
115
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
116
- mkdirSync(msDir, { recursive: true });
117
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
118
- run("git add .", tempDir);
119
- run("git commit -m \"add milestone\"", tempDir);
109
+ commitMilestoneContext(tempDir, "M003");
120
110
 
121
111
  const wtPath2 = createAutoWorktree(tempDir, "M003");
122
112
  assert.ok(existsSync(wtPath2), "worktree re-created");
@@ -192,13 +182,62 @@ describe("auto-worktree lifecycle", () => {
192
182
  }
193
183
  });
194
184
 
185
+ test("legacy symlink-resolved auto worktree is detected after module state reset", () => {
186
+ tempDir = createTempRepo();
187
+ const savedGsdHome = process.env.GSD_HOME;
188
+ const fakeHome = realpathSync(mkdtempSync(join(tmpdir(), "auto-wt-home-legacy-")));
189
+ const storage = join(fakeHome, ".gsd", "projects", "abc123def456");
190
+ mkdirSync(join(storage, "milestones", "M001"), { recursive: true });
191
+ writeFileSync(join(storage, "milestones", "M001", "CONTEXT.md"), "# M001\n");
192
+ symlinkSync(storage, join(tempDir, ".gsd"));
193
+ process.env.GSD_HOME = join(fakeHome, ".gsd");
194
+
195
+ try {
196
+ // Worktrees created by older versions live at .gsd/worktrees/<MID>;
197
+ // git resolves the symlink and registers them under external state.
198
+ // Detection must keep working for them — canonical .gsd-worktrees/
199
+ // creation never crosses the symlink, so create the legacy worktree
200
+ // explicitly.
201
+ const wtPath = join(tempDir, ".gsd", "worktrees", "M001");
202
+ run(`git worktree add -b milestone/M001 "${wtPath}"`, tempDir);
203
+ const realWtPath = realpathSync(wtPath);
204
+ assert.ok(realWtPath.startsWith(storage), "git registered the symlink-resolved worktree path");
205
+
206
+ _resetAutoWorktreeOriginalBaseForTests();
207
+ process.chdir(realWtPath);
208
+
209
+ assert.ok(isInAutoWorktree(tempDir), "structural detection works without module originalBase");
210
+ const resolved = getAutoWorktreePath(realWtPath, "M001");
211
+ assert.ok(resolved, "existing legacy worktree is found when basePath is the worktree path");
212
+ assert.equal(realpathSync(resolved!), realWtPath);
213
+
214
+ enterAutoWorktree(tempDir, "M001");
215
+ process.chdir(realWtPath);
216
+ assert.deepStrictEqual(
217
+ getActiveAutoWorktreeContext(),
218
+ {
219
+ originalBase: tempDir,
220
+ worktreeName: "M001",
221
+ branch: "milestone/M001",
222
+ },
223
+ "active context is detected from a symlink-resolved legacy worktree cwd",
224
+ );
225
+ } finally {
226
+ process.chdir(tempDir);
227
+ try {
228
+ teardownAutoWorktree(tempDir, "M001");
229
+ } catch {
230
+ // Best-effort cleanup for partially-created temp worktrees.
231
+ }
232
+ if (savedGsdHome === undefined) delete process.env.GSD_HOME;
233
+ else process.env.GSD_HOME = savedGsdHome;
234
+ rmSync(fakeHome, { recursive: true, force: true });
235
+ }
236
+ });
237
+
195
238
  test("coexistence with manual worktree", async () => {
196
239
  tempDir = createTempRepo();
197
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
198
- mkdirSync(msDir, { recursive: true });
199
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
200
- run("git add .", tempDir);
201
- run("git commit -m \"add milestone\"", tempDir);
240
+ commitMilestoneContext(tempDir, "M003");
202
241
 
203
242
  // Import createWorktree directly for manual worktree
204
243
  const { createWorktree } = await import("../../worktree-manager.ts");
@@ -221,11 +260,7 @@ describe("auto-worktree lifecycle", () => {
221
260
 
222
261
  test("split-brain prevention: originalBase cleared after teardown", () => {
223
262
  tempDir = createTempRepo();
224
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
225
- mkdirSync(msDir, { recursive: true });
226
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
227
- run("git add .", tempDir);
228
- run("git commit -m \"add milestone\"", tempDir);
263
+ commitMilestoneContext(tempDir, "M003");
229
264
 
230
265
  createAutoWorktree(tempDir, "M003");
231
266
  teardownAutoWorktree(tempDir, "M003");
@@ -235,11 +270,7 @@ describe("auto-worktree lifecycle", () => {
235
270
 
236
271
  test("#1526: getMainBranch returns milestone/<MID> in auto-worktree", async () => {
237
272
  tempDir = createTempRepo();
238
- const msDir = join(tempDir, ".gsd", "milestones", "M005");
239
- mkdirSync(msDir, { recursive: true });
240
- writeFileSync(join(msDir, "CONTEXT.md"), "# M005 Context\n");
241
- run("git add .", tempDir);
242
- run("git commit -m \"add milestone\"", tempDir);
273
+ commitMilestoneContext(tempDir, "M005");
243
274
 
244
275
  const { GitServiceImpl } = await import("../../git-service.ts");
245
276
 
@@ -259,11 +290,7 @@ describe("auto-worktree lifecycle", () => {
259
290
 
260
291
  test("#1713: stale worktree directory without .git file", async () => {
261
292
  tempDir = createTempRepo();
262
- const msDir = join(tempDir, ".gsd", "milestones", "M010");
263
- mkdirSync(msDir, { recursive: true });
264
- writeFileSync(join(msDir, "CONTEXT.md"), "# M010 Context\n");
265
- run("git add .", tempDir);
266
- run("git commit -m \"add milestone\"", tempDir);
293
+ commitMilestoneContext(tempDir, "M010");
267
294
 
268
295
  // Simulate a crash leaving a stale directory with no .git file.
269
296
  const { worktreePath } = await import("../../worktree-manager.ts");
@@ -284,11 +311,7 @@ describe("auto-worktree lifecycle", () => {
284
311
 
285
312
  test("#778: re-attach does not reconcile plan checkboxes into a worktree-local .gsd projection", async () => {
286
313
  tempDir = createTempRepo();
287
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
288
- mkdirSync(msDir, { recursive: true });
289
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
290
- run("git add .", tempDir);
291
- run("git commit -m \"add milestone\"", tempDir);
314
+ commitMilestoneContext(tempDir, "M003");
292
315
 
293
316
  const planRelPath = join(".gsd", "milestones", "M004", "slices", "S01", "S01-PLAN.md");
294
317
  const planDir = join(tempDir, ".gsd", "milestones", "M004", "slices", "S01");
@@ -348,11 +371,7 @@ describe("auto-worktree lifecycle", () => {
348
371
 
349
372
  test("#2791: mcp.json is not copied into worktree on creation after copyPlanningArtifacts removal", () => {
350
373
  tempDir = createTempRepo();
351
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
352
- mkdirSync(msDir, { recursive: true });
353
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
354
- run("git add .", tempDir);
355
- run("git commit -m \"add milestone\"", tempDir);
374
+ commitMilestoneContext(tempDir, "M003");
356
375
 
357
376
  // Create mcp.json in .gsd/ AFTER the commit (untracked, like real usage).
358
377
  // Phase C removed copyPlanningArtifacts, so creation should not seed a
@@ -377,11 +396,7 @@ describe("auto-worktree lifecycle", () => {
377
396
 
378
397
  test("#2791: mcp.json synced via syncGsdStateToWorktree (ROOT_STATE_FILES)", () => {
379
398
  tempDir = createTempRepo();
380
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
381
- mkdirSync(msDir, { recursive: true });
382
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
383
- run("git add .", tempDir);
384
- run("git commit -m \"add milestone\"", tempDir);
399
+ commitMilestoneContext(tempDir, "M003");
385
400
 
386
401
  // Create worktree first (no mcp.json yet)
387
402
  const wtPath = createAutoWorktree(tempDir, "M003");
@@ -0,0 +1,80 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+
6
+ export type GsdIntegrationProject = {
7
+ root: string;
8
+ };
9
+
10
+ export type CreateGsdIntegrationProjectOptions = {
11
+ prefix?: string;
12
+ initialFiles?: Record<string, string>;
13
+ };
14
+
15
+ export function projectRoot(project: GsdIntegrationProject | string): string {
16
+ return typeof project === "string" ? project : project.root;
17
+ }
18
+
19
+ export function git(project: GsdIntegrationProject | string, ...args: string[]): string {
20
+ return execFileSync("git", args, {
21
+ cwd: projectRoot(project),
22
+ encoding: "utf-8",
23
+ stdio: ["ignore", "pipe", "pipe"],
24
+ }).trim();
25
+ }
26
+
27
+ export function createGsdIntegrationProject(
28
+ options: CreateGsdIntegrationProjectOptions | string = {},
29
+ ): GsdIntegrationProject {
30
+ const resolvedOptions = typeof options === "string" ? { prefix: options } : options;
31
+ const prefix = resolvedOptions.prefix ?? "gsd-integration-";
32
+ const root = realpathSync(mkdtempSync(join(tmpdir(), prefix)));
33
+
34
+ git(root, "init");
35
+ git(root, "config", "user.email", "test@test.com");
36
+ git(root, "config", "user.name", "Test");
37
+ git(root, "config", "core.autocrlf", "false");
38
+
39
+ writeProjectFile(root, "README.md", "# test\n");
40
+ for (const [relativePath, content] of Object.entries(resolvedOptions.initialFiles ?? {})) {
41
+ writeProjectFile(root, relativePath, content);
42
+ }
43
+ git(root, "add", ".");
44
+ git(root, "commit", "-m", "init");
45
+ git(root, "branch", "-M", "main");
46
+
47
+ return { root };
48
+ }
49
+
50
+ export function writeProjectFile(
51
+ project: GsdIntegrationProject | string,
52
+ relativePath: string,
53
+ content: string,
54
+ ): string {
55
+ const filePath = join(projectRoot(project), relativePath);
56
+ mkdirSync(dirname(filePath), { recursive: true });
57
+ writeFileSync(filePath, content, "utf-8");
58
+ return filePath;
59
+ }
60
+
61
+ export function writeGsdMilestoneContext(
62
+ project: GsdIntegrationProject | string,
63
+ milestoneId: string,
64
+ content = `# ${milestoneId} Context\n`,
65
+ ): string {
66
+ return writeProjectFile(
67
+ project,
68
+ join(".gsd", "milestones", milestoneId, "CONTEXT.md"),
69
+ content,
70
+ );
71
+ }
72
+
73
+ export function commitAll(project: GsdIntegrationProject | string, message: string): void {
74
+ git(project, "add", ".");
75
+ git(project, "commit", "-m", message);
76
+ }
77
+
78
+ export function cleanupGsdIntegrationProject(project: GsdIntegrationProject | string): void {
79
+ rmSync(projectRoot(project), { recursive: true, force: true });
80
+ }
@@ -60,7 +60,9 @@ test("ensureProjectWorkflowMcpConfig creates .mcp.json with workflow and browser
60
60
  assert.equal(typeof browserArgs[mcpArgIndex + 6], "string");
61
61
  assert.ok((browserArgs[mcpArgIndex + 6] ?? "").length > 0, "identity-key must be non-empty");
62
62
  assert.equal(browserArgs[mcpArgIndex + 7], "--identity-project");
63
- assert.equal(browserArgs[mcpArgIndex + 8], projectRoot);
63
+ assert.equal(typeof browserArgs[mcpArgIndex + 8], "string");
64
+ assert.ok((browserArgs[mcpArgIndex + 8] ?? "").length > 0, "identity-project must be non-empty");
65
+ assert.doesNotMatch(browserArgs[mcpArgIndex + 8] ?? "", /[\\/]/, "identity-project must not be a filesystem path");
64
66
  assert.equal((browserServer as { cwd?: string })?.cwd, projectRoot);
65
67
 
66
68
  const settings = JSON.parse(readFileSync(join(projectRoot, ".claude", "settings.local.json"), "utf-8")) as {
@@ -14,7 +14,7 @@ import { tmpdir } from "node:os";
14
14
  import { resolveExpectedArtifactPath } from "../auto-artifact-paths.ts";
15
15
  import { unitPhaseLabel, unitVerb } from "../auto-dashboard.ts";
16
16
  import { classifyUnitPhase } from "../metrics.ts";
17
- import { resolveModelWithFallbacksForUnit } from "../preferences-models.ts";
17
+ import { resolveDefaultSessionModel, resolveModelWithFallbacksForUnit } from "../preferences-models.ts";
18
18
  import { KNOWN_UNIT_LABELS } from "../preferences-types.ts";
19
19
 
20
20
  function withModelPreferences<T>(fn: () => T): T {
@@ -92,6 +92,37 @@ test("run-uat falls back to completion when uat bucket is not configured", () =>
92
92
  }
93
93
  });
94
94
 
95
+ test("default session model resolves from the explicit project base path", () => {
96
+ const oldHome = process.env.GSD_HOME;
97
+ const originalCwd = process.cwd();
98
+ const home = mkdtempSync(join(tmpdir(), "gsd-model-map-home-"));
99
+ const base = mkdtempSync(join(tmpdir(), "gsd-model-map-project-"));
100
+
101
+ try {
102
+ process.env.GSD_HOME = home;
103
+ mkdirSync(join(base, ".gsd"), { recursive: true });
104
+ writeFileSync(join(base, ".gsd", "PREFERENCES.md"), [
105
+ "---",
106
+ "models:",
107
+ " execution: gpt-5.5",
108
+ "---",
109
+ "",
110
+ ].join("\n"));
111
+ process.chdir(home);
112
+
113
+ assert.deepEqual(resolveDefaultSessionModel("openai-codex", base), {
114
+ provider: "openai-codex",
115
+ id: "gpt-5.5",
116
+ });
117
+ } finally {
118
+ process.chdir(originalCwd);
119
+ if (oldHome === undefined) delete process.env.GSD_HOME;
120
+ else process.env.GSD_HOME = oldHome;
121
+ rmSync(home, { recursive: true, force: true });
122
+ rmSync(base, { recursive: true, force: true });
123
+ }
124
+ });
125
+
95
126
  test("every known unit label with a dispatch phase resolves when all model buckets are configured", () => {
96
127
  withModelPreferences(() => {
97
128
  const missing = KNOWN_UNIT_LABELS.filter((unitType) => !resolveModelWithFallbacksForUnit(unitType));
@@ -306,6 +306,38 @@ describe("notification-store", () => {
306
306
  rmSync(lockPath, { force: true });
307
307
  });
308
308
 
309
+ test("structured meta persists kind and scope on the entry", () => {
310
+ initNotificationStore(tmp);
311
+ appendNotification("Auto-mode blocked — validation gate", "warning", "notify", { kind: "auto-stop", scope: "M005" });
312
+
313
+ const entries = readNotifications();
314
+ assert.equal(entries.length, 1);
315
+ assert.equal(entries[0].kind, "auto-stop");
316
+ assert.equal(entries[0].scope, "M005");
317
+ });
318
+
319
+ test("dedup keys on kind+scope when present, not on prose", () => {
320
+ initNotificationStore(tmp);
321
+ appendNotification("Auto-mode blocked — validation gate", "warning", "notify", { kind: "auto-stop", scope: "M005" });
322
+ // Rephrased message, same structured identity → deduped within the window
323
+ appendNotification("Auto-mode blocked — gate rejected", "warning", "notify", { kind: "auto-stop", scope: "M005" });
324
+ // Same kind, different scope → distinct
325
+ appendNotification("Auto-mode blocked — validation gate", "warning", "notify", { kind: "auto-stop", scope: "M006" });
326
+
327
+ assert.equal(readNotifications().length, 2);
328
+ });
329
+
330
+ test("readNotifications filters by kind and scope", () => {
331
+ initNotificationStore(tmp);
332
+ appendNotification("a", "info", "notify", { kind: "auto-stop", scope: "M005" });
333
+ appendNotification("b", "info", "notify", { kind: "provider-error-pause", scope: "M005" });
334
+ appendNotification("c", "info");
335
+
336
+ assert.equal(readNotifications(tmp, { kind: "auto-stop" }).length, 1);
337
+ assert.equal(readNotifications(tmp, { scope: "M005" }).length, 2);
338
+ assert.equal(readNotifications(tmp, { kind: "provider-error-pause", scope: "M005" })[0].message, "b");
339
+ });
340
+
309
341
  test("listeners are notified on append, markAllRead, and clear", () => {
310
342
  initNotificationStore(tmp);
311
343
  let calls = 0;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * OAuth/subscription vs pay-per-token API routing for bare model IDs.
3
+ *
4
+ * When the same model ID (e.g. gpt-5.5) exists on multiple providers, resolveModelId
5
+ * must prefer subscription/OAuth routes over platform API keys.
6
+ */
7
+
8
+ import test from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { MODELS } from "../../../../../packages/pi-ai/dist/models.generated.js";
11
+ import {
12
+ BARE_ID_SUBSCRIPTION_PROVIDER_PRECEDENCE,
13
+ resolveModelId,
14
+ } from "../auto-model-selection.js";
15
+
16
+ type ModelRef = { id: string; provider: string };
17
+
18
+ function modelsForProviders(modelId: string, providers: string[]): ModelRef[] {
19
+ return providers.map((provider) => ({ id: modelId, provider }));
20
+ }
21
+
22
+ /** Table-driven precedence cases — one row per conflict shape, not per model ID. */
23
+ const PRECEDENCE_CASES: Array<{
24
+ label: string;
25
+ modelId: string;
26
+ providers: string[];
27
+ expected: string;
28
+ currentProvider?: string;
29
+ }> = [
30
+ {
31
+ label: "ChatGPT OAuth beats OpenAI API (gpt-5.5)",
32
+ modelId: "gpt-5.5",
33
+ providers: ["openai", "openai-codex"],
34
+ expected: "openai-codex",
35
+ },
36
+ {
37
+ label: "Codex beats Copilot beats OpenAI API (gpt-5.5 triple)",
38
+ modelId: "gpt-5.5",
39
+ providers: ["openai", "github-copilot", "openai-codex"],
40
+ expected: "openai-codex",
41
+ },
42
+ {
43
+ label: "Copilot OAuth beats OpenAI API when no Codex (gpt-5.5)",
44
+ modelId: "gpt-5.5",
45
+ providers: ["openai", "github-copilot"],
46
+ expected: "github-copilot",
47
+ },
48
+ {
49
+ label: "Copilot OAuth beats OpenAI API (gpt-5-mini)",
50
+ modelId: "gpt-5-mini",
51
+ providers: ["openai", "github-copilot"],
52
+ expected: "github-copilot",
53
+ },
54
+ {
55
+ label: "Anthropic beats Copilot for Claude (claude-sonnet-4-6)",
56
+ modelId: "claude-sonnet-4-6",
57
+ providers: ["anthropic", "github-copilot"],
58
+ expected: "anthropic",
59
+ },
60
+ {
61
+ label: "Copilot beats Google API for Gemini (gemini-2.5-pro)",
62
+ modelId: "gemini-2.5-pro",
63
+ providers: ["google", "github-copilot"],
64
+ expected: "github-copilot",
65
+ },
66
+ {
67
+ label: "Gemini CLI beats Google API (gemini-2.5-pro)",
68
+ modelId: "gemini-2.5-pro",
69
+ providers: ["google", "google-gemini-cli"],
70
+ expected: "google-gemini-cli",
71
+ },
72
+ {
73
+ label: "Gemini CLI beats Copilot beats Google API",
74
+ modelId: "gemini-2.5-pro",
75
+ providers: ["google", "github-copilot", "google-gemini-cli"],
76
+ expected: "google-gemini-cli",
77
+ },
78
+ {
79
+ label: "Session provider still wins (openai-codex explicit)",
80
+ modelId: "gpt-5.5",
81
+ providers: ["openai", "openai-codex", "github-copilot"],
82
+ expected: "github-copilot",
83
+ currentProvider: "github-copilot",
84
+ },
85
+ {
86
+ label: "claude-code session beats anthropic (#3772)",
87
+ modelId: "claude-sonnet-4-6",
88
+ providers: ["anthropic", "claude-code"],
89
+ expected: "claude-code",
90
+ currentProvider: "claude-code",
91
+ },
92
+ ];
93
+
94
+ for (const row of PRECEDENCE_CASES) {
95
+ test(`resolveModelId precedence: ${row.label}`, () => {
96
+ const available = modelsForProviders(row.modelId, row.providers);
97
+ const result = resolveModelId(row.modelId, available, row.currentProvider);
98
+ assert.ok(result, `expected a match for ${row.modelId}`);
99
+ assert.equal(result.provider, row.expected);
100
+ });
101
+ }
102
+
103
+ test("BARE_ID_SUBSCRIPTION_PROVIDER_PRECEDENCE covers known OAuth/subscription providers", () => {
104
+ const expected = [
105
+ "openai-codex",
106
+ "google-gemini-cli",
107
+ "anthropic",
108
+ "github-copilot",
109
+ "google-antigravity",
110
+ ];
111
+ assert.deepEqual([...BARE_ID_SUBSCRIPTION_PROVIDER_PRECEDENCE], expected);
112
+ });
113
+
114
+ test("every catalog overlap with OAuth/subscription + API resolves to subscription route", () => {
115
+ const oauthProviders = new Set(["anthropic", "github-copilot", "openai-codex"]);
116
+ const subscriptionCli = new Set(["google-gemini-cli", "google-antigravity", "claude-code"]);
117
+ const apiPayPerToken = new Set(["openai", "google", "azure-openai-responses"]);
118
+
119
+ const byId = new Map<string, Set<string>>();
120
+ for (const [provider, models] of Object.entries(MODELS)) {
121
+ for (const id of Object.keys(models)) {
122
+ if (!byId.has(id)) byId.set(id, new Set());
123
+ byId.get(id)!.add(provider);
124
+ }
125
+ }
126
+
127
+ const failures: string[] = [];
128
+
129
+ for (const [modelId, providers] of byId) {
130
+ if (providers.size < 2) continue;
131
+
132
+ const hasApi = [...providers].some((p) => apiPayPerToken.has(p));
133
+ const subscriptionCandidates = [...providers].filter(
134
+ (p) => oauthProviders.has(p) || subscriptionCli.has(p),
135
+ );
136
+ if (!hasApi || subscriptionCandidates.length === 0) continue;
137
+
138
+ const testProviders = new Set<string>(subscriptionCandidates);
139
+ for (const p of providers) {
140
+ if (apiPayPerToken.has(p)) testProviders.add(p);
141
+ }
142
+
143
+ const available = modelsForProviders(modelId, [...testProviders]);
144
+ const result = resolveModelId(modelId, available, undefined);
145
+ if (!result) {
146
+ failures.push(`${modelId}: no resolution among ${[...testProviders].join(", ")}`);
147
+ continue;
148
+ }
149
+
150
+ const winnerIsSubscription =
151
+ oauthProviders.has(result.provider) ||
152
+ subscriptionCli.has(result.provider) ||
153
+ result.provider === "anthropic";
154
+
155
+ if (!winnerIsSubscription) {
156
+ failures.push(
157
+ `${modelId}: got ${result.provider}, expected subscription route among ${[...testProviders].join(", ")}`,
158
+ );
159
+ }
160
+ }
161
+
162
+ assert.equal(
163
+ failures.length,
164
+ 0,
165
+ `catalog overlap routing failures:\n${failures.join("\n")}`,
166
+ );
167
+ });