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.
- package/.env.example +13 -3
- package/.framework/cli/commands/bootstrap.ts +392 -0
- package/.framework/cli/commands/dev.ts +91 -218
- package/.framework/cli/commands/doctor.ts +132 -26
- package/.framework/cli/commands/generate.ts +38 -298
- package/.framework/cli/commands/init.ts +20 -1
- package/.framework/cli/commands/migrate.ts +36 -30
- package/.framework/cli/commands/migrations.ts +16 -56
- package/.framework/cli/commands/upgrade.ts +35 -0
- package/.framework/cli/commands/validate-schema.ts +147 -0
- package/.framework/cli/context.ts +223 -110
- package/.framework/cli/index.ts +6 -0
- package/.framework/cli/lib/project-files.ts +241 -1
- package/.framework/functions/_shared/agent-runner.ts +1000 -81
- package/.framework/functions/_shared/cil.ts +2 -1
- package/.framework/functions/_shared/db.ts +3 -6
- package/.framework/functions/_shared/hash-api-key.ts +183 -79
- package/.framework/functions/_shared/kb.ts +61 -7
- package/.framework/functions/_shared/logging.ts +562 -224
- package/.framework/functions/_shared/message-sequence.ts +53 -0
- package/.framework/functions/_shared/middleware.ts +47 -44
- package/.framework/functions/_shared/pipeline-runner.ts +150 -37
- package/.framework/functions/_shared/principal.ts +9 -6
- package/.framework/functions/_shared/trigger-engine.ts +149 -40
- package/.framework/functions/admin-data.ts +48 -31
- package/.framework/functions/ai-agents.ts +103 -33
- package/.framework/functions/api-keys.ts +301 -122
- package/.framework/functions/apps.ts +43 -26
- package/.framework/functions/cil-ingest.ts +48 -15
- package/.framework/functions/debug-auth.ts +14 -8
- package/.framework/functions/deliveries.ts +39 -12
- package/.framework/functions/embeddings.ts +145 -76
- package/.framework/functions/integration-outbound.ts +67 -27
- package/.framework/functions/integrations.ts +254 -127
- package/.framework/functions/logs.ts +273 -133
- package/.framework/functions/observability.ts +277 -147
- package/.framework/functions/pipeline-executions.ts +1 -1
- package/.framework/functions/pipeline-runner-background.ts +120 -0
- package/.framework/functions/pipelines.ts +14 -9
- package/.framework/functions/prompt-configs.ts +7 -4
- package/.framework/functions/roles.ts +8 -5
- package/.framework/functions/security.ts +30 -17
- package/.framework/functions/system-cron.ts +250 -128
- package/.framework/functions/tests.ts +7 -6
- package/.framework/functions/timers.ts +310 -107
- package/.framework/functions/trigger-scheduler.ts +65 -5
- package/.framework/functions/triggers.ts +28 -13
- package/.framework/functions/types.ts +13 -10
- package/.framework/migrations/064_episode_leases.sql +196 -0
- package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
- package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
- package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
- package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
- package/.framework/migrations/069_log_stats_window.sql +54 -0
- package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
- package/.framework/migrations/071_timer_interval_keys.sql +96 -0
- package/.framework/migrations/072_slug_autofill.sql +77 -0
- package/.framework/migrations/073_seed_member_roles.sql +26 -0
- package/.framework/schemas/spine.config.schema.json +62 -0
- package/.framework/src/App.tsx +3 -1
- package/.framework/src/apps/admin/index.tsx +1141 -775
- package/.framework/src/components/ErrorBoundary.tsx +136 -0
- package/.framework/src/components/admin/AdminListPage.tsx +81 -73
- package/.framework/src/components/admin/AdminSidebar.tsx +92 -81
- package/.framework/src/components/admin/EntitySelect.tsx +181 -0
- package/.framework/src/components/admin/PageInstructions.tsx +86 -127
- package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
- package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
- package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
- package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
- package/.framework/src/components/layout/Layout.tsx +4 -2
- package/.framework/src/components/layout/Sidebar.tsx +304 -179
- package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
- package/.framework/src/components/runtime/DataHeader.tsx +96 -48
- package/.framework/src/components/runtime/DataListPage.tsx +3 -26
- package/.framework/src/components/runtime/DataStats.tsx +10 -10
- package/.framework/src/components/runtime/DataTable.tsx +12 -10
- package/.framework/src/components/ui/sheet.tsx +24 -26
- package/.framework/src/components/ui/textarea.tsx +74 -48
- package/.framework/src/hooks/useBranding.ts +47 -0
- package/.framework/src/hooks/useEntityList.ts +12 -9
- package/.framework/src/hooks/useListSchema.ts +108 -58
- package/.framework/src/instructions/README.md +57 -0
- package/.framework/src/instructions/ai-agents.md +69 -0
- package/.framework/src/instructions/alerts.md +80 -0
- package/.framework/src/instructions/api-keys.md +87 -0
- package/.framework/src/instructions/apps.md +77 -0
- package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
- package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
- package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
- package/.framework/src/instructions/concepts/automation.md +186 -0
- package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
- package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
- package/.framework/src/instructions/concepts/design-schema.md +152 -0
- package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
- package/.framework/src/instructions/concepts/observability.md +116 -0
- package/.framework/src/instructions/concepts/permissions.md +150 -0
- package/.framework/src/instructions/concepts/runtime-records.md +111 -0
- package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
- package/.framework/src/instructions/concepts/spine-way.md +172 -0
- package/.framework/src/instructions/deliveries.md +59 -0
- package/.framework/src/instructions/guide/00-orientation.md +106 -0
- package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
- package/.framework/src/instructions/guide/02-project-map.md +147 -0
- package/.framework/src/instructions/guide/03-domain-model.md +223 -0
- package/.framework/src/instructions/guide/04-first-app.md +227 -0
- package/.framework/src/instructions/guide/05-glossary.md +183 -0
- package/.framework/src/instructions/integrations.md +96 -0
- package/.framework/src/instructions/internal/cil-markers.md +83 -0
- package/.framework/src/instructions/invites.md +60 -0
- package/.framework/src/instructions/kb.md +80 -0
- package/.framework/src/instructions/logs.md +77 -0
- package/.framework/src/instructions/observability.md +61 -0
- package/.framework/src/instructions/operations/deployment.md +115 -0
- package/.framework/src/instructions/operations/migrations.md +125 -0
- package/.framework/src/instructions/operations/security-checklist.md +148 -0
- package/.framework/src/instructions/operations/troubleshooting.md +170 -0
- package/.framework/src/instructions/operations/upgrade.md +116 -0
- package/.framework/src/instructions/pipeline-executions.md +62 -0
- package/.framework/src/instructions/pipelines.md +78 -0
- package/.framework/src/instructions/prompts.md +95 -0
- package/.framework/src/instructions/reference/api/admin-data.md +162 -0
- package/.framework/src/instructions/reference/api/auth.md +113 -0
- package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
- package/.framework/src/instructions/reference/app-manifest.md +150 -0
- package/.framework/src/instructions/reference/cli.md +124 -0
- package/.framework/src/instructions/reference/database.md +132 -0
- package/.framework/src/instructions/reference/design-schema.md +280 -0
- package/.framework/src/instructions/reference/env-vars.md +133 -0
- package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
- package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
- package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
- package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
- package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
- package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
- package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
- package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
- package/.framework/src/instructions/reference/functions/apps.md +140 -0
- package/.framework/src/instructions/reference/functions/auth.md +99 -0
- package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
- package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
- package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
- package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
- package/.framework/src/instructions/reference/functions/index.md +103 -0
- package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
- package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
- package/.framework/src/instructions/reference/functions/integrations.md +130 -0
- package/.framework/src/instructions/reference/functions/invites.md +182 -0
- package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
- package/.framework/src/instructions/reference/functions/kb.md +166 -0
- package/.framework/src/instructions/reference/functions/logs.md +141 -0
- package/.framework/src/instructions/reference/functions/observability.md +105 -0
- package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
- package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
- package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
- package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
- package/.framework/src/instructions/reference/functions/roles.md +121 -0
- package/.framework/src/instructions/reference/functions/security.md +140 -0
- package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
- package/.framework/src/instructions/reference/functions/system.md +106 -0
- package/.framework/src/instructions/reference/functions/tests.md +97 -0
- package/.framework/src/instructions/reference/functions/timers.md +117 -0
- package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
- package/.framework/src/instructions/reference/functions/triggers.md +140 -0
- package/.framework/src/instructions/reference/functions/types.md +146 -0
- package/.framework/src/instructions/reference/seeds.md +126 -0
- package/.framework/src/instructions/reference/shared-modules.md +127 -0
- package/.framework/src/instructions/reference/spine-config.md +114 -0
- package/.framework/src/instructions/roles.md +80 -0
- package/.framework/src/instructions/runtime-detail.md +70 -0
- package/.framework/src/instructions/runtime-list.md +77 -0
- package/.framework/src/instructions/security.md +67 -0
- package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
- package/.framework/src/instructions/tasks/add-a-role.md +96 -0
- package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
- package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
- package/.framework/src/instructions/tasks/add-a-type.md +151 -0
- package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
- package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
- package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
- package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
- package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
- package/.framework/src/instructions/tasks/test-your-code.md +108 -0
- package/.framework/src/instructions/tasks/write-a-function.md +132 -0
- package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
- package/.framework/src/instructions/testing.md +59 -0
- package/.framework/src/instructions/timers.md +82 -0
- package/.framework/src/instructions/triggers.md +69 -0
- package/.framework/src/instructions/types.md +93 -0
- package/.framework/src/lib/branding.ts +128 -0
- package/.framework/src/pages/NoAppPage.tsx +3 -1
- package/.framework/src/pages/NotFoundPage.tsx +62 -31
- package/.framework/src/pages/SetupPage.tsx +5 -2
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
- package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
- package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
- package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
- package/.framework/src/pages/admin/AppsPage.tsx +104 -66
- package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
- package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
- package/.framework/src/pages/admin/LogsPage.tsx +144 -76
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
- package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
- package/.framework/src/pages/admin/RolesPage.tsx +86 -43
- package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
- package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
- package/.framework/src/pages/admin/TimersPage.tsx +90 -62
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
- package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
- package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
- package/.framework/src/pages/admin/TypesPage.tsx +90 -48
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
- package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
- package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
- package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
- package/.framework/src/pages/auth/LoginPage.tsx +4 -2
- package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
- package/.framework/templates/agent/AGENTS.md +150 -0
- package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
- package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
- package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
- package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
- package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
- package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
- package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
- package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
- package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
- package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
- package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
- package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
- package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
- package/.framework/templates/project.json +5 -1
- package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
- package/.framework/tests/unit/agent-files.test.ts +355 -0
- package/.framework/tests/unit/agent-runner.test.ts +496 -47
- package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
- package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
- package/.framework/tests/unit/bootstrap.test.ts +242 -0
- package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
- package/.framework/tests/unit/debug-auth.test.ts +38 -0
- package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
- package/.framework/tests/unit/episode-lease.test.ts +695 -0
- package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
- package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
- package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
- package/.framework/tests/unit/logging-app-id.test.ts +75 -0
- package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
- package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
- package/.framework/tests/unit/principal-cache.test.ts +15 -0
- package/.framework/tests/unit/system-context-denial.test.ts +145 -0
- package/.framework/tests/unit/system-cron.test.ts +240 -0
- package/.framework/tests/unit/timers.test.ts +159 -0
- package/.framework/tests/unit/trigger-engine.test.ts +278 -91
- package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
- package/README.md +125 -166
- package/config/tsconfig.cli.json +2 -2
- package/netlify.toml +5 -0
- package/package.json +15 -4
- package/templates/project/package-lock.json +2 -2
- package/.framework/docs/admin-routes.md +0 -76
- package/.framework/docs/api-docs-progress.md +0 -38
- package/.framework/docs/api-governance.md +0 -146
- package/.framework/docs/api-testing-results.md +0 -212
- package/.framework/docs/assembly-launch-guide.md +0 -138
- package/.framework/docs/audit-results.md +0 -590
- package/.framework/docs/db-api-inventory.md +0 -95
- package/.framework/docs/gap-remediation-backlog.md +0 -103
- package/.framework/docs/ui-adoption-verification.md +0 -111
- package/.framework/docs/ui-api-coverage.md +0 -84
- package/.framework/docs/v2-compatibility-audit.md +0 -228
- package/.framework/mcp/index.ts +0 -133
- package/.framework/mcp/load-env.ts +0 -60
- package/.framework/mcp/shared/app-manifest.ts +0 -275
- package/.framework/mcp/shared/db.ts +0 -477
- package/.framework/mcp/shared/kb.ts +0 -118
- package/.framework/mcp/tools/architect.ts +0 -431
- package/.framework/mcp/tools/cil/context.ts +0 -345
- package/.framework/mcp/tools/cil/graph.ts +0 -242
- package/.framework/mcp/tools/cil/ingest.ts +0 -1217
- package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
- package/.framework/mcp/tools/cil/pipeline.ts +0 -767
- package/.framework/mcp/tools/cil/project.ts +0 -791
- package/.framework/mcp/tools/cil/retrieval.ts +0 -336
- package/.framework/mcp/tools/cil/search-core.ts +0 -454
- package/.framework/mcp/tools/cil/search.ts +0 -97
- package/.framework/mcp/tools/cil/suggest.ts +0 -186
- package/.framework/mcp/tools/cil/validate.ts +0 -1369
- package/.framework/mcp/tools/kb/delete.ts +0 -139
- package/.framework/mcp/tools/kb/index.ts +0 -52
- package/.framework/mcp/tools/kb/ingest.ts +0 -84
- package/.framework/mcp/tools/kb/list-types.ts +0 -102
- package/.framework/mcp/tools/kb/publish.ts +0 -100
- package/.framework/mcp/tools/kb/reindex.ts +0 -86
- package/.framework/mcp/tools/kb/revoke.ts +0 -85
- package/.framework/mcp/tools/kb/search.ts +0 -101
- package/.framework/mcp/tools/kb/upsert.ts +0 -125
- 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).
|