spine-framework 0.3.124 → 0.3.126

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 (319) hide show
  1. package/.framework/cli/commands/agents.ts +35 -37
  2. package/.framework/cli/commands/auth.ts +35 -34
  3. package/.framework/cli/commands/create-app.ts +104 -40
  4. package/.framework/cli/commands/dev.ts +104 -69
  5. package/.framework/cli/commands/doctor.ts +179 -69
  6. package/.framework/cli/commands/generate.ts +83 -143
  7. package/.framework/cli/commands/init.ts +72 -34
  8. package/.framework/cli/commands/install-app.ts +260 -125
  9. package/.framework/cli/commands/items.ts +33 -44
  10. package/.framework/cli/commands/migrate.ts +122 -40
  11. package/.framework/cli/commands/migrations.ts +33 -40
  12. package/.framework/cli/commands/pipelines.ts +31 -39
  13. package/.framework/cli/commands/status.ts +47 -23
  14. package/.framework/cli/commands/system.ts +47 -37
  15. package/.framework/cli/commands/test.ts +112 -68
  16. package/.framework/cli/commands/validate-app.ts +215 -121
  17. package/.framework/cli/context.ts +112 -130
  18. package/.framework/cli/env-loader.ts +47 -18
  19. package/.framework/cli/index.ts +81 -42
  20. package/.framework/docs/runtime-types.md +1024 -0
  21. package/.framework/functions/_shared/agent-runner.ts +1296 -961
  22. package/.framework/functions/_shared/app-manifest.ts +82 -68
  23. package/.framework/functions/_shared/audit.ts +135 -83
  24. package/.framework/functions/_shared/{cil-state.ts → cil.ts} +158 -56
  25. package/.framework/functions/_shared/db.ts +80 -126
  26. package/.framework/functions/_shared/hash-api-key.ts +63 -0
  27. package/.framework/functions/_shared/index.ts +224 -335
  28. package/.framework/functions/_shared/kb.ts +319 -0
  29. package/.framework/functions/_shared/llm.ts +92 -0
  30. package/.framework/functions/_shared/logging.ts +288 -0
  31. package/.framework/functions/_shared/middleware.ts +329 -365
  32. package/.framework/functions/_shared/outcomes.ts +36 -0
  33. package/.framework/functions/_shared/permissions.ts +359 -643
  34. package/.framework/functions/_shared/pipeline-runner.ts +243 -316
  35. package/.framework/functions/_shared/principal.ts +376 -468
  36. package/.framework/functions/_shared/resolve-credentials.ts +102 -0
  37. package/.framework/functions/_shared/resolve-ids.ts +188 -16
  38. package/.framework/functions/_shared/retry-deliveries.ts +104 -0
  39. package/.framework/functions/_shared/schema-utils.ts +611 -373
  40. package/.framework/functions/_shared/testing.ts +207 -137
  41. package/.framework/functions/_shared/tracing.ts +276 -0
  42. package/.framework/functions/_shared/trigger-engine.ts +159 -211
  43. package/.framework/functions/_shared/webhook-registration.ts +95 -81
  44. package/.framework/functions/_shared/webhook-registry.ts +153 -75
  45. package/.framework/functions/account-nodes.ts +31 -53
  46. package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
  47. package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
  48. package/.framework/functions/adapters/openai-outbound.ts +40 -0
  49. package/.framework/functions/admin-data.ts +685 -537
  50. package/.framework/functions/admin-funnel.ts +31 -20
  51. package/.framework/functions/admin-install-funnel.ts +47 -28
  52. package/.framework/functions/admin-signals.ts +47 -29
  53. package/.framework/functions/ai-agents.ts +122 -147
  54. package/.framework/functions/ai-interactions.ts +180 -90
  55. package/.framework/functions/api-keys.ts +250 -192
  56. package/.framework/functions/apps.ts +203 -197
  57. package/.framework/functions/auth.ts +98 -91
  58. package/.framework/functions/cil-agent-ingest.ts +773 -0
  59. package/.framework/functions/cil-agent.ts +592 -0
  60. package/.framework/functions/cil-callmap.ts +1227 -0
  61. package/.framework/functions/cil-config.ts +583 -0
  62. package/.framework/functions/cil-context.ts +445 -0
  63. package/.framework/functions/cil-doc.ts +1752 -0
  64. package/.framework/functions/cil-embeddings.ts +1202 -0
  65. package/.framework/functions/cil-graph.ts +585 -0
  66. package/.framework/functions/cil-ingest.ts +2416 -0
  67. package/.framework/functions/cil-pipeline.ts +318 -0
  68. package/.framework/functions/cil-project.ts +499 -0
  69. package/.framework/functions/cil-validate.ts +469 -0
  70. package/.framework/functions/debug-auth.ts +73 -53
  71. package/.framework/functions/deliveries.ts +117 -0
  72. package/.framework/functions/embeddings.ts +528 -516
  73. package/.framework/functions/eval-harness.ts +158 -106
  74. package/.framework/functions/integration-outbound.ts +311 -0
  75. package/.framework/functions/integration-routes.ts +240 -466
  76. package/.framework/functions/integrations.ts +132 -140
  77. package/.framework/functions/invites.ts +401 -201
  78. package/.framework/functions/item-progress.ts +113 -53
  79. package/.framework/functions/kb.ts +28 -0
  80. package/.framework/functions/logs.ts +239 -222
  81. package/.framework/functions/observability.ts +308 -259
  82. package/.framework/functions/pipeline-executions.ts +179 -235
  83. package/.framework/functions/pipelines.ts +163 -205
  84. package/.framework/functions/prompt-configs.ts +109 -156
  85. package/.framework/functions/roles.ts +97 -147
  86. package/.framework/functions/system-cron.ts +132 -218
  87. package/.framework/functions/system.ts +112 -52
  88. package/.framework/functions/tests.ts +32 -28
  89. package/.framework/functions/timers.ts +162 -210
  90. package/.framework/functions/triggers.ts +282 -346
  91. package/.framework/functions/types.ts +283 -400
  92. package/.framework/migrations/001_core_schema.sql +630 -0
  93. package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
  94. package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
  95. package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
  96. package/.framework/migrations/005_core_observability.sql +598 -0
  97. package/.framework/migrations/006_security_hardening_rls.sql +97 -0
  98. package/.framework/migrations/007_kb_article_list.sql +237 -0
  99. package/.framework/migrations/008_items_target.sql +8 -0
  100. package/.framework/migrations/009_roles_slug_unique.sql +11 -0
  101. package/.framework/public/spine.config.json +91 -0
  102. package/.framework/scripts/app-install-cli.ts +112 -98
  103. package/.framework/src/App.tsx +67 -20
  104. package/.framework/src/apps/admin/index.tsx +97 -27
  105. package/.framework/src/components/AppWrapper.tsx +50 -11
  106. package/.framework/src/components/CustomAppLoader.tsx +131 -22
  107. package/.framework/src/components/admin/AdminListPage.tsx +75 -8
  108. package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
  109. package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
  110. package/.framework/src/components/admin/PageInstructions.tsx +49 -0
  111. package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
  112. package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
  113. package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
  114. package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
  115. package/.framework/src/components/app-sidebar.tsx +50 -3
  116. package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
  117. package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
  118. package/.framework/src/components/kb/KBArticleList.tsx +557 -0
  119. package/.framework/src/components/layout/AppShell.tsx +54 -16
  120. package/.framework/src/components/layout/Header.tsx +50 -38
  121. package/.framework/src/components/layout/Layout.tsx +48 -32
  122. package/.framework/src/components/layout/Sidebar.tsx +167 -16
  123. package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
  124. package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
  125. package/.framework/src/components/runtime/DataFilters.tsx +78 -1
  126. package/.framework/src/components/runtime/DataHeader.tsx +32 -29
  127. package/.framework/src/components/runtime/DataListPage.tsx +161 -2
  128. package/.framework/src/components/runtime/DataStats.tsx +48 -1
  129. package/.framework/src/components/runtime/DataTable.tsx +153 -1
  130. package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
  131. package/.framework/src/components/runtime/index.ts +16 -9
  132. package/.framework/src/components/search-form.tsx +33 -1
  133. package/.framework/src/components/shared/AgentView.tsx +48 -51
  134. package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
  135. package/.framework/src/components/shared/InviteManager.tsx +113 -52
  136. package/.framework/src/components/shared/MemberList.tsx +182 -0
  137. package/.framework/src/components/shared/SchemaFields.tsx +96 -78
  138. package/.framework/src/components/ui/DataTable.tsx +86 -56
  139. package/.framework/src/components/ui/Form.tsx +188 -109
  140. package/.framework/src/components/ui/ItemCard.tsx +86 -58
  141. package/.framework/src/components/ui/ItemListView.tsx +88 -61
  142. package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
  143. package/.framework/src/components/ui/Modal.tsx +81 -4
  144. package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
  145. package/.framework/src/components/ui/accordion.tsx +97 -5
  146. package/.framework/src/components/ui/alert-dialog.tsx +225 -13
  147. package/.framework/src/components/ui/alert.tsx +113 -6
  148. package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
  149. package/.framework/src/components/ui/avatar.tsx +129 -7
  150. package/.framework/src/components/ui/badge.tsx +65 -3
  151. package/.framework/src/components/ui/breadcrumb.tsx +145 -8
  152. package/.framework/src/components/ui/button-group.tsx +81 -4
  153. package/.framework/src/components/ui/button.tsx +49 -2
  154. package/.framework/src/components/ui/calendar.tsx +65 -3
  155. package/.framework/src/components/ui/card.tsx +145 -8
  156. package/.framework/src/components/ui/carousel.tsx +129 -7
  157. package/.framework/src/components/ui/chart.tsx +161 -10
  158. package/.framework/src/components/ui/checkbox.tsx +49 -2
  159. package/.framework/src/components/ui/collapsible.tsx +81 -5
  160. package/.framework/src/components/ui/combobox.tsx +305 -20
  161. package/.framework/src/components/ui/command.tsx +177 -11
  162. package/.framework/src/components/ui/context-menu.tsx +273 -17
  163. package/.framework/src/components/ui/dialog.tsx +209 -27
  164. package/.framework/src/components/ui/direction.tsx +64 -4
  165. package/.framework/src/components/ui/drawer.tsx +193 -12
  166. package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
  167. package/.framework/src/components/ui/empty.tsx +145 -9
  168. package/.framework/src/components/ui/field.tsx +209 -14
  169. package/.framework/src/components/ui/hover-card.tsx +81 -4
  170. package/.framework/src/components/ui/input-group.tsx +162 -10
  171. package/.framework/src/components/ui/input-otp.tsx +98 -5
  172. package/.framework/src/components/ui/input.tsx +49 -2
  173. package/.framework/src/components/ui/item.tsx +225 -13
  174. package/.framework/src/components/ui/kbd.tsx +65 -3
  175. package/.framework/src/components/ui/label.tsx +49 -2
  176. package/.framework/src/components/ui/menubar.tsx +289 -17
  177. package/.framework/src/components/ui/native-select.tsx +97 -5
  178. package/.framework/src/components/ui/navigation-menu.tsx +177 -10
  179. package/.framework/src/components/ui/pagination.tsx +161 -9
  180. package/.framework/src/components/ui/popover.tsx +145 -8
  181. package/.framework/src/components/ui/progress.tsx +49 -2
  182. package/.framework/src/components/ui/radio-group.tsx +65 -3
  183. package/.framework/src/components/ui/resizable.tsx +82 -4
  184. package/.framework/src/components/ui/scroll-area.tsx +65 -3
  185. package/.framework/src/components/ui/select.tsx +193 -11
  186. package/.framework/src/components/ui/separator.tsx +49 -2
  187. package/.framework/src/components/ui/sheet.tsx +193 -11
  188. package/.framework/src/components/ui/sidebar.tsx +481 -38
  189. package/.framework/src/components/ui/skeleton.tsx +49 -2
  190. package/.framework/src/components/ui/slider.tsx +49 -2
  191. package/.framework/src/components/ui/sonner.tsx +49 -2
  192. package/.framework/src/components/ui/spinner.tsx +49 -2
  193. package/.framework/src/components/ui/switch.tsx +49 -2
  194. package/.framework/src/components/ui/table-primitives.tsx +160 -33
  195. package/.framework/src/components/ui/table.tsx +161 -9
  196. package/.framework/src/components/ui/tabs.tsx +113 -6
  197. package/.framework/src/components/ui/textarea.tsx +49 -2
  198. package/.framework/src/components/ui/toggle-group.tsx +81 -4
  199. package/.framework/src/components/ui/toggle.tsx +65 -3
  200. package/.framework/src/components/ui/tooltip.tsx +97 -5
  201. package/.framework/src/contexts/AppContext.tsx +129 -45
  202. package/.framework/src/contexts/AuthContext.tsx +187 -144
  203. package/.framework/src/hooks/use-mobile.ts +48 -2
  204. package/.framework/src/hooks/useApi.ts +208 -159
  205. package/.framework/src/hooks/useAppPath.ts +32 -23
  206. package/.framework/src/hooks/useApps.ts +51 -20
  207. package/.framework/src/hooks/useEntityList.ts +32 -78
  208. package/.framework/src/hooks/useEntityRecord.ts +35 -96
  209. package/.framework/src/hooks/useForm.ts +32 -92
  210. package/.framework/src/hooks/useListSchema.ts +49 -78
  211. package/.framework/src/hooks/useSchemaRecord.ts +114 -105
  212. package/.framework/src/lib/api.ts +195 -116
  213. package/.framework/src/lib/supabase.ts +98 -64
  214. package/.framework/src/lib/utils.ts +287 -151
  215. package/.framework/src/main.tsx +49 -3
  216. package/.framework/src/pages/DashboardPage.tsx +52 -29
  217. package/.framework/src/pages/NotFoundPage.tsx +32 -10
  218. package/.framework/src/pages/SetupPage.tsx +32 -12
  219. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
  220. package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
  221. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
  222. package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
  223. package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
  224. package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
  225. package/.framework/src/pages/admin/AppsPage.tsx +49 -2
  226. package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
  227. package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
  228. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
  229. package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
  230. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
  231. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
  232. package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
  233. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
  234. package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
  235. package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
  236. package/.framework/src/pages/admin/LogsPage.tsx +422 -75
  237. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
  238. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
  239. package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
  240. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
  241. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
  242. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
  243. package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
  244. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
  245. package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
  246. package/.framework/src/pages/admin/RolesPage.tsx +171 -18
  247. package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
  248. package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
  249. package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
  250. package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
  251. package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
  252. package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
  253. package/.framework/src/pages/admin/TimersPage.tsx +48 -20
  254. package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
  255. package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
  256. package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
  257. package/.framework/src/pages/admin/TypesPage.tsx +48 -20
  258. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
  259. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
  260. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
  261. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
  262. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
  263. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
  264. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
  265. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
  266. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
  267. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
  268. package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
  269. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
  270. package/.framework/src/pages/auth/LoginPage.tsx +50 -23
  271. package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
  272. package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
  273. package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
  274. package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
  275. package/.framework/src/types/auth.ts +128 -71
  276. package/.framework/src/types/types.ts +373 -157
  277. package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
  278. package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
  279. package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
  280. package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
  281. package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
  282. package/docs/README.md +9 -130
  283. package/netlify.toml +5 -2
  284. package/package.json +13 -25
  285. package/scripts/kb-ingest-docs.ts +203 -0
  286. package/spine.config.json +65 -4
  287. package/.framework/migrations/001_schema.sql +0 -425
  288. package/.framework/migrations/002_rls_and_functions.sql +0 -763
  289. package/.framework/migrations/004_enhancements.sql +0 -43
  290. package/.framework/migrations/005_auth_user_trigger.sql +0 -105
  291. package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
  292. package/.framework/migrations/016_invites.sql +0 -27
  293. package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
  294. package/.framework/migrations/018_apps_required_roles.sql +0 -17
  295. package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
  296. package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
  297. package/.framework/migrations/021_app_domains.sql +0 -112
  298. package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
  299. package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
  300. package/.framework/migrations/024_messages_account_id.sql +0 -9
  301. package/.framework/migrations/025_retype_threads_messages.sql +0 -240
  302. package/.framework/migrations/026_fix_rls_policies.sql +0 -47
  303. package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
  304. package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
  305. package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
  306. package/.framework/migrations/030_messages_updated_by.sql +0 -6
  307. package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
  308. package/docs/APPS.md +0 -232
  309. package/docs/ARCHITECTURE.md +0 -246
  310. package/docs/INSTALL.md +0 -155
  311. package/docs/design-schema-spec.md +0 -1837
  312. package/docs/dev-guides/01-create-an-app.md +0 -183
  313. package/docs/dev-guides/02-create-a-function.md +0 -178
  314. package/docs/dev-guides/03-create-a-component.md +0 -178
  315. package/docs/dev-guides/04-webhook-handlers.md +0 -129
  316. package/docs/dev-guides/05-testing.md +0 -168
  317. package/docs/dev-guides/README.md +0 -40
  318. package/docs/npm-packages-plan.md +0 -288
  319. package/docs/spine-core--api-reference.md +0 -822
