stagent 0.9.6 → 0.11.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 (396) hide show
  1. package/README.md +20 -44
  2. package/dist/cli.js +66 -18
  3. package/docs/.coverage-gaps.json +144 -56
  4. package/docs/.last-generated +1 -1
  5. package/docs/features/agent-intelligence.md +12 -2
  6. package/docs/features/chat.md +40 -5
  7. package/docs/features/cost-usage.md +1 -1
  8. package/docs/features/documents.md +5 -2
  9. package/docs/features/inbox-notifications.md +10 -2
  10. package/docs/features/keyboard-navigation.md +12 -3
  11. package/docs/features/provider-runtimes.md +20 -2
  12. package/docs/features/schedules.md +32 -4
  13. package/docs/features/settings.md +28 -5
  14. package/docs/features/shared-components.md +7 -3
  15. package/docs/features/tables.md +11 -2
  16. package/docs/features/tool-permissions.md +6 -2
  17. package/docs/features/workflows.md +14 -4
  18. package/docs/index.md +1 -1
  19. package/docs/journeys/developer.md +39 -2
  20. package/docs/journeys/personal-use.md +32 -8
  21. package/docs/journeys/power-user.md +45 -14
  22. package/docs/journeys/work-use.md +17 -8
  23. package/docs/manifest.json +15 -15
  24. package/docs/superpowers/plans/2026-04-07-instance-bootstrap.md +1691 -0
  25. package/docs/superpowers/plans/2026-04-08-schedule-orchestration.md +2983 -0
  26. package/docs/superpowers/plans/2026-04-11-schedule-maxturns-api-control.md +551 -0
  27. package/docs/superpowers/plans/2026-04-11-task-create-profile-validation.md +864 -0
  28. package/docs/superpowers/plans/2026-04-11-task-runtime-stagent-mcp-injection.md +739 -0
  29. package/docs/superpowers/plans/2026-04-14-chat-command-namespace-refactor.md +1390 -0
  30. package/docs/superpowers/plans/2026-04-14-chat-environment-integration.md +1561 -0
  31. package/docs/superpowers/plans/2026-04-14-chat-polish-bundle-v1.md +1219 -0
  32. package/docs/superpowers/plans/2026-04-14-chat-session-persistence-provider-closeout.md +399 -0
  33. package/docs/superpowers/specs/2026-04-08-chat-sse-resilience-hotfix-design.md +201 -0
  34. package/docs/superpowers/specs/2026-04-08-schedule-orchestration-design.md +371 -0
  35. package/docs/superpowers/specs/2026-04-08-swarm-visibility-design.md +213 -0
  36. package/next.config.mjs +1 -0
  37. package/package.json +3 -2
  38. package/src/__tests__/instrumentation-smoke.test.ts +15 -0
  39. package/src/app/analytics/page.tsx +1 -21
  40. package/src/app/api/chat/conversations/[id]/messages/route.ts +22 -1
  41. package/src/app/api/chat/conversations/[id]/skills/__tests__/activate.test.ts +141 -0
  42. package/src/app/api/chat/conversations/[id]/skills/activate/route.ts +74 -0
  43. package/src/app/api/chat/conversations/[id]/skills/deactivate/route.ts +33 -0
  44. package/src/app/api/chat/export/route.ts +52 -0
  45. package/src/app/api/chat/files/search/route.ts +50 -0
  46. package/src/app/api/diagnostics/chat-streams/route.ts +65 -0
  47. package/src/app/api/environment/rescan-if-stale/__tests__/route.test.ts +45 -0
  48. package/src/app/api/environment/rescan-if-stale/route.ts +23 -0
  49. package/src/app/api/environment/skills/route.ts +13 -0
  50. package/src/app/api/instance/config/route.ts +41 -0
  51. package/src/app/api/instance/init/route.ts +34 -0
  52. package/src/app/api/instance/upgrade/check/route.ts +26 -0
  53. package/src/app/api/instance/upgrade/route.ts +96 -0
  54. package/src/app/api/instance/upgrade/status/route.ts +35 -0
  55. package/src/app/api/memory/route.ts +0 -11
  56. package/src/app/api/notifications/route.ts +4 -2
  57. package/src/app/api/projects/[id]/route.ts +5 -155
  58. package/src/app/api/projects/__tests__/delete-project.test.ts +10 -19
  59. package/src/app/api/schedules/[id]/execute/route.ts +111 -0
  60. package/src/app/api/schedules/[id]/route.ts +9 -1
  61. package/src/app/api/schedules/__tests__/execute-route.test.ts +118 -0
  62. package/src/app/api/schedules/route.ts +3 -12
  63. package/src/app/api/settings/chat/pins/route.ts +94 -0
  64. package/src/app/api/settings/chat/saved-searches/__tests__/route.test.ts +119 -0
  65. package/src/app/api/settings/chat/saved-searches/route.ts +79 -0
  66. package/src/app/api/settings/environment/route.ts +26 -0
  67. package/src/app/api/settings/openai/login/route.ts +22 -0
  68. package/src/app/api/settings/openai/logout/route.ts +7 -0
  69. package/src/app/api/settings/openai/route.ts +21 -1
  70. package/src/app/api/settings/providers/route.ts +35 -8
  71. package/src/app/api/tables/[id]/enrich/__tests__/route.test.ts +153 -0
  72. package/src/app/api/tables/[id]/enrich/plan/route.ts +98 -0
  73. package/src/app/api/tables/[id]/enrich/route.ts +147 -0
  74. package/src/app/api/tables/[id]/enrich/runs/route.ts +25 -0
  75. package/src/app/api/tasks/[id]/execute/route.ts +52 -33
  76. package/src/app/api/tasks/[id]/respond/route.ts +31 -15
  77. package/src/app/api/tasks/[id]/resume/route.ts +24 -3
  78. package/src/app/api/workflows/[id]/resume/route.ts +59 -0
  79. package/src/app/api/workflows/[id]/status/route.ts +22 -8
  80. package/src/app/api/workspace/context/route.ts +2 -0
  81. package/src/app/api/workspace/fix-data-dir/route.ts +81 -0
  82. package/src/app/chat/page.tsx +11 -0
  83. package/src/app/documents/page.tsx +4 -1
  84. package/src/app/inbox/page.tsx +12 -5
  85. package/src/app/layout.tsx +42 -21
  86. package/src/app/page.tsx +0 -2
  87. package/src/app/settings/page.tsx +8 -9
  88. package/src/components/chat/__tests__/capability-banner.test.tsx +38 -0
  89. package/src/components/chat/__tests__/chat-session-provider.test.tsx +573 -0
  90. package/src/components/chat/__tests__/skill-row.test.tsx +91 -0
  91. package/src/components/chat/capability-banner.tsx +68 -0
  92. package/src/components/chat/chat-command-popover.tsx +670 -49
  93. package/src/components/chat/chat-input.tsx +104 -10
  94. package/src/components/chat/chat-message.tsx +12 -3
  95. package/src/components/chat/chat-session-provider.tsx +790 -0
  96. package/src/components/chat/chat-shell.tsx +151 -401
  97. package/src/components/chat/command-tab-bar.tsx +68 -0
  98. package/src/components/chat/conversation-template-picker.tsx +421 -0
  99. package/src/components/chat/help-dialog.tsx +39 -0
  100. package/src/components/chat/skill-composition-conflict-dialog.tsx +96 -0
  101. package/src/components/chat/skill-row.tsx +147 -0
  102. package/src/components/documents/document-browser.tsx +37 -19
  103. package/src/components/instance/__tests__/instance-section.test.tsx +125 -0
  104. package/src/components/instance/instance-section.tsx +382 -0
  105. package/src/components/instance/upgrade-badge.tsx +219 -0
  106. package/src/components/notifications/__tests__/batch-proposal-review.test.tsx +95 -0
  107. package/src/components/notifications/__tests__/notification-item.test.tsx +106 -0
  108. package/src/components/notifications/__tests__/permission-response-actions.test.tsx +70 -0
  109. package/src/components/notifications/batch-proposal-review.tsx +20 -5
  110. package/src/components/notifications/inbox-list.tsx +11 -2
  111. package/src/components/notifications/notification-item.tsx +56 -2
  112. package/src/components/notifications/pending-approval-host.tsx +56 -37
  113. package/src/components/notifications/permission-response-actions.tsx +155 -1
  114. package/src/components/schedules/schedule-create-sheet.tsx +19 -1
  115. package/src/components/schedules/schedule-edit-sheet.tsx +20 -1
  116. package/src/components/schedules/schedule-form.tsx +31 -0
  117. package/src/components/settings/__tests__/providers-runtimes-section.test.tsx +149 -0
  118. package/src/components/settings/auth-method-selector.tsx +19 -4
  119. package/src/components/settings/auth-status-badge.tsx +28 -3
  120. package/src/components/settings/environment-section.tsx +102 -0
  121. package/src/components/settings/openai-chatgpt-auth-control.tsx +278 -0
  122. package/src/components/settings/openai-runtime-section.tsx +7 -1
  123. package/src/components/settings/providers-runtimes-section.tsx +138 -19
  124. package/src/components/shared/__tests__/filter-hint.test.tsx +40 -0
  125. package/src/components/shared/__tests__/saved-searches-manager.test.tsx +147 -0
  126. package/src/components/shared/app-sidebar.tsx +4 -3
  127. package/src/components/shared/command-palette.tsx +266 -7
  128. package/src/components/shared/filter-hint.tsx +70 -0
  129. package/src/components/shared/filter-input.tsx +59 -0
  130. package/src/components/shared/saved-searches-manager.tsx +199 -0
  131. package/src/components/shared/theme-toggle.tsx +5 -24
  132. package/src/components/shared/workspace-indicator.tsx +61 -2
  133. package/src/components/tables/__tests__/table-enrichment-sheet.test.tsx +130 -0
  134. package/src/components/tables/table-create-sheet.tsx +4 -0
  135. package/src/components/tables/table-enrichment-runs.tsx +103 -0
  136. package/src/components/tables/table-enrichment-sheet.tsx +538 -0
  137. package/src/components/tables/table-spreadsheet.tsx +29 -5
  138. package/src/components/tables/table-toolbar.tsx +10 -1
  139. package/src/components/tasks/kanban-board.tsx +1 -0
  140. package/src/components/tasks/kanban-column.tsx +53 -14
  141. package/src/components/tasks/task-bento-grid.tsx +31 -2
  142. package/src/components/tasks/task-card.tsx +29 -3
  143. package/src/components/tasks/task-chip-bar.tsx +54 -1
  144. package/src/components/tasks/task-result-renderer.tsx +1 -1
  145. package/src/components/workflows/delay-step-body.tsx +109 -0
  146. package/src/components/workflows/hooks/use-workflow-status.ts +50 -0
  147. package/src/components/workflows/loop-status-view.tsx +1 -1
  148. package/src/components/workflows/shared/step-result.tsx +78 -0
  149. package/src/components/workflows/shared/workflow-header.tsx +141 -0
  150. package/src/components/workflows/shared/workflow-loading-skeleton.tsx +36 -0
  151. package/src/components/workflows/swarm-dashboard.tsx +2 -15
  152. package/src/components/workflows/views/loop-pattern-view.tsx +137 -0
  153. package/src/components/workflows/views/sequence-pattern-view.tsx +511 -0
  154. package/src/components/workflows/workflow-form-view.tsx +133 -16
  155. package/src/components/workflows/workflow-status-view.tsx +30 -740
  156. package/src/hooks/__tests__/use-chat-autocomplete-tabs.test.ts +47 -0
  157. package/src/hooks/__tests__/use-saved-searches.test.ts +70 -0
  158. package/src/hooks/use-active-skills.ts +110 -0
  159. package/src/hooks/use-chat-autocomplete.ts +120 -7
  160. package/src/hooks/use-enriched-skills.ts +19 -0
  161. package/src/hooks/use-pinned-entries.ts +104 -0
  162. package/src/hooks/use-recent-user-messages.ts +19 -0
  163. package/src/hooks/use-saved-searches.ts +142 -0
  164. package/src/instrumentation-node.ts +94 -0
  165. package/src/instrumentation.ts +4 -48
  166. package/src/lib/agents/__tests__/claude-agent-sdk-options.test.ts +56 -0
  167. package/src/lib/agents/__tests__/claude-agent.test.ts +212 -0
  168. package/src/lib/agents/__tests__/execution-manager.test.ts +1 -27
  169. package/src/lib/agents/__tests__/failure-reason.test.ts +68 -0
  170. package/src/lib/agents/__tests__/learned-context.test.ts +0 -11
  171. package/src/lib/agents/__tests__/learning-session.test.ts +158 -0
  172. package/src/lib/agents/__tests__/pattern-extractor.test.ts +48 -0
  173. package/src/lib/agents/__tests__/task-dispatch.test.ts +166 -0
  174. package/src/lib/agents/__tests__/tool-permissions.test.ts +60 -0
  175. package/src/lib/agents/claude-agent.ts +217 -21
  176. package/src/lib/agents/execution-manager.ts +0 -35
  177. package/src/lib/agents/handoff/bus.ts +2 -2
  178. package/src/lib/agents/learned-context.ts +0 -12
  179. package/src/lib/agents/learning-session.ts +18 -5
  180. package/src/lib/agents/profiles/__tests__/list-fused-profiles.test.ts +110 -0
  181. package/src/lib/agents/profiles/__tests__/registry.test.ts +53 -4
  182. package/src/lib/agents/profiles/builtins/upgrade-assistant/SKILL.md +97 -0
  183. package/src/lib/agents/profiles/builtins/upgrade-assistant/profile.yaml +36 -0
  184. package/src/lib/agents/profiles/list-fused-profiles.ts +104 -0
  185. package/src/lib/agents/profiles/registry.ts +18 -0
  186. package/src/lib/agents/profiles/types.ts +7 -1
  187. package/src/lib/agents/router.ts +3 -6
  188. package/src/lib/agents/runtime/__tests__/catalog.test.ts +130 -0
  189. package/src/lib/agents/runtime/__tests__/execution-target.test.ts +183 -0
  190. package/src/lib/agents/runtime/__tests__/openai-codex-auth.test.ts +118 -0
  191. package/src/lib/agents/runtime/anthropic-direct.ts +8 -0
  192. package/src/lib/agents/runtime/catalog.ts +121 -0
  193. package/src/lib/agents/runtime/claude-sdk.ts +32 -0
  194. package/src/lib/agents/runtime/codex-app-server-client.ts +11 -5
  195. package/src/lib/agents/runtime/execution-target.ts +456 -0
  196. package/src/lib/agents/runtime/index.ts +4 -0
  197. package/src/lib/agents/runtime/launch-failure.ts +101 -0
  198. package/src/lib/agents/runtime/openai-codex-auth.ts +389 -0
  199. package/src/lib/agents/runtime/openai-codex.ts +64 -60
  200. package/src/lib/agents/runtime/openai-direct.ts +8 -0
  201. package/src/lib/agents/runtime/types.ts +8 -0
  202. package/src/lib/agents/task-dispatch.ts +220 -0
  203. package/src/lib/agents/tool-permissions.ts +16 -1
  204. package/src/lib/book/chapter-mapping.ts +11 -0
  205. package/src/lib/book/content.ts +10 -0
  206. package/src/lib/chat/__tests__/active-skill-injection.test.ts +261 -0
  207. package/src/lib/chat/__tests__/active-streams.test.ts +49 -0
  208. package/src/lib/chat/__tests__/clean-filter-input.test.ts +68 -0
  209. package/src/lib/chat/__tests__/command-tabs.test.ts +68 -0
  210. package/src/lib/chat/__tests__/context-builder-files.test.ts +112 -0
  211. package/src/lib/chat/__tests__/dismissals.test.ts +65 -0
  212. package/src/lib/chat/__tests__/engine-sdk-options.test.ts +117 -0
  213. package/src/lib/chat/__tests__/finalize-safety-net.test.ts +139 -0
  214. package/src/lib/chat/__tests__/reconcile.test.ts +137 -0
  215. package/src/lib/chat/__tests__/skill-conflict.test.ts +35 -0
  216. package/src/lib/chat/__tests__/stream-telemetry.test.ts +151 -0
  217. package/src/lib/chat/__tests__/types.test.ts +28 -0
  218. package/src/lib/chat/active-skills.ts +31 -0
  219. package/src/lib/chat/active-streams.ts +27 -0
  220. package/src/lib/chat/clean-filter-input.ts +30 -0
  221. package/src/lib/chat/codex-engine.ts +46 -24
  222. package/src/lib/chat/command-tabs.ts +61 -0
  223. package/src/lib/chat/context-builder.ts +146 -4
  224. package/src/lib/chat/dismissals.ts +73 -0
  225. package/src/lib/chat/engine.ts +159 -18
  226. package/src/lib/chat/files/__tests__/search.test.ts +135 -0
  227. package/src/lib/chat/files/expand-mention.ts +76 -0
  228. package/src/lib/chat/files/search.ts +99 -0
  229. package/src/lib/chat/reconcile.ts +117 -0
  230. package/src/lib/chat/skill-composition.ts +210 -0
  231. package/src/lib/chat/skill-conflict.ts +105 -0
  232. package/src/lib/chat/stagent-tools.ts +7 -19
  233. package/src/lib/chat/stream-telemetry.ts +137 -0
  234. package/src/lib/chat/suggested-prompts.ts +28 -1
  235. package/src/lib/chat/system-prompt.ts +48 -1
  236. package/src/lib/chat/tool-catalog.ts +35 -4
  237. package/src/lib/chat/tools/__tests__/enrich-table-tool.test.ts +127 -0
  238. package/src/lib/chat/tools/__tests__/profile-tools.test.ts +51 -0
  239. package/src/lib/chat/tools/__tests__/schedule-tools.test.ts +261 -0
  240. package/src/lib/chat/tools/__tests__/settings-tools.test.ts +294 -0
  241. package/src/lib/chat/tools/__tests__/skill-tools.test.ts +474 -0
  242. package/src/lib/chat/tools/__tests__/task-tools.test.ts +399 -0
  243. package/src/lib/chat/tools/__tests__/workflow-tools-dedup.test.ts +351 -0
  244. package/src/lib/chat/tools/blueprint-tools.ts +190 -0
  245. package/src/lib/chat/tools/document-tools.ts +29 -13
  246. package/src/lib/chat/tools/helpers.ts +41 -0
  247. package/src/lib/chat/tools/notification-tools.ts +9 -5
  248. package/src/lib/chat/tools/profile-tools.ts +120 -23
  249. package/src/lib/chat/tools/project-tools.ts +33 -0
  250. package/src/lib/chat/tools/schedule-tools.ts +44 -11
  251. package/src/lib/chat/tools/skill-tools.ts +183 -0
  252. package/src/lib/chat/tools/table-tools.ts +71 -0
  253. package/src/lib/chat/tools/task-tools.ts +89 -21
  254. package/src/lib/chat/tools/workflow-tools.ts +275 -32
  255. package/src/lib/chat/types.ts +15 -0
  256. package/src/lib/constants/settings.ts +10 -18
  257. package/src/lib/data/__tests__/clear.test.ts +56 -2
  258. package/src/lib/data/clear.ts +17 -16
  259. package/src/lib/data/delete-project.ts +171 -0
  260. package/src/lib/db/__tests__/bootstrap.test.ts +1 -1
  261. package/src/lib/db/bootstrap.ts +62 -16
  262. package/src/lib/db/index.ts +5 -0
  263. package/src/lib/db/migrations/0009_add_app_instances.sql +25 -0
  264. package/src/lib/db/migrations/0024_add_workflow_resume_at.sql +10 -0
  265. package/src/lib/db/migrations/0025_drop_app_instances.sql +3 -0
  266. package/src/lib/db/migrations/0026_drop_license.sql +3 -0
  267. package/src/lib/db/migrations/meta/_journal.json +21 -0
  268. package/src/lib/db/schema.ts +94 -23
  269. package/src/lib/environment/__tests__/auto-promote.test.ts +132 -0
  270. package/src/lib/environment/__tests__/list-skills-enriched.test.ts +55 -0
  271. package/src/lib/environment/__tests__/skill-enrichment.test.ts +129 -0
  272. package/src/lib/environment/__tests__/skill-recommendations.test.ts +87 -0
  273. package/src/lib/environment/data.ts +9 -0
  274. package/src/lib/environment/list-skills.ts +176 -0
  275. package/src/lib/environment/parsers/__tests__/skill.test.ts +54 -0
  276. package/src/lib/environment/parsers/skill.ts +26 -5
  277. package/src/lib/environment/profile-generator.ts +54 -0
  278. package/src/lib/environment/skill-enrichment.ts +106 -0
  279. package/src/lib/environment/skill-recommendations.ts +66 -0
  280. package/src/lib/environment/workspace-context.ts +13 -1
  281. package/src/lib/filters/__tests__/parse.quoted.test.ts +40 -0
  282. package/src/lib/filters/__tests__/parse.test.ts +135 -0
  283. package/src/lib/filters/parse.ts +86 -0
  284. package/src/lib/import/dedup.ts +4 -54
  285. package/src/lib/instance/__tests__/bootstrap.test.ts +362 -0
  286. package/src/lib/instance/__tests__/detect.test.ts +115 -0
  287. package/src/lib/instance/__tests__/fingerprint.test.ts +48 -0
  288. package/src/lib/instance/__tests__/git-ops.test.ts +95 -0
  289. package/src/lib/instance/__tests__/settings.test.ts +83 -0
  290. package/src/lib/instance/__tests__/upgrade-poller.test.ts +181 -0
  291. package/src/lib/instance/bootstrap.ts +270 -0
  292. package/src/lib/instance/detect.ts +49 -0
  293. package/src/lib/instance/fingerprint.ts +76 -0
  294. package/src/lib/instance/git-ops.ts +95 -0
  295. package/src/lib/instance/settings.ts +61 -0
  296. package/src/lib/instance/types.ts +77 -0
  297. package/src/lib/instance/upgrade-poller.ts +205 -0
  298. package/src/lib/notifications/__tests__/visibility.test.ts +51 -0
  299. package/src/lib/notifications/visibility.ts +33 -0
  300. package/src/lib/schedules/__tests__/collision-check.test.ts +93 -0
  301. package/src/lib/schedules/__tests__/config.test.ts +62 -0
  302. package/src/lib/schedules/__tests__/firing-metrics.test.ts +99 -0
  303. package/src/lib/schedules/__tests__/integration.test.ts +82 -0
  304. package/src/lib/schedules/__tests__/slot-claim.test.ts +242 -0
  305. package/src/lib/schedules/__tests__/tick-scheduler.test.ts +102 -0
  306. package/src/lib/schedules/__tests__/turn-budget.test.ts +228 -0
  307. package/src/lib/schedules/collision-check.ts +105 -0
  308. package/src/lib/schedules/config.ts +53 -0
  309. package/src/lib/schedules/scheduler.ts +236 -17
  310. package/src/lib/schedules/slot-claim.ts +105 -0
  311. package/src/lib/settings/__tests__/openai-auth.test.ts +101 -0
  312. package/src/lib/settings/__tests__/openai-login-manager.test.ts +64 -0
  313. package/src/lib/settings/__tests__/runtime-setup.test.ts +33 -0
  314. package/src/lib/settings/openai-auth.ts +105 -10
  315. package/src/lib/settings/openai-login-manager.ts +260 -0
  316. package/src/lib/settings/runtime-setup.ts +14 -4
  317. package/src/lib/tables/__tests__/enrichment-planner.test.ts +124 -0
  318. package/src/lib/tables/__tests__/enrichment.test.ts +147 -0
  319. package/src/lib/tables/enrichment-planner.ts +454 -0
  320. package/src/lib/tables/enrichment.ts +328 -0
  321. package/src/lib/tables/query-builder.ts +5 -2
  322. package/src/lib/tables/trigger-evaluator.ts +3 -2
  323. package/src/lib/theme.ts +71 -0
  324. package/src/lib/usage/ledger.ts +2 -18
  325. package/src/lib/util/__tests__/similarity.test.ts +106 -0
  326. package/src/lib/util/similarity.ts +77 -0
  327. package/src/lib/utils/format-timestamp.ts +24 -0
  328. package/src/lib/utils/stagent-paths.ts +12 -0
  329. package/src/lib/validators/__tests__/blueprint.test.ts +172 -0
  330. package/src/lib/validators/__tests__/settings.test.ts +10 -0
  331. package/src/lib/validators/blueprint.ts +70 -9
  332. package/src/lib/validators/profile.ts +2 -2
  333. package/src/lib/validators/settings.ts +3 -1
  334. package/src/lib/workflows/__tests__/delay.test.ts +196 -0
  335. package/src/lib/workflows/__tests__/engine.test.ts +8 -0
  336. package/src/lib/workflows/__tests__/loop-executor.test.ts +54 -0
  337. package/src/lib/workflows/__tests__/post-action.test.ts +108 -0
  338. package/src/lib/workflows/blueprints/__tests__/render-prompt.test.ts +124 -0
  339. package/src/lib/workflows/blueprints/instantiator.ts +22 -1
  340. package/src/lib/workflows/blueprints/render-prompt.ts +71 -0
  341. package/src/lib/workflows/blueprints/types.ts +16 -2
  342. package/src/lib/workflows/delay.ts +106 -0
  343. package/src/lib/workflows/engine.ts +212 -7
  344. package/src/lib/workflows/loop-executor.ts +349 -24
  345. package/src/lib/workflows/post-action.ts +91 -0
  346. package/src/lib/workflows/types.ts +166 -1
  347. package/src/test/setup.ts +10 -0
  348. package/src/app/api/license/checkout/route.ts +0 -28
  349. package/src/app/api/license/portal/route.ts +0 -26
  350. package/src/app/api/license/route.ts +0 -89
  351. package/src/app/api/license/usage/route.ts +0 -63
  352. package/src/app/api/marketplace/browse/route.ts +0 -15
  353. package/src/app/api/marketplace/import/route.ts +0 -28
  354. package/src/app/api/marketplace/publish/route.ts +0 -40
  355. package/src/app/api/onboarding/email/route.ts +0 -53
  356. package/src/app/api/settings/telemetry/route.ts +0 -14
  357. package/src/app/api/sync/export/route.ts +0 -54
  358. package/src/app/api/sync/restore/route.ts +0 -37
  359. package/src/app/api/sync/sessions/route.ts +0 -24
  360. package/src/app/auth/callback/route.ts +0 -73
  361. package/src/app/marketplace/page.tsx +0 -19
  362. package/src/components/analytics/analytics-gate-card.tsx +0 -101
  363. package/src/components/marketplace/blueprint-card.tsx +0 -61
  364. package/src/components/marketplace/marketplace-browser.tsx +0 -131
  365. package/src/components/onboarding/email-capture-card.tsx +0 -104
  366. package/src/components/settings/activation-form.tsx +0 -95
  367. package/src/components/settings/cloud-account-section.tsx +0 -147
  368. package/src/components/settings/cloud-sync-section.tsx +0 -155
  369. package/src/components/settings/subscription-section.tsx +0 -410
  370. package/src/components/settings/telemetry-section.tsx +0 -80
  371. package/src/components/shared/premium-gate-overlay.tsx +0 -50
  372. package/src/components/shared/schedule-gate-dialog.tsx +0 -64
  373. package/src/components/shared/upgrade-banner.tsx +0 -112
  374. package/src/hooks/use-supabase-auth.ts +0 -79
  375. package/src/lib/billing/email.ts +0 -54
  376. package/src/lib/billing/products.ts +0 -80
  377. package/src/lib/billing/stripe.ts +0 -101
  378. package/src/lib/cloud/supabase-browser.ts +0 -32
  379. package/src/lib/cloud/supabase-client.ts +0 -56
  380. package/src/lib/license/__tests__/features.test.ts +0 -56
  381. package/src/lib/license/__tests__/key-format.test.ts +0 -88
  382. package/src/lib/license/__tests__/manager.test.ts +0 -64
  383. package/src/lib/license/__tests__/tier-limits.test.ts +0 -79
  384. package/src/lib/license/cloud-validation.ts +0 -60
  385. package/src/lib/license/features.ts +0 -44
  386. package/src/lib/license/key-format.ts +0 -101
  387. package/src/lib/license/limit-check.ts +0 -111
  388. package/src/lib/license/limit-queries.ts +0 -51
  389. package/src/lib/license/manager.ts +0 -345
  390. package/src/lib/license/notifications.ts +0 -59
  391. package/src/lib/license/tier-limits.ts +0 -71
  392. package/src/lib/marketplace/marketplace-client.ts +0 -107
  393. package/src/lib/sync/cloud-sync.ts +0 -235
  394. package/src/lib/telemetry/conversion-events.ts +0 -71
  395. package/src/lib/telemetry/queue.ts +0 -122
  396. package/src/lib/validators/license.ts +0 -33
