gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.f55d16d13

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 (303) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/GSD-WORKFLOW.md +2 -2
  3. package/dist/resources/extensions/github-sync/templates.js +39 -8
  4. package/dist/resources/extensions/gsd/auto/loop.js +16 -9
  5. package/dist/resources/extensions/gsd/auto/phases.js +37 -30
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +19 -15
  7. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  10. package/dist/resources/extensions/gsd/auto-prompts.js +111 -1
  11. package/dist/resources/extensions/gsd/auto-recovery.js +154 -8
  12. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  13. package/dist/resources/extensions/gsd/auto.js +9 -1
  14. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +15 -1
  15. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -1
  16. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  17. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  18. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  19. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  20. package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
  21. package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
  22. package/dist/resources/extensions/gsd/detection.js +106 -0
  23. package/dist/resources/extensions/gsd/graph.js +9 -3
  24. package/dist/resources/extensions/gsd/gsd-db.js +102 -2
  25. package/dist/resources/extensions/gsd/guided-flow.js +2 -2
  26. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  27. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  28. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  29. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  30. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  31. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  32. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  33. package/dist/web/standalone/.next/BUILD_ID +1 -1
  34. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  35. package/dist/web/standalone/.next/build-manifest.json +3 -3
  36. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  37. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  55. package/dist/web/standalone/.next/server/app/index.html +1 -1
  56. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  63. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  64. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  67. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  68. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  69. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  70. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  71. package/package.json +10 -6
  72. package/packages/contracts/package.json +1 -1
  73. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  74. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  75. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  76. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  77. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  78. package/packages/pi-ai/dist/models/index.js +8 -0
  79. package/packages/pi-ai/dist/models/index.js.map +1 -1
  80. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  81. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  82. package/packages/pi-ai/dist/providers/fake.js +319 -0
  83. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  84. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  85. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  86. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  87. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  88. package/packages/pi-ai/src/models/index.ts +9 -0
  89. package/packages/pi-ai/src/providers/fake.ts +376 -0
  90. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  91. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  92. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +74 -0
  93. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  95. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
  97. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
  99. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  100. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  102. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  103. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
  104. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  106. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  109. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -17
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +60 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
  141. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  143. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  148. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +23 -0
  150. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  153. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  155. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  156. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  157. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  163. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  165. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  168. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  169. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  177. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +87 -0
  178. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
  179. package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
  180. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  181. package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
  182. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  183. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  184. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  185. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  186. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  187. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  188. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  189. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  190. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  191. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +118 -17
  192. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
  193. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -1
  194. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  195. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  196. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +25 -0
  197. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  198. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  199. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  200. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  201. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  202. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  203. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  204. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  205. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  206. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  207. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  208. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  209. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  210. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  211. package/packages/pi-tui/dist/index.d.ts +1 -0
  212. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  213. package/packages/pi-tui/dist/index.js +2 -0
  214. package/packages/pi-tui/dist/index.js.map +1 -1
  215. package/packages/pi-tui/dist/style.d.ts +41 -0
  216. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  217. package/packages/pi-tui/dist/style.js +158 -0
  218. package/packages/pi-tui/dist/style.js.map +1 -0
  219. package/packages/pi-tui/dist/tui.d.ts +0 -1
  220. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  221. package/packages/pi-tui/dist/tui.js +3 -8
  222. package/packages/pi-tui/dist/tui.js.map +1 -1
  223. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  224. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  225. package/packages/pi-tui/src/index.ts +9 -0
  226. package/packages/pi-tui/src/style.ts +225 -0
  227. package/packages/pi-tui/src/tui.ts +3 -8
  228. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  229. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  230. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  231. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  232. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  233. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  234. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  235. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  236. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  237. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  238. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  239. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  240. package/src/resources/GSD-WORKFLOW.md +2 -2
  241. package/src/resources/extensions/github-sync/templates.ts +38 -8
  242. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  243. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  244. package/src/resources/extensions/gsd/auto/loop.ts +17 -10
  245. package/src/resources/extensions/gsd/auto/phases.ts +42 -28
  246. package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
  247. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  248. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  249. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  250. package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
  251. package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
  252. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  253. package/src/resources/extensions/gsd/auto.ts +12 -1
  254. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -1
  255. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -1
  256. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  257. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  258. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  259. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  260. package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
  261. package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
  262. package/src/resources/extensions/gsd/detection.ts +128 -0
  263. package/src/resources/extensions/gsd/graph.ts +12 -5
  264. package/src/resources/extensions/gsd/gsd-db.ts +119 -1
  265. package/src/resources/extensions/gsd/guided-flow.ts +2 -2
  266. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  267. package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  268. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  269. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  270. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  271. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  272. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +84 -5
  273. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
  274. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  275. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  276. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  277. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  278. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  279. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  280. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  281. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  282. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  283. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  284. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  285. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  286. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  287. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  288. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  289. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  290. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  291. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  292. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
  293. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  294. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  295. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  296. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  297. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  298. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  299. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  300. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  301. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  302. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_buildManifest.js +0 -0
  303. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_ssgManifest.js +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake.js","sourceRoot":"","sources":["../../src/providers/fake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAWvC,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAEvE,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAe,CAAC;AA4BxC,SAAS,eAAe,CAAC,IAAY;IACpC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjE,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC;YACJ,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACd,wCAAwC,IAAI,SAAS,CAAC,GAAG,CAAC,KAAM,GAAa,CAAC,OAAO,aAAa,IAAI,EAAE,CACxG,CAAC;QACH,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,eAAe,CAAC,GAAY;IACpC,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnD,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,QAAQ,CAAC,CAAc;IAC/B,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,OAAO,CAAC;IACpD,OAAO,CAAC,CAAC,OAAO;SACd,MAAM,CAAC,CAAC,CAAC,EAAuC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;SACrE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;SAClB,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC;AAED,SAAS,iBAAiB,CAAC,KAA6B,EAAE,GAAY,EAAE,IAAoB;IAC3F,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACtB,IAAI,CAAC,CAAC;QAAE,OAAO;IACf,MAAM,IAAI,GAAG,CAAC,GAAW,EAAS,EAAE;QACnC,6DAA6D;QAC7D,MAAM,MAAM,GAAG;YACd,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,KAAK,CAAC,EAAE;YACjB,YAAY,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM;YACjC,YAAY,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,GAAG,CAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI;YACzF,SAAS,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE;SAC9C,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,IAAI,0BAA0B,GAAG,eAAe,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAClH,CAAC,CAAC;IAEF,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;QACvD,IAAI,CAAC,oBAAoB,CAAC,CAAC,OAAO,SAAS,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,CAAC,CAAC,YAAY,KAAK,SAAS,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC;QAC5E,IAAI,CAAC,yBAAyB,CAAC,CAAC,YAAY,SAAS,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,CAAC,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI;YAAE,IAAI,CAAC,qCAAqC,CAAC,CAAC,YAAY,+BAA+B,CAAC,CAAC;QACpG,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAK,CAAC,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC,qCAAqC,CAAC,CAAC,YAAY,WAAW,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3F,CAAC;IACF,CAAC;IACD,IAAI,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;YACvC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,IAAI,CAAC,qCAAqC,MAAM,GAAG,CAAC,CAAC;QACjF,CAAC;IACF,CAAC;IACD,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3D,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QACzC,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACnF,IAAI,CAAC,sBAAsB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACvF,CAAC;IACF,CAAC;IACD,IAAI,CAAC,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,gBAAgB,EAAE,CAAC;YACjF,IAAI,CAAC,iDAAiD,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC;QAC9E,CAAC;IACF,CAAC;AACF,CAAC;AAED,SAAS,qBAAqB,CAC7B,KAA6B,EAC7B,IAAc;IAEd,MAAM,OAAO,GAAgC,EAAE,CAAC;IAChD,IAAI,UAAU,GAAmC,MAAM,CAAC;IAExD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAChD,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC;IACxC,CAAC;SAAM,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACrC,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9C,MAAM,EAAE,GAAa;gBACpB,IAAI,EAAE,UAAU;gBAChB,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,aAAa,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE;gBAC7C,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,SAAS,EAAE,IAAI,CAAC,KAAK;aACrB,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;QACD,UAAU,GAAG,SAAS,CAAC;IACxB,CAAC;IAED,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,OAAO;QACP,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,EAAE;QACf,KAAK,EAAE;YACN,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,CAAC;YACT,SAAS,EAAE,CAAC;YACZ,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;YACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;SACpE;QACD,UAAU;QACV,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACrB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAgC;IAClE,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxD,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,SAAS,QAAQ;QAChB,IAAI,MAAM,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACd,8BAA8B,MAAM,GAAG,CAAC,kCAAkC,UAAU,CAAC,MAAM,+BAA+B,IAAI,CAAC,cAAc,GAAG,CAChJ,CAAC;QACH,CAAC;QACD,OAAO,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,SAAS,UAAU,CAAC,KAA6B,EAAE,GAAY;QAC9D,MAAM,MAAM,GAAG,IAAI,2BAA2B,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;QAExB,yEAAyE;QACzE,uEAAuE;QACvE,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAEpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QAEvB,cAAc,CAAC,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC;gBACJ,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBAC/B,MAAM,QAAQ,GAAqB;wBAClC,IAAI,EAAE,WAAW;wBACjB,OAAO,EAAE,EAAE;wBACX,GAAG,EAAE,KAAK,CAAC,GAAG;wBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;wBACxB,KAAK,EAAE,KAAK,CAAC,EAAE;wBACf,KAAK,EAAE;4BACN,KAAK,EAAE,CAAC;4BACR,MAAM,EAAE,CAAC;4BACT,SAAS,EAAE,CAAC;4BACZ,UAAU,EAAE,CAAC;4BACb,WAAW,EAAE,CAAC;4BACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;yBACpE;wBACD,UAAU,EAAE,OAAO;wBACnB,YAAY,EAAE,IAAI,CAAC,OAAO,IAAI,qBAAqB;wBACnD,YAAY,EAAE,IAAI,CAAC,YAAY;wBAC/B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;qBACrB,CAAC;oBACF,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;oBACjE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBACrB,OAAO;gBACR,CAAC;gBAED,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBAC/B,kEAAkE;oBAClE,uDAAuD;oBACvD,MAAM,QAAQ,GAAqB;wBAClC,IAAI,EAAE,WAAW;wBACjB,OAAO,EAAE,EAAE;wBACX,GAAG,EAAE,KAAK,CAAC,GAAG;wBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;wBACxB,KAAK,EAAE,KAAK,CAAC,EAAE;wBACf,KAAK,EAAE;4BACN,KAAK,EAAE,CAAC;4BACR,MAAM,EAAE,CAAC;4BACT,SAAS,EAAE,CAAC;4BACZ,UAAU,EAAE,CAAC;4BACb,WAAW,EAAE,CAAC;4BACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;yBACpE;wBACD,UAAU,EAAE,OAAO;wBACnB,YAAY,EAAE,IAAI,CAAC,OAAO,IAAI,oBAAoB;wBAClD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;qBACrB,CAAC;oBACF,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;oBACjE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBACrB,OAAO;gBACR,CAAC;gBAED,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC;oBACrC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;oBAC/C,wEAAwE;oBACxE,MAAM,QAAQ,GAAqB;wBAClC,IAAI,EAAE,WAAW;wBACjB,OAAO,EAAE,EAAE;wBACX,GAAG,EAAE,KAAK,CAAC,GAAG;wBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;wBACxB,KAAK,EAAE,KAAK,CAAC,EAAE;wBACf,KAAK,EAAE;4BACN,KAAK,EAAE,CAAC;4BACR,MAAM,EAAE,CAAC;4BACT,SAAS,EAAE,CAAC;4BACZ,UAAU,EAAE,CAAC;4BACb,WAAW,EAAE,CAAC;4BACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;yBACpE;wBACD,UAAU,EAAE,OAAO;wBACnB,YAAY,EAAE,SAAS;wBACvB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;qBACrB,CAAC;oBACF,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;oBACjE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBACrB,OAAO;gBACR,CAAC;gBAED,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;gBACnD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;gBAErE,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC1B,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;oBACvE,MAAM,CAAC,IAAI,CAAC;wBACX,IAAI,EAAE,YAAY;wBAClB,YAAY,EAAE,CAAC;wBACf,KAAK,EAAE,IAAI,CAAC,IAAI;wBAChB,OAAO,EAAE,OAAO;qBAChB,CAAC,CAAC;oBACH,MAAM,CAAC,IAAI,CAAC;wBACX,IAAI,EAAE,UAAU;wBAChB,YAAY,EAAE,CAAC;wBACf,OAAO,EAAE,IAAI,CAAC,IAAI;wBAClB,OAAO,EAAE,OAAO;qBAChB,CAAC,CAAC;gBACJ,CAAC;qBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;oBACrC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;wBAChD,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU;4BAAE,SAAS;wBACpC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;wBAC3E,MAAM,CAAC,IAAI,CAAC;4BACX,IAAI,EAAE,cAAc;4BACpB,YAAY,EAAE,CAAC;4BACf,QAAQ,EAAE,CAAC;4BACX,OAAO,EAAE,OAAO;yBAChB,CAAC,CAAC;oBACJ,CAAC;gBACF,CAAC;gBAED,MAAM,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,MAAM;oBACZ,MAAM,EAAE,OAAO,CAAC,UAAyD;oBACzE,OAAO;iBACP,CAAC,CAAC;gBACH,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,QAAQ,GAAqB;oBAClC,IAAI,EAAE,WAAW;oBACjB,OAAO,EAAE,EAAE;oBACX,GAAG,EAAE,KAAK,CAAC,GAAG;oBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,KAAK,EAAE,KAAK,CAAC,EAAE;oBACf,KAAK,EAAE;wBACN,KAAK,EAAE,CAAC;wBACR,MAAM,EAAE,CAAC;wBACT,SAAS,EAAE,CAAC;wBACZ,UAAU,EAAE,CAAC;wBACb,WAAW,EAAE,CAAC;wBACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;qBACpE;oBACD,UAAU,EAAE,OAAO;oBACnB,YAAY,EAAG,GAAa,CAAC,OAAO;oBACpC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACrB,CAAC;gBACF,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACjE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACtB,CAAC;QACF,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IACf,CAAC;IAED,OAAO;QACN,GAAG,EAAE,QAAQ;QACb,MAAM,EAAE,CAAC,CAAC,KAA6B,EAAE,GAAY,EAAE,KAAqB,EAAE,EAAE,CAC/E,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAA2C;QAClE,YAAY,EAAE,CAAC,CAAC,KAA6B,EAAE,GAAY,EAAE,KAA2B,EAAE,EAAE,CAC3F,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAiD;KACxE,CAAC;AACH,CAAC","sourcesContent":["/**\n * GSD-2 fake LLM provider — deterministic JSONL replay for e2e tests.\n *\n * Activated only when `GSD_FAKE_LLM_TRANSCRIPT` env var is set. Reads a\n * JSONL transcript file (one turn per line) and replays scripted responses\n * sequentially. Each turn carries structural assertions about the incoming\n * request — if the request shape drifts, the provider fails loudly so tests\n * surface it instead of silently consuming wrong inputs.\n *\n * IMPORTANT: env var must be set BEFORE pi-ai is imported. Tests achieve\n * this by setting it on the subprocess they spawn. In-process tests cannot\n * mix fake and real providers in the same Node process.\n *\n * Transcript format (JSONL, one turn per line):\n * {\n * \"turn\": 1,\n * \"expect\": {\n * \"modelId\": \"gsd-fake-model\",\n * \"messageCount\": 2, // optional, exact match\n * \"lastUserText\": \"do X\", // optional, substring match\n * \"systemContains\": [\"...\"], // optional, all must be present\n * \"toolNames\": [\"read_file\"], // optional, exact set\n * \"hasToolResultFor\": \"read_file\" // optional, last message is a toolResult for this name\n * },\n * \"emit\": { \"kind\": \"text\", \"text\": \"...\", \"stopReason\": \"stop\" }\n * | { \"kind\": \"tool_use\", \"calls\": [...], \"stopReason\": \"toolUse\" }\n * | { \"kind\": \"error_429\", \"message\": \"rate limited\", \"retryAfterMs\": 1000 }\n * | { \"kind\": \"malformed\" }\n * | { \"kind\": \"timeout\", \"delayMs\": 60000 }\n * }\n */\n\nimport { readFileSync } from \"node:fs\";\nimport type { ApiProvider } from \"../api-registry.js\";\nimport type {\n\tAssistantMessage,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamOptions,\n\tToolCall,\n\tUserMessage,\n} from \"../types.js\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.js\";\n\nexport const FAKE_API = \"fake\" as const;\n\ninterface ExpectFields {\n\tmodelId?: string;\n\tmessageCount?: number;\n\tlastUserText?: string;\n\tsystemContains?: string[];\n\ttoolNames?: string[];\n\thasToolResultFor?: string;\n}\n\ntype EmitSpec =\n\t| { kind: \"text\"; text: string; stopReason?: \"stop\" | \"length\" }\n\t| {\n\t\t\tkind: \"tool_use\";\n\t\t\tcalls: { id?: string; name: string; input: Record<string, unknown> }[];\n\t\t\tstopReason?: \"toolUse\";\n\t }\n\t| { kind: \"error_429\"; message?: string; retryAfterMs?: number }\n\t| { kind: \"malformed\"; message?: string }\n\t| { kind: \"timeout\"; delayMs?: number };\n\ninterface TranscriptTurn {\n\tturn: number;\n\texpect?: ExpectFields;\n\temit: EmitSpec;\n}\n\nfunction parseTranscript(path: string): TranscriptTurn[] {\n\tconst raw = readFileSync(path, \"utf8\");\n\tconst lines = raw.split(\"\\n\").filter((l) => l.trim().length > 0);\n\tconst turns: TranscriptTurn[] = [];\n\tfor (const [i, line] of lines.entries()) {\n\t\ttry {\n\t\t\tturns.push(JSON.parse(line));\n\t\t} catch (err) {\n\t\t\tthrow new Error(\n\t\t\t\t`fake-llm: failed to parse transcript ${path} line ${i + 1}: ${(err as Error).message}\\n line: ${line}`,\n\t\t\t);\n\t\t}\n\t}\n\treturn turns;\n}\n\nfunction lastUserMessage(ctx: Context): UserMessage | undefined {\n\tfor (let i = ctx.messages.length - 1; i >= 0; i--) {\n\t\tconst m = ctx.messages[i];\n\t\tif (m.role === \"user\") return m;\n\t}\n\treturn undefined;\n}\n\nfunction userText(m: UserMessage): string {\n\tif (typeof m.content === \"string\") return m.content;\n\treturn m.content\n\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t.map((c) => c.text)\n\t\t.join(\"\\n\");\n}\n\nfunction checkExpectations(model: Model<typeof FAKE_API>, ctx: Context, turn: TranscriptTurn): void {\n\tconst e = turn.expect;\n\tif (!e) return;\n\tconst fail = (msg: string): never => {\n\t\t// Surface mismatch with enough context to debug, then throw.\n\t\tconst detail = {\n\t\t\tturn: turn.turn,\n\t\t\tmodelId: model.id,\n\t\t\tmessageCount: ctx.messages.length,\n\t\t\tlastUserText: lastUserMessage(ctx) ? userText(lastUserMessage(ctx)!).slice(0, 200) : null,\n\t\t\ttoolNames: ctx.tools?.map((t) => t.name) ?? [],\n\t\t};\n\t\tthrow new Error(`fake-llm: turn ${turn.turn} expectation mismatch: ${msg}\\n actual: ${JSON.stringify(detail)}`);\n\t};\n\n\tif (e.modelId !== undefined && model.id !== e.modelId) {\n\t\tfail(`expected modelId=${e.modelId}, got ${model.id}`);\n\t}\n\tif (e.messageCount !== undefined && ctx.messages.length !== e.messageCount) {\n\t\tfail(`expected messageCount=${e.messageCount}, got ${ctx.messages.length}`);\n\t}\n\tif (e.lastUserText !== undefined) {\n\t\tconst last = lastUserMessage(ctx);\n\t\tif (!last) fail(`expected lastUserText to contain \"${e.lastUserText}\", but no user messages found`);\n\t\tconst text = userText(last!);\n\t\tif (!text.includes(e.lastUserText)) {\n\t\t\tfail(`expected lastUserText to contain \"${e.lastUserText}\", got \"${text.slice(0, 200)}\"`);\n\t\t}\n\t}\n\tif (e.systemContains && e.systemContains.length > 0) {\n\t\tconst sys = ctx.systemPrompt ?? \"\";\n\t\tfor (const needle of e.systemContains) {\n\t\t\tif (!sys.includes(needle)) fail(`expected systemPrompt to contain \"${needle}\"`);\n\t\t}\n\t}\n\tif (e.toolNames) {\n\t\tconst actual = (ctx.tools ?? []).map((t) => t.name).sort();\n\t\tconst expected = [...e.toolNames].sort();\n\t\tif (actual.length !== expected.length || actual.some((n, i) => n !== expected[i])) {\n\t\t\tfail(`expected toolNames=${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);\n\t\t}\n\t}\n\tif (e.hasToolResultFor !== undefined) {\n\t\tconst last = ctx.messages[ctx.messages.length - 1];\n\t\tif (!last || last.role !== \"toolResult\" || last.toolName !== e.hasToolResultFor) {\n\t\t\tfail(`expected last message to be a toolResult for \"${e.hasToolResultFor}\"`);\n\t\t}\n\t}\n}\n\nfunction buildAssistantMessage(\n\tmodel: Model<typeof FAKE_API>,\n\temit: EmitSpec,\n): AssistantMessage {\n\tconst content: AssistantMessage[\"content\"] = [];\n\tlet stopReason: AssistantMessage[\"stopReason\"] = \"stop\";\n\n\tif (emit.kind === \"text\") {\n\t\tcontent.push({ type: \"text\", text: emit.text });\n\t\tstopReason = emit.stopReason ?? \"stop\";\n\t} else if (emit.kind === \"tool_use\") {\n\t\tfor (const [i, call] of emit.calls.entries()) {\n\t\t\tconst tc: ToolCall = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: call.id ?? `fake-tool-${Date.now()}-${i}`,\n\t\t\t\tname: call.name,\n\t\t\t\targuments: call.input,\n\t\t\t};\n\t\t\tcontent.push(tc);\n\t\t}\n\t\tstopReason = \"toolUse\";\n\t}\n\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent,\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason,\n\t\ttimestamp: Date.now(),\n\t};\n}\n\n/**\n * Create a fake provider bound to a transcript file. Each call to stream()\n * advances the turn cursor by one. Caller is responsible for ensuring the\n * transcript has enough turns.\n */\nexport function createFakeProvider(opts: { transcriptPath: string }): ApiProvider<typeof FAKE_API> {\n\tconst transcript = parseTranscript(opts.transcriptPath);\n\tlet cursor = 0;\n\n\tfunction nextTurn(): TranscriptTurn {\n\t\tif (cursor >= transcript.length) {\n\t\t\tthrow new Error(\n\t\t\t\t`fake-llm: provider invoked ${cursor + 1} times but transcript only has ${transcript.length} turns. Add another turn to ${opts.transcriptPath}.`,\n\t\t\t);\n\t\t}\n\t\treturn transcript[cursor++];\n\t}\n\n\tfunction streamTurn(model: Model<typeof FAKE_API>, ctx: Context): AssistantMessageEventStream {\n\t\tconst stream = new AssistantMessageEventStream();\n\t\tconst turn = nextTurn();\n\n\t\t// Synchronously validate expectations BEFORE doing any async work — this\n\t\t// way drift mismatches are reported with the request that caused them.\n\t\tcheckExpectations(model, ctx, turn);\n\n\t\tconst emit = turn.emit;\n\n\t\tqueueMicrotask(async () => {\n\t\t\ttry {\n\t\t\t\tif (emit.kind === \"error_429\") {\n\t\t\t\t\tconst errorMsg: AssistantMessage = {\n\t\t\t\t\t\trole: \"assistant\",\n\t\t\t\t\t\tcontent: [],\n\t\t\t\t\t\tapi: model.api,\n\t\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\t\tmodel: model.id,\n\t\t\t\t\t\tusage: {\n\t\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstopReason: \"error\",\n\t\t\t\t\t\terrorMessage: emit.message ?? \"rate_limit_exceeded\",\n\t\t\t\t\t\tretryAfterMs: emit.retryAfterMs,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t};\n\t\t\t\t\tstream.push({ type: \"error\", reason: \"error\", error: errorMsg });\n\t\t\t\t\tstream.end(errorMsg);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (emit.kind === \"malformed\") {\n\t\t\t\t\t// Simulate a provider that emits a corrupted/incomplete response.\n\t\t\t\t\t// The agent loop converts this to stopReason: \"error\".\n\t\t\t\t\tconst errorMsg: AssistantMessage = {\n\t\t\t\t\t\trole: \"assistant\",\n\t\t\t\t\t\tcontent: [],\n\t\t\t\t\t\tapi: model.api,\n\t\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\t\tmodel: model.id,\n\t\t\t\t\t\tusage: {\n\t\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstopReason: \"error\",\n\t\t\t\t\t\terrorMessage: emit.message ?? \"malformed_response\",\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t};\n\t\t\t\t\tstream.push({ type: \"error\", reason: \"error\", error: errorMsg });\n\t\t\t\t\tstream.end(errorMsg);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (emit.kind === \"timeout\") {\n\t\t\t\t\tconst delay = emit.delayMs ?? 60_000;\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, delay));\n\t\t\t\t\t// If the caller hasn't already aborted, emit a synthetic timeout error.\n\t\t\t\t\tconst errorMsg: AssistantMessage = {\n\t\t\t\t\t\trole: \"assistant\",\n\t\t\t\t\t\tcontent: [],\n\t\t\t\t\t\tapi: model.api,\n\t\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\t\tmodel: model.id,\n\t\t\t\t\t\tusage: {\n\t\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstopReason: \"error\",\n\t\t\t\t\t\terrorMessage: \"timeout\",\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t};\n\t\t\t\t\tstream.push({ type: \"error\", reason: \"error\", error: errorMsg });\n\t\t\t\t\tstream.end(errorMsg);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst message = buildAssistantMessage(model, emit);\n\t\t\t\tstream.push({ type: \"start\", partial: { ...message, content: [] } });\n\n\t\t\t\tif (emit.kind === \"text\") {\n\t\t\t\t\tstream.push({ type: \"text_start\", contentIndex: 0, partial: message });\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\tcontentIndex: 0,\n\t\t\t\t\t\tdelta: emit.text,\n\t\t\t\t\t\tpartial: message,\n\t\t\t\t\t});\n\t\t\t\t\tstream.push({\n\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\tcontentIndex: 0,\n\t\t\t\t\t\tcontent: emit.text,\n\t\t\t\t\t\tpartial: message,\n\t\t\t\t\t});\n\t\t\t\t} else if (emit.kind === \"tool_use\") {\n\t\t\t\t\tfor (const [i, c] of message.content.entries()) {\n\t\t\t\t\t\tif (c.type !== \"toolCall\") continue;\n\t\t\t\t\t\tstream.push({ type: \"toolcall_start\", contentIndex: i, partial: message });\n\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\t\t\tcontentIndex: i,\n\t\t\t\t\t\t\ttoolCall: c,\n\t\t\t\t\t\t\tpartial: message,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tstream.push({\n\t\t\t\t\ttype: \"done\",\n\t\t\t\t\treason: message.stopReason as \"stop\" | \"length\" | \"toolUse\" | \"pauseTurn\",\n\t\t\t\t\tmessage,\n\t\t\t\t});\n\t\t\t\tstream.end(message);\n\t\t\t} catch (err) {\n\t\t\t\tconst errorMsg: AssistantMessage = {\n\t\t\t\t\trole: \"assistant\",\n\t\t\t\t\tcontent: [],\n\t\t\t\t\tapi: model.api,\n\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\tmodel: model.id,\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t\t},\n\t\t\t\t\tstopReason: \"error\",\n\t\t\t\t\terrorMessage: (err as Error).message,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t};\n\t\t\t\tstream.push({ type: \"error\", reason: \"error\", error: errorMsg });\n\t\t\t\tstream.end(errorMsg);\n\t\t\t}\n\t\t});\n\n\t\treturn stream;\n\t}\n\n\treturn {\n\t\tapi: FAKE_API,\n\t\tstream: ((model: Model<typeof FAKE_API>, ctx: Context, _opts?: StreamOptions) =>\n\t\t\tstreamTurn(model, ctx)) as ApiProvider<typeof FAKE_API>[\"stream\"],\n\t\tstreamSimple: ((model: Model<typeof FAKE_API>, ctx: Context, _opts?: SimpleStreamOptions) =>\n\t\t\tstreamTurn(model, ctx)) as ApiProvider<typeof FAKE_API>[\"streamSimple\"],\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"register-builtins.d.ts","sourceRoot":"","sources":["../../src/providers/register-builtins.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAoB,qBAAqB,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEhH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAY1D,UAAU,qBAAqB;IAC9B,aAAa,EAAE,CACd,KAAK,EAAE,KAAK,CAAC,yBAAyB,CAAC,EACvC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,cAAc,KACpB,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAC1C,mBAAmB,EAAE,CACpB,KAAK,EAAE,KAAK,CAAC,yBAAyB,CAAC,EACvC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,KACzB,aAAa,CAAC,qBAAqB,CAAC,CAAC;CAC1C;AASD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAE5E;AAsJD,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC"}