@@ -1,110 +1,97 @@
1
- /**
2
- * @module triggers
3
- * @audience core-contributor
4
- * @layer api-handler
5
- * @stability stable
6
- *
7
- * CRUD API for the `triggers` table, plus execution history access.
8
- * Triggers bind event types to pipelines. When an event fires, the trigger
9
- * engine evaluates active triggers via `_shared/trigger-engine.ts`.
10
- *
11
- * **Routed by:** `GET/POST/PATCH/DELETE /.netlify/functions/triggers`
12
- *
13
- * **Actions:**
14
- * | method | ?action | handler |
15
- * |--------|------------|---------------|
16
- * | GET | by-event | listByEvent |
17
- * | GET | executions | getExecutions |
18
- * | POST | toggle | toggle |
19
- * | GET | ?id | get |
20
- * | GET | (default) | list |
21
- * | POST | — | create |
22
- * | PATCH | — | update |
23
- * | DELETE | — | remove (soft) |
24
- *
25
- * **Authorization:** `create` requires system admin OR first-surface `canCreate`
26
- * permission. All other operations use `ctx.db` RLS.
27
- *
28
- * Also exports `AGENT_EVENT_TYPES` constant and `getAgentEventTypes()` helper
29
- * for referencing well-known agent event slugs in trigger configuration.
30
- *
31
- * @seeAlso trigger-engine.ts (checkAndFireTriggers — runtime evaluation)
32
- * @seeAlso pipelines.ts (pipeline_id FK on triggers)
33
- * @seeAlso audit.ts (emitLog for trigger.* events)
34
- */
35
1
 
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+ // CIL[BEGIN chunk_id="triggers_module_header_1783931956" type="module_header" identity="module_header" parent="none"]
12
+ // CIL[DOC-MACRO purpose="Import dependencies for triggers CRUD API"]
13
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide middleware, database, audit and permissions utilities"]
14
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/functions/_shared/schema-utils.ts\",\"custom/apps/portal/pages/ContentPage.tsx\",\"\\\"]"]
15
+ // CIL[DOC-CONTRACT inputs="none" outputs="imported modules and permissions instance" errors="none" side_effects="none"]
16
+ // CIL[DOC-MICRO logic_summary="Imports all required modules for trigger management operations" notable_decisions="[]"]
17
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
18
+ // CIL[DOC-STABILITY stability="stable"]
19
+ // CIL[DOC-AUDIENCE audience="developer"]
20
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/principal.ts, .framework/functions/_shared/schema-utils.ts, custom/apps/portal/pages/ContentPage.tsx, \."]
21
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
22
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
23
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
24
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
25
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
36
26
  import { createHandler } from './_shared/middleware'
