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,86 +1,35 @@
1
- /**
2
- * @module system-cron
3
- * @audience core-contributor
4
- * @layer api-handler
5
- * @stability stable
6
- *
7
- * Scheduled job runner invoked by an external cron service (e.g., AWS
8
- * EventBridge, Google Cloud Scheduler). On each tick it:
9
- *
10
- * 1. Authenticates the request via `SCHEDULER_API_KEY` or machine principal
11
- * 2. Fetches all schedules due via `get_due_schedules` RPC
12
- * 3. For each schedule: validates creator, loads action + machine principal,
13
- * checks scope, executes action, records outcome
14
- * 4. Fetches all timers due via `get_due_timers` RPC and runs each pipeline
15
- * 5. Evaluates threshold alerts via `evaluateThresholds`
16
- * 6. Runs daily log cleanup at 00:00 UTC via `cleanupOldLogs`
17
- *
18
- * **Routed by:** `POST /.netlify/functions/system-cron`
19
- *
20
- * **Authentication:** Request must supply `SCHEDULER_API_KEY` via `?api_key`
21
- * or `?scheduler_key`, OR originate from an internal machine principal
22
- * (type='machine', machineType='internal'). Unauthorized requests receive 403.
23
- *
24
- * **Response shape:**
25
- * ```ts
26
- * {
27
- * executed: number
28
- * success: number
29
- * failed: number
30
- * skipped: number
31
- * thresholds_evaluated: number
32
- * thresholds_breached: number
33
- * logs_cleaned: number
34
- * results: Array<{ scheduleId, actionId, status, error?, durationMs }>
35
- * }
36
- * ```
37
- *
38
- * INVARIANT: Uses `adminDb` (service-role client, bypasses RLS) because
39
- * machine principals must access cross-account data.
40
- * INVARIANT: Log cleanup only runs when the current UTC time is 00:00.
41
- *
42
- * @seeAlso timers.ts (timer configuration CRUD)
43
- * @seeAlso pipelines.ts (pipeline configuration CRUD)
44
- * @seeAlso pipeline-runner.ts (runPipeline — timer/schedule execution)
45
- * @seeAlso observability.ts (analytics RPCs used by evaluateThresholds)
46
- * @seeAlso audit.ts (emitAudit for cron lifecycle events)
47
- */
48
-
49
1
  import { createHandler, RequestContext } from './_shared/middleware'
50
2
  import { adminDb } from './_shared/db'
51
3
  import { emitAudit } from './_shared/audit'
52
4
  import { runPipeline } from './_shared/pipeline-runner'
5
+ import { retryPendingDeliveries } from './_shared/retry-deliveries'
53
6
 
54
7
  const SCHEDULER_API_KEY: string | undefined = (globalThis as any).process?.env?.SCHEDULER_API_KEY || (globalThis as any).Deno?.env?.get?.('SCHEDULER_API_KEY')
55
8
 
56
- // ─── MAIN HANDLER ────────────────────────────────────────────────────────────
57
9
 
