spine-framework 0.4.4 → 0.5.0-rc.5
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/install-app.ts +183 -13
- 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-app.ts +127 -1
- 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/app-manifest.ts +2 -0
- package/.framework/functions/_shared/hash-api-key.ts +183 -79
- package/.framework/functions/_shared/logging.ts +562 -224
- package/.framework/functions/_shared/middleware.ts +47 -44
- package/.framework/functions/_shared/permissions.ts +4 -0
- package/.framework/functions/admin-data.ts +51 -28
- package/.framework/functions/ai-agents.ts +10 -6
- package/.framework/functions/api-keys.ts +301 -122
- package/.framework/functions/apps.ts +63 -29
- 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 +149 -78
- 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 +278 -148
- package/.framework/functions/pipeline-executions.ts +1 -1
- 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 +240 -127
- package/.framework/functions/tests.ts +7 -6
- package/.framework/functions/timers.ts +310 -107
- package/.framework/functions/triggers.ts +28 -13
- package/.framework/functions/types.ts +13 -10
- 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/migrations/074_app_route_prefix_domains.sql +168 -0
- package/.framework/migrations/075_security_definer_grants.sql +157 -0
- 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 +88 -79
- package/.framework/src/components/admin/EntitySelect.tsx +181 -0
- package/.framework/src/components/admin/PageInstructions.tsx +86 -127
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
- package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
- package/.framework/src/components/layout/Sidebar.tsx +300 -177
- 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/contexts/AppContext.tsx +5 -12
- package/.framework/src/contexts/AuthContext.tsx +17 -9
- package/.framework/src/hooks/useApps.ts +18 -22
- package/.framework/src/hooks/useEntityList.ts +12 -9
- package/.framework/src/hooks/useListSchema.ts +108 -58
- package/.framework/src/instructions/README.md +74 -0
- package/.framework/src/instructions/accounts.md +97 -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 +228 -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/internal/docs-contract.md +158 -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/notifications.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 +170 -0
- package/.framework/src/instructions/operations/troubleshooting.md +170 -0
- package/.framework/src/instructions/operations/upgrade.md +116 -0
- package/.framework/src/instructions/people.md +91 -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 +184 -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/frontend-sdk.md +207 -0
- package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
- package/.framework/src/instructions/reference/functions/admin-data.md +197 -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-collaboration.md +106 -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/document-your-app.md +117 -0
- package/.framework/src/instructions/tasks/package-and-install-an-app.md +136 -0
- package/.framework/src/instructions/tasks/register-an-app.md +137 -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 +84 -0
- package/.framework/src/instructions/triggers.md +69 -0
- package/.framework/src/instructions/types.md +93 -0
- package/.framework/src/lib/app-mount.ts +88 -0
- package/.framework/src/pages/NotFoundPage.tsx +62 -31
- 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/LoginPage.tsx +3 -2
- package/.framework/src/pages/auth/RegisterPage.tsx +16 -8
- package/.framework/templates/agent/AGENTS.md +167 -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 +79 -0
- package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
- package/.framework/templates/agent/skills/spine-new-app/SKILL.md +79 -0
- package/.framework/templates/agent/skills/spine-new-function/SKILL.md +71 -0
- package/.framework/templates/agent/skills/spine-new-type/SKILL.md +72 -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 +4 -1
- package/.framework/tests/unit/account-scoping-gates.test.ts +394 -0
- package/.framework/tests/unit/agent-files.test.ts +355 -0
- package/.framework/tests/unit/agent-runner.test.ts +479 -44
- 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/app-mount.test.ts +80 -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/integration-outbound-credentials.test.ts +167 -0
- package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
- package/.framework/tests/unit/logging-app-id.test.ts +75 -0
- package/.framework/tests/unit/security-definer-grants.test.ts +154 -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/README.md +125 -166
- package/config/tsconfig.cli.json +2 -2
- 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,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).
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: ai-interactions
|
|
3
|
+
audience: both
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/ai-interactions.ts
|
|
9
|
+
- .framework/functions/ai-agents.ts
|
|
10
|
+
- .framework/functions/_shared/agent-runner.ts
|
|
11
|
+
- .framework/functions/_shared/pipeline-runner.ts
|
|
12
|
+
- .framework/functions/_shared/middleware.ts
|
|
13
|
+
- .framework/functions/_shared/funnel.ts
|
|
14
|
+
- .framework/functions/_shared/resolve-ids.ts
|
|
15
|
+
- public/_redirects
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# ai-interactions
|
|
19
|
+
|
|
20
|
+
## What it does
|
|
21
|
+
|
|
22
|
+
`ai-interactions` (`.framework/functions/ai-interactions.ts`) writes the
|
|
23
|
+
feedback and observability side of the AI loop as `items` rows. It is *not*
|
|
24
|
+
the conversation engine:
|
|
25
|
+
|
|
26
|
+
- Interactive turns run through `POST /api/ai-agents?action=run` — `runAgent`
|
|
27
|
+
in `.framework/functions/_shared/agent-runner.ts` drives a `threads` row,
|
|
28
|
+
persists `messages`, and manages the episode lease.
|
|
29
|
+
- Stateless pipeline inference runs through the `agent_inference` stage
|
|
30
|
+
(`.framework/functions/_shared/pipeline-runner.ts`) — it POSTs `{ context,
|
|
31
|
+
model, temperature, max_tokens }` to `config.webhook_url` via
|
|
32
|
+
`integration-outbound` (`generic-http`), and returns a marked mock response
|
|
33
|
+
when `webhook_url` is unset.
|
|
34
|
+
- `ai-interactions` records what happened around those paths: customer
|
|
35
|
+
thumbs feedback, per-interaction telemetry rows, and draft learning
|
|
36
|
+
proposals.
|
|
37
|
+
|
|
38
|
+
Actions (dispatched on `?action=`):
|
|
39
|
+
|
|
40
|
+
| Action | Method | Purpose |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `submit_feedback` | POST | Record `helpful`/`not_helpful` on an AI message; escalate or resolve the ticket. |
|
|
43
|
+
| `create_interaction` | POST | Insert a telemetry item (default type `ai_feedback_record`, overridable via `type_slug`). The pipeline `create_interaction` stage lazy-imports this handler. |
|
|
44
|
+
| `update_interaction` | PATCH or POST | Merge allow-listed fields into an existing interaction item. |
|
|
45
|
+
| `create_cil_proposal` | POST | Insert a `status: 'draft'` learning-proposal item (fixed `cil_*` type slug) from ticket context and source references. |
|
|
46
|
+
| `list` | GET | List interaction items for the caller's account (default action). |
|
|
47
|
+
|
|
48
|
+
## Route and methods
|
|
49
|
+
|
|
50
|
+
- `/api/ai-interactions?action=<action>` — `public/_redirects` maps `/api/*`
|
|
51
|
+
to `/.netlify/functions/:splat`; the export `handler` is a
|
|
52
|
+
`createHandler`-wrapped dispatcher reading `ctx.query.action` and
|
|
53
|
+
`ctx.query.method` (populated from the HTTP method).
|
|
54
|
+
- `GET` with no `action` (or an unrecognized one) falls through to `list`;
|
|
55
|
+
other invalid action/method pairs throw `Invalid action or method`.
|
|
56
|
+
|
|
57
|
+
## Authentication
|
|
58
|
+
|
|
59
|
+
`createHandler` resolves a principal and rejects anonymous requests with 401.
|
|
60
|
+
There is no role gate — any authenticated human or machine principal may call
|
|
61
|
+
the actions. `list` reads through `ctx.db` scoped to `ctx.accountId`;
|
|
62
|
+
`create_interaction`/`create_cil_proposal`/`update_interaction` write through
|
|
63
|
+
admin-data `create`/`update`, so the caller's permissions, audit, and
|
|
64
|
+
triggers apply. `submit_feedback` writes `messages`/`items` via `adminDb`
|
|
65
|
+
directly (service role) — it does not check that the caller owns the ticket.
|
|
66
|
+
|
|
67
|
+
## Request contract
|
|
68
|
+
|
|
69
|
+
`submit_feedback` (POST):
|
|
70
|
+
|
|
71
|
+
| Field | Required | Notes |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `message_id` | yes | `messages.id` of the AI reply. |
|
|
74
|
+
| `ticket_id` | yes | `items.id` of the ticket under discussion. |
|
|
75
|
+
| `feedback` | yes | `'helpful'` or `'not_helpful'`. |
|
|
76
|
+
| `reason` | no | Free text, truncated to 2000 chars. |
|
|
77
|
+
|
|
78
|
+
`create_interaction` (POST): `thread_id` *or* `ai_message_id` required.
|
|
79
|
+
Optional: `type_slug` (default `ai_feedback_record`; skipped silently when
|
|
80
|
+
the type isn't installed), `retrieved_sources`, `per_source_feedback`,
|
|
81
|
+
`ai_confidence`, `retrieval_confidence`, `kb_candidate`, `answer_quality`,
|
|
82
|
+
`assistance_outcome`, `eval_case_id`, the learning-proposal linkage id,
|
|
83
|
+
`review_notes`, `reviewed_by`, `reviewed_at`. The record is titled
|
|
84
|
+
`AI Interaction — <date>`, `status: 'active'`.
|
|
85
|
+
|
|
86
|
+
`update_interaction` (PATCH/POST): `id` required plus ≥1 allow-listed field:
|
|
87
|
+
`kb_article_id`, `human_message_id`, `human_edits_made`, `customer_feedback`,
|
|
88
|
+
`kb_candidate`, `reuse_count`, `per_source_feedback`, `answer_quality`,
|
|
89
|
+
`assistance_outcome`, `eval_case_id`, the learning-proposal linkage id,
|
|
90
|
+
`review_notes`, `reviewed_by`, `reviewed_at`.
|
|
91
|
+
|
|
92
|
+
`create_cil_proposal` (POST): `ticket_id` and `proposed_summary` required;
|
|
93
|
+
optional `thread_id`, `ai_message_id`, `source_message_ids`,
|
|
94
|
+
`source_chunk_ids`, `proposed_tags`. Returns `{ skipped: true }` when the
|
|
95
|
+
proposal type isn't installed.
|
|
96
|
+
|
|
97
|
+
`list` (GET): query params `feedback`, `kb_candidate`, `limit` (default 50),
|
|
98
|
+
`offset` (default 0).
|
|
99
|
+
|
|
100
|
+
## Response contract
|
|
101
|
+
|
|
102
|
+
- `submit_feedback` → `{ message_id, ticket_id, feedback, escalated }` where
|
|
103
|
+
`escalated` is `feedback === 'not_helpful'`.
|
|
104
|
+
- `create_interaction` / `create_cil_proposal` → the created `items` record,
|
|
105
|
+
or `{ skipped: true, reason: '<type> type not found' }` when the type slug
|
|
106
|
+
can't be resolved — **not** an error.
|
|
107
|
+
- `update_interaction` → the admin-data `update` result.
|
|
108
|
+
- `list` → an array of `items` rows (filtered to the `ai_feedback_record`
|
|
109
|
+
type when that type exists).
|
|
110
|
+
|
|
111
|
+
## Side effects
|
|
112
|
+
|
|
113
|
+
- `submit_feedback` stamps `messages.data.feedback` (+ `feedback_reason`,
|
|
114
|
+
and for `not_helpful` a `feedback_diagnosis` + `needs_review: true`).
|
|
115
|
+
Diagnosis is heuristic over the message's own evidence trail:
|
|
116
|
+
`missing_knowledge` (no retrieved sources), `retrieval_miss` (mean
|
|
117
|
+
similarity < 0.35), `reasoning_error` (sources retrieved but none cited),
|
|
118
|
+
else `wrong_answer`.
|
|
119
|
+
- Ticket lifecycle side effects on the `items` row: `not_helpful` sets
|
|
120
|
+
`status: 'human_assigned'` from any state except `human_assigned`/
|
|
121
|
+
`in_progress`, reopening `resolved`/`closed` tickets (`reopened_at`,
|
|
122
|
+
`reopen_reason: 'thumbs_down'`, resolution stamps cleared). `helpful` on a
|
|
123
|
+
`pending_resolution` ticket resolves it with `resolved_via:
|
|
124
|
+
ai_confirmed|human_confirmed` (chosen from `pending_proposed_by`) and
|
|
125
|
+
`aim_resolved_by_ai`.
|
|
126
|
+
- The first `items` row whose `data.ai_message_id` matches gets
|
|
127
|
+
`customer_feedback` (+reason/diagnosis) stamped.
|
|
128
|
+
- `ai_interaction.feedback_submitted` audit event plus a `funnel_signal`
|
|
129
|
+
`feedback_submitted` journey signal (fire-and-forget).
|
|
130
|
+
|
|
131
|
+
## Errors
|
|
132
|
+
|
|
133
|
+
| Condition | Error |
|
|
134
|
+
|---|---|
|
|
135
|
+
| Missing inputs | `message_id is required` · `ticket_id is required` · `id is required` · `thread_id or ai_message_id is required` · `proposed_summary is required` |
|
|
136
|
+
| Bad enum | `feedback must be "helpful" or "not_helpful"` |
|
|
137
|
+
| Empty patch | `No updatable fields provided` |
|
|
138
|
+
| Unknown interaction id | `Interaction record not found` |
|
|
139
|
+
| Bad dispatch | `Invalid action or method` |
|
|
140
|
+
|
|
141
|
+
## Agent notes
|
|
142
|
+
|
|
143
|
+
- Use `type_slug` on `create_interaction` to record under an app-specific
|
|
144
|
+
item type; the `{ skipped: true }` return makes the call safe to fire even
|
|
145
|
+
when the type isn't installed — check for `skipped` instead of treating it
|
|
146
|
+
as failure.
|
|
147
|
+
- `submit_feedback` is a lifecycle accelerator, not just a thumbs counter:
|
|
148
|
+
thumbs-up is the explicit customer confirmation that closes a
|
|
149
|
+
`pending_resolution` ticket, thumbs-down escalates and can reopen. Calling
|
|
150
|
+
it outside the support-ticket statuses is a no-op for the ticket fields.
|
|
151
|
+
- The pipeline `create_interaction` stage resolves `ai_message_id`/
|
|
152
|
+
`thread_id` from trigger data and passes `config.type_slug` through.
|
|
153
|
+
- `list` queries `ctx.db` — a machine principal sees account-scoped rows via
|
|
154
|
+
`adminDb`, a human sees RLS-filtered rows.
|
|
155
|
+
- See [AI agents, prompt configs, and embeddings](../../concepts/ai-and-prompts.md)
|
|
156
|
+
for how the run path, inference, and these records fit together.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: api-keys
|
|
3
|
+
audience: both
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/api-keys.ts
|
|
9
|
+
- .framework/functions/_shared/hash-api-key.ts
|
|
10
|
+
- .framework/functions/_shared/middleware.ts
|
|
11
|
+
- .framework/public/_redirects
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# api-keys
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
Mints, validates, revokes, and lists API keys — the `sk_…` credentials
|
|
19
|
+
that resolve to machine principals on every other endpoint. Also exposes
|
|
20
|
+
per-key usage logs.
|
|
21
|
+
|
|
22
|
+
## Route and methods
|
|
23
|
+
|
|
24
|
+
`/.netlify/functions/api-keys`, reachable as `/api/api-keys` (`/api/*` →
|
|
25
|
+
`/.netlify/functions/:splat`). Dispatch is by `?action=` plus method
|
|
26
|
+
(`?method=` can override the HTTP method):
|
|
27
|
+
|
|
28
|
+
| Call | Action |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `GET /api/api-keys` | `list` |
|
|
31
|
+
| `GET /api/api-keys?id=<uuid>` | `get` |
|
|
32
|
+
| `POST /api/api-keys` | `create` |
|
|
33
|
+
| `POST /api/api-keys?action=validate` | `validate` |
|
|
34
|
+
| `POST /api/api-keys?action=revoke` | `revoke` |
|
|
35
|
+
| `GET /api/api-keys?action=usage-logs` | `listUsageLogs` |
|
|
36
|
+
|
|
37
|
+
Unrecognized `action` values fall through to the method-based default
|
|
38
|
+
branch, so `GET`/`POST` still dispatch; only methods other than `GET`,
|
|
39
|
+
`POST` (or a matched action with the wrong method) reach
|
|
40
|
+
`Invalid action or method`.
|
|
41
|
+
|
|
42
|
+
## Authentication
|
|
43
|
+
|
|
44
|
+
Standard `createHandler`: `x-api-key`, internal secret, or
|
|
45
|
+
`Bearer <jwt>`; anonymous → 401. `list`, `create`, and `usage-logs`
|
|
46
|
+
additionally require `ctx.accountId` ("Account context required").
|
|
47
|
+
|
|
48
|
+
- System admins read via `adminDb` (all accounts); other principals use
|
|
49
|
+
`ctx.db` — JWT-scoped (RLS) for humans, service role for machines.
|
|
50
|
+
- There is **no machine-scope check** in this file. A machine principal
|
|
51
|
+
with an `account_id` can create/revoke/read keys within what its
|
|
52
|
+
service-role client reaches — treat key management as a trusted-surface
|
|
53
|
+
operation.
|
|
54
|
+
|
|
55
|
+
## Request contract
|
|
56
|
+
|
|
57
|
+
**list** (query): `integration_id`, `key_type`, `is_active` (`'true'`/
|
|
58
|
+
`'false'`), `expires_before`, `expires_after`, `limit` (default 100),
|
|
59
|
+
`offset` (default 0). Ordered `created_at` desc.
|
|
60
|
+
|
|
61
|
+
**get** (query): `id`.
|
|
62
|
+
|
|
63
|
+
**create** (body): `name` required. Optional: `slug` (defaults to a
|
|
64
|
+
slugified `name`), `key_type` (default `'inbound'`), `permissions`,
|
|
65
|
+
`scopes`, `rate_limit` (default `100`), `expires_at`, `metadata`,
|
|
66
|
+
`integration_id`, `app_id` (resolved via `resolveAppUuid`, falling back
|
|
67
|
+
to the `x-app-id` header's app), `machine_type` (default `'integration'`),
|
|
68
|
+
`is_internal` (default `false`). `account_id` and `is_active: true` are
|
|
69
|
+
stamped from context; `created_by` is the principal id.
|
|
70
|
+
|
|
71
|
+
**validate** (body): `key_value` required; `required_permissions`
|
|
72
|
+
optional array.
|
|
73
|
+
|
|
74
|
+
**revoke** (body): `id` required.
|
|
75
|
+
|
|
76
|
+
**usage-logs** (query): `api_key_id`, `response_status`, `success`,
|
|
77
|
+
`date_from`, `date_to`, `limit` (default 100), `offset` (default 0).
|
|
78
|
+
|
|
79
|
+
## Response contract
|
|
80
|
+
|
|
81
|
+
Standard `{ data, error, meta }` envelope.
|
|
82
|
+
|
|
83
|
+
- `create` → the inserted row (`id, name, key_type, rate_limit,
|
|
84
|
+
expires_at, is_active, created_at`) **plus `raw_key` — the only time
|
|
85
|
+
the secret is returned** — and `warning: 'Store this key now. It will
|
|
86
|
+
not be shown again.'` Raw key format: `sk_<32 hex chars>`.
|
|
87
|
+
- `validate` → `{ is_valid: true, api_key_id, integration_id, app_id,
|
|
88
|
+
account_id, scopes, permissions }`, or `{ is_valid: false, error }`
|
|
89
|
+
(HTTP 200 in both cases; `Insufficient permissions` carries
|
|
90
|
+
`api_key_id`).
|
|
91
|
+
- `list`/`get`/`revoke`/`usage-logs` → rows passed through
|
|
92
|
+
`sanitizeRecordData` + `omitSecretFields`, so `key_hash`/`key_value`-type
|
|
93
|
+
secret columns never leave the function. Only `key_prefix` (first 8
|
|
94
|
+
chars of the raw key) is stored in plaintext as the lookup index.
|
|
95
|
+
|
|
96
|
+
## Side effects
|
|
97
|
+
|
|
98
|
+
- `create` inserts an `api_keys` row storing `key_prefix` and a
|
|
99
|
+
PBKDF2-SHA256 `key_hash` (the raw key is never stored).
|
|
100
|
+
- `revoke` sets `is_active = false` (keys are never hard-deleted here).
|
|
101
|
+
- `emitLog` writes `api_key.created` / `api_key.revoked` audit entries.
|
|
102
|
+
- Note: `api_key_usage_logs` rows are written by `resolvePrincipal` on
|
|
103
|
+
every authenticated request that uses a key, not by this endpoint.
|
|
104
|
+
|
|
105
|
+
## Errors
|
|
106
|
+
|
|
107
|
+
| Condition | HTTP | Message |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| Missing `name` / `id` / `key_value` | 500 | `name is required` / `API key ID is required` / `key_value is required` |
|
|
110
|
+
| No account context | 500 | `Account context required` |
|
|
111
|
+
| Unknown action/method | 500 | `Invalid action or method` |
|
|
112
|
+
| DB failures | 500 | PostgREST message |
|
|
113
|
+
|
|
114
|
+
`validate` failures are payload-level (`is_valid: false`), not HTTP
|
|
115
|
+
errors.
|
|
116
|
+
|
|
117
|
+
## Agent notes
|
|
118
|
+
|
|
119
|
+
- `validate` checks permissions as **any-match**: a key passes if at least
|
|
120
|
+
one of its `permissions` entries appears in `required_permissions`.
|
|
121
|
+
Wildcard `*:*` in `scopes` is honored at principal resolution, not here.
|
|
122
|
+
- Key verification happens on prefix-matched candidates (`key_prefix` =
|
|
123
|
+
first 8 chars), then PBKDF2 verify — `validate` mirrors exactly what
|
|
124
|
+
`resolvePrincipal` does on a live request.
|
|
125
|
+
- To call `validate`/`revoke`/`get` a caller needs only to be
|
|
126
|
+
authenticated (no `ctx.accountId` check on those paths); `revoke` on a
|
|
127
|
+
machine principal runs with the service role — pin management calls to
|
|
128
|
+
humans or `*:*` keys in practice.
|
|
129
|
+
- `expires_at` is nullable; expiry is enforced at resolution time
|
|
130
|
+
(`expires_at.is.null OR expires_at >= now`).
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: apps
|
|
3
|
+
audience: both
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/apps.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/permissions.ts
|
|
11
|
+
- .framework/migrations/013_get_account_apps_role_filter.sql
|
|
12
|
+
- .framework/public/_redirects
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# apps
|
|
16
|
+
|
|
17
|
+
## What it does
|
|
18
|
+
|
|
19
|
+
CRUD over `public.apps` — the app registry that controls routing
|
|
20
|
+
(`route_prefix`, `renderer`), visibility (`min_role`, `required_roles`),
|
|
21
|
+
and install metadata (`version`, `app_type`, `source`,
|
|
22
|
+
`integration_deps`). Also exposes schema, availability, and version-bump
|
|
23
|
+
RPC pass-throughs.
|
|
24
|
+
|
|
25
|
+
## Route and methods
|
|
26
|
+
|
|
27
|
+
`/.netlify/functions/apps`, reachable as `/api/apps` (`/api/*` →
|
|
28
|
+
`/.netlify/functions/:splat`, `.framework/public/_redirects`). Dispatch
|
|
29
|
+
is by method plus `?action=`/`?id`/`?slug`:
|
|
30
|
+
|
|
31
|
+
| Call | Handler |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `GET /api/apps` | `list` (also `?action=list`) |
|
|
34
|
+
| `GET /api/apps?id=<uuid>` or `?slug=<s>` or `?action=get` | `get` |
|
|
35
|
+
| `GET /api/apps?action=schema&slug=<s>` | `getSchema` → RPC `get_app_schema` |
|
|
36
|
+
| `GET /api/apps?action=available&slug=<s>` | `checkAvailability` → RPC `is_app_available` |
|
|
37
|
+
| `POST /api/apps` | `create` |
|
|
38
|
+
| `POST /api/apps?action=version` | `updateVersion` → RPC `update_app_version` |
|
|
39
|
+
| `PATCH /api/apps?id=<uuid>` (or `id` in body) | `update` |
|
|
40
|
+
| `DELETE /api/apps?id=<uuid>` (or `id` in body) | `remove` |
|
|
41
|
+
|
|
42
|
+
## Authentication
|
|
43
|
+
|
|
44
|
+
Standard `createHandler` auth: `x-api-key` → machine principal,
|
|
45
|
+
`x-internal-secret` → internal principal, `Bearer <jwt>` → human;
|
|
46
|
+
anonymous → 401.
|
|
47
|
+
|
|
48
|
+
- `list` requires an account context (`ctx.accountId` or an explicit
|
|
49
|
+
`account_id` query param).
|
|
50
|
+
- `create` allows non-system-admins only when
|
|
51
|
+
`resolveFirstSurfacePermissions(principal.id, accountId, 'app',
|
|
52
|
+
'create')` grants `canCreate`.
|
|
53
|
+
- `update`/`remove` have no role gate in this file — field-level control
|
|
54
|
+
comes from `validateUpdatePermissions` (update only) and RLS on
|
|
55
|
+
`ctx.db` for human callers. Machine principals run `ctx.db` as the
|
|
56
|
+
service role.
|
|
57
|
+
|
|
58
|
+
## Request contract
|
|
59
|
+
|
|
60
|
+
**list** (query): `account_id` (defaults to `ctx.accountId`),
|
|
61
|
+
`include_system` (default true; `'false'` excludes), `include_inactive`
|
|
62
|
+
(`'true'` to include). Backed by the `get_account_apps` RPC, which
|
|
63
|
+
filters by ownership, active state, `required_roles`/`min_role` against
|
|
64
|
+
the caller's role slugs unless the caller is a system admin
|
|
65
|
+
(`.framework/migrations/013_get_account_apps_role_filter.sql`).
|
|
66
|
+
|
|
67
|
+
**get** (query): `id` or `slug` (active rows only).
|
|
68
|
+
|
|
69
|
+
**getSchema** / **checkAvailability** (query): `slug`.
|
|
70
|
+
|
|
71
|
+
**create** (body): `slug`, `name` required. Optional with defaults:
|
|
72
|
+
`version` `'1.0.0'`, `app_type` `'custom'`, `source` `'custom'`,
|
|
73
|
+
`owner_account_id` → caller's account, `config` `{}`, `nav_items` `[]`,
|
|
74
|
+
`min_role` `'member'`, `required_roles` `[]`, `integration_deps` `[]`, `metadata` `{}`,
|
|
75
|
+
`route_prefix` `'/' + slug`, `renderer` `'generic'`; `is_active: true`,
|
|
76
|
+
`is_system: false` are forced.
|
|
77
|
+
|
|
78
|
+
**update** (`id` in body or query + body): **no field allowlist** —
|
|
79
|
+
every body key except `id` is written verbatim plus `updated_at`, after
|
|
80
|
+
`validateUpdatePermissions(ctx, updates, current, 'apps')` passes.
|
|
81
|
+
|
|
82
|
+
**remove** (`id` in body or query): sets `is_active = false`.
|
|
83
|
+
|
|
84
|
+
**updateVersion** (body): `id`, `version` required.
|
|
85
|
+
|
|
86
|
+
## Response contract
|
|
87
|
+
|
|
88
|
+
Standard `{ data, error, meta }` envelope.
|
|
89
|
+
|
|
90
|
+
- `list` → rows from `get_account_apps`, each passed through
|
|
91
|
+
`sanitizeRecordData`. Non-system-admins receive only the routing
|
|
92
|
+
whitelist (`id, slug, name, description, route_prefix, min_role,
|
|
93
|
+
required_roles, preview_domain, prod_domain, is_active, is_system,
|
|
94
|
+
renderer, created_at, updated_at`); admins get the full row.
|
|
95
|
+
- `get` → sanitized row. `getSchema` → raw RPC result.
|
|
96
|
+
`checkAvailability` → `{ available: boolean }`. `updateVersion` →
|
|
97
|
+
`{ success: true }`.
|
|
98
|
+
|
|
99
|
+
## Side effects
|
|
100
|
+
|
|
101
|
+
- `create` inserts into `public.apps`; a duplicate `slug` is rejected by
|
|
102
|
+
a pre-check before insert (slug has a global `UNIQUE` constraint in
|
|
103
|
+
`.framework/migrations/001_core_schema.sql`).
|
|
104
|
+
- `update`/`remove` write `updated_at`; remove is a soft delete.
|
|
105
|
+
- `emitLog` writes `app.created`, `app.updated`, `app.deleted`, and
|
|
106
|
+
`app.version_updated` audit rows to `public.logs`.
|
|
107
|
+
|
|
108
|
+
## Errors
|
|
109
|
+
|
|
110
|
+
| Condition | HTTP | Message |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| No account context (list, available) | 500 | `Account context required` |
|
|
113
|
+
| Missing slug/id | 500 | `App ID or slug is required` / `App slug is required` / `App ID is required` |
|
|
114
|
+
| Missing create fields | 500 | `slug and name are required` |
|
|
115
|
+
| Non-admin without create grant | 500 | `Insufficient permissions to create apps` |
|
|
116
|
+
| Duplicate slug | 500 | `App slug already exists` |
|
|
117
|
+
| Missing row (update/remove) | 500 | `App not found` |
|
|
118
|
+
| Missing version args | 500 | `App ID and version are required` |
|
|
119
|
+
| Unknown method | 500 | `Unsupported method: <METHOD>` |
|
|
120
|
+
|
|
121
|
+
Validation failures surface as HTTP 500 — thrown `Error`s without a
|
|
122
|
+
`statusCode` map to 500 in the shared error path.
|
|
123
|
+
|
|
124
|
+
## Agent notes
|
|
125
|
+
|
|
126
|
+
- The four RPCs called here — `get_app_schema`, `is_app_available`,
|
|
127
|
+
`update_app_version` (plus `get_type_schema` for types) — are provided
|
|
128
|
+
by `.framework/migrations/070_app_type_schema_rpcs.sql`;
|
|
129
|
+
`get_account_apps` exists via
|
|
130
|
+
`.framework/migrations/013_get_account_apps_role_filter.sql`. Installs
|
|
131
|
+
missing migration 070 will see `function does not exist` errors.
|
|
132
|
+
- `update` will write **any** column present in the body (including
|
|
133
|
+
`slug`, `is_system`, `owner_account_id`) — the only guard is
|
|
134
|
+
`validateUpdatePermissions`, which is config-object (second-surface)
|
|
135
|
+
logic. Treat it as an admin-trusted endpoint.
|
|
136
|
+
- `remove` does not check `is_system` or permissions at all beyond
|
|
137
|
+
authentication + RLS for human callers.
|
|
138
|
+
- `renderer` defaults to `'generic'` on create — custom-renderer apps
|
|
139
|
+
must pass `renderer: 'custom'` explicitly (see
|
|
140
|
+
[Apps and renderers](../../concepts/apps-and-renderers.md)).
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: auth
|
|
3
|
+
audience: both
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/auth.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/principal.ts
|
|
11
|
+
- .framework/public/_redirects
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# auth
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
Returns the caller's resolved user context: the `people` row bound to the
|
|
19
|
+
JWT, its role and permissions, and the set of accounts the person can
|
|
20
|
+
reach through the account hierarchy. This is the endpoint the frontend
|
|
21
|
+
calls after login to learn "who am I".
|
|
22
|
+
|
|
23
|
+
## Route and methods
|
|
24
|
+
|
|
25
|
+
`/.netlify/functions/auth`, reachable as `/api/auth` (`/api/*` →
|
|
26
|
+
`/.netlify/functions/:splat`). Dispatch is on `ctx.query.method`, which
|
|
27
|
+
the middleware fills from the HTTP method but a `?method=` query
|
|
28
|
+
parameter can override:
|
|
29
|
+
|
|
30
|
+
| Call | Result |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `GET /api/auth` | `context` — the user-context payload below |
|
|
33
|
+
| `GET /api/auth?method=HEALTH` | `health` — `{ status: 'healthy', timestamp, service: 'auth' }` |
|
|
34
|
+
| any other method | error `Unsupported method: <method>` |
|
|
35
|
+
|
|
36
|
+
`HEALTH` is a query-param convention, not an HTTP method — there is no
|
|
37
|
+
`?action=` dispatch.
|
|
38
|
+
|
|
39
|
+
## Authentication
|
|
40
|
+
|
|
41
|
+
Standard `createHandler`: `x-api-key`, internal secret, or
|
|
42
|
+
`Bearer <jwt>`; anonymous is rejected with 401, and `context` re-checks
|
|
43
|
+
`principal.id !== 'anonymous'`. All queries run on `ctx.db` — JWT-scoped
|
|
44
|
+
for humans (RLS applies), service role for machines/system. In practice
|
|
45
|
+
`context` is human-only: a machine principal's id is an API-key id, not a
|
|
46
|
+
`people.id`, so the person lookup returns "User not found".
|
|
47
|
+
|
|
48
|
+
## Request contract
|
|
49
|
+
|
|
50
|
+
No parameters for `context` — identity comes entirely from the resolved
|
|
51
|
+
principal. `?method=HEALTH` selects the health check.
|
|
52
|
+
|
|
53
|
+
## Response contract
|
|
54
|
+
|
|
55
|
+
`context` returns inside the standard `{ data, error, meta }` envelope:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"id": "<people.id>",
|
|
60
|
+
"email": "...",
|
|
61
|
+
"full_name": "...",
|
|
62
|
+
"account_id": "<uuid>",
|
|
63
|
+
"account": { "id", "slug", "display_name", "parent_id" },
|
|
64
|
+
"roles": ["<role slug>"],
|
|
65
|
+
"permissions": ["read", "write", ...],
|
|
66
|
+
"is_system_admin": false,
|
|
67
|
+
"status": "active",
|
|
68
|
+
"accessible_accounts": [{ "id", "slug", "display_name", "parent_id" }]
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- `roles` is a single-element array of the person's role slug.
|
|
73
|
+
- `permissions` is `['read', 'write', 'admin', 'system']` for
|
|
74
|
+
`system_admin`, else the role row's `permissions` array (possibly `[]`).
|
|
75
|
+
- `accessible_accounts` comes from the `get_accessible_accounts` RPC,
|
|
76
|
+
raced against a 5-second timeout; on failure or timeout it degrades to
|
|
77
|
+
`[]` rather than erroring.
|
|
78
|
+
|
|
79
|
+
## Side effects
|
|
80
|
+
|
|
81
|
+
None — read-only (the middleware still emits request spans/logs).
|
|
82
|
+
|
|
83
|
+
## Errors
|
|
84
|
+
|
|
85
|
+
| Condition | HTTP | Message |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| Anonymous | 401 | `Authentication required` |
|
|
88
|
+
| `people` row missing/inactive | 500 | `User not found: <principal.id>` |
|
|
89
|
+
| Unsupported method | 500 | `Unsupported method: <method>` |
|
|
90
|
+
|
|
91
|
+
## Agent notes
|
|
92
|
+
|
|
93
|
+
- `is_system_admin` and `permissions` are derived here, not authoritative
|
|
94
|
+
checks — server-side endpoints re-evaluate roles from the principal.
|
|
95
|
+
- The 5-second `get_accessible_accounts` timeout means a slow hierarchy
|
|
96
|
+
query yields `accessible_accounts: []`, not an error; clients should not
|
|
97
|
+
treat an empty list as proof of a single-account deployment.
|
|
98
|
+
- Role/account changes can lag up to ~60 seconds because human principals
|
|
99
|
+
are cached per warm function instance.
|