37
- import { joins } from './_shared/db'
27
+ import { joins, adminDb } from './_shared/db'
38
28
  import { emitLog } from './_shared/audit'
39
29
  import { PermissionEngine, sanitizeRecordData } from './_shared/permissions'
30
+ import { isSystemAdmin } from './_shared/principal'
40
31
 
41
32
  const permissions = PermissionEngine as any
42
-
43
- // ─── CONSTANTS ─────────────────────────────────────────────────────────────────
44
-
45
- /**
46
- * Well-known agent event type slugs for use in trigger `event_type` config.
47
- * These are emitted by `agent-runner.ts` during inference, tool dispatch,
48
- * and escalation workflows.
49
- */
33
+ // CIL[END chunk_id="triggers_module_header_1783931956"]
34
+ // CIL[BEGIN chunk_id="triggers_constants_1783931956" type="constants" identity="constants" parent="none"]
35
+ // CIL[DOC-MACRO purpose="Define agent event type constants"]
36
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide well-known event type slugs for triggers"]
37
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
38
+ // CIL[DOC-CONTRACT inputs="none" outputs="event type constants object" errors="none" side_effects="none"]
39
+ // CIL[DOC-MICRO logic_summary="Defines constants for inference, tool, conversation, and escalation events" notable_decisions="[]"]
40
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
41
+ // CIL[DOC-STABILITY stability="stable"]
42
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/schema-utils.ts, .framework/functions/_shared/principal.ts, \."]
43
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
44
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
45
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
46
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
47
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
50
48
  export const AGENT_EVENT_TYPES = {
51
- // Inference events
52
49
  INFERENCE_COMPLETED: 'agent.inference.completed',
53
50
  INFERENCE_FAILED: 'agent.inference.failed',
54
51
  LOW_CONFIDENCE: 'agent.inference.low_confidence',
55
-
56
- // Tool events
57
52
  TOOL_CALLED: 'agent.tool.called',
58
53
  TOOL_COMPLETED: 'agent.tool.completed',
59
54
  TOOL_FAILED: 'agent.tool.failed',
60
-
61
- // Conversation events
62
55
  MESSAGE_RECEIVED: 'agent.message.received',
63
56
  MESSAGE_SENT: 'agent.message.sent',
64
57
  THREAD_CREATED: 'agent.thread.created',
65
-
66
- // Escalation events
67
58
  ESCALATION_TRIGGERED: 'agent.escalation.triggered',
68
59
  ESCALATION_RESOLVED: 'agent.escalation.resolved',
69
60
  HUMAN_HANDOFF: 'agent.human.handoff'
70
61
  } as const