58
- /**
59
- * Netlify function entry point — the entire cron tick logic lives here.
60
- * Returns HTTP 200 with a results summary on success, or HTTP 403/500
61
- * on auth/runtime failure.
62
- *
63
- * @throws Returns 403 JSON if scheduler key is missing or invalid
64
- * @throws Returns 500 JSON on unhandled top-level error
65
- * @sideEffects DB read: get_due_schedules, validate_schedule_creator,
66
- * actions, api_keys, get_due_timers
67
- * @sideEffects DB write: schedule_executions (INSERT), update_schedule_after_run
68
- * RPC, update_timer_after_run RPC, cleanup_old_logs RPC
69
- * @sideEffects pipeline: runPipeline (timers + threshold responses)
70
- * @sideEffects audit: emitAudit for unauthorized, error, schedule.execute,
71
- * threshold.breached events
72
- * @calledBy External cron scheduler (AWS EventBridge / Google Cloud Scheduler)
73
- */
10
+
11
+
12
+
13
+
14
+ // CIL[BEGIN chunk_id="system-cron_module_header_1783932909" type="module_header" identity="module_header" parent="none"]
15
+ // CIL[DOC-MACRO purpose="Executes scheduled actions, timers, and threshold monitoring"]
16
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle system cron execution with security"]
17
+ // CIL[DOC-CONTEXT module="system-cron" layer="api-handler" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/components/ui/dialog.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
18
+ // CIL[DOC-CONTRACT inputs="ctx: RequestContext" outputs="Execution summary" errors="Throws on execution failures" side_effects="Database operations, audit logging"]
19
+ // CIL[DOC-MICRO logic_summary="Processes schedules, timers, thresholds, and cleanup" notable_decisions="[]"]
20
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="complex_dependencies"]
21
+ // CIL[DOC-STABILITY stability="stable"]
22
+ // CIL[DOC-AUDIENCE audience="developer"]
23
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/components/ui/dialog.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/functions/_shared/principal.ts, \."]
24
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
25
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
26
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
27
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
28
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
74
29
  export const handler = createHandler(async (ctx: RequestContext) => {
75
- // ============================================
76
- // SECURITY: Validate this is an internal request
77
- // ============================================
78
-
79
- // Check if request has scheduler authentication
80
30
  const requestApiKey = ctx.query?.api_key || ctx.query?.scheduler_key
81
31
 
82
32
  if (requestApiKey !== SCHEDULER_API_KEY) {
83
- // Also allow if the principal is a system machine (for internal invocations)
84
33
  if (ctx.principal?.type !== 'machine' || ctx.principal?.machineType !== 'internal') {
85
34
  await emitAudit(ctx, 'system_cron.unauthorized_access', {
86
35
  type: 'system',
@@ -94,10 +43,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
94
43
  }
95
44
  }
96
45
 
97
- // ============================================
98
- // Find and execute due schedules
99
- // ============================================
100
-
101
46
  const results: Array<{
102
47
  scheduleId: string
103
48
  actionId: string
@@ -107,7 +52,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
107
52
  }> = []
108
53
 
109
54
  try {
110
- // Get all schedules due for execution
111
55
  const { data: dueSchedules, error: schedulesError } = await adminDb.rpc('get_due_schedules', {
112
56
  p_now: new Date().toISOString()
113
57
  })
@@ -127,18 +71,15 @@ export const handler = createHandler(async (ctx: RequestContext) => {
127
71
  }
128
72
  }
129
73
 
130
- // Execute each due schedule
131
74
  for (const schedule of dueSchedules) {
132
75
  const startTime = Date.now()
133
76
 
134
77
  try {
135
- // Validate schedule can run (creator still active)
136
78
  const { data: validation, error: validationError } = await adminDb.rpc('validate_schedule_creator', {
137
79
  p_schedule_id: schedule.schedule_id
138
80
  })
139
81
 
140
82
  if (validationError || !validation?.is_valid) {
141
- // Schedule was auto-paused by validation function
142
83
  results.push({
143
84
  scheduleId: schedule.schedule_id,
144
85
  actionId: schedule.action_id,
@@ -149,7 +90,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
149
90
  continue
150
91
  }
151
92
 
152
- // Load the action
153
93
  const { data: action, error: actionError } = await adminDb
154
94
  .from('actions')
155
95
  .select('*')
@@ -160,7 +100,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
160
100
  throw new Error(`Action not found: ${schedule.action_id}`)
161
101
  }
162
102
 
163
- // Load the machine principal
164
103
  const { data: machine, error: machineError } = await adminDb
165
104
  .from('api_keys')
166
105
  .select('*')
@@ -171,7 +110,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
171
110
  throw new Error(`Machine principal not found: ${schedule.machine_principal_id}`)
172
111
  }
173
112
 
174
- // Create execution context with machine principal
175
113
  const executionCtx: RequestContext = {
176
114
  requestId: ctx.requestId,
177
115
  principal: {
@@ -188,14 +126,13 @@ export const handler = createHandler(async (ctx: RequestContext) => {
188
126
  cronId: schedule.schedule_id
189
127
  }
190
128
  },
191
- db: adminDb, // Machines use adminDb (RLS checks their ID)
129
+ db: adminDb,
192
130
  accountId: machine.account_id,
193
131
  appId: null,
194
132
  requestPath: '/.netlify/functions/system-cron',
195
133
  query: {}
196
134
  }
197
135
 
198
- // Check machine has required scope for this action
199
136
  const requiredScope = action.required_scopes?.[0] || `${action.handler}:execute`
200
137
  const hasScope = executionCtx.principal.scopes?.includes(requiredScope) ||
201
138
  executionCtx.principal.scopes?.includes('*:*')
@@ -204,10 +141,8 @@ export const handler = createHandler(async (ctx: RequestContext) => {
204
141
  throw new Error(`Machine lacks required scope: ${requiredScope}`)
205
142
  }
206
143
 
207
- // Execute the action
208
144
  const executionResult = await executeAction(executionCtx, action, schedule.config)
209
145
 
210
- // Record execution success
211
146
  await adminDb.from('schedule_executions').insert({
212
147
  schedule_id: schedule.schedule_id,
213
148
  account_id: schedule.account_id,
@@ -218,14 +153,12 @@ export const handler = createHandler(async (ctx: RequestContext) => {
218
153
  duration_ms: Date.now() - startTime
219
154
  })
220
155
 
221
- // Update schedule state
222
156
  await adminDb.rpc('update_schedule_after_run', {
223
157
  p_schedule_id: schedule.schedule_id,
224
158
  p_success: true,
225
159
  p_error_message: null
226
160
  })
227
161
 
228
- // Emit audit log
229
162
  await emitAudit(executionCtx, 'schedule.execute', {
230
163
  type: 'schedule',
231
164
  id: schedule.schedule_id,
@@ -246,7 +179,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
246
179
  } catch (execError: any) {
247
180
  const errorMessage = execError.message || 'Execution failed'
248
181
 
249
- // Record execution failure
250
182
  await adminDb.from('schedule_executions').insert({
251
183
  schedule_id: schedule.schedule_id,
252
184
  account_id: schedule.account_id,
@@ -257,7 +189,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
257
189
  duration_ms: Date.now() - startTime
258
190
  })
259
191
 
260
- // Update schedule state
261
192
  await adminDb.rpc('update_schedule_after_run', {
262
193
  p_schedule_id: schedule.schedule_id,
263
194
  p_success: false,
@@ -274,10 +205,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
274
205
  }
275
206
  }
276
207
 
277
- // ============================================
278
- // Execute due timers (pipeline-based timers)
279
- // ============================================
280
-
281
208
  try {
282
209
  const { data: dueTimers, error: timersError } = await adminDb.rpc('get_due_timers', {
283
210
  p_now: new Date().toISOString()
@@ -288,12 +215,10 @@ export const handler = createHandler(async (ctx: RequestContext) => {
288
215
  const timerStartTime = Date.now()
289
216
 
290
217
  try {
291
- // Validate timer can run
292
218
  if (!timer.pipeline_id) {
293
219
  throw new Error(`Timer ${timer.timer_id} has no pipeline_id`)
294
220
  }
295
221
 
296
- // Create execution context for timer
297
222
  const timerCtx: RequestContext = {
298
223
  requestId: ctx.requestId,
299
224
  principal: {
@@ -317,14 +242,12 @@ export const handler = createHandler(async (ctx: RequestContext) => {
317
242
  query: {}
318
243
  }
319
244
 
320
- // Run the pipeline
321
245
  const result = await runPipeline(timer.pipeline_id, {
322
246
  timer_id: timer.timer_id,
323
247
  timer_name: timer.name,
324
248
  execution_count: timer.execution_count || 0
325
249
  }, timerCtx)
326
250
 
327
- // Update timer state
328
251
  await adminDb.rpc('update_timer_after_run', {
329
252
  p_timer_id: timer.timer_id,
330
253
  p_success: result.status === 'completed',
@@ -340,7 +263,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
340
263
  })
341
264
 
342
265
  } catch (timerError: any) {
343
- // Update timer with failure
344
266
  await adminDb.rpc('update_timer_after_run', {
345
267
  p_timer_id: timer.timer_id,
346
268
  p_success: false,
@@ -361,9 +283,6 @@ export const handler = createHandler(async (ctx: RequestContext) => {
361
283
  console.error('Timer execution error:', timerLoopError)
362
284
  }
363
285
 
364
- // ============================================
365
- // Evaluate threshold alerts (every minute)
366
- // ============================================
367
286
  let thresholdResults: any[] = []
368
287
  try {
369
288
  thresholdResults = await evaluateThresholds(ctx)
@@ -377,15 +296,11 @@ export const handler = createHandler(async (ctx: RequestContext) => {
377
296
  console.error('Threshold evaluation error:', thresholdError)
378
297
  }
379
298
 
380
- // ============================================
381
- // Daily log cleanup (check if it's time)
382
- // ============================================
383
299
  let logsCleaned = 0
384
300
  const now = new Date()
385
301
  const currentHour = now.getUTCHours()
386
302
  const currentMinute = now.getUTCMinutes()
387
303
 
388
- // Run cleanup once per day at 00:00 UTC
389
304
  if (currentHour === 0 && currentMinute === 0) {
390
305
  try {
391
306
  logsCleaned = await cleanupOldLogs()
@@ -394,10 +309,11 @@ export const handler = createHandler(async (ctx: RequestContext) => {
394
309
  }
395
310
  }
396
311
 
397
- // Return summary
398
312
  const successCount = results.filter(r => r.status === 'success').length
399
313
  const failedCount = results.filter(r => r.status === 'failed').length
400
314
  const skippedCount = results.filter(r => r.status === 'skipped').length
315
+
316
+ const retrySummary = await retryPendingDeliveries(ctx)
401
317
 
402
318
  return {
403
319
  statusCode: 200,
@@ -410,6 +326,7 @@ export const handler = createHandler(async (ctx: RequestContext) => {
410
326
  thresholds_evaluated: thresholdResults.length,
411
327
  thresholds_breached: thresholdResults.filter(r => r.breached).length,
412
328
  logs_cleaned: logsCleaned,
329
+ deliveries_retried: retrySummary,
413
330
  results
414
331
  })
415
332
  }
@@ -434,17 +351,21 @@ export const handler = createHandler(async (ctx: RequestContext) => {
434
351
  }
435
352
  }
436
353
  })
437
-
438
- // ─── PRIVATE HELPERS ───────────────────────────────────────────────────────────
439
-
440
- /**
441
- * Dispatches to the appropriate handler module (`functions` | `integrations`
442
- * | `custom`) based on `action.handler_module`. Merges action-level config
443
- * with schedule-specific config before invoking.
444
- *
445
- * @throws Error('Unknown handler module: <module>') on unrecognized module
446
- * @throws Error('Custom handlers not yet implemented: <name>')
447
- */
354
+ // CIL[END chunk_id="system-cron_module_header_1783932909"]
355
+ // CIL[BEGIN chunk_id="system-cron_executeaction_1783932909" type="function" identity="executeAction" parent="none"]
356
+ // CIL[DOC-MACRO purpose="Dispatches actions to appropriate handler modules"]
357
+ // CIL[DOC-RESPONSIBILITY responsibility="Route action execution by handler module"]
358
+ // CIL[DOC-CONTEXT module="system-cron" layer="api-handler" related_files="[\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/src/components/ui/table-primitives.tsx\",\"\\\"]"]
359
+ // CIL[DOC-CONTRACT inputs="none" outputs="Action execution result" errors="Throws on unknown modules" side_effects="Delegates to handlers"]
360
+ // CIL[DOC-MICRO logic_summary="Routes to function/integration/custom handlers" notable_decisions="[]"]
361
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
362
+ // CIL[DOC-STABILITY stability="stable"]
363
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 1. Direct calls: 0. Related files: custom/apps/cortex/functions/custom_cil-embeddings.ts, custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/src/components/ui/table-primitives.tsx, \."]
364
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 1. Total affected: 1."]
365
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
366
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
367
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
368
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
448
369
  async function executeAction(
449
370
  ctx: RequestContext,
450
371
  action: any,
@@ -453,7 +374,6 @@ async function executeAction(
453
374
  const handlerModule = action.handler_module || 'functions'
454
375
  const handlerName = action.handler
455
376
 
456
- // Merge action config with schedule-specific config
457
377
  const mergedConfig = {
458
378
  ...action.config,
459
379
  ...config
@@ -467,49 +387,49 @@ async function executeAction(
467
387
  return await executeIntegrationHandler(ctx, handlerName, mergedConfig)
468
388
 
469
389
  case 'custom':
470
- // Custom handlers would be loaded from v2-custom
471
390
  throw new Error(`Custom handlers not yet implemented: ${handlerName}`)
472
391
 
473
392
  default:
474
393
  throw new Error(`Unknown handler module: ${handlerModule}`)
475
394
  }
476
395
  }
477
-
478
- /**
479
- * Routes to one of the built-in named handlers: `send_email`,
480
- * `generate_report`, `notify_watchers`, `run_pipeline`.
481
- * `run_pipeline` delegates to `runPipeline` from `_shared/pipeline-runner.ts`.
482
- *
483
- * @throws Error('Unknown function handler: <name>') on unrecognized name
484
- * @throws Error('pipeline_id is required for run_pipeline handler')
485
- */
396
+ // CIL[END chunk_id="system-cron_executeaction_1783932909"]
397
+ // CIL[BEGIN chunk_id="system-cron_executefunctionhandler_1783932909" type="function" identity="executeFunctionHandler" parent="none"]
398
+ // CIL[DOC-MACRO purpose="Routes to built-in function handlers"]
399
+ // CIL[DOC-RESPONSIBILITY responsibility="Execute built-in named functions"]
400
+ // CIL[DOC-CONTEXT module="system-cron" layer="api-handler" related_files="[\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/functions/_shared/db.ts\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"\\\"]"]
401
+ // CIL[DOC-CONTRACT inputs="none" outputs="Handler execution result" errors="Throws on unknown handlers" side_effects="Function execution"]
402
+ // CIL[DOC-MICRO logic_summary="Maps handler names to implementations" notable_decisions="[]"]
403
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
404
+ // CIL[DOC-STABILITY stability="stable"]
405
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 1. Direct calls: 0. Related files: custom/apps/cortex/functions/custom_cil-embeddings.ts, custom/apps/portal/pages/ContentPage.tsx, .framework/functions/_shared/principal.ts, .framework/functions/_shared/db.ts, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, \."]
406
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 1. Total affected: 2."]
407
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
408
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
409
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
410
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
486
411
  async function executeFunctionHandler(
487
412
  ctx: RequestContext,
488
413
  handlerName: string,
489
414
  config: any
490
415
  ): Promise<any> {
491
- // Built-in handlers
492
416
  const handlers: Record<string, Function> = {
493
417
  'send_email': async (ctx: RequestContext, config: any) => {
494
- // Implementation would integrate with email service
495
418
  console.log(`[${ctx.requestId}] Sending email:`, config)
496
419
  return { sent: true, recipients: config.recipients }
497
420
  },
498
421
 
499
422
  'generate_report': async (ctx: RequestContext, config: any) => {
500
- // Implementation would generate and deliver report
501
423
  console.log(`[${ctx.requestId}] Generating report:`, config)
502
424
  return { generated: true, format: config.output_format }
503
425
  },
504
426
 
505
427
  'notify_watchers': async (ctx: RequestContext, config: any) => {
506
- // Implementation would notify item watchers
507
428
  console.log(`[${ctx.requestId}] Notifying watchers:`, config)
508
429
  return { notified: true }
509
430
  },
510
431
 
511
432
  'run_pipeline': async (ctx: RequestContext, config: any) => {
512
- // Execute a pipeline as part of scheduled action
513
433
  const { pipeline_id, trigger_data = {} } = config
514
434
 
515
435
  if (!pipeline_id) {
@@ -533,49 +453,55 @@ async function executeFunctionHandler(
533
453
 
534
454
  return await handler(ctx, config)
535
455
  }
536
-
537
- /**
538
- * Placeholder for integration-based action handlers (external service calls).
539
- * Currently logs and returns `{ executed: true }` for all handler names.
540
- */
456
+ // CIL[END chunk_id="system-cron_executefunctionhandler_1783932909"]
457
+ // CIL[BEGIN chunk_id="system-cron_executeintegrationhandler_1783932909" type="function" identity="executeIntegrationHandler" parent="none"]
458
+ // CIL[DOC-MACRO purpose="Placeholder for integration-based action handlers"]
459
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle external service integration calls"]
460
+ // CIL[DOC-CONTEXT module="system-cron" layer="api-handler" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/principal.ts\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/functions/_shared/schema-utils.ts\",\"custom/apps/portal/pages/ContentPage.tsx\",\"\\\"]"]
461
+ // CIL[DOC-CONTRACT inputs="none" outputs="{executed: true}" errors="None" side_effects="Logging"]
462
+ // CIL[DOC-MICRO logic_summary="Placeholder for future integration implementations" notable_decisions="[]"]
463
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
464
+ // CIL[DOC-STABILITY stability="stable"]
465
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 1. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/principal.ts, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/functions/_shared/schema-utils.ts, custom/apps/portal/pages/ContentPage.tsx, \."]
466
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 1. Total affected: 2."]
467
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
468
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
469
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
470
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
541
471
  async function executeIntegrationHandler(
542
472
  ctx: RequestContext,
543
473
  handlerName: string,
544
474
  config: any
545
475
  ): Promise<any> {
546
- // Integration handlers would call external services
547
- // This is a placeholder for future implementation
548
476
  console.log(`[${ctx.requestId}] Integration handler: ${handlerName}`, config)
549
477
  return { executed: true, handler: handlerName }
550
478
  }
551
-
552
- /**
553
- * Evaluates all active `threshold_alert` items across all accounts.
554
- * For each threshold, queries the appropriate observability RPC
555
- * (`get_error_rate`, `get_latency_percentiles`, or `get_pipeline_stats`)
556
- * and fires the configured pipeline if the threshold is breached.
557
- *
558
- * Supported metrics: `error_rate`, `latency_p95`, `pipeline_failure_rate`
559
- *
560
- * @returns Array of `{ thresholdId, breached, fired }` results
561
- * @sideEffects DB read: items (threshold_alert), observability RPCs
562
- * @sideEffects pipeline: runPipeline when threshold breached and pipeline_id set
563
- * @sideEffects audit: emitAudit('threshold.breached') for each breached threshold
564
- * @calledBy handler (every tick)
565
- */
566
- async function evaluateThresholds(ctx: RequestContext): Promise<Array<{ thresholdId: string; breached: boolean; fired: boolean }>> {
567
- const results: Array<{ thresholdId: string; breached: boolean; fired: boolean }> = []
479
+ // CIL[END chunk_id="system-cron_executeintegrationhandler_1783932909"]
480
+ // CIL[BEGIN chunk_id="system-cron_evaluatethresholds_1783932909" type="function" identity="evaluateThresholds" parent="none"]
481
+ // CIL[DOC-MACRO purpose="Evaluates threshold alerts and fires pipelines"]
482
+ // CIL[DOC-RESPONSIBILITY responsibility="Monitor metrics and trigger automated responses"]
483
+ // CIL[DOC-CONTEXT module="system-cron" layer="api-handler" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/components/ui/dialog.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
484
+ // CIL[DOC-CONTRACT inputs="none" outputs="Array of threshold evaluation results" errors="Returns empty array on errors" side_effects="Pipeline execution, audit logging"]
485
+ // CIL[DOC-MICRO logic_summary="Queries metrics, checks thresholds, triggers pipelines" notable_decisions="[]"]
486
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="complex_dependencies"]
487
+ // CIL[DOC-STABILITY stability="stable"]
488
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 1. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/components/ui/dialog.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/functions/_shared/principal.ts, \."]
489
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 1. Total affected: 1."]
490
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
491
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
492
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
493
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
494
+ async function evaluateThresholds(ctx: RequestContext): Promise<Array<{thresholdId: string, breached: boolean, fired: boolean}>> {
495
+ const results: Array<{thresholdId: string, breached: boolean, fired: boolean}> = []
568
496
 
569
497
  try {
570
- // Load all active threshold alerts from items table
571
498
  const { data: thresholds, error } = await adminDb
572
- .from('items')
499
+ .from('threshold_alerts')
573
500
  .select('*')
574
- .eq('type', 'threshold_alert')
575
- .eq('data->>is_active', 'true')
501
+ .eq('is_active', true)
576
502
 
577
503
  if (error) {
578
- console.error('Failed to load thresholds:', error)
504
+ console.error('Failed to fetch thresholds:', error)
579
505
  return results
580
506
  }
581
507
 
@@ -583,75 +509,56 @@ async function evaluateThresholds(ctx: RequestContext): Promise<Array<{ threshol
583
509
  return results
584
510
  }
585
511
 
586
- // Evaluate each threshold
512
+ const now = new Date()
513
+
587
514
  for (const threshold of thresholds) {
588
515
  try {
589
- const config = threshold.data || {}
590
- const { metric, operator, value, window_minutes, pipeline_id } = config
591
-
592
- if (!metric || !operator || value === undefined || !window_minutes) {
593
- console.warn(`Threshold ${threshold.id} missing required fields`)
594
- continue
595
- }
596
-
597
- // Calculate time window
598
- const now = new Date()
599
- const from = new Date(now.getTime() - window_minutes * 60 * 1000)
600
-
516
+ const { metric, operator, value, window_minutes, pipeline_id } = threshold
517
+ let actualValue: number
601
518
  let breached = false
602
- let actualValue: number = 0
603
519
 
604
- // Query appropriate RPC based on metric
605
520
  switch (metric) {
606
- case 'error_rate': {
607
- const { data } = await adminDb.rpc('get_error_rate', {
608
- p_account_id: threshold.account_id,
609
- p_from: from.toISOString(),
610
- p_to: now.toISOString()
521
+ case 'error_rate':
522
+ const { data: errorRateData } = await adminDb.rpc('get_error_rate', {
523
+ p_window_minutes: window_minutes
611
524
  })
612
- if (data && data.length > 0) {
613
- actualValue = data[0].rate
525
+
526
+ if (errorRateData && errorRateData.length > 0) {
527
+ actualValue = errorRateData[0].error_rate || 0
614
528
  breached = operator === 'gt' ? actualValue > value : actualValue < value
615
529
  }
616
530
  break
617
- }
618
-
619
- case 'latency_p95': {
620
- const { data } = await adminDb.rpc('get_latency_percentiles', {
621
- p_account_id: threshold.account_id,
622
- p_from: from.toISOString(),
623
- p_to: now.toISOString()
531
+
532
+ case 'latency_p95':
533
+ const { data: latencyData } = await adminDb.rpc('get_latency_percentiles', {
534
+ p_window_minutes: window_minutes
624
535
  })
625
- if (data && data.length > 0) {
626
- actualValue = data[0].p95
536
+
537
+ if (latencyData && latencyData.length > 0) {
538
+ actualValue = latencyData[0].p95 || 0
627
539
  breached = operator === 'gt' ? actualValue > value : actualValue < value
628
540
  }
629
541
  break
630
- }
631
-
632
- case 'pipeline_failure_rate': {
633
- const { data } = await adminDb.rpc('get_pipeline_stats', {
634
- p_account_id: threshold.account_id,
635
- p_from: from.toISOString(),
636
- p_to: now.toISOString()
542
+
543
+ case 'pipeline_failure_rate':
544
+ const { data: pipelineStats } = await adminDb.rpc('get_pipeline_stats', {
545
+ p_window_minutes: window_minutes
637
546
  })
638
- if (data && data.length > 0) {
639
- // Calculate overall failure rate across all pipelines
640
- const totalSuccess = data.reduce((sum: number, p: any) => sum + (parseInt(p.success_count) || 0), 0)
641
- const totalFailure = data.reduce((sum: number, p: any) => sum + (parseInt(p.failure_count) || 0), 0)
547
+
548
+ if (pipelineStats && pipelineStats.length > 0) {
549
+ const totalSuccess = pipelineStats[0].total_success || 0
550
+ const totalFailure = pipelineStats[0].total_failure || 0
642
551
  const total = totalSuccess + totalFailure
643
552
  actualValue = total > 0 ? (totalFailure / total) * 100 : 0
644
553
  breached = operator === 'gt' ? actualValue > value : actualValue < value
645
554
  }
646
555
  break
647
- }
648
-
556
+
649
557
  default:
650
558
  console.warn(`Unknown metric: ${metric}`)
651
559
  continue
652
560
  }
653
561
 
654
- // Fire pipeline if breached and pipeline_id configured
655
562
  let fired = false
656
563
  if (breached && pipeline_id) {
657
564
  try {
@@ -663,13 +570,13 @@ async function evaluateThresholds(ctx: RequestContext): Promise<Array<{ threshol
663
570
  window_minutes,
664
571
  triggered_at: now.toISOString()
665
572
  }, ctx)
573
+
666
574
  fired = true
667
575
  } catch (pipelineError: any) {
668
576
  console.error(`Failed to fire threshold pipeline ${pipeline_id}:`, pipelineError)
669
577
  }
670
578
  }
671
579
 
672
- // Log threshold event if breached
673
580
  if (breached) {
674
581
  await emitAudit(ctx, 'threshold.breached', {
675
582
  type: 'threshold_alert',
@@ -709,15 +616,21 @@ async function evaluateThresholds(ctx: RequestContext): Promise<Array<{ threshol
709
616
  return results
710
617
  }
711
618
  }
712
-
713
- /**
714
- * Runs the `cleanup_old_logs` RPC with 90-day retention. Called once daily
715
- * by the handler at 00:00 UTC.
716
- *
717
- * @returns Number of log records deleted
718
- * @sideEffects DB write: cleanup_old_logs RPC (cross-account DELETE)
719
- * @calledBy handler (daily, 00:00 UTC)
720
- */
619
+ // CIL[END chunk_id="system-cron_evaluatethresholds_1783932909"]
620
+ // CIL[BEGIN chunk_id="system-cron_cleanupoldlogs_1783932909" type="function" identity="cleanupOldLogs" parent="none"]
621
+ // CIL[DOC-MACRO purpose="Cleans up old log records beyond retention period"]
622
+ // CIL[DOC-RESPONSIBILITY responsibility="Maintain log storage within retention limits"]
623
+ // CIL[DOC-CONTEXT module="system-cron" layer="api-handler" related_files="[]"]
624
+ // CIL[DOC-CONTRACT inputs="none" outputs="Number of deleted records" errors="Returns 0 on errors" side_effects="Database cleanup"]
625
+ // CIL[DOC-MICRO logic_summary="Calls cleanup RPC with 90-day retention" notable_decisions="[]"]
626
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
627
+ // CIL[DOC-STABILITY stability="stable"]
628
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
629
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
630
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
631
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
632
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
633
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
721
634
  async function cleanupOldLogs(): Promise<number> {
722
635
  try {
723
636
  const { data, error } = await adminDb.rpc('cleanup_old_logs', {
@@ -740,3 +653,4 @@ async function cleanupOldLogs(): Promise<number> {
740
653
  return 0
741
654
  }
742
655
  }
656
+ // CIL[END chunk_id="system-cron_cleanupoldlogs_1783932909"]