stagent 0.1.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 (333) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +399 -0
  3. package/components.json +21 -0
  4. package/dist/cli.js +171 -0
  5. package/drizzle.config.ts +12 -0
  6. package/next.config.mjs +15 -0
  7. package/package.json +114 -0
  8. package/postcss.config.mjs +8 -0
  9. package/public/icon-512.png +0 -0
  10. package/public/icon.svg +13 -0
  11. package/public/readme/home-workspace.png +0 -0
  12. package/public/readme/inbox-approvals.png +0 -0
  13. package/public/readme/workflow-blueprints.png +0 -0
  14. package/public/stagent-s-128.png +0 -0
  15. package/public/stagent-s-64.png +0 -0
  16. package/src/app/api/blueprints/[id]/instantiate/route.ts +27 -0
  17. package/src/app/api/blueprints/[id]/route.ts +39 -0
  18. package/src/app/api/blueprints/import/route.ts +68 -0
  19. package/src/app/api/blueprints/route.ts +29 -0
  20. package/src/app/api/command-palette/recent/route.ts +31 -0
  21. package/src/app/api/data/clear/route.ts +22 -0
  22. package/src/app/api/data/seed/route.ts +22 -0
  23. package/src/app/api/documents/[id]/file/route.ts +44 -0
  24. package/src/app/api/documents/[id]/route.ts +123 -0
  25. package/src/app/api/documents/route.ts +59 -0
  26. package/src/app/api/logs/stream/route.ts +101 -0
  27. package/src/app/api/notifications/[id]/route.ts +36 -0
  28. package/src/app/api/notifications/mark-all-read/route.ts +13 -0
  29. package/src/app/api/notifications/pending-approvals/route.ts +10 -0
  30. package/src/app/api/notifications/pending-approvals/stream/route.ts +101 -0
  31. package/src/app/api/notifications/route.ts +34 -0
  32. package/src/app/api/permissions/route.ts +46 -0
  33. package/src/app/api/profiles/[id]/route.ts +79 -0
  34. package/src/app/api/profiles/[id]/test/route.ts +42 -0
  35. package/src/app/api/profiles/import/route.ts +108 -0
  36. package/src/app/api/profiles/route.ts +50 -0
  37. package/src/app/api/projects/[id]/route.ts +72 -0
  38. package/src/app/api/projects/route.ts +53 -0
  39. package/src/app/api/schedules/[id]/route.ts +185 -0
  40. package/src/app/api/schedules/route.ts +117 -0
  41. package/src/app/api/settings/budgets/route.ts +24 -0
  42. package/src/app/api/settings/openai/route.ts +24 -0
  43. package/src/app/api/settings/route.ts +21 -0
  44. package/src/app/api/settings/test/route.ts +26 -0
  45. package/src/app/api/tasks/[id]/cancel/route.ts +21 -0
  46. package/src/app/api/tasks/[id]/execute/route.ts +90 -0
  47. package/src/app/api/tasks/[id]/logs/route.ts +95 -0
  48. package/src/app/api/tasks/[id]/output/route.ts +47 -0
  49. package/src/app/api/tasks/[id]/respond/route.ts +64 -0
  50. package/src/app/api/tasks/[id]/resume/route.ts +76 -0
  51. package/src/app/api/tasks/[id]/route.ts +77 -0
  52. package/src/app/api/tasks/assist/route.ts +35 -0
  53. package/src/app/api/tasks/route.ts +82 -0
  54. package/src/app/api/uploads/[id]/route.ts +81 -0
  55. package/src/app/api/uploads/cleanup/route.ts +7 -0
  56. package/src/app/api/uploads/route.ts +66 -0
  57. package/src/app/api/workflows/[id]/execute/route.ts +82 -0
  58. package/src/app/api/workflows/[id]/route.ts +133 -0
  59. package/src/app/api/workflows/[id]/status/route.ts +54 -0
  60. package/src/app/api/workflows/[id]/steps/[stepId]/retry/route.ts +22 -0
  61. package/src/app/api/workflows/route.ts +61 -0
  62. package/src/app/apple-icon.tsx +31 -0
  63. package/src/app/costs/page.tsx +256 -0
  64. package/src/app/dashboard/page.tsx +44 -0
  65. package/src/app/documents/[id]/page.tsx +46 -0
  66. package/src/app/documents/page.tsx +45 -0
  67. package/src/app/error.tsx +26 -0
  68. package/src/app/global-error.tsx +23 -0
  69. package/src/app/globals.css +733 -0
  70. package/src/app/icon.tsx +30 -0
  71. package/src/app/inbox/loading.tsx +15 -0
  72. package/src/app/inbox/page.tsx +35 -0
  73. package/src/app/layout.tsx +78 -0
  74. package/src/app/manifest.ts +32 -0
  75. package/src/app/monitor/page.tsx +37 -0
  76. package/src/app/page.tsx +162 -0
  77. package/src/app/profiles/[id]/edit/page.tsx +39 -0
  78. package/src/app/profiles/[id]/page.tsx +33 -0
  79. package/src/app/profiles/new/page.tsx +22 -0
  80. package/src/app/profiles/page.tsx +19 -0
  81. package/src/app/projects/[id]/page.tsx +134 -0
  82. package/src/app/projects/loading.tsx +17 -0
  83. package/src/app/projects/page.tsx +32 -0
  84. package/src/app/schedules/[id]/page.tsx +47 -0
  85. package/src/app/schedules/page.tsx +18 -0
  86. package/src/app/settings/loading.tsx +24 -0
  87. package/src/app/settings/page.tsx +27 -0
  88. package/src/app/tasks/[id]/page.tsx +45 -0
  89. package/src/app/tasks/new/page.tsx +27 -0
  90. package/src/app/workflows/[id]/edit/page.tsx +66 -0
  91. package/src/app/workflows/[id]/page.tsx +37 -0
  92. package/src/app/workflows/blueprints/[id]/page.tsx +40 -0
  93. package/src/app/workflows/blueprints/new/page.tsx +20 -0
  94. package/src/app/workflows/blueprints/page.tsx +11 -0
  95. package/src/app/workflows/new/page.tsx +36 -0
  96. package/src/app/workflows/page.tsx +18 -0
  97. package/src/components/charts/donut-ring.tsx +64 -0
  98. package/src/components/charts/mini-bar.tsx +75 -0
  99. package/src/components/charts/sparkline.tsx +107 -0
  100. package/src/components/costs/cost-dashboard.tsx +877 -0
  101. package/src/components/costs/cost-filters.tsx +179 -0
  102. package/src/components/dashboard/activity-feed.tsx +95 -0
  103. package/src/components/dashboard/greeting.tsx +30 -0
  104. package/src/components/dashboard/priority-queue.tsx +79 -0
  105. package/src/components/dashboard/quick-actions.tsx +62 -0
  106. package/src/components/dashboard/recent-projects.tsx +79 -0
  107. package/src/components/dashboard/stats-cards.tsx +114 -0
  108. package/src/components/documents/document-browser.tsx +235 -0
  109. package/src/components/documents/document-detail-view.tsx +367 -0
  110. package/src/components/documents/document-grid.tsx +78 -0
  111. package/src/components/documents/document-preview.tsx +68 -0
  112. package/src/components/documents/document-table.tsx +119 -0
  113. package/src/components/documents/document-upload-dialog.tsx +153 -0
  114. package/src/components/documents/types.ts +6 -0
  115. package/src/components/documents/utils.ts +57 -0
  116. package/src/components/monitoring/connection-indicator.tsx +14 -0
  117. package/src/components/monitoring/log-entry.tsx +79 -0
  118. package/src/components/monitoring/log-filters.tsx +57 -0
  119. package/src/components/monitoring/log-stream.tsx +144 -0
  120. package/src/components/monitoring/monitor-overview-wrapper.tsx +64 -0
  121. package/src/components/monitoring/monitor-overview.tsx +119 -0
  122. package/src/components/notifications/failure-action.tsx +38 -0
  123. package/src/components/notifications/inbox-list.tsx +165 -0
  124. package/src/components/notifications/message-response.tsx +196 -0
  125. package/src/components/notifications/notification-item.tsx +250 -0
  126. package/src/components/notifications/pending-approval-host.tsx +478 -0
  127. package/src/components/notifications/permission-action.tsx +37 -0
  128. package/src/components/notifications/permission-response-actions.tsx +126 -0
  129. package/src/components/notifications/unread-badge.tsx +35 -0
  130. package/src/components/profiles/profile-browser.tsx +117 -0
  131. package/src/components/profiles/profile-card.tsx +78 -0
  132. package/src/components/profiles/profile-detail-view.tsx +564 -0
  133. package/src/components/profiles/profile-form-view.tsx +480 -0
  134. package/src/components/profiles/profile-import-dialog.tsx +113 -0
  135. package/src/components/projects/project-card.tsx +58 -0
  136. package/src/components/projects/project-create-dialog.tsx +140 -0
  137. package/src/components/projects/project-detail.tsx +68 -0
  138. package/src/components/projects/project-edit-dialog.tsx +219 -0
  139. package/src/components/projects/project-list.tsx +108 -0
  140. package/src/components/schedules/schedule-create-dialog.tsx +403 -0
  141. package/src/components/schedules/schedule-detail-view.tsx +274 -0
  142. package/src/components/schedules/schedule-list.tsx +242 -0
  143. package/src/components/schedules/schedule-status-badge.tsx +16 -0
  144. package/src/components/settings/api-key-form.tsx +141 -0
  145. package/src/components/settings/auth-config-section.tsx +141 -0
  146. package/src/components/settings/auth-method-selector.tsx +67 -0
  147. package/src/components/settings/auth-status-badge.tsx +40 -0
  148. package/src/components/settings/auth-status-dot.tsx +59 -0
  149. package/src/components/settings/budget-guardrails-section.tsx +842 -0
  150. package/src/components/settings/data-management-section.tsx +141 -0
  151. package/src/components/settings/openai-runtime-section.tsx +104 -0
  152. package/src/components/settings/permissions-section.tsx +91 -0
  153. package/src/components/shared/app-sidebar.tsx +123 -0
  154. package/src/components/shared/card-skeleton.tsx +42 -0
  155. package/src/components/shared/command-palette.tsx +250 -0
  156. package/src/components/shared/confirm-dialog.tsx +52 -0
  157. package/src/components/shared/empty-state.tsx +24 -0
  158. package/src/components/shared/error-state.tsx +32 -0
  159. package/src/components/shared/form-section-card.tsx +33 -0
  160. package/src/components/shared/section-heading.tsx +14 -0
  161. package/src/components/shared/stagent-logo.tsx +21 -0
  162. package/src/components/shared/theme-toggle.tsx +46 -0
  163. package/src/components/tasks/ai-assist-panel.tsx +210 -0
  164. package/src/components/tasks/content-preview.tsx +89 -0
  165. package/src/components/tasks/empty-board.tsx +12 -0
  166. package/src/components/tasks/file-upload.tsx +120 -0
  167. package/src/components/tasks/kanban-board.tsx +275 -0
  168. package/src/components/tasks/kanban-column.tsx +75 -0
  169. package/src/components/tasks/skeleton-board.tsx +21 -0
  170. package/src/components/tasks/task-attachments.tsx +114 -0
  171. package/src/components/tasks/task-card.tsx +101 -0
  172. package/src/components/tasks/task-create-panel.tsx +360 -0
  173. package/src/components/tasks/task-detail-view.tsx +356 -0
  174. package/src/components/ui/alert-dialog.tsx +196 -0
  175. package/src/components/ui/badge.tsx +50 -0
  176. package/src/components/ui/button.tsx +71 -0
  177. package/src/components/ui/card.tsx +92 -0
  178. package/src/components/ui/checkbox.tsx +32 -0
  179. package/src/components/ui/command.tsx +184 -0
  180. package/src/components/ui/dialog.tsx +158 -0
  181. package/src/components/ui/dropdown-menu.tsx +257 -0
  182. package/src/components/ui/form.tsx +167 -0
  183. package/src/components/ui/input.tsx +21 -0
  184. package/src/components/ui/label.tsx +24 -0
  185. package/src/components/ui/popover.tsx +89 -0
  186. package/src/components/ui/progress.tsx +31 -0
  187. package/src/components/ui/radio-group.tsx +45 -0
  188. package/src/components/ui/scroll-area.tsx +58 -0
  189. package/src/components/ui/select.tsx +190 -0
  190. package/src/components/ui/separator.tsx +28 -0
  191. package/src/components/ui/sheet.tsx +143 -0
  192. package/src/components/ui/sidebar.tsx +726 -0
  193. package/src/components/ui/skeleton.tsx +13 -0
  194. package/src/components/ui/slider.tsx +63 -0
  195. package/src/components/ui/sonner.tsx +36 -0
  196. package/src/components/ui/switch.tsx +35 -0
  197. package/src/components/ui/table.tsx +116 -0
  198. package/src/components/ui/tabs.tsx +91 -0
  199. package/src/components/ui/textarea.tsx +18 -0
  200. package/src/components/ui/tooltip.tsx +57 -0
  201. package/src/components/workflows/blueprint-editor.tsx +109 -0
  202. package/src/components/workflows/blueprint-gallery.tsx +155 -0
  203. package/src/components/workflows/blueprint-preview.tsx +240 -0
  204. package/src/components/workflows/loop-status-view.tsx +272 -0
  205. package/src/components/workflows/swarm-dashboard.tsx +185 -0
  206. package/src/components/workflows/workflow-form-view.tsx +1376 -0
  207. package/src/components/workflows/workflow-list.tsx +230 -0
  208. package/src/components/workflows/workflow-status-view.tsx +477 -0
  209. package/src/hooks/use-mobile.ts +19 -0
  210. package/src/instrumentation.ts +7 -0
  211. package/src/lib/agents/claude-agent.ts +737 -0
  212. package/src/lib/agents/execution-manager.ts +27 -0
  213. package/src/lib/agents/profiles/assignment-validation.ts +75 -0
  214. package/src/lib/agents/profiles/builtins/code-reviewer/SKILL.md +21 -0
  215. package/src/lib/agents/profiles/builtins/code-reviewer/profile.yaml +28 -0
  216. package/src/lib/agents/profiles/builtins/data-analyst/SKILL.md +25 -0
  217. package/src/lib/agents/profiles/builtins/data-analyst/profile.yaml +27 -0
  218. package/src/lib/agents/profiles/builtins/devops-engineer/SKILL.md +34 -0
  219. package/src/lib/agents/profiles/builtins/devops-engineer/profile.yaml +27 -0
  220. package/src/lib/agents/profiles/builtins/document-writer/SKILL.md +16 -0
  221. package/src/lib/agents/profiles/builtins/document-writer/profile.yaml +27 -0
  222. package/src/lib/agents/profiles/builtins/general/SKILL.md +13 -0
  223. package/src/lib/agents/profiles/builtins/general/profile.yaml +18 -0
  224. package/src/lib/agents/profiles/builtins/health-fitness-coach/SKILL.md +34 -0
  225. package/src/lib/agents/profiles/builtins/health-fitness-coach/profile.yaml +26 -0
  226. package/src/lib/agents/profiles/builtins/learning-coach/SKILL.md +35 -0
  227. package/src/lib/agents/profiles/builtins/learning-coach/profile.yaml +26 -0
  228. package/src/lib/agents/profiles/builtins/project-manager/SKILL.md +26 -0
  229. package/src/lib/agents/profiles/builtins/project-manager/profile.yaml +26 -0
  230. package/src/lib/agents/profiles/builtins/researcher/SKILL.md +15 -0
  231. package/src/lib/agents/profiles/builtins/researcher/profile.yaml +27 -0
  232. package/src/lib/agents/profiles/builtins/shopping-assistant/SKILL.md +34 -0
  233. package/src/lib/agents/profiles/builtins/shopping-assistant/profile.yaml +26 -0
  234. package/src/lib/agents/profiles/builtins/technical-writer/SKILL.md +31 -0
  235. package/src/lib/agents/profiles/builtins/technical-writer/profile.yaml +29 -0
  236. package/src/lib/agents/profiles/builtins/travel-planner/SKILL.md +23 -0
  237. package/src/lib/agents/profiles/builtins/travel-planner/profile.yaml +26 -0
  238. package/src/lib/agents/profiles/builtins/wealth-manager/SKILL.md +24 -0
  239. package/src/lib/agents/profiles/builtins/wealth-manager/profile.yaml +26 -0
  240. package/src/lib/agents/profiles/compatibility.ts +109 -0
  241. package/src/lib/agents/profiles/registry.ts +293 -0
  242. package/src/lib/agents/profiles/test-runner.ts +18 -0
  243. package/src/lib/agents/profiles/test-types.ts +20 -0
  244. package/src/lib/agents/profiles/types.ts +43 -0
  245. package/src/lib/agents/router.ts +56 -0
  246. package/src/lib/agents/runtime/catalog.ts +85 -0
  247. package/src/lib/agents/runtime/claude-sdk.ts +12 -0
  248. package/src/lib/agents/runtime/claude.ts +370 -0
  249. package/src/lib/agents/runtime/codex-app-server-client.ts +289 -0
  250. package/src/lib/agents/runtime/index.ts +167 -0
  251. package/src/lib/agents/runtime/openai-codex.ts +1089 -0
  252. package/src/lib/agents/runtime/task-assist-types.ts +8 -0
  253. package/src/lib/agents/runtime/types.ts +30 -0
  254. package/src/lib/constants/settings.ts +13 -0
  255. package/src/lib/constants/status-colors.ts +44 -0
  256. package/src/lib/constants/task-status.ts +49 -0
  257. package/src/lib/data/clear.ts +63 -0
  258. package/src/lib/data/seed-data/documents.ts +715 -0
  259. package/src/lib/data/seed-data/logs.ts +195 -0
  260. package/src/lib/data/seed-data/notifications.ts +141 -0
  261. package/src/lib/data/seed-data/profiles.ts +175 -0
  262. package/src/lib/data/seed-data/projects.ts +61 -0
  263. package/src/lib/data/seed-data/schedules.ts +108 -0
  264. package/src/lib/data/seed-data/tasks.ts +341 -0
  265. package/src/lib/data/seed-data/usage-ledger.ts +130 -0
  266. package/src/lib/data/seed-data/workflows.ts +213 -0
  267. package/src/lib/data/seed.ts +129 -0
  268. package/src/lib/db/index.ts +221 -0
  269. package/src/lib/db/migrations/0000_aromatic_gargoyle.sql +59 -0
  270. package/src/lib/db/migrations/0001_first_iron_patriot.sql +6 -0
  271. package/src/lib/db/migrations/0002_add_resume_count.sql +1 -0
  272. package/src/lib/db/migrations/0003_add_settings.sql +5 -0
  273. package/src/lib/db/migrations/0004_add_documents.sql +20 -0
  274. package/src/lib/db/migrations/0005_add_document_preprocessing.sql +4 -0
  275. package/src/lib/db/migrations/0006_add_agent_profile.sql +2 -0
  276. package/src/lib/db/migrations/0007_add_usage_metering_ledger.sql +30 -0
  277. package/src/lib/db/migrations/0008_add_document_version.sql +1 -0
  278. package/src/lib/db/migrations/meta/0000_snapshot.json +416 -0
  279. package/src/lib/db/migrations/meta/0001_snapshot.json +461 -0
  280. package/src/lib/db/migrations/meta/0002_snapshot.json +469 -0
  281. package/src/lib/db/migrations/meta/_journal.json +27 -0
  282. package/src/lib/db/schema.ts +227 -0
  283. package/src/lib/documents/cleanup.ts +50 -0
  284. package/src/lib/documents/context-builder.ts +75 -0
  285. package/src/lib/documents/output-scanner.ts +166 -0
  286. package/src/lib/documents/processor.ts +120 -0
  287. package/src/lib/documents/processors/image.ts +21 -0
  288. package/src/lib/documents/processors/office.ts +36 -0
  289. package/src/lib/documents/processors/pdf.ts +12 -0
  290. package/src/lib/documents/processors/spreadsheet.ts +18 -0
  291. package/src/lib/documents/processors/text.ts +8 -0
  292. package/src/lib/documents/registry.ts +25 -0
  293. package/src/lib/notifications/actionable.ts +108 -0
  294. package/src/lib/notifications/permissions.ts +169 -0
  295. package/src/lib/queries/chart-data.ts +184 -0
  296. package/src/lib/schedules/interval-parser.ts +110 -0
  297. package/src/lib/schedules/scheduler.ts +220 -0
  298. package/src/lib/settings/auth.ts +98 -0
  299. package/src/lib/settings/budget-guardrails.ts +590 -0
  300. package/src/lib/settings/helpers.ts +23 -0
  301. package/src/lib/settings/openai-auth.ts +80 -0
  302. package/src/lib/settings/permissions.ts +102 -0
  303. package/src/lib/usage/ledger.ts +489 -0
  304. package/src/lib/usage/pricing.ts +68 -0
  305. package/src/lib/utils/crypto.ts +90 -0
  306. package/src/lib/utils/format-timestamp.ts +46 -0
  307. package/src/lib/utils/session-cleanup.ts +26 -0
  308. package/src/lib/utils/stagent-paths.ts +18 -0
  309. package/src/lib/utils.ts +6 -0
  310. package/src/lib/validators/blueprint.ts +43 -0
  311. package/src/lib/validators/profile.ts +64 -0
  312. package/src/lib/validators/project.ts +17 -0
  313. package/src/lib/validators/settings.ts +57 -0
  314. package/src/lib/validators/task.ts +30 -0
  315. package/src/lib/workflows/blueprints/builtins/code-review-pipeline.yaml +72 -0
  316. package/src/lib/workflows/blueprints/builtins/documentation-generation.yaml +62 -0
  317. package/src/lib/workflows/blueprints/builtins/investment-research.yaml +81 -0
  318. package/src/lib/workflows/blueprints/builtins/meal-planning.yaml +73 -0
  319. package/src/lib/workflows/blueprints/builtins/product-research.yaml +72 -0
  320. package/src/lib/workflows/blueprints/builtins/research-report.yaml +77 -0
  321. package/src/lib/workflows/blueprints/builtins/sprint-planning.yaml +77 -0
  322. package/src/lib/workflows/blueprints/builtins/travel-planning.yaml +80 -0
  323. package/src/lib/workflows/blueprints/instantiator.ts +131 -0
  324. package/src/lib/workflows/blueprints/registry.ts +128 -0
  325. package/src/lib/workflows/blueprints/template.ts +58 -0
  326. package/src/lib/workflows/blueprints/types.ts +38 -0
  327. package/src/lib/workflows/definition-validation.ts +121 -0
  328. package/src/lib/workflows/engine.ts +1113 -0
  329. package/src/lib/workflows/loop-executor.ts +270 -0
  330. package/src/lib/workflows/parallel.ts +55 -0
  331. package/src/lib/workflows/swarm.ts +97 -0
  332. package/src/lib/workflows/types.ts +112 -0
  333. package/tsconfig.json +41 -0