71
-
72
- // ─── CHUNK_START: TRIGGERS_GET_AGENT_EVENT_TYPES ──────────────────────────────────────────────
73
- /**
74
- * @chunk-id TRIGGERS_GET_AGENT_EVENT_TYPES_1_0_0
75
- * @version 1.0.0
76
- * @hash ef90bc655886f31a4417d82b5bac35cfece4ba4e03770d71a449fc36e4cecb24
77
- * @macro Agent Event Types Helper
78
- * @micro Returns array of all agent event type slugs for UI dropdowns
79
- * @inputs None
80
- * @outputs string[] — All AGENT_EVENT_TYPES values
81
- * @depends-on [AGENT_EVENT_TYPES constant]
82
- * @depended-by [UI components, trigger configuration forms]
83
- * @side-effects [None]
84
- * @tags triggers, helper, event-types, ui
85
- */
62
+ // CIL[END chunk_id="triggers_constants_1783931956"]
63
+ // CIL[BEGIN chunk_id="triggers_getagenteventtypes_1783931956" type="function" identity="getAgentEventTypes" parent="none"]
64
+ // CIL[DOC-MACRO purpose="Return array of agent event type slugs"]
65
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide event type options for UI dropdowns"]
66
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\".framework/functions/_shared/schema-utils.ts\",\"\\\"]"]
67
+ // CIL[DOC-CONTRACT inputs="none" outputs="string array of event types" errors="none" side_effects="none"]
68
+ // CIL[DOC-MICRO logic_summary="Extracts values from AGENT_EVENT_TYPES constant for UI consumption" notable_decisions="[]"]
69
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
70
+ // CIL[DOC-STABILITY stability="stable"]
71
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/functions/custom_cil-embeddings.ts, .framework/functions/_shared/schema-utils.ts, \."]
72
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
73
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
74
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
75
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
76
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
86
77
  export function getAgentEventTypes(): string[] {
87
78
  return Object.values(AGENT_EVENT_TYPES)
88
79
  }
