gsd-pi 2.10.12 → 2.12.0

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 (504) hide show
  1. package/dist/bundled-extension-paths.d.ts +2 -0
  2. package/dist/bundled-extension-paths.js +10 -0
  3. package/dist/loader.js +45 -28
  4. package/dist/onboarding.js +100 -0
  5. package/dist/resource-loader.js +12 -3
  6. package/dist/resources/GSD-WORKFLOW.md +661 -0
  7. package/dist/resources/agents/researcher.md +29 -0
  8. package/dist/resources/agents/scout.md +56 -0
  9. package/dist/resources/agents/worker.md +31 -0
  10. package/dist/resources/extensions/ask-user-questions.ts +289 -0
  11. package/dist/resources/extensions/async-jobs/async-bash-tool.ts +211 -0
  12. package/dist/resources/extensions/async-jobs/await-tool.ts +101 -0
  13. package/dist/resources/extensions/async-jobs/cancel-job-tool.ts +34 -0
  14. package/dist/resources/extensions/async-jobs/index.ts +133 -0
  15. package/dist/resources/extensions/async-jobs/job-manager.ts +211 -0
  16. package/dist/resources/extensions/bg-shell/index.ts +3179 -0
  17. package/dist/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
  18. package/dist/resources/extensions/browser-tools/capture.ts +165 -0
  19. package/dist/resources/extensions/browser-tools/core.d.ts +205 -0
  20. package/dist/resources/extensions/browser-tools/core.js +1057 -0
  21. package/dist/resources/extensions/browser-tools/evaluate-helpers.ts +184 -0
  22. package/dist/resources/extensions/browser-tools/index.ts +51 -0
  23. package/dist/resources/extensions/browser-tools/lifecycle.ts +270 -0
  24. package/dist/resources/extensions/browser-tools/package.json +24 -0
  25. package/dist/resources/extensions/browser-tools/refs.ts +264 -0
  26. package/dist/resources/extensions/browser-tools/settle.ts +197 -0
  27. package/dist/resources/extensions/browser-tools/state.ts +408 -0
  28. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +652 -0
  29. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +614 -0
  30. package/dist/resources/extensions/browser-tools/tools/assertions.ts +342 -0
  31. package/dist/resources/extensions/browser-tools/tools/forms.ts +801 -0
  32. package/dist/resources/extensions/browser-tools/tools/inspection.ts +492 -0
  33. package/dist/resources/extensions/browser-tools/tools/intent.ts +614 -0
  34. package/dist/resources/extensions/browser-tools/tools/interaction.ts +865 -0
  35. package/dist/resources/extensions/browser-tools/tools/navigation.ts +232 -0
  36. package/dist/resources/extensions/browser-tools/tools/pages.ts +303 -0
  37. package/dist/resources/extensions/browser-tools/tools/refs.ts +541 -0
  38. package/dist/resources/extensions/browser-tools/tools/screenshot.ts +83 -0
  39. package/dist/resources/extensions/browser-tools/tools/session.ts +400 -0
  40. package/dist/resources/extensions/browser-tools/tools/wait.ts +247 -0
  41. package/dist/resources/extensions/browser-tools/utils.ts +660 -0
  42. package/dist/resources/extensions/context7/index.ts +428 -0
  43. package/dist/resources/extensions/context7/package.json +11 -0
  44. package/dist/resources/extensions/get-secrets-from-user.ts +625 -0
  45. package/dist/resources/extensions/google-search/index.ts +336 -0
  46. package/dist/resources/extensions/google-search/package.json +9 -0
  47. package/dist/resources/extensions/gsd/activity-log.ts +116 -0
  48. package/dist/resources/extensions/gsd/auto.ts +3732 -0
  49. package/dist/resources/extensions/gsd/commands.ts +544 -0
  50. package/dist/resources/extensions/gsd/crash-recovery.ts +105 -0
  51. package/dist/resources/extensions/gsd/dashboard-overlay.ts +564 -0
  52. package/dist/resources/extensions/gsd/dispatch-guard.ts +78 -0
  53. package/dist/resources/extensions/gsd/docs/preferences-reference.md +278 -0
  54. package/dist/resources/extensions/gsd/doctor.ts +766 -0
  55. package/dist/resources/extensions/gsd/exit-command.ts +18 -0
  56. package/dist/resources/extensions/gsd/files.ts +857 -0
  57. package/dist/resources/extensions/gsd/git-service.ts +904 -0
  58. package/dist/resources/extensions/gsd/gitignore.ts +200 -0
  59. package/dist/resources/extensions/gsd/guided-flow.ts +1054 -0
  60. package/dist/resources/extensions/gsd/index.ts +735 -0
  61. package/dist/resources/extensions/gsd/metrics.ts +374 -0
  62. package/dist/resources/extensions/gsd/migrate/command.ts +218 -0
  63. package/dist/resources/extensions/gsd/migrate/index.ts +42 -0
  64. package/dist/resources/extensions/gsd/migrate/parser.ts +323 -0
  65. package/dist/resources/extensions/gsd/migrate/parsers.ts +624 -0
  66. package/dist/resources/extensions/gsd/migrate/preview.ts +48 -0
  67. package/dist/resources/extensions/gsd/migrate/transformer.ts +346 -0
  68. package/dist/resources/extensions/gsd/migrate/types.ts +370 -0
  69. package/dist/resources/extensions/gsd/migrate/validator.ts +55 -0
  70. package/dist/resources/extensions/gsd/migrate/writer.ts +539 -0
  71. package/dist/resources/extensions/gsd/native-git-bridge.ts +181 -0
  72. package/dist/resources/extensions/gsd/native-parser-bridge.ts +135 -0
  73. package/dist/resources/extensions/gsd/observability-validator.ts +408 -0
  74. package/dist/resources/extensions/gsd/package.json +11 -0
  75. package/dist/resources/extensions/gsd/paths.ts +339 -0
  76. package/dist/resources/extensions/gsd/post-unit-hooks.ts +449 -0
  77. package/dist/resources/extensions/gsd/preferences.ts +988 -0
  78. package/dist/resources/extensions/gsd/prompt-loader.ts +92 -0
  79. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
  80. package/dist/resources/extensions/gsd/prompts/complete-slice.md +27 -0
  81. package/dist/resources/extensions/gsd/prompts/discuss.md +237 -0
  82. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
  83. package/dist/resources/extensions/gsd/prompts/execute-task.md +63 -0
  84. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -0
  85. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -0
  86. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +61 -0
  87. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +3 -0
  88. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +29 -0
  89. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -0
  90. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +13 -0
  91. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
  92. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +78 -0
  93. package/dist/resources/extensions/gsd/prompts/plan-slice.md +55 -0
  94. package/dist/resources/extensions/gsd/prompts/queue.md +100 -0
  95. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
  96. package/dist/resources/extensions/gsd/prompts/replan-slice.md +39 -0
  97. package/dist/resources/extensions/gsd/prompts/research-milestone.md +37 -0
  98. package/dist/resources/extensions/gsd/prompts/research-slice.md +28 -0
  99. package/dist/resources/extensions/gsd/prompts/review-migration.md +66 -0
  100. package/dist/resources/extensions/gsd/prompts/run-uat.md +109 -0
  101. package/dist/resources/extensions/gsd/prompts/system.md +190 -0
  102. package/dist/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
  103. package/dist/resources/extensions/gsd/roadmap-slices.ts +50 -0
  104. package/dist/resources/extensions/gsd/session-forensics.ts +500 -0
  105. package/dist/resources/extensions/gsd/skill-discovery.ts +137 -0
  106. package/dist/resources/extensions/gsd/state.ts +569 -0
  107. package/dist/resources/extensions/gsd/templates/context.md +76 -0
  108. package/dist/resources/extensions/gsd/templates/decisions.md +8 -0
  109. package/dist/resources/extensions/gsd/templates/milestone-summary.md +73 -0
  110. package/dist/resources/extensions/gsd/templates/plan.md +131 -0
  111. package/dist/resources/extensions/gsd/templates/preferences.md +23 -0
  112. package/dist/resources/extensions/gsd/templates/project.md +31 -0
  113. package/dist/resources/extensions/gsd/templates/reassessment.md +28 -0
  114. package/dist/resources/extensions/gsd/templates/requirements.md +81 -0
  115. package/dist/resources/extensions/gsd/templates/research.md +46 -0
  116. package/dist/resources/extensions/gsd/templates/roadmap.md +118 -0
  117. package/dist/resources/extensions/gsd/templates/secrets-manifest.md +22 -0
  118. package/dist/resources/extensions/gsd/templates/slice-context.md +58 -0
  119. package/dist/resources/extensions/gsd/templates/slice-summary.md +99 -0
  120. package/dist/resources/extensions/gsd/templates/state.md +19 -0
  121. package/dist/resources/extensions/gsd/templates/task-plan.md +52 -0
  122. package/dist/resources/extensions/gsd/templates/task-summary.md +57 -0
  123. package/dist/resources/extensions/gsd/templates/uat.md +54 -0
  124. package/dist/resources/extensions/gsd/tests/activity-log-prune.test.ts +297 -0
  125. package/dist/resources/extensions/gsd/tests/activity-log-save.test.ts +127 -0
  126. package/dist/resources/extensions/gsd/tests/auto-draft-pause.test.ts +109 -0
  127. package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +45 -0
  128. package/dist/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +196 -0
  129. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
  130. package/dist/resources/extensions/gsd/tests/collect-from-manifest.test.ts +469 -0
  131. package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +200 -0
  132. package/dist/resources/extensions/gsd/tests/cost-projection.test.ts +134 -0
  133. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +312 -0
  134. package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +299 -0
  135. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +660 -0
  136. package/dist/resources/extensions/gsd/tests/discuss-prompt.test.ts +27 -0
  137. package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +91 -0
  138. package/dist/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +170 -0
  139. package/dist/resources/extensions/gsd/tests/doctor.test.ts +594 -0
  140. package/dist/resources/extensions/gsd/tests/draft-promotion.test.ts +169 -0
  141. package/dist/resources/extensions/gsd/tests/exit-command.test.ts +50 -0
  142. package/dist/resources/extensions/gsd/tests/git-service.test.ts +1788 -0
  143. package/dist/resources/extensions/gsd/tests/idle-recovery.test.ts +577 -0
  144. package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +580 -0
  145. package/dist/resources/extensions/gsd/tests/manifest-status.test.ts +283 -0
  146. package/dist/resources/extensions/gsd/tests/metrics-io.test.ts +176 -0
  147. package/dist/resources/extensions/gsd/tests/metrics.test.ts +188 -0
  148. package/dist/resources/extensions/gsd/tests/migrate-command.test.ts +369 -0
  149. package/dist/resources/extensions/gsd/tests/migrate-parser.test.ts +757 -0
  150. package/dist/resources/extensions/gsd/tests/migrate-transformer.test.ts +635 -0
  151. package/dist/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +414 -0
  152. package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +297 -0
  153. package/dist/resources/extensions/gsd/tests/migrate-writer.test.ts +398 -0
  154. package/dist/resources/extensions/gsd/tests/must-have-parser.test.ts +291 -0
  155. package/dist/resources/extensions/gsd/tests/network-error-fallback.test.ts +54 -0
  156. package/dist/resources/extensions/gsd/tests/next-milestone-id.test.ts +66 -0
  157. package/dist/resources/extensions/gsd/tests/orphaned-branch.test.ts +353 -0
  158. package/dist/resources/extensions/gsd/tests/parsers.test.ts +1664 -0
  159. package/dist/resources/extensions/gsd/tests/plan-milestone.test.ts +133 -0
  160. package/dist/resources/extensions/gsd/tests/plan-quality-validator.test.ts +363 -0
  161. package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
  162. package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
  163. package/dist/resources/extensions/gsd/tests/queue-draft-detection.test.ts +126 -0
  164. package/dist/resources/extensions/gsd/tests/reassess-prompt.test.ts +142 -0
  165. package/dist/resources/extensions/gsd/tests/regex-hardening.test.ts +281 -0
  166. package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
  167. package/dist/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
  168. package/dist/resources/extensions/gsd/tests/replan-slice.test.ts +493 -0
  169. package/dist/resources/extensions/gsd/tests/requirements.test.ts +106 -0
  170. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +35 -0
  171. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
  172. package/dist/resources/extensions/gsd/tests/roadmap-slices.test.ts +41 -0
  173. package/dist/resources/extensions/gsd/tests/run-uat.test.ts +316 -0
  174. package/dist/resources/extensions/gsd/tests/secure-env-collect.test.ts +185 -0
  175. package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +123 -0
  176. package/dist/resources/extensions/gsd/tests/test-helpers.ts +61 -0
  177. package/dist/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +219 -0
  178. package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +234 -0
  179. package/dist/resources/extensions/gsd/tests/workspace-index.test.ts +75 -0
  180. package/dist/resources/extensions/gsd/tests/worktree-integration.test.ts +234 -0
  181. package/dist/resources/extensions/gsd/tests/worktree-manager.test.ts +141 -0
  182. package/dist/resources/extensions/gsd/tests/worktree.test.ts +597 -0
  183. package/dist/resources/extensions/gsd/tests/write-gate.test.ts +122 -0
  184. package/dist/resources/extensions/gsd/types.ts +296 -0
  185. package/dist/resources/extensions/gsd/unit-runtime.ts +184 -0
  186. package/dist/resources/extensions/gsd/workspace-index.ts +189 -0
  187. package/dist/resources/extensions/gsd/worktree-command.ts +845 -0
  188. package/dist/resources/extensions/gsd/worktree-manager.ts +459 -0
  189. package/dist/resources/extensions/gsd/{worktree.js → worktree.ts} +92 -61
  190. package/dist/resources/extensions/mac-tools/index.ts +852 -0
  191. package/dist/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
  192. package/dist/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
  193. package/dist/resources/extensions/mcporter/index.ts +429 -0
  194. package/dist/resources/extensions/remote-questions/config.ts +81 -0
  195. package/dist/resources/extensions/remote-questions/discord-adapter.ts +128 -0
  196. package/dist/resources/extensions/remote-questions/format.ts +163 -0
  197. package/dist/resources/extensions/remote-questions/manager.ts +192 -0
  198. package/dist/resources/extensions/remote-questions/remote-command.ts +361 -0
  199. package/dist/resources/extensions/remote-questions/slack-adapter.ts +92 -0
  200. package/dist/resources/extensions/remote-questions/status.ts +31 -0
  201. package/dist/resources/extensions/remote-questions/store.ts +77 -0
  202. package/dist/resources/extensions/remote-questions/types.ts +75 -0
  203. package/dist/resources/extensions/search-the-web/cache.ts +78 -0
  204. package/dist/resources/extensions/search-the-web/command-search-provider.ts +99 -0
  205. package/dist/resources/extensions/search-the-web/format.ts +258 -0
  206. package/dist/resources/extensions/search-the-web/http.ts +238 -0
  207. package/dist/resources/extensions/search-the-web/index.ts +65 -0
  208. package/dist/resources/extensions/search-the-web/native-search.ts +165 -0
  209. package/dist/resources/extensions/search-the-web/provider.ts +136 -0
  210. package/dist/resources/extensions/search-the-web/tavily.ts +116 -0
  211. package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +581 -0
  212. package/dist/resources/extensions/search-the-web/tool-llm-context.ts +620 -0
  213. package/dist/resources/extensions/search-the-web/tool-search.ts +635 -0
  214. package/dist/resources/extensions/search-the-web/url-utils.ts +91 -0
  215. package/dist/resources/extensions/shared/bundled-extension-paths.ts +11 -0
  216. package/dist/resources/extensions/shared/confirm-ui.ts +126 -0
  217. package/dist/resources/extensions/shared/interview-ui.ts +613 -0
  218. package/dist/resources/extensions/shared/next-action-ui.ts +197 -0
  219. package/dist/resources/extensions/shared/progress-widget.ts +282 -0
  220. package/dist/resources/extensions/shared/terminal.ts +23 -0
  221. package/dist/resources/extensions/shared/thinking-widget.ts +107 -0
  222. package/dist/resources/extensions/shared/ui.ts +400 -0
  223. package/dist/resources/extensions/shared/wizard-ui.ts +551 -0
  224. package/dist/resources/extensions/slash-commands/audit.ts +88 -0
  225. package/dist/resources/extensions/slash-commands/clear.ts +10 -0
  226. package/dist/resources/extensions/slash-commands/create-extension.ts +297 -0
  227. package/dist/resources/extensions/slash-commands/create-slash-command.ts +234 -0
  228. package/dist/resources/extensions/slash-commands/index.ts +12 -0
  229. package/dist/resources/extensions/subagent/agents.ts +126 -0
  230. package/dist/resources/extensions/subagent/index.ts +1072 -0
  231. package/dist/resources/extensions/subagent/isolation.ts +498 -0
  232. package/dist/resources/extensions/ttsr/index.ts +163 -0
  233. package/dist/resources/extensions/ttsr/rule-loader.ts +121 -0
  234. package/dist/resources/extensions/ttsr/ttsr-interrupt.md +6 -0
  235. package/dist/resources/extensions/ttsr/ttsr-manager.ts +430 -0
  236. package/dist/resources/extensions/universal-config/discovery.ts +78 -0
  237. package/dist/resources/extensions/universal-config/format.ts +160 -0
  238. package/dist/resources/extensions/universal-config/index.ts +118 -0
  239. package/dist/resources/extensions/universal-config/package.json +11 -0
  240. package/dist/resources/extensions/universal-config/scanners.ts +579 -0
  241. package/dist/resources/extensions/universal-config/tests/discovery.test.ts +111 -0
  242. package/dist/resources/extensions/universal-config/tests/format.test.ts +111 -0
  243. package/dist/resources/extensions/universal-config/tests/scanners.test.ts +438 -0
  244. package/dist/resources/extensions/universal-config/tools.ts +60 -0
  245. package/dist/resources/extensions/universal-config/types.ts +116 -0
  246. package/dist/resources/extensions/voice/index.ts +271 -0
  247. package/dist/resources/extensions/voice/speech-recognizer.py +504 -0
  248. package/dist/resources/extensions/voice/speech-recognizer.swift +154 -0
  249. package/dist/resources/skills/debug-like-expert/SKILL.md +231 -0
  250. package/dist/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
  251. package/dist/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
  252. package/dist/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
  253. package/dist/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
  254. package/dist/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
  255. package/dist/resources/skills/frontend-design/SKILL.md +45 -0
  256. package/dist/resources/skills/github-workflows/SKILL.md +92 -0
  257. package/dist/resources/skills/github-workflows/references/gh/SKILL.md +255 -0
  258. package/dist/resources/skills/github-workflows/references/gh/references/issue-stories.md +204 -0
  259. package/dist/resources/skills/github-workflows/references/gh/references/labels.md +170 -0
  260. package/dist/resources/skills/github-workflows/references/gh/references/milestones.md +158 -0
  261. package/dist/resources/skills/github-workflows/references/gh/references/projects-v2.md +177 -0
  262. package/dist/resources/skills/github-workflows/references/gh/scripts/experiment_cleanup.py +191 -0
  263. package/dist/resources/skills/github-workflows/references/gh/scripts/github_project_setup.py +799 -0
  264. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  265. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +608 -0
  266. package/dist/resources/skills/swiftui/SKILL.md +208 -0
  267. package/dist/resources/skills/swiftui/references/animations.md +921 -0
  268. package/dist/resources/skills/swiftui/references/architecture.md +1561 -0
  269. package/dist/resources/skills/swiftui/references/layout-system.md +1186 -0
  270. package/dist/resources/skills/swiftui/references/navigation.md +1492 -0
  271. package/dist/resources/skills/swiftui/references/networking-async.md +214 -0
  272. package/dist/resources/skills/swiftui/references/performance.md +1706 -0
  273. package/dist/resources/skills/swiftui/references/platform-integration.md +204 -0
  274. package/dist/resources/skills/swiftui/references/state-management.md +1443 -0
  275. package/dist/resources/skills/swiftui/references/swiftdata.md +297 -0
  276. package/dist/resources/skills/swiftui/references/testing-debugging.md +247 -0
  277. package/dist/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
  278. package/dist/resources/skills/swiftui/workflows/add-feature.md +191 -0
  279. package/dist/resources/skills/swiftui/workflows/build-new-app.md +311 -0
  280. package/dist/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
  281. package/dist/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
  282. package/dist/resources/skills/swiftui/workflows/ship-app.md +203 -0
  283. package/dist/resources/skills/swiftui/workflows/write-tests.md +235 -0
  284. package/dist/wizard.js +1 -0
  285. package/package.json +8 -6
  286. package/packages/native/dist/fd/index.d.ts +1 -0
  287. package/packages/native/dist/fd/index.js +1 -0
  288. package/packages/native/dist/grep/index.d.ts +2 -1
  289. package/packages/native/dist/grep/index.js +1 -0
  290. package/packages/native/src/__tests__/fd.test.mjs +35 -0
  291. package/packages/native/src/__tests__/grep.test.mjs +26 -9
  292. package/packages/native/src/fd/index.ts +1 -0
  293. package/packages/native/src/grep/index.ts +3 -2
  294. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  295. package/packages/pi-agent-core/dist/agent-loop.js +169 -55
  296. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  297. package/packages/pi-agent-core/dist/agent.d.ts +13 -1
  298. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  299. package/packages/pi-agent-core/dist/agent.js +16 -0
  300. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  301. package/packages/pi-agent-core/dist/types.d.ts +91 -1
  302. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  303. package/packages/pi-agent-core/dist/types.js.map +1 -1
  304. package/packages/pi-agent-core/src/agent-loop.ts +273 -63
  305. package/packages/pi-agent-core/src/agent.ts +24 -0
  306. package/packages/pi-agent-core/src/types.ts +98 -0
  307. package/packages/pi-ai/dist/env-api-keys.js +2 -0
  308. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  309. package/packages/pi-ai/dist/models.generated.d.ts +314 -0
  310. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  311. package/packages/pi-ai/dist/models.generated.js +236 -0
  312. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  313. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
  314. package/packages/pi-ai/dist/providers/google-gemini-cli.js +6 -0
  315. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
  316. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  317. package/packages/pi-ai/dist/providers/openai-completions.js +2 -0
  318. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  319. package/packages/pi-ai/dist/types.d.ts +1 -1
  320. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  321. package/packages/pi-ai/dist/types.js.map +1 -1
  322. package/packages/pi-ai/src/env-api-keys.ts +2 -0
  323. package/packages/pi-ai/src/models.generated.ts +236 -0
  324. package/packages/pi-ai/src/providers/google-gemini-cli.ts +8 -0
  325. package/packages/pi-ai/src/providers/openai-completions.ts +3 -0
  326. package/packages/pi-ai/src/types.ts +2 -1
  327. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  328. package/packages/pi-coding-agent/dist/cli/args.js +1 -0
  329. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  330. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +26 -0
  331. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/agent-session.js +137 -22
  333. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  334. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +20 -0
  335. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  336. package/packages/pi-coding-agent/dist/core/auth-storage.js +43 -0
  337. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  338. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts +51 -0
  339. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/fallback-resolver.js +119 -0
  341. package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -0
  342. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.d.ts +2 -0
  343. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.d.ts.map +1 -0
  344. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +183 -0
  345. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -0
  346. package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
  347. package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
  348. package/packages/pi-coding-agent/dist/core/index.js +1 -0
  349. package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
  350. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  352. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +12 -1
  354. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  355. package/packages/pi-coding-agent/dist/core/session-manager.js +37 -1
  356. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  357. package/packages/pi-coding-agent/dist/core/session-manager.test.d.ts +2 -0
  358. package/packages/pi-coding-agent/dist/core/session-manager.test.d.ts.map +1 -0
  359. package/packages/pi-coding-agent/dist/core/session-manager.test.js +61 -0
  360. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -0
  361. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
  362. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  363. package/packages/pi-coding-agent/dist/core/settings-manager.js +47 -0
  364. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  365. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  366. package/packages/pi-coding-agent/dist/core/tools/find.js +1 -0
  367. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  368. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  369. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +6 -15
  370. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  371. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +18 -1
  372. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  373. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +46 -4
  374. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  375. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  376. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +25 -2
  377. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  378. package/packages/pi-coding-agent/src/cli/args.ts +1 -0
  379. package/packages/pi-coding-agent/src/core/agent-session.ts +167 -22
  380. package/packages/pi-coding-agent/src/core/auth-storage.ts +45 -0
  381. package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +229 -0
  382. package/packages/pi-coding-agent/src/core/fallback-resolver.ts +165 -0
  383. package/packages/pi-coding-agent/src/core/index.ts +1 -0
  384. package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
  385. package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -0
  386. package/packages/pi-coding-agent/src/core/session-manager.ts +51 -1
  387. package/packages/pi-coding-agent/src/core/settings-manager.ts +66 -0
  388. package/packages/pi-coding-agent/src/core/tools/find.ts +1 -0
  389. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +6 -16
  390. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +49 -4
  391. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -2
  392. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
  393. package/packages/pi-tui/dist/autocomplete.js +3 -4
  394. package/packages/pi-tui/dist/autocomplete.js.map +1 -1
  395. package/packages/pi-tui/dist/components/editor.d.ts +7 -0
  396. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  397. package/packages/pi-tui/dist/components/editor.js +57 -50
  398. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  399. package/packages/pi-tui/src/autocomplete.ts +3 -5
  400. package/packages/pi-tui/src/components/editor.ts +70 -49
  401. package/pkg/dist/core/export-html/ansi-to-html.d.ts +22 -0
  402. package/pkg/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  403. package/pkg/dist/core/export-html/ansi-to-html.js +249 -0
  404. package/pkg/dist/core/export-html/ansi-to-html.js.map +1 -0
  405. package/pkg/dist/core/export-html/index.d.ts +37 -0
  406. package/pkg/dist/core/export-html/index.d.ts.map +1 -0
  407. package/pkg/dist/core/export-html/index.js +223 -0
  408. package/pkg/dist/core/export-html/index.js.map +1 -0
  409. package/pkg/dist/core/export-html/template.css +971 -0
  410. package/pkg/dist/core/export-html/template.html +54 -0
  411. package/pkg/dist/core/export-html/template.js +1583 -0
  412. package/pkg/dist/core/export-html/tool-renderer.d.ts +38 -0
  413. package/pkg/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  414. package/pkg/dist/core/export-html/tool-renderer.js +70 -0
  415. package/pkg/dist/core/export-html/tool-renderer.js.map +1 -0
  416. package/pkg/dist/core/export-html/vendor/highlight.min.js +1213 -0
  417. package/pkg/dist/core/export-html/vendor/marked.min.js +6 -0
  418. package/scripts/postinstall.js +30 -17
  419. package/src/resources/extensions/bg-shell/index.ts +51 -7
  420. package/src/resources/extensions/gsd/activity-log.ts +61 -14
  421. package/src/resources/extensions/gsd/auto.ts +288 -19
  422. package/src/resources/extensions/gsd/commands.ts +9 -3
  423. package/src/resources/extensions/gsd/dashboard-overlay.ts +55 -12
  424. package/src/resources/extensions/gsd/docs/preferences-reference.md +25 -0
  425. package/src/resources/extensions/gsd/doctor.ts +60 -3
  426. package/src/resources/extensions/gsd/files.ts +45 -1
  427. package/src/resources/extensions/gsd/git-service.ts +46 -41
  428. package/src/resources/extensions/gsd/guided-flow.ts +81 -9
  429. package/src/resources/extensions/gsd/index.ts +62 -1
  430. package/src/resources/extensions/gsd/native-git-bridge.ts +181 -0
  431. package/src/resources/extensions/gsd/paths.ts +35 -4
  432. package/src/resources/extensions/gsd/post-unit-hooks.ts +449 -0
  433. package/src/resources/extensions/gsd/preferences.ts +232 -1
  434. package/src/resources/extensions/gsd/prompt-loader.ts +45 -3
  435. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  436. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -3
  437. package/src/resources/extensions/gsd/prompts/discuss.md +10 -8
  438. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
  439. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -1
  440. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -1
  441. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  442. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +3 -1
  443. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +4 -2
  444. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -1
  445. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +3 -1
  446. package/src/resources/extensions/gsd/prompts/plan-milestone.md +9 -12
  447. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -3
  448. package/src/resources/extensions/gsd/prompts/queue.md +3 -1
  449. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  450. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  451. package/src/resources/extensions/gsd/state.ts +130 -18
  452. package/src/resources/extensions/gsd/templates/context.md +1 -1
  453. package/src/resources/extensions/gsd/templates/state.md +3 -3
  454. package/src/resources/extensions/gsd/tests/activity-log-save.test.ts +127 -0
  455. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +5 -1
  456. package/src/resources/extensions/gsd/tests/doctor.test.ts +115 -1
  457. package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +6 -2
  458. package/src/resources/extensions/gsd/tests/git-service.test.ts +35 -35
  459. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +8 -8
  460. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +4 -1
  461. package/src/resources/extensions/gsd/tests/network-error-fallback.test.ts +54 -0
  462. package/src/resources/extensions/gsd/tests/orphaned-branch.test.ts +5 -5
  463. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
  464. package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
  465. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +12 -0
  466. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +6 -0
  467. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +4 -4
  468. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +4 -4
  469. package/src/resources/extensions/gsd/tests/worktree.test.ts +19 -19
  470. package/src/resources/extensions/gsd/types.ts +109 -0
  471. package/src/resources/extensions/gsd/worktree-manager.ts +72 -13
  472. package/src/resources/extensions/gsd/worktree.ts +5 -4
  473. package/src/resources/extensions/search-the-web/command-search-provider.ts +8 -4
  474. package/src/resources/extensions/search-the-web/native-search.ts +32 -23
  475. package/src/resources/extensions/search-the-web/provider.ts +19 -2
  476. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +62 -0
  477. package/src/resources/extensions/search-the-web/tool-llm-context.ts +62 -3
  478. package/src/resources/extensions/search-the-web/tool-search.ts +62 -3
  479. package/src/resources/extensions/shared/bundled-extension-paths.ts +11 -0
  480. package/src/resources/extensions/subagent/index.ts +2 -1
  481. package/src/resources/skills/github-workflows/SKILL.md +5 -0
  482. package/dist/resources/extensions/gsd/git-service.d.ts +0 -225
  483. package/dist/resources/extensions/gsd/git-service.js +0 -756
  484. package/dist/resources/extensions/gsd/preferences.d.ts +0 -122
  485. package/dist/resources/extensions/gsd/preferences.js +0 -616
  486. package/dist/resources/extensions/gsd/worktree.d.ts +0 -106
  487. package/dist/resources/extensions/remote-questions/config.d.ts +0 -14
  488. package/dist/resources/extensions/remote-questions/config.js +0 -68
  489. package/dist/resources/extensions/remote-questions/discord-adapter.d.ts +0 -17
  490. package/dist/resources/extensions/remote-questions/discord-adapter.js +0 -118
  491. package/dist/resources/extensions/remote-questions/format.d.ts +0 -38
  492. package/dist/resources/extensions/remote-questions/format.js +0 -119
  493. package/dist/resources/extensions/remote-questions/manager.d.ts +0 -23
  494. package/dist/resources/extensions/remote-questions/manager.js +0 -161
  495. package/dist/resources/extensions/remote-questions/remote-command.d.ts +0 -6
  496. package/dist/resources/extensions/remote-questions/remote-command.js +0 -358
  497. package/dist/resources/extensions/remote-questions/slack-adapter.d.ts +0 -15
  498. package/dist/resources/extensions/remote-questions/slack-adapter.js +0 -82
  499. package/dist/resources/extensions/remote-questions/status.d.ts +0 -9
  500. package/dist/resources/extensions/remote-questions/status.js +0 -25
  501. package/dist/resources/extensions/remote-questions/store.d.ts +0 -11
  502. package/dist/resources/extensions/remote-questions/store.js +0 -66
  503. package/dist/resources/extensions/remote-questions/types.d.ts +0 -68
  504. package/dist/resources/extensions/remote-questions/types.js +0 -4