1
+ {"version":3,"file":"register-builtins.d.ts","sourceRoot":"","sources":["../../src/providers/register-builtins.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAoB,qBAAqB,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEhH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAY1D,UAAU,qBAAqB;IAC9B,aAAa,EAAE,CACd,KAAK,EAAE,KAAK,CAAC,yBAAyB,CAAC,EACvC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,cAAc,KACpB,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAC1C,mBAAmB,EAAE,CACpB,KAAK,EAAE,KAAK,CAAC,yBAAyB,CAAC,EACvC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,KACzB,aAAa,CAAC,qBAAqB,CAAC,CAAC;CAC1C;AASD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAE5E;AAsJD,wBAAgB,iBAAiB,IAAI,IAAI,CAIxC"}
@@ -1,4 +1,5 @@
1
1
  import { clearApiProviders, registerApiProvider } from "../api-registry.js";
2
+ import { createFakeProvider } from "./fake.js";
2
3
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
3
4
  import { streamAnthropic, streamSimpleAnthropic } from "./anthropic.js";
4
5
  import { streamAnthropicVertex, streamSimpleAnthropicVertex } from "./anthropic-vertex.js";
@@ -139,6 +140,29 @@ function registerBuiltInApiProviders() {
139
140
  export function resetApiProviders() {
140
141
  clearApiProviders();
141
142
  registerBuiltInApiProviders();
143
+ registerFakeProviderIfEnabled();
144
+ }
145
+ /**
146
+ * E2E-test-only: when `GSD_FAKE_LLM_TRANSCRIPT` is set, register a
147
+ * deterministic JSONL-replay provider under api "fake". The env var must
148
+ * be set BEFORE this module is imported. Subprocess-spawned e2e tests do
149
+ * this by setting it on the spawn env.
150
+ *
151
+ * Synchronous registration: any failure throws here so the CLI startup
152
+ * fails loudly instead of silently falling through to a real provider.
153
+ */
154
+ function registerFakeProviderIfEnabled() {
155
+ const transcriptPath = process.env.GSD_FAKE_LLM_TRANSCRIPT;
156
+ if (!transcriptPath)
157
+ return;
158
+ try {
159
+ registerApiProvider(createFakeProvider({ transcriptPath }), "fake");
160
+ }
161
+ catch (err) {
162
+ process.stderr.write(`fake-llm: failed to register: ${err.message}\n`);
163
+ throw err;
164
+ }
142
165
  }
143
166
  registerBuiltInApiProviders();
167
+ registerFakeProviderIfEnabled();
144
168
  //# sourceMappingURL=register-builtins.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"register-builtins.js","sourceRoot":"","sources":["../../src/providers/register-builtins.ts"],"names":[],"mappings":"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","sourcesContent":["import { clearApiProviders, registerApiProvider } from \"../api-registry.js\";\nimport type { AssistantMessage, AssistantMessageEvent, Context, Model, SimpleStreamOptions } from \"../types.js\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.js\";\nimport type { BedrockOptions } from \"./amazon-bedrock.js\";\nimport { streamAnthropic, streamSimpleAnthropic } from \"./anthropic.js\";\nimport { streamAnthropicVertex, streamSimpleAnthropicVertex } from \"./anthropic-vertex.js\";\nimport { streamAzureOpenAIResponses, streamSimpleAzureOpenAIResponses } from \"./azure-openai-responses.js\";\nimport { streamGoogle, streamSimpleGoogle } from \"./google.js\";\nimport { streamGoogleGeminiCli, streamSimpleGoogleGeminiCli } from \"./google-gemini-cli.js\";\nimport { streamGoogleVertex, streamSimpleGoogleVertex } from \"./google-vertex.js\";\nimport { streamMistral, streamSimpleMistral } from \"./mistral.js\";\nimport { streamOpenAICodexResponses, streamSimpleOpenAICodexResponses } from \"./openai-codex-responses.js\";\nimport { streamOpenAICompletions, streamSimpleOpenAICompletions } from \"./openai-completions.js\";\nimport { streamOpenAIResponses, streamSimpleOpenAIResponses } from \"./openai-responses.js\";\n\ninterface BedrockProviderModule {\n\tstreamBedrock: (\n\t\tmodel: Model<\"bedrock-converse-stream\">,\n\t\tcontext: Context,\n\t\toptions?: BedrockOptions,\n\t) => AsyncIterable<AssistantMessageEvent>;\n\tstreamSimpleBedrock: (\n\t\tmodel: Model<\"bedrock-converse-stream\">,\n\t\tcontext: Context,\n\t\toptions?: SimpleStreamOptions,\n\t) => AsyncIterable<AssistantMessageEvent>;\n}\n\ntype DynamicImport = (specifier: string) => Promise<unknown>;\n\nconst dynamicImport: DynamicImport = (specifier) => import(specifier);\nconst BEDROCK_PROVIDER_SPECIFIER = \"./amazon-\" + \"bedrock.js\";\n\nlet bedrockProviderModuleOverride: BedrockProviderModule | undefined;\n\nexport function setBedrockProviderModule(module: BedrockProviderModule): void {\n\tbedrockProviderModuleOverride = module;\n}\n\nasync function loadBedrockProviderModule(): Promise<BedrockProviderModule> {\n\tif (bedrockProviderModuleOverride) {\n\t\treturn bedrockProviderModuleOverride;\n\t}\n\tconst module = await dynamicImport(BEDROCK_PROVIDER_SPECIFIER);\n\treturn module as BedrockProviderModule;\n}\n\nfunction forwardStream(target: AssistantMessageEventStream, source: AsyncIterable<AssistantMessageEvent>): void {\n\t(async () => {\n\t\tfor await (const event of source) {\n\t\t\ttarget.push(event);\n\t\t}\n\t\ttarget.end();\n\t})();\n}\n\nfunction createLazyLoadErrorMessage(model: Model<\"bedrock-converse-stream\">, error: unknown): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: \"bedrock-converse-stream\",\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction streamBedrockLazy(\n\tmodel: Model<\"bedrock-converse-stream\">,\n\tcontext: Context,\n\toptions?: BedrockOptions,\n): AssistantMessageEventStream {\n\tconst outer = new AssistantMessageEventStream();\n\n\tloadBedrockProviderModule()\n\t\t.then((module) => {\n\t\t\tconst inner = module.streamBedrock(model, context, options);\n\t\t\tforwardStream(outer, inner);\n\t\t})\n\t\t.catch((error) => {\n\t\t\tconst message = createLazyLoadErrorMessage(model, error);\n\t\t\touter.push({ type: \"error\", reason: \"error\", error: message });\n\t\t\touter.end(message);\n\t\t});\n\n\treturn outer;\n}\n\nfunction streamSimpleBedrockLazy(\n\tmodel: Model<\"bedrock-converse-stream\">,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream {\n\tconst outer = new AssistantMessageEventStream();\n\n\tloadBedrockProviderModule()\n\t\t.then((module) => {\n\t\t\tconst inner = module.streamSimpleBedrock(model, context, options);\n\t\t\tforwardStream(outer, inner);\n\t\t})\n\t\t.catch((error) => {\n\t\t\tconst message = createLazyLoadErrorMessage(model, error);\n\t\t\touter.push({ type: \"error\", reason: \"error\", error: message });\n\t\t\touter.end(message);\n\t\t});\n\n\treturn outer;\n}\n\nfunction registerBuiltInApiProviders(): void {\n\tregisterApiProvider({\n\t\tapi: \"anthropic-messages\",\n\t\tstream: streamAnthropic,\n\t\tstreamSimple: streamSimpleAnthropic,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"openai-completions\",\n\t\tstream: streamOpenAICompletions,\n\t\tstreamSimple: streamSimpleOpenAICompletions,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"mistral-conversations\",\n\t\tstream: streamMistral,\n\t\tstreamSimple: streamSimpleMistral,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"openai-responses\",\n\t\tstream: streamOpenAIResponses,\n\t\tstreamSimple: streamSimpleOpenAIResponses,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"azure-openai-responses\",\n\t\tstream: streamAzureOpenAIResponses,\n\t\tstreamSimple: streamSimpleAzureOpenAIResponses,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"openai-codex-responses\",\n\t\tstream: streamOpenAICodexResponses,\n\t\tstreamSimple: streamSimpleOpenAICodexResponses,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"google-generative-ai\",\n\t\tstream: streamGoogle,\n\t\tstreamSimple: streamSimpleGoogle,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"google-gemini-cli\",\n\t\tstream: streamGoogleGeminiCli,\n\t\tstreamSimple: streamSimpleGoogleGeminiCli,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"google-vertex\",\n\t\tstream: streamGoogleVertex,\n\t\tstreamSimple: streamSimpleGoogleVertex,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"anthropic-vertex\",\n\t\tstream: streamAnthropicVertex,\n\t\tstreamSimple: streamSimpleAnthropicVertex,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"bedrock-converse-stream\",\n\t\tstream: streamBedrockLazy,\n\t\tstreamSimple: streamSimpleBedrockLazy,\n\t});\n}\n\nexport function resetApiProviders(): void {\n\tclearApiProviders();\n\tregisterBuiltInApiProviders();\n}\n\nregisterBuiltInApiProviders();\n"]}
1
+ {"version":3,"file":"register-builtins.js","sourceRoot":"","sources":["../../src/providers/register-builtins.ts"],"names":[],"mappings":"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","sourcesContent":["import { clearApiProviders, registerApiProvider } from \"../api-registry.js\";\nimport { createFakeProvider } from \"./fake.js\";\nimport type { AssistantMessage, AssistantMessageEvent, Context, Model, SimpleStreamOptions } from \"../types.js\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.js\";\nimport type { BedrockOptions } from \"./amazon-bedrock.js\";\nimport { streamAnthropic, streamSimpleAnthropic } from \"./anthropic.js\";\nimport { streamAnthropicVertex, streamSimpleAnthropicVertex } from \"./anthropic-vertex.js\";\nimport { streamAzureOpenAIResponses, streamSimpleAzureOpenAIResponses } from \"./azure-openai-responses.js\";\nimport { streamGoogle, streamSimpleGoogle } from \"./google.js\";\nimport { streamGoogleGeminiCli, streamSimpleGoogleGeminiCli } from \"./google-gemini-cli.js\";\nimport { streamGoogleVertex, streamSimpleGoogleVertex } from \"./google-vertex.js\";\nimport { streamMistral, streamSimpleMistral } from \"./mistral.js\";\nimport { streamOpenAICodexResponses, streamSimpleOpenAICodexResponses } from \"./openai-codex-responses.js\";\nimport { streamOpenAICompletions, streamSimpleOpenAICompletions } from \"./openai-completions.js\";\nimport { streamOpenAIResponses, streamSimpleOpenAIResponses } from \"./openai-responses.js\";\n\ninterface BedrockProviderModule {\n\tstreamBedrock: (\n\t\tmodel: Model<\"bedrock-converse-stream\">,\n\t\tcontext: Context,\n\t\toptions?: BedrockOptions,\n\t) => AsyncIterable<AssistantMessageEvent>;\n\tstreamSimpleBedrock: (\n\t\tmodel: Model<\"bedrock-converse-stream\">,\n\t\tcontext: Context,\n\t\toptions?: SimpleStreamOptions,\n\t) => AsyncIterable<AssistantMessageEvent>;\n}\n\ntype DynamicImport = (specifier: string) => Promise<unknown>;\n\nconst dynamicImport: DynamicImport = (specifier) => import(specifier);\nconst BEDROCK_PROVIDER_SPECIFIER = \"./amazon-\" + \"bedrock.js\";\n\nlet bedrockProviderModuleOverride: BedrockProviderModule | undefined;\n\nexport function setBedrockProviderModule(module: BedrockProviderModule): void {\n\tbedrockProviderModuleOverride = module;\n}\n\nasync function loadBedrockProviderModule(): Promise<BedrockProviderModule> {\n\tif (bedrockProviderModuleOverride) {\n\t\treturn bedrockProviderModuleOverride;\n\t}\n\tconst module = await dynamicImport(BEDROCK_PROVIDER_SPECIFIER);\n\treturn module as BedrockProviderModule;\n}\n\nfunction forwardStream(target: AssistantMessageEventStream, source: AsyncIterable<AssistantMessageEvent>): void {\n\t(async () => {\n\t\tfor await (const event of source) {\n\t\t\ttarget.push(event);\n\t\t}\n\t\ttarget.end();\n\t})();\n}\n\nfunction createLazyLoadErrorMessage(model: Model<\"bedrock-converse-stream\">, error: unknown): AssistantMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: \"bedrock-converse-stream\",\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction streamBedrockLazy(\n\tmodel: Model<\"bedrock-converse-stream\">,\n\tcontext: Context,\n\toptions?: BedrockOptions,\n): AssistantMessageEventStream {\n\tconst outer = new AssistantMessageEventStream();\n\n\tloadBedrockProviderModule()\n\t\t.then((module) => {\n\t\t\tconst inner = module.streamBedrock(model, context, options);\n\t\t\tforwardStream(outer, inner);\n\t\t})\n\t\t.catch((error) => {\n\t\t\tconst message = createLazyLoadErrorMessage(model, error);\n\t\t\touter.push({ type: \"error\", reason: \"error\", error: message });\n\t\t\touter.end(message);\n\t\t});\n\n\treturn outer;\n}\n\nfunction streamSimpleBedrockLazy(\n\tmodel: Model<\"bedrock-converse-stream\">,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream {\n\tconst outer = new AssistantMessageEventStream();\n\n\tloadBedrockProviderModule()\n\t\t.then((module) => {\n\t\t\tconst inner = module.streamSimpleBedrock(model, context, options);\n\t\t\tforwardStream(outer, inner);\n\t\t})\n\t\t.catch((error) => {\n\t\t\tconst message = createLazyLoadErrorMessage(model, error);\n\t\t\touter.push({ type: \"error\", reason: \"error\", error: message });\n\t\t\touter.end(message);\n\t\t});\n\n\treturn outer;\n}\n\nfunction registerBuiltInApiProviders(): void {\n\tregisterApiProvider({\n\t\tapi: \"anthropic-messages\",\n\t\tstream: streamAnthropic,\n\t\tstreamSimple: streamSimpleAnthropic,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"openai-completions\",\n\t\tstream: streamOpenAICompletions,\n\t\tstreamSimple: streamSimpleOpenAICompletions,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"mistral-conversations\",\n\t\tstream: streamMistral,\n\t\tstreamSimple: streamSimpleMistral,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"openai-responses\",\n\t\tstream: streamOpenAIResponses,\n\t\tstreamSimple: streamSimpleOpenAIResponses,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"azure-openai-responses\",\n\t\tstream: streamAzureOpenAIResponses,\n\t\tstreamSimple: streamSimpleAzureOpenAIResponses,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"openai-codex-responses\",\n\t\tstream: streamOpenAICodexResponses,\n\t\tstreamSimple: streamSimpleOpenAICodexResponses,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"google-generative-ai\",\n\t\tstream: streamGoogle,\n\t\tstreamSimple: streamSimpleGoogle,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"google-gemini-cli\",\n\t\tstream: streamGoogleGeminiCli,\n\t\tstreamSimple: streamSimpleGoogleGeminiCli,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"google-vertex\",\n\t\tstream: streamGoogleVertex,\n\t\tstreamSimple: streamSimpleGoogleVertex,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"anthropic-vertex\",\n\t\tstream: streamAnthropicVertex,\n\t\tstreamSimple: streamSimpleAnthropicVertex,\n\t});\n\n\tregisterApiProvider({\n\t\tapi: \"bedrock-converse-stream\",\n\t\tstream: streamBedrockLazy,\n\t\tstreamSimple: streamSimpleBedrockLazy,\n\t});\n}\n\nexport function resetApiProviders(): void {\n\tclearApiProviders();\n\tregisterBuiltInApiProviders();\n\tregisterFakeProviderIfEnabled();\n}\n\n/**\n * E2E-test-only: when `GSD_FAKE_LLM_TRANSCRIPT` is set, register a\n * deterministic JSONL-replay provider under api \"fake\". The env var must\n * be set BEFORE this module is imported. Subprocess-spawned e2e tests do\n * this by setting it on the spawn env.\n *\n * Synchronous registration: any failure throws here so the CLI startup\n * fails loudly instead of silently falling through to a real provider.\n */\nfunction registerFakeProviderIfEnabled(): void {\n\tconst transcriptPath = process.env.GSD_FAKE_LLM_TRANSCRIPT;\n\tif (!transcriptPath) return;\n\ttry {\n\t\tregisterApiProvider(createFakeProvider({ transcriptPath }), \"fake\");\n\t} catch (err) {\n\t\tprocess.stderr.write(`fake-llm: failed to register: ${(err as Error).message}\\n`);\n\t\tthrow err;\n\t}\n}\n\nregisterBuiltInApiProviders();\nregisterFakeProviderIfEnabled();\n"]}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * GSD-2 fake model — paired with the fake LLM provider for e2e tests.
3
+ *
4
+ * Only registered when `GSD_FAKE_LLM_TRANSCRIPT` env var is set, via the
5
+ * conditional branch in models/index.ts. The model is invisible to normal
6
+ * users; it shows up in `--list-models` only inside e2e test subprocesses.
7
+ */
8
+
9
+ import type { Model } from "../types.js";
10
+
11
+ export const FAKE_PROVIDER = "gsd-fake" as const;
12
+ export const FAKE_MODEL_ID = "gsd-fake-model" as const;
13
+
14
+ export const FAKE_MODEL: Model<"fake"> = {
15
+ id: FAKE_MODEL_ID,
16
+ name: "GSD Fake (e2e replay)",
17
+ api: "fake",
18
+ provider: FAKE_PROVIDER,
19
+ baseUrl: "https://fake.gsd.local/v1",
20
+ reasoning: false,
21
+ input: ["text"],
22
+ cost: {
23
+ input: 0,
24
+ output: 0,
25
+ cacheRead: 0,
26
+ cacheWrite: 0,
27
+ },
28
+ contextWindow: 200_000,
29
+ maxTokens: 8_192,
30
+ };
@@ -1,6 +1,7 @@
1
1
  import { MODELS } from "./generated/index.js";
2
2
  import { CUSTOM_MODELS } from "./custom.js";
3
3
  import { CAPABILITY_PATCHES, applyCapabilityPatches } from "./capability-patches.js";
4
+ import { FAKE_MODEL, FAKE_MODEL_ID, FAKE_PROVIDER } from "./fake-model.js";
4
5
  import type { Api, KnownProvider, Model, Usage } from "../types.js";
5
6
 
6
7
  const modelRegistry: Map<string, Map<string, Model<Api>>> = new Map();
@@ -29,6 +30,14 @@ for (const [provider, models] of Object.entries(CUSTOM_MODELS)) {
29
30
  }
30
31
  }
31
32
 
33
+ // E2E-test-only: register the fake model when GSD_FAKE_LLM_TRANSCRIPT is set.
34
+ // Env var must be set BEFORE this module is imported. See providers/fake.ts.
35
+ if (process.env.GSD_FAKE_LLM_TRANSCRIPT) {
36
+ const providerModels = new Map<string, Model<Api>>();
37
+ providerModels.set(FAKE_MODEL_ID, FAKE_MODEL as Model<Api>);
38
+ modelRegistry.set(FAKE_PROVIDER, providerModels);
39
+ }
40
+
32
41
  // Apply patches to the static registry at module load
33
42
  for (const [, providerModels] of modelRegistry) {
34
43
  for (const [id, model] of providerModels) {
@@ -0,0 +1,376 @@
1
+ /**
2
+ * GSD-2 fake LLM provider — deterministic JSONL replay for e2e tests.
3
+ *
4
+ * Activated only when `GSD_FAKE_LLM_TRANSCRIPT` env var is set. Reads a
5
+ * JSONL transcript file (one turn per line) and replays scripted responses
6
+ * sequentially. Each turn carries structural assertions about the incoming
7
+ * request — if the request shape drifts, the provider fails loudly so tests
8
+ * surface it instead of silently consuming wrong inputs.
9
+ *
10
+ * IMPORTANT: env var must be set BEFORE pi-ai is imported. Tests achieve
11
+ * this by setting it on the subprocess they spawn. In-process tests cannot
12
+ * mix fake and real providers in the same Node process.
13
+ *
14
+ * Transcript format (JSONL, one turn per line):
15
+ * {
16
+ * "turn": 1,
17
+ * "expect": {
18
+ * "modelId": "gsd-fake-model",
19
+ * "messageCount": 2, // optional, exact match
20
+ * "lastUserText": "do X", // optional, substring match
21
+ * "systemContains": ["..."], // optional, all must be present
22
+ * "toolNames": ["read_file"], // optional, exact set
23
+ * "hasToolResultFor": "read_file" // optional, last message is a toolResult for this name
24
+ * },
25
+ * "emit": { "kind": "text", "text": "...", "stopReason": "stop" }
26
+ * | { "kind": "tool_use", "calls": [...], "stopReason": "toolUse" }
27
+ * | { "kind": "error_429", "message": "rate limited", "retryAfterMs": 1000 }
28
+ * | { "kind": "malformed" }
29
+ * | { "kind": "timeout", "delayMs": 60000 }
30
+ * }
31
+ */
32
+
33
+ import { readFileSync } from "node:fs";
34
+ import type { ApiProvider } from "../api-registry.js";
35
+ import type {
36
+ AssistantMessage,
37
+ Context,
38
+ Model,
39
+ SimpleStreamOptions,
40
+ StreamOptions,
41
+ ToolCall,
42
+ UserMessage,
43
+ } from "../types.js";
44
+ import { AssistantMessageEventStream } from "../utils/event-stream.js";
45
+
46
+ export const FAKE_API = "fake" as const;
47
+
48
+ interface ExpectFields {
49
+ modelId?: string;
50
+ messageCount?: number;
51
+ lastUserText?: string;
52
+ systemContains?: string[];
53
+ toolNames?: string[];
54
+ hasToolResultFor?: string;
55
+ }
56
+
57
+ type EmitSpec =
58
+ | { kind: "text"; text: string; stopReason?: "stop" | "length" }
59
+ | {
60
+ kind: "tool_use";
61
+ calls: { id?: string; name: string; input: Record<string, unknown> }[];
62
+ stopReason?: "toolUse";
63
+ }
64
+ | { kind: "error_429"; message?: string; retryAfterMs?: number }
65
+ | { kind: "malformed"; message?: string }
66
+ | { kind: "timeout"; delayMs?: number };
67
+
68
+ interface TranscriptTurn {
69
+ turn: number;
70
+ expect?: ExpectFields;
71
+ emit: EmitSpec;
72
+ }
73
+
74
+ function parseTranscript(path: string): TranscriptTurn[] {
75
+ const raw = readFileSync(path, "utf8");
76
+ const lines = raw.split("\n").filter((l) => l.trim().length > 0);
77
+ const turns: TranscriptTurn[] = [];
78
+ for (const [i, line] of lines.entries()) {
79
+ try {
80
+ turns.push(JSON.parse(line));
81
+ } catch (err) {
82
+ throw new Error(
83
+ `fake-llm: failed to parse transcript ${path} line ${i + 1}: ${(err as Error).message}\n line: ${line}`,
84
+ );
85
+ }
86
+ }
87
+ return turns;
88
+ }
89
+
90
+ function lastUserMessage(ctx: Context): UserMessage | undefined {
91
+ for (let i = ctx.messages.length - 1; i >= 0; i--) {
92
+ const m = ctx.messages[i];
93
+ if (m.role === "user") return m;
94
+ }
95
+ return undefined;
96
+ }
97
+
98
+ function userText(m: UserMessage): string {
99
+ if (typeof m.content === "string") return m.content;
100
+ return m.content
101
+ .filter((c): c is { type: "text"; text: string } => c.type === "text")
102
+ .map((c) => c.text)
103
+ .join("\n");
104
+ }
105
+
106
+ function checkExpectations(model: Model<typeof FAKE_API>, ctx: Context, turn: TranscriptTurn): void {
107
+ const e = turn.expect;
108
+ if (!e) return;
109
+ const fail = (msg: string): never => {
110
+ // Surface mismatch with enough context to debug, then throw.
111
+ const detail = {
112
+ turn: turn.turn,
113
+ modelId: model.id,
114
+ messageCount: ctx.messages.length,
115
+ lastUserText: lastUserMessage(ctx) ? userText(lastUserMessage(ctx)!).slice(0, 200) : null,
116
+ toolNames: ctx.tools?.map((t) => t.name) ?? [],
117
+ };
118
+ throw new Error(`fake-llm: turn ${turn.turn} expectation mismatch: ${msg}\n actual: ${JSON.stringify(detail)}`);
119
+ };
120
+
121
+ if (e.modelId !== undefined && model.id !== e.modelId) {
122
+ fail(`expected modelId=${e.modelId}, got ${model.id}`);
123
+ }
124
+ if (e.messageCount !== undefined && ctx.messages.length !== e.messageCount) {
125
+ fail(`expected messageCount=${e.messageCount}, got ${ctx.messages.length}`);
126
+ }
127
+ if (e.lastUserText !== undefined) {
128
+ const last = lastUserMessage(ctx);
129
+ if (!last) fail(`expected lastUserText to contain "${e.lastUserText}", but no user messages found`);
130
+ const text = userText(last!);
131
+ if (!text.includes(e.lastUserText)) {
132
+ fail(`expected lastUserText to contain "${e.lastUserText}", got "${text.slice(0, 200)}"`);
133
+ }
134
+ }
135
+ if (e.systemContains && e.systemContains.length > 0) {
136
+ const sys = ctx.systemPrompt ?? "";
137
+ for (const needle of e.systemContains) {
138
+ if (!sys.includes(needle)) fail(`expected systemPrompt to contain "${needle}"`);
139
+ }
140
+ }
141
+ if (e.toolNames) {
142
+ const actual = (ctx.tools ?? []).map((t) => t.name).sort();
143
+ const expected = [...e.toolNames].sort();
144
+ if (actual.length !== expected.length || actual.some((n, i) => n !== expected[i])) {
145
+ fail(`expected toolNames=${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
146
+ }
147
+ }
148
+ if (e.hasToolResultFor !== undefined) {
149
+ const last = ctx.messages[ctx.messages.length - 1];
150
+ if (!last || last.role !== "toolResult" || last.toolName !== e.hasToolResultFor) {
151
+ fail(`expected last message to be a toolResult for "${e.hasToolResultFor}"`);
152
+ }
153
+ }
154
+ }
155
+
156
+ function buildAssistantMessage(
157
+ model: Model<typeof FAKE_API>,
158
+ emit: EmitSpec,
159
+ ): AssistantMessage {
160
+ const content: AssistantMessage["content"] = [];
161
+ let stopReason: AssistantMessage["stopReason"] = "stop";
162
+
163
+ if (emit.kind === "text") {
164
+ content.push({ type: "text", text: emit.text });
165
+ stopReason = emit.stopReason ?? "stop";
166
+ } else if (emit.kind === "tool_use") {
167
+ for (const [i, call] of emit.calls.entries()) {
168
+ const tc: ToolCall = {
169
+ type: "toolCall",
170
+ id: call.id ?? `fake-tool-${Date.now()}-${i}`,
171
+ name: call.name,
172
+ arguments: call.input,
173
+ };
174
+ content.push(tc);
175
+ }
176
+ stopReason = "toolUse";
177
+ }
178
+
179
+ return {
180
+ role: "assistant",
181
+ content,
182
+ api: model.api,
183
+ provider: model.provider,
184
+ model: model.id,
185
+ usage: {
186
+ input: 0,
187
+ output: 0,
188
+ cacheRead: 0,
189
+ cacheWrite: 0,
190
+ totalTokens: 0,
191
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
192
+ },
193
+ stopReason,
194
+ timestamp: Date.now(),
195
+ };
196
+ }
197
+
198
+ /**
199
+ * Create a fake provider bound to a transcript file. Each call to stream()
200
+ * advances the turn cursor by one. Caller is responsible for ensuring the
201
+ * transcript has enough turns.
202
+ */
203
+ export function createFakeProvider(opts: { transcriptPath: string }): ApiProvider<typeof FAKE_API> {
204
+ const transcript = parseTranscript(opts.transcriptPath);
205
+ let cursor = 0;
206
+
207
+ function nextTurn(): TranscriptTurn {
208
+ if (cursor >= transcript.length) {
209
+ throw new Error(
210
+ `fake-llm: provider invoked ${cursor + 1} times but transcript only has ${transcript.length} turns. Add another turn to ${opts.transcriptPath}.`,
211
+ );
212
+ }
213
+ return transcript[cursor++];
214
+ }
215
+
216
+ function streamTurn(model: Model<typeof FAKE_API>, ctx: Context): AssistantMessageEventStream {
217
+ const stream = new AssistantMessageEventStream();
218
+ const turn = nextTurn();
219
+
220
+ // Synchronously validate expectations BEFORE doing any async work — this
221
+ // way drift mismatches are reported with the request that caused them.
222
+ checkExpectations(model, ctx, turn);
223
+
224
+ const emit = turn.emit;
225
+
226
+ queueMicrotask(async () => {
227
+ try {
228
+ if (emit.kind === "error_429") {
229
+ const errorMsg: AssistantMessage = {
230
+ role: "assistant",
231
+ content: [],
232
+ api: model.api,
233
+ provider: model.provider,
234
+ model: model.id,
235
+ usage: {
236
+ input: 0,
237
+ output: 0,
238
+ cacheRead: 0,
239
+ cacheWrite: 0,
240
+ totalTokens: 0,
241
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
242
+ },
243
+ stopReason: "error",
244
+ errorMessage: emit.message ?? "rate_limit_exceeded",
245
+ retryAfterMs: emit.retryAfterMs,
246
+ timestamp: Date.now(),
247
+ };
248
+ stream.push({ type: "error", reason: "error", error: errorMsg });
249
+ stream.end(errorMsg);
250
+ return;
251
+ }
252
+
253
+ if (emit.kind === "malformed") {
254
+ // Simulate a provider that emits a corrupted/incomplete response.
255
+ // The agent loop converts this to stopReason: "error".
256
+ const errorMsg: AssistantMessage = {
257
+ role: "assistant",
258
+ content: [],
259
+ api: model.api,
260
+ provider: model.provider,
261
+ model: model.id,
262
+ usage: {
263
+ input: 0,
264
+ output: 0,
265
+ cacheRead: 0,
266
+ cacheWrite: 0,
267
+ totalTokens: 0,
268
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
269
+ },
270
+ stopReason: "error",
271
+ errorMessage: emit.message ?? "malformed_response",
272
+ timestamp: Date.now(),
273
+ };
274
+ stream.push({ type: "error", reason: "error", error: errorMsg });
275
+ stream.end(errorMsg);
276
+ return;
277
+ }
278
+
279
+ if (emit.kind === "timeout") {
280
+ const delay = emit.delayMs ?? 60_000;
281
+ await new Promise((r) => setTimeout(r, delay));
282
+ // If the caller hasn't already aborted, emit a synthetic timeout error.
283
+ const errorMsg: AssistantMessage = {
284
+ role: "assistant",
285
+ content: [],
286
+ api: model.api,
287
+ provider: model.provider,
288
+ model: model.id,
289
+ usage: {
290
+ input: 0,
291
+ output: 0,
292
+ cacheRead: 0,
293
+ cacheWrite: 0,
294
+ totalTokens: 0,
295
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
296
+ },
297
+ stopReason: "error",
298
+ errorMessage: "timeout",
299
+ timestamp: Date.now(),
300
+ };
301
+ stream.push({ type: "error", reason: "error", error: errorMsg });
302
+ stream.end(errorMsg);
303
+ return;
304
+ }
305
+
306
+ const message = buildAssistantMessage(model, emit);
307
+ stream.push({ type: "start", partial: { ...message, content: [] } });
308
+
309
+ if (emit.kind === "text") {
310
+ stream.push({ type: "text_start", contentIndex: 0, partial: message });
311
+ stream.push({
312
+ type: "text_delta",
313
+ contentIndex: 0,
314
+ delta: emit.text,
315
+ partial: message,
316
+ });
317
+ stream.push({
318
+ type: "text_end",
319
+ contentIndex: 0,
320
+ content: emit.text,
321
+ partial: message,
322
+ });
323
+ } else if (emit.kind === "tool_use") {
324
+ for (const [i, c] of message.content.entries()) {
325
+ if (c.type !== "toolCall") continue;
326
+ stream.push({ type: "toolcall_start", contentIndex: i, partial: message });
327
+ stream.push({
328
+ type: "toolcall_end",
329
+ contentIndex: i,
330
+ toolCall: c,
331
+ partial: message,
332
+ });
333
+ }
334
+ }
335
+
336
+ stream.push({
337
+ type: "done",
338
+ reason: message.stopReason as "stop" | "length" | "toolUse" | "pauseTurn",
339
+ message,
340
+ });
341
+ stream.end(message);
342
+ } catch (err) {
343
+ const errorMsg: AssistantMessage = {
344
+ role: "assistant",
345
+ content: [],
346
+ api: model.api,
347
+ provider: model.provider,
348
+ model: model.id,
349
+ usage: {
350
+ input: 0,
351
+ output: 0,
352
+ cacheRead: 0,
353
+ cacheWrite: 0,
354
+ totalTokens: 0,
355
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
356
+ },
357
+ stopReason: "error",
358
+ errorMessage: (err as Error).message,
359
+ timestamp: Date.now(),
360
+ };
361
+ stream.push({ type: "error", reason: "error", error: errorMsg });
362
+ stream.end(errorMsg);
363
+ }
364
+ });
365
+
366
+ return stream;
367
+ }
368
+
369
+ return {
370
+ api: FAKE_API,
371
+ stream: ((model: Model<typeof FAKE_API>, ctx: Context, _opts?: StreamOptions) =>
372
+ streamTurn(model, ctx)) as ApiProvider<typeof FAKE_API>["stream"],
373
+ streamSimple: ((model: Model<typeof FAKE_API>, ctx: Context, _opts?: SimpleStreamOptions) =>
374
+ streamTurn(model, ctx)) as ApiProvider<typeof FAKE_API>["streamSimple"],
375
+ };
376
+ }
@@ -1,4 +1,5 @@
1
1
  import { clearApiProviders, registerApiProvider } from "../api-registry.js";
2
+ import { createFakeProvider } from "./fake.js";
2
3
  import type { AssistantMessage, AssistantMessageEvent, Context, Model, SimpleStreamOptions } from "../types.js";
3
4
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
4
5
  import type { BedrockOptions } from "./amazon-bedrock.js";
@@ -188,6 +189,28 @@ function registerBuiltInApiProviders(): void {
188
189
  export function resetApiProviders(): void {
189
190
  clearApiProviders();
190
191
  registerBuiltInApiProviders();
192
+ registerFakeProviderIfEnabled();
193
+ }
194
+
195
+ /**
196
+ * E2E-test-only: when `GSD_FAKE_LLM_TRANSCRIPT` is set, register a
197
+ * deterministic JSONL-replay provider under api "fake". The env var must
198
+ * be set BEFORE this module is imported. Subprocess-spawned e2e tests do
199
+ * this by setting it on the spawn env.
200
+ *
201
+ * Synchronous registration: any failure throws here so the CLI startup
202
+ * fails loudly instead of silently falling through to a real provider.
203
+ */
204
+ function registerFakeProviderIfEnabled(): void {
205
+ const transcriptPath = process.env.GSD_FAKE_LLM_TRANSCRIPT;
206
+ if (!transcriptPath) return;
207
+ try {
208
+ registerApiProvider(createFakeProvider({ transcriptPath }), "fake");
209
+ } catch (err) {
210
+ process.stderr.write(`fake-llm: failed to register: ${(err as Error).message}\n`);
211
+ throw err;
212
+ }
191
213
  }
192
214
 
193
215
  registerBuiltInApiProviders();
216
+ registerFakeProviderIfEnabled();