@@ -0,0 +1,456 @@
1
+ import { getProfile } from "@/lib/agents/profiles/registry";
2
+ import { profileSupportsRuntime } from "@/lib/agents/profiles/compatibility";
3
+ import { suggestRuntime } from "@/lib/agents/router";
4
+ import {
5
+ DEFAULT_AGENT_RUNTIME,
6
+ getRuntimeCatalogEntry,
7
+ getRuntimeFeatures,
8
+ resolveAgentRuntime,
9
+ type AgentRuntimeId,
10
+ } from "./catalog";
11
+ import { testRuntimeConnection } from "./index";
12
+ import { getRoutingPreference } from "@/lib/settings/routing";
13
+ import { getRuntimeSetupStates, listConfiguredRuntimeIds } from "@/lib/settings/runtime-setup";
14
+ import { DEFAULT_CHAT_MODEL, getRuntimeForModel } from "@/lib/chat/types";
15
+
16
+ const FILESYSTEM_TOOL_NAMES = new Set([
17
+ "Read",
18
+ "Write",
19
+ "Edit",
20
+ "MultiEdit",
21
+ "Grep",
22
+ "Glob",
23
+ ]);
24
+
25
+ const CHAT_MODEL_FALLBACKS: Record<string, string[]> = {
26
+ haiku: ["gpt-5.4-mini", "gpt-5.3-codex", "gpt-5.4"],
27
+ sonnet: ["gpt-5.3-codex", "gpt-5.4", "gpt-5.4-mini"],
28
+ opus: ["gpt-5.4", "gpt-5.3-codex", "gpt-5.4-mini"],
29
+ "gpt-5.4-mini": ["haiku", "sonnet", "opus"],
30
+ "gpt-5.3-codex": ["sonnet", "haiku", "opus"],
31
+ "gpt-5.4": ["opus", "sonnet", "haiku"],
32
+ };
33
+
34
+ export class RuntimeUnavailableError extends Error {
35
+ constructor(message: string) {
36
+ super(message);
37
+ this.name = "RuntimeUnavailableError";
38
+ }
39
+ }
40
+
41
+ export class RequestedModelUnavailableError extends Error {
42
+ constructor(message: string) {
43
+ super(message);
44
+ this.name = "RequestedModelUnavailableError";
45
+ }
46
+ }
47
+
48
+ export class NoCompatibleRuntimeError extends Error {
49
+ constructor(message: string) {
50
+ super(message);
51
+ this.name = "NoCompatibleRuntimeError";
52
+ }
53
+ }
54
+
55
+ export interface ResolvedExecutionTarget {
56
+ requestedRuntimeId: AgentRuntimeId | null;
57
+ effectiveRuntimeId: AgentRuntimeId;
58
+ requestedModelId: string | null;
59
+ effectiveModelId: string | null;
60
+ fallbackApplied: boolean;
61
+ fallbackReason: string | null;
62
+ }
63
+
64
+ type RuntimeRequirements = {
65
+ requiresBash: boolean;
66
+ requiresFilesystem: boolean;
67
+ };
68
+
69
+ type RuntimeAvailability = {
70
+ available: boolean;
71
+ reason: string | null;
72
+ };
73
+
74
+ function unique<T>(values: T[]): T[] {
75
+ return Array.from(new Set(values));
76
+ }
77
+
78
+ function getRuntimeLabel(runtimeId: AgentRuntimeId): string {
79
+ return getRuntimeCatalogEntry(runtimeId).label;
80
+ }
81
+
82
+ function detectRuntimeRequirements(profileId?: string | null): RuntimeRequirements {
83
+ const profile = profileId ? getProfile(profileId) : undefined;
84
+ const allowedTools = profile?.allowedTools ?? [];
85
+
86
+ const requiresBash = allowedTools.some(
87
+ (tool) => tool === "Bash" || tool.startsWith("Bash(")
88
+ );
89
+ const requiresFilesystem =
90
+ requiresBash ||
91
+ allowedTools.some((tool) => FILESYSTEM_TOOL_NAMES.has(tool));
92
+
93
+ return { requiresBash, requiresFilesystem };
94
+ }
95
+
96
+ function runtimeMeetsRequirements(
97
+ runtimeId: AgentRuntimeId,
98
+ requirements: RuntimeRequirements
99
+ ): boolean {
100
+ const features = getRuntimeFeatures(runtimeId);
101
+ if (requirements.requiresBash && !features.hasBash) {
102
+ return false;
103
+ }
104
+ if (requirements.requiresFilesystem && !features.hasFilesystemTools) {
105
+ return false;
106
+ }
107
+ return true;
108
+ }
109
+
110
+ function filterCompatibleRuntimes(
111
+ runtimeIds: AgentRuntimeId[],
112
+ profileId?: string | null
113
+ ): AgentRuntimeId[] {
114
+ if (!profileId) {
115
+ return runtimeIds;
116
+ }
117
+
118
+ const profile = getProfile(profileId);
119
+ if (!profile) {
120
+ return [];
121
+ }
122
+
123
+ return runtimeIds.filter((runtimeId) =>
124
+ profileSupportsRuntime(profile, runtimeId)
125
+ );
126
+ }
127
+
128
+ async function checkRuntimeAvailability(
129
+ runtimeId: AgentRuntimeId
130
+ ): Promise<RuntimeAvailability> {
131
+ const states = await getRuntimeSetupStates();
132
+ if (!states[runtimeId]?.configured) {
133
+ return {
134
+ available: false,
135
+ reason: `${getRuntimeLabel(runtimeId)} is not configured`,
136
+ };
137
+ }
138
+
139
+ try {
140
+ const connection = await testRuntimeConnection(runtimeId);
141
+ if (connection.connected) {
142
+ return { available: true, reason: null };
143
+ }
144
+ return {
145
+ available: false,
146
+ reason:
147
+ connection.error ??
148
+ `${getRuntimeLabel(runtimeId)} is unavailable`,
149
+ };
150
+ } catch (error) {
151
+ return {
152
+ available: false,
153
+ reason: error instanceof Error ? error.message : String(error),
154
+ };
155
+ }
156
+ }
157
+
158
+ async function getConfiguredCandidateRuntimes(
159
+ profileId?: string | null
160
+ ): Promise<AgentRuntimeId[]> {
161
+ const states = await getRuntimeSetupStates();
162
+ return filterCompatibleRuntimes(
163
+ listConfiguredRuntimeIds(states) as AgentRuntimeId[],
164
+ profileId
165
+ );
166
+ }
167
+
168
+ function buildTaskFallbackOrder(input: {
169
+ title: string;
170
+ description?: string | null;
171
+ profileId?: string | null;
172
+ requestedRuntimeId: AgentRuntimeId | null;
173
+ compatibleRuntimeIds: AgentRuntimeId[];
174
+ }): AgentRuntimeId[] {
175
+ const alternates = input.compatibleRuntimeIds.filter(
176
+ (runtimeId) => runtimeId !== input.requestedRuntimeId
177
+ );
178
+ if (alternates.length === 0) {
179
+ return [];
180
+ }
181
+
182
+ const preferred = suggestRuntime(
183
+ input.title,
184
+ input.description,
185
+ input.profileId,
186
+ alternates,
187
+ "quality"
188
+ ).runtimeId;
189
+
190
+ return unique([
191
+ preferred,
192
+ ...alternates.filter(
193
+ (runtimeId) =>
194
+ input.requestedRuntimeId != null &&
195
+ getRuntimeCatalogEntry(runtimeId).providerId ===
196
+ getRuntimeCatalogEntry(input.requestedRuntimeId).providerId
197
+ ),
198
+ ...alternates,
199
+ ]);
200
+ }
201
+
202
+ function buildRuntimeFallbackReason(input: {
203
+ requestedRuntimeId: AgentRuntimeId | null;
204
+ effectiveRuntimeId: AgentRuntimeId;
205
+ unavailableReason: string | null;
206
+ }): string | null {
207
+ if (!input.requestedRuntimeId) {
208
+ return null;
209
+ }
210
+
211
+ const requestedLabel = getRuntimeLabel(input.requestedRuntimeId);
212
+ const effectiveLabel = getRuntimeLabel(input.effectiveRuntimeId);
213
+ const reason = input.unavailableReason ?? `${requestedLabel} is unavailable`;
214
+ return `${reason}. Fell back to ${effectiveLabel}.`;
215
+ }
216
+
217
+ export async function resolveTaskExecutionTarget(input: {
218
+ title: string;
219
+ description?: string | null;
220
+ requestedRuntimeId?: string | null;
221
+ profileId?: string | null;
222
+ unavailableRuntimeIds?: string[];
223
+ unavailableReasons?: Record<string, string>;
224
+ }): Promise<ResolvedExecutionTarget> {
225
+ const requestedRuntimeId = input.requestedRuntimeId
226
+ ? resolveAgentRuntime(input.requestedRuntimeId)
227
+ : null;
228
+ const requirements = detectRuntimeRequirements(input.profileId);
229
+ const unavailableRuntimeIds = new Set(
230
+ (input.unavailableRuntimeIds ?? []).map((runtimeId) =>
231
+ resolveAgentRuntime(runtimeId)
232
+ )
233
+ );
234
+ const configuredCandidates = await getConfiguredCandidateRuntimes(input.profileId);
235
+ const compatibleCandidates = configuredCandidates.filter((runtimeId) =>
236
+ runtimeMeetsRequirements(runtimeId, requirements)
237
+ );
238
+ const launchableCandidates = compatibleCandidates.filter(
239
+ (runtimeId) => !unavailableRuntimeIds.has(runtimeId)
240
+ );
241
+
242
+ if (compatibleCandidates.length === 0) {
243
+ throw new NoCompatibleRuntimeError(
244
+ "No compatible configured runtime is available for this task."
245
+ );
246
+ }
247
+
248
+ if (requestedRuntimeId) {
249
+ if (
250
+ compatibleCandidates.includes(requestedRuntimeId) &&
251
+ !unavailableRuntimeIds.has(requestedRuntimeId) &&
252
+ (await checkRuntimeAvailability(requestedRuntimeId)).available
253
+ ) {
254
+ return {
255
+ requestedRuntimeId,
256
+ effectiveRuntimeId: requestedRuntimeId,
257
+ requestedModelId: null,
258
+ effectiveModelId: null,
259
+ fallbackApplied: false,
260
+ fallbackReason: null,
261
+ };
262
+ }
263
+
264
+ const availability = unavailableRuntimeIds.has(requestedRuntimeId)
265
+ ? {
266
+ available: false,
267
+ reason:
268
+ input.unavailableReasons?.[requestedRuntimeId] ??
269
+ `${getRuntimeLabel(requestedRuntimeId)} is temporarily unavailable`,
270
+ }
271
+ : compatibleCandidates.includes(requestedRuntimeId)
272
+ ? await checkRuntimeAvailability(requestedRuntimeId)
273
+ : {
274
+ available: false,
275
+ reason: `${getRuntimeLabel(requestedRuntimeId)} does not support this task/profile`,
276
+ };
277
+ const fallbackOrder = buildTaskFallbackOrder({
278
+ title: input.title,
279
+ description: input.description,
280
+ profileId: input.profileId,
281
+ requestedRuntimeId,
282
+ compatibleRuntimeIds: launchableCandidates,
283
+ });
284
+
285
+ for (const candidate of fallbackOrder) {
286
+ const candidateAvailability = await checkRuntimeAvailability(candidate);
287
+ if (candidateAvailability.available) {
288
+ return {
289
+ requestedRuntimeId,
290
+ effectiveRuntimeId: candidate,
291
+ requestedModelId: null,
292
+ effectiveModelId: null,
293
+ fallbackApplied: true,
294
+ fallbackReason: buildRuntimeFallbackReason({
295
+ requestedRuntimeId,
296
+ effectiveRuntimeId: candidate,
297
+ unavailableReason: availability.reason,
298
+ }),
299
+ };
300
+ }
301
+ }
302
+
303
+ throw new NoCompatibleRuntimeError(
304
+ availability.reason ??
305
+ `No healthy alternate runtime is available for ${getRuntimeLabel(requestedRuntimeId)}.`
306
+ );
307
+ }
308
+
309
+ const routingPreference = await getRoutingPreference();
310
+ const suggested = suggestRuntime(
311
+ input.title,
312
+ input.description,
313
+ input.profileId,
314
+ launchableCandidates,
315
+ routingPreference
316
+ ).runtimeId;
317
+ const autoOrder = unique([
318
+ suggested,
319
+ ...launchableCandidates,
320
+ ]);
321
+
322
+ for (const candidate of autoOrder) {
323
+ const availability = await checkRuntimeAvailability(candidate);
324
+ if (availability.available) {
325
+ return {
326
+ requestedRuntimeId: null,
327
+ effectiveRuntimeId: candidate,
328
+ requestedModelId: null,
329
+ effectiveModelId: null,
330
+ fallbackApplied: false,
331
+ fallbackReason: null,
332
+ };
333
+ }
334
+ }
335
+
336
+ throw new RuntimeUnavailableError(
337
+ "No healthy runtime is currently available to execute this task."
338
+ );
339
+ }
340
+
341
+ export async function resolveResumeExecutionTarget(input: {
342
+ requestedRuntimeId?: string | null;
343
+ effectiveRuntimeId?: string | null;
344
+ }): Promise<ResolvedExecutionTarget> {
345
+ const requestedRuntimeId = input.requestedRuntimeId
346
+ ? resolveAgentRuntime(input.requestedRuntimeId)
347
+ : null;
348
+ const resumeRuntimeId = input.effectiveRuntimeId
349
+ ? resolveAgentRuntime(input.effectiveRuntimeId)
350
+ : requestedRuntimeId ?? DEFAULT_AGENT_RUNTIME;
351
+ const availability = await checkRuntimeAvailability(resumeRuntimeId);
352
+
353
+ if (!availability.available) {
354
+ throw new RuntimeUnavailableError(
355
+ availability.reason ??
356
+ `${getRuntimeLabel(resumeRuntimeId)} is unavailable for resume. Use Retry for a fresh execution.`
357
+ );
358
+ }
359
+
360
+ return {
361
+ requestedRuntimeId,
362
+ effectiveRuntimeId: resumeRuntimeId,
363
+ requestedModelId: null,
364
+ effectiveModelId: null,
365
+ fallbackApplied: false,
366
+ fallbackReason: null,
367
+ };
368
+ }
369
+
370
+ function buildChatFallbackOrder(requestedModelId: string): string[] {
371
+ const fallbacks = CHAT_MODEL_FALLBACKS[requestedModelId] ?? [];
372
+ return unique([requestedModelId, ...fallbacks]);
373
+ }
374
+
375
+ function buildChatFallbackReason(input: {
376
+ requestedRuntimeId: AgentRuntimeId;
377
+ effectiveRuntimeId: AgentRuntimeId;
378
+ requestedModelId: string;
379
+ effectiveModelId: string;
380
+ unavailableReason: string | null;
381
+ }): string | null {
382
+ if (
383
+ input.requestedRuntimeId === input.effectiveRuntimeId &&
384
+ input.requestedModelId === input.effectiveModelId
385
+ ) {
386
+ return null;
387
+ }
388
+
389
+ const requestedLabel = `${input.requestedModelId} on ${getRuntimeLabel(input.requestedRuntimeId)}`;
390
+ const effectiveLabel = `${input.effectiveModelId} on ${getRuntimeLabel(input.effectiveRuntimeId)}`;
391
+ const reason = input.unavailableReason ?? `${requestedLabel} is unavailable`;
392
+ return `${reason}. Using ${effectiveLabel} for this turn.`;
393
+ }
394
+
395
+ export async function resolveChatExecutionTarget(input: {
396
+ requestedRuntimeId?: string | null;
397
+ requestedModelId?: string | null;
398
+ }): Promise<ResolvedExecutionTarget> {
399
+ const requestedModelId =
400
+ input.requestedModelId ??
401
+ (input.requestedRuntimeId
402
+ ? getRuntimeCatalogEntry(resolveAgentRuntime(input.requestedRuntimeId)).models.default
403
+ : DEFAULT_CHAT_MODEL);
404
+ const requestedRuntimeId = resolveAgentRuntime(
405
+ input.requestedRuntimeId ?? getRuntimeForModel(requestedModelId)
406
+ );
407
+
408
+ const modelOrder = buildChatFallbackOrder(requestedModelId);
409
+ let requestedAvailability: RuntimeAvailability | null = null;
410
+
411
+ for (const candidateModelId of modelOrder) {
412
+ const candidateRuntimeId = resolveAgentRuntime(
413
+ getRuntimeForModel(candidateModelId)
414
+ );
415
+ if (
416
+ candidateRuntimeId !== "claude-code" &&
417
+ candidateRuntimeId !== "openai-codex-app-server" &&
418
+ candidateRuntimeId !== "ollama"
419
+ ) {
420
+ continue;
421
+ }
422
+
423
+ const availability = await checkRuntimeAvailability(candidateRuntimeId);
424
+ if (
425
+ candidateRuntimeId === requestedRuntimeId &&
426
+ requestedAvailability === null
427
+ ) {
428
+ requestedAvailability = availability;
429
+ }
430
+ if (!availability.available) {
431
+ continue;
432
+ }
433
+
434
+ return {
435
+ requestedRuntimeId,
436
+ effectiveRuntimeId: candidateRuntimeId,
437
+ requestedModelId,
438
+ effectiveModelId: candidateModelId,
439
+ fallbackApplied:
440
+ candidateRuntimeId !== requestedRuntimeId ||
441
+ candidateModelId !== requestedModelId,
442
+ fallbackReason: buildChatFallbackReason({
443
+ requestedRuntimeId,
444
+ effectiveRuntimeId: candidateRuntimeId,
445
+ requestedModelId,
446
+ effectiveModelId: candidateModelId,
447
+ unavailableReason: requestedAvailability?.reason ?? null,
448
+ }),
449
+ };
450
+ }
451
+
452
+ throw new RequestedModelUnavailableError(
453
+ requestedAvailability?.reason ??
454
+ `No healthy runtime is available for ${requestedModelId}.`
455
+ );
456
+ }
@@ -2,6 +2,7 @@ import {
2
2
  DEFAULT_AGENT_RUNTIME,
3
3
  getRuntimeCapabilities,
4
4
  getRuntimeCatalogEntry,
5
+ getRuntimeFeatures,
5
6
  listRuntimeCatalog,
6
7
  resolveAgentRuntime,
7
8
  type AgentRuntimeId,
@@ -187,3 +188,6 @@ export async function testRuntimeConnection(
187
188
  }
188
189
  return adapter.testConnection();
189
190
  }
