spine-framework 0.4.3 → 0.5.0-rc.4

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 (320) hide show
  1. package/.env.example +13 -3
  2. package/.framework/cli/commands/bootstrap.ts +392 -0
  3. package/.framework/cli/commands/dev.ts +91 -218
  4. package/.framework/cli/commands/doctor.ts +132 -26
  5. package/.framework/cli/commands/generate.ts +38 -298
  6. package/.framework/cli/commands/init.ts +20 -1
  7. package/.framework/cli/commands/migrate.ts +36 -30
  8. package/.framework/cli/commands/migrations.ts +16 -56
  9. package/.framework/cli/commands/upgrade.ts +35 -0
  10. package/.framework/cli/commands/validate-schema.ts +147 -0
  11. package/.framework/cli/context.ts +223 -110
  12. package/.framework/cli/index.ts +6 -0
  13. package/.framework/cli/lib/project-files.ts +241 -1
  14. package/.framework/functions/_shared/agent-runner.ts +1000 -81
  15. package/.framework/functions/_shared/cil.ts +2 -1
  16. package/.framework/functions/_shared/db.ts +3 -6
  17. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  18. package/.framework/functions/_shared/kb.ts +61 -7
  19. package/.framework/functions/_shared/logging.ts +562 -224
  20. package/.framework/functions/_shared/message-sequence.ts +53 -0
  21. package/.framework/functions/_shared/middleware.ts +47 -44
  22. package/.framework/functions/_shared/pipeline-runner.ts +150 -37
  23. package/.framework/functions/_shared/principal.ts +9 -6
  24. package/.framework/functions/_shared/trigger-engine.ts +149 -40
  25. package/.framework/functions/admin-data.ts +48 -31
  26. package/.framework/functions/ai-agents.ts +103 -33
  27. package/.framework/functions/api-keys.ts +301 -122
  28. package/.framework/functions/apps.ts +43 -26
  29. package/.framework/functions/cil-ingest.ts +48 -15
  30. package/.framework/functions/debug-auth.ts +14 -8
  31. package/.framework/functions/deliveries.ts +39 -12
  32. package/.framework/functions/embeddings.ts +145 -76
  33. package/.framework/functions/integration-outbound.ts +67 -27
  34. package/.framework/functions/integrations.ts +254 -127
  35. package/.framework/functions/logs.ts +273 -133
  36. package/.framework/functions/observability.ts +277 -147
  37. package/.framework/functions/pipeline-executions.ts +1 -1
  38. package/.framework/functions/pipeline-runner-background.ts +120 -0
  39. package/.framework/functions/pipelines.ts +14 -9
  40. package/.framework/functions/prompt-configs.ts +7 -4
  41. package/.framework/functions/roles.ts +8 -5
  42. package/.framework/functions/security.ts +30 -17
  43. package/.framework/functions/system-cron.ts +250 -128
  44. package/.framework/functions/tests.ts +7 -6
  45. package/.framework/functions/timers.ts +310 -107
  46. package/.framework/functions/trigger-scheduler.ts +65 -5
  47. package/.framework/functions/triggers.ts +28 -13
  48. package/.framework/functions/types.ts +13 -10
  49. package/.framework/migrations/064_episode_leases.sql +196 -0
  50. package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
  51. package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
  52. package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
  53. package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
  54. package/.framework/migrations/069_log_stats_window.sql +54 -0
  55. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  56. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  57. package/.framework/migrations/072_slug_autofill.sql +77 -0
  58. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  59. package/.framework/schemas/spine.config.schema.json +62 -0
  60. package/.framework/src/App.tsx +3 -1
  61. package/.framework/src/apps/admin/index.tsx +1141 -775
  62. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  63. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  64. package/.framework/src/components/admin/AdminSidebar.tsx +92 -81
  65. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  66. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  67. package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
  68. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  69. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  70. package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
  71. package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
  72. package/.framework/src/components/layout/Layout.tsx +4 -2
  73. package/.framework/src/components/layout/Sidebar.tsx +304 -179
  74. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  75. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  76. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  77. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  78. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  79. package/.framework/src/components/ui/sheet.tsx +24 -26
  80. package/.framework/src/components/ui/textarea.tsx +74 -48
  81. package/.framework/src/hooks/useBranding.ts +47 -0
  82. package/.framework/src/hooks/useEntityList.ts +12 -9
  83. package/.framework/src/hooks/useListSchema.ts +108 -58
  84. package/.framework/src/instructions/README.md +57 -0
  85. package/.framework/src/instructions/ai-agents.md +69 -0
  86. package/.framework/src/instructions/alerts.md +80 -0
  87. package/.framework/src/instructions/api-keys.md +87 -0
  88. package/.framework/src/instructions/apps.md +77 -0
  89. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  90. package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
  91. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  92. package/.framework/src/instructions/concepts/automation.md +186 -0
  93. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  94. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  95. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  96. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  97. package/.framework/src/instructions/concepts/observability.md +116 -0
  98. package/.framework/src/instructions/concepts/permissions.md +150 -0
  99. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  100. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  101. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  102. package/.framework/src/instructions/deliveries.md +59 -0
  103. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  104. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  105. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  106. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  107. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  108. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  109. package/.framework/src/instructions/integrations.md +96 -0
  110. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  111. package/.framework/src/instructions/invites.md +60 -0
  112. package/.framework/src/instructions/kb.md +80 -0
  113. package/.framework/src/instructions/logs.md +77 -0
  114. package/.framework/src/instructions/observability.md +61 -0
  115. package/.framework/src/instructions/operations/deployment.md +115 -0
  116. package/.framework/src/instructions/operations/migrations.md +125 -0
  117. package/.framework/src/instructions/operations/security-checklist.md +148 -0
  118. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  119. package/.framework/src/instructions/operations/upgrade.md +116 -0
  120. package/.framework/src/instructions/pipeline-executions.md +62 -0
  121. package/.framework/src/instructions/pipelines.md +78 -0
  122. package/.framework/src/instructions/prompts.md +95 -0
  123. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  124. package/.framework/src/instructions/reference/api/auth.md +113 -0
  125. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  126. package/.framework/src/instructions/reference/app-manifest.md +150 -0
  127. package/.framework/src/instructions/reference/cli.md +124 -0
  128. package/.framework/src/instructions/reference/database.md +132 -0
  129. package/.framework/src/instructions/reference/design-schema.md +280 -0
  130. package/.framework/src/instructions/reference/env-vars.md +133 -0
  131. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  132. package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
  133. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  134. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  135. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  136. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  137. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  138. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  139. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  140. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  141. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  142. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  143. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  144. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  145. package/.framework/src/instructions/reference/functions/index.md +103 -0
  146. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  147. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  148. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  149. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  150. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  151. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  152. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  153. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  154. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  155. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  156. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  157. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  158. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  159. package/.framework/src/instructions/reference/functions/security.md +140 -0
  160. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  161. package/.framework/src/instructions/reference/functions/system.md +106 -0
  162. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  163. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  164. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  165. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  166. package/.framework/src/instructions/reference/functions/types.md +146 -0
  167. package/.framework/src/instructions/reference/seeds.md +126 -0
  168. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  169. package/.framework/src/instructions/reference/spine-config.md +114 -0
  170. package/.framework/src/instructions/roles.md +80 -0
  171. package/.framework/src/instructions/runtime-detail.md +70 -0
  172. package/.framework/src/instructions/runtime-list.md +77 -0
  173. package/.framework/src/instructions/security.md +67 -0
  174. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  175. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  176. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  177. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  178. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  179. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  180. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  181. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  182. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  183. package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
  184. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  185. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  186. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  187. package/.framework/src/instructions/testing.md +59 -0
  188. package/.framework/src/instructions/timers.md +82 -0
  189. package/.framework/src/instructions/triggers.md +69 -0
  190. package/.framework/src/instructions/types.md +93 -0
  191. package/.framework/src/lib/branding.ts +128 -0
  192. package/.framework/src/pages/NoAppPage.tsx +3 -1
  193. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  194. package/.framework/src/pages/SetupPage.tsx +5 -2
  195. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  196. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  197. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  198. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  199. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  200. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  201. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  202. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  203. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  204. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  205. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  206. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  207. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  208. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  209. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  210. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  211. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  212. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  213. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  214. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  215. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  216. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  217. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  218. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  219. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  220. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  221. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  222. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  223. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  224. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  225. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  226. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  227. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  228. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  229. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  230. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  231. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  232. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  233. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  234. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  235. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  236. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  237. package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
  238. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
  239. package/.framework/src/pages/auth/LoginPage.tsx +4 -2
  240. package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
  241. package/.framework/templates/agent/AGENTS.md +150 -0
  242. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  243. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  244. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  245. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  246. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
  247. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  248. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
  249. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
  250. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
  251. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  252. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  253. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  254. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  255. package/.framework/templates/project.json +5 -1
  256. package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
  257. package/.framework/tests/unit/agent-files.test.ts +355 -0
  258. package/.framework/tests/unit/agent-runner.test.ts +496 -47
  259. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  260. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  261. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  262. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  263. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  264. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  265. package/.framework/tests/unit/episode-lease.test.ts +695 -0
  266. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  267. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  268. package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
  269. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  270. package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
  271. package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
  272. package/.framework/tests/unit/principal-cache.test.ts +15 -0
  273. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  274. package/.framework/tests/unit/system-cron.test.ts +240 -0
  275. package/.framework/tests/unit/timers.test.ts +159 -0
  276. package/.framework/tests/unit/trigger-engine.test.ts +278 -91
  277. package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
  278. package/README.md +125 -166
  279. package/config/tsconfig.cli.json +2 -2
  280. package/netlify.toml +5 -0
  281. package/package.json +15 -4
  282. package/templates/project/package-lock.json +2 -2
  283. package/.framework/docs/admin-routes.md +0 -76
  284. package/.framework/docs/api-docs-progress.md +0 -38
  285. package/.framework/docs/api-governance.md +0 -146
  286. package/.framework/docs/api-testing-results.md +0 -212
  287. package/.framework/docs/assembly-launch-guide.md +0 -138
  288. package/.framework/docs/audit-results.md +0 -590
  289. package/.framework/docs/db-api-inventory.md +0 -95
  290. package/.framework/docs/gap-remediation-backlog.md +0 -103
  291. package/.framework/docs/ui-adoption-verification.md +0 -111
  292. package/.framework/docs/ui-api-coverage.md +0 -84
  293. package/.framework/docs/v2-compatibility-audit.md +0 -228
  294. package/.framework/mcp/index.ts +0 -133
  295. package/.framework/mcp/load-env.ts +0 -60
  296. package/.framework/mcp/shared/app-manifest.ts +0 -275
  297. package/.framework/mcp/shared/db.ts +0 -477
  298. package/.framework/mcp/shared/kb.ts +0 -118
  299. package/.framework/mcp/tools/architect.ts +0 -431
  300. package/.framework/mcp/tools/cil/context.ts +0 -345
  301. package/.framework/mcp/tools/cil/graph.ts +0 -242
  302. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  303. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  304. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  305. package/.framework/mcp/tools/cil/project.ts +0 -791
  306. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  307. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  308. package/.framework/mcp/tools/cil/search.ts +0 -97
  309. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  310. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  311. package/.framework/mcp/tools/kb/delete.ts +0 -139
  312. package/.framework/mcp/tools/kb/index.ts +0 -52
  313. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  314. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  315. package/.framework/mcp/tools/kb/publish.ts +0 -100
  316. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  317. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  318. package/.framework/mcp/tools/kb/search.ts +0 -101
  319. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  320. package/.framework/mcp/tools/observability.ts +0 -510
