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,29 @@
1
+ id: technical-writer
2
+ name: Technical Writer
3
+ version: "1.0.0"
4
+ domain: work
5
+ tags: [documentation, api-docs, adr, readme, changelog, technical-writing]
6
+ supportedRuntimes: [claude-code, openai-codex-app-server]
7
+
8
+ allowedTools:
9
+ - Read
10
+ - Grep
11
+ - Glob
12
+ - Write
13
+ - Edit
14
+
15
+ canUseToolPolicy:
16
+ autoApprove: [Read, Grep, Glob]
17
+ autoDeny: []
18
+
19
+ temperature: 0.4
20
+ maxTurns: 20
21
+ outputFormat: markdown
22
+
23
+ author: stagent
24
+
25
+ tests:
26
+ - task: "Write API documentation for the /api/tasks endpoint"
27
+ expectedKeywords: [endpoint, request, response, parameters, status]
28
+ - task: "Create an ADR for switching from REST to GraphQL"
29
+ expectedKeywords: [context, decision, consequences, alternatives, status]
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: travel-planner
3
+ description: Itinerary building, budget optimization, and booking research
4
+ ---
5
+
6
+ You are an experienced travel planner who creates detailed, practical itineraries.
7
+
8
+ ## Core Capabilities
9
+
10
+ 1. **Itinerary Building** — Day-by-day plans with timing, logistics, and alternatives
11
+ 2. **Budget Optimization** — Cost breakdowns, money-saving tips, price comparisons
12
+ 3. **Destination Research** — Local tips, seasonal considerations, safety information
13
+ 4. **Logistics Planning** — Transportation routing, accommodation recommendations, booking timing
14
+ 5. **Personalization** — Adapt plans to traveler preferences, pace, and interests
15
+
16
+ ## Output Format
17
+
18
+ Structure itineraries as:
19
+ - **Overview**: Destination, dates, budget range, travel style
20
+ - **Day-by-Day Plan**: Morning/afternoon/evening activities with addresses and costs
21
+ - **Budget Breakdown**: Accommodation, transport, food, activities, contingency
22
+ - **Booking Checklist**: What to book when, with priority order
23
+ - **Tips**: Local customs, packing suggestions, money-saving hacks
@@ -0,0 +1,26 @@
1
+ id: travel-planner
2
+ name: Travel Planner
3
+ version: "1.0.0"
4
+ domain: personal
5
+ tags: [travel, itinerary, budget, booking, vacation, trip]
6
+ supportedRuntimes: [claude-code]
7
+
8
+ allowedTools:
9
+ - WebSearch
10
+ - WebFetch
11
+ - Read
12
+
13
+ canUseToolPolicy:
14
+ autoApprove: [WebSearch, WebFetch, Read]
15
+ autoDeny: [Bash, Write, Edit]
16
+
17
+ temperature: 0.6
18
+ maxTurns: 25
19
+
20
+ author: stagent
21
+
22
+ tests:
23
+ - task: "Plan a 5-day trip to Tokyo on a moderate budget"
24
+ expectedKeywords: [itinerary, budget, accommodation, transport]
25
+ - task: "Compare flight options from New York to London in March"
26
+ expectedKeywords: [flight, price, comparison, booking]
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: wealth-manager
3
+ description: Portfolio analysis, tax optimization, and risk assessment with appropriate disclaimers
4
+ ---
5
+
6
+ You are a knowledgeable financial analyst who helps users understand their financial situation. You provide educational analysis, NOT financial advice.
7
+
8
+ **IMPORTANT DISCLAIMER**: You are an AI assistant providing general financial education and analysis. This is NOT personalized financial advice. Always recommend consulting a licensed financial advisor for investment decisions.
9
+
10
+ ## Core Capabilities
11
+
12
+ 1. **Portfolio Analysis** — Asset allocation review, diversification assessment, sector exposure
13
+ 2. **Tax Optimization** — Tax-loss harvesting opportunities, account type optimization (401k/IRA/taxable)
14
+ 3. **Risk Assessment** — Risk tolerance alignment, drawdown analysis, concentration risk
15
+ 4. **Goal Planning** — Retirement projections, savings rate analysis, milestone tracking
16
+ 5. **Market Context** — Relate portfolio positioning to current market conditions
17
+
18
+ ## Output Format
19
+
20
+ - **Portfolio Summary**: Current allocation with percentages
21
+ - **Analysis**: Key observations about diversification, risk, and performance
22
+ - **Opportunities**: Potential optimizations with pros/cons
23
+ - **Risks**: Concentration or strategy risks identified
24
+ - **Disclaimer**: Always end with the standard financial disclaimer
@@ -0,0 +1,26 @@
1
+ id: wealth-manager
2
+ name: Wealth Manager
3
+ version: "1.0.0"
4
+ domain: personal
5
+ tags: [finance, investing, tax, portfolio, retirement, savings]
6
+ supportedRuntimes: [claude-code]
7
+
8
+ allowedTools:
9
+ - WebSearch
10
+ - WebFetch
11
+ - Read
12
+
13
+ canUseToolPolicy:
14
+ autoApprove: [Read]
15
+ autoDeny: [Bash, Write, Edit]
16
+
17
+ temperature: 0.3
18
+ maxTurns: 20
19
+
20
+ author: stagent
21
+
22
+ tests:
23
+ - task: "Review my portfolio allocation and suggest improvements"
24
+ expectedKeywords: [allocation, diversification, risk, disclaimer]
25
+ - task: "What tax optimization strategies should I consider this year?"
26
+ expectedKeywords: [tax, optimization, strategy, disclaimer]
@@ -0,0 +1,109 @@
1
+ import {
2
+ DEFAULT_AGENT_RUNTIME,
3
+ SUPPORTED_AGENT_RUNTIMES,
4
+ resolveAgentRuntime,
5
+ type AgentRuntimeId,
6
+ } from "@/lib/agents/runtime/catalog";
7
+ import type {
8
+ CanUseToolPolicy,
9
+ ProfileRuntimeOverride,
10
+ ProfileSmokeTest,
11
+ } from "./types";
12
+
13
+ type CompatibilityProfile = {
14
+ id: string;
15
+ name: string;
16
+ skillMd?: string;
17
+ systemPrompt?: string;
18
+ allowedTools?: string[];
19
+ mcpServers?: Record<string, unknown>;
20
+ canUseToolPolicy?: CanUseToolPolicy;
21
+ tests?: ProfileSmokeTest[];
22
+ supportedRuntimes?: AgentRuntimeId[];
23
+ runtimeOverrides?: Partial<Record<AgentRuntimeId, ProfileRuntimeOverride>>;
24
+ };
25
+
26
+ export interface ProfileRuntimeCompatibility {
27
+ runtimeId: AgentRuntimeId;
28
+ supported: boolean;
29
+ reason?: string;
30
+ instructionsSource: "shared" | "runtime-override";
31
+ }
32
+
33
+ export interface ResolvedProfileRuntimePayload
34
+ extends ProfileRuntimeCompatibility {
35
+ profileId: string;
36
+ profileName: string;
37
+ instructions: string;
38
+ allowedTools?: string[];
39
+ mcpServers?: Record<string, unknown>;
40
+ canUseToolPolicy?: CanUseToolPolicy;
41
+ tests?: ProfileSmokeTest[];
42
+ }
43
+
44
+ export function getSupportedRuntimes(
45
+ profile: Pick<CompatibilityProfile, "supportedRuntimes">
46
+ ): AgentRuntimeId[] {
47
+ const supported =
48
+ profile.supportedRuntimes && profile.supportedRuntimes.length > 0
49
+ ? profile.supportedRuntimes
50
+ : ["claude-code"];
51
+
52
+ return SUPPORTED_AGENT_RUNTIMES.filter((runtimeId) =>
53
+ supported.includes(runtimeId)
54
+ );
55
+ }
56
+
57
+ export function getProfileRuntimeCompatibility(
58
+ profile: CompatibilityProfile,
59
+ runtimeId?: string | null
60
+ ): ProfileRuntimeCompatibility {
61
+ const resolvedRuntime = resolveAgentRuntime(runtimeId ?? DEFAULT_AGENT_RUNTIME);
62
+ const supportedRuntimes = getSupportedRuntimes(profile);
63
+ const supported = supportedRuntimes.includes(resolvedRuntime);
64
+ const runtimeOverride = profile.runtimeOverrides?.[resolvedRuntime];
65
+
66
+ return {
67
+ runtimeId: resolvedRuntime,
68
+ supported,
69
+ reason: supported
70
+ ? undefined
71
+ : `${profile.name} does not support ${resolvedRuntime}`,
72
+ instructionsSource: runtimeOverride?.instructions ? "runtime-override" : "shared",
73
+ };
74
+ }
75
+
76
+ export function profileSupportsRuntime(
77
+ profile: CompatibilityProfile,
78
+ runtimeId?: string | null
79
+ ): boolean {
80
+ return getProfileRuntimeCompatibility(profile, runtimeId).supported;
81
+ }
82
+
83
+ export function resolveProfileRuntimePayload(
84
+ profile: CompatibilityProfile,
85
+ runtimeId?: string | null
86
+ ): ResolvedProfileRuntimePayload {
87
+ const compatibility = getProfileRuntimeCompatibility(profile, runtimeId);
88
+ const runtimeOverride = profile.runtimeOverrides?.[compatibility.runtimeId];
89
+ const instructions =
90
+ runtimeOverride?.instructions ??
91
+ profile.skillMd ??
92
+ profile.systemPrompt ??
93
+ "";
94
+
95
+ return {
96
+ profileId: profile.id,
97
+ profileName: profile.name,
98
+ runtimeId: compatibility.runtimeId,
99
+ supported: compatibility.supported,
100
+ reason: compatibility.reason,
101
+ instructionsSource: compatibility.instructionsSource,
102
+ instructions,
103
+ allowedTools: runtimeOverride?.allowedTools ?? profile.allowedTools,
104
+ mcpServers: runtimeOverride?.mcpServers ?? profile.mcpServers,
105
+ canUseToolPolicy:
106
+ runtimeOverride?.canUseToolPolicy ?? profile.canUseToolPolicy,
107
+ tests: runtimeOverride?.tests ?? profile.tests,
108
+ };
109
+ }
@@ -0,0 +1,293 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import yaml from "js-yaml";
4
+ import { ProfileConfigSchema } from "@/lib/validators/profile";
5
+ import type { ProfileConfig } from "@/lib/validators/profile";
6
+ import { getSupportedRuntimes } from "./compatibility";
7
+ import type { AgentProfile } from "./types";
8
+
9
+ /**
10
+ * Builtins ship inside the repo at src/lib/agents/profiles/builtins/.
11
+ * At runtime they are copied (if missing) to ~/.claude/skills/ so users
12
+ * can customize them without touching source.
13
+ */
14
+ const BUILTINS_DIR = path.resolve(
15
+ import.meta.dirname ?? __dirname,
16
+ "builtins"
17
+ );
18
+
19
+ const SKILLS_DIR = path.join(
20
+ process.env.HOME ?? process.env.USERPROFILE ?? ".",
21
+ ".claude",
22
+ "skills"
23
+ );
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Cache
27
+ // ---------------------------------------------------------------------------
28
+
29
+ let profileCache: Map<string, AgentProfile> | null = null;
30
+ let profileCacheSignature: string | null = null;
31
+
32
+ function getSkillsDirectorySignature(): string {
33
+ if (!fs.existsSync(SKILLS_DIR)) {
34
+ return "missing";
35
+ }
36
+
37
+ const entries = fs
38
+ .readdirSync(SKILLS_DIR, { withFileTypes: true })
39
+ .filter((entry) => entry.isDirectory())
40
+ .sort((a, b) => a.name.localeCompare(b.name));
41
+
42
+ const signatureParts: string[] = [];
43
+
44
+ for (const entry of entries) {
45
+ const dir = path.join(SKILLS_DIR, entry.name);
46
+ const yamlPath = path.join(dir, "profile.yaml");
47
+ const skillPath = path.join(dir, "SKILL.md");
48
+
49
+ signatureParts.push(entry.name);
50
+
51
+ if (fs.existsSync(yamlPath)) {
52
+ const stats = fs.statSync(yamlPath);
53
+ signatureParts.push(`yaml:${stats.mtimeMs}:${stats.size}`);
54
+ }
55
+
56
+ if (fs.existsSync(skillPath)) {
57
+ const stats = fs.statSync(skillPath);
58
+ signatureParts.push(`skill:${stats.mtimeMs}:${stats.size}`);
59
+ }
60
+ }
61
+
62
+ return signatureParts.join("|");
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // ensureBuiltins — copy missing builtins to .claude/skills/ (idempotent)
67
+ // ---------------------------------------------------------------------------
68
+
69
+ function ensureBuiltins(): void {
70
+ if (!fs.existsSync(BUILTINS_DIR)) return;
71
+
72
+ fs.mkdirSync(SKILLS_DIR, { recursive: true });
73
+
74
+ for (const entry of fs.readdirSync(BUILTINS_DIR, { withFileTypes: true })) {
75
+ if (!entry.isDirectory()) continue;
76
+
77
+ const targetDir = path.join(SKILLS_DIR, entry.name);
78
+ const targetYaml = path.join(targetDir, "profile.yaml");
79
+ const srcYaml = path.join(BUILTINS_DIR, entry.name, "profile.yaml");
80
+
81
+ // Never overwrite user edits — only copy if profile.yaml is missing
82
+ if (fs.existsSync(targetYaml)) {
83
+ try {
84
+ const source = (yaml.load(fs.readFileSync(srcYaml, "utf-8")) ??
85
+ {}) as Record<string, unknown>;
86
+ const target = (yaml.load(fs.readFileSync(targetYaml, "utf-8")) ??
87
+ {}) as Record<string, unknown>;
88
+ let changed = false;
89
+
90
+ if (
91
+ source.supportedRuntimes !== undefined &&
92
+ target.supportedRuntimes === undefined
93
+ ) {
94
+ target.supportedRuntimes = source.supportedRuntimes;
95
+ changed = true;
96
+ }
97
+
98
+ if (
99
+ source.runtimeOverrides !== undefined &&
100
+ target.runtimeOverrides === undefined
101
+ ) {
102
+ target.runtimeOverrides = source.runtimeOverrides;
103
+ changed = true;
104
+ }
105
+
106
+ if (changed) {
107
+ fs.writeFileSync(targetYaml, yaml.dump(target));
108
+ }
109
+ } catch {
110
+ // If a user has customized or broken the YAML, leave it untouched.
111
+ }
112
+ continue;
113
+ }
114
+
115
+ fs.mkdirSync(targetDir, { recursive: true });
116
+
117
+ const srcDir = path.join(BUILTINS_DIR, entry.name);
118
+ for (const file of fs.readdirSync(srcDir)) {
119
+ fs.copyFileSync(path.join(srcDir, file), path.join(targetDir, file));
120
+ }
121
+ }
122
+ }
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // scanProfiles — read .claude/skills/*/profile.yaml, validate, pair w/ SKILL.md
126
+ // ---------------------------------------------------------------------------
127
+
128
+ function scanProfiles(): Map<string, AgentProfile> {
129
+ const profiles = new Map<string, AgentProfile>();
130
+
131
+ if (!fs.existsSync(SKILLS_DIR)) return profiles;
132
+
133
+ for (const entry of fs.readdirSync(SKILLS_DIR, { withFileTypes: true })) {
134
+ if (!entry.isDirectory()) continue;
135
+
136
+ const dir = path.join(SKILLS_DIR, entry.name);
137
+ const yamlPath = path.join(dir, "profile.yaml");
138
+ const skillPath = path.join(dir, "SKILL.md");
139
+
140
+ if (!fs.existsSync(yamlPath)) continue;
141
+
142
+ try {
143
+ const rawYaml = fs.readFileSync(yamlPath, "utf-8");
144
+ const parsed = yaml.load(rawYaml);
145
+ const result = ProfileConfigSchema.safeParse(parsed);
146
+
147
+ if (!result.success) {
148
+ console.warn(
149
+ `[profiles] Invalid profile.yaml in ${entry.name}:`,
150
+ result.error.issues.map((i) => i.message).join(", ")
151
+ );
152
+ continue;
153
+ }
154
+
155
+ const config = result.data;
156
+ const skillMd = fs.existsSync(skillPath)
157
+ ? fs.readFileSync(skillPath, "utf-8")
158
+ : "";
159
+
160
+ // Extract description from SKILL.md frontmatter or fall back to name
161
+ const descMatch = skillMd.match(
162
+ /^---\s*\n[\s\S]*?description:\s*(.+?)\s*\n[\s\S]*?---/
163
+ );
164
+ const description = descMatch?.[1] ?? config.name;
165
+
166
+ profiles.set(config.id, {
167
+ id: config.id,
168
+ name: config.name,
169
+ description,
170
+ domain: config.domain,
171
+ tags: config.tags,
172
+ systemPrompt: skillMd, // backward compat
173
+ skillMd,
174
+ allowedTools: config.allowedTools,
175
+ mcpServers: config.mcpServers as Record<string, unknown>,
176
+ canUseToolPolicy: config.canUseToolPolicy,
177
+ temperature: config.temperature,
178
+ maxTurns: config.maxTurns,
179
+ outputFormat: config.outputFormat,
180
+ version: config.version,
181
+ author: config.author,
182
+ source: config.source,
183
+ tests: config.tests,
184
+ supportedRuntimes: getSupportedRuntimes(config),
185
+ runtimeOverrides: config.runtimeOverrides,
186
+ });
187
+ } catch (err) {
188
+ console.warn(`[profiles] Error loading profile ${entry.name}:`, err);
189
+ }
190
+ }
191
+
192
+ return profiles;
193
+ }
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // Initialization — lazy on first access
197
+ // ---------------------------------------------------------------------------
198
+
199
+ function ensureLoaded(): Map<string, AgentProfile> {
200
+ ensureBuiltins();
201
+ const signature = getSkillsDirectorySignature();
202
+
203
+ if (!profileCache || profileCacheSignature !== signature) {
204
+ profileCache = scanProfiles();
205
+ profileCacheSignature = signature;
206
+ }
207
+ return profileCache;
208
+ }
209
+
210
+ // ---------------------------------------------------------------------------
211
+ // Public API — same synchronous signatures as before
212
+ // ---------------------------------------------------------------------------
213
+
214
+ export function getProfile(id: string): AgentProfile | undefined {
215
+ return ensureLoaded().get(id);
216
+ }
217
+
218
+ export function listProfiles(): AgentProfile[] {
219
+ return Array.from(ensureLoaded().values());
220
+ }
221
+
222
+ export function getProfileTags(): Map<string, string[]> {
223
+ const tagMap = new Map<string, string[]>();
224
+ for (const profile of ensureLoaded().values()) {
225
+ tagMap.set(profile.id, profile.tags);
226
+ }
227
+ return tagMap;
228
+ }
229
+
230
+ /** Force re-scan of .claude/skills/ — call after user adds/edits profiles */
231
+ export function reloadProfiles(): void {
232
+ profileCache = null;
233
+ profileCacheSignature = null;
234
+ }
235
+
236
+ /** Check if a profile ID is a built-in (exists in builtins/ source directory) */
237
+ export function isBuiltin(id: string): boolean {
238
+ return fs.existsSync(path.join(BUILTINS_DIR, id, "profile.yaml"));
239
+ }
240
+
241
+ /** Create a new custom profile in ~/.claude/skills/ */
242
+ export function createProfile(config: ProfileConfig, skillMd: string): void {
243
+ const result = ProfileConfigSchema.safeParse(config);
244
+ if (!result.success) {
245
+ throw new Error(`Invalid profile: ${result.error.issues.map(i => i.message).join(", ")}`);
246
+ }
247
+
248
+ const dir = path.join(SKILLS_DIR, config.id);
249
+ if (fs.existsSync(path.join(dir, "profile.yaml"))) {
250
+ throw new Error(`Profile "${config.id}" already exists`);
251
+ }
252
+
253
+ fs.mkdirSync(dir, { recursive: true });
254
+ fs.writeFileSync(path.join(dir, "profile.yaml"), yaml.dump(config));
255
+ fs.writeFileSync(path.join(dir, "SKILL.md"), skillMd);
256
+ reloadProfiles();
257
+ }
258
+
259
+ /** Update an existing custom profile (rejects builtins) */
260
+ export function updateProfile(id: string, config: ProfileConfig, skillMd: string): void {
261
+ if (isBuiltin(id)) {
262
+ throw new Error("Cannot modify built-in profiles");
263
+ }
264
+
265
+ const result = ProfileConfigSchema.safeParse(config);
266
+ if (!result.success) {
267
+ throw new Error(`Invalid profile: ${result.error.issues.map(i => i.message).join(", ")}`);
268
+ }
269
+
270
+ const dir = path.join(SKILLS_DIR, id);
271
+ if (!fs.existsSync(dir)) {
272
+ throw new Error(`Profile "${id}" not found`);
273
+ }
274
+
275
+ fs.writeFileSync(path.join(dir, "profile.yaml"), yaml.dump(config));
276
+ fs.writeFileSync(path.join(dir, "SKILL.md"), skillMd);
277
+ reloadProfiles();
278
+ }
279
+
280
+ /** Delete a custom profile (rejects builtins) */
281
+ export function deleteProfile(id: string): void {
282
+ if (isBuiltin(id)) {
283
+ throw new Error("Cannot delete built-in profiles");
284
+ }
285
+
286
+ const dir = path.join(SKILLS_DIR, id);
287
+ if (!fs.existsSync(dir)) {
288
+ throw new Error(`Profile "${id}" not found`);
289
+ }
290
+
291
+ fs.rmSync(dir, { recursive: true, force: true });
292
+ reloadProfiles();
293
+ }
@@ -0,0 +1,18 @@
1
+ import {
2
+ DEFAULT_AGENT_RUNTIME,
3
+ type AgentRuntimeId,
4
+ } from "@/lib/agents/runtime/catalog";
5
+ import { runProfileTestsWithRuntime } from "@/lib/agents/runtime";
6
+
7
+ export type { ProfileTestResult, ProfileTestReport } from "./test-types";
8
+
9
+ /**
10
+ * Run behavioral smoke tests for a profile through the active runtime layer.
11
+ * Default remains Claude until additional runtimes are wired into the UI.
12
+ */
13
+ export async function runProfileTests(
14
+ profileId: string,
15
+ runtimeId: AgentRuntimeId = DEFAULT_AGENT_RUNTIME
16
+ ) {
17
+ return runProfileTestsWithRuntime(profileId, runtimeId);
18
+ }
@@ -0,0 +1,20 @@
1
+ import type { AgentRuntimeId } from "@/lib/agents/runtime/catalog";
2
+
3
+ export interface ProfileTestResult {
4
+ task: string;
5
+ expectedKeywords: string[];
6
+ foundKeywords: string[];
7
+ missingKeywords: string[];
8
+ passed: boolean;
9
+ }
10
+
11
+ export interface ProfileTestReport {
12
+ profileId: string;
13
+ profileName: string;
14
+ runtimeId: AgentRuntimeId;
15
+ results: ProfileTestResult[];
16
+ totalPassed: number;
17
+ totalFailed: number;
18
+ unsupported?: boolean;
19
+ unsupportedReason?: string;
20
+ }
@@ -0,0 +1,43 @@
1
+ import type { AgentRuntimeId } from "@/lib/agents/runtime/catalog";
2
+
3
+ export interface CanUseToolPolicy {
4
+ autoApprove?: string[];
5
+ autoDeny?: string[];
6
+ }
7
+
8
+ export interface ProfileSmokeTest {
9
+ task: string;
10
+ expectedKeywords: string[];
11
+ }
12
+
13
+ export interface ProfileRuntimeOverride {
14
+ instructions?: string;
15
+ allowedTools?: string[];
16
+ mcpServers?: Record<string, unknown>;
17
+ canUseToolPolicy?: CanUseToolPolicy;
18
+ tests?: ProfileSmokeTest[];
19
+ }
20
+
21
+ export interface AgentProfile {
22
+ id: string;
23
+ name: string;
24
+ description: string;
25
+ domain: string;
26
+ tags: string[];
27
+ /** @deprecated Use skillMd instead — kept for backward compat during migration */
28
+ systemPrompt: string;
29
+ /** Full content of the SKILL.md file (system prompt + behavioral instructions) */
30
+ skillMd: string;
31
+ allowedTools?: string[];
32
+ mcpServers?: Record<string, unknown>;
33
+ canUseToolPolicy?: CanUseToolPolicy;
34
+ temperature?: number;
35
+ maxTurns?: number;
36
+ outputFormat?: string;
37
+ version?: string;
38
+ author?: string;
39
+ source?: string;
40
+ tests?: ProfileSmokeTest[];
41
+ supportedRuntimes: AgentRuntimeId[];
42
+ runtimeOverrides?: Partial<Record<AgentRuntimeId, ProfileRuntimeOverride>>;
43
+ }
@@ -0,0 +1,56 @@
1
+ import { listProfiles } from "./profiles/registry";
2
+ import { profileSupportsRuntime } from "./profiles/compatibility";
3
+ import {
4
+ executeTaskWithRuntime,
5
+ resumeTaskWithRuntime,
6
+ } from "./runtime";
7
+ import { DEFAULT_AGENT_RUNTIME } from "./runtime/catalog";
8
+
9
+ /**
10
+ * Classify a task into an agent profile based on keyword matching.
11
+ * Scores each profile by keyword hits in title + description.
12
+ * Returns the highest-scoring profile ID, or "general" if no strong match.
13
+ */
14
+ export function classifyTaskProfile(
15
+ title: string,
16
+ description?: string | null,
17
+ runtimeId: string | null | undefined = DEFAULT_AGENT_RUNTIME
18
+ ): string {
19
+ const text = `${title} ${description ?? ""}`.toLowerCase();
20
+ const profiles = listProfiles().filter((profile) =>
21
+ profileSupportsRuntime(profile, runtimeId)
22
+ );
23
+
24
+ let bestProfile = "general";
25
+ let bestScore = 0;
26
+
27
+ for (const profile of profiles) {
28
+ const profileId = profile.id;
29
+ if (profileId === "general") continue;
30
+ let score = 0;
31
+ for (const tag of profile.tags) {
32
+ if (text.includes(tag)) score++;
33
+ }
34
+ if (score > bestScore) {
35
+ bestScore = score;
36
+ bestProfile = profileId;
37
+ }
38
+ }
39
+
40
+ // Require at least 2 keyword hits to avoid false positives
41
+ return bestScore >= 2 ? bestProfile : "general";
42
+ }
43
+
44
+ export async function executeTaskWithAgent(
45
+ taskId: string,
46
+ agentType: string | null | undefined = DEFAULT_AGENT_RUNTIME
47
+ ): Promise<void> {
48
+ return executeTaskWithRuntime(taskId, agentType);
49
+ }
50
+
51
+ export async function resumeTaskWithAgent(
52
+ taskId: string,
53
+ agentType: string | null | undefined = DEFAULT_AGENT_RUNTIME
54
+ ): Promise<void> {
55
+ return resumeTaskWithRuntime(taskId, agentType);
56
+ }