@intelligo-dev/core 1.0.0-beta.1
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/LICENSE +201 -0
- package/dist/conversations/errors.d.ts +31 -0
- package/dist/conversations/errors.d.ts.map +1 -0
- package/dist/conversations/errors.js +25 -0
- package/dist/conversations/errors.js.map +1 -0
- package/dist/conversations/index.d.ts +13 -0
- package/dist/conversations/index.d.ts.map +1 -0
- package/dist/conversations/index.js +12 -0
- package/dist/conversations/index.js.map +1 -0
- package/dist/conversations/service.d.ts +132 -0
- package/dist/conversations/service.d.ts.map +1 -0
- package/dist/conversations/service.js +347 -0
- package/dist/conversations/service.js.map +1 -0
- package/dist/conversations/types.d.ts +10 -0
- package/dist/conversations/types.d.ts.map +1 -0
- package/dist/conversations/types.js +5 -0
- package/dist/conversations/types.js.map +1 -0
- package/dist/db/client.d.ts +19 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +52 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/index.d.ts +4 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +8 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema/agents.d.ts +400 -0
- package/dist/db/schema/agents.d.ts.map +1 -0
- package/dist/db/schema/agents.js +39 -0
- package/dist/db/schema/agents.js.map +1 -0
- package/dist/db/schema/ai.d.ts +2222 -0
- package/dist/db/schema/ai.d.ts.map +1 -0
- package/dist/db/schema/ai.js +313 -0
- package/dist/db/schema/ai.js.map +1 -0
- package/dist/db/schema/auth.d.ts +1222 -0
- package/dist/db/schema/auth.d.ts.map +1 -0
- package/dist/db/schema/auth.js +162 -0
- package/dist/db/schema/auth.js.map +1 -0
- package/dist/db/schema/billing.d.ts +1199 -0
- package/dist/db/schema/billing.d.ts.map +1 -0
- package/dist/db/schema/billing.js +150 -0
- package/dist/db/schema/billing.js.map +1 -0
- package/dist/db/schema/feature-flags.d.ts +143 -0
- package/dist/db/schema/feature-flags.d.ts.map +1 -0
- package/dist/db/schema/feature-flags.js +25 -0
- package/dist/db/schema/feature-flags.js.map +1 -0
- package/dist/db/schema/identity.d.ts +1295 -0
- package/dist/db/schema/identity.d.ts.map +1 -0
- package/dist/db/schema/identity.js +258 -0
- package/dist/db/schema/identity.js.map +1 -0
- package/dist/db/schema/index.d.ts +16 -0
- package/dist/db/schema/index.d.ts.map +1 -0
- package/dist/db/schema/index.js +43 -0
- package/dist/db/schema/index.js.map +1 -0
- package/dist/db/schema/notifications.d.ts +182 -0
- package/dist/db/schema/notifications.d.ts.map +1 -0
- package/dist/db/schema/notifications.js +38 -0
- package/dist/db/schema/notifications.js.map +1 -0
- package/dist/db/schema/rag.d.ts +171 -0
- package/dist/db/schema/rag.d.ts.map +1 -0
- package/dist/db/schema/rag.js +42 -0
- package/dist/db/schema/rag.js.map +1 -0
- package/dist/db/schema/usage.d.ts +1193 -0
- package/dist/db/schema/usage.d.ts.map +1 -0
- package/dist/db/schema/usage.js +224 -0
- package/dist/db/schema/usage.js.map +1 -0
- package/dist/db/validate.d.ts +19 -0
- package/dist/db/validate.d.ts.map +1 -0
- package/dist/db/validate.js +58 -0
- package/dist/db/validate.js.map +1 -0
- package/dist/db/workspace-queries.d.ts +75 -0
- package/dist/db/workspace-queries.d.ts.map +1 -0
- package/dist/db/workspace-queries.js +78 -0
- package/dist/db/workspace-queries.js.map +1 -0
- package/dist/documents/classifier.d.ts +31 -0
- package/dist/documents/classifier.d.ts.map +1 -0
- package/dist/documents/classifier.js +41 -0
- package/dist/documents/classifier.js.map +1 -0
- package/dist/documents/errors.d.ts +32 -0
- package/dist/documents/errors.d.ts.map +1 -0
- package/dist/documents/errors.js +27 -0
- package/dist/documents/errors.js.map +1 -0
- package/dist/documents/index.d.ts +14 -0
- package/dist/documents/index.d.ts.map +1 -0
- package/dist/documents/index.js +13 -0
- package/dist/documents/index.js.map +1 -0
- package/dist/documents/service.d.ts +54 -0
- package/dist/documents/service.d.ts.map +1 -0
- package/dist/documents/service.js +146 -0
- package/dist/documents/service.js.map +1 -0
- package/dist/documents/types.d.ts +27 -0
- package/dist/documents/types.d.ts.map +1 -0
- package/dist/documents/types.js +5 -0
- package/dist/documents/types.js.map +1 -0
- package/dist/email/index.d.ts +5 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +5 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/provider.d.ts +68 -0
- package/dist/email/provider.d.ts.map +1 -0
- package/dist/email/provider.js +199 -0
- package/dist/email/provider.js.map +1 -0
- package/dist/email/send.d.ts +47 -0
- package/dist/email/send.d.ts.map +1 -0
- package/dist/email/send.js +110 -0
- package/dist/email/send.js.map +1 -0
- package/dist/email/senders.d.ts +93 -0
- package/dist/email/senders.d.ts.map +1 -0
- package/dist/email/senders.js +232 -0
- package/dist/email/senders.js.map +1 -0
- package/dist/email/templates/base-layout.d.ts +7 -0
- package/dist/email/templates/base-layout.d.ts.map +1 -0
- package/dist/email/templates/base-layout.js +69 -0
- package/dist/email/templates/base-layout.js.map +1 -0
- package/dist/email/templates/index.d.ts +11 -0
- package/dist/email/templates/index.d.ts.map +1 -0
- package/dist/email/templates/index.js +17 -0
- package/dist/email/templates/index.js.map +1 -0
- package/dist/email/templates/password-reset.d.ts +7 -0
- package/dist/email/templates/password-reset.d.ts.map +1 -0
- package/dist/email/templates/password-reset.js +59 -0
- package/dist/email/templates/password-reset.js.map +1 -0
- package/dist/email/templates/payment-failed.d.ts +8 -0
- package/dist/email/templates/payment-failed.d.ts.map +1 -0
- package/dist/email/templates/payment-failed.js +62 -0
- package/dist/email/templates/payment-failed.js.map +1 -0
- package/dist/email/templates/quota-warning.d.ts +11 -0
- package/dist/email/templates/quota-warning.d.ts.map +1 -0
- package/dist/email/templates/quota-warning.js +82 -0
- package/dist/email/templates/quota-warning.js.map +1 -0
- package/dist/email/templates/subscription-confirmed.d.ts +9 -0
- package/dist/email/templates/subscription-confirmed.d.ts.map +1 -0
- package/dist/email/templates/subscription-confirmed.js +72 -0
- package/dist/email/templates/subscription-confirmed.js.map +1 -0
- package/dist/email/templates/trial-expiry.d.ts +9 -0
- package/dist/email/templates/trial-expiry.d.ts.map +1 -0
- package/dist/email/templates/trial-expiry.js +86 -0
- package/dist/email/templates/trial-expiry.js.map +1 -0
- package/dist/email/templates/trial-warning.d.ts +11 -0
- package/dist/email/templates/trial-warning.d.ts.map +1 -0
- package/dist/email/templates/trial-warning.js +70 -0
- package/dist/email/templates/trial-warning.js.map +1 -0
- package/dist/email/templates/verify-email.d.ts +7 -0
- package/dist/email/templates/verify-email.d.ts.map +1 -0
- package/dist/email/templates/verify-email.js +59 -0
- package/dist/email/templates/verify-email.js.map +1 -0
- package/dist/email/templates/welcome.d.ts +7 -0
- package/dist/email/templates/welcome.d.ts.map +1 -0
- package/dist/email/templates/welcome.js +51 -0
- package/dist/email/templates/welcome.js.map +1 -0
- package/dist/email/templates/workspace-invitation.d.ts +10 -0
- package/dist/email/templates/workspace-invitation.d.ts.map +1 -0
- package/dist/email/templates/workspace-invitation.js +79 -0
- package/dist/email/templates/workspace-invitation.js.map +1 -0
- package/dist/env.d.ts +21 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +129 -0
- package/dist/env.js.map +1 -0
- package/dist/identity/audit.d.ts +39 -0
- package/dist/identity/audit.d.ts.map +1 -0
- package/dist/identity/audit.js +51 -0
- package/dist/identity/audit.js.map +1 -0
- package/dist/identity/errors.d.ts +32 -0
- package/dist/identity/errors.d.ts.map +1 -0
- package/dist/identity/errors.js +25 -0
- package/dist/identity/errors.js.map +1 -0
- package/dist/identity/index.d.ts +16 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +15 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/service.d.ts +70 -0
- package/dist/identity/service.d.ts.map +1 -0
- package/dist/identity/service.js +179 -0
- package/dist/identity/service.js.map +1 -0
- package/dist/identity/types.d.ts +27 -0
- package/dist/identity/types.d.ts.map +1 -0
- package/dist/identity/types.js +5 -0
- package/dist/identity/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +50 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +183 -0
- package/dist/logger.js.map +1 -0
- package/dist/notifications/index.d.ts +86 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +112 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/triggers.d.ts +68 -0
- package/dist/notifications/triggers.d.ts.map +1 -0
- package/dist/notifications/triggers.js +145 -0
- package/dist/notifications/triggers.js.map +1 -0
- package/dist/notifications/types.d.ts +19 -0
- package/dist/notifications/types.d.ts.map +1 -0
- package/dist/notifications/types.js +11 -0
- package/dist/notifications/types.js.map +1 -0
- package/package.json +101 -0
- package/src/db/migrations/0000_sharp_night_nurse.sql +52 -0
- package/src/db/migrations/0001_hesitant_vapor.sql +40 -0
- package/src/db/migrations/0002_numerous_lady_vermin.sql +235 -0
- package/src/db/migrations/0003_mighty_jean_grey.sql +37 -0
- package/src/db/migrations/0004_cynical_grandmaster.sql +2 -0
- package/src/db/migrations/0005_careful_switch.sql +8 -0
- package/src/db/migrations/0006_tense_blue_marvel.sql +16 -0
- package/src/db/migrations/0007_next_squadron_sinister.sql +1 -0
- package/src/db/migrations/0008_powerful_scream.sql +39 -0
- package/src/db/migrations/0009_sad_ink.sql +1 -0
- package/src/db/migrations/0010_shocking_omega_flight.sql +2 -0
- package/src/db/migrations/0011_ambiguous_otto_octavius.sql +151 -0
- package/src/db/migrations/0011_hnsw_vector_index.sql +17 -0
- package/src/db/migrations/0012_user_profiles.sql +30 -0
- package/src/db/migrations/0013_trending_seasonal_columns.sql +9 -0
- package/src/db/migrations/0014_add_product_type_to_agents.sql +6 -0
- package/src/db/migrations/0015_new_v1_tables.sql +149 -0
- package/src/db/migrations/0016_document_types.sql +30 -0
- package/src/db/migrations/0017_document_metadata.sql +2 -0
- package/src/db/migrations/0018_identity_graph.sql +200 -0
- package/src/db/migrations/0019_db_driven_prompts.sql +20 -0
- package/src/db/migrations/0020_pending_extractions.sql +35 -0
- package/src/db/migrations/0021_rag_documents.sql +39 -0
- package/src/db/migrations/0022_user_quotas_usage_jsonb.sql +70 -0
- package/src/db/migrations/0023_user_facts_user_scoped_unique.sql +17 -0
- package/src/db/migrations/0024_user_memories_hnsw.sql +23 -0
- package/src/db/migrations/0025_audit_trigger_errcode.sql +21 -0
- package/src/db/migrations/0026_user_profile_snapshots_composite_pk.sql +17 -0
- package/src/db/migrations/0027_billing_settings_mnt_columns.sql +54 -0
- package/src/db/migrations/0028_trial_credits_normalized_email.sql +37 -0
- package/src/db/migrations/0029_usage_records_conversation_idx.sql +13 -0
- package/src/db/migrations/0030_pending_extractions_composite_idx.sql +11 -0
- package/src/db/migrations/0031_rag_documents_hnsw.sql +16 -0
- package/src/db/migrations/0032_rate_limit_unique_idx.sql +16 -0
- package/src/db/migrations/0033_pending_extractions_partial_idx.sql +18 -0
- package/src/db/migrations/0034_rate_limit_count.sql +9 -0
- package/src/db/migrations/0035_credit_reservations.sql +24 -0
- package/src/db/migrations/0036_executions_audit_jobs.sql +96 -0
- package/src/db/migrations/0037_rate_limit_minute_bucket.sql +46 -0
- package/src/db/migrations/0038_user_quotas_generic_usage.sql +25 -0
- package/src/db/migrations/0039_platform_admin_impersonation.sql +27 -0
- package/src/db/migrations/0040_ee_audit_chain.sql +23 -0
- package/src/db/migrations/README.md +57 -0
- package/src/db/migrations/meta/0000_snapshot.json +348 -0
- package/src/db/migrations/meta/0001_snapshot.json +631 -0
- package/src/db/migrations/meta/0002_snapshot.json +2269 -0
- package/src/db/migrations/meta/0003_snapshot.json +2488 -0
- package/src/db/migrations/meta/0004_snapshot.json +2500 -0
- package/src/db/migrations/meta/0005_snapshot.json +2548 -0
- package/src/db/migrations/meta/0006_snapshot.json +2650 -0
- package/src/db/migrations/meta/0007_snapshot.json +2657 -0
- package/src/db/migrations/meta/0008_snapshot.json +2719 -0
- package/src/db/migrations/meta/0009_snapshot.json +2726 -0
- package/src/db/migrations/meta/0010_snapshot.json +2739 -0
- package/src/db/migrations/meta/0011_snapshot.json +3886 -0
- package/src/db/migrations/meta/_journal.json +300 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
-- NOTE (2026-08-26): every table below is also created by
|
|
2
|
+
-- 0011_ambiguous_otto_octavius.sql, which runs first and whose
|
|
3
|
+
-- shared_reports definition is the fuller one (it has conversation_id
|
|
4
|
+
-- and parent_report_id; this file's does not). Replaying the chain
|
|
5
|
+
-- against an empty database therefore failed here with
|
|
6
|
+
-- `relation "knowledge_articles" already exists`.
|
|
7
|
+
--
|
|
8
|
+
-- Guarded rather than deleted: the file is in the journal, and on any
|
|
9
|
+
-- database where it did run first its statements are the ones that
|
|
10
|
+
-- created these tables. IF NOT EXISTS makes both orders converge on
|
|
11
|
+
-- 0011's schema.
|
|
12
|
+
|
|
13
|
+
-- Migration: 0015_new_v1_tables
|
|
14
|
+
-- Description: Add 6 new tables for v1.0 features (knowledge, competition, referral, shared reports)
|
|
15
|
+
-- Phase: 72-02
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS "knowledge_articles" (
|
|
18
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
19
|
+
"slug" text NOT NULL,
|
|
20
|
+
"category" text NOT NULL,
|
|
21
|
+
"title" jsonb NOT NULL,
|
|
22
|
+
"description" jsonb NOT NULL,
|
|
23
|
+
"content" jsonb NOT NULL,
|
|
24
|
+
"tags" text[],
|
|
25
|
+
"reading_time_minutes" integer DEFAULT 5 NOT NULL,
|
|
26
|
+
"sort_order" integer DEFAULT 0 NOT NULL,
|
|
27
|
+
"is_published" boolean DEFAULT false NOT NULL,
|
|
28
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
29
|
+
"updated_at" timestamp DEFAULT now() NOT NULL
|
|
30
|
+
);
|
|
31
|
+
--> statement-breakpoint
|
|
32
|
+
CREATE TABLE IF NOT EXISTS "competitions" (
|
|
33
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
34
|
+
"name" jsonb NOT NULL,
|
|
35
|
+
"description" jsonb NOT NULL,
|
|
36
|
+
"start_date" timestamp NOT NULL,
|
|
37
|
+
"end_date" timestamp NOT NULL,
|
|
38
|
+
"is_active" boolean DEFAULT false NOT NULL,
|
|
39
|
+
"prize_description" jsonb,
|
|
40
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
41
|
+
"updated_at" timestamp DEFAULT now() NOT NULL
|
|
42
|
+
);
|
|
43
|
+
--> statement-breakpoint
|
|
44
|
+
CREATE TABLE IF NOT EXISTS "competition_entries" (
|
|
45
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
46
|
+
"competition_id" text NOT NULL,
|
|
47
|
+
"workspace_id" text NOT NULL,
|
|
48
|
+
"school_name" text NOT NULL,
|
|
49
|
+
"class_name" text NOT NULL,
|
|
50
|
+
"assessment_count" integer DEFAULT 0 NOT NULL,
|
|
51
|
+
"total_score" integer DEFAULT 0 NOT NULL,
|
|
52
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
53
|
+
"updated_at" timestamp DEFAULT now() NOT NULL
|
|
54
|
+
);
|
|
55
|
+
--> statement-breakpoint
|
|
56
|
+
CREATE TABLE IF NOT EXISTS "referral_codes" (
|
|
57
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
58
|
+
"user_id" text NOT NULL,
|
|
59
|
+
"code" text NOT NULL,
|
|
60
|
+
"created_at" timestamp DEFAULT now() NOT NULL
|
|
61
|
+
);
|
|
62
|
+
--> statement-breakpoint
|
|
63
|
+
CREATE TABLE IF NOT EXISTS "referrals" (
|
|
64
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
65
|
+
"referrer_id" text NOT NULL,
|
|
66
|
+
"referred_user_id" text NOT NULL,
|
|
67
|
+
"referral_code_id" text NOT NULL,
|
|
68
|
+
"status" text DEFAULT 'pending' NOT NULL,
|
|
69
|
+
"completed_at" timestamp,
|
|
70
|
+
"created_at" timestamp DEFAULT now() NOT NULL
|
|
71
|
+
);
|
|
72
|
+
--> statement-breakpoint
|
|
73
|
+
CREATE TABLE IF NOT EXISTS "shared_reports" (
|
|
74
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
75
|
+
"user_id" text NOT NULL,
|
|
76
|
+
"workspace_id" text NOT NULL,
|
|
77
|
+
"report_type" text DEFAULT 'career' NOT NULL,
|
|
78
|
+
"report_data" jsonb NOT NULL,
|
|
79
|
+
"is_public" boolean DEFAULT true NOT NULL,
|
|
80
|
+
"view_count" integer DEFAULT 0 NOT NULL,
|
|
81
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
82
|
+
"updated_at" timestamp DEFAULT now() NOT NULL
|
|
83
|
+
);
|
|
84
|
+
--> statement-breakpoint
|
|
85
|
+
DO $$ BEGIN
|
|
86
|
+
ALTER TABLE "competition_entries" ADD CONSTRAINT "competition_entries_competition_id_competitions_id_fk" FOREIGN KEY ("competition_id") REFERENCES "public"."competitions"("id") ON DELETE cascade ON UPDATE no action;
|
|
87
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
88
|
+
END $$;
|
|
89
|
+
--> statement-breakpoint
|
|
90
|
+
DO $$ BEGIN
|
|
91
|
+
ALTER TABLE "competition_entries" ADD CONSTRAINT "competition_entries_workspace_id_organization_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;
|
|
92
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
93
|
+
END $$;
|
|
94
|
+
--> statement-breakpoint
|
|
95
|
+
DO $$ BEGIN
|
|
96
|
+
ALTER TABLE "referral_codes" ADD CONSTRAINT "referral_codes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
97
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
98
|
+
END $$;
|
|
99
|
+
--> statement-breakpoint
|
|
100
|
+
DO $$ BEGIN
|
|
101
|
+
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referrer_id_users_id_fk" FOREIGN KEY ("referrer_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
102
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
103
|
+
END $$;
|
|
104
|
+
--> statement-breakpoint
|
|
105
|
+
DO $$ BEGIN
|
|
106
|
+
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referred_user_id_users_id_fk" FOREIGN KEY ("referred_user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
107
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
108
|
+
END $$;
|
|
109
|
+
--> statement-breakpoint
|
|
110
|
+
DO $$ BEGIN
|
|
111
|
+
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referral_code_id_referral_codes_id_fk" FOREIGN KEY ("referral_code_id") REFERENCES "public"."referral_codes"("id") ON DELETE cascade ON UPDATE no action;
|
|
112
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
113
|
+
END $$;
|
|
114
|
+
--> statement-breakpoint
|
|
115
|
+
DO $$ BEGIN
|
|
116
|
+
ALTER TABLE "shared_reports" ADD CONSTRAINT "shared_reports_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
117
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
118
|
+
END $$;
|
|
119
|
+
--> statement-breakpoint
|
|
120
|
+
DO $$ BEGIN
|
|
121
|
+
ALTER TABLE "shared_reports" ADD CONSTRAINT "shared_reports_workspace_id_organization_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;
|
|
122
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
123
|
+
END $$;
|
|
124
|
+
--> statement-breakpoint
|
|
125
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "knowledge_articles_slug_idx" ON "knowledge_articles" USING btree ("slug");
|
|
126
|
+
--> statement-breakpoint
|
|
127
|
+
CREATE INDEX IF NOT EXISTS "knowledge_articles_category_idx" ON "knowledge_articles" USING btree ("category");
|
|
128
|
+
--> statement-breakpoint
|
|
129
|
+
CREATE INDEX IF NOT EXISTS "competitions_is_active_idx" ON "competitions" USING btree ("is_active");
|
|
130
|
+
--> statement-breakpoint
|
|
131
|
+
CREATE INDEX IF NOT EXISTS "competition_entries_competition_id_idx" ON "competition_entries" USING btree ("competition_id");
|
|
132
|
+
--> statement-breakpoint
|
|
133
|
+
CREATE INDEX IF NOT EXISTS "competition_entries_workspace_id_idx" ON "competition_entries" USING btree ("workspace_id");
|
|
134
|
+
--> statement-breakpoint
|
|
135
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "competition_entries_competition_workspace_idx" ON "competition_entries" USING btree ("competition_id","workspace_id");
|
|
136
|
+
--> statement-breakpoint
|
|
137
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "referral_codes_code_idx" ON "referral_codes" USING btree ("code");
|
|
138
|
+
--> statement-breakpoint
|
|
139
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "referral_codes_user_id_idx" ON "referral_codes" USING btree ("user_id");
|
|
140
|
+
--> statement-breakpoint
|
|
141
|
+
CREATE INDEX IF NOT EXISTS "referrals_referrer_id_idx" ON "referrals" USING btree ("referrer_id");
|
|
142
|
+
--> statement-breakpoint
|
|
143
|
+
CREATE INDEX IF NOT EXISTS "referrals_referred_user_id_idx" ON "referrals" USING btree ("referred_user_id");
|
|
144
|
+
--> statement-breakpoint
|
|
145
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "referrals_referred_user_id_unique_idx" ON "referrals" USING btree ("referred_user_id");
|
|
146
|
+
--> statement-breakpoint
|
|
147
|
+
CREATE INDEX IF NOT EXISTS "shared_reports_user_id_idx" ON "shared_reports" USING btree ("user_id");
|
|
148
|
+
--> statement-breakpoint
|
|
149
|
+
CREATE INDEX IF NOT EXISTS "shared_reports_workspace_id_idx" ON "shared_reports" USING btree ("workspace_id");
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- Document Types reference table
|
|
2
|
+
CREATE TABLE IF NOT EXISTS "document_types" (
|
|
3
|
+
"id" text PRIMARY KEY,
|
|
4
|
+
"name" text NOT NULL,
|
|
5
|
+
"name_mn" text NOT NULL,
|
|
6
|
+
"description" text,
|
|
7
|
+
"icon" text,
|
|
8
|
+
"sort_order" integer NOT NULL DEFAULT 0,
|
|
9
|
+
"is_active" boolean NOT NULL DEFAULT true,
|
|
10
|
+
"created_at" timestamp DEFAULT now() NOT NULL
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
-- Add type_id column to documents
|
|
14
|
+
ALTER TABLE "documents" ADD COLUMN IF NOT EXISTS "type_id" text REFERENCES "document_types"("id") ON DELETE SET NULL;
|
|
15
|
+
|
|
16
|
+
-- Index for type lookups
|
|
17
|
+
CREATE INDEX IF NOT EXISTS "documents_type_id_idx" ON "documents" ("type_id");
|
|
18
|
+
|
|
19
|
+
-- Seed 5 career document types
|
|
20
|
+
INSERT INTO "document_types" ("id", "name", "name_mn", "description", "icon", "sort_order") VALUES
|
|
21
|
+
('career-report', 'Career Report', 'Карьерын тайлан', 'Full career assessment report with recommendations', 'file-text', 0),
|
|
22
|
+
('deep-dive-report', 'Deep Dive Report', 'Гүнзгий шинжилгээ', 'Detailed analysis of a specific career path', 'search', 1),
|
|
23
|
+
('scholarship-report', 'Scholarship Report', 'Тэтгэлгийн тайлан', 'Scholarship matching and financial aid report', 'gift', 2),
|
|
24
|
+
('comparison-report', 'Comparison Report', 'Харьцуулалт', 'Side-by-side career or university comparison', 'columns', 3),
|
|
25
|
+
('skills-report', 'Skills Report', 'Ур чадварын тайлан', 'Skills gap analysis and development plan', 'target', 4)
|
|
26
|
+
ON CONFLICT ("id") DO NOTHING;
|
|
27
|
+
|
|
28
|
+
-- Backfill existing career report documents
|
|
29
|
+
UPDATE "documents" SET "type_id" = 'career-report'
|
|
30
|
+
WHERE "kind" = 'text' AND ("title" LIKE '%Карьерын Тайлан%' OR "title" LIKE '%Career Report%');
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
-- AI-ARCHITECTURE Phase B: User Identity Graph
|
|
2
|
+
--
|
|
3
|
+
-- Adds the four tables that back the cross-product memory system.
|
|
4
|
+
-- See packages/core/src/db/schema/identity.ts for the Drizzle schema
|
|
5
|
+
-- and docs/AI-ARCHITECTURE.md §2 for the design rationale.
|
|
6
|
+
--
|
|
7
|
+
-- Numbering note: this file is named 0018 to slot after the existing
|
|
8
|
+
-- 0017_document_metadata. The project mixes two migration patterns —
|
|
9
|
+
-- 0000–0011 are tracked by drizzle's _journal.json (auto-generated by
|
|
10
|
+
-- `drizzle-kit generate`), while 0012–0018 are hand-rolled SQL applied
|
|
11
|
+
-- via `pnpm --filter @intelligo-dev/core db:push` and have no journal
|
|
12
|
+
-- entry. Future hand-rolled migrations should continue the 0019,
|
|
13
|
+
-- 0020, ... sequence and stay out of _journal.json.
|
|
14
|
+
--
|
|
15
|
+
-- This migration is hand-crafted (not produced by `drizzle-kit generate`)
|
|
16
|
+
-- because the auto-generator also picks up unrelated drift between the
|
|
17
|
+
-- current schema files and the last applied migration. Keeping the
|
|
18
|
+
-- diff narrow lets this PR ship the identity layer without dragging in
|
|
19
|
+
-- those unrelated alterations.
|
|
20
|
+
|
|
21
|
+
-- ---------------------------------------------------------------------------
|
|
22
|
+
-- 1. user_facts — structured, evidence-backed facts
|
|
23
|
+
-- ---------------------------------------------------------------------------
|
|
24
|
+
CREATE TABLE "user_facts" (
|
|
25
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
26
|
+
"user_id" text NOT NULL,
|
|
27
|
+
"workspace_id" text NOT NULL,
|
|
28
|
+
"category" text NOT NULL,
|
|
29
|
+
"key" text NOT NULL,
|
|
30
|
+
"value" jsonb NOT NULL,
|
|
31
|
+
"confidence" double precision NOT NULL,
|
|
32
|
+
"importance" integer DEFAULT 5 NOT NULL,
|
|
33
|
+
"source_product" text,
|
|
34
|
+
"source_session_id" text,
|
|
35
|
+
"source_message_id" text,
|
|
36
|
+
"extraction_method" text,
|
|
37
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
38
|
+
"last_confirmed_at" timestamp,
|
|
39
|
+
"expires_at" timestamp
|
|
40
|
+
);
|
|
41
|
+
--> statement-breakpoint
|
|
42
|
+
ALTER TABLE "user_facts" ADD CONSTRAINT "user_facts_user_id_users_id_fk"
|
|
43
|
+
FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade;
|
|
44
|
+
--> statement-breakpoint
|
|
45
|
+
ALTER TABLE "user_facts" ADD CONSTRAINT "user_facts_workspace_id_organization_id_fk"
|
|
46
|
+
FOREIGN KEY ("workspace_id") REFERENCES "public"."organization"("id") ON DELETE cascade;
|
|
47
|
+
--> statement-breakpoint
|
|
48
|
+
CREATE INDEX "user_facts_user_idx" ON "user_facts" USING btree ("user_id");
|
|
49
|
+
--> statement-breakpoint
|
|
50
|
+
CREATE INDEX "user_facts_workspace_idx" ON "user_facts" USING btree ("workspace_id");
|
|
51
|
+
--> statement-breakpoint
|
|
52
|
+
CREATE INDEX "user_facts_category_idx" ON "user_facts" USING btree ("workspace_id", "category");
|
|
53
|
+
--> statement-breakpoint
|
|
54
|
+
CREATE UNIQUE INDEX "user_facts_workspace_category_key_uniq"
|
|
55
|
+
ON "user_facts" USING btree ("workspace_id", "category", "key");
|
|
56
|
+
--> statement-breakpoint
|
|
57
|
+
|
|
58
|
+
-- ---------------------------------------------------------------------------
|
|
59
|
+
-- 2. user_memories — episodic memories with pgvector embedding
|
|
60
|
+
-- ---------------------------------------------------------------------------
|
|
61
|
+
CREATE TABLE "user_memories" (
|
|
62
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
63
|
+
"user_id" text NOT NULL,
|
|
64
|
+
"workspace_id" text NOT NULL,
|
|
65
|
+
"kind" text NOT NULL,
|
|
66
|
+
"content" text NOT NULL,
|
|
67
|
+
-- 1536 dimensions matches OpenAI text-embedding-3-small (the default
|
|
68
|
+
-- embedding model used across @intelligo-dev/ai). If the embedding model
|
|
69
|
+
-- ever changes the dimension constraint will need a new migration —
|
|
70
|
+
-- pgvector cannot resize a vector column in place. Search for
|
|
71
|
+
-- "vector(1536)" in apps/* before editing.
|
|
72
|
+
"embedding" vector(1536),
|
|
73
|
+
"source_product" text,
|
|
74
|
+
"source_session_id" text,
|
|
75
|
+
"source_message_ids" text[],
|
|
76
|
+
"importance" integer DEFAULT 5 NOT NULL,
|
|
77
|
+
"decay_factor" double precision DEFAULT 1 NOT NULL,
|
|
78
|
+
"metadata" jsonb,
|
|
79
|
+
"created_at" timestamp DEFAULT now() NOT NULL
|
|
80
|
+
);
|
|
81
|
+
--> statement-breakpoint
|
|
82
|
+
ALTER TABLE "user_memories" ADD CONSTRAINT "user_memories_user_id_users_id_fk"
|
|
83
|
+
FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade;
|
|
84
|
+
--> statement-breakpoint
|
|
85
|
+
ALTER TABLE "user_memories" ADD CONSTRAINT "user_memories_workspace_id_organization_id_fk"
|
|
86
|
+
FOREIGN KEY ("workspace_id") REFERENCES "public"."organization"("id") ON DELETE cascade;
|
|
87
|
+
--> statement-breakpoint
|
|
88
|
+
CREATE INDEX "user_memories_user_idx" ON "user_memories" USING btree ("user_id");
|
|
89
|
+
--> statement-breakpoint
|
|
90
|
+
CREATE INDEX "user_memories_workspace_idx" ON "user_memories" USING btree ("workspace_id");
|
|
91
|
+
--> statement-breakpoint
|
|
92
|
+
CREATE INDEX "user_memories_kind_idx" ON "user_memories" USING btree ("workspace_id", "kind");
|
|
93
|
+
--> statement-breakpoint
|
|
94
|
+
-- IVFFlat vector index for cosine-similarity recall. Drizzle-orm
|
|
95
|
+
-- doesn't expose this syntax natively, so it lives in the migration.
|
|
96
|
+
--
|
|
97
|
+
-- `lists = 100` is a starter value tuned for ~100k–1M rows
|
|
98
|
+
-- (rule of thumb: rows / 1000). IVFFlat performance degrades when the
|
|
99
|
+
-- table grows past `lists * sqrt(rows)`, so once Phase F starts
|
|
100
|
+
-- ingesting real memories at scale this should be either:
|
|
101
|
+
-- (a) raised to ~sqrt(actual_rows), then REINDEX, or
|
|
102
|
+
-- (b) replaced with HNSW (`USING hnsw (...) WITH (m=16, ef_construction=64)`),
|
|
103
|
+
-- which doesn't have IVFFlat's tuning sensitivity but uses more memory.
|
|
104
|
+
-- See docs/AI-ARCHITECTURE.md §2.2 for the recall path that uses this.
|
|
105
|
+
CREATE INDEX "user_memories_embedding_ivfflat"
|
|
106
|
+
ON "user_memories" USING ivfflat ("embedding" vector_cosine_ops) WITH (lists = 100);
|
|
107
|
+
--> statement-breakpoint
|
|
108
|
+
|
|
109
|
+
-- ---------------------------------------------------------------------------
|
|
110
|
+
-- 3. user_profile_snapshots — synthesized, fast-read profile cache
|
|
111
|
+
-- ---------------------------------------------------------------------------
|
|
112
|
+
CREATE TABLE "user_profile_snapshots" (
|
|
113
|
+
"user_id" text PRIMARY KEY NOT NULL,
|
|
114
|
+
"workspace_id" text NOT NULL,
|
|
115
|
+
"version" integer NOT NULL,
|
|
116
|
+
"summary" text NOT NULL,
|
|
117
|
+
"summary_en" text,
|
|
118
|
+
"summary_mn" text,
|
|
119
|
+
"facts_digest" jsonb NOT NULL,
|
|
120
|
+
"active_goals" jsonb,
|
|
121
|
+
"personality_signals" jsonb,
|
|
122
|
+
"relationship_notes" jsonb,
|
|
123
|
+
"synthesized_by_model" text,
|
|
124
|
+
"synthesized_at" timestamp DEFAULT now() NOT NULL,
|
|
125
|
+
"next_synthesis_at" timestamp,
|
|
126
|
+
"trigger_reason" text
|
|
127
|
+
);
|
|
128
|
+
--> statement-breakpoint
|
|
129
|
+
ALTER TABLE "user_profile_snapshots" ADD CONSTRAINT "user_profile_snapshots_user_id_users_id_fk"
|
|
130
|
+
FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade;
|
|
131
|
+
--> statement-breakpoint
|
|
132
|
+
ALTER TABLE "user_profile_snapshots" ADD CONSTRAINT "user_profile_snapshots_workspace_id_organization_id_fk"
|
|
133
|
+
FOREIGN KEY ("workspace_id") REFERENCES "public"."organization"("id") ON DELETE cascade;
|
|
134
|
+
--> statement-breakpoint
|
|
135
|
+
CREATE INDEX "user_profile_snapshots_workspace_idx"
|
|
136
|
+
ON "user_profile_snapshots" USING btree ("workspace_id");
|
|
137
|
+
--> statement-breakpoint
|
|
138
|
+
|
|
139
|
+
-- ---------------------------------------------------------------------------
|
|
140
|
+
-- 4. user_memory_audit — append-only audit log
|
|
141
|
+
-- ---------------------------------------------------------------------------
|
|
142
|
+
CREATE TABLE "user_memory_audit" (
|
|
143
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
144
|
+
"user_id" text NOT NULL,
|
|
145
|
+
"workspace_id" text NOT NULL,
|
|
146
|
+
"target_kind" text NOT NULL,
|
|
147
|
+
"target_id" text NOT NULL,
|
|
148
|
+
"action" text NOT NULL,
|
|
149
|
+
"actor_kind" text NOT NULL,
|
|
150
|
+
"actor_id" text,
|
|
151
|
+
"before_value" jsonb,
|
|
152
|
+
"after_value" jsonb,
|
|
153
|
+
"reason" text,
|
|
154
|
+
"created_at" timestamp DEFAULT now() NOT NULL
|
|
155
|
+
);
|
|
156
|
+
--> statement-breakpoint
|
|
157
|
+
ALTER TABLE "user_memory_audit" ADD CONSTRAINT "user_memory_audit_user_id_users_id_fk"
|
|
158
|
+
FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade;
|
|
159
|
+
--> statement-breakpoint
|
|
160
|
+
ALTER TABLE "user_memory_audit" ADD CONSTRAINT "user_memory_audit_workspace_id_organization_id_fk"
|
|
161
|
+
FOREIGN KEY ("workspace_id") REFERENCES "public"."organization"("id") ON DELETE cascade;
|
|
162
|
+
--> statement-breakpoint
|
|
163
|
+
CREATE INDEX "user_memory_audit_user_idx" ON "user_memory_audit" USING btree ("user_id");
|
|
164
|
+
--> statement-breakpoint
|
|
165
|
+
CREATE INDEX "user_memory_audit_workspace_idx" ON "user_memory_audit" USING btree ("workspace_id");
|
|
166
|
+
--> statement-breakpoint
|
|
167
|
+
CREATE INDEX "user_memory_audit_target_idx"
|
|
168
|
+
ON "user_memory_audit" USING btree ("target_kind", "target_id");
|
|
169
|
+
--> statement-breakpoint
|
|
170
|
+
|
|
171
|
+
-- Append-only enforcement: facts/memories/snapshots can be updated and
|
|
172
|
+
-- deleted via the application layer, but their audit trail must not.
|
|
173
|
+
-- A future PR can replace this with a row-level revoke if RLS is wired
|
|
174
|
+
-- up; the trigger guard is the smaller change for now.
|
|
175
|
+
--
|
|
176
|
+
-- The function is fully qualified as `public.user_memory_audit_block_mutation`
|
|
177
|
+
-- so a future schema split (e.g., moving identity tables into an
|
|
178
|
+
-- "identity" schema) won't shadow it.
|
|
179
|
+
--
|
|
180
|
+
-- The exception message deliberately omits NEW.* / OLD.* — Postgres
|
|
181
|
+
-- writes RAISE EXCEPTION strings to the server log by default, and the
|
|
182
|
+
-- audit row's before/after values can contain user PII (interest,
|
|
183
|
+
-- skill, etc. extracted by an LLM). ERRCODE 25006 = read_only_sql_transaction;
|
|
184
|
+
-- close enough for upstream callers to recognize the class without
|
|
185
|
+
-- needing a custom code.
|
|
186
|
+
CREATE OR REPLACE FUNCTION public.user_memory_audit_block_mutation()
|
|
187
|
+
RETURNS trigger AS $$
|
|
188
|
+
BEGIN
|
|
189
|
+
RAISE EXCEPTION 'user_memory_audit is append-only'
|
|
190
|
+
USING ERRCODE = '25006', HINT = 'INSERT new audit rows; do not UPDATE or DELETE existing ones.';
|
|
191
|
+
END;
|
|
192
|
+
$$ LANGUAGE plpgsql;
|
|
193
|
+
--> statement-breakpoint
|
|
194
|
+
CREATE TRIGGER user_memory_audit_no_update
|
|
195
|
+
BEFORE UPDATE ON "user_memory_audit"
|
|
196
|
+
FOR EACH ROW EXECUTE FUNCTION public.user_memory_audit_block_mutation();
|
|
197
|
+
--> statement-breakpoint
|
|
198
|
+
CREATE TRIGGER user_memory_audit_no_delete
|
|
199
|
+
BEFORE DELETE ON "user_memory_audit"
|
|
200
|
+
FOR EACH ROW EXECUTE FUNCTION public.user_memory_audit_block_mutation();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- Phase C — DB-driven prompts and model config.
|
|
2
|
+
--
|
|
3
|
+
-- Adds two columns to the agents table that turn the database into the
|
|
4
|
+
-- single source of truth for prompts and per-agent model selection. Both
|
|
5
|
+
-- columns are nullable so the migration is safe to apply before the seed
|
|
6
|
+
-- backfill runs; once the seed has populated the new fields, application
|
|
7
|
+
-- code reads only from the database (the legacy
|
|
8
|
+
-- apps/app/lib/prompts/career-advisor.ts file is removed in the same
|
|
9
|
+
-- phase).
|
|
10
|
+
--
|
|
11
|
+
-- system_prompt is shaped as the BilingualText JSON {"en": "...", "mn": "..."},
|
|
12
|
+
-- matching the existing `name` and `description` columns. model_config is a
|
|
13
|
+
-- forward-looking JSONB blob that will eventually replace `default_model`
|
|
14
|
+
-- with full routing rules; for now it stores at minimum { primary: "..." }.
|
|
15
|
+
--
|
|
16
|
+
-- Apply with: psql $DATABASE_URL -f packages/core/src/db/migrations/0019_db_driven_prompts.sql
|
|
17
|
+
|
|
18
|
+
ALTER TABLE "agents"
|
|
19
|
+
ADD COLUMN IF NOT EXISTS "system_prompt" jsonb,
|
|
20
|
+
ADD COLUMN IF NOT EXISTS "model_config" jsonb;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
-- Phase F — pending_extractions queue.
|
|
2
|
+
--
|
|
3
|
+
-- Background-job queue for fact extraction. The chat handler enqueues
|
|
4
|
+
-- one row per ended conversation; the worker (in @intelligo-dev/agents)
|
|
5
|
+
-- polls 'pending' rows, transitions them through 'processing' →
|
|
6
|
+
-- ('completed' | 'failed'), and the conversation_id uniqueness
|
|
7
|
+
-- constraint guarantees a conversation is never enqueued twice.
|
|
8
|
+
--
|
|
9
|
+
-- Cron poller wiring lands in a follow-up; this migration only
|
|
10
|
+
-- creates the storage layer so the worker has somewhere to read from.
|
|
11
|
+
--
|
|
12
|
+
-- Apply with: psql $DATABASE_URL -f packages/core/src/db/migrations/0020_pending_extractions.sql
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS "pending_extractions" (
|
|
15
|
+
"id" text PRIMARY KEY,
|
|
16
|
+
"conversation_id" text NOT NULL,
|
|
17
|
+
"workspace_id" text NOT NULL REFERENCES "organization"("id") ON DELETE CASCADE,
|
|
18
|
+
"user_id" text NOT NULL REFERENCES "users"("id") ON DELETE CASCADE,
|
|
19
|
+
"product_slug" text NOT NULL,
|
|
20
|
+
|
|
21
|
+
"status" text NOT NULL DEFAULT 'pending',
|
|
22
|
+
"attempts" integer NOT NULL DEFAULT 0,
|
|
23
|
+
"max_attempts" integer NOT NULL DEFAULT 3,
|
|
24
|
+
|
|
25
|
+
"last_error" text,
|
|
26
|
+
"next_retry_at" timestamp,
|
|
27
|
+
|
|
28
|
+
"enqueued_at" timestamp NOT NULL DEFAULT NOW(),
|
|
29
|
+
"started_at" timestamp,
|
|
30
|
+
"completed_at" timestamp
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CREATE INDEX IF NOT EXISTS "pending_extractions_status_idx" ON "pending_extractions" ("status");
|
|
34
|
+
CREATE INDEX IF NOT EXISTS "pending_extractions_workspace_idx" ON "pending_extractions" ("workspace_id");
|
|
35
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "pending_extractions_conversation_uniq" ON "pending_extractions" ("conversation_id");
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
-- Phase H — rag_documents table.
|
|
2
|
+
--
|
|
3
|
+
-- One shared table that backs every collection in
|
|
4
|
+
-- @intelligo-dev/agents/rag. Each typed Collection wrapper in a product
|
|
5
|
+
-- package writes through this table with its own `collection` slug
|
|
6
|
+
-- and Zod-validated metadata, so adding a new collection means
|
|
7
|
+
-- adding a TS file, not running a migration.
|
|
8
|
+
--
|
|
9
|
+
-- The pgvector index uses IVFFlat with cosine ops; the lists
|
|
10
|
+
-- parameter starts conservative for early-launch traffic and can be
|
|
11
|
+
-- tuned upward via REINDEX once we measure real recall vs. query
|
|
12
|
+
-- latency. The composite (collection, embedding) layout means a
|
|
13
|
+
-- per-collection ANN search filters first then ranks.
|
|
14
|
+
--
|
|
15
|
+
-- Apply with: psql $DATABASE_URL -f packages/core/src/db/migrations/0021_rag_documents.sql
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS "rag_documents" (
|
|
18
|
+
"id" text PRIMARY KEY,
|
|
19
|
+
"collection" text NOT NULL,
|
|
20
|
+
"external_id" text,
|
|
21
|
+
|
|
22
|
+
"content" text NOT NULL,
|
|
23
|
+
"metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
24
|
+
"embedding" vector(1536),
|
|
25
|
+
|
|
26
|
+
"created_at" timestamp NOT NULL DEFAULT NOW(),
|
|
27
|
+
"updated_at" timestamp NOT NULL DEFAULT NOW()
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE INDEX IF NOT EXISTS "rag_documents_collection_idx" ON "rag_documents" ("collection");
|
|
31
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "rag_documents_collection_external_uniq" ON "rag_documents" ("collection", "external_id");
|
|
32
|
+
|
|
33
|
+
-- IVFFlat index for cosine similarity. lists=100 is a reasonable
|
|
34
|
+
-- starting point for ~10k documents per collection; bump after
|
|
35
|
+
-- measuring recall.
|
|
36
|
+
CREATE INDEX IF NOT EXISTS "rag_documents_embedding_idx"
|
|
37
|
+
ON "rag_documents"
|
|
38
|
+
USING ivfflat ("embedding" vector_cosine_ops)
|
|
39
|
+
WITH (lists = 100);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
-- NOTE (2026-08-26): user_quotas is created here.
|
|
2
|
+
--
|
|
3
|
+
-- No migration ever created it — the table existed only in schema.ts,
|
|
4
|
+
-- so every environment got it from `drizzle-kit push` and replaying
|
|
5
|
+
-- the chain against an empty database died on the ALTER below. Of the
|
|
6
|
+
-- 49 tables in the schema it was the only one missing from the chain.
|
|
7
|
+
--
|
|
8
|
+
-- Created in this file rather than a new earlier one because this is
|
|
9
|
+
-- the first migration that touches the table; a repair migration would
|
|
10
|
+
-- have needed a number that sorts between 0021 and 0022. Guarded, so
|
|
11
|
+
-- databases that already have it are unaffected.
|
|
12
|
+
CREATE TABLE IF NOT EXISTS "user_quotas" (
|
|
13
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
14
|
+
"user_id" text NOT NULL,
|
|
15
|
+
"workspace_id" text NOT NULL,
|
|
16
|
+
"plan" text DEFAULT 'free' NOT NULL,
|
|
17
|
+
"chat_messages_used" integer DEFAULT 0 NOT NULL,
|
|
18
|
+
"assessments_used" integer DEFAULT 0 NOT NULL,
|
|
19
|
+
"reports_used" integer DEFAULT 0 NOT NULL,
|
|
20
|
+
"total_cost_usd" real DEFAULT 0 NOT NULL,
|
|
21
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
22
|
+
"updated_at" timestamp DEFAULT now() NOT NULL,
|
|
23
|
+
CONSTRAINT "user_quotas_user_id_unique" UNIQUE("user_id")
|
|
24
|
+
);
|
|
25
|
+
--> statement-breakpoint
|
|
26
|
+
DO $$ BEGIN
|
|
27
|
+
ALTER TABLE "user_quotas" ADD CONSTRAINT "user_quotas_user_id_users_id_fk"
|
|
28
|
+
FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
29
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
30
|
+
END $$;
|
|
31
|
+
--> statement-breakpoint
|
|
32
|
+
DO $$ BEGIN
|
|
33
|
+
ALTER TABLE "user_quotas" ADD CONSTRAINT "user_quotas_workspace_id_organization_id_fk"
|
|
34
|
+
FOREIGN KEY ("workspace_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;
|
|
35
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
36
|
+
END $$;
|
|
37
|
+
--> statement-breakpoint
|
|
38
|
+
CREATE INDEX IF NOT EXISTS "user_quotas_user_id_idx" ON "user_quotas" USING btree ("user_id");
|
|
39
|
+
--> statement-breakpoint
|
|
40
|
+
CREATE INDEX IF NOT EXISTS "user_quotas_workspace_id_idx" ON "user_quotas" USING btree ("workspace_id");
|
|
41
|
+
--> statement-breakpoint
|
|
42
|
+
|
|
43
|
+
-- Wave 4 — userQuotas.usage JSONB column for generic per-action counters.
|
|
44
|
+
--
|
|
45
|
+
-- Adds a generic `usage` JSONB column alongside the legacy career-only
|
|
46
|
+
-- chat_messages_used / assessments_used / reports_used integer columns.
|
|
47
|
+
-- The new column starts at '{}' for every row; recordFeatureUsage in
|
|
48
|
+
-- @intelligo-dev/billing/feature-quota.ts dual-writes both the legacy
|
|
49
|
+
-- column AND the corresponding `usage[action]` slot so the read path
|
|
50
|
+
-- can be flipped atomically in a follow-up commit once production
|
|
51
|
+
-- soak confirms the JSONB pathway is reliable.
|
|
52
|
+
--
|
|
53
|
+
-- Backfill query (optional, for any rows that already have legacy
|
|
54
|
+
-- counts that need to be reflected in the JSONB shape):
|
|
55
|
+
--
|
|
56
|
+
-- UPDATE user_quotas
|
|
57
|
+
-- SET usage = jsonb_build_object(
|
|
58
|
+
-- 'chat', chat_messages_used,
|
|
59
|
+
-- 'assessment', assessments_used,
|
|
60
|
+
-- 'report', reports_used
|
|
61
|
+
-- )
|
|
62
|
+
-- WHERE usage = '{}'::jsonb;
|
|
63
|
+
--
|
|
64
|
+
-- Run the backfill once after the deploy and verify counts match
|
|
65
|
+
-- before flipping the read path.
|
|
66
|
+
--
|
|
67
|
+
-- Apply with: psql $DATABASE_URL -f packages/core/src/db/migrations/0022_user_quotas_usage_jsonb.sql
|
|
68
|
+
|
|
69
|
+
ALTER TABLE "user_quotas"
|
|
70
|
+
ADD COLUMN IF NOT EXISTS "usage" jsonb NOT NULL DEFAULT '{}'::jsonb;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- AI-ARCHITECTURE follow-up: user_facts uniqueness must include user_id.
|
|
2
|
+
--
|
|
3
|
+
-- The original 0018 migration scoped the unique index to
|
|
4
|
+
-- (workspace_id, category, key), which made it impossible for two
|
|
5
|
+
-- members of the same org (family, classroom, team) to hold the
|
|
6
|
+
-- same fact key — e.g. two students both with `interest: mathematics`.
|
|
7
|
+
-- The second upsert would collide on the unique index and either
|
|
8
|
+
-- fail or silently overwrite the first user's fact.
|
|
9
|
+
--
|
|
10
|
+
-- Drop the old index and replace it with a (user_id, workspace_id,
|
|
11
|
+
-- category, key) unique index so facts are always scoped to the
|
|
12
|
+
-- owning user.
|
|
13
|
+
|
|
14
|
+
DROP INDEX IF EXISTS "user_facts_workspace_category_key_uniq";
|
|
15
|
+
--> statement-breakpoint
|
|
16
|
+
CREATE UNIQUE INDEX "user_facts_user_workspace_category_key_uniq"
|
|
17
|
+
ON "user_facts" USING btree ("user_id", "workspace_id", "category", "key");
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- AI-ARCHITECTURE follow-up: switch user_memories embedding index to HNSW.
|
|
2
|
+
--
|
|
3
|
+
-- The original 0018 migration used IVFFlat with `lists = 100`, a value
|
|
4
|
+
-- tuned for ~100k–1M rows. At launch the table starts empty, so for
|
|
5
|
+
-- the first several months IVFFlat recall will be degraded: with
|
|
6
|
+
-- rows << lists the probe count dominates and approximation quality
|
|
7
|
+
-- drops.
|
|
8
|
+
--
|
|
9
|
+
-- HNSW (available in pgvector >= 0.5.0) has no such tuning cliff —
|
|
10
|
+
-- recall is good from a handful of rows up through tens of millions,
|
|
11
|
+
-- at the cost of somewhat higher build time and ~2x memory per vector.
|
|
12
|
+
-- For this workload (synthesis-time recall, not per-request search)
|
|
13
|
+
-- that tradeoff is clearly worth it.
|
|
14
|
+
--
|
|
15
|
+
-- m / ef_construction values follow the pgvector docs' defaults
|
|
16
|
+
-- tuned for text embeddings; we can raise ef_construction later via
|
|
17
|
+
-- REINDEX if recall needs tightening.
|
|
18
|
+
|
|
19
|
+
DROP INDEX IF EXISTS "user_memories_embedding_ivfflat";
|
|
20
|
+
--> statement-breakpoint
|
|
21
|
+
CREATE INDEX "user_memories_embedding_hnsw"
|
|
22
|
+
ON "user_memories" USING hnsw ("embedding" vector_cosine_ops)
|
|
23
|
+
WITH (m = 16, ef_construction = 64);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- AI-ARCHITECTURE follow-up: fix audit trigger exception class.
|
|
2
|
+
--
|
|
3
|
+
-- 0018 used ERRCODE 25006 (read_only_sql_transaction) for the
|
|
4
|
+
-- append-only guard on user_memory_audit. That code is documented
|
|
5
|
+
-- for "SQL transaction is read-only" and is semantically wrong for
|
|
6
|
+
-- "this table rejects UPDATE/DELETE" — a Sentry filter or pg error
|
|
7
|
+
-- class handler could misroute the exception into the transient
|
|
8
|
+
-- retry bucket.
|
|
9
|
+
--
|
|
10
|
+
-- P0001 (raise_exception) is the correct generic class for
|
|
11
|
+
-- application-level invariants raised via RAISE EXCEPTION. The
|
|
12
|
+
-- trigger still fires on BEFORE UPDATE and BEFORE DELETE and the
|
|
13
|
+
-- hint stays PII-free.
|
|
14
|
+
|
|
15
|
+
CREATE OR REPLACE FUNCTION public.user_memory_audit_block_mutation()
|
|
16
|
+
RETURNS trigger AS $$
|
|
17
|
+
BEGIN
|
|
18
|
+
RAISE EXCEPTION 'user_memory_audit is append-only'
|
|
19
|
+
USING ERRCODE = 'P0001', HINT = 'INSERT new audit rows; do not UPDATE or DELETE existing ones.';
|
|
20
|
+
END;
|
|
21
|
+
$$ LANGUAGE plpgsql;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- AI-ARCHITECTURE follow-up: user_profile_snapshots composite primary key.
|
|
2
|
+
--
|
|
3
|
+
-- 0018 used user_id alone as the PK, which hardcoded "one snapshot per
|
|
4
|
+
-- user globally". The moment a user belongs to more than one workspace
|
|
5
|
+
-- (family + classroom, personal + team, etc.) those workspaces would
|
|
6
|
+
-- be forced to share a single synthesized profile — collapsing
|
|
7
|
+
-- context that should stay separate.
|
|
8
|
+
--
|
|
9
|
+
-- Switch to a composite PK on (user_id, workspace_id). Existing rows
|
|
10
|
+
-- already have workspace_id populated, so the migration is a straight
|
|
11
|
+
-- PK drop + re-add.
|
|
12
|
+
|
|
13
|
+
ALTER TABLE "user_profile_snapshots" DROP CONSTRAINT "user_profile_snapshots_pkey";
|
|
14
|
+
--> statement-breakpoint
|
|
15
|
+
ALTER TABLE "user_profile_snapshots"
|
|
16
|
+
ADD CONSTRAINT "user_profile_snapshots_user_id_workspace_id_pk"
|
|
17
|
+
PRIMARY KEY ("user_id", "workspace_id");
|