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,183 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import type { AgentRuntimeId } from "@/lib/agents/runtime/catalog";
3
+
4
+ const {
5
+ mockGetRuntimeSetupStates,
6
+ mockListConfiguredRuntimeIds,
7
+ mockGetRoutingPreference,
8
+ mockTestRuntimeConnection,
9
+ mockGetProfile,
10
+ mockProfileSupportsRuntime,
11
+ mockSuggestRuntime,
12
+ } = vi.hoisted(() => ({
13
+ mockGetRuntimeSetupStates: vi.fn(),
14
+ mockListConfiguredRuntimeIds: vi.fn(),
15
+ mockGetRoutingPreference: vi.fn(),
16
+ mockTestRuntimeConnection: vi.fn(),
17
+ mockGetProfile: vi.fn(),
18
+ mockProfileSupportsRuntime: vi.fn(),
19
+ mockSuggestRuntime: vi.fn(),
20
+ }));
21
+
22
+ vi.mock("@/lib/settings/runtime-setup", () => ({
23
+ getRuntimeSetupStates: mockGetRuntimeSetupStates,
24
+ listConfiguredRuntimeIds: mockListConfiguredRuntimeIds,
25
+ }));
26
+
27
+ vi.mock("@/lib/settings/routing", () => ({
28
+ getRoutingPreference: mockGetRoutingPreference,
29
+ }));
30
+
31
+ vi.mock("@/lib/agents/runtime/index", () => ({
32
+ testRuntimeConnection: mockTestRuntimeConnection,
33
+ }));
34
+
35
+ vi.mock("@/lib/agents/profiles/registry", () => ({
36
+ getProfile: mockGetProfile,
37
+ }));
38
+
39
+ vi.mock("@/lib/agents/profiles/compatibility", () => ({
40
+ profileSupportsRuntime: mockProfileSupportsRuntime,
41
+ }));
42
+
43
+ vi.mock("@/lib/agents/router", () => ({
44
+ suggestRuntime: mockSuggestRuntime,
45
+ }));
46
+
47
+ import {
48
+ RequestedModelUnavailableError,
49
+ resolveChatExecutionTarget,
50
+ resolveResumeExecutionTarget,
51
+ resolveTaskExecutionTarget,
52
+ } from "../execution-target";
53
+
54
+ function makeStates(configured: AgentRuntimeId[]) {
55
+ const all: AgentRuntimeId[] = [
56
+ "claude-code",
57
+ "openai-codex-app-server",
58
+ "anthropic-direct",
59
+ "openai-direct",
60
+ "ollama",
61
+ ];
62
+
63
+ return Object.fromEntries(
64
+ all.map((runtimeId) => [
65
+ runtimeId,
66
+ {
67
+ runtimeId,
68
+ configured: configured.includes(runtimeId),
69
+ },
70
+ ])
71
+ );
72
+ }
73
+
74
+ describe("execution target resolver", () => {
75
+ beforeEach(() => {
76
+ vi.clearAllMocks();
77
+ mockGetRuntimeSetupStates.mockResolvedValue(
78
+ makeStates(["claude-code", "openai-codex-app-server"])
79
+ );
80
+ mockListConfiguredRuntimeIds.mockReturnValue([
81
+ "claude-code",
82
+ "openai-codex-app-server",
83
+ ]);
84
+ mockGetRoutingPreference.mockResolvedValue("latency");
85
+ mockProfileSupportsRuntime.mockReturnValue(true);
86
+ mockSuggestRuntime.mockImplementation(
87
+ (
88
+ _title: string,
89
+ _description: string | undefined,
90
+ _profileId: string | undefined,
91
+ availableRuntimeIds: AgentRuntimeId[]
92
+ ) => ({
93
+ runtimeId: availableRuntimeIds[0],
94
+ reason: "test",
95
+ })
96
+ );
97
+ mockTestRuntimeConnection.mockImplementation((runtimeId: AgentRuntimeId) => {
98
+ if (runtimeId === "claude-code") {
99
+ return Promise.resolve({
100
+ connected: false,
101
+ error: "Claude Code process exited with code 1",
102
+ });
103
+ }
104
+ return Promise.resolve({ connected: true });
105
+ });
106
+ });
107
+
108
+ it("falls back from an unavailable requested task runtime to a compatible alternate", async () => {
109
+ mockGetProfile.mockReturnValue({
110
+ id: "upgrade-assistant",
111
+ allowedTools: ["Bash(git status)", "Read", "Write"],
112
+ });
113
+
114
+ const target = await resolveTaskExecutionTarget({
115
+ title: "Upgrade local branch",
116
+ description: "Merge upstream main safely",
117
+ requestedRuntimeId: "claude-code",
118
+ profileId: "upgrade-assistant",
119
+ });
120
+
121
+ expect(target.requestedRuntimeId).toBe("claude-code");
122
+ expect(target.effectiveRuntimeId).toBe("openai-codex-app-server");
123
+ expect(target.fallbackApplied).toBe(true);
124
+ expect(target.fallbackReason).toContain("Fell back to OpenAI Codex App Server");
125
+ });
126
+
127
+ it("auto-selects a healthy runtime when no task runtime was requested", async () => {
128
+ mockGetProfile.mockReturnValue({
129
+ id: "general",
130
+ allowedTools: [],
131
+ preferredRuntime: "anthropic-direct",
132
+ });
133
+ mockSuggestRuntime.mockReturnValue({
134
+ runtimeId: "openai-codex-app-server",
135
+ reason: "test",
136
+ });
137
+
138
+ const target = await resolveTaskExecutionTarget({
139
+ title: "Fix failing build",
140
+ description: "Debug and patch the repo",
141
+ profileId: "general",
142
+ });
143
+
144
+ expect(target.requestedRuntimeId).toBeNull();
145
+ expect(target.effectiveRuntimeId).toBe("openai-codex-app-server");
146
+ expect(target.fallbackApplied).toBe(false);
147
+ });
148
+
149
+ it("falls back chat turns to the mapped alternate model when the requested runtime is unavailable", async () => {
150
+ const target = await resolveChatExecutionTarget({
151
+ requestedRuntimeId: "claude-code",
152
+ requestedModelId: "sonnet",
153
+ });
154
+
155
+ expect(target.requestedRuntimeId).toBe("claude-code");
156
+ expect(target.effectiveRuntimeId).toBe("openai-codex-app-server");
157
+ expect(target.effectiveModelId).toBe("gpt-5.3-codex");
158
+ expect(target.fallbackApplied).toBe(true);
159
+ });
160
+
161
+ it("refuses resume when the last effective runtime is unavailable", async () => {
162
+ await expect(
163
+ resolveResumeExecutionTarget({
164
+ requestedRuntimeId: "claude-code",
165
+ effectiveRuntimeId: "claude-code",
166
+ })
167
+ ).rejects.toThrow("Claude Code process exited with code 1");
168
+ });
169
+
170
+ it("throws a named error when no chat runtime is healthy", async () => {
171
+ mockTestRuntimeConnection.mockResolvedValue({
172
+ connected: false,
173
+ error: "all down",
174
+ });
175
+
176
+ await expect(
177
+ resolveChatExecutionTarget({
178
+ requestedRuntimeId: "claude-code",
179
+ requestedModelId: "sonnet",
180
+ })
181
+ ).rejects.toBeInstanceOf(RequestedModelUnavailableError);
182
+ });
183
+ });
@@ -0,0 +1,118 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+
3
+ import {
4
+ extractPlanTypeFromIdToken,
5
+ readCodexAuthStateFromClient,
6
+ } from "@/lib/agents/runtime/openai-codex-auth";
7
+
8
+ describe("openai codex auth", () => {
9
+ it("keeps a ChatGPT session connected when rate limit decoding fails", async () => {
10
+ const client = {
11
+ request: vi.fn(async (method: string) => {
12
+ if (method === "account/read") {
13
+ return {
14
+ account: {
15
+ type: "chatgpt",
16
+ email: "sehgal.manav@gmail.com",
17
+ planType: "prolite",
18
+ },
19
+ requiresOpenaiAuth: false,
20
+ };
21
+ }
22
+
23
+ if (method === "account/rateLimits/read") {
24
+ throw new Error("unknown variant `prolite`");
25
+ }
26
+
27
+ throw new Error(`Unexpected method: ${method}`);
28
+ }),
29
+ };
30
+
31
+ const state = await readCodexAuthStateFromClient(client as never, {
32
+ refreshToken: true,
33
+ });
34
+
35
+ expect(state.connected).toBe(true);
36
+ expect(state.account).toEqual({
37
+ type: "chatgpt",
38
+ email: "sehgal.manav@gmail.com",
39
+ planType: "prolite",
40
+ });
41
+ expect(state.rateLimits).toBeNull();
42
+ expect(state.authMode).toBe("chatgpt");
43
+ });
44
+
45
+ it("recovers the plan type from the rate limit error payload when account/read omits it", async () => {
46
+ const client = {
47
+ request: vi.fn(async (method: string) => {
48
+ if (method === "account/read") {
49
+ return {
50
+ account: {
51
+ type: "chatgpt",
52
+ email: "sehgal.manav@gmail.com",
53
+ planType: null,
54
+ },
55
+ requiresOpenaiAuth: false,
56
+ };
57
+ }
58
+
59
+ if (method === "account/rateLimits/read") {
60
+ throw new Error('Decode error: body={ "plan_type": "prolite" }');
61
+ }
62
+
63
+ throw new Error(`Unexpected method: ${method}`);
64
+ }),
65
+ };
66
+
67
+ const state = await readCodexAuthStateFromClient(client as never);
68
+
69
+ expect(state.connected).toBe(true);
70
+ expect(state.account?.planType).toBe("prolite");
71
+ expect(state.rateLimits).toBeNull();
72
+ });
73
+
74
+ it("treats account/read planType=unknown as missing and recovers the upstream plan", async () => {
75
+ const client = {
76
+ request: vi.fn(async (method: string) => {
77
+ if (method === "account/read") {
78
+ return {
79
+ account: {
80
+ type: "chatgpt",
81
+ email: "sehgal.manav@gmail.com",
82
+ planType: "unknown",
83
+ },
84
+ requiresOpenaiAuth: false,
85
+ };
86
+ }
87
+
88
+ if (method === "account/rateLimits/read") {
89
+ throw new Error('Decode error: body={ "plan_type": "prolite" }');
90
+ }
91
+
92
+ throw new Error(`Unexpected method: ${method}`);
93
+ }),
94
+ };
95
+
96
+ const state = await readCodexAuthStateFromClient(client as never);
97
+
98
+ expect(state.connected).toBe(true);
99
+ expect(state.account?.planType).toBe("prolite");
100
+ expect(state.rateLimits).toBeNull();
101
+ });
102
+
103
+ it("extracts the plan type from the stored id token payload", async () => {
104
+ const payload = Buffer.from(
105
+ JSON.stringify({
106
+ "https://api.openai.com/auth": {
107
+ chatgpt_plan_type: "prolite",
108
+ },
109
+ })
110
+ )
111
+ .toString("base64")
112
+ .replace(/\+/g, "-")
113
+ .replace(/\//g, "_")
114
+ .replace(/=+$/g, "");
115
+
116
+ expect(extractPlanTypeFromIdToken(`header.${payload}.signature`)).toBe("prolite");
117
+ });
118
+ });
@@ -481,6 +481,14 @@ async function executeAnthropicDirectTask(taskId: string, isResume = false): Pro
481
481
  startedAt: usageState.startedAt,
482
482
  finishedAt: new Date(),
483
483
  });