@@ -290,6 +290,7 @@ ${chalk.bold("Environment Variables:")}
290
290
  AI_GATEWAY_API_KEY - Vercel AI Gateway API key
291
291
  ZAI_API_KEY - ZAI API key
292
292
  MISTRAL_API_KEY - Mistral API key
293
+ OLLAMA_API_KEY - Ollama Cloud API key
293
294
  MINIMAX_API_KEY - MiniMax API key
294
295
  OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key
295
296
  KIMI_API_KEY - Kimi For Coding API key
@@ -67,9 +67,9 @@ import {
67
67
  type TurnEndEvent,
68
68
  type TurnStartEvent,
69
69
  wrapRegisteredTools,
70
- wrapToolsWithExtensions,
71
70
  } from "./extensions/index.js";
72
71
  import type { BashExecutionMessage, CustomMessage } from "./messages.js";
72
+ import { FallbackResolver } from "./fallback-resolver.js";
73
73
  import type { ModelRegistry } from "./model-registry.js";
74
74
  import { expandPromptTemplate, type PromptTemplate } from "./prompt-templates.js";
75
75
  import type { ResourceExtensionPaths, ResourceLoader } from "./resource-loader.js";
@@ -120,7 +120,10 @@ export type AgentSessionEvent =
120
120
  errorMessage?: string;