191
+
192
+ export { getRuntimeFeatures };
193
+ export type { RuntimeFeatures } from "./catalog";
@@ -0,0 +1,101 @@
1
+ import {
2
+ getRuntimeCatalogEntry,
3
+ type AgentRuntimeId,
4
+ } from "@/lib/agents/runtime/catalog";
5
+
6
+ export interface RuntimeLaunchProgress {
7
+ hasTurnStarted?: boolean;
8
+ hasToolUse?: boolean;
9
+ hasResult?: boolean;
10
+ }
11
+
12
+ export class RetryableRuntimeLaunchError extends Error {
13
+ runtimeId: AgentRuntimeId;
14
+ cause: unknown;
15
+
16
+ constructor(input: {
17
+ runtimeId: AgentRuntimeId;
18
+ message: string;
19
+ cause: unknown;
20
+ }) {
21
+ super(input.message);
22
+ this.name = "RetryableRuntimeLaunchError";
23
+ this.runtimeId = input.runtimeId;
24
+ this.cause = input.cause;
25
+ }
26
+ }
27
+
28
+ function isLikelyRuntimeUnavailableMessage(message: string): boolean {
29
+ const lower = message.toLowerCase();
30
+
31
+ return (
32
+ lower.includes("process exited with code") ||
33
+ lower.includes("command not found") ||
34
+ lower.includes("enoent") ||
35
+ lower.includes("not logged in") ||
36
+ lower.includes("authentication") ||
37
+ lower.includes("oauth") ||
38
+ lower.includes("token expired") ||
39
+ lower.includes("api key") ||
40
+ lower.includes("chatgpt sign-in is not configured") ||
41
+ lower.includes("failed to start") ||
42
+ lower.includes("runtime unavailable")
43
+ );
44
+ }
45
+
46
+ export function classifyTaskFailureReason(error: unknown): string {
47
+ if (!(error instanceof Error)) return "sdk_error";
48
+ if (error.name === "AbortError" || error.message.includes("aborted")) {
49
+ return "aborted";
50
+ }
51
+ const lower = error.message.toLowerCase();
52
+ if (
53
+ lower.includes("turn") &&
54
+ (lower.includes("limit") || lower.includes("exhausted") || lower.includes("max"))
55
+ ) {
56
+ return "turn_limit_exceeded";
57
+ }
58
+ if (lower.includes("timeout") || lower.includes("timed out")) return "timeout";
59
+ if (lower.includes("budget")) return "budget_exceeded";
60
+ if (
61
+ lower.includes("authentication") ||
62
+ lower.includes("oauth") ||
63
+ lower.includes("not logged in") ||
64
+ lower.includes("token expired")
65
+ ) {
66
+ return "auth_failed";
67
+ }
68
+ if (lower.includes("rate limit") || lower.includes("429")) {
69
+ return "rate_limited";
70
+ }
71
+ return "sdk_error";
72
+ }
73
+
74
+ export function toRetryableRuntimeLaunchError(input: {
75
+ runtimeId: AgentRuntimeId;
76
+ error: unknown;
77
+ progress: RuntimeLaunchProgress;
78
+ }): RetryableRuntimeLaunchError | null {
79
+ if (!(input.error instanceof Error)) {
80
+ return null;
81
+ }
82
+
83
+ if (
84
+ input.progress.hasTurnStarted ||
85
+ input.progress.hasToolUse ||
86
+ input.progress.hasResult
87
+ ) {
88
+ return null;
89
+ }
90
+
91
+ if (!isLikelyRuntimeUnavailableMessage(input.error.message)) {
92
+ return null;
93
+ }
94
+
95
+ const label = getRuntimeCatalogEntry(input.runtimeId).label;
96
+ return new RetryableRuntimeLaunchError({
97
+ runtimeId: input.runtimeId,
98
+ message: `${label} failed to launch before task execution started: ${input.error.message}`,
99
+ cause: input.error,
100
+ });
101
+ }