@@ -0,0 +1,195 @@
1
+ ---
2
+ title: admin-data
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/admin-data.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/permissions.ts
11
+ - .framework/functions/_shared/access.ts
12
+ - .framework/functions/_shared/security.ts
13
+ - .framework/public/_redirects
14
+ ---
15
+
16
+ # admin-data
17
+
18
+ ## What it does
19
+
20
+ `admin-data` is the generic CRUD endpoint for Spine's runtime and config
21
+ tables. One function exposes list, get, stats, create, update, and remove
22
+ across a fixed allowlist of entities, with record-permission evaluation,
23
+ field-level sanitization, type-schema stamping, and trigger firing built in.
24
+ Custom apps are expected to use this endpoint for all runtime-record CRUD
25
+ rather than wrapping it.
26
+
27
+ ## Route and methods
28
+
29
+ `/.netlify/functions/admin-data`, reachable publicly as `/api/admin-data`
30
+ (`/api/*` is redirected to `/.netlify/functions/:splat` in
31
+ `.framework/public/_redirects`).
32
+
33
+ Dispatch is by `?action=` plus HTTP method (`.framework/functions/admin-data.ts`
34
+ handler):
35
+
36
+ | Call | Action |
37
+ |---|---|
38
+ | `GET /api/admin-data?action=list&entity=…` | `list` |
39
+ | `GET /api/admin-data?action=get&entity=…&id=…` | `get` |
40
+ | `GET /api/admin-data?action=stats&entity=…` | `stats` |
41
+ | `POST /api/admin-data?action=create` | `create` |
42
+ | `PATCH /api/admin-data?action=update&entity=…&id=…` | `update` |
43
+ | `DELETE /api/admin-data?action=remove&entity=…&id=…` | `remove` |
44
+
45
+ Without a recognized `action` (absent or unmatched), the HTTP method alone
46
+ dispatches: `GET` → `get` when `id` is present else `list`, `POST` →
47
+ `create`, `PATCH` → `update`, `DELETE` → `remove`. A `?method=` query
48
+ parameter overrides the HTTP method for dispatch (the middleware copies
49
+ `event.httpMethod` into `query.method` only when the query did not set
50
+ it). A matched action with the wrong method (e.g. `action=list` on `POST`)
51
+ falls through to `Invalid action or method`.
52
+
53
+ ## Authentication
54
+
55
+ Standard `createHandler` authentication: `x-api-key` (machine), internal
56
+ invoke secret (system), or `Authorization: Bearer <jwt>` (human). Anonymous
57
+ requests are rejected with 401 before the handler runs.
58
+
59
+ - Humans query through a JWT-scoped client (`ctx.db`), so row-level security
60
+ applies. Machines, anonymous, and system principals get the service-role
61
+ client — endpoint-level checks carry the weight.
62
+ - `list` switches to `adminDb` when `principal.isInternal` or the principal
63
+ is a system admin; otherwise it uses `ctx.db`.
64
+ - `create` calls `resolveFirstSurfacePermissions(personId, accountId,
65
+ typeSlug, 'create', designSchema)` — a machine whose scopes contain `*:*`
66
+ bypasses (`isMachineGlobal`); a `*:*` machine also resolves as
67
+ `system_admin`.
68
+ - `update` runs `validateUpdatePermissions` and lets system admins through
69
+ regardless of the result.
70
+ - `remove` calls `resolveFirstSurfacePermissions(..., 'delete',
71
+ existing.design_schema)` with a system-admin bypass.
72
+ - Non-internal audiences never see `visibility: 'internal'` threads, and
73
+ internal messages on external threads are projected to label-only
74
+ skeletons via `projectMessageForCustomer`.
75
+
76
+ ## Request contract
77
+
78
+ `entity` is required on every call and must be one of `VALID_ENTITIES`:
79
+ `accounts`, `people`, `items`, `threads`, `messages`, `links`,
80
+ `attachments`, `watchers`, `item_progress`, `logs`, `test_runs`,
81
+ `observability`, `test_cases`, `types`, `link_types`, `ai_agents`,
82
+ `prompt_configs`.
83
+
84
+ **list** (query params): `entity`, `search` (ILIKE against a per-entity
85
+ field — e.g. `title` for items, `full_name` for people, `message` for
86
+ logs), `sort_field` (default `created_at`), `sort_direction` (`desc`
87
+ default), `limit` (default 50), `offset` (default 0), `type_slug`,
88
+ `type_slugs` (comma-separated, resolved through `ENTITY_TO_KIND` to
89
+ `type_id` filters), `view`, `flag_only` / `exclude_flag` (filter on
90
+ `data->><flag>` for `items`, `threads`, `messages`, `links`, `attachments`,
91
+ `watchers`). Every other query parameter becomes an `.eq(key, value)`
92
+ filter, with boolean coercion for `is_active`, `is_verified`, `is_primary`.
93
+ Soft-deleted rows are hidden by default — pass `is_active=false` or
94
+ `include_inactive=true` to include them.
95
+
96
+ **get** (query params): `entity`, `id`, `view`, `lineage` — for `items`,
97
+ parent/child lineage is attached unless `lineage=0`.
98
+
99
+ **stats** (query params): `entity` only.
100
+
101
+ **create** (body): `entity` (or `?entity=`), `type_slug` or `type_id` —
102
+ required for every non-raw entity; `account_id` (optional override);
103
+ arbitrary record fields. `design_schema`, `validation_schema`, and `id` in
104
+ the body are stripped. `thread_id`, `target_id`, or `parent_id` in the body
105
+ pull account and `scope` from the parent record.
106
+
107
+ **update** (query `entity` + `id`; body): record fields; `design_schema`,
108
+ `validation_schema`, `type_id`, `type_slug`, `id`, `entity` are stripped.
109
+ `account_id` is honored only for `entity=people`.
110
+
111
+ **remove** (query params): `entity`, `id`, `soft` (default `'true'`).
112
+
113
+ ## Response contract
114
+
115
+ All responses use the middleware envelope:
116
+ `{ "data": <result>, "error": null, "meta": { "requestId", "duration" } }`
117
+ with HTTP 200. Errors return `{ "data": null, "error": "<message>" }` with
118
+ HTTP `err.statusCode || 500`.
119
+
120
+ - `list` → array of sanitized records; when both `view` and `type_slug`
121
+ are given and the type resolves, `{ data, schema, view }` (schema = the
122
+ type's `design_schema`, `view` = `schema.views[view]` or `null`). The
123
+ exact-count probe runs but the count is not returned.
124
+ - `get` → one sanitized record; `items` records additionally carry
125
+ `parents`, `children`, and `type_slug` fields from
126
+ `design_schema.functionality.lineage`. With `view`, returns
127
+ `{ data, schema, view }`.
128
+ - `stats` → `{ entity, count }`.
129
+ - `create` / `update` → the sanitized row.
130
+ - `remove` → `{ deleted: true, soft: true, data }` on soft delete, or
131
+ `{ deleted: true, soft: false }` on hard delete.
132
+
133
+ ## Side effects
134
+
135
+ - Writes to the target table. On create, `design_schema` and
136
+ `validation_schema` are stamped onto the record from the resolved type
137
+ (missing `record_permissions` defaults to `PERMISSIONS_ALL` — all
138
+ actions for everyone), plus `created_by`, `created_at`, `updated_at`,
139
+ and `account_id` (except `accounts`, which gets `parent_id` instead).
140
+ `types` and `link_types` skip `created_by`/`updated_by` (`UNAUDITED_ENTITIES`).
141
+ - `scope: 'platform'` schemas force `account_id` to the `spine-system`
142
+ account; a `thread_id`/`target_id`/`parent_id` parent overrides both
143
+ account and scope.
144
+ - `fireCreateTriggers` runs after every create (including raw entities) and
145
+ `fireUpdateTriggers` after every update; trigger errors are logged, not
146
+ raised. **`fireDeleteTriggers` is imported but never called** — removing
147
+ a record fires no triggers.
148
+ - `logs`, `test_runs`, `observability`, `test_cases` (`RAW_ENTITIES`)
149
+ bypass type resolution entirely: create inserts a fixed column whitelist
150
+ (e.g. `level`+`message` required for `logs`, `suite` for `test_runs`,
151
+ `metric_name`+`metric_value` for `observability`, `slug`+`name` for
152
+ `test_cases`) and update skips the `updated_by`/`updated_at` stamps.
153
+ - `checkTypeSlugRateLimit` enforces the app's `rate_limits.<type_slug>_create`
154
+ policies on create.
155
+ - Every action is wrapped in `withAdminLogging`, which writes
156
+ admin-operation log rows (entity, action, method, duration, row count).
157
+
158
+ ## Errors
159
+
160
+ | Condition | HTTP | Message |
161
+ |---|---|---|
162
+ | No/invalid `entity` | 400 (create) / 500 (others) | `Valid entity parameter is required` |
163
+ | Missing `id` on get/update/remove | 500 | `ID parameter is required` / `ID is required for update|delete` |
164
+ | Missing type on create | 400 | `type_id or type_slug is required — every runtime record must reference a type` |
165
+ | Bad `type_id` / inactive type | 500 | `type_id not found: …` / `type_id references an inactive type: …` |
166
+ | `type_slug` on an unmappable entity | 400 | `type_slug is not supported for entity: …` |
167
+ | Missing parent | 403 | `Parent record not found or not accessible` |
168
+ | Permission denied | 403 | `Insufficient permissions to create|update|delete this record` |
169
+ | Record missing or RLS-hidden | 500 | `Record not found or access denied` |
170
+ | Bad flag name | 400 | `Invalid flag field name` |
171
+ | Unknown action/method | 500 | `Invalid action or method` |
172
+
173
+ ## Agent notes
174
+
175
+ - Always pass `type_slug` (not `type_id`) when creating runtime records —
176
+ the endpoint resolves it per `kind` via `ENTITY_TO_KIND`
177
+ (`items`→`item`, `threads`→`thread`, `messages`→`message`, `links`→`link`,
178
+ `attachments`→`attachment`, `watchers`→`watcher`, `accounts`→`account`,
179
+ `people`→`person`, `item_progress`→`item_progress`).
180
+ - Soft delete is the default and only applies to `accounts`, `people`,
181
+ `items`, `threads`, `messages`, `watchers`, `item_progress`,
182
+ `test_cases` — it sets `is_active=false`. Everything else hard-deletes
183
+ even without `soft=false`; pass `soft=false` explicitly when you mean it.
184
+ - Delete triggers do not exist in practice: `remove` never calls
185
+ `fireDeleteTriggers`, so automations keyed on delete events will not run.
186
+ - `?id=` is mirrored into the JSON body by the middleware before the
187
+ handler runs.
188
+ - Record reads are sanitized per principal: second/third-surface tables and
189
+ field-level `permissions` in `design_schema` shape what comes back, and a
190
+ principal with no account context gets only `id`/`created_at`/`updated_at`.
191
+ - A machine API key without `*:*` scope cannot create (the permission
192
+ lookup is by `people.id`, which machine keys don't have) — use `*:*`
193
+ keys for server-side writes, or a human JWT.
194
+ - `update` does not re-stamp `design_schema`; changing a record's type
195
+ is not possible through this endpoint (`type_id` is stripped).
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: admin-funnel
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/admin-funnel.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/funnel.ts
11
+ ---
12
+
13
+ # admin-funnel
14
+
15
+ ## What it does
16
+
17
+ Computes anonymous-visitor → identified-user → installed-instance
18
+ conversion metrics for the platform funnel. It reads `items` rows under
19
+ three app-seeded item types — `funnel_signal`, `anonymous_session`, and
20
+ `spine_instance` — and returns counts, conversion rates, and a
21
+ stage distribution over the last 30 days.
22
+
23
+ This is framework-internal telemetry for the platform operator (growth
24
+ and install funnel for Spine itself), not per-customer app analytics.
25
+
26
+ ## Route and methods
27
+
28
+ **None — this file exports no `handler`.** It exports a single
29
+ `createHandler`-wrapped const, `metrics`, which nothing else imports or
30
+ dispatches. As shipped there is no HTTP entrypoint: the file is deployed
31
+ to the functions directory by the assembly script like every other
32
+ `.framework/functions` file, but Netlify has no `handler` export to
33
+ invoke. Treat it as a dormant module pending a dispatch handler (or a
34
+ wiring bug); do not cite it as a live endpoint.
35
+
36
+ ## Authentication
37
+
38
+ If `metrics` were wired: `createHandler` requires any non-anonymous
39
+ principal — no role check inside the function. All queries run on
40
+ `adminDb` across **all accounts** (see Agent notes).
41
+
42
+ ## Request contract
43
+
44
+ `metrics(ctx, body)` takes no parameters — the 30-day window is hardcoded.
45
+
46
+ ## Response contract
47
+
48
+ `{ anonymous_visitors, identified_users, installed_instances,
49
+ conversion_rates: { anonymous_to_identified, identified_to_installed },
50
+ stage_distribution: [{ stage, count, percentage }] }`
51
+
52
+ - `anonymous_visitors` — `anonymous_session` items in the last 30 days
53
+ whose `data->lifecycle->>stitched_to_account_id` is null.
54
+ - `identified_users` — distinct `account_id`s on `funnel_signal` items in
55
+ the last 30 days.
56
+ - `installed_instances` — `spine_instance` items with a non-null
57
+ `data->>claimed_by_person_id` (all time).
58
+ - `stage_distribution` — counts of `funnel_signal.data->>stage` over the
59
+ last 30 days, only counting rows that have an `account_id`.
60
+
61
+ ## Side effects
62
+
63
+ None — read-only aggregation.
64
+
65
+ ## Errors
66
+
67
+ No explicit error paths. Missing signal types are expected on a clean
68
+ install (the types are seeded by installed apps): when none of the three
69
+ type slugs resolves, `metrics` returns a zeroed payload instead of
70
+ failing. Database errors would surface as thrown errors through
71
+ `createHandler`'s 500 path.
72
+
73
+ ## Agent notes
74
+
75
+ - **Verify before use.** The missing `handler` export means there is no
76
+ routed endpoint; check the current tree before assuming
77
+ `GET /api/admin-funnel` does anything.
78
+ - **Unscoped platform-wide reads.** If wired as-is, every query runs on
79
+ `adminDb` with no `account_id` filter and no `isSystemAdmin` gate — any
80
+ authenticated principal would see cross-account funnel data. A
81
+ `system_admin` check belongs in any dispatch handler that exposes it.
82
+ - Signal data is written elsewhere — `emitSignal` in
83
+ `.framework/functions/_shared/funnel.ts` inserts typed signal items
84
+ (`funnel_signal` among them) and fires `item_created` triggers; these
85
+ admin modules only read the results.
86
+ - The `funnel_signal_use` type seed exists in the framework
87
+ (`.framework/seeds/types/funnel_signal_use.json`); the
88
+ `funnel_signal`/`anonymous_session`/`spine_instance` slugs the queries
89
+ look for are seeded by installed apps, hence the degrade-to-zeros
90
+ behavior.
@@ -0,0 +1,107 @@
1
+ ---
2
+ title: admin-install-funnel
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/admin-install-funnel.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/funnel.ts
11
+ ---
12
+
13
+ # admin-install-funnel
14
+
15
+ ## What it does
16
+
17
+ Install-funnel analytics for the platform operator: download/install/
18
+ first-use/claimed counts, per-day install trends over 30 days, recent
19
+ install records with contact and account joins, a version breakdown, and
20
+ a full export of install-instance records. All data comes from `items`
21
+ rows under the `funnel_signal` type where `data->>source = 'inst'`, plus
22
+ `spine_instance` rows for the version split.
23
+
24
+ Framework-internal telemetry about Spine installations (serials, app
25
+ versions, environments), not customer app data.
26
+
27
+ ## Route and methods
28
+
29
+ **None — this file exports no `handler`.** It exports two
30
+ `createHandler`-wrapped consts, `metrics` and `exportData`, which nothing
31
+ imports or dispatches. As shipped there is no HTTP entrypoint; treat it
32
+ as a dormant module pending a dispatch handler (or a wiring bug), not a
33
+ live endpoint.
34
+
35
+ ## Authentication
36
+
37
+ If wired: `createHandler` requires any non-anonymous principal — no role
38
+ check inside either export. All queries run on `adminDb` across **all
39
+ accounts** (see Agent notes).
40
+
41
+ ## Request contract
42
+
43
+ Neither export reads parameters — `metrics` uses fixed windows and
44
+ `exportData` returns the full matching set.
45
+
46
+ ## Response contract
47
+
48
+ `metrics` →
49
+
50
+ ```json
51
+ {
52
+ "total_downloads": 0,
53
+ "unique_installers": 0,
54
+ "active_instances": 0,
55
+ "conversion_funnel": { "downloads": 0, "installations": 0, "first_use": 0, "claimed": 0 },
56
+ "install_trends": [{ "date": "YYYY-MM-DD", "downloads": 0, "installations": 0 }],
57
+ "recent_installs": [{ "id": "…", "serial": "…", "app_version": "…", "environment": "…",
58
+ "first_seen_at": "…", "last_signal_at": "…",
59
+ "claimed_by": "…", "claimed_at": "…",
60
+ "account_name": "…", "person_email": "…" }],
61
+ "top_versions": [{ "version": "…", "count": 0, "percentage": 0 }]
62
+ }
63
+ ```
64
+
65
+ Field semantics:
66
+
67
+ - `total_downloads` / `installations` / `first_use` — `funnel_signal`
68
+ rows with `action_type` `download` / `install` / `first_command`.
69
+ - `unique_installers` — distinct `data->>serial` values.
70
+ - `active_instances` — serialized signals seen in the last 7 days.
71
+ - `claimed` — signals with non-null `data->>claimed_by_person_id`.
72
+ - `install_trends` — 30 daily buckets of download + install counts.
73
+ - `recent_installs` — latest 20 serialized signals joined to
74
+ `people(email)` and `accounts(name)`.
75
+ - `top_versions` — up to 10 versions by `spine_instance` count share.
76
+
77
+ `exportData` → `{ instances, exported_at, total_count }` — every
78
+ `funnel_signal` row with `source='inst'` and a serial, full `data` bag,
79
+ `account_id`, and the same people/accounts joins, newest first.
80
+
81
+ ## Side effects
82
+
83
+ None — read-only.
84
+
85
+ ## Errors
86
+
87
+ No explicit error paths. When the `funnel_signal` type is absent
88
+ (app-seeded; missing on a clean install) `metrics` returns a zeroed
89
+ payload and `exportData` returns `{ instances: [], …, total_count: 0 }`.
90
+ The 30-day trend loop issues two count queries per day (60 sequential
91
+ queries) — slow but not an error.
92
+
93
+ ## Agent notes
94
+
95
+ - **Verify before use** — no `handler` export means no routed endpoint;
96
+ `?action=export`-style dispatch does not exist in the file.
97
+ - **Platform-wide PII.** `exportData` and the `recent_installs` join
98
+ return `people.email` and `accounts.name` across all accounts via
99
+ `adminDb`, gated only by authentication if this is ever wired. Any
100
+ dispatch handler added later must gate on `system_admin` (or narrower)
101
+ before exposing these exports.
102
+ - The daily-trend loop is 60 round trips per call — do not put it on a
103
+ hot path; if the endpoint is wired for real use, fold the loop into a
104
+ single grouped query or RPC.
105
+ - `serial` is the installer-instance identity; `claimed_by_person_id`
106
+ links an install to a registered person — that join is what makes the
107
+ export PII-bearing.
@@ -0,0 +1,102 @@
1
+ ---
2
+ title: admin-signals
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/admin-signals.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/funnel.ts
11
+ ---
12
+
13
+ # admin-signals
14
+
15
+ ## What it does
16
+
17
+ Two read surfaces over `funnel_signal` items for the platform operator:
18
+ `metrics` aggregates signal totals (all-time, today, this week), a
19
+ conversion rate from high-value signals, top sources, and the 20 most
20
+ recent signals; `audit` lists individual signal records with filters,
21
+ pagination, attribution fields (UTM/referrer/anonymous identity), and an
22
+ unpaginated export mode.
23
+
24
+ Signals record product-journey events (`action_type`, `stage`, `source`,
25
+ `rating`) — framework-internal telemetry for the platform, not customer
26
+ app analytics.
27
+
28
+ ## Route and methods
29
+
30
+ **None — this file exports no `handler`.** It exports `metrics` and
31
+ `audit`, both `createHandler`-wrapped, which nothing imports or
32
+ dispatches. As shipped there is no HTTP entrypoint; treat it as a dormant
33
+ module pending a dispatch handler, not a live endpoint.
34
+
35
+ ## Authentication
36
+
37
+ If wired: `createHandler` requires any non-anonymous principal — no role
38
+ check in either export. All queries run on `adminDb` across **all
39
+ accounts** (see Agent notes).
40
+
41
+ ## Request contract
42
+
43
+ `metrics` — no parameters; windows are hardcoded (today, trailing 7
44
+ days).
45
+
46
+ `audit` — query parameters:
47
+
48
+ | Param | Effect |
49
+ |---|---|
50
+ | `page` (default 1), `limit` (default 50) | Pagination (ignored in export mode) |
51
+ | `date_from`, `date_to` | `created_at` bounds |
52
+ | `source`, `stage`, `action_type` | Exact match on `data->>` fields |
53
+ | `min_rating` | `data->>rating >= value` |
54
+ | `account_id` | Caller-supplied account filter |
55
+ | `search` | OR-match on `accounts.name`, `people.email`, `data->attribution->>first_touch_referrer_domain` (ILIKE) |
56
+ | `export` (any value) | Returns the full formatted array with no range limit |
57
+
58
+ ## Response contract
59
+
60
+ `metrics` → `{ total_signals, signals_today, signals_this_week,
61
+ conversion_rate, top_sources: [{source, count}], recent_signals: [{id,
62
+ action_type, source, stage, created_at, account_name, rating}] }`.
63
+ `conversion_rate` is the share of this week's signals with
64
+ `data->>rating >= '4'` — a "high-value signal" rate, not a funnel
65
+ conversion.
66
+
67
+ `audit` → `{ signals, total, page, limit }`, or in export mode the bare
68
+ formatted array. Each signal row: `{ id, action_type, action_value,
69
+ stage, source, created_at, account_name, person_email, anonymous_id,
70
+ session_id, rating, raw_score, referrer_domain, utm_source, utm_medium,
71
+ utm_campaign }` — joined to `people(email)` and `accounts(name)`, with
72
+ identity fields from `data->identity` and attribution from
73
+ `data->attribution`.
74
+
75
+ ## Side effects
76
+
77
+ None — read-only.
78
+
79
+ ## Errors
80
+
81
+ - `Failed to fetch signals: <message>` — the one thrown error, on query
82
+ failure inside `audit`.
83
+ - Missing `funnel_signal` type (app-seeded; absent on clean installs)
84
+ degrades gracefully: `metrics` returns zeroed metrics, `audit` returns
85
+ an empty page or `[]` in export mode.
86
+
87
+ ## Agent notes
88
+
89
+ - **Verify before use** — no `handler` export means no routed endpoint.
90
+ - **Cross-account PII, caller-steered.** `audit` accepts an arbitrary
91
+ `account_id` filter and returns `people.email`, `accounts.name`,
92
+ anonymous IDs, session IDs, and UTM attribution across all accounts via
93
+ `adminDb`. If a dispatch handler is ever added, it needs a
94
+ `system_admin` gate before this surface is exposed.
95
+ - `export` mode drops the range limit — it returns the entire filtered
96
+ set, so combined with the joins it is the heaviest call in the file.
97
+ - Rating comparisons are string comparisons on `data->>rating`
98
+ (`gte '4'`) — works for single digits, but `"10" >= "4"` is false
99
+ lexicographically; values above 9 would misclassify.
100
+ - Signals are produced by `emitSignal` in
101
+ `.framework/functions/_shared/funnel.ts` (deduped once per account +
102
+ `action_type` by default) — this module only reads them.
@@ -0,0 +1,148 @@
1
+ ---
2
+ title: ai-agents
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/ai-agents.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/agent-runner.ts
11
+ - .framework/functions/_shared/rate-limit.ts
12
+ - .framework/functions/_shared/security.ts
13
+ - .framework/public/_redirects
14
+ ---
15
+
16
+ # ai-agents
17
+
18
+ ## What it does
19
+
20
+ CRUD over `public.ai_agents` (agent definitions: `agent_type`,
21
+ `model_config`, `system_prompt`, `tools`, `capabilities`,
22
+ `constraints`), plus `action=run` — the interactive entry point that
23
+ executes a multi-turn agent episode against a **thread** (not against an
24
+ agent row directly; the agent and prompt config are resolved inside
25
+ `runAgent` from the thread's `data.agent_id` /
26
+ `data.prompt_config_id` and type schema).
27
+
28
+ ## Route and methods
29
+
30
+ `/.netlify/functions/ai-agents`, reachable as `/api/ai-agents` (`/api/*`
31
+ → `/.netlify/functions/:splat`, `.framework/public/_redirects`).
32
+ Dispatch is by `?action=` then method:
33
+
34
+ | Call | Handler |
35
+ |---|---|
36
+ | `POST /api/ai-agents?action=run` | `run` (any method dispatches; a JSON body is required in practice) |
37
+ | `GET /api/ai-agents` | `list` |
38
+ | `GET /api/ai-agents?id=<uuid>` | `get` |
39
+ | `POST /api/ai-agents` | `create` |
40
+ | `PATCH /api/ai-agents` (`id` in body or query) | `update` |
41
+ | `DELETE /api/ai-agents?id=<uuid>` | `remove` |
42
+
43
+ ## Authentication
44
+
45
+ Standard `createHandler` auth: `x-api-key` → machine principal,
46
+ `x-internal-secret` → internal principal, `Bearer <jwt>` → human;
47
+ anonymous → 401.
48
+
49
+ - `list`/`create`/`update`/`run` require `ctx.accountId`.
50
+ - No role gate anywhere in this file — `create`/`update`/`remove` only
51
+ require an authenticated person+account context; humans are bounded by
52
+ RLS on `ctx.db`, machines run service-role.
53
+ - `run` debits the `agent_step` rate limit
54
+ (`.framework/functions/_shared/rate-limit.ts`) — a fail-closed,
55
+ cost-bearing action; over-limit throws `RateLimitError` (HTTP 429 with
56
+ `retry_after`).
57
+
58
+ ## Request contract
59
+
60
+ **list** (query): `agent_type`, `is_active` (`'true'`/`'false'`),
61
+ `limit` (default 100), `offset` (default 0).
62
+
63
+ **get** (query): `id`.
64
+
65
+ **create** (body): `name`, `agent_type` required. Optional: `app_id`,
66
+ `description`, `model_config` `{}`, `system_prompt`, `tools` `[]`,
67
+ `capabilities` `[]`, `constraints` `{}`, `metadata` `{}`. `account_id`
68
+ and `created_by` stamped from context.
69
+
70
+ **update** (`id` in body or query + body): allowlist — `name`,
71
+ `description`, `agent_type`, `model_config`, `system_prompt`, `tools`,
72
+ `capabilities`, `constraints`, `metadata`, `is_active`, `app_id`.
73
+
74
+ **remove** (query): `id` — **hard delete**.
75
+
76
+ **run** (body): `thread_id` required; `user_message`; `mode`
77
+ (`'step'`/`'complete'`/`'episode'`, default `'complete'`);
78
+ `driver_token` (optional — lets a client keep a stable driver identity
79
+ across calls instead of `ctx.requestId`).
80
+
81
+ ## Response contract
82
+
83
+ Standard `{ data, error, meta }` envelope.
84
+
85
+ - CRUD → sanitized row(s) (`{ id }` for non-admins); `remove` →
86
+ `{ success: true }`.
87
+ - `run` → `{ ok, thread_id, message_id, status, turn_count, content,
88
+ message_type, data, agent_id, prompt_config_id }` where `status` comes
89
+ from the thread's `data.research_state` (`complete`,
90
+ `awaiting_input`, `failed`, …).
91
+ - Lease-refused runs return HTTP 200 with `{ ok: true, status:
92
+ 'running', driver_id }` (plus `already_running: true` in episode
93
+ mode) — callers poll the thread rather than interleave writes. A
94
+ replay of an already-answered episode returns `status: 'complete'`
95
+ with `already_done: true` and the last outbound message.
96
+
97
+ ## Side effects
98
+
99
+ - `run` performs a prompt-injection pre-flight (`detectInjection`); the
100
+ app's `config.security.prompt_injection` mode (`'off'`/`'flag'`
101
+ default/`'block'`) decides whether the hit is audited
102
+ (`security.injection_detected`) or rejected with HTTP 400.
103
+ - `run` claims the thread's episode lease — atomically via the
104
+ `episode_leases` RPC (`.framework/migrations/064_episode_leases.sql`),
105
+ with a legacy heartbeat check as fallback — so concurrent drivers
106
+ can't double-drive a thread.
107
+ - `run` executes `runAgent`
108
+ (`.framework/functions/_shared/agent-runner.ts`), which writes agent
109
+ messages to `public.messages` and mutates `threads.data
110
+ .research_state`. Episode mode runs inline under an 8-second budget,
111
+ then the client's step loop continues under the same lease.
112
+ - Rate limit is debited **only after** a successful lease claim —
113
+ refused requests never double-charge an episode.
114
+ - CRUD emits `ai_agent.created` / `.updated` / `.deleted` audit rows to
115
+ `public.logs`.
116
+
117
+ ## Errors
118
+
119
+ | Condition | HTTP | Message |
120
+ |---|---|---|
121
+ | Missing `thread_id` | 500 | `thread_id is required` |
122
+ | No person+account context | 500 | `User context (person and account) required` |
123
+ | Missing thread | 500 | `Thread not found` / PostgREST message |
124
+ | Injection in `block` mode | 400 | `Your message could not be processed automatically. A support specialist will review it.` |
125
+ | `agent_step` over limit | 429 | rate-limit message + `retry_after` |
126
+ | Missing `id` | 500 | `Agent ID is required` |
127
+ | Row missing on delete | 500 | `Agent not found` |
128
+ | Bad action/method | 500 | `Invalid action or method` |
129
+
130
+ Most `Error`s map to 500 (no `statusCode`); the injection-block and
131
+ rate-limit paths carry explicit 400/429 codes.
132
+
133
+ ## Agent notes
134
+
135
+ - `run` takes a thread, not an agent id — the agent binding lives on
136
+ `threads.data.agent_id` (or the thread type's
137
+ `design_schema.default_agent_id`). Creating an `ai_agents` row does
138
+ not wire it to anything.
139
+ - Episode single-flight is keyed by `driver_id`: pass a stable
140
+ `driver_token` from a polling client so re-entry renews the lease
141
+ instead of being refused.
142
+ - A "stuck" live driver blocks the thread for the 60-second lease TTL —
143
+ `status: 'running'` is a normal response, not an error.
144
+ - `remove` is a hard delete of the agent row; in-flight episodes are
145
+ unaffected (they hold ids in thread data).
146
+ - The dispatch `case 'run'` ignores the HTTP method — a `GET
147
+ ?action=run` reaches `run` and fails on missing `thread_id` (body is
148
+ null on GET).