spine-framework 0.3.124 → 0.3.126
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.framework/cli/commands/agents.ts +35 -37
- package/.framework/cli/commands/auth.ts +35 -34
- package/.framework/cli/commands/create-app.ts +104 -40
- package/.framework/cli/commands/dev.ts +104 -69
- package/.framework/cli/commands/doctor.ts +179 -69
- package/.framework/cli/commands/generate.ts +83 -143
- package/.framework/cli/commands/init.ts +72 -34
- package/.framework/cli/commands/install-app.ts +260 -125
- package/.framework/cli/commands/items.ts +33 -44
- package/.framework/cli/commands/migrate.ts +122 -40
- package/.framework/cli/commands/migrations.ts +33 -40
- package/.framework/cli/commands/pipelines.ts +31 -39
- package/.framework/cli/commands/status.ts +47 -23
- package/.framework/cli/commands/system.ts +47 -37
- package/.framework/cli/commands/test.ts +112 -68
- package/.framework/cli/commands/validate-app.ts +215 -121
- package/.framework/cli/context.ts +112 -130
- package/.framework/cli/env-loader.ts +47 -18
- package/.framework/cli/index.ts +81 -42
- package/.framework/docs/runtime-types.md +1024 -0
- package/.framework/functions/_shared/agent-runner.ts +1296 -961
- package/.framework/functions/_shared/app-manifest.ts +82 -68
- package/.framework/functions/_shared/audit.ts +135 -83
- package/.framework/functions/_shared/{cil-state.ts → cil.ts} +158 -56
- package/.framework/functions/_shared/db.ts +80 -126
- package/.framework/functions/_shared/hash-api-key.ts +63 -0
- package/.framework/functions/_shared/index.ts +224 -335
- package/.framework/functions/_shared/kb.ts +319 -0
- package/.framework/functions/_shared/llm.ts +92 -0
- package/.framework/functions/_shared/logging.ts +288 -0
- package/.framework/functions/_shared/middleware.ts +329 -365
- package/.framework/functions/_shared/outcomes.ts +36 -0
- package/.framework/functions/_shared/permissions.ts +359 -643
- package/.framework/functions/_shared/pipeline-runner.ts +243 -316
- package/.framework/functions/_shared/principal.ts +376 -468
- package/.framework/functions/_shared/resolve-credentials.ts +102 -0
- package/.framework/functions/_shared/resolve-ids.ts +188 -16
- package/.framework/functions/_shared/retry-deliveries.ts +104 -0
- package/.framework/functions/_shared/schema-utils.ts +611 -373
- package/.framework/functions/_shared/testing.ts +207 -137
- package/.framework/functions/_shared/tracing.ts +276 -0
- package/.framework/functions/_shared/trigger-engine.ts +159 -211
- package/.framework/functions/_shared/webhook-registration.ts +95 -81
- package/.framework/functions/_shared/webhook-registry.ts +153 -75
- package/.framework/functions/account-nodes.ts +31 -53
- package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
- package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
- package/.framework/functions/adapters/openai-outbound.ts +40 -0
- package/.framework/functions/admin-data.ts +685 -537
- package/.framework/functions/admin-funnel.ts +31 -20
- package/.framework/functions/admin-install-funnel.ts +47 -28
- package/.framework/functions/admin-signals.ts +47 -29
- package/.framework/functions/ai-agents.ts +122 -147
- package/.framework/functions/ai-interactions.ts +180 -90
- package/.framework/functions/api-keys.ts +250 -192
- package/.framework/functions/apps.ts +203 -197
- package/.framework/functions/auth.ts +98 -91
- package/.framework/functions/cil-agent-ingest.ts +773 -0
- package/.framework/functions/cil-agent.ts +592 -0
- package/.framework/functions/cil-callmap.ts +1227 -0
- package/.framework/functions/cil-config.ts +583 -0
- package/.framework/functions/cil-context.ts +445 -0
- package/.framework/functions/cil-doc.ts +1752 -0
- package/.framework/functions/cil-embeddings.ts +1202 -0
- package/.framework/functions/cil-graph.ts +585 -0
- package/.framework/functions/cil-ingest.ts +2416 -0
- package/.framework/functions/cil-pipeline.ts +318 -0
- package/.framework/functions/cil-project.ts +499 -0
- package/.framework/functions/cil-validate.ts +469 -0
- package/.framework/functions/debug-auth.ts +73 -53
- package/.framework/functions/deliveries.ts +117 -0
- package/.framework/functions/embeddings.ts +528 -516
- package/.framework/functions/eval-harness.ts +158 -106
- package/.framework/functions/integration-outbound.ts +311 -0
- package/.framework/functions/integration-routes.ts +240 -466
- package/.framework/functions/integrations.ts +132 -140
- package/.framework/functions/invites.ts +401 -201
- package/.framework/functions/item-progress.ts +113 -53
- package/.framework/functions/kb.ts +28 -0
- package/.framework/functions/logs.ts +239 -222
- package/.framework/functions/observability.ts +308 -259
- package/.framework/functions/pipeline-executions.ts +179 -235
- package/.framework/functions/pipelines.ts +163 -205
- package/.framework/functions/prompt-configs.ts +109 -156
- package/.framework/functions/roles.ts +97 -147
- package/.framework/functions/system-cron.ts +132 -218
- package/.framework/functions/system.ts +112 -52
- package/.framework/functions/tests.ts +32 -28
- package/.framework/functions/timers.ts +162 -210
- package/.framework/functions/triggers.ts +282 -346
- package/.framework/functions/types.ts +283 -400
- package/.framework/migrations/001_core_schema.sql +630 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
- package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
- package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
- package/.framework/migrations/005_core_observability.sql +598 -0
- package/.framework/migrations/006_security_hardening_rls.sql +97 -0
- package/.framework/migrations/007_kb_article_list.sql +237 -0
- package/.framework/migrations/008_items_target.sql +8 -0
- package/.framework/migrations/009_roles_slug_unique.sql +11 -0
- package/.framework/public/spine.config.json +91 -0
- package/.framework/scripts/app-install-cli.ts +112 -98
- package/.framework/src/App.tsx +67 -20
- package/.framework/src/apps/admin/index.tsx +97 -27
- package/.framework/src/components/AppWrapper.tsx +50 -11
- package/.framework/src/components/CustomAppLoader.tsx +131 -22
- package/.framework/src/components/admin/AdminListPage.tsx +75 -8
- package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
- package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
- package/.framework/src/components/admin/PageInstructions.tsx +49 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
- package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
- package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
- package/.framework/src/components/app-sidebar.tsx +50 -3
- package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
- package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
- package/.framework/src/components/kb/KBArticleList.tsx +557 -0
- package/.framework/src/components/layout/AppShell.tsx +54 -16
- package/.framework/src/components/layout/Header.tsx +50 -38
- package/.framework/src/components/layout/Layout.tsx +48 -32
- package/.framework/src/components/layout/Sidebar.tsx +167 -16
- package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
- package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
- package/.framework/src/components/runtime/DataFilters.tsx +78 -1
- package/.framework/src/components/runtime/DataHeader.tsx +32 -29
- package/.framework/src/components/runtime/DataListPage.tsx +161 -2
- package/.framework/src/components/runtime/DataStats.tsx +48 -1
- package/.framework/src/components/runtime/DataTable.tsx +153 -1
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
- package/.framework/src/components/runtime/index.ts +16 -9
- package/.framework/src/components/search-form.tsx +33 -1
- package/.framework/src/components/shared/AgentView.tsx +48 -51
- package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
- package/.framework/src/components/shared/InviteManager.tsx +113 -52
- package/.framework/src/components/shared/MemberList.tsx +182 -0
- package/.framework/src/components/shared/SchemaFields.tsx +96 -78
- package/.framework/src/components/ui/DataTable.tsx +86 -56
- package/.framework/src/components/ui/Form.tsx +188 -109
- package/.framework/src/components/ui/ItemCard.tsx +86 -58
- package/.framework/src/components/ui/ItemListView.tsx +88 -61
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
- package/.framework/src/components/ui/Modal.tsx +81 -4
- package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
- package/.framework/src/components/ui/accordion.tsx +97 -5
- package/.framework/src/components/ui/alert-dialog.tsx +225 -13
- package/.framework/src/components/ui/alert.tsx +113 -6
- package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
- package/.framework/src/components/ui/avatar.tsx +129 -7
- package/.framework/src/components/ui/badge.tsx +65 -3
- package/.framework/src/components/ui/breadcrumb.tsx +145 -8
- package/.framework/src/components/ui/button-group.tsx +81 -4
- package/.framework/src/components/ui/button.tsx +49 -2
- package/.framework/src/components/ui/calendar.tsx +65 -3
- package/.framework/src/components/ui/card.tsx +145 -8
- package/.framework/src/components/ui/carousel.tsx +129 -7
- package/.framework/src/components/ui/chart.tsx +161 -10
- package/.framework/src/components/ui/checkbox.tsx +49 -2
- package/.framework/src/components/ui/collapsible.tsx +81 -5
- package/.framework/src/components/ui/combobox.tsx +305 -20
- package/.framework/src/components/ui/command.tsx +177 -11
- package/.framework/src/components/ui/context-menu.tsx +273 -17
- package/.framework/src/components/ui/dialog.tsx +209 -27
- package/.framework/src/components/ui/direction.tsx +64 -4
- package/.framework/src/components/ui/drawer.tsx +193 -12
- package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
- package/.framework/src/components/ui/empty.tsx +145 -9
- package/.framework/src/components/ui/field.tsx +209 -14
- package/.framework/src/components/ui/hover-card.tsx +81 -4
- package/.framework/src/components/ui/input-group.tsx +162 -10
- package/.framework/src/components/ui/input-otp.tsx +98 -5
- package/.framework/src/components/ui/input.tsx +49 -2
- package/.framework/src/components/ui/item.tsx +225 -13
- package/.framework/src/components/ui/kbd.tsx +65 -3
- package/.framework/src/components/ui/label.tsx +49 -2
- package/.framework/src/components/ui/menubar.tsx +289 -17
- package/.framework/src/components/ui/native-select.tsx +97 -5
- package/.framework/src/components/ui/navigation-menu.tsx +177 -10
- package/.framework/src/components/ui/pagination.tsx +161 -9
- package/.framework/src/components/ui/popover.tsx +145 -8
- package/.framework/src/components/ui/progress.tsx +49 -2
- package/.framework/src/components/ui/radio-group.tsx +65 -3
- package/.framework/src/components/ui/resizable.tsx +82 -4
- package/.framework/src/components/ui/scroll-area.tsx +65 -3
- package/.framework/src/components/ui/select.tsx +193 -11
- package/.framework/src/components/ui/separator.tsx +49 -2
- package/.framework/src/components/ui/sheet.tsx +193 -11
- package/.framework/src/components/ui/sidebar.tsx +481 -38
- package/.framework/src/components/ui/skeleton.tsx +49 -2
- package/.framework/src/components/ui/slider.tsx +49 -2
- package/.framework/src/components/ui/sonner.tsx +49 -2
- package/.framework/src/components/ui/spinner.tsx +49 -2
- package/.framework/src/components/ui/switch.tsx +49 -2
- package/.framework/src/components/ui/table-primitives.tsx +160 -33
- package/.framework/src/components/ui/table.tsx +161 -9
- package/.framework/src/components/ui/tabs.tsx +113 -6
- package/.framework/src/components/ui/textarea.tsx +49 -2
- package/.framework/src/components/ui/toggle-group.tsx +81 -4
- package/.framework/src/components/ui/toggle.tsx +65 -3
- package/.framework/src/components/ui/tooltip.tsx +97 -5
- package/.framework/src/contexts/AppContext.tsx +129 -45
- package/.framework/src/contexts/AuthContext.tsx +187 -144
- package/.framework/src/hooks/use-mobile.ts +48 -2
- package/.framework/src/hooks/useApi.ts +208 -159
- package/.framework/src/hooks/useAppPath.ts +32 -23
- package/.framework/src/hooks/useApps.ts +51 -20
- package/.framework/src/hooks/useEntityList.ts +32 -78
- package/.framework/src/hooks/useEntityRecord.ts +35 -96
- package/.framework/src/hooks/useForm.ts +32 -92
- package/.framework/src/hooks/useListSchema.ts +49 -78
- package/.framework/src/hooks/useSchemaRecord.ts +114 -105
- package/.framework/src/lib/api.ts +195 -116
- package/.framework/src/lib/supabase.ts +98 -64
- package/.framework/src/lib/utils.ts +287 -151
- package/.framework/src/main.tsx +49 -3
- package/.framework/src/pages/DashboardPage.tsx +52 -29
- package/.framework/src/pages/NotFoundPage.tsx +32 -10
- package/.framework/src/pages/SetupPage.tsx +32 -12
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
- package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
- package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
- package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
- package/.framework/src/pages/admin/AppsPage.tsx +49 -2
- package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
- package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
- package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
- package/.framework/src/pages/admin/LogsPage.tsx +422 -75
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
- package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
- package/.framework/src/pages/admin/RolesPage.tsx +171 -18
- package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
- package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
- package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
- package/.framework/src/pages/admin/TimersPage.tsx +48 -20
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
- package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
- package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
- package/.framework/src/pages/admin/TypesPage.tsx +48 -20
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
- package/.framework/src/pages/auth/LoginPage.tsx +50 -23
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
- package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
- package/.framework/src/types/auth.ts +128 -71
- package/.framework/src/types/types.ts +373 -157
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
- package/docs/README.md +9 -130
- package/netlify.toml +5 -2
- package/package.json +13 -25
- package/scripts/kb-ingest-docs.ts +203 -0
- package/spine.config.json +65 -4
- package/.framework/migrations/001_schema.sql +0 -425
- package/.framework/migrations/002_rls_and_functions.sql +0 -763
- package/.framework/migrations/004_enhancements.sql +0 -43
- package/.framework/migrations/005_auth_user_trigger.sql +0 -105
- package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
- package/.framework/migrations/016_invites.sql +0 -27
- package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
- package/.framework/migrations/018_apps_required_roles.sql +0 -17
- package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
- package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
- package/.framework/migrations/021_app_domains.sql +0 -112
- package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
- package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
- package/.framework/migrations/024_messages_account_id.sql +0 -9
- package/.framework/migrations/025_retype_threads_messages.sql +0 -240
- package/.framework/migrations/026_fix_rls_policies.sql +0 -47
- package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
- package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
- package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
- package/.framework/migrations/030_messages_updated_by.sql +0 -6
- package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
- package/docs/APPS.md +0 -232
- package/docs/ARCHITECTURE.md +0 -246
- package/docs/INSTALL.md +0 -155
- package/docs/design-schema-spec.md +0 -1837
- package/docs/dev-guides/01-create-an-app.md +0 -183
- package/docs/dev-guides/02-create-a-function.md +0 -178
- package/docs/dev-guides/03-create-a-component.md +0 -178
- package/docs/dev-guides/04-webhook-handlers.md +0 -129
- package/docs/dev-guides/05-testing.md +0 -168
- package/docs/dev-guides/README.md +0 -40
- package/docs/npm-packages-plan.md +0 -288
- package/docs/spine-core--api-reference.md +0 -822
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
-- Spine v2 consolidated observability and knowledge search
|
|
2
|
+
-- Logs trace columns, observability metric RPCs, KB item RLS, and updated match_embeddings.
|
|
3
|
+
|
|
4
|
+
-- Migration: extend public.logs to store W3C Trace Context and span records.
|
|
5
|
+
-- Logs become the unified runtime evidence table: events, audit records, and spans.
|
|
6
|
+
|
|
7
|
+
ALTER TABLE public.logs
|
|
8
|
+
ADD COLUMN IF NOT EXISTS trace_id text,
|
|
9
|
+
ADD COLUMN IF NOT EXISTS span_id text,
|
|
10
|
+
ADD COLUMN IF NOT EXISTS parent_span_id text,
|
|
11
|
+
ADD COLUMN IF NOT EXISTS trace_flags smallint,
|
|
12
|
+
ADD COLUMN IF NOT EXISTS tracestate text,
|
|
13
|
+
ADD COLUMN IF NOT EXISTS span_name text,
|
|
14
|
+
ADD COLUMN IF NOT EXISTS started_at timestamptz,
|
|
15
|
+
ADD COLUMN IF NOT EXISTS ended_at timestamptz,
|
|
16
|
+
ADD COLUMN IF NOT EXISTS duration_ms numeric,
|
|
17
|
+
ADD COLUMN IF NOT EXISTS sampled boolean DEFAULT true,
|
|
18
|
+
ADD COLUMN IF NOT EXISTS kind text,
|
|
19
|
+
ADD COLUMN IF NOT EXISTS outcome text,
|
|
20
|
+
ADD COLUMN IF NOT EXISTS event_category text;
|
|
21
|
+
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_logs_trace_id ON public.logs (trace_id);
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_logs_span_id ON public.logs (span_id);
|
|
24
|
+
CREATE INDEX IF NOT EXISTS idx_logs_parent_span_id ON public.logs (parent_span_id);
|
|
25
|
+
CREATE INDEX IF NOT EXISTS idx_logs_started_at ON public.logs (started_at);
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_logs_account_started_at ON public.logs (account_id, started_at);
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_logs_account_trace ON public.logs (account_id, trace_id);
|
|
28
|
+
|
|
29
|
+
-- Add a lightweight check to keep trace IDs valid hex length where present.
|
|
30
|
+
ALTER TABLE public.logs
|
|
31
|
+
ADD CONSTRAINT chk_trace_id_hex CHECK (trace_id IS NULL OR trace_id ~ '^[0-9a-f]{32}$'),
|
|
32
|
+
ADD CONSTRAINT chk_span_id_hex CHECK (span_id IS NULL OR span_id ~ '^[0-9a-f]{16}$'),
|
|
33
|
+
ADD CONSTRAINT chk_parent_span_id_hex CHECK (parent_span_id IS NULL OR parent_span_id ~ '^[0-9a-f]{16}$');
|
|
34
|
+
|
|
35
|
+
-- Migration: add SQL aggregation functions for traces and derived metrics.
|
|
36
|
+
|
|
37
|
+
CREATE OR REPLACE FUNCTION public.get_trace_tree(
|
|
38
|
+
p_account_id uuid,
|
|
39
|
+
p_trace_id text
|
|
40
|
+
)
|
|
41
|
+
RETURNS TABLE (
|
|
42
|
+
span_id text,
|
|
43
|
+
parent_span_id text,
|
|
44
|
+
span_name text,
|
|
45
|
+
kind text,
|
|
46
|
+
level text,
|
|
47
|
+
source text,
|
|
48
|
+
started_at timestamptz,
|
|
49
|
+
ended_at timestamptz,
|
|
50
|
+
duration_ms numeric,
|
|
51
|
+
status text,
|
|
52
|
+
outcome text,
|
|
53
|
+
message text,
|
|
54
|
+
metadata jsonb
|
|
55
|
+
)
|
|
56
|
+
LANGUAGE plpgsql
|
|
57
|
+
SECURITY INVOKER
|
|
58
|
+
AS $$
|
|
59
|
+
BEGIN
|
|
60
|
+
RETURN QUERY
|
|
61
|
+
SELECT
|
|
62
|
+
l.span_id,
|
|
63
|
+
l.parent_span_id,
|
|
64
|
+
l.span_name,
|
|
65
|
+
l.kind,
|
|
66
|
+
l.level,
|
|
67
|
+
l.source,
|
|
68
|
+
l.started_at,
|
|
69
|
+
l.ended_at,
|
|
70
|
+
l.duration_ms,
|
|
71
|
+
CASE
|
|
72
|
+
WHEN l.level ILIKE '%error%' OR l.outcome = 'failure' THEN 'error'
|
|
73
|
+
ELSE 'ok'
|
|
74
|
+
END AS status,
|
|
75
|
+
l.outcome,
|
|
76
|
+
l.message,
|
|
77
|
+
COALESCE(to_jsonb(l.metadata), '{}'::jsonb) AS metadata
|
|
78
|
+
FROM public.logs l
|
|
79
|
+
WHERE l.account_id = p_account_id
|
|
80
|
+
AND l.trace_id = p_trace_id
|
|
81
|
+
ORDER BY l.started_at ASC, l.span_name ASC;
|
|
82
|
+
END;
|
|
83
|
+
$$;
|
|
84
|
+
|
|
85
|
+
CREATE OR REPLACE FUNCTION public.get_trace_stats(
|
|
86
|
+
p_account_id uuid,
|
|
87
|
+
p_date_from timestamptz DEFAULT NULL,
|
|
88
|
+
p_date_to timestamptz DEFAULT NULL
|
|
89
|
+
)
|
|
90
|
+
RETURNS TABLE (
|
|
91
|
+
trace_id text,
|
|
92
|
+
root_span_name text,
|
|
93
|
+
started_at timestamptz,
|
|
94
|
+
duration_ms numeric,
|
|
95
|
+
span_count bigint,
|
|
96
|
+
error_count bigint,
|
|
97
|
+
outcome text
|
|
98
|
+
)
|
|
99
|
+
LANGUAGE plpgsql
|
|
100
|
+
SECURITY INVOKER
|
|
101
|
+
AS $$
|
|
102
|
+
BEGIN
|
|
103
|
+
RETURN QUERY
|
|
104
|
+
SELECT
|
|
105
|
+
l.trace_id,
|
|
106
|
+
MAX(l.span_name) FILTER (WHERE l.parent_span_id IS NULL) AS root_span_name,
|
|
107
|
+
MIN(l.started_at) AS started_at,
|
|
108
|
+
MAX(l.duration_ms) FILTER (WHERE l.parent_span_id IS NULL) AS duration_ms,
|
|
109
|
+
COUNT(*) AS span_count,
|
|
110
|
+
COUNT(*) FILTER (WHERE l.level ILIKE '%error%' OR l.outcome = 'failure') AS error_count,
|
|
111
|
+
MAX(l.outcome) AS outcome
|
|
112
|
+
FROM public.logs l
|
|
113
|
+
WHERE l.account_id = p_account_id
|
|
114
|
+
AND l.trace_id IS NOT NULL
|
|
115
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
116
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
117
|
+
GROUP BY l.trace_id
|
|
118
|
+
ORDER BY started_at DESC;
|
|
119
|
+
END;
|
|
120
|
+
$$;
|
|
121
|
+
|
|
122
|
+
CREATE OR REPLACE FUNCTION public.get_metric_series(
|
|
123
|
+
p_account_id uuid,
|
|
124
|
+
p_metric_name text,
|
|
125
|
+
p_granularity text DEFAULT '1 hour',
|
|
126
|
+
p_date_from timestamptz DEFAULT NULL,
|
|
127
|
+
p_date_to timestamptz DEFAULT NULL
|
|
128
|
+
)
|
|
129
|
+
RETURNS TABLE (
|
|
130
|
+
bucket timestamptz,
|
|
131
|
+
value numeric
|
|
132
|
+
)
|
|
133
|
+
LANGUAGE plpgsql
|
|
134
|
+
SECURITY INVOKER
|
|
135
|
+
AS $$
|
|
136
|
+
BEGIN
|
|
137
|
+
IF p_metric_name = 'request_count' THEN
|
|
138
|
+
RETURN QUERY
|
|
139
|
+
SELECT
|
|
140
|
+
date_trunc(p_granularity, l.started_at) AS bucket,
|
|
141
|
+
COUNT(*)::numeric AS value
|
|
142
|
+
FROM public.logs l
|
|
143
|
+
WHERE l.account_id = p_account_id
|
|
144
|
+
AND l.span_name = 'request'
|
|
145
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
146
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
147
|
+
GROUP BY bucket
|
|
148
|
+
ORDER BY bucket;
|
|
149
|
+
|
|
150
|
+
ELSIF p_metric_name = 'error_rate' THEN
|
|
151
|
+
RETURN QUERY
|
|
152
|
+
SELECT
|
|
153
|
+
date_trunc(p_granularity, l.started_at) AS bucket,
|
|
154
|
+
CASE WHEN COUNT(*) > 0 THEN
|
|
155
|
+
(COUNT(*) FILTER (WHERE l.level ILIKE '%error%' OR l.outcome = 'failure')::numeric / COUNT(*)::numeric)
|
|
156
|
+
ELSE 0 END AS value
|
|
157
|
+
FROM public.logs l
|
|
158
|
+
WHERE l.account_id = p_account_id
|
|
159
|
+
AND l.trace_id IS NOT NULL
|
|
160
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
161
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
162
|
+
GROUP BY bucket
|
|
163
|
+
ORDER BY bucket;
|
|
164
|
+
|
|
165
|
+
ELSIF p_metric_name = 'avg_handler_duration' THEN
|
|
166
|
+
RETURN QUERY
|
|
167
|
+
SELECT
|
|
168
|
+
date_trunc(p_granularity, l.started_at) AS bucket,
|
|
169
|
+
AVG(l.duration_ms) AS value
|
|
170
|
+
FROM public.logs l
|
|
171
|
+
WHERE l.account_id = p_account_id
|
|
172
|
+
AND l.span_name = 'request'
|
|
173
|
+
AND l.duration_ms IS NOT NULL
|
|
174
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
175
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
176
|
+
GROUP BY bucket
|
|
177
|
+
ORDER BY bucket;
|
|
178
|
+
|
|
179
|
+
ELSIF p_metric_name = 'agent_tool_count' THEN
|
|
180
|
+
RETURN QUERY
|
|
181
|
+
SELECT
|
|
182
|
+
date_trunc(p_granularity, l.started_at) AS bucket,
|
|
183
|
+
COUNT(*)::numeric AS value
|
|
184
|
+
FROM public.logs l
|
|
185
|
+
WHERE l.account_id = p_account_id
|
|
186
|
+
AND l.span_name = 'agent.tool'
|
|
187
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
188
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
189
|
+
GROUP BY bucket
|
|
190
|
+
ORDER BY bucket;
|
|
191
|
+
|
|
192
|
+
ELSIF p_metric_name = 'pipeline_failure_rate' THEN
|
|
193
|
+
RETURN QUERY
|
|
194
|
+
SELECT
|
|
195
|
+
date_trunc(p_granularity, l.started_at) AS bucket,
|
|
196
|
+
CASE WHEN COUNT(*) > 0 THEN
|
|
197
|
+
(COUNT(*) FILTER (WHERE l.span_name LIKE 'pipeline%' AND (l.level ILIKE '%error%' OR l.outcome = 'failure'))::numeric / NULLIF(COUNT(*) FILTER (WHERE l.span_name LIKE 'pipeline%'), 0))
|
|
198
|
+
ELSE 0 END AS value
|
|
199
|
+
FROM public.logs l
|
|
200
|
+
WHERE l.account_id = p_account_id
|
|
201
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
202
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
203
|
+
GROUP BY bucket
|
|
204
|
+
ORDER BY bucket;
|
|
205
|
+
|
|
206
|
+
ELSE
|
|
207
|
+
RETURN QUERY
|
|
208
|
+
SELECT
|
|
209
|
+
date_trunc(p_granularity, l.started_at) AS bucket,
|
|
210
|
+
COUNT(*)::numeric AS value
|
|
211
|
+
FROM public.logs l
|
|
212
|
+
WHERE l.account_id = p_account_id
|
|
213
|
+
AND l.span_name = p_metric_name
|
|
214
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
215
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
216
|
+
GROUP BY bucket
|
|
217
|
+
ORDER BY bucket;
|
|
218
|
+
END IF;
|
|
219
|
+
END;
|
|
220
|
+
$$;
|
|
221
|
+
|
|
222
|
+
CREATE OR REPLACE FUNCTION public.get_business_outcomes(
|
|
223
|
+
p_account_id uuid,
|
|
224
|
+
p_date_from timestamptz DEFAULT NULL,
|
|
225
|
+
p_date_to timestamptz DEFAULT NULL
|
|
226
|
+
)
|
|
227
|
+
RETURNS TABLE (
|
|
228
|
+
intent text,
|
|
229
|
+
outcome text,
|
|
230
|
+
count bigint
|
|
231
|
+
)
|
|
232
|
+
LANGUAGE plpgsql
|
|
233
|
+
SECURITY INVOKER
|
|
234
|
+
AS $$
|
|
235
|
+
BEGIN
|
|
236
|
+
RETURN QUERY
|
|
237
|
+
SELECT
|
|
238
|
+
l.span_name AS intent,
|
|
239
|
+
l.outcome,
|
|
240
|
+
COUNT(*) AS count
|
|
241
|
+
FROM public.logs l
|
|
242
|
+
WHERE l.account_id = p_account_id
|
|
243
|
+
AND l.event_category = 'business_outcome'
|
|
244
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
245
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
246
|
+
GROUP BY l.span_name, l.outcome
|
|
247
|
+
ORDER BY count DESC;
|
|
248
|
+
END;
|
|
249
|
+
$$;
|
|
250
|
+
|
|
251
|
+
-- Ensure functions are callable from the authenticated role used by supabase-js.
|
|
252
|
+
GRANT EXECUTE ON FUNCTION public.get_trace_tree(uuid, text) TO authenticated;
|
|
253
|
+
GRANT EXECUTE ON FUNCTION public.get_trace_stats(uuid, timestamptz, timestamptz) TO authenticated;
|
|
254
|
+
GRANT EXECUTE ON FUNCTION public.get_metric_series(uuid, text, text, timestamptz, timestamptz) TO authenticated;
|
|
255
|
+
GRANT EXECUTE ON FUNCTION public.get_business_outcomes(uuid, timestamptz, timestamptz) TO authenticated;
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
-- Fix get_metric_series to accept human-readable granularities such as '1 hour', '1 day', '7 days', '30 days'.
|
|
260
|
+
-- date_trunc does not accept interval-style strings, so use date_bin with a fixed UTC origin instead.
|
|
261
|
+
|
|
262
|
+
CREATE OR REPLACE FUNCTION public.get_metric_series(
|
|
263
|
+
p_account_id uuid,
|
|
264
|
+
p_metric_name text,
|
|
265
|
+
p_granularity text DEFAULT '1 hour',
|
|
266
|
+
p_date_from timestamptz DEFAULT NULL,
|
|
267
|
+
p_date_to timestamptz DEFAULT NULL
|
|
268
|
+
)
|
|
269
|
+
RETURNS TABLE (
|
|
270
|
+
bucket timestamptz,
|
|
271
|
+
value numeric
|
|
272
|
+
)
|
|
273
|
+
LANGUAGE plpgsql
|
|
274
|
+
SECURITY INVOKER
|
|
275
|
+
AS $$
|
|
276
|
+
DECLARE
|
|
277
|
+
v_stride interval;
|
|
278
|
+
v_origin timestamptz := '2000-01-01 00:00:00+00'::timestamptz;
|
|
279
|
+
BEGIN
|
|
280
|
+
v_stride := p_granularity::interval;
|
|
281
|
+
|
|
282
|
+
IF p_metric_name = 'request_count' THEN
|
|
283
|
+
RETURN QUERY
|
|
284
|
+
SELECT
|
|
285
|
+
date_bin(v_stride, l.started_at, v_origin) AS bucket,
|
|
286
|
+
COUNT(*)::numeric AS value
|
|
287
|
+
FROM public.logs l
|
|
288
|
+
WHERE l.account_id = p_account_id
|
|
289
|
+
AND l.span_name = 'request'
|
|
290
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
291
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
292
|
+
GROUP BY bucket
|
|
293
|
+
ORDER BY bucket;
|
|
294
|
+
|
|
295
|
+
ELSIF p_metric_name = 'error_rate' THEN
|
|
296
|
+
RETURN QUERY
|
|
297
|
+
SELECT
|
|
298
|
+
date_bin(v_stride, l.started_at, v_origin) AS bucket,
|
|
299
|
+
CASE WHEN COUNT(*) > 0 THEN
|
|
300
|
+
(COUNT(*) FILTER (WHERE l.level ILIKE '%error%' OR l.outcome = 'failure')::numeric / COUNT(*)::numeric)
|
|
301
|
+
ELSE 0 END AS value
|
|
302
|
+
FROM public.logs l
|
|
303
|
+
WHERE l.account_id = p_account_id
|
|
304
|
+
AND l.trace_id IS NOT NULL
|
|
305
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
306
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
307
|
+
GROUP BY bucket
|
|
308
|
+
ORDER BY bucket;
|
|
309
|
+
|
|
310
|
+
ELSIF p_metric_name = 'avg_handler_duration' THEN
|
|
311
|
+
RETURN QUERY
|
|
312
|
+
SELECT
|
|
313
|
+
date_bin(v_stride, l.started_at, v_origin) AS bucket,
|
|
314
|
+
AVG(l.duration_ms) AS value
|
|
315
|
+
FROM public.logs l
|
|
316
|
+
WHERE l.account_id = p_account_id
|
|
317
|
+
AND l.span_name = 'request'
|
|
318
|
+
AND l.duration_ms IS NOT NULL
|
|
319
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
320
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
321
|
+
GROUP BY bucket
|
|
322
|
+
ORDER BY bucket;
|
|
323
|
+
|
|
324
|
+
ELSIF p_metric_name = 'agent_tool_count' THEN
|
|
325
|
+
RETURN QUERY
|
|
326
|
+
SELECT
|
|
327
|
+
date_bin(v_stride, l.started_at, v_origin) AS bucket,
|
|
328
|
+
COUNT(*)::numeric AS value
|
|
329
|
+
FROM public.logs l
|
|
330
|
+
WHERE l.account_id = p_account_id
|
|
331
|
+
AND l.span_name = 'agent.tool'
|
|
332
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
333
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
334
|
+
GROUP BY bucket
|
|
335
|
+
ORDER BY bucket;
|
|
336
|
+
|
|
337
|
+
ELSIF p_metric_name = 'pipeline_failure_rate' THEN
|
|
338
|
+
RETURN QUERY
|
|
339
|
+
SELECT
|
|
340
|
+
date_bin(v_stride, l.started_at, v_origin) AS bucket,
|
|
341
|
+
CASE WHEN COUNT(*) > 0 THEN
|
|
342
|
+
(COUNT(*) FILTER (WHERE l.span_name LIKE 'pipeline%' AND (l.level ILIKE '%error%' OR l.outcome = 'failure'))::numeric / NULLIF(COUNT(*) FILTER (WHERE l.span_name LIKE 'pipeline%'), 0))
|
|
343
|
+
ELSE 0 END AS value
|
|
344
|
+
FROM public.logs l
|
|
345
|
+
WHERE l.account_id = p_account_id
|
|
346
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
347
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
348
|
+
GROUP BY bucket
|
|
349
|
+
ORDER BY bucket;
|
|
350
|
+
|
|
351
|
+
ELSE
|
|
352
|
+
RETURN QUERY
|
|
353
|
+
SELECT
|
|
354
|
+
date_bin(v_stride, l.started_at, v_origin) AS bucket,
|
|
355
|
+
COUNT(*)::numeric AS value
|
|
356
|
+
FROM public.logs l
|
|
357
|
+
WHERE l.account_id = p_account_id
|
|
358
|
+
AND l.span_name = p_metric_name
|
|
359
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
360
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
361
|
+
GROUP BY bucket
|
|
362
|
+
ORDER BY bucket;
|
|
363
|
+
END IF;
|
|
364
|
+
END;
|
|
365
|
+
$$;
|
|
366
|
+
|
|
367
|
+
-- Re-apply grants in case they were not preserved by CREATE OR REPLACE FUNCTION in older Postgres versions.
|
|
368
|
+
GRANT EXECUTE ON FUNCTION public.get_metric_series(uuid, text, text, timestamptz, timestamptz) TO authenticated;
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
-- Add optional limit/offset/total support to get_trace_stats so the UI can paginate trace lists.
|
|
372
|
+
CREATE OR REPLACE FUNCTION public.get_trace_stats(
|
|
373
|
+
p_account_id uuid,
|
|
374
|
+
p_date_from timestamptz DEFAULT NULL,
|
|
375
|
+
p_date_to timestamptz DEFAULT NULL,
|
|
376
|
+
p_limit bigint DEFAULT NULL,
|
|
377
|
+
p_offset bigint DEFAULT NULL,
|
|
378
|
+
p_sort text DEFAULT 'started'
|
|
379
|
+
)
|
|
380
|
+
RETURNS TABLE (
|
|
381
|
+
trace_id text,
|
|
382
|
+
root_span_name text,
|
|
383
|
+
started_at timestamptz,
|
|
384
|
+
duration_ms numeric,
|
|
385
|
+
span_count bigint,
|
|
386
|
+
error_count bigint,
|
|
387
|
+
outcome text,
|
|
388
|
+
total bigint
|
|
389
|
+
)
|
|
390
|
+
LANGUAGE plpgsql
|
|
391
|
+
SECURITY INVOKER
|
|
392
|
+
AS $$
|
|
393
|
+
BEGIN
|
|
394
|
+
RETURN QUERY
|
|
395
|
+
WITH grouped AS (
|
|
396
|
+
SELECT
|
|
397
|
+
l.trace_id,
|
|
398
|
+
MAX(l.span_name) FILTER (WHERE l.parent_span_id IS NULL) AS root_span_name,
|
|
399
|
+
MIN(l.started_at) AS started_at,
|
|
400
|
+
MAX(l.duration_ms) FILTER (WHERE l.parent_span_id IS NULL) AS duration_ms,
|
|
401
|
+
COUNT(*) AS span_count,
|
|
402
|
+
COUNT(*) FILTER (WHERE l.level ILIKE '%error%' OR l.outcome = 'failure') AS error_count,
|
|
403
|
+
MAX(l.outcome) AS outcome
|
|
404
|
+
FROM public.logs l
|
|
405
|
+
WHERE l.account_id = p_account_id
|
|
406
|
+
AND l.trace_id IS NOT NULL
|
|
407
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
408
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
409
|
+
GROUP BY l.trace_id
|
|
410
|
+
)
|
|
411
|
+
SELECT
|
|
412
|
+
g.*,
|
|
413
|
+
COUNT(*) OVER() AS total
|
|
414
|
+
FROM grouped g
|
|
415
|
+
ORDER BY
|
|
416
|
+
CASE WHEN p_sort = 'duration' THEN g.duration_ms END DESC NULLS LAST,
|
|
417
|
+
g.started_at DESC
|
|
418
|
+
LIMIT p_limit OFFSET p_offset;
|
|
419
|
+
END;
|
|
420
|
+
$$;
|
|
421
|
+
|
|
422
|
+
-- Add optional limit/offset/total support to get_business_outcomes.
|
|
423
|
+
CREATE OR REPLACE FUNCTION public.get_business_outcomes(
|
|
424
|
+
p_account_id uuid,
|
|
425
|
+
p_date_from timestamptz DEFAULT NULL,
|
|
426
|
+
p_date_to timestamptz DEFAULT NULL,
|
|
427
|
+
p_limit bigint DEFAULT NULL,
|
|
428
|
+
p_offset bigint DEFAULT NULL
|
|
429
|
+
)
|
|
430
|
+
RETURNS TABLE (
|
|
431
|
+
intent text,
|
|
432
|
+
outcome text,
|
|
433
|
+
count bigint,
|
|
434
|
+
total bigint
|
|
435
|
+
)
|
|
436
|
+
LANGUAGE plpgsql
|
|
437
|
+
SECURITY INVOKER
|
|
438
|
+
AS $$
|
|
439
|
+
BEGIN
|
|
440
|
+
RETURN QUERY
|
|
441
|
+
WITH grouped AS (
|
|
442
|
+
SELECT
|
|
443
|
+
l.span_name AS intent,
|
|
444
|
+
l.outcome,
|
|
445
|
+
COUNT(*) AS count
|
|
446
|
+
FROM public.logs l
|
|
447
|
+
WHERE l.account_id = p_account_id
|
|
448
|
+
AND l.event_category = 'business_outcome'
|
|
449
|
+
AND (p_date_from IS NULL OR l.started_at >= p_date_from)
|
|
450
|
+
AND (p_date_to IS NULL OR l.started_at <= p_date_to)
|
|
451
|
+
GROUP BY l.span_name, l.outcome
|
|
452
|
+
)
|
|
453
|
+
SELECT
|
|
454
|
+
g.*,
|
|
455
|
+
COUNT(*) OVER() AS total
|
|
456
|
+
FROM grouped g
|
|
457
|
+
ORDER BY g.count DESC
|
|
458
|
+
LIMIT p_limit OFFSET p_offset;
|
|
459
|
+
END;
|
|
460
|
+
$$;
|
|
461
|
+
|
|
462
|
+
-- Re-apply grants for the new signatures.
|
|
463
|
+
GRANT EXECUTE ON FUNCTION public.get_trace_stats(uuid, timestamptz, timestamptz, bigint, bigint, text) TO authenticated;
|
|
464
|
+
|
|
465
|
+
GRANT EXECUTE ON FUNCTION public.get_business_outcomes(uuid, timestamptz, timestamptz, bigint, bigint) TO authenticated;
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
-- Postgres created overloaded variants because CREATE OR REPLACE FUNCTION cannot change signatures.
|
|
469
|
+
-- Drop the old 3-argument versions so callers using three arguments resolve to the new paginated variants.
|
|
470
|
+
DROP FUNCTION IF EXISTS public.get_trace_stats(uuid, timestamptz, timestamptz);
|
|
471
|
+
DROP FUNCTION IF EXISTS public.get_business_outcomes(uuid, timestamptz, timestamptz);
|
|
472
|
+
|
|
473
|
+
DROP POLICY IF EXISTS items_access ON public.items;
|
|
474
|
+
|
|
475
|
+
CREATE POLICY items_access ON public.items FOR ALL
|
|
476
|
+
USING (
|
|
477
|
+
account_id IN (SELECT public.get_accessible_accounts(public.current_actor_id()))
|
|
478
|
+
OR (design_schema->>'scope' = 'platform' AND public.current_actor_id() IS NOT NULL)
|
|
479
|
+
OR public.current_actor_id() IS NULL
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
CREATE INDEX IF NOT EXISTS items_status_idx ON public.items (status);
|
|
483
|
+
|
|
484
|
+
-- Update match_embeddings to support item-type/document filtering and to
|
|
485
|
+
-- exclude revoked or unapproved embeddings.
|
|
486
|
+
|
|
487
|
+
CREATE OR REPLACE FUNCTION public.match_embeddings(
|
|
488
|
+
query_embedding vector(1536),
|
|
489
|
+
match_count int DEFAULT 10,
|
|
490
|
+
filter_account_ids uuid[] DEFAULT NULL,
|
|
491
|
+
filter_vector_type text DEFAULT NULL,
|
|
492
|
+
similarity_threshold float DEFAULT 0.15,
|
|
493
|
+
filter_item_types text[] DEFAULT NULL,
|
|
494
|
+
filter_document_ids text[] DEFAULT NULL
|
|
495
|
+
)
|
|
496
|
+
RETURNS TABLE (
|
|
497
|
+
id uuid,
|
|
498
|
+
document_id text,
|
|
499
|
+
content text,
|
|
500
|
+
metadata jsonb,
|
|
501
|
+
account_id uuid,
|
|
502
|
+
similarity float
|
|
503
|
+
)
|
|
504
|
+
LANGUAGE plpgsql
|
|
505
|
+
SECURITY DEFINER
|
|
506
|
+
SET search_path = public
|
|
507
|
+
AS $$
|
|
508
|
+
BEGIN
|
|
509
|
+
RETURN QUERY
|
|
510
|
+
SELECT
|
|
511
|
+
e.id,
|
|
512
|
+
e.document_id,
|
|
513
|
+
e.content,
|
|
514
|
+
e.metadata,
|
|
515
|
+
e.account_id,
|
|
516
|
+
1 - (e.embedding <=> query_embedding) AS similarity
|
|
517
|
+
FROM public.embeddings e
|
|
518
|
+
WHERE
|
|
519
|
+
(filter_account_ids IS NULL OR e.account_id = ANY(filter_account_ids))
|
|
520
|
+
AND (filter_vector_type IS NULL OR e.metadata->>'vector_type' = filter_vector_type)
|
|
521
|
+
AND (filter_item_types IS NULL OR e.metadata->>'item_type' = ANY(filter_item_types))
|
|
522
|
+
AND (filter_document_ids IS NULL OR e.document_id = ANY(filter_document_ids))
|
|
523
|
+
AND e.approved_at IS NOT NULL
|
|
524
|
+
AND e.revoked_at IS NULL
|
|
525
|
+
AND (1 - (e.embedding <=> query_embedding)) >= similarity_threshold
|
|
526
|
+
ORDER BY e.embedding <=> query_embedding
|
|
527
|
+
LIMIT match_count;
|
|
528
|
+
END
|
|
529
|
+
$$;
|
|
530
|
+
|
|
531
|
+
GRANT EXECUTE ON FUNCTION public.match_embeddings(
|
|
532
|
+
vector(1536), int, uuid[], text, float, text[], text[]
|
|
533
|
+
) TO authenticated, service_role;
|
|
534
|
+
|
|
535
|
+
-- =====================================================
|
|
536
|
+
-- DATA RETENTION
|
|
537
|
+
-- =====================================================
|
|
538
|
+
|
|
539
|
+
CREATE OR REPLACE FUNCTION public.prune_logs(
|
|
540
|
+
p_account_id uuid
|
|
541
|
+
)
|
|
542
|
+
RETURNS TABLE (deleted_count bigint)
|
|
543
|
+
LANGUAGE plpgsql
|
|
544
|
+
SECURITY INVOKER
|
|
545
|
+
SET search_path = public
|
|
546
|
+
AS $$
|
|
547
|
+
DECLARE
|
|
548
|
+
v_retention_days int;
|
|
549
|
+
v_cutoff timestamptz;
|
|
550
|
+
v_count bigint;
|
|
551
|
+
BEGIN
|
|
552
|
+
SELECT COALESCE(a.logs_retention_days, 30) INTO v_retention_days
|
|
553
|
+
FROM public.accounts a
|
|
554
|
+
WHERE a.id = p_account_id;
|
|
555
|
+
|
|
556
|
+
v_cutoff := now() - (v_retention_days || ' days')::interval;
|
|
557
|
+
|
|
558
|
+
DELETE FROM public.logs l
|
|
559
|
+
WHERE l.account_id = p_account_id
|
|
560
|
+
AND l.started_at < v_cutoff;
|
|
561
|
+
|
|
562
|
+
GET DIAGNOSTICS v_count = ROW_COUNT;
|
|
563
|
+
RETURN QUERY SELECT v_count;
|
|
564
|
+
END
|
|
565
|
+
$$;
|
|
566
|
+
|
|
567
|
+
CREATE OR REPLACE FUNCTION public.prune_observability(
|
|
568
|
+
p_account_id uuid
|
|
569
|
+
)
|
|
570
|
+
RETURNS TABLE (deleted_count bigint)
|
|
571
|
+
LANGUAGE plpgsql
|
|
572
|
+
SECURITY INVOKER
|
|
573
|
+
SET search_path = public
|
|
574
|
+
AS $$
|
|
575
|
+
DECLARE
|
|
576
|
+
v_retention_days int;
|
|
577
|
+
v_cutoff timestamptz;
|
|
578
|
+
v_count bigint;
|
|
579
|
+
BEGIN
|
|
580
|
+
SELECT COALESCE(a.observability_retention_days, 90) INTO v_retention_days
|
|
581
|
+
FROM public.accounts a
|
|
582
|
+
WHERE a.id = p_account_id;
|
|
583
|
+
|
|
584
|
+
v_cutoff := now() - (v_retention_days || ' days')::interval;
|
|
585
|
+
|
|
586
|
+
DELETE FROM public.observability o
|
|
587
|
+
WHERE o.account_id = p_account_id
|
|
588
|
+
AND o.created_at < v_cutoff;
|
|
589
|
+
|
|
590
|
+
GET DIAGNOSTICS v_count = ROW_COUNT;
|
|
591
|
+
RETURN QUERY SELECT v_count;
|
|
592
|
+
END
|
|
593
|
+
$$;
|
|
594
|
+
|
|
595
|
+
GRANT EXECUTE ON FUNCTION public.prune_logs(uuid) TO authenticated;
|
|
596
|
+
GRANT EXECUTE ON FUNCTION public.prune_logs(uuid) TO service_role;
|
|
597
|
+
GRANT EXECUTE ON FUNCTION public.prune_observability(uuid) TO authenticated;
|
|
598
|
+
GRANT EXECUTE ON FUNCTION public.prune_observability(uuid) TO service_role;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
-- Security hardening: close RLS bypasses that allowed unauthenticated
|
|
2
|
+
-- (no-JWT / anon-key-only) requests to read and write sensitive tables
|
|
3
|
+
-- directly via PostgREST, and remove `OR true` policies that made
|
|
4
|
+
-- `types`/`watchers` writable by anyone.
|
|
5
|
+
--
|
|
6
|
+
-- Context: service_role (adminDb) already has explicit, dedicated
|
|
7
|
+
-- `*_service_role` bypass policies (see 004_core_rpc_and_broker.sql), so
|
|
8
|
+
-- none of the clauses removed here were needed for legitimate backend
|
|
9
|
+
-- access. They only ever widened access for requests made with the
|
|
10
|
+
-- public anon key and no Supabase Auth JWT (auth.uid() IS NULL /
|
|
11
|
+
-- public.current_actor_id() IS NULL), which is exactly the case for an
|
|
12
|
+
-- unauthenticated browser calling PostgREST directly.
|
|
13
|
+
|
|
14
|
+
-- ============================================
|
|
15
|
+
-- integrations / api_keys / api_key_usage_logs / webhook_handlers /
|
|
16
|
+
-- webhook_deliveries: remove the "OR auth.uid() IS NULL" bypass
|
|
17
|
+
-- introduced in 004_core_rpc_and_broker.sql.
|
|
18
|
+
-- ============================================
|
|
19
|
+
|
|
20
|
+
DROP POLICY IF EXISTS integrations_access ON public.integrations;
|
|
21
|
+
CREATE POLICY integrations_access ON public.integrations FOR ALL
|
|
22
|
+
USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid())));
|
|
23
|
+
|
|
24
|
+
DROP POLICY IF EXISTS api_keys_access ON public.api_keys;
|
|
25
|
+
CREATE POLICY api_keys_access ON public.api_keys FOR ALL
|
|
26
|
+
USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid())));
|
|
27
|
+
|
|
28
|
+
DROP POLICY IF EXISTS akul_access ON public.api_key_usage_logs;
|
|
29
|
+
CREATE POLICY akul_access ON public.api_key_usage_logs FOR ALL
|
|
30
|
+
USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid())));
|
|
31
|
+
|
|
32
|
+
DROP POLICY IF EXISTS webhook_handlers_access ON public.webhook_handlers;
|
|
33
|
+
CREATE POLICY webhook_handlers_access ON public.webhook_handlers FOR ALL
|
|
34
|
+
USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid())));
|
|
35
|
+
|
|
36
|
+
DROP POLICY IF EXISTS webhook_deliveries_access ON public.webhook_deliveries;
|
|
37
|
+
CREATE POLICY webhook_deliveries_access ON public.webhook_deliveries FOR ALL
|
|
38
|
+
USING (account_id IN (SELECT public.get_accessible_accounts(auth.uid())));
|
|
39
|
+
|
|
40
|
+
-- ============================================
|
|
41
|
+
-- items: remove the "OR public.current_actor_id() IS NULL" bypass
|
|
42
|
+
-- introduced in 005_core_observability.sql. Keep the platform-scope
|
|
43
|
+
-- clause for authenticated users.
|
|
44
|
+
-- ============================================
|
|
45
|
+
|
|
46
|
+
DROP POLICY IF EXISTS items_access ON public.items;
|
|
47
|
+
CREATE POLICY items_access ON public.items FOR ALL
|
|
48
|
+
USING (
|
|
49
|
+
account_id IN (SELECT public.get_accessible_accounts(public.current_actor_id()))
|
|
50
|
+
OR (design_schema->>'scope' = 'platform' AND public.current_actor_id() IS NOT NULL)
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
-- ============================================
|
|
54
|
+
-- types: was `USING (public.current_actor_id() IS NOT NULL OR true)`,
|
|
55
|
+
-- i.e. always true regardless of the left-hand side, so ANY caller
|
|
56
|
+
-- (including unauthenticated anon-key requests) could INSERT/UPDATE/
|
|
57
|
+
-- DELETE type/design-schema definitions directly, bypassing the
|
|
58
|
+
-- system-admin check that `types.ts` enforces at the application layer.
|
|
59
|
+
-- Split into a public read policy (matches existing `roles_read`
|
|
60
|
+
-- pattern; type/schema metadata is not sensitive) and a system-admin
|
|
61
|
+
-- write policy.
|
|
62
|
+
-- ============================================
|
|
63
|
+
|
|
64
|
+
DROP POLICY IF EXISTS types_access ON public.types;
|
|
65
|
+
|
|
66
|
+
CREATE POLICY types_read ON public.types FOR SELECT USING (true);
|
|
67
|
+
|
|
68
|
+
CREATE POLICY types_insert ON public.types FOR INSERT
|
|
69
|
+
WITH CHECK (public.person_is_system_admin(public.current_actor_id()));
|
|
70
|
+
|
|
71
|
+
CREATE POLICY types_update ON public.types FOR UPDATE
|
|
72
|
+
USING (public.person_is_system_admin(public.current_actor_id()));
|
|
73
|
+
|
|
74
|
+
CREATE POLICY types_delete ON public.types FOR DELETE
|
|
75
|
+
USING (public.person_is_system_admin(public.current_actor_id()));
|
|
76
|
+
|
|
77
|
+
-- Service role (adminDb) always bypasses RLS regardless of policy, but
|
|
78
|
+
-- keep an explicit policy for parity with other tables and future-proofing
|
|
79
|
+
-- against BYPASSRLS being revoked from service_role.
|
|
80
|
+
DROP POLICY IF EXISTS types_service_role ON public.types;
|
|
81
|
+
CREATE POLICY types_service_role ON public.types FOR ALL TO service_role USING (true) WITH CHECK (true);
|
|
82
|
+
|
|
83
|
+
-- ============================================
|
|
84
|
+
-- watchers: was `USING (auth.uid() IS NOT NULL OR true)`, same always-true
|
|
85
|
+
-- bug as types. Scope to the caller's accessible accounts like other
|
|
86
|
+
-- account-scoped runtime tables.
|
|
87
|
+
-- ============================================
|
|
88
|
+
|
|
89
|
+
DROP POLICY IF EXISTS watchers_access ON public.watchers;
|
|
90
|
+
CREATE POLICY watchers_access ON public.watchers FOR ALL
|
|
91
|
+
USING (
|
|
92
|
+
account_id IS NULL
|
|
93
|
+
OR account_id IN (SELECT public.get_accessible_accounts(auth.uid()))
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
DROP POLICY IF EXISTS watchers_service_role ON public.watchers;
|
|
97
|
+
CREATE POLICY watchers_service_role ON public.watchers FOR ALL TO service_role USING (true) WITH CHECK (true);
|