121
121
  }
122
122
  | { type: "auto_retry_start"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }
123
- | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string };
123
+ | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string }
124
+ | { type: "fallback_provider_switch"; from: string; to: string; reason: string }
125
+ | { type: "fallback_provider_restored"; provider: string; reason: string }
126
+ | { type: "fallback_chain_exhausted"; reason: string };
124
127
 
125
128
  /** Listener function for agent session events */
126
129
  export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
@@ -267,6 +270,9 @@ export class AgentSession {
267
270
  // Model registry for API key resolution
268
271
  private _modelRegistry: ModelRegistry;
269
272
 
273
+ // Provider fallback resolver
274
+ private _fallbackResolver: FallbackResolver;
275
+
270
276
  // Tool registry for extension getTools/setTools
271
277
  private _toolRegistry: Map<string, AgentTool> = new Map();
272
278
  private _toolPromptSnippets: Map<string, string> = new Map();
@@ -284,6 +290,11 @@ export class AgentSession {
284
290
  this._customTools = config.customTools ?? [];
285
291
  this._cwd = config.cwd;
286
292
  this._modelRegistry = config.modelRegistry;
293
+ this._fallbackResolver = new FallbackResolver(
294
+ this.settingsManager,
295
+ this._modelRegistry.authStorage,
296
+ this._modelRegistry,
297
+ );
287
298
  this._extensionRunnerRef = config.extensionRunnerRef;
288
299
  this._initialActiveToolNames = config.initialActiveToolNames;
289
300
  this._baseToolsOverride = config.baseToolsOverride;
@@ -292,6 +303,11 @@ export class AgentSession {
292
303
  // (session persistence, extensions, auto-compaction, retry logic)
293
304
  this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
294
305
 
306
+ // Install tool hooks that await the event queue before emitting extension events.
307
+ // This ensures extensions always see settled state (e.g., assistant message appended)
308
+ // even when tools execute in parallel.
309
+ this._installAgentToolHooks();
310
+
295
311
  this._buildRuntime({
296
312
  activeToolNames: this._initialActiveToolNames,
297
313
  includeAllExtensionTools: true,
@@ -303,6 +319,11 @@ export class AgentSession {
303
319
  return this._modelRegistry;
304
320
  }
305
321
 
322
+ /** Fallback resolver for cross-provider fallback */
323
+ get fallbackResolver(): FallbackResolver {
324
+ return this._fallbackResolver;
325
+ }
326
+
306
327
  // =========================================================================
307
328
  // Event Subscription
308
329
  // =========================================================================
@@ -460,6 +481,73 @@ export class AgentSession {
460
481
  }
461
482
  }
462
483
 
484
+ /**
485
+ * Install beforeToolCall/afterToolCall hooks on the Agent.
486
+ *
487
+ * These hooks await `_agentEventQueue` before emitting extension events,
488
+ * ensuring that all prior events (including `message_end` which appends
489
+ * the assistant message) have fully settled. This prevents a race condition
490
+ * in parallel tool execution where extension `tool_call` handlers could
491
+ * see stale agent state.
492
+ */
493
+ private _installAgentToolHooks(): void {
494
+ this.agent.setBeforeToolCall(async ({ toolCall, args }) => {
495
+ // Wait for all queued agent events to settle before emitting to extensions
496
+ await this._agentEventQueue;
497
+
498
+ if (!this._extensionRunner?.hasHandlers("tool_call")) return undefined;
499
+
500
+ try {
501
+ const callResult = await this._extensionRunner.emitToolCall({
502
+ type: "tool_call",
503
+ toolName: toolCall.name,
504
+ toolCallId: toolCall.id,
505
+ input: args as Record<string, unknown>,
506
+ });
507
+
508
+ if (callResult?.block) {
509
+ return {
510
+ block: true,
511
+ reason: callResult.reason || "Tool execution was blocked by an extension",
512
+ };
513
+ }
514
+ } catch (err) {
515
+ if (err instanceof Error) {
516
+ return { block: true, reason: err.message };
517
+ }
518
+ return { block: true, reason: `Extension failed, blocking execution: ${String(err)}` };
519
+ }
520
+
521
+ return undefined;
522
+ });
523
+
524
+ this.agent.setAfterToolCall(async ({ toolCall, args, result, isError }) => {
525
+ // Wait for all queued agent events to settle
526
+ await this._agentEventQueue;
527
+
528
+ if (!this._extensionRunner?.hasHandlers("tool_result")) return undefined;
529
+
530
+ const resultResult = await this._extensionRunner.emitToolResult({
531
+ type: "tool_result",
532
+ toolName: toolCall.name,
533
+ toolCallId: toolCall.id,
534
+ input: args as Record<string, unknown>,
535
+ content: result.content,
536
+ details: result.details,
537
+ isError,
538
+ });
539
+
540
+ if (resultResult) {
541
+ return {
542
+ content: resultResult.content ?? undefined,
543
+ details: resultResult.details ?? undefined,
544
+ };
545
+ }
546
+
547
+ return undefined;
548
+ });
549
+ }
550
+
463
551
  /** Extract text content from a message */
464
552
  private _getUserMessageText(message: Message): string {
465
553
  if (message.role !== "user") return "";
@@ -868,6 +956,19 @@ export class AgentSession {
868
956
  );
869
957
  }
870
958
 
959
+ // Check if a higher-priority provider in the fallback chain has recovered
960
+ const restoration = await this._fallbackResolver.checkForRestoration(this.model);
961
+ if (restoration) {
962
+ const previousProvider = `${this.model.provider}/${this.model.id}`;
963
+ this.agent.setModel(restoration.model);
964
+ this.sessionManager.appendModelChange(restoration.model.provider, restoration.model.id);
965
+ this._emit({
966
+ type: "fallback_provider_restored",
967
+ provider: `${restoration.model.provider}/${restoration.model.id}`,
968
+ reason: `Restored from ${previousProvider}`,
969
+ });
970
+ }
971
+
871
972
  // Validate API key
872
973
  const apiKey = await this._modelRegistry.getApiKey(this.model, this.sessionId);
873
974
  if (!apiKey) {
@@ -2151,12 +2252,10 @@ export class AgentSession {
2151
2252
  toolRegistry.set(tool.name, tool);
2152
2253
  }
2153
2254
 
2154
- if (this._extensionRunner) {
2155
- const wrappedAllTools = wrapToolsWithExtensions(Array.from(toolRegistry.values()), this._extensionRunner);
2156
- this._toolRegistry = new Map(wrappedAllTools.map((tool) => [tool.name, tool]));
2157
- } else {
2158
- this._toolRegistry = toolRegistry;
2159
- }
2255
+ // Tool interception (tool_call/tool_result extension events) is handled by
2256
+ // beforeToolCall/afterToolCall hooks installed in _installAgentToolHooks(),
2257
+ // which await _agentEventQueue for safe parallel execution.
2258
+ this._toolRegistry = toolRegistry;
2160
2259
 
2161
2260
  const nextActiveToolNames = options?.activeToolNames
2162
2261
  ? [...options.activeToolNames]
@@ -2354,20 +2453,66 @@ export class AgentSession {
2354
2453
  return true;
2355
2454
  }
2356
2455
 
2357
- // All credentials are backed off. For quota-exhausted errors the backoff is very
2358
- // long (30+ min), so retrying immediately is futile and will only produce
2359
- // confusing secondary errors (e.g. "Authentication failed"). Give up now and
2360
- // surface the original quota error to the user.
2361
- if (errorType === "quota_exhausted") {
2362
- this._emit({
2363
- type: "auto_retry_end",
2364
- success: false,
2365
- attempt: this._retryAttempt,
2366
- finalError: message.errorMessage,
2367
- });
2368
- this._retryAttempt = 0;
2369
- this._resolveRetry();
2370
- return false;
2456
+ // All credentials are backed off. Try cross-provider fallback before giving up.
2457
+ if (isCredentialError) {
2458
+ const fallbackResult = await this._fallbackResolver.findFallback(
2459
+ this.model,
2460
+ errorType,
2461
+ );
2462
+
2463
+ if (fallbackResult) {
2464
+ // Swap to fallback model — don't persist to settings
2465
+ const previousProvider = this.model.provider;
2466
+ this.agent.setModel(fallbackResult.model);
2467
+ this.sessionManager.appendModelChange(fallbackResult.model.provider, fallbackResult.model.id);
2468
+
2469
+ // Remove error message from agent state
2470
+ const msgs = this.agent.state.messages;
2471
+ if (msgs.length > 0 && msgs[msgs.length - 1].role === "assistant") {
2472
+ this.agent.replaceMessages(msgs.slice(0, -1));
2473
+ }
2474
+
2475
+ this._emit({
2476
+ type: "fallback_provider_switch",
2477
+ from: `${previousProvider}/${this.model?.id}`,
2478
+ to: `${fallbackResult.model.provider}/${fallbackResult.model.id}`,
2479
+ reason: fallbackResult.reason,
2480
+ });
2481
+
2482
+ this._emit({
2483
+ type: "auto_retry_start",
2484
+ attempt: this._retryAttempt + 1,
2485
+ maxAttempts: settings.maxRetries,
2486
+ delayMs: 0,
2487
+ errorMessage: `${message.errorMessage} (${fallbackResult.reason})`,
2488
+ });
2489
+
2490
+ // Retry immediately with fallback provider - don't increment _retryAttempt
2491
+ setTimeout(() => {
2492
+ this.agent.continue().catch(() => {
2493
+ // Retry failed - will be caught by next agent_end
2494
+ });
2495
+ }, 0);
2496
+
2497
+ return true;
2498
+ }
2499
+
2500
+ // No fallback available either
2501
+ if (errorType === "quota_exhausted") {
2502
+ this._emit({
2503
+ type: "fallback_chain_exhausted",
2504
+ reason: `All providers exhausted for ${this.model.provider}/${this.model.id}`,
2505
+ });
2506
+ this._emit({
2507
+ type: "auto_retry_end",
2508
+ success: false,
2509
+ attempt: this._retryAttempt,
2510
+ finalError: message.errorMessage,
2511
+ });
2512
+ this._retryAttempt = 0;
2513
+ this._resolveRetry();
2514
+ return false;
2515
+ }
2371
2516
  }
2372
2517
  }
2373
2518
 
@@ -248,6 +248,13 @@ export class AuthStorage {
248
248
  */
249
249
  private credentialBackoff: Map<string, Map<number, number>> = new Map();
250
250
 
251
+ /**
252
+ * Provider-level backoff tracking.
253
+ * Set when all credentials for a provider are backed off.
254
+ * Map<provider, backoffExpiresAt>
255
+ */
256
+ private providerBackoff: Map<string, number> = new Map();
257
+
251
258
  private constructor(private storage: AuthStorageBackend) {
252
259
  this.reload();
253
260
  }
@@ -398,6 +405,7 @@ export class AuthStorage {
398
405
  delete this.data[provider];
399
406
  this.providerRoundRobinIndex.delete(provider);
400
407
  this.credentialBackoff.delete(provider);
408
+ this.providerBackoff.delete(provider);
401
409
  this.persistProviderChange(provider, undefined);
402
410
  }
403
411
 
@@ -484,6 +492,43 @@ export class AuthStorage {
484
492
  return true;
485
493
  }
486
494
 
495
+ /**
496
+ * Mark an entire provider as exhausted.
497
+ * Called when all credentials for a provider are backed off.
498
+ */
499
+ markProviderExhausted(provider: string, errorType: UsageLimitErrorType): void {
500
+ const backoffMs = getBackoffDuration(errorType);
501
+ this.providerBackoff.set(provider, Date.now() + backoffMs);
502
+ }
503
+
504
+ /**
505
+ * Check if a provider is currently available (not backed off at provider level).
506
+ */
507
+ isProviderAvailable(provider: string): boolean {
508
+ const expiresAt = this.providerBackoff.get(provider);
509
+ if (expiresAt === undefined) return true;
510
+ if (Date.now() >= expiresAt) {
511
+ this.providerBackoff.delete(provider);
512
+ return true;
513
+ }
514
+ return false;
515
+ }
516
+
517
+ /**
518
+ * Get milliseconds remaining until provider backoff expires.
519
+ * Returns 0 if provider is available.
520
+ */
521
+ getProviderBackoffRemaining(provider: string): number {
522
+ const expiresAt = this.providerBackoff.get(provider);
523
+ if (expiresAt === undefined) return 0;
524
+ const remaining = expiresAt - Date.now();
525
+ if (remaining <= 0) {
526
+ this.providerBackoff.delete(provider);
527
+ return 0;
528
+ }
529
+ return remaining;
530
+ }
531
+
487
532
  /**
488
533
  * Check if a credential index is currently backed off.
489
534
  */
@@ -0,0 +1,229 @@
1
+ // GSD Provider Fallback Resolver Tests
2
+ // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
+
4
+ import { describe, it, beforeEach, mock } from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import { FallbackResolver } from "./fallback-resolver.js";
7
+ import type { Api, Model } from "@gsd/pi-ai";
8
+ import type { AuthStorage } from "./auth-storage.js";
9
+ import type { ModelRegistry } from "./model-registry.js";
10
+ import type { FallbackChainEntry, SettingsManager } from "./settings-manager.js";
11
+
12
+ function createMockModel(provider: string, id: string): Model<Api> {
13
+ return {
14
+ id,
15
+ name: id,
16
+ api: "openai-completions" as Api,
17
+ provider,
18
+ baseUrl: `https://api.${provider}.com`,
19
+ reasoning: false,
20
+ input: ["text"],
21
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
22
+ contextWindow: 128000,
23
+ maxTokens: 16384,
24
+ } as Model<Api>;
25
+ }
26
+
27
+ const zaiModel = createMockModel("zai", "glm-5");
28
+ const alibabaModel = createMockModel("alibaba", "glm-5");
29
+ const openaiModel = createMockModel("openai", "gpt-4.1");
30
+
31
+ const defaultChain: FallbackChainEntry[] = [
32
+ { provider: "zai", model: "glm-5", priority: 1 },
33
+ { provider: "alibaba", model: "glm-5", priority: 2 },
34
+ { provider: "openai", model: "gpt-4.1", priority: 3 },
35
+ ];
36
+
37
+ function createResolver(overrides?: {
38
+ enabled?: boolean;
39
+ isProviderAvailable?: (provider: string) => boolean;
40
+ hasAuth?: (provider: string) => boolean;
41
+ find?: (provider: string, modelId: string) => Model<Api> | undefined;
42
+ }) {
43
+ const settingsManager = {
44
+ getFallbackSettings: () => ({
45
+ enabled: overrides?.enabled ?? true,
46
+ chains: { coding: defaultChain },
47
+ }),
48
+ } as unknown as SettingsManager;
49
+
50
+ const authStorage = {
51
+ markProviderExhausted: mock.fn(),
52
+ isProviderAvailable: overrides?.isProviderAvailable ?? (() => true),
53
+ hasAuth: overrides?.hasAuth ?? (() => true),
54
+ } as unknown as AuthStorage;
55
+
56
+ const modelRegistry = {
57
+ find: overrides?.find ?? ((provider: string, modelId: string) => {
58
+ if (provider === "zai" && modelId === "glm-5") return zaiModel;
59
+ if (provider === "alibaba" && modelId === "glm-5") return alibabaModel;
60
+ if (provider === "openai" && modelId === "gpt-4.1") return openaiModel;
61
+ return undefined;
62
+ }),
63
+ } as unknown as ModelRegistry;
64
+
65
+ return { resolver: new FallbackResolver(settingsManager, authStorage, modelRegistry), authStorage };
66
+ }
67
+
68
+ // ─── findFallback ────────────────────────────────────────────────────────────
69
+
70
+ describe("FallbackResolver — findFallback", () => {
71
+ it("returns next available provider when current fails", async () => {
72
+ const { resolver } = createResolver();
73
+ const result = await resolver.findFallback(zaiModel, "quota_exhausted");
74
+
75
+ assert.notEqual(result, null);
76
+ assert.equal(result!.model.provider, "alibaba");
77
+ assert.equal(result!.model.id, "glm-5");
78
+ assert.equal(result!.chainName, "coding");
79
+ });
80
+
81
+ it("marks current provider as exhausted", async () => {
82
+ const { resolver, authStorage } = createResolver();
83
+ await resolver.findFallback(zaiModel, "rate_limit");
84
+
85
+ const fn = authStorage.markProviderExhausted as any;
86
+ assert.equal(fn.mock.calls.length, 1);
87
+ assert.equal(fn.mock.calls[0].arguments[0], "zai");
88
+ assert.equal(fn.mock.calls[0].arguments[1], "rate_limit");
89
+ });
90
+
91
+ it("skips backed-off providers", async () => {
92
+ const { resolver } = createResolver({
93
+ isProviderAvailable: (provider: string) => provider !== "alibaba",
94
+ });
95
+
96
+ const result = await resolver.findFallback(zaiModel, "quota_exhausted");
97
+
98
+ assert.notEqual(result, null);
99
+ assert.equal(result!.model.provider, "openai");
100
+ assert.equal(result!.model.id, "gpt-4.1");
101
+ });
102
+
103
+ it("returns null when all providers are backed off", async () => {
104
+ const { resolver } = createResolver({
105
+ isProviderAvailable: () => false,
106
+ });
107
+
108
+ const result = await resolver.findFallback(zaiModel, "quota_exhausted");
109
+ assert.equal(result, null);
110
+ });
111
+
112
+ it("returns null when fallback is disabled", async () => {
113
+ const { resolver } = createResolver({ enabled: false });
114
+ const result = await resolver.findFallback(zaiModel, "quota_exhausted");
115
+ assert.equal(result, null);
116
+ });
117
+
118
+ it("returns null when model is not in any chain", async () => {
119
+ const { resolver } = createResolver();
120
+ const unknownModel = createMockModel("unknown", "some-model");
121
+ const result = await resolver.findFallback(unknownModel, "quota_exhausted");
122
+ assert.equal(result, null);
123
+ });
124
+
125
+ it("skips providers without auth", async () => {
126
+ const { resolver } = createResolver({
127
+ hasAuth: (provider: string) => provider !== "alibaba",
128
+ });
129
+
130
+ const result = await resolver.findFallback(zaiModel, "quota_exhausted");
131
+
132
+ assert.notEqual(result, null);
133
+ assert.equal(result!.model.provider, "openai");
134
+ });
135
+
136
+ it("skips providers with no model in registry", async () => {
137
+ const { resolver } = createResolver({
138
+ find: (provider: string, modelId: string) => {
139
+ if (provider === "alibaba") return undefined;
140
+ if (provider === "openai" && modelId === "gpt-4.1") return openaiModel;
141
+ return undefined;
142
+ },
143
+ });
144
+
145
+ const result = await resolver.findFallback(zaiModel, "quota_exhausted");
146
+
147
+ assert.notEqual(result, null);
148
+ assert.equal(result!.model.provider, "openai");
149
+ });
150
+ });
151
+
152
+ // ─── checkForRestoration ─────────────────────────────────────────────────────
153
+
154
+ describe("FallbackResolver — checkForRestoration", () => {
155
+ it("returns higher-priority provider when recovered", async () => {
156
+ const { resolver } = createResolver();
157
+ const result = await resolver.checkForRestoration(alibabaModel);
158
+
159
+ assert.notEqual(result, null);
160
+ assert.equal(result!.model.provider, "zai");
161
+ assert.equal(result!.model.id, "glm-5");
162
+ });
163
+
164
+ it("returns null when already at highest priority", async () => {
165
+ const { resolver } = createResolver();
166
+ const result = await resolver.checkForRestoration(zaiModel);
167
+ assert.equal(result, null);
168
+ });
169
+
170
+ it("returns null when higher-priority provider is still backed off", async () => {
171
+ const { resolver } = createResolver({
172
+ isProviderAvailable: (provider: string) => provider !== "zai",
173
+ });
174
+
175
+ const result = await resolver.checkForRestoration(alibabaModel);
176
+ assert.equal(result, null);
177
+ });
178
+
179
+ it("returns null when fallback is disabled", async () => {
180
+ const { resolver } = createResolver({ enabled: false });
181
+ const result = await resolver.checkForRestoration(alibabaModel);
182
+ assert.equal(result, null);
183
+ });
184
+ });
185
+
186
+ // ─── getBestAvailable ────────────────────────────────────────────────────────
187
+
188
+ describe("FallbackResolver — getBestAvailable", () => {
189
+ it("returns highest-priority available provider", async () => {
190
+ const { resolver } = createResolver();
191
+ const result = await resolver.getBestAvailable("coding");
192
+
193
+ assert.notEqual(result, null);
194
+ assert.equal(result!.model.provider, "zai");
195
+ });
196
+
197
+ it("skips backed-off providers", async () => {
198
+ const { resolver } = createResolver({
199
+ isProviderAvailable: (provider: string) => provider !== "zai",
200
+ });
201
+
202
+ const result = await resolver.getBestAvailable("coding");
203
+
204
+ assert.notEqual(result, null);
205
+ assert.equal(result!.model.provider, "alibaba");
206
+ });
207
+
208
+ it("returns null for unknown chain", async () => {
209
+ const { resolver } = createResolver();
210
+ const result = await resolver.getBestAvailable("nonexistent");
211
+ assert.equal(result, null);
212
+ });
213
+ });
214
+
215
+ // ─── findChainsForModel ──────────────────────────────────────────────────────
216
+
217
+ describe("FallbackResolver — findChainsForModel", () => {
218
+ it("finds chains containing a model", () => {
219
+ const { resolver } = createResolver();
220
+ const chains = resolver.findChainsForModel("zai", "glm-5");
221
+ assert.deepEqual(chains, ["coding"]);
222
+ });
223
+
224
+ it("returns empty array for model not in any chain", () => {
225
+ const { resolver } = createResolver();
226
+ const chains = resolver.findChainsForModel("unknown", "model");
227
+ assert.deepEqual(chains, []);
228
+ });
229
+ });