89
- // ─── CHUNK_END: TRIGGERS_GET_AGENT_EVENT_TYPES ────────────────────────────────────────────────
90
-
91
- // ─── HANDLERS ─────────────────────────────────────────────────────────────────
92
-
93
- // ─── CHUNK_START: TRIGGERS_LIST_BY_EVENT ──────────────────────────────────────────────
94
- /**
95
- * @chunk-id TRIGGERS_LIST_BY_EVENT_1_0_0
96
- * @version 1.0.0
97
- * @hash c6d2127dc2ced6d0618b338d227d8823b699b86977fc14ce455a27a29a2b1fa5
98
- * @macro Triggers by Event List Handler
99
- * @micro Lists triggers filtered by event type with sanitization
100
- * @inputs ctx: CoreContext — Request context with principal and database
101
- * @inputs body: any — Request body (unused for GET)
102
- * @outputs Array of sanitized trigger records with app/createdBy joins
103
- * @depends-on [createHandler, joins, sanitizeRecordData]
104
- * @depended-by [Netlify function routing]
105
- * @side-effects [DB queries, permission sanitization]
106
- * @tags triggers, list, event-type, crud
107
- */
80
+ // CIL[END chunk_id="triggers_getagenteventtypes_1783931956"]
81
+ // CIL[BEGIN chunk_id="triggers_listbyevent_1783931956" type="function" identity="listByEvent" parent="none"]
82
+ // CIL[DOC-MACRO purpose="List triggers filtered by event type"]
83
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle event-specific trigger listing with sanitization"]
84
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\"\\\"]"]
85
+ // CIL[DOC-CONTRACT inputs="none" outputs="sanitized trigger array" errors="throws" side_effects="database queries"]
86
+ // CIL[DOC-MICRO logic_summary="Filters triggers by event_type and app, sanitizes based on permissions" notable_decisions="[]"]
87
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
88
+ // CIL[DOC-STABILITY stability="stable"]
89
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/functions/_shared/schema-utils.ts, \."]
90
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
91
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
92
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
93
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
94
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
108
95
  export const listByEvent = createHandler(async (ctx, body) => {
109
96
  const { event_type, app_id, include_inactive } = ctx.query || {}
110
97
 
@@ -116,9 +103,11 @@ export const listByEvent = createHandler(async (ctx, body) => {
116
103
  throw new Error('Account context required')
117
104
  }
118
105
 
119
- let query = ctx.db
106
+ const db = isSystemAdmin(ctx.principal) ? adminDb : ctx.db
107
+
108
+ let query = db
120
109
  .from('triggers')
121
- .select(`*, ${joins.app}, ${joins.createdBy}`)
110
+ .select('*')
122
111
  .eq('event_type', event_type)
123
112
  .order('name')
124
113
 
@@ -133,7 +122,6 @@ export const listByEvent = createHandler(async (ctx, body) => {
133
122
 
134
123
  if (err) throw err
135
124
 
136
- // Sanitize each record based on role permissions
137
125
  const sanitized = []
138
126
  for (const trigger of data || []) {
139
127
  sanitized.push(await sanitizeRecordData(ctx, trigger, 'trigger'))
@@ -141,23 +129,21 @@ export const listByEvent = createHandler(async (ctx, body) => {
141
129
 
142
130
  return sanitized
143
131
  })
144
- // ─── CHUNK_END: TRIGGERS_LIST_BY_EVENT ────────────────────────────────────────────────
145
-
146
- // ─── CHUNK_START: TRIGGERS_LIST ──────────────────────────────────────────────
147
- /**
148
- * @chunk-id TRIGGERS_LIST_1_0_0
149
- * @version 1.0.0
150
- * @hash b65a861b2a1b7c63a6a2bbe395dfb6138fe00e0517a0eb40f012c2317fbcf45f
151
- * @macro Triggers List Handler
152
- * @micro Lists triggers with filtering and sanitization
153
- * @inputs ctx: CoreContext — Request context with principal and database
154
- * @inputs body: any — Request body (unused for GET)
155
- * @outputs Array of sanitized trigger records with app/createdBy joins
156
- * @depends-on [createHandler, joins, sanitizeRecordData]
157
- * @depended-by [Netlify function routing]
158
- * @side-effects [DB queries, permission sanitization]
159
- * @tags triggers, list, crud, filtering
160
- */
132
+ // CIL[END chunk_id="triggers_listbyevent_1783931956"]
133
+ // CIL[BEGIN chunk_id="triggers_list_1783931956" type="function" identity="list" parent="none"]
134
+ // CIL[DOC-MACRO purpose="List all triggers with filtering and sanitization"]
135
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle general trigger listing with authentication"]
136
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[]"]
137
+ // CIL[DOC-CONTRACT inputs="none" outputs="sanitized trigger array" errors="throws" side_effects="database queries"]
138
+ // CIL[DOC-MICRO logic_summary="Filters triggers by app/event_type, sanitizes based on permissions" notable_decisions="[]"]
139
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
140
+ // CIL[DOC-STABILITY stability="stable"]
141
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
142
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
143
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
144
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
145
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
146
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
161
147
  export const list = createHandler(async (ctx, body) => {
162
148
  const { app_id, event_type, include_inactive } = ctx.query || {}
163
149
 
@@ -165,10 +151,11 @@ export const list = createHandler(async (ctx, body) => {
165
151
  throw new Error('Account context required')
166
152
  }
167
153
 
168
- // RLS automatically filters to accessible accounts
169
- let query = ctx.db
154
+ const db = isSystemAdmin(ctx.principal) ? adminDb : ctx.db
155
+
156
+ let query = db
170
157
  .from('triggers')
171
- .select(`*, ${joins.app}, ${joins.createdBy}`)
158
+ .select('*')
172
159
  .order('name')
173
160
 
174
161
  if (app_id) {
@@ -185,7 +172,6 @@ export const list = createHandler(async (ctx, body) => {
185
172
 
186
173
  if (err) throw err
187
174
 
188
- // Sanitize each record based on role permissions
189
175
  const sanitized = []
190
176
  for (const trigger of data || []) {
191
177
  sanitized.push(await sanitizeRecordData(ctx, trigger, 'trigger'))
@@ -193,23 +179,21 @@ export const list = createHandler(async (ctx, body) => {
193
179
 
194
180
  return sanitized
195
181
  })
196
- // ─── CHUNK_END: TRIGGERS_LIST ────────────────────────────────────────────────
197
-
198
- // ─── CHUNK_START: TRIGGERS_GET ──────────────────────────────────────────────
199
- /**
200
- * @chunk-id TRIGGERS_GET_1_0_0
201
- * @version 1.0.0
202
- * @hash 106ae81cd0528145f976f7c8e0cc16cacaf32468d0e2950a16b8f167bb88143d
203
- * @macro Trigger Get Handler
204
- * @micro Returns single trigger record with joins and sanitization
205
- * @inputs ctx: CoreContext — Request context with principal and database
206
- * @inputs body: any — Request body (unused for GET)
207
- * @outputs Sanitized trigger record with app/createdBy joins
208
- * @depends-on [createHandler, joins, sanitizeRecordData]
209
- * @depended-by [Netlify function routing]
210
- * @side-effects [DB single row query, permission sanitization]
211
- * @tags triggers, get, crud, single-record
212
- */
182
+ // CIL[END chunk_id="triggers_list_1783931956"]
183
+ // CIL[BEGIN chunk_id="triggers_get_1783931956" type="function" identity="get" parent="none"]
184
+ // CIL[DOC-MACRO purpose="Get single trigger by ID with sanitization"]
185
+ // CIL[DOC-RESPONSIBILITY responsibility="Retrieve and sanitize individual trigger records"]
186
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[]"]
187
+ // CIL[DOC-CONTRACT inputs="none" outputs="sanitized trigger object" errors="throws" side_effects="database query"]
188
+ // CIL[DOC-MICRO logic_summary="Queries trigger by ID with app/createdBy joins, sanitizes based on permissions" notable_decisions="[]"]
189
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
190
+ // CIL[DOC-STABILITY stability="stable"]
191
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
192
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
193
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
194
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
195
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
196
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
213
197
  export const get = createHandler(async (ctx, body) => {
214
198
  const { id } = ctx.query || {}
215
199
 
@@ -217,46 +201,88 @@ export const get = createHandler(async (ctx, body) => {
217
201
  throw new Error('Trigger ID is required')
218
202
  }
219
203
 
220
- const { data, error: err } = await ctx.db
204
+ const db = isSystemAdmin(ctx.principal) ? adminDb : ctx.db
205
+
206
+ const { data, error: err } = await db
221
207
  .from('triggers')
222
- .select(`*, ${joins.app}, ${joins.createdBy}`)
208
+ .select('*')
223
209
  .eq('id', id)
224
210
  .single()
225
211
 
226
212
  if (err) throw err
227
213
 
228
- // Sanitize based on role permissions
229
214
  return await sanitizeRecordData(ctx, data, 'trigger')
230
215
  })
231
- // ─── CHUNK_END: TRIGGERS_GET ────────────────────────────────────────────────
232
-
233
- // ─── CHUNK_START: TRIGGERS_CREATE ──────────────────────────────────────────────
234
- /**
235
- * @chunk-id TRIGGERS_CREATE_1_0_0
236
- * @version 1.0.0
237
- * @hash 4411a0111e10158d29446e4b989fbccaf1e55057b4f809129eed33f93ee21901
238
- * @macro Trigger Create Handler
239
- * @micro Creates trigger with permission validation and audit logging
240
- * @inputs ctx: CoreContext — Request context with principal and database
241
- * @inputs body: object — Trigger data including name, trigger_type, and optional fields
242
- * @outputs Inserted trigger record
243
- * @depends-on [createHandler, PermissionEngine, emitLog]
244
- * @depended-by [Netlify function routing]
245
- * @side-effects [DB insert, permission validation, audit logging]
246
- * @tags triggers, create, crud, permissions, audit
247
- */
216
+ // CIL[END chunk_id="triggers_get_1783931956"]
217
+ // CIL[BEGIN chunk_id="triggers_getexecutions_1783931956" type="function" identity="getExecutions" parent="none"]
218
+ // CIL[DOC-MACRO purpose="Get execution history for a specific trigger"]
219
+ // CIL[DOC-RESPONSIBILITY responsibility="Retrieve trigger execution records with pagination"]
220
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[]"]
221
+ // CIL[DOC-CONTRACT inputs="none" outputs="execution records array" errors="throws" side_effects="database queries"]
222
+ // CIL[DOC-MICRO logic_summary="Queries trigger_executions table with pagination and ordering" notable_decisions="[]"]
223
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
224
+ // CIL[DOC-STABILITY stability="stable"]
225
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
226
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
227
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
228
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
229
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
230
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
231
+ export const getExecutions = createHandler(async (ctx, body) => {
232
+ const { trigger_id, limit = '50', offset = '0' } = ctx.query || {}
233
+
234
+ if (!trigger_id) {
235
+ throw new Error('trigger_id is required')
236
+ }
237
+
238
+ if (!ctx.accountId) {
239
+ throw new Error('Account context required')
240
+ }
241
+
242
+ const { data, error: err } = await ctx.db
243
+ .from('trigger_executions')
244
+ .select('*')
245
+ .eq('trigger_id', trigger_id)
246
+ .order('executed_at', { ascending: false })
247
+ .range(parseInt(offset), parseInt(offset) + parseInt(limit) - 1)
248
+
249
+ if (err) throw err
250
+
251
+ return data || []
252
+ })
253
+ // CIL[END chunk_id="triggers_getexecutions_1783931956"]
254
+ // CIL[BEGIN chunk_id="triggers_create_1783931956" type="function" identity="create" parent="none"]
255
+ // CIL[DOC-MACRO purpose="Create new trigger with permissions and audit"]
256
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle trigger creation with validation and logging"]
257
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[]"]
258
+ // CIL[DOC-CONTRACT inputs="none" outputs="created trigger record" errors="throws" side_effects="database insert, audit log"]
259
+ // CIL[DOC-MICRO logic_summary="Validates permissions, creates trigger with defaults, emits audit log" notable_decisions="[]"]
260
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
261
+ // CIL[DOC-STABILITY stability="stable"]
262
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
263
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
264
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
265
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
266
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
267
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
248
268
  export const create = createHandler(async (ctx, body) => {
249
- const { app_id, name, description, trigger_type, event_type, config, pipeline_id, metadata, is_active } = body
269
+ const { app_id, name, description, event_type, pipeline_id, config, metadata } = body
250
270
 
251
- if (!name || !trigger_type) {
252
- throw new Error('name and trigger_type are required')
271
+ if (!name || !event_type || !pipeline_id) {
272
+ throw new Error('name, event_type, and pipeline_id are required')
253
273
  }
254
274
 
255
275
  if (!ctx.principal || ctx.principal.id === 'anonymous' || !ctx.accountId) {
256
276
  throw new Error('User context (person and account) required')
257
277
  }
258
278
 
259
- // Check create permissions
279
+ // NOTE: previously called `permissions.isSystemAdmin(ctx.principal)` (wrong
280
+ // argument shape — that method reads `ctx.principal.roles`, so passing the
281
+ // Principal itself made it always resolve false) followed by a
282
+ // `permissions.hasPermission(...)` call that doesn't exist on PermissionEngine,
283
+ // so this handler always threw at runtime. Use the same
284
+ // isSystemAdmin(ctx)-then-resolveFirstSurfacePermissions pattern as
285
+ // apps.ts/roles.ts/types.ts.
260
286
  if (!permissions.isSystemAdmin(ctx)) {
261
287
  const perms = await permissions.resolveFirstSurfacePermissions(
262
288
  ctx.principal.id,
@@ -264,7 +290,7 @@ export const create = createHandler(async (ctx, body) => {
264
290
  'trigger',
265
291
  'create'
266
292
  )
267
-
293
+
268
294
  if (!perms.canCreate) {
269
295
  throw new Error('Insufficient permissions to create triggers')
270
296
  }
@@ -273,17 +299,15 @@ export const create = createHandler(async (ctx, body) => {
273
299
  const { data, error: err } = await ctx.db
274
300
  .from('triggers')
275
301
  .insert({
276
- app_id,
302
+ app_id: app_id || null,
303
+ account_id: ctx.accountId,
277
304
  name,
278
- description,
279
- trigger_type,
305
+ description: description || null,
280
306
  event_type,
307
+ pipeline_id,
281
308
  config: config || {},
282
- pipeline_id: pipeline_id || null,
283
309
  metadata: metadata || {},
284
- is_active: is_active ?? true,
285
- created_by: ctx.principal.id,
286
- account_id: ctx.accountId
310
+ created_by: ctx.principal.id
287
311
  })
288
312
  .select()
289
313
  .single()
@@ -292,65 +316,43 @@ export const create = createHandler(async (ctx, body) => {
292
316
 
293
317
  await emitLog(ctx, 'trigger.created',
294
318
  { type: 'trigger', id: data.id },
295
- { after: data }
319
+ { after: { name, event_type } }
296
320
  )
297
321
 
298
322
  return data
299
323
  })
300
- // ─── CHUNK_END: TRIGGERS_CREATE ────────────────────────────────────────────────
301
-
302
- // ─── CHUNK_START: TRIGGERS_UPDATE ──────────────────────────────────────────────
303
- /**
304
- * @chunk-id TRIGGERS_UPDATE_1_0_0
305
- * @version 1.0.0
306
- * @hash 8c7ee9556f7e18c9b63746baaf49ae73a4526735227a828bc77aaf518d4796fb
307
- * @macro Trigger Update Handler
308
- * @micro Updates trigger with audit logging and before/after state
309
- * @inputs ctx: CoreContext — Request context with principal and database
310
- * @inputs body: object — Trigger updates including id and updatable fields
311
- * @outputs Updated trigger record
312
- * @depends-on [createHandler, emitLog]
313
- * @depended-by [Netlify function routing]
314
- * @side-effects [DB update, audit logging]
315
- * @tags triggers, update, crud, audit
316
- */
324
+ // CIL[END chunk_id="triggers_create_1783931956"]
325
+ // CIL[BEGIN chunk_id="triggers_update_1783931956" type="function" identity="update" parent="none"]
326
+ // CIL[DOC-MACRO purpose="Update existing trigger with field allowlist"]
327
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle trigger updates with validation and audit logging"]
328
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[]"]
329
+ // CIL[DOC-CONTRACT inputs="none" outputs="updated trigger record" errors="throws" side_effects="database update, audit log"]
330
+ // CIL[DOC-MICRO logic_summary="Updates only allowed fields, adds timestamp, emits audit log" notable_decisions="[]"]
331
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
332
+ // CIL[DOC-STABILITY stability="stable"]
333
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
334
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
335
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
336
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
337
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
338
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
317
339
  export const update = createHandler(async (ctx, body) => {
318
340
  const id = body?.id || ctx.query?.id
319
- const { id: _bodyId, app_id, name, description, trigger_type, event_type, config, pipeline_id, metadata, is_active } = body || {}
341
+ const { id: _bodyId, ...updates } = body || {}
320
342
 
321
343
  if (!id) {
322
344
  throw new Error('Trigger ID is required')
323
345
  }
324
346
 
325
- if (!ctx.principal || ctx.principal.id === 'anonymous' || !ctx.accountId) {
326
- throw new Error('User context (person and account) required')
327
- }
328
-
329
- // Get current state for audit - RLS will filter to accessible triggers
330
- const { data: current } = await ctx.db
331
- .from('triggers')
332
- .select('*')
333
- .eq('id', id)
334
- .single()
335
-
336
- if (!current) {
337
- throw new Error('Trigger not found')
347
+ const allowed = ['name', 'description', 'config', 'pipeline_id', 'metadata', 'is_active', 'event_type', 'app_id']
348
+ const updateData: Record<string, any> = { updated_at: new Date().toISOString() }
349
+ for (const key of allowed) {
350
+ if (updates[key] !== undefined) updateData[key] = updates[key]
338
351
  }
339
352
 
340
353
  const { data, error: err } = await ctx.db
341
354
  .from('triggers')
342
- .update({
343
- app_id,
344
- name,
345
- description,
346
- trigger_type,
347
- event_type,
348
- config,
349
- pipeline_id,
350
- metadata,
351
- is_active,
352
- updated_at: new Date().toISOString()
353
- })
355
+ .update(updateData)
354
356
  .eq('id', id)
355
357
  .select()
356
358
  .single()
@@ -359,28 +361,26 @@ export const update = createHandler(async (ctx, body) => {
359
361
 
360
362
  await emitLog(ctx, 'trigger.updated',
361
363
  { type: 'trigger', id },
362
- { before: current, after: data }
364
+ { after: updateData }
363
365
  )
364
366
 
365
367
  return data
366
368
  })
367
- // ─── CHUNK_END: TRIGGERS_UPDATE ────────────────────────────────────────────────
368
-
369
- // ─── CHUNK_START: TRIGGERS_REMOVE ──────────────────────────────────────────────
370
- /**
371
- * @chunk-id TRIGGERS_REMOVE_1_0_0
372
- * @version 1.0.0
373
- * @hash d25952bb0f5bcd15fb5272f6d54a975b8a0f1cc6b85bc514b0e91df2fcb35ad5
374
- * @macro Trigger Remove Handler
375
- * @micro Soft-deletes trigger with audit logging and before/after state
376
- * @inputs ctx: CoreContext — Request context with principal and database
377
- * @inputs body: object — Trigger id for deletion
378
- * @outputs Updated trigger record with is_active: false
379
- * @depends-on [createHandler, emitLog]
380
- * @depended-by [Netlify function routing]
381
- * @side-effects [DB soft delete, audit logging]
382
- * @tags triggers, remove, soft-delete, audit
383
- */
369
+ // CIL[END chunk_id="triggers_update_1783931956"]
370
+ // CIL[BEGIN chunk_id="triggers_remove_1783931956" type="function" identity="remove" parent="none"]
371
+ // CIL[DOC-MACRO purpose="Soft delete trigger with validation and audit"]
372
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle trigger deletion with safety checks and logging"]
373
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[]"]
374
+ // CIL[DOC-CONTRACT inputs="none" outputs="success indicator" errors="throws" side_effects="database update, audit log"]
375
+ // CIL[DOC-MICRO logic_summary="Validates trigger exists, soft deletes by setting is_active=false, emits audit log" notable_decisions="[]"]
376
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
377
+ // CIL[DOC-STABILITY stability="stable"]
378
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
379
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
380
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
381
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
382
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
383
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
384
384
  export const remove = createHandler(async (ctx, body) => {
385
385
  const id = body?.id || ctx.query?.id
386
386
 
@@ -388,68 +388,71 @@ export const remove = createHandler(async (ctx, body) => {
388
388
  throw new Error('Trigger ID is required')
389
389
  }
390
390
 
391
- if (!ctx.principal || ctx.principal.id === 'anonymous' || !ctx.accountId) {
392
- throw new Error('User context (person and account) required')
393
- }
394
-
395
- // Get current state for audit - RLS will filter to accessible triggers
396
- const { data: current } = await ctx.db
391
+ const { data: existing } = await ctx.db
397
392
  .from('triggers')
398
- .select('*')
393
+ .select('name, event_type')
399
394
  .eq('id', id)
400
395
  .single()
401
396
 
402
- if (!current) {
397
+ if (!existing) {
403
398
  throw new Error('Trigger not found')
404
399
  }
405
400
 
406
- const { data, error: err } = await ctx.db
401
+ const { error: err } = await ctx.db
407
402
  .from('triggers')
408
- .update({
403
+ .update({
409
404
  is_active: false,
410
405
  updated_at: new Date().toISOString()
411
406
  })
412
407
  .eq('id', id)
413
- .select()
414
- .single()
415
408
 
416
409
  if (err) throw err
417
410
 
418
- await emitLog(ctx, 'trigger.deleted',
419
- { type: 'trigger', id },
420
- { before: current, after: data }
411
+ await emitLog(ctx, 'trigger.deleted',
412
+ { type: 'trigger', id },
413
+ { before: { name: existing.name, event_type: existing.event_type } }
421
414
  )
422
415
 
423
- return data
416
+ return { success: true }
424
417
  })
425
- // ─── CHUNK_END: TRIGGERS_REMOVE ────────────────────────────────────────────────
426
-
427
- // ─── CHUNK_START: TRIGGERS_TOGGLE ──────────────────────────────────────────────
428
- /**
429
- * @chunk-id TRIGGERS_TOGGLE_1_0_0
430
- * @version 1.0.0
431
- * @hash 56add34659ac69c93b3570eb63f3bc3072d6a15083f79eaa7a9926f40f275373
432
- * @macro Trigger Toggle Handler
433
- * @micro Activates or deactivates trigger with audit logging
434
- * @inputs ctx: CoreContext — Request context with principal and database
435
- * @inputs body: object — Trigger id and is_active boolean
436
- * @outputs Updated trigger record
437
- * @depends-on [createHandler, emitLog]
438
- * @depended-by [Netlify function routing]
439
- * @side-effects [DB update, audit logging]
440
- * @tags triggers, toggle, state-transition, audit
441
- */
418
+ // CIL[END chunk_id="triggers_remove_1783931956"]
419
+ // CIL[BEGIN chunk_id="triggers_toggle_1783931956" type="function" identity="toggle" parent="none"]
420
+ // CIL[DOC-MACRO purpose="Toggle trigger active status with audit logging"]
421
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle trigger activation/deactivation with validation"]
422
+ // CIL[DOC-CONTEXT module="triggers" layer="api-handler" related_files="[]"]
423
+ // CIL[DOC-CONTRACT inputs="none" outputs="updated trigger record" errors="throws" side_effects="database update, audit log"]
424
+ // CIL[DOC-MICRO logic_summary="Flips is_active boolean, updates timestamp, emits audit log" notable_decisions="[]"]
425
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
426
+ // CIL[DOC-STABILITY stability="stable"]
427
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
428
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
429
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
430
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
431
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
432
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
442
433
  export const toggle = createHandler(async (ctx, body) => {
443
- const { id, is_active } = body
434
+ const id = body?.id || ctx.query?.id
435
+
436
+ if (!id) {
437
+ throw new Error('Trigger ID is required')
438
+ }
439
+
440
+ const { data: existing } = await ctx.db
441
+ .from('triggers')
442
+ .select('is_active, name')
443
+ .eq('id', id)
444
+ .single()
444
445
 
445
- if (!id || is_active === undefined) {
446
- throw new Error('Trigger ID and is_active are required')
446
+ if (!existing) {
447
+ throw new Error('Trigger not found')
447
448
  }
448
449
 
450
+ const newStatus = !existing.is_active
451
+
449
452
  const { data, error: err } = await ctx.db
450
453
  .from('triggers')
451
- .update({
452
- is_active,
454
+ .update({
455
+ is_active: newStatus,
453
456
  updated_at: new Date().toISOString()
454
457
  })
455
458
  .eq('id', id)
@@ -460,104 +463,37 @@ export const toggle = createHandler(async (ctx, body) => {
460
463
 
461
464
  await emitLog(ctx, 'trigger.toggled',
462
465
  { type: 'trigger', id },
463
- { after: { is_active } }
466
+ {
467
+ before: { is_active: existing.is_active },
468
+ after: { is_active: newStatus }
469
+ }
464
470
  )
465
471
 
466
472
  return data
467
473
  })
468
- // ─── CHUNK_END: TRIGGERS_TOGGLE ────────────────────────────────────────────────
469
-
470
- // ─── CHUNK_START: TRIGGERS_GET_EXECUTIONS ──────────────────────────────────────────────
471
- /**
472
- * @chunk-id TRIGGERS_GET_EXECUTIONS_1_0_0
473
- * @version 1.0.0
474
- * @hash cf649749d76cd4d5909f96eb9193fd791f30f1e6268e1a0e6cb35ee4676d3071
475
- * @macro Trigger Executions Handler
476
- * @micro Returns paginated execution history for a trigger
477
- * @inputs ctx: CoreContext — Request context with principal and database
478
- * @inputs body: any — Request body (unused for GET)
479
- * @outputs Array of trigger_executions rows ordered by triggered_at desc
480
- * @depends-on [createHandler]
481
- * @depended-by [Netlify function routing]
482
- * @side-effects [DB queries, pagination]
483
- * @tags triggers, executions, history, pagination
484
- */
485
- export const getExecutions = createHandler(async (ctx, body) => {
486
- const { trigger_id, limit = 50, offset = 0 } = ctx.query || {}
487
-
488
- if (!trigger_id) {
489
- throw new Error('Trigger ID is required')
490
- }
491
-
492
- const parsedLimit = parseInt(limit.toString())
493
- const parsedOffset = parseInt(offset.toString())
474
+ // CIL[END chunk_id="triggers_toggle_1783931956"]
494
475
 
495
- const { data, error: err } = await ctx.db
496
- .from('trigger_executions')
497
- .select('*')
498
- .eq('trigger_id', trigger_id)
499
- .order('triggered_at', { ascending: false })
500
- .range(parsedOffset, parsedOffset + parsedLimit - 1)
501
-
502
- if (err) throw err
503
-
504
- return data
505
- })
506
- // ─── CHUNK_END: TRIGGERS_GET_EXECUTIONS ────────────────────────────────────────────────
507
-
508
- // ─── MAIN HANDLER ────────────────────────────────────────────────────────────
509
-
510
- // ─── CHUNK_START: TRIGGERS_HANDLER ──────────────────────────────────────────────
511
- /**
512
- * @chunk-id TRIGGERS_HANDLER_1_0_0
513
- * @version 1.0.0
514
- * @hash 4efc53a8d92acf3346a939c519e3fbd0d64be57f4ee65a81ee213c5aba24e2ce
515
- * @macro Triggers Router
516
- * @micro Routes HTTP methods and actions to appropriate trigger handlers
517
- * @inputs ctx: CoreContext — Request context with principal and database
518
- * @inputs body: any — Request body for POST/PATCH/DELETE operations
519
- * @outputs Varies — Depends on routed handler (listByEvent/getExecutions/toggle/list/get/create/update/remove)
520
- * @depends-on [createHandler, listByEvent, getExecutions, toggle, list, get, create, update, remove]
521
- * @depended-by [Netlify function routing]
522
- * @side-effects [Delegates to appropriate handler]
523
- * @tags triggers, router, crud, netlify-function
524
- */
525
476
  export const handler = createHandler(async (ctx, body) => {
526
477
  const { action } = ctx.query || {}
527
- const method = ctx.query?.method || 'GET'
528
478
 
529
479
  switch (action) {
530
- case 'by-event':
531
- if (method === 'GET') {
532
- return await listByEvent(ctx, body)
533
- }
534
- break
535
- case 'executions':
536
- if (method === 'GET') {
537
- return await getExecutions(ctx, body)
538
- }
539
- break
480
+ case 'list':
481
+ return await list(ctx, body)
482
+ case 'listByEvent':
483
+ return await listByEvent(ctx, body)
484
+ case 'get':
485
+ return await get(ctx, body)
486
+ case 'getExecutions':
487
+ return await getExecutions(ctx, body)
488
+ case 'create':
489
+ return await create(ctx, body)
490
+ case 'update':
491
+ return await update(ctx, body)
492
+ case 'remove':
493
+ return await remove(ctx, body)
540
494
  case 'toggle':
541
- if (method === 'POST') {
542
- return await toggle(ctx, body)
543
- }
544
- break
495
+ return await toggle(ctx, body)
545
496
  default:
546
- if (method === 'GET') {
547
- if (ctx.query?.id) {
548
- return await get(ctx, body)
549
- } else {
550
- return await list(ctx, body)
551
- }
552
- } else if (method === 'POST') {
553
- return await create(ctx, body)
554
- } else if (method === 'PATCH') {
555
- return await update(ctx, body)
556
- } else if (method === 'DELETE') {
557
- return await remove(ctx, body)
558
- }
497
+ throw new Error(`Unsupported action: ${action}`)
559
498
  }
560
-
561
- throw new Error('Invalid action or method')
562
- })
563
- // ─── CHUNK_END: TRIGGERS_HANDLER ────────────────────────────────────────────────
499
+ })