@apart-tech/intelligence-core 1.15.2 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/index.d.ts +4 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/services/__tests__/search-service.test.js +3 -3
  6. package/dist/services/__tests__/search-service.test.js.map +1 -1
  7. package/dist/services/billing-event-service.d.ts +22 -0
  8. package/dist/services/billing-event-service.d.ts.map +1 -0
  9. package/dist/services/billing-event-service.js +30 -0
  10. package/dist/services/billing-event-service.js.map +1 -0
  11. package/dist/services/document-service.d.ts +3 -1
  12. package/dist/services/document-service.d.ts.map +1 -1
  13. package/dist/services/document-service.js +7 -1
  14. package/dist/services/document-service.js.map +1 -1
  15. package/dist/services/quota-service.d.ts +43 -0
  16. package/dist/services/quota-service.d.ts.map +1 -0
  17. package/dist/services/quota-service.js +148 -0
  18. package/dist/services/quota-service.js.map +1 -0
  19. package/dist/services/quota-service.test.d.ts +2 -0
  20. package/dist/services/quota-service.test.d.ts.map +1 -0
  21. package/dist/services/quota-service.test.js +165 -0
  22. package/dist/services/quota-service.test.js.map +1 -0
  23. package/dist/services/search-service.d.ts +7 -0
  24. package/dist/services/search-service.d.ts.map +1 -1
  25. package/dist/services/search-service.js +68 -23
  26. package/dist/services/search-service.js.map +1 -1
  27. package/dist/services/search-service.test.d.ts +2 -0
  28. package/dist/services/search-service.test.d.ts.map +1 -0
  29. package/dist/services/search-service.test.js +65 -0
  30. package/dist/services/search-service.test.js.map +1 -0
  31. package/dist/services/usage-service.d.ts +49 -2
  32. package/dist/services/usage-service.d.ts.map +1 -1
  33. package/dist/services/usage-service.js +109 -19
  34. package/dist/services/usage-service.js.map +1 -1
  35. package/dist/services/usage-service.test.d.ts +2 -0
  36. package/dist/services/usage-service.test.d.ts.map +1 -0
  37. package/dist/services/usage-service.test.js +191 -0
  38. package/dist/services/usage-service.test.js.map +1 -0
  39. package/package.json +1 -1
  40. package/prisma/schema.prisma +90 -5
@@ -3,11 +3,21 @@ const PLAN_LIMITS = {
3
3
  maxNodes: 500,
4
4
  maxApiCallsPerMonth: 1_000,
5
5
  maxMembers: 3,
6
+ maxOcrPagesPerMonth: 25,
7
+ maxStorageBytes: 1_073_741_824, // 1 GB
8
+ maxAgentRunsPerMonth: 0,
9
+ maxFileSizeBytes: 10_485_760, // 10 MB
10
+ maxPagesPerDocument: 20,
6
11
  },
7
12
  pro: {
8
13
  maxNodes: Infinity,
9
14
  maxApiCallsPerMonth: 50_000,
10
15
  maxMembers: Infinity,
16
+ maxOcrPagesPerMonth: 500, // per seat — multiplied at runtime
17
+ maxStorageBytes: 10_737_418_240, // 10 GB per seat — multiplied at runtime
18
+ maxAgentRunsPerMonth: 50, // per seat — multiplied at runtime
19
+ maxFileSizeBytes: 52_428_800, // 50 MB
20
+ maxPagesPerDocument: Infinity,
11
21
  },
12
22
  };
13
23
  /**
@@ -22,18 +32,27 @@ export class UsageService {
22
32
  constructor(db) {
23
33
  this.db = db;
24
34
  }
35
+ /**
36
+ * Get the base plan limits for a given plan name.
37
+ */
38
+ static getPlanLimits(plan) {
39
+ return PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
40
+ }
25
41
  /**
26
42
  * Get the full usage snapshot for an organization.
27
43
  */