484
+
485
+ await db
486
+ .update(tasks)
487
+ .set({
488
+ effectiveModelId: result.totalUsage.modelId ?? modelId,
489
+ updatedAt: new Date(),
490
+ })
491
+ .where(eq(tasks.id, taskId));
484
492
  } catch (err) {
485
493
  if (!abortController.signal.aborted) {
486
494
  const errorMsg = err instanceof Error ? err.message : String(err);
@@ -21,6 +21,51 @@ export interface RuntimeCapabilities {
21
21
  authHealthCheck: boolean;
22
22
  }
23
23
 
24
+ /**
25
+ * LLM-surface features that affect what the model sees and which tools/skills
26
+ * Stagent exposes to it. Distinct from RuntimeCapabilities above, which is
27
+ * adapter-plumbing concerns (can the adapter resume/cancel/etc.).
28
+ *
29
+ * Values reflect post-Phase-1 capability (what the runtime SDK *can* do),
30
+ * not current engagement (what `engine.ts` currently activates). Downstream
31
+ * features read this bag to decide rendering, filtering, and dispatch.
32
+ */
33
+ export interface RuntimeFeatures {
34
+ /** SDK provides a native skill-invocation tool (e.g. Claude SDK `Skill` tool). */
35
+ hasNativeSkills: boolean;
36
+ /** SDK loads skill metadata first, full SKILL.md on demand. */
37
+ hasProgressiveDisclosure: boolean;
38
+ /** Read/Grep/Glob/Edit/Write available as LLM tools. */
39
+ hasFilesystemTools: boolean;
40
+ /** Bash tool available (Stagent gates via permission bridge). */
41
+ hasBash: boolean;
42
+ /** TodoWrite tool available. */
43
+ hasTodoWrite: boolean;
44
+ /** Runtime supports delegating to sub-agents (e.g. Task tool). */
45
+ hasSubagentDelegation: boolean;
46
+ /** Runtime loads filesystem hooks (pre/post tool-use shell scripts). */
47
+ hasHooks: boolean;
48
+ /** Which project-level instructions file the runtime auto-loads, if any. */
49
+ autoLoadsInstructions: "CLAUDE.md" | "AGENTS.md" | null;
50
+ /**
51
+ * Runtime has no native skill support — Stagent must inject SKILL.md content
52
+ * into the system prompt to expose skills to the LLM.
53
+ */
54
+ stagentInjectsSkills: boolean;
55
+ /**
56
+ * Runtime supports composing multiple active skills in one conversation.
57
+ * When false, only one skill may be active at a time (Ollama: context
58
+ * budget too tight). When true, `activate_skill mode:"add"` is allowed
59
+ * up to `maxActiveSkills`.
60
+ */
61
+ supportsSkillComposition: boolean;
62
+ /**
63
+ * Maximum number of skills that may be simultaneously active. Enforced
64
+ * by the activate_skill tool. Ignored when supportsSkillComposition=false.
65
+ */
66
+ maxActiveSkills: number;
67
+ }
68
+
24
69
  export interface RuntimeModelConfig {
25
70
  /** Default model ID for this runtime */
26
71
  default: string;
@@ -34,6 +79,7 @@ export interface RuntimeCatalogEntry {
34
79
  description: string;
35
80
  providerId: "anthropic" | "openai" | "ollama";
36
81
  capabilities: RuntimeCapabilities;
82
+ features: RuntimeFeatures;
37
83
  /** Model catalog — default and supported model IDs for this runtime */
38
84
  models: RuntimeModelConfig;
39
85
  }
@@ -54,6 +100,19 @@ const RUNTIME_CATALOG: Record<AgentRuntimeId, RuntimeCatalogEntry> = {
54
100
  profileAssist: true,
55
101
  authHealthCheck: true,
56
102
  },
103
+ features: {
104
+ hasNativeSkills: true,
105
+ hasProgressiveDisclosure: true,
106
+ hasFilesystemTools: true,
107
+ hasBash: true,
108
+ hasTodoWrite: true,
109
+ hasSubagentDelegation: false, // Stagent task primitives replace SDK Task tool
110
+ hasHooks: false, // excluded per Q2
111
+ autoLoadsInstructions: "CLAUDE.md",
112
+ stagentInjectsSkills: false,
113
+ supportsSkillComposition: true,
114
+ maxActiveSkills: 3,
115
+ },
57
116
  models: {
58
117
  default: "sonnet",
59
118
  supported: ["haiku", "sonnet", "opus"],
@@ -74,6 +133,19 @@ const RUNTIME_CATALOG: Record<AgentRuntimeId, RuntimeCatalogEntry> = {
74
133
  profileAssist: false,
75
134
  authHealthCheck: true,
76
135
  },
136
+ features: {
137
+ hasNativeSkills: true,
138
+ hasProgressiveDisclosure: true,
139
+ hasFilesystemTools: true,
140
+ hasBash: true,
141
+ hasTodoWrite: true,
142
+ hasSubagentDelegation: false,
143
+ hasHooks: false,
144
+ autoLoadsInstructions: "AGENTS.md",
145
+ stagentInjectsSkills: false,
146
+ supportsSkillComposition: true,
147
+ maxActiveSkills: 3,
148
+ },
77
149
  models: {
78
150
  default: "gpt-5.4",
79
151
  supported: ["gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex"],
@@ -94,6 +166,21 @@ const RUNTIME_CATALOG: Record<AgentRuntimeId, RuntimeCatalogEntry> = {
94
166
  profileAssist: true,
95
167
  authHealthCheck: true,
96
168
  },
169
+ features: {
170
+ // Direct Messages API — no SDK-native skill machinery.
171
+ // Revisit when chat-claude-sdk-skills designs direct-API skill injection.
172
+ hasNativeSkills: false,
173
+ hasProgressiveDisclosure: false,
174
+ hasFilesystemTools: false,
175
+ hasBash: false,
176
+ hasTodoWrite: false,
177
+ hasSubagentDelegation: false,
178
+ hasHooks: false,
179
+ autoLoadsInstructions: null,
180
+ stagentInjectsSkills: false,
181
+ supportsSkillComposition: true,
182
+ maxActiveSkills: 3,
183
+ },
97
184
  models: {
98
185
  default: "claude-sonnet-4-20250514",
99
186
  supported: ["claude-haiku-4-5-20251001", "claude-sonnet-4-20250514", "claude-opus-4-20250514"],
@@ -114,6 +201,21 @@ const RUNTIME_CATALOG: Record<AgentRuntimeId, RuntimeCatalogEntry> = {
114
201
  profileAssist: false,
115
202
  authHealthCheck: true,
116
203
  },
204
+ features: {
205
+ // Direct Responses API — no SDK-native skill machinery.
206
+ // Revisit when chat-claude-sdk-skills designs direct-API skill injection.
207
+ hasNativeSkills: false,
208
+ hasProgressiveDisclosure: false,
209
+ hasFilesystemTools: false,
210
+ hasBash: false,
211
+ hasTodoWrite: false,
212
+ hasSubagentDelegation: false,
213
+ hasHooks: false,
214
+ autoLoadsInstructions: null,
215
+ stagentInjectsSkills: false,
216
+ supportsSkillComposition: true,
217
+ maxActiveSkills: 3,
218
+ },
117
219
  models: {
118
220
  default: "gpt-4.1",
119
221
  supported: ["gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano"],
@@ -134,6 +236,19 @@ const RUNTIME_CATALOG: Record<AgentRuntimeId, RuntimeCatalogEntry> = {
134
236
  profileAssist: false,
135
237
  authHealthCheck: true,
136
238
  },
239
+ features: {
240
+ hasNativeSkills: false,
241
+ hasProgressiveDisclosure: false,
242
+ hasFilesystemTools: false,
243
+ hasBash: false,
244
+ hasTodoWrite: false, // Stagent MCP exposes todo tools separately
245
+ hasSubagentDelegation: false,
246
+ hasHooks: false,
247
+ autoLoadsInstructions: null,
248
+ stagentInjectsSkills: true,
249
+ supportsSkillComposition: false,
250
+ maxActiveSkills: 1,
251
+ },
137
252
  models: {
138
253
  default: "llama3",
139
254
  supported: [], // Dynamic — populated from Ollama API at runtime
@@ -157,6 +272,12 @@ export function getRuntimeCapabilities(
157
272
  return getRuntimeCatalogEntry(runtimeId).capabilities;
158
273
  }
159
274
 
275
+ export function getRuntimeFeatures(
276
+ runtimeId: AgentRuntimeId = DEFAULT_AGENT_RUNTIME
277
+ ): RuntimeFeatures {
278
+ return getRuntimeCatalogEntry(runtimeId).features;
279
+ }
280
+
160
281
  export function resolveAgentRuntime(runtimeId?: string | null): AgentRuntimeId {
161
282
  if (!runtimeId) return DEFAULT_AGENT_RUNTIME;
162
283
  if (isAgentRuntimeId(runtimeId)) return runtimeId;
@@ -1,3 +1,35 @@
1
+ // ─── Claude Agent SDK options shared by chat and task runtimes ──────
2
+ //
3
+ // Chat (src/lib/chat/engine.ts) and task (src/lib/agents/claude-agent.ts)
4
+ // both construct query() options for the `claude-code` runtime. These
5
+ // constants are the single source of truth so the two code paths cannot
6
+ // drift — a drift that would manifest as "skills work in chat but vanish
7
+ // in tasks on the same project." See features/task-runtime-skill-parity.md
8
+ // and features/chat-claude-sdk-skills.md.
9
+
10
+ export const CLAUDE_SDK_SETTING_SOURCES = ["user", "project"] as const;
11
+
12
+ export const CLAUDE_SDK_ALLOWED_TOOLS = [
13
+ "Skill",
14
+ "Read",
15
+ "Grep",
16
+ "Glob",
17
+ "Edit",
18
+ "Write",
19
+ "Bash",
20
+ "TodoWrite",
21
+ ] as const;
22
+
23
+ /**
24
+ * Filesystem tools safe to auto-allow without a permission prompt.
25
+ * Mirrors the existing browser/exa read-only auto-allow pattern.
26
+ */
27
+ export const CLAUDE_SDK_READ_ONLY_FS_TOOLS = new Set<string>([
28
+ "Read",
29
+ "Grep",
30
+ "Glob",
31
+ ]);
32
+
1
33
  /**
2
34
  * Build the environment for the Claude Agent SDK subprocess.
3
35
  *
@@ -24,7 +24,7 @@ interface JsonRpcNotification {
24
24
  type JsonRpcMessage = JsonRpcRequest | JsonRpcResponse | JsonRpcNotification;
25
25
 
26
26
  export interface CodexAppServerClientOptions {
27
- env?: Record<string, string>;
27
+ env?: Record<string, string | undefined>;
28
28
  cwd?: string;
29
29
  }
30
30
 
@@ -59,15 +59,21 @@ export class CodexAppServerClient {
59
59
  ): Promise<CodexAppServerClient> {
60
60
  const port = await reservePort();
61
61
  const listenUrl = `ws://127.0.0.1:${port}`;
62
+ const env: NodeJS.ProcessEnv = { ...process.env };
63
+ for (const [key, value] of Object.entries(options.env ?? {})) {
64
+ if (value === undefined) {
65
+ delete env[key];
66
+ } else {
67
+ env[key] = value;
68
+ }
69
+ }
70
+
62
71
  const child = spawn(
63
72
  "codex",
64
73
  ["app-server", "--listen", listenUrl],
65
74
  {
66
75
  cwd: options.cwd,
67
- env: {
68
- ...process.env,
69
- ...(options.env ?? {}),
70
- },
76
+ env,
71
77
  stdio: ["ignore", "pipe", "pipe"],
72
78
  }
73
79
  );