@@ -0,0 +1,85 @@
1
+ export const SUPPORTED_AGENT_RUNTIMES = [
2
+ "claude-code",
3
+ "openai-codex-app-server",
4
+ ] as const;
5
+
6
+ export type AgentRuntimeId = (typeof SUPPORTED_AGENT_RUNTIMES)[number];
7
+
8
+ export const DEFAULT_AGENT_RUNTIME: AgentRuntimeId = "claude-code";
9
+
10
+ export interface RuntimeCapabilities {
11
+ resume: boolean;
12
+ cancel: boolean;
13
+ approvals: boolean;
14
+ mcpServers: boolean;
15
+ profileTests: boolean;
16
+ taskAssist: boolean;
17
+ authHealthCheck: boolean;
18
+ }
19
+
20
+ export interface RuntimeCatalogEntry {
21
+ id: AgentRuntimeId;
22
+ label: string;
23
+ description: string;
24
+ providerId: "anthropic" | "openai";
25
+ capabilities: RuntimeCapabilities;
26
+ }
27
+
28
+ const RUNTIME_CATALOG: Record<AgentRuntimeId, RuntimeCatalogEntry> = {
29
+ "claude-code": {
30
+ id: "claude-code",
31
+ label: "Claude Code",
32
+ description: "Anthropic Claude Agent SDK runtime with approvals, resume, and MCP passthrough.",
33
+ providerId: "anthropic",
34
+ capabilities: {
35
+ resume: true,
36
+ cancel: true,
37
+ approvals: true,
38
+ mcpServers: true,
39
+ profileTests: true,
40
+ taskAssist: true,
41
+ authHealthCheck: true,
42
+ },
43
+ },
44
+ "openai-codex-app-server": {
45
+ id: "openai-codex-app-server",
46
+ label: "OpenAI Codex App Server",
47
+ description: "OpenAI Codex runtime over the app server protocol with resumable threads and inbox approvals.",
48
+ providerId: "openai",
49
+ capabilities: {
50
+ resume: true,
51
+ cancel: true,
52
+ approvals: true,
53
+ mcpServers: true,
54
+ profileTests: false,
55
+ taskAssist: true,
56
+ authHealthCheck: true,
57
+ },
58
+ },
59
+ };
60
+
61
+ export function isAgentRuntimeId(value: string): value is AgentRuntimeId {
62
+ return SUPPORTED_AGENT_RUNTIMES.includes(value as AgentRuntimeId);
63
+ }
64
+
65
+ export function getRuntimeCatalogEntry(
66
+ runtimeId: AgentRuntimeId = DEFAULT_AGENT_RUNTIME
67
+ ): RuntimeCatalogEntry {
68
+ return RUNTIME_CATALOG[runtimeId];
69
+ }
70
+
71
+ export function getRuntimeCapabilities(
72
+ runtimeId: AgentRuntimeId = DEFAULT_AGENT_RUNTIME
73
+ ): RuntimeCapabilities {
74
+ return getRuntimeCatalogEntry(runtimeId).capabilities;
75
+ }
76
+
77
+ export function resolveAgentRuntime(runtimeId?: string | null): AgentRuntimeId {
78
+ if (!runtimeId) return DEFAULT_AGENT_RUNTIME;
79
+ if (isAgentRuntimeId(runtimeId)) return runtimeId;
80
+ throw new Error(`Unknown agent type: ${runtimeId}`);
81
+ }
82
+
83
+ export function listRuntimeCatalog(): RuntimeCatalogEntry[] {
84
+ return SUPPORTED_AGENT_RUNTIMES.map((runtimeId) => RUNTIME_CATALOG[runtimeId]);
85
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Build the environment for the Claude Agent SDK subprocess.
3
+ * Returns undefined when no changes are needed.
4
+ */
5
+ export function buildClaudeSdkEnv(
6
+ authEnv?: Record<string, string>
7
+ ): Record<string, string> | undefined {
8
+ const isNested = "CLAUDECODE" in process.env;
9
+ if (!authEnv && !isNested) return undefined;
10
+ const { CLAUDECODE, ...cleanEnv } = process.env as Record<string, string>;
11
+ return { ...cleanEnv, ...authEnv };
12
+ }
@@ -0,0 +1,370 @@
1
+ import { query } from "@anthropic-ai/claude-agent-sdk";
2
+ import { db } from "@/lib/db";
3
+ import { tasks } from "@/lib/db/schema";
4
+ import { eq } from "drizzle-orm";
5
+ import { updateAuthStatus, getAuthEnv } from "@/lib/settings/auth";
6
+ import { getExecution, removeExecution } from "@/lib/agents/execution-manager";
7
+ import { getProfile } from "@/lib/agents/profiles/registry";
8
+ import { resolveProfileRuntimePayload } from "@/lib/agents/profiles/compatibility";
9
+ import { executeClaudeTask, resumeClaudeTask } from "@/lib/agents/claude-agent";
10
+ import { getRuntimeCapabilities, getRuntimeCatalogEntry } from "./catalog";
11
+ import { buildClaudeSdkEnv } from "./claude-sdk";
12
+ import type {
13
+ AgentRuntimeAdapter,
14
+ RuntimeConnectionResult,
15
+ TaskAssistInput,
16
+ } from "./types";
17
+ import type { ProfileTestResult, ProfileTestReport } from "@/lib/agents/profiles/test-types";
18
+ import type { TaskAssistResponse } from "./task-assist-types";
19
+ import {
20
+ extractUsageSnapshot,
21
+ mergeUsageSnapshot,
22
+ recordUsageLedgerEntry,
23
+ type UsageSnapshot,
24
+ } from "@/lib/usage/ledger";
25
+
26
+ const TASK_ASSIST_SYSTEM_PROMPT = `You are an AI task definition assistant. Analyze the given task and return ONLY a JSON object (no markdown, no code fences) with:
27
+ - "improvedDescription": A clearer version of the task for an AI agent to execute
28
+ - "breakdown": Array of {title, description} sub-tasks if complex (empty array if simple)
29
+ - "recommendedPattern": "single", "sequence", "planner-executor", or "checkpoint"
30
+ - "complexity": "simple", "moderate", or "complex"
31
+ - "needsCheckpoint": true if irreversible actions or needs human review
32
+ - "reasoning": Brief explanation`;
33
+
34
+ async function collectResultText(
35
+ response: AsyncIterable<Record<string, unknown>>
36
+ ): Promise<{ resultText: string; usage: UsageSnapshot }> {
37
+ let resultText = "";
38
+ let usage: UsageSnapshot = {};
39
+
40
+ for await (const raw of response) {
41
+ usage = mergeUsageSnapshot(usage, extractUsageSnapshot(raw));
42
+ if (raw.type === "result" && "result" in raw) {
43
+ resultText =
44
+ typeof raw.result === "string"
45
+ ? raw.result
46
+ : JSON.stringify(raw.result);
47
+ break;
48
+ }
49
+ }
50
+
51
+ return { resultText, usage };
52
+ }
53
+
54
+ async function runSingleProfileTest(
55
+ profileId: string,
56
+ test: { task: string; expectedKeywords: string[] }
57
+ ): Promise<ProfileTestResult> {
58
+ const profile = getProfile(profileId);
59
+ if (!profile) {
60
+ throw new Error(`Profile "${profileId}" not found`);
61
+ }
62
+
63
+ const payload = resolveProfileRuntimePayload(profile, "claude-code");
64
+ if (!payload.supported) {
65
+ throw new Error(payload.reason ?? `Profile "${profile.name}" is not supported on Claude Code`);
66
+ }
67
+
68
+ const prompt = `${payload.instructions}\n\n---\n\nTask: ${test.task}\n\nProvide a brief analysis (2-3 paragraphs max). Include specific terminology relevant to your domain.`;
69
+ const authEnv = await getAuthEnv();
70
+ const abortController = new AbortController();
71
+ const timeout = setTimeout(() => abortController.abort(), 30_000);
72
+ const startedAt = new Date();
73
+ let usage: UsageSnapshot = {};
74
+ let ledgerRecorded = false;
75
+
76
+ try {
77
+ const response = query({
78
+ prompt,
79
+ options: {
80
+ abortController,
81
+ includePartialMessages: true,
82
+ env: buildClaudeSdkEnv(authEnv),
83
+ allowedTools: [],
84
+ },
85
+ });
86
+
87
+ let responseText = "";
88
+ for await (const event of response as AsyncIterable<Record<string, unknown>>) {
89
+ usage = mergeUsageSnapshot(usage, extractUsageSnapshot(event));
90
+ if (event.type === "content_block_delta") {
91
+ const delta = event.delta as Record<string, unknown> | undefined;
92
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
93
+ responseText += delta.text;
94
+ }
95
+ } else if (event.type === "result") {
96
+ const resultText = event.result;
97
+ if (typeof resultText === "string") {
98
+ responseText = resultText;
99
+ }
100
+ }
101
+ }
102
+
103
+ const lowerResponse = responseText.toLowerCase();
104
+ const foundKeywords = test.expectedKeywords.filter((kw) =>
105
+ lowerResponse.includes(kw.toLowerCase())
106
+ );
107
+ const missingKeywords = test.expectedKeywords.filter(
108
+ (kw) => !lowerResponse.includes(kw.toLowerCase())
109
+ );
110
+
111
+ await recordUsageLedgerEntry({
112
+ activityType: "profile_test",
113
+ runtimeId: "claude-code",
114
+ providerId: "anthropic",
115
+ modelId: usage.modelId ?? null,
116
+ inputTokens: usage.inputTokens ?? null,
117
+ outputTokens: usage.outputTokens ?? null,
118
+ totalTokens: usage.totalTokens ?? null,
119
+ status: "completed",
120
+ startedAt,
121
+ finishedAt: new Date(),
122
+ });
123
+ ledgerRecorded = true;
124
+
125
+ return {
126
+ task: test.task,
127
+ expectedKeywords: test.expectedKeywords,
128
+ foundKeywords,
129
+ missingKeywords,
130
+ passed: missingKeywords.length === 0,
131
+ };
132
+ } catch {
133
+ await recordUsageLedgerEntry({
134
+ activityType: "profile_test",
135
+ runtimeId: "claude-code",
136
+ providerId: "anthropic",
137
+ modelId: usage.modelId ?? null,
138
+ inputTokens: usage.inputTokens ?? null,
139
+ outputTokens: usage.outputTokens ?? null,
140
+ totalTokens: usage.totalTokens ?? null,
141
+ status: "failed",
142
+ startedAt,
143
+ finishedAt: new Date(),
144
+ });
145
+ ledgerRecorded = true;
146
+ return {
147
+ task: test.task,
148
+ expectedKeywords: test.expectedKeywords,
149
+ foundKeywords: [],
150
+ missingKeywords: test.expectedKeywords,
151
+ passed: false,
152
+ };
153
+ } finally {
154
+ if (!ledgerRecorded && (abortController.signal.aborted || usage.modelId || usage.totalTokens != null)) {
155
+ await recordUsageLedgerEntry({
156
+ activityType: "profile_test",
157
+ runtimeId: "claude-code",
158
+ providerId: "anthropic",
159
+ modelId: usage.modelId ?? null,
160
+ inputTokens: usage.inputTokens ?? null,
161
+ outputTokens: usage.outputTokens ?? null,
162
+ totalTokens: usage.totalTokens ?? null,
163
+ status: "failed",
164
+ startedAt,
165
+ finishedAt: new Date(),
166
+ }).catch(() => {});
167
+ }
168
+ clearTimeout(timeout);
169
+ }
170
+ }
171
+
172
+ async function runClaudeProfileTests(profileId: string): Promise<ProfileTestReport> {
173
+ const profile = getProfile(profileId);
174
+ if (!profile) {
175
+ throw new Error(`Profile "${profileId}" not found`);
176
+ }
177
+
178
+ const payload = resolveProfileRuntimePayload(profile, "claude-code");
179
+ if (!payload.supported) {
180
+ return {
181
+ profileId,
182
+ profileName: profile.name,
183
+ runtimeId: "claude-code",
184
+ results: [],
185
+ totalPassed: 0,
186
+ totalFailed: 0,
187
+ unsupported: true,
188
+ unsupportedReason: payload.reason,
189
+ };
190
+ }
191
+
192
+ if (!payload.tests || payload.tests.length === 0) {
193
+ return {
194
+ profileId,
195
+ profileName: profile.name,
196
+ runtimeId: "claude-code",
197
+ results: [],
198
+ totalPassed: 0,
199
+ totalFailed: 0,
200
+ };
201
+ }
202
+
203
+ const results: ProfileTestResult[] = [];
204
+ for (const test of payload.tests) {
205
+ results.push(await runSingleProfileTest(profileId, test));
206
+ }
207
+
208
+ return {
209
+ profileId,
210
+ profileName: profile.name,
211
+ runtimeId: "claude-code",
212
+ results,
213
+ totalPassed: results.filter((result) => result.passed).length,
214
+ totalFailed: results.filter((result) => !result.passed).length,
215
+ };
216
+ }
217
+
218
+ async function runClaudeTaskAssist(
219
+ input: TaskAssistInput
220
+ ): Promise<TaskAssistResponse> {
221
+ const userMessage = [
222
+ input.title ? `Task title: ${input.title}` : "",
223
+ input.description ? `Description: ${input.description}` : "",
224
+ ]
225
+ .filter(Boolean)
226
+ .join("\n");
227
+
228
+ const authEnv = await getAuthEnv();
229
+ const prompt = `${TASK_ASSIST_SYSTEM_PROMPT}\n\n${userMessage}`;
230
+ const startedAt = new Date();
231
+ let usage: UsageSnapshot = {};
232
+
233
+ try {
234
+ const response = query({
235
+ prompt,
236
+ options: {
237
+ cwd: process.cwd(),
238
+ env: buildClaudeSdkEnv(authEnv),
239
+ },
240
+ });
241
+
242
+ const collected = await collectResultText(
243
+ response as AsyncIterable<Record<string, unknown>>
244
+ );
245
+ usage = collected.usage;
246
+
247
+ if (!collected.resultText) {
248
+ throw new Error("No result from AI");
249
+ }
250
+
251
+ const jsonMatch = collected.resultText.match(/\{[\s\S]*\}/);
252
+ if (!jsonMatch) {
253
+ throw new Error("Could not parse AI response");
254
+ }
255
+
256
+ const parsed = JSON.parse(jsonMatch[0]) as TaskAssistResponse;
257
+
258
+ await recordUsageLedgerEntry({
259
+ activityType: "task_assist",
260
+ runtimeId: "claude-code",
261
+ providerId: "anthropic",
262
+ modelId: usage.modelId ?? null,
263
+ inputTokens: usage.inputTokens ?? null,
264
+ outputTokens: usage.outputTokens ?? null,
265
+ totalTokens: usage.totalTokens ?? null,
266
+ status: "completed",
267
+ startedAt,
268
+ finishedAt: new Date(),
269
+ });
270
+
271
+ return parsed;
272
+ } catch (error) {
273
+ await recordUsageLedgerEntry({
274
+ activityType: "task_assist",
275
+ runtimeId: "claude-code",
276
+ providerId: "anthropic",
277
+ modelId: usage.modelId ?? null,
278
+ inputTokens: usage.inputTokens ?? null,
279
+ outputTokens: usage.outputTokens ?? null,
280
+ totalTokens: usage.totalTokens ?? null,
281
+ status: "failed",
282
+ startedAt,
283
+ finishedAt: new Date(),
284
+ });
285
+ throw error;
286
+ }
287
+ }
288
+
289
+ async function testClaudeConnection(): Promise<RuntimeConnectionResult> {
290
+ const abortController = new AbortController();
291
+ const timeout = setTimeout(() => abortController.abort(), 10_000);
292
+
293
+ try {
294
+ const authEnv = await getAuthEnv();
295
+ const response = query({
296
+ prompt: "Reply with exactly: OK",
297
+ options: {
298
+ abortController,
299
+ maxTurns: 1,
300
+ cwd: process.cwd(),
301
+ env: buildClaudeSdkEnv(authEnv),
302
+ },
303
+ });
304
+
305
+ for await (const raw of response as AsyncIterable<Record<string, unknown>>) {
306
+ const message = raw as {
307
+ type?: string;
308
+ subtype?: string;
309
+ api_key_source?: string;
310
+ };
311
+
312
+ if (message.type === "system" && message.subtype === "init") {
313
+ const source = (message.api_key_source ?? "unknown") as RuntimeConnectionResult["apiKeySource"];
314
+ await updateAuthStatus(source ?? "unknown");
315
+ abortController.abort();
316
+ return { connected: true, apiKeySource: source ?? "unknown" };
317
+ }
318
+ }
319
+
320
+ return { connected: true, apiKeySource: "unknown" };
321
+ } catch (error: unknown) {
322
+ const message = error instanceof Error ? error.message : String(error);
323
+
324
+ if (abortController.signal.aborted && !message.includes("auth")) {
325
+ return { connected: true, apiKeySource: "unknown" };
326
+ }
327
+
328
+ return { connected: false, error: message };
329
+ } finally {
330
+ clearTimeout(timeout);
331
+ }
332
+ }
333
+
334
+ async function cancelClaudeTask(taskId: string): Promise<void> {
335
+ const execution = getExecution(taskId);
336
+ if (execution) {
337
+ execution.abortController.abort();
338
+ removeExecution(taskId);
339
+ }
340
+
341
+ await db
342
+ .update(tasks)
343
+ .set({ status: "cancelled", updatedAt: new Date() })
344
+ .where(eq(tasks.id, taskId));
345
+ }
346
+
347
+ export const claudeRuntimeAdapter: AgentRuntimeAdapter = {
348
+ metadata: {
349
+ ...getRuntimeCatalogEntry("claude-code"),
350
+ capabilities: getRuntimeCapabilities("claude-code"),
351
+ },
352
+ executeTask(taskId: string) {
353
+ return executeClaudeTask(taskId);
354
+ },
355
+ resumeTask(taskId: string) {
356
+ return resumeClaudeTask(taskId);
357
+ },
358
+ cancelTask(taskId: string) {
359
+ return cancelClaudeTask(taskId);
360
+ },
361
+ runTaskAssist(input: TaskAssistInput) {
362
+ return runClaudeTaskAssist(input);
363
+ },
364
+ runProfileTests(profileId: string) {
365
+ return runClaudeProfileTests(profileId);
366
+ },
367
+ testConnection() {
368
+ return testClaudeConnection();
369
+ },
370
+ };