28
44
  async getUsage(organizationId) {
29
- const [org, nodeCount, memberCount, apiCalls] = await Promise.all([
45
+ const [org, nodeCount, memberCount, apiCalls, ocrPages, storageBytes, agentRuns] = await Promise.all([
30
46
  this.db.organization.findUnique({
31
47
  where: { id: organizationId },
32
48
  select: { plan: true, stripeCustomerId: true, stripeSubscriptionId: true, currentPeriodEnd: true },
33
49
  }),
34
50
  this.db.node.count({ where: { organizationId } }),
35
51
  this.db.membership.count({ where: { organizationId } }),
36
- this.getApiCallCount(organizationId),
52
+ this.getMetricCount(organizationId, "api_calls"),
53
+ this.getMetricCount(organizationId, "ocr_pages"),
54
+ this.getStorageUsed(organizationId),
55
+ this.getMetricCount(organizationId, "agent_runs"),
37
56
  ]);
38
57
  const plan = org?.plan ?? "free";
39
58
  const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
@@ -44,6 +63,9 @@ export class UsageService {
44
63
  nodes: nodeCount,
45
64
  apiCalls,
46
65
  members: memberCount,
66
+ ocrPages,
67
+ storageBytes,
68
+ agentRuns,
47
69
  },
48
70
  stripeCustomerId: org?.stripeCustomerId ?? null,
49
71
  stripeSubscriptionId: org?.stripeSubscriptionId ?? null,
@@ -55,18 +77,98 @@ export class UsageService {
55
77
  * Uses upsert to create or increment atomically.
56
78
  */
57
79
  async incrementApiCalls(organizationId, count = 1) {
80
+ await this.incrementMetric(organizationId, "api_calls", count);
81
+ }
82
+ /**
83
+ * Increment the OCR pages counter for the current month.
84
+ */
85
+ async incrementOcrPages(organizationId, count = 1) {
86
+ await this.incrementMetric(organizationId, "ocr_pages", count);
87
+ }
88
+ /**
89
+ * Increment the agent runs counter for the current month.
90
+ */
91
+ async incrementAgentRuns(organizationId, count = 1) {
92
+ await this.incrementMetric(organizationId, "agent_runs", count);
93
+ }
94
+ /**
95
+ * Get OCR pages used in the current period.
96
+ */
97
+ async getOcrPagesUsed(organizationId) {
98
+ return this.getMetricCount(organizationId, "ocr_pages");
99
+ }
100
+ /**
101
+ * Get agent runs used in the current period.
102
+ */
103
+ async getAgentRunsUsed(organizationId) {
104
+ return this.getMetricCount(organizationId, "agent_runs");
105
+ }
106
+ /**
107
+ * Get total storage used by an org, computed as SUM(fileSizeBytes) from documents.
108
+ */
109
+ async getStorageUsed(organizationId) {
110
+ const result = await this.db.document.aggregate({
111
+ where: { organizationId },
112
+ _sum: { fileSizeBytes: true },
113
+ });
114
+ return result._sum.fileSizeBytes ?? 0;
115
+ }
116
+ /**
117
+ * Get usage history for a metric over the last N months.
118
+ */
119
+ async getUsageHistory(organizationId, metric, months = 6) {
120
+ const cutoff = new Date();
121
+ cutoff.setUTCMonth(cutoff.getUTCMonth() - months);
122
+ cutoff.setUTCDate(1);
123
+ cutoff.setUTCHours(0, 0, 0, 0);
124
+ const records = await this.db.usageRecord.findMany({
125
+ where: {
126
+ organizationId,
127
+ metric,
128
+ periodStart: { gte: cutoff },
129
+ },
130
+ orderBy: { periodStart: "asc" },
131
+ select: { periodStart: true, count: true },
132
+ });
133
+ return records;
134
+ }
135
+ /**
136
+ * Check whether an organization has exceeded a specific limit.
137
+ */
138
+ async isOverLimit(organizationId, metric) {
139
+ const snapshot = await this.getUsage(organizationId);
140
+ const limits = snapshot.limits;
141
+ switch (metric) {
142
+ case "nodes":
143
+ return snapshot.usage.nodes >= limits.maxNodes;
144
+ case "apiCalls":
145
+ return snapshot.usage.apiCalls >= limits.maxApiCallsPerMonth;
146
+ case "members":
147
+ return snapshot.usage.members >= limits.maxMembers;
148
+ case "ocrPages":
149
+ return snapshot.usage.ocrPages >= limits.maxOcrPagesPerMonth;
150
+ case "storageBytes":
151
+ return snapshot.usage.storageBytes >= limits.maxStorageBytes;
152
+ case "agentRuns":
153
+ return snapshot.usage.agentRuns >= limits.maxAgentRunsPerMonth;
154
+ }
155
+ }
156
+ /**
157
+ * Generic metric increment — upsert a usage_records row for the current period.
158
+ */
159
+ async incrementMetric(organizationId, metric, count) {
58
160
  const periodStart = this.currentPeriodStart();
59
161
  await this.db.usageRecord.upsert({
60
162
  where: {
61
163
  organizationId_metric_periodStart: {
62
164
  organizationId,
63
- metric: "api_calls",
165
+ metric,
64
166
  periodStart,
65
167
  },
66
168
  },
67
169
  create: {
68
170
  organizationId,
69
- metric: "api_calls",
171
+ metric,
70
172
  periodStart,
71
173
  count,
72
174
  },
@@ -77,27 +179,15 @@ export class UsageService {
77
179
  });
78
180
  }
79
181
  /**
80
- * Check whether an organization has exceeded a specific limit.
182
+ * Get the count for a metric in the current period.
81
183
  */
82
- async isOverLimit(organizationId, metric) {
83
- const snapshot = await this.getUsage(organizationId);
84
- const limits = snapshot.limits;
85
- switch (metric) {
86
- case "nodes":
87
- return snapshot.usage.nodes >= limits.maxNodes;
88
- case "apiCalls":
89
- return snapshot.usage.apiCalls >= limits.maxApiCallsPerMonth;
90
- case "members":
91
- return snapshot.usage.members >= limits.maxMembers;
92
- }
93
- }
94
- async getApiCallCount(organizationId) {
184
+ async getMetricCount(organizationId, metric) {
95
185
  const periodStart = this.currentPeriodStart();
96
186
  const record = await this.db.usageRecord.findUnique({
97
187
  where: {
98
188
  organizationId_metric_periodStart: {
99
189
  organizationId,
100
- metric: "api_calls",
190
+ metric,
101
191
  periodStart,
102
192
  },
103
193
  },
@@ -1 +1 @@
1
- {"version":3,"file":"usage-service.js","sourceRoot":"","sources":["../../src/services/usage-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usage-service.js","sourceRoot":"","sources":["../../src/services/usage-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=usage-service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-service.test.d.ts","sourceRoot":"","sources":["../../src/services/usage-service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,191 @@
1
+ import { describe, expect, it, vi, beforeEach } from "vitest";
2
+ import { UsageService } from "./usage-service.js";
3
+ const ORG_ID = "org-1";
4
+ function makeMockDb() {
5
+ const usageStore = {};
6
+ const nodeCount = 42;
7
+ const memberCount = 2;
8
+ const orgData = {
9
+ plan: "free",
10
+ stripeCustomerId: null,
11
+ stripeSubscriptionId: null,
12
+ currentPeriodEnd: null,
13
+ };
14
+ return {
15
+ organization: {
16
+ findUnique: vi.fn(async () => orgData),
17
+ },
18
+ node: {
19
+ count: vi.fn(async () => nodeCount),
20
+ },
21
+ membership: {
22
+ count: vi.fn(async () => memberCount),
23
+ },
24
+ document: {
25
+ aggregate: vi.fn(async () => ({
26
+ _sum: { fileSizeBytes: 512_000 },
27
+ })),
28
+ },
29
+ usageRecord: {
30
+ findUnique: vi.fn(async ({ where }) => {
31
+ const key = where.organizationId_metric_periodStart.metric;
32
+ return usageStore[key] ?? null;
33
+ }),
34
+ findMany: vi.fn(async () => []),
35
+ upsert: vi.fn(async ({ where, create, update }) => {
36
+ const key = where.organizationId_metric_periodStart.metric;
37
+ if (usageStore[key]) {
38
+ usageStore[key].count += update.count.increment;
39
+ }
40
+ else {
41
+ usageStore[key] = { count: create.count };
42
+ }
43
+ return usageStore[key];
44
+ }),
45
+ },
46
+ _usageStore: usageStore,
47
+ _orgData: orgData,
48
+ };
49
+ }
50
+ describe("UsageService", () => {
51
+ let db;
52
+ let svc;
53
+ beforeEach(() => {
54
+ db = makeMockDb();
55
+ svc = new UsageService(db);
56
+ });
57
+ describe("getUsage", () => {
58
+ it("returns full snapshot with all metrics", async () => {
59
+ const snapshot = await svc.getUsage(ORG_ID);
60
+ expect(snapshot.plan).toBe("free");
61
+ expect(snapshot.usage.nodes).toBe(42);
62
+ expect(snapshot.usage.members).toBe(2);
63
+ expect(snapshot.usage.apiCalls).toBe(0);
64
+ expect(snapshot.usage.ocrPages).toBe(0);
65
+ expect(snapshot.usage.storageBytes).toBe(512_000);
66
+ expect(snapshot.usage.agentRuns).toBe(0);
67
+ expect(snapshot.limits.maxNodes).toBe(500);
68
+ expect(snapshot.limits.maxOcrPagesPerMonth).toBe(25);
69
+ expect(snapshot.limits.maxStorageBytes).toBe(1_073_741_824);
70
+ expect(snapshot.limits.maxAgentRunsPerMonth).toBe(0);
71
+ });
72
+ it("returns pro limits for pro plan", async () => {
73
+ db._orgData.plan = "pro";
74
+ const snapshot = await svc.getUsage(ORG_ID);
75
+ expect(snapshot.plan).toBe("pro");
76
+ expect(snapshot.limits.maxNodes).toBe(Infinity);
77
+ expect(snapshot.limits.maxOcrPagesPerMonth).toBe(500);
78
+ expect(snapshot.limits.maxAgentRunsPerMonth).toBe(50);
79
+ });
80
+ it("falls back to free limits for unknown plan", async () => {
81
+ db._orgData.plan = "enterprise";
82
+ const snapshot = await svc.getUsage(ORG_ID);
83
+ expect(snapshot.limits.maxNodes).toBe(500);
84
+ });
85
+ });
86
+ describe("incrementApiCalls", () => {
87
+ it("creates a new record on first call", async () => {
88
+ await svc.incrementApiCalls(ORG_ID, 5);
89
+ expect(db.usageRecord.upsert).toHaveBeenCalledTimes(1);
90
+ expect(db._usageStore["api_calls"]?.count).toBe(5);
91
+ });
92
+ it("increments existing record", async () => {
93
+ await svc.incrementApiCalls(ORG_ID, 3);
94
+ await svc.incrementApiCalls(ORG_ID, 7);
95
+ expect(db._usageStore["api_calls"]?.count).toBe(10);
96
+ });
97
+ });
98
+ describe("incrementOcrPages", () => {
99
+ it("tracks OCR page usage", async () => {
100
+ await svc.incrementOcrPages(ORG_ID, 10);
101
+ expect(db._usageStore["ocr_pages"]?.count).toBe(10);
102
+ await svc.incrementOcrPages(ORG_ID, 5);
103
+ expect(db._usageStore["ocr_pages"]?.count).toBe(15);
104
+ });
105
+ });
106
+ describe("incrementAgentRuns", () => {
107
+ it("tracks agent run usage", async () => {
108
+ await svc.incrementAgentRuns(ORG_ID);
109
+ expect(db._usageStore["agent_runs"]?.count).toBe(1);
110
+ await svc.incrementAgentRuns(ORG_ID);
111
+ expect(db._usageStore["agent_runs"]?.count).toBe(2);
112
+ });
113
+ });
114
+ describe("getStorageUsed", () => {
115
+ it("returns sum of fileSizeBytes from documents", async () => {
116
+ const result = await svc.getStorageUsed(ORG_ID);
117
+ expect(result).toBe(512_000);
118
+ expect(db.document.aggregate).toHaveBeenCalledWith({
119
+ where: { organizationId: ORG_ID },
120
+ _sum: { fileSizeBytes: true },
121
+ });
122
+ });
123
+ it("returns 0 when no documents", async () => {
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
+ db.document.aggregate.mockResolvedValueOnce({ _sum: { fileSizeBytes: null } });
126
+ const result = await svc.getStorageUsed(ORG_ID);
127
+ expect(result).toBe(0);
128
+ });
129
+ });
130
+ describe("isOverLimit", () => {
131
+ it("returns true when nodes at limit", async () => {
132
+ // Free limit is 500 nodes; mock returns 42
133
+ const result = await svc.isOverLimit(ORG_ID, "nodes");
134
+ expect(result).toBe(false);
135
+ });
136
+ it("detects OCR over limit", async () => {
137
+ // Free limit is 25 OCR pages
138
+ db._usageStore["ocr_pages"] = { count: 30 };
139
+ const result = await svc.isOverLimit(ORG_ID, "ocrPages");
140
+ expect(result).toBe(true);
141
+ });
142
+ it("detects storage over limit", async () => {
143
+ // Free limit is 1 GB
144
+ db.document.aggregate.mockResolvedValueOnce({
145
+ _sum: { fileSizeBytes: 2_000_000_000 },
146
+ });
147
+ const result = await svc.isOverLimit(ORG_ID, "storageBytes");
148
+ expect(result).toBe(true);
149
+ });
150
+ it("detects agent runs at zero limit for free tier", async () => {
151
+ // Free tier has 0 agent runs
152
+ db._usageStore["agent_runs"] = { count: 1 };
153
+ const result = await svc.isOverLimit(ORG_ID, "agentRuns");
154
+ expect(result).toBe(true);
155
+ });
156
+ it("free tier blocks agent runs even at 0", async () => {
157
+ const result = await svc.isOverLimit(ORG_ID, "agentRuns");
158
+ // 0 >= 0 is true
159
+ expect(result).toBe(true);
160
+ });
161
+ });
162
+ describe("getUsageHistory", () => {
163
+ it("queries usage records for the metric", async () => {
164
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
165
+ db.usageRecord.findMany.mockResolvedValueOnce([
166
+ { periodStart: new Date("2026-01-01"), count: 100 },
167
+ { periodStart: new Date("2026-02-01"), count: 150 },
168
+ ]);
169
+ const history = await svc.getUsageHistory(ORG_ID, "api_calls", 3);
170
+ expect(history).toHaveLength(2);
171
+ expect(db.usageRecord.findMany).toHaveBeenCalledTimes(1);
172
+ });
173
+ });
174
+ describe("getPlanLimits (static)", () => {
175
+ it("returns correct free limits", () => {
176
+ const limits = UsageService.getPlanLimits("free");
177
+ expect(limits.maxNodes).toBe(500);
178
+ expect(limits.maxOcrPagesPerMonth).toBe(25);
179
+ });
180
+ it("returns correct pro limits", () => {
181
+ const limits = UsageService.getPlanLimits("pro");
182
+ expect(limits.maxNodes).toBe(Infinity);
183
+ expect(limits.maxOcrPagesPerMonth).toBe(500);
184
+ });
185
+ it("falls back to free for unknown plan", () => {
186
+ const limits = UsageService.getPlanLimits("unknown");
187
+ expect(limits.maxNodes).toBe(500);
188
+ });
189
+ });
190
+ });
191
+ //# sourceMappingURL=usage-service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-service.test.js","sourceRoot":"","sources":["../../src/services/usage-service.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apart-tech/intelligence-core",
3
- "version": "1.15.2",
3
+ "version": "1.17.0",
4
4
  "description": "Core library: database, services, and providers for Apart Intelligence",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -35,11 +35,15 @@ model Organization {
35
35
  orgAgentTypes OrgAgentType[]
36
36
  orgMcpServers OrgMcpServer[]
37
37
  usageRecords UsageRecord[]
38
- tagDefinitions TagDefinition[]
39
- nodeTags NodeTag[]
40
- nodeChunks NodeChunk[]
41
- piiQueryLogs PiiQueryLog[]
42
- documents Document[]
38
+ quotaOverrides QuotaOverride[]
39
+ onboardingPackages OnboardingPackage[]
40
+ billingEvents BillingEvent[]
41
+ searchMetrics SearchMetric[]
42
+ tagDefinitions TagDefinition[]
43
+ nodeTags NodeTag[]
44
+ nodeChunks NodeChunk[]
45
+ piiQueryLogs PiiQueryLog[]
46
+ documents Document[]
43
47
 
44
48
  @@map("organizations")
45
49
  }
@@ -55,6 +59,7 @@ model User {
55
59
  claudeOauthTokenEncrypted String? @map("claude_oauth_token_encrypted") @db.Text
56
60
  apartRefreshTokenEncrypted String? @map("apart_refresh_token_encrypted") @db.Text
57
61
  agentSecretsEncrypted String? @map("agent_secrets_encrypted") @db.Text
62
+ isSuperAdmin Boolean @default(false) @map("is_super_admin")
58
63
 
59
64
  memberships Membership[]
60
65
  createdAgentTypes OrgAgentType[] @relation("OrgAgentTypeCreatedBy")
@@ -616,6 +621,24 @@ model NodeTagAudit {
616
621
  @@map("node_tag_audit")
617
622
  }
618
623
 
624
+ // ── Quota Overrides ─────────────────────────────────────────────────────────
625
+
626
+ model QuotaOverride {
627
+ id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
628
+ organizationId String @map("organization_id") @db.Uuid
629
+ resource String @db.VarChar(50)
630
+ overrideLimit Int @map("override_limit")
631
+ reason String @default("") @db.Text
632
+ grantedBy String @map("granted_by") @db.VarChar(255)
633
+ expiresAt DateTime? @map("expires_at") @db.Timestamptz
634
+ createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
635
+
636
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
637
+
638
+ @@index([organizationId], map: "idx_quota_overrides_org")
639
+ @@map("quota_overrides")
640
+ }
641
+
619
642
  // ── Document Archive ────────────────────────────────────────────────────────
620
643
 
621
644
  model Document {
@@ -643,3 +666,65 @@ model Document {
643
666
  @@index([organizationId, status], map: "idx_documents_org_status")
644
667
  @@map("documents")
645
668
  }
669
+
670
+ // ── Onboarding Packages ──────────────────────────────────────────────────────
671
+
672
+ model OnboardingPackage {
673
+ id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
674
+ organizationId String @map("organization_id") @db.Uuid
675
+ status String @default("pending") @db.VarChar(20)
676
+ ocrPagesAllotment Int @default(10000) @map("ocr_pages_allotment")
677
+ ocrPagesUsed Int @default(0) @map("ocr_pages_used")
678
+ storageBytesAllotment BigInt @default(26843545600) @map("storage_bytes_allotment")
679
+ proSeats Int @default(3) @map("pro_seats")
680
+ proExpiresAt DateTime? @map("pro_expires_at") @db.Timestamptz
681
+ stripePaymentIntentId String? @map("stripe_payment_intent_id") @db.VarChar(255)
682
+ purchasedAt DateTime? @map("purchased_at") @db.Timestamptz
683
+ completedAt DateTime? @map("completed_at") @db.Timestamptz
684
+ createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
685
+
686
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
687
+
688
+ @@index([organizationId], map: "idx_onboarding_packages_org")
689
+ @@map("onboarding_packages")
690
+ }
691
+
692
+ // ── Billing Events (Audit Trail) ────────────────────────────────────────────
693
+
694
+ model BillingEvent {
695
+ id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
696
+ organizationId String @map("organization_id") @db.Uuid
697
+ eventType String @map("event_type") @db.VarChar(50)
698
+ detail Json @default("{}") @db.JsonB
699
+ actor String @default("system") @db.VarChar(255)
700
+ createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
701
+
702
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
703
+
704
+ @@index([organizationId], map: "idx_billing_events_org")
705
+ @@index([eventType], map: "idx_billing_events_type")
706
+ @@map("billing_events")
707
+ }
708
+
709
+ // ── Search Performance Metrics ──────────────────────────────────────────────
710
+
711
+ model SearchMetric {
712
+ id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
713
+ organizationId String @map("organization_id") @db.Uuid
714
+ queryHash String @map("query_hash") @db.VarChar(64)
715
+ chunkSemanticMs Float @map("chunk_semantic_ms")
716
+ nodeSemanticMs Float @map("node_semantic_ms")
717
+ keywordMs Float @map("keyword_ms")
718
+ substringMs Float @map("substring_ms")
719
+ trigramMs Float @map("trigram_ms")
720
+ totalMs Float @map("total_ms")
721
+ resultCount Int @map("result_count")
722
+ createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
723
+
724
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
725
+
726
+ @@index([organizationId], map: "idx_search_metrics_org")
727
+ @@index([createdAt], map: "idx_search_metrics_created_at")
728
+ @@index([organizationId, createdAt], map: "idx_search_metrics_org_time")
729
+ @@map("search_metrics")
730
+ }