@vellumai/assistant 0.4.30 → 0.4.32

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 (194) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +14 -8
  3. package/README.md +2 -2
  4. package/docs/architecture/memory.md +28 -29
  5. package/docs/runbook-trusted-contacts.md +1 -4
  6. package/package.json +1 -1
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -7
  8. package/src/__tests__/anthropic-provider.test.ts +86 -1
  9. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  10. package/src/__tests__/checker.test.ts +37 -98
  11. package/src/__tests__/commit-message-enrichment-service.test.ts +15 -4
  12. package/src/__tests__/config-schema.test.ts +6 -14
  13. package/src/__tests__/conflict-policy.test.ts +76 -0
  14. package/src/__tests__/conflict-store.test.ts +14 -20
  15. package/src/__tests__/contacts-tools.test.ts +8 -61
  16. package/src/__tests__/contradiction-checker.test.ts +5 -1
  17. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  18. package/src/__tests__/daemon-server-session-init.test.ts +1 -19
  19. package/src/__tests__/followup-tools.test.ts +0 -30
  20. package/src/__tests__/gemini-provider.test.ts +79 -1
  21. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  22. package/src/__tests__/guardian-routing-invariants.test.ts +6 -4
  23. package/src/__tests__/ipc-snapshot.test.ts +0 -4
  24. package/src/__tests__/managed-proxy-context.test.ts +163 -0
  25. package/src/__tests__/memory-lifecycle-e2e.test.ts +13 -12
  26. package/src/__tests__/memory-regressions.test.ts +6 -6
  27. package/src/__tests__/openai-provider.test.ts +82 -0
  28. package/src/__tests__/provider-fail-open-selection.test.ts +134 -1
  29. package/src/__tests__/provider-managed-proxy-integration.test.ts +269 -0
  30. package/src/__tests__/recurrence-types.test.ts +0 -15
  31. package/src/__tests__/registry.test.ts +0 -10
  32. package/src/__tests__/schedule-tools.test.ts +28 -44
  33. package/src/__tests__/script-proxy-session-runtime.test.ts +6 -1
  34. package/src/__tests__/session-agent-loop.test.ts +0 -2
  35. package/src/__tests__/session-conflict-gate.test.ts +243 -388
  36. package/src/__tests__/session-profile-injection.test.ts +0 -2
  37. package/src/__tests__/session-runtime-assembly.test.ts +2 -3
  38. package/src/__tests__/session-skill-tools.test.ts +0 -49
  39. package/src/__tests__/session-workspace-cache-state.test.ts +0 -1
  40. package/src/__tests__/session-workspace-injection.test.ts +0 -1
  41. package/src/__tests__/session-workspace-tool-tracking.test.ts +0 -1
  42. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  43. package/src/__tests__/task-management-tools.test.ts +111 -0
  44. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -1
  45. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -1
  46. package/src/__tests__/twilio-config.test.ts +0 -3
  47. package/src/amazon/session.ts +30 -91
  48. package/src/approvals/guardian-decision-primitive.ts +11 -7
  49. package/src/approvals/guardian-request-resolvers.ts +5 -3
  50. package/src/calls/call-controller.ts +423 -571
  51. package/src/calls/finalize-call.ts +20 -0
  52. package/src/calls/relay-access-wait.ts +340 -0
  53. package/src/calls/relay-server.ts +269 -899
  54. package/src/calls/relay-setup-router.ts +307 -0
  55. package/src/calls/relay-verification.ts +280 -0
  56. package/src/calls/twilio-config.ts +1 -8
  57. package/src/calls/voice-control-protocol.ts +184 -0
  58. package/src/calls/voice-session-bridge.ts +1 -8
  59. package/src/config/agent-schema.ts +1 -1
  60. package/src/config/bundled-skills/contacts/SKILL.md +7 -18
  61. package/src/config/bundled-skills/contacts/TOOLS.json +4 -20
  62. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +2 -4
  63. package/src/config/bundled-skills/contacts/tools/contact-search.ts +6 -12
  64. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +3 -24
  65. package/src/config/bundled-skills/followups/TOOLS.json +0 -4
  66. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  67. package/src/config/bundled-skills/schedule/TOOLS.json +2 -10
  68. package/src/config/bundled-tool-registry.ts +0 -5
  69. package/src/config/core-schema.ts +1 -1
  70. package/src/config/env.ts +0 -10
  71. package/src/config/feature-flag-registry.json +1 -1
  72. package/src/config/loader.ts +19 -0
  73. package/src/config/memory-schema.ts +0 -10
  74. package/src/config/schema.ts +2 -2
  75. package/src/config/system-prompt.ts +6 -0
  76. package/src/contacts/contact-store.ts +36 -62
  77. package/src/contacts/contacts-write.ts +14 -3
  78. package/src/contacts/types.ts +9 -4
  79. package/src/daemon/handlers/config-heartbeat.ts +1 -2
  80. package/src/daemon/handlers/contacts.ts +2 -2
  81. package/src/daemon/handlers/guardian-actions.ts +1 -1
  82. package/src/daemon/handlers/session-history.ts +398 -0
  83. package/src/daemon/handlers/session-user-message.ts +982 -0
  84. package/src/daemon/handlers/sessions.ts +9 -1337
  85. package/src/daemon/ipc-contract/contacts.ts +2 -2
  86. package/src/daemon/ipc-contract/sessions.ts +0 -6
  87. package/src/daemon/ipc-contract-inventory.json +0 -1
  88. package/src/daemon/lifecycle.ts +0 -29
  89. package/src/daemon/session-agent-loop.ts +1 -45
  90. package/src/daemon/session-conflict-gate.ts +21 -82
  91. package/src/daemon/session-memory.ts +7 -52
  92. package/src/daemon/session-process.ts +3 -1
  93. package/src/daemon/session-runtime-assembly.ts +18 -35
  94. package/src/heartbeat/heartbeat-service.ts +5 -1
  95. package/src/home-base/app-link-store.ts +0 -7
  96. package/src/memory/conflict-intent.ts +3 -6
  97. package/src/memory/conflict-policy.ts +34 -0
  98. package/src/memory/conflict-store.ts +10 -18
  99. package/src/memory/contradiction-checker.ts +2 -2
  100. package/src/memory/conversation-attention-store.ts +1 -1
  101. package/src/memory/conversation-store.ts +0 -51
  102. package/src/memory/db-init.ts +8 -0
  103. package/src/memory/job-handlers/conflict.ts +24 -7
  104. package/src/memory/migrations/105-contacts-and-triage.ts +4 -7
  105. package/src/memory/migrations/134-contacts-notes-column.ts +68 -0
  106. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +31 -0
  107. package/src/memory/migrations/index.ts +2 -0
  108. package/src/memory/migrations/registry.ts +6 -0
  109. package/src/memory/recall-cache.ts +0 -5
  110. package/src/memory/schema/calls.ts +274 -0
  111. package/src/memory/schema/contacts.ts +125 -0
  112. package/src/memory/schema/conversations.ts +129 -0
  113. package/src/memory/schema/guardian.ts +172 -0
  114. package/src/memory/schema/index.ts +8 -0
  115. package/src/memory/schema/infrastructure.ts +205 -0
  116. package/src/memory/schema/memory-core.ts +196 -0
  117. package/src/memory/schema/notifications.ts +191 -0
  118. package/src/memory/schema/tasks.ts +78 -0
  119. package/src/memory/schema.ts +1 -1402
  120. package/src/memory/slack-thread-store.ts +0 -69
  121. package/src/messaging/index.ts +0 -1
  122. package/src/messaging/types.ts +0 -38
  123. package/src/notifications/decisions-store.ts +2 -105
  124. package/src/notifications/deliveries-store.ts +0 -11
  125. package/src/notifications/preferences-store.ts +1 -58
  126. package/src/permissions/checker.ts +6 -17
  127. package/src/providers/anthropic/client.ts +6 -2
  128. package/src/providers/gemini/client.ts +13 -2
  129. package/src/providers/managed-proxy/constants.ts +55 -0
  130. package/src/providers/managed-proxy/context.ts +77 -0
  131. package/src/providers/registry.ts +112 -0
  132. package/src/runtime/auth/__tests__/guard-tests.test.ts +51 -23
  133. package/src/runtime/guardian-action-service.ts +3 -2
  134. package/src/runtime/guardian-outbound-actions.ts +3 -3
  135. package/src/runtime/guardian-reply-router.ts +4 -4
  136. package/src/runtime/http-server.ts +83 -710
  137. package/src/runtime/http-types.ts +0 -16
  138. package/src/runtime/middleware/auth.ts +0 -12
  139. package/src/runtime/routes/app-routes.ts +33 -0
  140. package/src/runtime/routes/approval-routes.ts +32 -0
  141. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +6 -3
  142. package/src/runtime/routes/attachment-routes.ts +32 -0
  143. package/src/runtime/routes/brain-graph-routes.ts +27 -0
  144. package/src/runtime/routes/call-routes.ts +41 -0
  145. package/src/runtime/routes/channel-readiness-routes.ts +20 -0
  146. package/src/runtime/routes/channel-routes.ts +70 -0
  147. package/src/runtime/routes/contact-routes.ts +371 -29
  148. package/src/runtime/routes/conversation-attention-routes.ts +15 -0
  149. package/src/runtime/routes/conversation-routes.ts +192 -194
  150. package/src/runtime/routes/debug-routes.ts +15 -0
  151. package/src/runtime/routes/events-routes.ts +16 -0
  152. package/src/runtime/routes/global-search-routes.ts +17 -2
  153. package/src/runtime/routes/guardian-action-routes.ts +23 -1
  154. package/src/runtime/routes/guardian-approval-interception.ts +2 -1
  155. package/src/runtime/routes/guardian-bootstrap-routes.ts +26 -1
  156. package/src/runtime/routes/guardian-refresh-routes.ts +20 -0
  157. package/src/runtime/routes/identity-routes.ts +20 -0
  158. package/src/runtime/routes/inbound-message-handler.ts +8 -0
  159. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +5 -1
  160. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +2 -1
  161. package/src/runtime/routes/integration-routes.ts +83 -0
  162. package/src/runtime/routes/invite-routes.ts +31 -0
  163. package/src/runtime/routes/migration-routes.ts +47 -17
  164. package/src/runtime/routes/pairing-routes.ts +18 -0
  165. package/src/runtime/routes/secret-routes.ts +20 -0
  166. package/src/runtime/routes/surface-action-routes.ts +26 -0
  167. package/src/runtime/routes/trust-rules-routes.ts +31 -0
  168. package/src/runtime/routes/twilio-routes.ts +79 -0
  169. package/src/schedule/recurrence-types.ts +1 -11
  170. package/src/tools/followups/followup_create.ts +9 -3
  171. package/src/tools/mcp/mcp-tool-factory.ts +0 -17
  172. package/src/tools/memory/definitions.ts +0 -6
  173. package/src/tools/network/script-proxy/session-manager.ts +38 -3
  174. package/src/tools/schedule/create.ts +1 -3
  175. package/src/tools/schedule/update.ts +9 -6
  176. package/src/twitter/session.ts +29 -77
  177. package/src/util/cookie-session.ts +114 -0
  178. package/src/workspace/git-service.ts +6 -4
  179. package/src/__tests__/conversation-routes.test.ts +0 -99
  180. package/src/__tests__/get-weather.test.ts +0 -393
  181. package/src/__tests__/task-tools.test.ts +0 -685
  182. package/src/__tests__/weather-skill-regression.test.ts +0 -276
  183. package/src/autonomy/autonomy-resolver.ts +0 -62
  184. package/src/autonomy/autonomy-store.ts +0 -138
  185. package/src/autonomy/disposition-mapper.ts +0 -31
  186. package/src/autonomy/index.ts +0 -11
  187. package/src/autonomy/types.ts +0 -43
  188. package/src/config/bundled-skills/weather/SKILL.md +0 -38
  189. package/src/config/bundled-skills/weather/TOOLS.json +0 -36
  190. package/src/config/bundled-skills/weather/icon.svg +0 -24
  191. package/src/config/bundled-skills/weather/tools/get-weather.ts +0 -12
  192. package/src/contacts/startup-migration.ts +0 -21
  193. package/src/messaging/triage-engine.ts +0 -344
  194. package/src/tools/weather/service.ts +0 -712
@@ -1,1402 +1 @@
1
- import {
2
- blob,
3
- index,
4
- integer,
5
- real,
6
- sqliteTable,
7
- text,
8
- uniqueIndex,
9
- } from "drizzle-orm/sqlite-core";
10
-
11
- import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
12
-
13
- export const conversations = sqliteTable(
14
- "conversations",
15
- {
16
- id: text("id").primaryKey(),
17
- title: text("title"),
18
- createdAt: integer("created_at").notNull(),
19
- updatedAt: integer("updated_at").notNull(),
20
- totalInputTokens: integer("total_input_tokens").notNull().default(0),
21
- totalOutputTokens: integer("total_output_tokens").notNull().default(0),
22
- totalEstimatedCost: real("total_estimated_cost").notNull().default(0),
23
- contextSummary: text("context_summary"),
24
- contextCompactedMessageCount: integer("context_compacted_message_count")
25
- .notNull()
26
- .default(0),
27
- contextCompactedAt: integer("context_compacted_at"),
28
- threadType: text("thread_type").notNull().default("standard"),
29
- source: text("source").notNull().default("user"),
30
- memoryScopeId: text("memory_scope_id").notNull().default("default"),
31
- originChannel: text("origin_channel"),
32
- originInterface: text("origin_interface"),
33
- isAutoTitle: integer("is_auto_title").notNull().default(1),
34
- scheduleJobId: text("schedule_job_id"),
35
- },
36
- (table) => [
37
- index("idx_conversations_updated_at").on(table.updatedAt),
38
- index("idx_conversations_thread_type").on(table.threadType),
39
- ],
40
- );
41
-
42
- export const messages = sqliteTable(
43
- "messages",
44
- {
45
- id: text("id").primaryKey(),
46
- conversationId: text("conversation_id")
47
- .notNull()
48
- .references(() => conversations.id, { onDelete: "cascade" }),
49
- role: text("role").notNull(),
50
- content: text("content").notNull(),
51
- createdAt: integer("created_at").notNull(),
52
- metadata: text("metadata"),
53
- },
54
- (table) => [index("idx_messages_conversation_id").on(table.conversationId)],
55
- );
56
-
57
- export const toolInvocations = sqliteTable(
58
- "tool_invocations",
59
- {
60
- id: text("id").primaryKey(),
61
- conversationId: text("conversation_id")
62
- .notNull()
63
- .references(() => conversations.id),
64
- toolName: text("tool_name").notNull(),
65
- input: text("input").notNull(),
66
- result: text("result").notNull(),
67
- decision: text("decision").notNull(),
68
- riskLevel: text("risk_level").notNull(),
69
- durationMs: integer("duration_ms").notNull(),
70
- createdAt: integer("created_at").notNull(),
71
- },
72
- (table) => [
73
- index("idx_tool_invocations_conversation_id").on(table.conversationId),
74
- ],
75
- );
76
-
77
- export const memorySegments = sqliteTable(
78
- "memory_segments",
79
- {
80
- id: text("id").primaryKey(),
81
- messageId: text("message_id")
82
- .notNull()
83
- .references(() => messages.id, { onDelete: "cascade" }),
84
- conversationId: text("conversation_id")
85
- .notNull()
86
- .references(() => conversations.id, { onDelete: "cascade" }),
87
- role: text("role").notNull(),
88
- segmentIndex: integer("segment_index").notNull(),
89
- text: text("text").notNull(),
90
- tokenEstimate: integer("token_estimate").notNull(),
91
- scopeId: text("scope_id").notNull().default("default"),
92
- contentHash: text("content_hash"),
93
- createdAt: integer("created_at").notNull(),
94
- updatedAt: integer("updated_at").notNull(),
95
- },
96
- (table) => [index("idx_memory_segments_scope_id").on(table.scopeId)],
97
- );
98
-
99
- export const memoryItems = sqliteTable(
100
- "memory_items",
101
- {
102
- id: text("id").primaryKey(),
103
- kind: text("kind").notNull(),
104
- subject: text("subject").notNull(),
105
- statement: text("statement").notNull(),
106
- status: text("status").notNull(),
107
- confidence: real("confidence").notNull(),
108
- importance: real("importance"),
109
- accessCount: integer("access_count").notNull().default(0),
110
- fingerprint: text("fingerprint").notNull(),
111
- verificationState: text("verification_state")
112
- .notNull()
113
- .default("assistant_inferred"),
114
- scopeId: text("scope_id").notNull().default("default"),
115
- firstSeenAt: integer("first_seen_at").notNull(),
116
- lastSeenAt: integer("last_seen_at").notNull(),
117
- lastUsedAt: integer("last_used_at"),
118
- validFrom: integer("valid_from"),
119
- invalidAt: integer("invalid_at"),
120
- },
121
- (table) => [
122
- index("idx_memory_items_scope_id").on(table.scopeId),
123
- index("idx_memory_items_fingerprint").on(table.fingerprint),
124
- ],
125
- );
126
-
127
- export const memoryItemSources = sqliteTable(
128
- "memory_item_sources",
129
- {
130
- memoryItemId: text("memory_item_id")
131
- .notNull()
132
- .references(() => memoryItems.id, { onDelete: "cascade" }),
133
- messageId: text("message_id")
134
- .notNull()
135
- .references(() => messages.id, { onDelete: "cascade" }),
136
- evidence: text("evidence"),
137
- createdAt: integer("created_at").notNull(),
138
- },
139
- (table) => [
140
- index("idx_memory_item_sources_memory_item_id").on(table.memoryItemId),
141
- ],
142
- );
143
-
144
- export const memoryItemConflicts = sqliteTable(
145
- "memory_item_conflicts",
146
- {
147
- id: text("id").primaryKey(),
148
- scopeId: text("scope_id").notNull().default("default"),
149
- existingItemId: text("existing_item_id")
150
- .notNull()
151
- .references(() => memoryItems.id, { onDelete: "cascade" }),
152
- candidateItemId: text("candidate_item_id")
153
- .notNull()
154
- .references(() => memoryItems.id, { onDelete: "cascade" }),
155
- relationship: text("relationship").notNull(),
156
- status: text("status").notNull(),
157
- clarificationQuestion: text("clarification_question"),
158
- resolutionNote: text("resolution_note"),
159
- lastAskedAt: integer("last_asked_at"),
160
- resolvedAt: integer("resolved_at"),
161
- createdAt: integer("created_at").notNull(),
162
- updatedAt: integer("updated_at").notNull(),
163
- },
164
- (table) => [index("idx_memory_item_conflicts_scope_id").on(table.scopeId)],
165
- );
166
-
167
- export const memorySummaries = sqliteTable(
168
- "memory_summaries",
169
- {
170
- id: text("id").primaryKey(),
171
- scope: text("scope").notNull(),
172
- scopeKey: text("scope_key").notNull(),
173
- summary: text("summary").notNull(),
174
- tokenEstimate: integer("token_estimate").notNull(),
175
- version: integer("version").notNull().default(1),
176
- scopeId: text("scope_id").notNull().default("default"),
177
- startAt: integer("start_at").notNull(),
178
- endAt: integer("end_at").notNull(),
179
- createdAt: integer("created_at").notNull(),
180
- updatedAt: integer("updated_at").notNull(),
181
- },
182
- (table) => [
183
- index("idx_memory_summaries_scope_id").on(table.scopeId),
184
- uniqueIndex("idx_memory_summaries_scope_scope_key").on(
185
- table.scope,
186
- table.scopeKey,
187
- ),
188
- ],
189
- );
190
-
191
- export const memoryEmbeddings = sqliteTable(
192
- "memory_embeddings",
193
- {
194
- id: text("id").primaryKey(),
195
- targetType: text("target_type").notNull(),
196
- targetId: text("target_id").notNull(),
197
- provider: text("provider").notNull(),
198
- model: text("model").notNull(),
199
- dimensions: integer("dimensions").notNull(),
200
- vectorJson: text("vector_json"),
201
- vectorBlob: blob("vector_blob"),
202
- contentHash: text("content_hash"),
203
- createdAt: integer("created_at").notNull(),
204
- updatedAt: integer("updated_at").notNull(),
205
- },
206
- (table) => [
207
- uniqueIndex("idx_memory_embeddings_target_provider_model").on(
208
- table.targetType,
209
- table.targetId,
210
- table.provider,
211
- table.model,
212
- ),
213
- ],
214
- );
215
-
216
- export const memoryJobs = sqliteTable("memory_jobs", {
217
- id: text("id").primaryKey(),
218
- type: text("type").notNull(),
219
- payload: text("payload").notNull(),
220
- status: text("status").notNull(),
221
- attempts: integer("attempts").notNull().default(0),
222
- deferrals: integer("deferrals").notNull().default(0),
223
- runAfter: integer("run_after").notNull(),
224
- lastError: text("last_error"),
225
- startedAt: integer("started_at"),
226
- createdAt: integer("created_at").notNull(),
227
- updatedAt: integer("updated_at").notNull(),
228
- });
229
-
230
- export const conversationKeys = sqliteTable("conversation_keys", {
231
- id: text("id").primaryKey(),
232
- conversationKey: text("conversation_key").notNull(),
233
- conversationId: text("conversation_id")
234
- .notNull()
235
- .references(() => conversations.id, { onDelete: "cascade" }),
236
- createdAt: integer("created_at").notNull(),
237
- });
238
-
239
- export const attachments = sqliteTable("attachments", {
240
- id: text("id").primaryKey(),
241
- originalFilename: text("original_filename").notNull(),
242
- mimeType: text("mime_type").notNull(),
243
- sizeBytes: integer("size_bytes").notNull(),
244
- kind: text("kind").notNull(),
245
- dataBase64: text("data_base64").notNull(),
246
- contentHash: text("content_hash"),
247
- thumbnailBase64: text("thumbnail_base64"),
248
- createdAt: integer("created_at").notNull(),
249
- });
250
-
251
- export const messageAttachments = sqliteTable("message_attachments", {
252
- id: text("id").primaryKey(),
253
- messageId: text("message_id")
254
- .notNull()
255
- .references(() => messages.id, { onDelete: "cascade" }),
256
- attachmentId: text("attachment_id")
257
- .notNull()
258
- .references(() => attachments.id, { onDelete: "cascade" }),
259
- position: integer("position").notNull().default(0),
260
- createdAt: integer("created_at").notNull(),
261
- });
262
-
263
- export const channelInboundEvents = sqliteTable("channel_inbound_events", {
264
- id: text("id").primaryKey(),
265
- sourceChannel: text("source_channel").notNull(),
266
- externalChatId: text("external_chat_id").notNull(),
267
- externalMessageId: text("external_message_id").notNull(),
268
- sourceMessageId: text("source_message_id"),
269
- conversationId: text("conversation_id")
270
- .notNull()
271
- .references(() => conversations.id, { onDelete: "cascade" }),
272
- messageId: text("message_id").references(() => messages.id, {
273
- onDelete: "cascade",
274
- }),
275
- deliveryStatus: text("delivery_status").notNull().default("pending"),
276
- processingStatus: text("processing_status").notNull().default("pending"),
277
- processingAttempts: integer("processing_attempts").notNull().default(0),
278
- lastProcessingError: text("last_processing_error"),
279
- retryAfter: integer("retry_after"),
280
- rawPayload: text("raw_payload"),
281
- deliveredSegmentCount: integer("delivered_segment_count")
282
- .notNull()
283
- .default(0),
284
- createdAt: integer("created_at").notNull(),
285
- updatedAt: integer("updated_at").notNull(),
286
- });
287
-
288
- export const memoryCheckpoints = sqliteTable("memory_checkpoints", {
289
- key: text("key").primaryKey(),
290
- value: text("value").notNull(),
291
- updatedAt: integer("updated_at").notNull(),
292
- });
293
-
294
- // ── Reminders ────────────────────────────────────────────────────────
295
-
296
- export const reminders = sqliteTable("reminders", {
297
- id: text("id").primaryKey(),
298
- label: text("label").notNull(),
299
- message: text("message").notNull(),
300
- fireAt: integer("fire_at").notNull(), // epoch ms, absolute timestamp
301
- mode: text("mode").notNull(), // 'notify' | 'execute'
302
- status: text("status").notNull(), // 'pending' | 'firing' | 'fired' | 'cancelled'
303
- firedAt: integer("fired_at"),
304
- conversationId: text("conversation_id"),
305
- routingIntent: text("routing_intent").notNull().default("all_channels"), // 'single_channel' | 'multi_channel' | 'all_channels'
306
- routingHintsJson: text("routing_hints_json").notNull().default("{}"),
307
- createdAt: integer("created_at").notNull(),
308
- updatedAt: integer("updated_at").notNull(),
309
- });
310
-
311
- // ── Recurrence Schedules ─────────────────────────────────────────────
312
-
313
- export const cronJobs = sqliteTable("cron_jobs", {
314
- id: text("id").primaryKey(),
315
- name: text("name").notNull(),
316
- enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
317
- cronExpression: text("cron_expression").notNull(), // e.g. '0 9 * * 1-5'
318
- scheduleSyntax: text("schedule_syntax").notNull().default("cron"), // 'cron' | 'rrule'
319
- timezone: text("timezone"), // e.g. 'America/Los_Angeles'
320
- message: text("message").notNull(),
321
- nextRunAt: integer("next_run_at").notNull(),
322
- lastRunAt: integer("last_run_at"),
323
- lastStatus: text("last_status"), // 'ok' | 'error'
324
- retryCount: integer("retry_count").notNull().default(0),
325
- createdBy: text("created_by").notNull(), // 'agent' | 'user'
326
- createdAt: integer("created_at").notNull(),
327
- updatedAt: integer("updated_at").notNull(),
328
- });
329
-
330
- export const accounts = sqliteTable("accounts", {
331
- id: text("id").primaryKey(),
332
- service: text("service").notNull(),
333
- username: text("username"),
334
- email: text("email"),
335
- displayName: text("display_name"),
336
- status: text("status").notNull().default("active"),
337
- credentialRef: text("credential_ref"),
338
- metadataJson: text("metadata_json"),
339
- createdAt: integer("created_at").notNull(),
340
- updatedAt: integer("updated_at").notNull(),
341
- });
342
-
343
- export const cronRuns = sqliteTable("cron_runs", {
344
- id: text("id").primaryKey(),
345
- jobId: text("job_id")
346
- .notNull()
347
- .references(() => cronJobs.id, { onDelete: "cascade" }),
348
- status: text("status").notNull(), // 'ok' | 'error'
349
- startedAt: integer("started_at").notNull(),
350
- finishedAt: integer("finished_at"),
351
- durationMs: integer("duration_ms"),
352
- output: text("output"),
353
- error: text("error"),
354
- conversationId: text("conversation_id"),
355
- createdAt: integer("created_at").notNull(),
356
- });
357
-
358
- // Recurrence-centric aliases — prefer these in new code.
359
- // Physical table names remain `cron_jobs` / `cron_runs` for migration compatibility.
360
- export const scheduleJobs = cronJobs;
361
- export const scheduleRuns = cronRuns;
362
-
363
- // ── LLM Usage Events (cost tracking ledger) ─────────────────────────
364
-
365
- // ── Entity Graph ─────────────────────────────────────────────────────
366
-
367
- export const memoryEntities = sqliteTable("memory_entities", {
368
- id: text("id").primaryKey(),
369
- name: text("name").notNull(),
370
- type: text("type").notNull(),
371
- aliases: text("aliases"),
372
- description: text("description"),
373
- firstSeenAt: integer("first_seen_at").notNull(),
374
- lastSeenAt: integer("last_seen_at").notNull(),
375
- mentionCount: integer("mention_count").notNull().default(1),
376
- });
377
-
378
- export const memoryEntityRelations = sqliteTable("memory_entity_relations", {
379
- id: text("id").primaryKey(),
380
- sourceEntityId: text("source_entity_id").notNull(),
381
- targetEntityId: text("target_entity_id").notNull(),
382
- relation: text("relation").notNull(),
383
- evidence: text("evidence"),
384
- firstSeenAt: integer("first_seen_at").notNull(),
385
- lastSeenAt: integer("last_seen_at").notNull(),
386
- });
387
-
388
- export const memoryItemEntities = sqliteTable("memory_item_entities", {
389
- memoryItemId: text("memory_item_id").notNull(),
390
- entityId: text("entity_id").notNull(),
391
- });
392
-
393
- export const sharedAppLinks = sqliteTable("shared_app_links", {
394
- id: text("id").primaryKey(),
395
- shareToken: text("share_token").notNull().unique(),
396
- bundleData: blob("bundle_data", { mode: "buffer" }).notNull(),
397
- bundleSizeBytes: integer("bundle_size_bytes").notNull(),
398
- manifestJson: text("manifest_json").notNull(),
399
- downloadCount: integer("download_count").notNull().default(0),
400
- createdAt: integer("created_at").notNull(),
401
- expiresAt: integer("expires_at"),
402
- });
403
-
404
- // ── Contacts ─────────────────────────────────────────────────────────
405
-
406
- export const contacts = sqliteTable("contacts", {
407
- id: text("id").primaryKey(),
408
- displayName: text("display_name").notNull(),
409
- relationship: text("relationship"), // e.g. 'colleague', 'friend', 'manager', 'client'
410
- importance: real("importance").notNull().default(0.5), // 0-1 scale, learned from interaction patterns
411
- responseExpectation: text("response_expectation"), // e.g. 'immediate', 'within_hours', 'casual'
412
- preferredTone: text("preferred_tone"), // e.g. 'formal', 'casual', 'friendly'
413
- lastInteraction: integer("last_interaction"), // epoch ms
414
- interactionCount: integer("interaction_count").notNull().default(0),
415
- createdAt: integer("created_at").notNull(),
416
- updatedAt: integer("updated_at").notNull(),
417
- role: text("role").notNull().default("contact"), // 'guardian' | 'contact'
418
- principalId: text("principal_id"), // internal auth principal (nullable)
419
- assistantId: text("assistant_id"), // which assistant this guardian is for (nullable, daemon default is DAEMON_INTERNAL_ASSISTANT_ID)
420
- contactType: text("contact_type").notNull().default("human"), // 'human' | 'assistant'
421
- });
422
-
423
- export const contactChannels = sqliteTable(
424
- "contact_channels",
425
- {
426
- id: text("id").primaryKey(),
427
- contactId: text("contact_id")
428
- .notNull()
429
- .references(() => contacts.id, { onDelete: "cascade" }),
430
- type: text("type").notNull(), // 'email', 'slack', 'whatsapp', 'phone', etc.
431
- address: text("address").notNull(), // the actual identifier on that channel
432
- isPrimary: integer("is_primary", { mode: "boolean" })
433
- .notNull()
434
- .default(false),
435
- externalUserId: text("external_user_id"), // channel-native user ID (e.g., Telegram numeric ID, E.164 phone)
436
- externalChatId: text("external_chat_id"), // delivery/notification routing address (e.g., Telegram chat ID)
437
- status: text("status").notNull().default("unverified"), // 'active' | 'pending' | 'revoked' | 'blocked' | 'unverified'
438
- policy: text("policy").notNull().default("allow"), // 'allow' | 'deny' | 'escalate'
439
- verifiedAt: integer("verified_at"), // epoch ms
440
- verifiedVia: text("verified_via"), // 'challenge' | 'invite' | 'bootstrap' | etc.
441
- inviteId: text("invite_id"), // reference to invite that onboarded
442
- revokedReason: text("revoked_reason"),
443
- blockedReason: text("blocked_reason"),
444
- lastSeenAt: integer("last_seen_at"), // epoch ms
445
- updatedAt: integer("updated_at"), // epoch ms
446
- createdAt: integer("created_at").notNull(),
447
- },
448
- (table) => [
449
- index("idx_contact_channels_type_ext_user").on(
450
- table.type,
451
- table.externalUserId,
452
- ),
453
- index("idx_contact_channels_type_ext_chat").on(
454
- table.type,
455
- table.externalChatId,
456
- ),
457
- ],
458
- );
459
-
460
- export const assistantContactMetadata = sqliteTable(
461
- "assistant_contact_metadata",
462
- {
463
- contactId: text("contact_id")
464
- .primaryKey()
465
- .references(() => contacts.id, { onDelete: "cascade" }),
466
- species: text("species").notNull(), // 'vellum' | 'openclaw'
467
- metadata: text("metadata"), // JSON blob for species-specific fields
468
- },
469
- );
470
-
471
- // ── Triage Results ───────────────────────────────────────────────────
472
-
473
- export const triageResults = sqliteTable("triage_results", {
474
- id: text("id").primaryKey(),
475
- channel: text("channel").notNull(),
476
- sender: text("sender").notNull(),
477
- category: text("category").notNull(),
478
- confidence: real("confidence").notNull(),
479
- suggestedAction: text("suggested_action").notNull(),
480
- matchedPlaybookIds: text("matched_playbook_ids"), // JSON array of playbook memory item IDs
481
- messageId: text("message_id"), // optional external message identifier
482
- createdAt: integer("created_at").notNull(),
483
- });
484
-
485
- // ── Follow-ups ──────────────────────────────────────────────────────
486
-
487
- export const followups = sqliteTable("followups", {
488
- id: text("id").primaryKey(),
489
- channel: text("channel").notNull(), // 'email', 'slack', 'whatsapp', etc.
490
- threadId: text("thread_id").notNull(), // external thread/conversation identifier
491
- contactId: text("contact_id").references(() => contacts.id, {
492
- onDelete: "set null",
493
- }),
494
- sentAt: integer("sent_at").notNull(), // epoch ms — when the outbound message was sent
495
- expectedResponseBy: integer("expected_response_by"), // epoch ms — deadline for expected reply
496
- status: text("status").notNull().default("pending"), // 'pending' | 'resolved' | 'overdue' | 'nudged'
497
- reminderCronId: text("reminder_cron_id"), // optional cron job ID for reminder
498
- createdAt: integer("created_at").notNull(),
499
- updatedAt: integer("updated_at").notNull(),
500
- });
501
-
502
- // ── Tasks ────────────────────────────────────────────────────────────
503
-
504
- export const tasks = sqliteTable("tasks", {
505
- id: text("id").primaryKey(),
506
- title: text("title").notNull(),
507
- template: text("template").notNull(),
508
- inputSchema: text("input_schema"),
509
- contextFlags: text("context_flags"),
510
- requiredTools: text("required_tools"),
511
- createdFromConversationId: text("created_from_conversation_id"),
512
- status: text("status").notNull().default("active"),
513
- createdAt: integer("created_at").notNull(),
514
- updatedAt: integer("updated_at").notNull(),
515
- });
516
-
517
- export const taskRuns = sqliteTable("task_runs", {
518
- id: text("id").primaryKey(),
519
- taskId: text("task_id")
520
- .notNull()
521
- .references(() => tasks.id, { onDelete: "cascade" }),
522
- conversationId: text("conversation_id"),
523
- status: text("status").notNull().default("pending"),
524
- startedAt: integer("started_at"),
525
- finishedAt: integer("finished_at"),
526
- error: text("error"),
527
- principalId: text("principal_id"),
528
- memoryScopeId: text("memory_scope_id"),
529
- createdAt: integer("created_at").notNull(),
530
- });
531
-
532
- export const taskCandidates = sqliteTable("task_candidates", {
533
- id: text("id").primaryKey(),
534
- sourceConversationId: text("source_conversation_id").notNull(),
535
- compiledTemplate: text("compiled_template").notNull(),
536
- confidence: real("confidence"),
537
- requiredTools: text("required_tools"), // JSON array string
538
- createdAt: integer("created_at").notNull(),
539
- promotedTaskId: text("promoted_task_id"), // set when candidate is promoted to a real task
540
- });
541
-
542
- // ── Work Items (Tasks) ───────────────────────────────────────────────
543
-
544
- export const workItems = sqliteTable("work_items", {
545
- id: text("id").primaryKey(),
546
- taskId: text("task_id")
547
- .notNull()
548
- .references(() => tasks.id),
549
- title: text("title").notNull(),
550
- notes: text("notes"),
551
- status: text("status").notNull().default("queued"), // queued | running | awaiting_review | failed | cancelled | done | archived
552
- priorityTier: integer("priority_tier").notNull().default(1), // 0=high, 1=medium, 2=low
553
- sortIndex: integer("sort_index"), // manual ordering within same priority tier; null = fall back to updated_at
554
- lastRunId: text("last_run_id"),
555
- lastRunConversationId: text("last_run_conversation_id"),
556
- lastRunStatus: text("last_run_status"), // 'completed' | 'failed' | null
557
- sourceType: text("source_type"), // reserved for future bridge (e.g. 'followup', 'triage')
558
- sourceId: text("source_id"), // reserved for future bridge
559
- requiredTools: text("required_tools"), // JSON array snapshot of tools needed for this run (null=unknown, []=none, ["bash",...]=specific)
560
- approvedTools: text("approved_tools"), // JSON array of pre-approved tool names
561
- approvalStatus: text("approval_status").default("none"), // 'none' | 'approved' | 'denied'
562
- createdAt: integer("created_at").notNull(),
563
- updatedAt: integer("updated_at").notNull(),
564
- });
565
-
566
- export const homeBaseAppLinks = sqliteTable("home_base_app_links", {
567
- id: text("id").primaryKey(),
568
- appId: text("app_id").notNull(),
569
- source: text("source").notNull(),
570
- createdAt: integer("created_at").notNull(),
571
- updatedAt: integer("updated_at").notNull(),
572
- });
573
-
574
- export const publishedPages = sqliteTable("published_pages", {
575
- id: text("id").primaryKey(),
576
- deploymentId: text("deployment_id").notNull().unique(),
577
- publicUrl: text("public_url").notNull(),
578
- pageTitle: text("page_title"),
579
- htmlHash: text("html_hash").notNull(),
580
- publishedAt: integer("published_at").notNull(),
581
- status: text("status").notNull().default("active"),
582
- appId: text("app_id"),
583
- projectSlug: text("project_slug"),
584
- });
585
-
586
- // ── Watchers (event-driven polling) ──────────────────────────────────
587
-
588
- export const watchers = sqliteTable("watchers", {
589
- id: text("id").primaryKey(),
590
- name: text("name").notNull(),
591
- providerId: text("provider_id").notNull(),
592
- enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
593
- pollIntervalMs: integer("poll_interval_ms").notNull().default(60000),
594
- actionPrompt: text("action_prompt").notNull(),
595
- watermark: text("watermark"),
596
- conversationId: text("conversation_id"),
597
- status: text("status").notNull().default("idle"), // idle | polling | error | disabled
598
- consecutiveErrors: integer("consecutive_errors").notNull().default(0),
599
- lastError: text("last_error"),
600
- lastPollAt: integer("last_poll_at"),
601
- nextPollAt: integer("next_poll_at").notNull(),
602
- configJson: text("config_json"),
603
- credentialService: text("credential_service").notNull(),
604
- createdAt: integer("created_at").notNull(),
605
- updatedAt: integer("updated_at").notNull(),
606
- });
607
-
608
- export const watcherEvents = sqliteTable("watcher_events", {
609
- id: text("id").primaryKey(),
610
- watcherId: text("watcher_id")
611
- .notNull()
612
- .references(() => watchers.id, { onDelete: "cascade" }),
613
- externalId: text("external_id").notNull(),
614
- eventType: text("event_type").notNull(),
615
- summary: text("summary").notNull(),
616
- payloadJson: text("payload_json").notNull(),
617
- disposition: text("disposition").notNull().default("pending"), // pending | silent | notify | escalate | error
618
- llmAction: text("llm_action"),
619
- processedAt: integer("processed_at"),
620
- createdAt: integer("created_at").notNull(),
621
- });
622
-
623
- export const llmRequestLogs = sqliteTable("llm_request_logs", {
624
- id: text("id").primaryKey(),
625
- conversationId: text("conversation_id").notNull(),
626
- requestPayload: text("request_payload").notNull(),
627
- responsePayload: text("response_payload").notNull(),
628
- createdAt: integer("created_at").notNull(),
629
- });
630
-
631
- export const llmUsageEvents = sqliteTable(
632
- "llm_usage_events",
633
- {
634
- id: text("id").primaryKey(),
635
- createdAt: integer("created_at").notNull(),
636
- conversationId: text("conversation_id"),
637
- runId: text("run_id"),
638
- requestId: text("request_id"),
639
- actor: text("actor").notNull(),
640
- provider: text("provider").notNull(),
641
- model: text("model").notNull(),
642
- inputTokens: integer("input_tokens").notNull(),
643
- outputTokens: integer("output_tokens").notNull(),
644
- cacheCreationInputTokens: integer("cache_creation_input_tokens"),
645
- cacheReadInputTokens: integer("cache_read_input_tokens"),
646
- estimatedCostUsd: real("estimated_cost_usd"),
647
- pricingStatus: text("pricing_status").notNull(),
648
- metadataJson: text("metadata_json"),
649
- },
650
- (table) => [
651
- index("idx_llm_usage_events_conversation_id").on(table.conversationId),
652
- ],
653
- );
654
-
655
- // ── Call Sessions (outgoing AI phone calls) ──────────────────────────
656
-
657
- export const callSessions = sqliteTable(
658
- "call_sessions",
659
- {
660
- id: text("id").primaryKey(),
661
- conversationId: text("conversation_id")
662
- .notNull()
663
- .references(() => conversations.id, { onDelete: "cascade" }),
664
- provider: text("provider").notNull(),
665
- providerCallSid: text("provider_call_sid"),
666
- fromNumber: text("from_number").notNull(),
667
- toNumber: text("to_number").notNull(),
668
- task: text("task"),
669
- status: text("status").notNull().default("initiated"),
670
- callMode: text("call_mode"),
671
- guardianVerificationSessionId: text("guardian_verification_session_id"),
672
- callerIdentityMode: text("caller_identity_mode"),
673
- callerIdentitySource: text("caller_identity_source"),
674
- assistantId: text("assistant_id"),
675
- initiatedFromConversationId: text("initiated_from_conversation_id"),
676
- startedAt: integer("started_at"),
677
- endedAt: integer("ended_at"),
678
- lastError: text("last_error"),
679
- createdAt: integer("created_at").notNull(),
680
- updatedAt: integer("updated_at").notNull(),
681
- },
682
- (table) => [index("idx_call_sessions_status").on(table.status)],
683
- );
684
-
685
- export const callEvents = sqliteTable("call_events", {
686
- id: text("id").primaryKey(),
687
- callSessionId: text("call_session_id")
688
- .notNull()
689
- .references(() => callSessions.id, { onDelete: "cascade" }),
690
- eventType: text("event_type").notNull(),
691
- payloadJson: text("payload_json").notNull().default("{}"),
692
- createdAt: integer("created_at").notNull(),
693
- });
694
-
695
- export const callPendingQuestions = sqliteTable("call_pending_questions", {
696
- id: text("id").primaryKey(),
697
- callSessionId: text("call_session_id")
698
- .notNull()
699
- .references(() => callSessions.id, { onDelete: "cascade" }),
700
- questionText: text("question_text").notNull(),
701
- status: text("status").notNull().default("pending"),
702
- askedAt: integer("asked_at").notNull(),
703
- answeredAt: integer("answered_at"),
704
- answerText: text("answer_text"),
705
- });
706
-
707
- export const processedCallbacks = sqliteTable("processed_callbacks", {
708
- id: text("id").primaryKey(),
709
- dedupeKey: text("dedupe_key").notNull().unique(),
710
- callSessionId: text("call_session_id")
711
- .notNull()
712
- .references(() => callSessions.id, { onDelete: "cascade" }),
713
- claimId: text("claim_id"),
714
- createdAt: integer("created_at").notNull(),
715
- });
716
-
717
- // ── External Conversation Bindings ───────────────────────────────────
718
- // UNIQUE (source_channel, external_chat_id) enforced via idx_ext_conv_bindings_channel_chat_unique in db.ts
719
-
720
- export const externalConversationBindings = sqliteTable(
721
- "external_conversation_bindings",
722
- {
723
- conversationId: text("conversation_id")
724
- .primaryKey()
725
- .references(() => conversations.id, { onDelete: "cascade" }),
726
- sourceChannel: text("source_channel").notNull(),
727
- externalChatId: text("external_chat_id").notNull(),
728
- externalUserId: text("external_user_id"),
729
- displayName: text("display_name"),
730
- username: text("username"),
731
- createdAt: integer("created_at").notNull(),
732
- updatedAt: integer("updated_at").notNull(),
733
- lastInboundAt: integer("last_inbound_at"),
734
- lastOutboundAt: integer("last_outbound_at"),
735
- },
736
- );
737
-
738
- // ── Channel Guardian Bindings ────────────────────────────────────────
739
- // Dropped in migration 131-drop-legacy-member-guardian-tables.
740
- // Data lives in the contacts/contact_channels tables.
741
-
742
- // ── Channel Guardian Verification Challenges ─────────────────────────
743
-
744
- export const channelGuardianVerificationChallenges = sqliteTable(
745
- "channel_guardian_verification_challenges",
746
- {
747
- id: text("id").primaryKey(),
748
- assistantId: text("assistant_id").notNull(),
749
- channel: text("channel").notNull(),
750
- challengeHash: text("challenge_hash").notNull(),
751
- expiresAt: integer("expires_at").notNull(),
752
- status: text("status").notNull().default("pending"),
753
- createdBySessionId: text("created_by_session_id"),
754
- consumedByExternalUserId: text("consumed_by_external_user_id"),
755
- consumedByChatId: text("consumed_by_chat_id"),
756
- // Outbound session: expected-identity binding
757
- expectedExternalUserId: text("expected_external_user_id"),
758
- expectedChatId: text("expected_chat_id"),
759
- expectedPhoneE164: text("expected_phone_e164"),
760
- identityBindingStatus: text("identity_binding_status").default("bound"),
761
- // Outbound session: delivery tracking
762
- destinationAddress: text("destination_address"),
763
- lastSentAt: integer("last_sent_at"),
764
- sendCount: integer("send_count").default(0),
765
- nextResendAt: integer("next_resend_at"),
766
- // Session configuration
767
- codeDigits: integer("code_digits").default(6),
768
- maxAttempts: integer("max_attempts").default(3),
769
- // Distinguishes guardian verification from trusted contact verification
770
- verificationPurpose: text("verification_purpose").default("guardian"),
771
- // Telegram bootstrap deep-link token hash
772
- bootstrapTokenHash: text("bootstrap_token_hash"),
773
- createdAt: integer("created_at").notNull(),
774
- updatedAt: integer("updated_at").notNull(),
775
- },
776
- );
777
-
778
- // ── Channel Guardian Approval Requests ───────────────────────────────
779
-
780
- export const channelGuardianApprovalRequests = sqliteTable(
781
- "channel_guardian_approval_requests",
782
- {
783
- id: text("id").primaryKey(),
784
- runId: text("run_id").notNull(),
785
- requestId: text("request_id"),
786
- conversationId: text("conversation_id").notNull(),
787
- assistantId: text("assistant_id")
788
- .notNull()
789
- .default(DAEMON_INTERNAL_ASSISTANT_ID),
790
- channel: text("channel").notNull(),
791
- requesterExternalUserId: text("requester_external_user_id").notNull(),
792
- requesterChatId: text("requester_chat_id").notNull(),
793
- guardianExternalUserId: text("guardian_external_user_id").notNull(),
794
- guardianChatId: text("guardian_chat_id").notNull(),
795
- toolName: text("tool_name").notNull(),
796
- riskLevel: text("risk_level"),
797
- reason: text("reason"),
798
- status: text("status").notNull().default("pending"),
799
- decidedByExternalUserId: text("decided_by_external_user_id"),
800
- expiresAt: integer("expires_at").notNull(),
801
- createdAt: integer("created_at").notNull(),
802
- updatedAt: integer("updated_at").notNull(),
803
- },
804
- );
805
-
806
- // ── Channel Guardian Verification Rate Limits ────────────────────────
807
-
808
- export const channelGuardianRateLimits = sqliteTable(
809
- "channel_guardian_rate_limits",
810
- {
811
- id: text("id").primaryKey(),
812
- assistantId: text("assistant_id").notNull(),
813
- channel: text("channel").notNull(),
814
- actorExternalUserId: text("actor_external_user_id").notNull(),
815
- actorChatId: text("actor_chat_id").notNull(),
816
- // Legacy columns kept with defaults for backward compatibility with upgraded databases
817
- // that still have the old NOT NULL columns without DEFAULT. Not read by app logic.
818
- invalidAttempts: integer("invalid_attempts").notNull().default(0),
819
- windowStartedAt: integer("window_started_at").notNull().default(0),
820
- attemptTimestampsJson: text("attempt_timestamps_json")
821
- .notNull()
822
- .default("[]"),
823
- lockedUntil: integer("locked_until"),
824
- createdAt: integer("created_at").notNull(),
825
- updatedAt: integer("updated_at").notNull(),
826
- },
827
- );
828
-
829
- // ── Media Assets ─────────────────────────────────────────────────────
830
-
831
- export const mediaAssets = sqliteTable("media_assets", {
832
- id: text("id").primaryKey(),
833
- title: text("title").notNull(),
834
- filePath: text("file_path").notNull(),
835
- mimeType: text("mime_type").notNull(),
836
- durationSeconds: real("duration_seconds"),
837
- fileHash: text("file_hash").notNull(),
838
- status: text("status").notNull().default("registered"), // registered | processing | indexed | failed
839
- mediaType: text("media_type").notNull(), // video | audio | image
840
- metadata: text("metadata"), // JSON
841
- createdAt: integer("created_at").notNull(),
842
- updatedAt: integer("updated_at").notNull(),
843
- });
844
-
845
- export const processingStages = sqliteTable("processing_stages", {
846
- id: text("id").primaryKey(),
847
- assetId: text("asset_id")
848
- .notNull()
849
- .references(() => mediaAssets.id, { onDelete: "cascade" }),
850
- stage: text("stage").notNull(),
851
- status: text("status").notNull().default("pending"), // pending | running | completed | failed
852
- progress: integer("progress").notNull().default(0), // 0-100
853
- lastError: text("last_error"),
854
- startedAt: integer("started_at"),
855
- completedAt: integer("completed_at"),
856
- });
857
-
858
- export const mediaKeyframes = sqliteTable("media_keyframes", {
859
- id: text("id").primaryKey(),
860
- assetId: text("asset_id")
861
- .notNull()
862
- .references(() => mediaAssets.id, { onDelete: "cascade" }),
863
- timestamp: real("timestamp").notNull(),
864
- filePath: text("file_path").notNull(),
865
- metadata: text("metadata"), // JSON
866
- createdAt: integer("created_at").notNull(),
867
- });
868
-
869
- export const mediaVisionOutputs = sqliteTable("media_vision_outputs", {
870
- id: text("id").primaryKey(),
871
- assetId: text("asset_id")
872
- .notNull()
873
- .references(() => mediaAssets.id, { onDelete: "cascade" }),
874
- keyframeId: text("keyframe_id")
875
- .notNull()
876
- .references(() => mediaKeyframes.id, { onDelete: "cascade" }),
877
- analysisType: text("analysis_type").notNull(),
878
- output: text("output").notNull(), // JSON
879
- confidence: real("confidence"),
880
- createdAt: integer("created_at").notNull(),
881
- });
882
-
883
- export const mediaTimelines = sqliteTable("media_timelines", {
884
- id: text("id").primaryKey(),
885
- assetId: text("asset_id")
886
- .notNull()
887
- .references(() => mediaAssets.id, { onDelete: "cascade" }),
888
- startTime: real("start_time").notNull(),
889
- endTime: real("end_time").notNull(),
890
- segmentType: text("segment_type").notNull(),
891
- attributes: text("attributes"), // JSON
892
- confidence: real("confidence"),
893
- createdAt: integer("created_at").notNull(),
894
- });
895
-
896
- export const mediaEvents = sqliteTable("media_events", {
897
- id: text("id").primaryKey(),
898
- assetId: text("asset_id")
899
- .notNull()
900
- .references(() => mediaAssets.id, { onDelete: "cascade" }),
901
- eventType: text("event_type").notNull(),
902
- startTime: real("start_time").notNull(),
903
- endTime: real("end_time").notNull(),
904
- confidence: real("confidence").notNull(),
905
- reasons: text("reasons").notNull(), // JSON array
906
- metadata: text("metadata"), // JSON
907
- createdAt: integer("created_at").notNull(),
908
- });
909
-
910
- export const mediaTrackingProfiles = sqliteTable("media_tracking_profiles", {
911
- id: text("id").primaryKey(),
912
- assetId: text("asset_id")
913
- .notNull()
914
- .references(() => mediaAssets.id, { onDelete: "cascade" }),
915
- capabilities: text("capabilities").notNull(), // JSON: { [capName]: { enabled, tier } }
916
- createdAt: integer("created_at").notNull(),
917
- });
918
-
919
- export const mediaEventFeedback = sqliteTable("media_event_feedback", {
920
- id: text("id").primaryKey(),
921
- assetId: text("asset_id")
922
- .notNull()
923
- .references(() => mediaAssets.id, { onDelete: "cascade" }),
924
- eventId: text("event_id")
925
- .notNull()
926
- .references(() => mediaEvents.id, { onDelete: "cascade" }),
927
- feedbackType: text("feedback_type").notNull(), // correct | incorrect | boundary_edit | missed
928
- originalStartTime: real("original_start_time"),
929
- originalEndTime: real("original_end_time"),
930
- correctedStartTime: real("corrected_start_time"),
931
- correctedEndTime: real("corrected_end_time"),
932
- notes: text("notes"),
933
- createdAt: integer("created_at").notNull(),
934
- });
935
-
936
- // ── Guardian Action Requests (cross-channel voice guardian) ──────────
937
-
938
- export const guardianActionRequests = sqliteTable(
939
- "guardian_action_requests",
940
- {
941
- id: text("id").primaryKey(),
942
- assistantId: text("assistant_id")
943
- .notNull()
944
- .default(DAEMON_INTERNAL_ASSISTANT_ID),
945
- kind: text("kind").notNull(), // 'ask_guardian'
946
- sourceChannel: text("source_channel").notNull(), // 'voice'
947
- sourceConversationId: text("source_conversation_id").notNull(),
948
- callSessionId: text("call_session_id")
949
- .notNull()
950
- .references(() => callSessions.id, { onDelete: "cascade" }),
951
- pendingQuestionId: text("pending_question_id")
952
- .notNull()
953
- .references(() => callPendingQuestions.id, { onDelete: "cascade" }),
954
- questionText: text("question_text").notNull(),
955
- requestCode: text("request_code").notNull(), // short human-readable code for routing replies
956
- status: text("status").notNull().default("pending"), // pending | answered | expired | cancelled
957
- answerText: text("answer_text"),
958
- answeredByChannel: text("answered_by_channel"),
959
- answeredByExternalUserId: text("answered_by_external_user_id"),
960
- answeredAt: integer("answered_at"),
961
- expiresAt: integer("expires_at").notNull(),
962
- expiredReason: text("expired_reason"), // call_timeout | sweep_timeout | cancelled
963
- followupState: text("followup_state").notNull().default("none"), // none | awaiting_guardian_choice | dispatching | completed | declined | failed
964
- lateAnswerText: text("late_answer_text"),
965
- lateAnsweredAt: integer("late_answered_at"),
966
- followupAction: text("followup_action"), // call_back | message_back | decline
967
- followupCompletedAt: integer("followup_completed_at"),
968
- toolName: text("tool_name"), // tool identity for tool-approval requests
969
- inputDigest: text("input_digest"), // canonical SHA-256 digest of tool input
970
- supersededByRequestId: text("superseded_by_request_id"), // links to the request that replaced this one
971
- supersededAt: integer("superseded_at"), // epoch ms when supersession occurred
972
- createdAt: integer("created_at").notNull(),
973
- updatedAt: integer("updated_at").notNull(),
974
- },
975
- (table) => [
976
- index("idx_guardian_action_requests_session_status_created").on(
977
- table.callSessionId,
978
- table.status,
979
- table.createdAt,
980
- ),
981
- ],
982
- );
983
-
984
- // ── Guardian Action Deliveries (per-channel delivery tracking) ───────
985
-
986
- export const guardianActionDeliveries = sqliteTable(
987
- "guardian_action_deliveries",
988
- {
989
- id: text("id").primaryKey(),
990
- requestId: text("request_id")
991
- .notNull()
992
- .references(() => guardianActionRequests.id, { onDelete: "cascade" }),
993
- destinationChannel: text("destination_channel").notNull(), // 'telegram' | 'sms' | 'vellum'
994
- destinationConversationId: text("destination_conversation_id"),
995
- destinationChatId: text("destination_chat_id"),
996
- destinationExternalUserId: text("destination_external_user_id"),
997
- status: text("status").notNull().default("pending"), // pending | sent | failed | answered | expired | cancelled
998
- sentAt: integer("sent_at"),
999
- respondedAt: integer("responded_at"),
1000
- lastError: text("last_error"),
1001
- createdAt: integer("created_at").notNull(),
1002
- updatedAt: integer("updated_at").notNull(),
1003
- },
1004
- (table) => [
1005
- index("idx_guardian_action_deliveries_dest_conversation").on(
1006
- table.destinationConversationId,
1007
- ),
1008
- ],
1009
- );
1010
-
1011
- // ── Canonical Guardian Requests (unified cross-source guardian domain) ─
1012
-
1013
- export const canonicalGuardianRequests = sqliteTable(
1014
- "canonical_guardian_requests",
1015
- {
1016
- id: text("id").primaryKey(),
1017
- kind: text("kind").notNull(),
1018
- sourceType: text("source_type").notNull(),
1019
- sourceChannel: text("source_channel"),
1020
- conversationId: text("conversation_id"),
1021
- requesterExternalUserId: text("requester_external_user_id"),
1022
- requesterChatId: text("requester_chat_id"),
1023
- guardianExternalUserId: text("guardian_external_user_id"),
1024
- guardianPrincipalId: text("guardian_principal_id"),
1025
- callSessionId: text("call_session_id"),
1026
- pendingQuestionId: text("pending_question_id"),
1027
- questionText: text("question_text"),
1028
- requestCode: text("request_code"),
1029
- toolName: text("tool_name"),
1030
- inputDigest: text("input_digest"),
1031
- status: text("status").notNull().default("pending"),
1032
- answerText: text("answer_text"),
1033
- decidedByExternalUserId: text("decided_by_external_user_id"),
1034
- decidedByPrincipalId: text("decided_by_principal_id"),
1035
- followupState: text("followup_state"),
1036
- expiresAt: text("expires_at"),
1037
- createdAt: text("created_at").notNull(),
1038
- updatedAt: text("updated_at").notNull(),
1039
- },
1040
- (table) => [
1041
- index("idx_canonical_guardian_requests_status").on(table.status),
1042
- index("idx_canonical_guardian_requests_guardian").on(
1043
- table.guardianExternalUserId,
1044
- table.status,
1045
- ),
1046
- index("idx_canonical_guardian_requests_conversation").on(
1047
- table.conversationId,
1048
- table.status,
1049
- ),
1050
- index("idx_canonical_guardian_requests_source").on(
1051
- table.sourceType,
1052
- table.status,
1053
- ),
1054
- index("idx_canonical_guardian_requests_kind").on(table.kind, table.status),
1055
- index("idx_canonical_guardian_requests_request_code").on(table.requestCode),
1056
- ],
1057
- );
1058
-
1059
- // ── Canonical Guardian Deliveries (per-channel delivery tracking) ─────
1060
-
1061
- export const canonicalGuardianDeliveries = sqliteTable(
1062
- "canonical_guardian_deliveries",
1063
- {
1064
- id: text("id").primaryKey(),
1065
- requestId: text("request_id")
1066
- .notNull()
1067
- .references(() => canonicalGuardianRequests.id, { onDelete: "cascade" }),
1068
- destinationChannel: text("destination_channel").notNull(),
1069
- destinationConversationId: text("destination_conversation_id"),
1070
- destinationChatId: text("destination_chat_id"),
1071
- destinationMessageId: text("destination_message_id"),
1072
- status: text("status").notNull().default("pending"),
1073
- createdAt: text("created_at").notNull(),
1074
- updatedAt: text("updated_at").notNull(),
1075
- },
1076
- (table) => [
1077
- index("idx_canonical_guardian_deliveries_request_id").on(table.requestId),
1078
- index("idx_canonical_guardian_deliveries_status").on(table.status),
1079
- ],
1080
- );
1081
-
1082
- // ── Assistant Inbox ──────────────────────────────────────────────────
1083
-
1084
- export const assistantIngressInvites = sqliteTable(
1085
- "assistant_ingress_invites",
1086
- {
1087
- id: text("id").primaryKey(),
1088
- assistantId: text("assistant_id")
1089
- .notNull()
1090
- .default(DAEMON_INTERNAL_ASSISTANT_ID),
1091
- sourceChannel: text("source_channel").notNull(),
1092
- tokenHash: text("token_hash").notNull(),
1093
- createdBySessionId: text("created_by_session_id"),
1094
- note: text("note"),
1095
- maxUses: integer("max_uses").notNull().default(1),
1096
- useCount: integer("use_count").notNull().default(0),
1097
- expiresAt: integer("expires_at").notNull(),
1098
- status: text("status").notNull().default("active"),
1099
- redeemedByExternalUserId: text("redeemed_by_external_user_id"),
1100
- redeemedByExternalChatId: text("redeemed_by_external_chat_id"),
1101
- redeemedAt: integer("redeemed_at"),
1102
- // Voice invite fields (nullable — non-voice invites leave these NULL)
1103
- expectedExternalUserId: text("expected_external_user_id"),
1104
- voiceCodeHash: text("voice_code_hash"),
1105
- voiceCodeDigits: integer("voice_code_digits"),
1106
- // Display metadata for personalized voice prompts (nullable — non-voice invites leave these NULL)
1107
- friendName: text("friend_name"),
1108
- guardianName: text("guardian_name"),
1109
- createdAt: integer("created_at").notNull(),
1110
- updatedAt: integer("updated_at").notNull(),
1111
- },
1112
- );
1113
-
1114
- // ── Assistant Ingress Members ─────────────────────────────────────────
1115
- // Dropped in migration 131-drop-legacy-member-guardian-tables.
1116
- // Data lives in the contacts/contact_channels tables.
1117
-
1118
- export const assistantInboxThreadState = sqliteTable(
1119
- "assistant_inbox_thread_state",
1120
- {
1121
- conversationId: text("conversation_id")
1122
- .primaryKey()
1123
- .references(() => conversations.id, { onDelete: "cascade" }),
1124
- assistantId: text("assistant_id")
1125
- .notNull()
1126
- .default(DAEMON_INTERNAL_ASSISTANT_ID),
1127
- sourceChannel: text("source_channel").notNull(),
1128
- externalChatId: text("external_chat_id").notNull(),
1129
- externalUserId: text("external_user_id"),
1130
- displayName: text("display_name"),
1131
- username: text("username"),
1132
- lastInboundAt: integer("last_inbound_at"),
1133
- lastOutboundAt: integer("last_outbound_at"),
1134
- lastMessageAt: integer("last_message_at"),
1135
- unreadCount: integer("unread_count").notNull().default(0),
1136
- pendingEscalationCount: integer("pending_escalation_count")
1137
- .notNull()
1138
- .default(0),
1139
- hasPendingEscalation: integer("has_pending_escalation")
1140
- .notNull()
1141
- .default(0),
1142
- createdAt: integer("created_at").notNull(),
1143
- updatedAt: integer("updated_at").notNull(),
1144
- },
1145
- );
1146
-
1147
- // ── Notification System ──────────────────────────────────────────────
1148
-
1149
- export const notificationEvents = sqliteTable("notification_events", {
1150
- id: text("id").primaryKey(),
1151
- assistantId: text("assistant_id").notNull(),
1152
- sourceEventName: text("source_event_name").notNull(),
1153
- sourceChannel: text("source_channel").notNull(),
1154
- sourceSessionId: text("source_session_id").notNull(),
1155
- attentionHintsJson: text("attention_hints_json").notNull().default("{}"),
1156
- payloadJson: text("payload_json").notNull().default("{}"),
1157
- dedupeKey: text("dedupe_key"),
1158
- createdAt: integer("created_at").notNull(),
1159
- updatedAt: integer("updated_at").notNull(),
1160
- });
1161
-
1162
- export const notificationDecisions = sqliteTable("notification_decisions", {
1163
- id: text("id").primaryKey(),
1164
- notificationEventId: text("notification_event_id")
1165
- .notNull()
1166
- .references(() => notificationEvents.id, { onDelete: "cascade" }),
1167
- shouldNotify: integer("should_notify").notNull(),
1168
- selectedChannels: text("selected_channels").notNull().default("[]"),
1169
- reasoningSummary: text("reasoning_summary").notNull(),
1170
- confidence: real("confidence").notNull(),
1171
- fallbackUsed: integer("fallback_used").notNull().default(0),
1172
- promptVersion: text("prompt_version"),
1173
- validationResults: text("validation_results"),
1174
- createdAt: integer("created_at").notNull(),
1175
- });
1176
-
1177
- export const notificationPreferences = sqliteTable("notification_preferences", {
1178
- id: text("id").primaryKey(),
1179
- assistantId: text("assistant_id").notNull(),
1180
- preferenceText: text("preference_text").notNull(),
1181
- appliesWhenJson: text("applies_when_json").notNull().default("{}"),
1182
- priority: integer("priority").notNull().default(0),
1183
- createdAt: integer("created_at").notNull(),
1184
- updatedAt: integer("updated_at").notNull(),
1185
- });
1186
-
1187
- // ── Sequences (multi-step outreach) ──────────────────────────────────
1188
-
1189
- export const sequences = sqliteTable("sequences", {
1190
- id: text("id").primaryKey(),
1191
- name: text("name").notNull(),
1192
- description: text("description"),
1193
- channel: text("channel").notNull(),
1194
- steps: text("steps").notNull(), // JSON array of SequenceStep
1195
- exitOnReply: integer("exit_on_reply", { mode: "boolean" })
1196
- .notNull()
1197
- .default(true),
1198
- status: text("status").notNull().default("active"), // active | paused | archived
1199
- createdAt: integer("created_at").notNull(),
1200
- updatedAt: integer("updated_at").notNull(),
1201
- });
1202
-
1203
- export const sequenceEnrollments = sqliteTable(
1204
- "sequence_enrollments",
1205
- {
1206
- id: text("id").primaryKey(),
1207
- sequenceId: text("sequence_id")
1208
- .notNull()
1209
- .references(() => sequences.id, { onDelete: "cascade" }),
1210
- contactEmail: text("contact_email").notNull(),
1211
- contactName: text("contact_name"),
1212
- currentStep: integer("current_step").notNull().default(0),
1213
- status: text("status").notNull().default("active"), // active | paused | completed | replied | cancelled | failed
1214
- threadId: text("thread_id"),
1215
- nextStepAt: integer("next_step_at"), // epoch ms
1216
- context: text("context"), // JSON
1217
- createdAt: integer("created_at").notNull(),
1218
- updatedAt: integer("updated_at").notNull(),
1219
- },
1220
- (table) => [
1221
- index("idx_seq_enrollments_status_next_step").on(
1222
- table.status,
1223
- table.nextStepAt,
1224
- ),
1225
- index("idx_seq_enrollments_sequence_id").on(table.sequenceId),
1226
- index("idx_seq_enrollments_contact_email").on(table.contactEmail),
1227
- ],
1228
- );
1229
-
1230
- export const notificationDeliveries = sqliteTable(
1231
- "notification_deliveries",
1232
- {
1233
- id: text("id").primaryKey(),
1234
- notificationDecisionId: text("notification_decision_id")
1235
- .notNull()
1236
- .references(() => notificationDecisions.id, { onDelete: "cascade" }),
1237
- assistantId: text("assistant_id").notNull(),
1238
- channel: text("channel").notNull(),
1239
- destination: text("destination").notNull(),
1240
- status: text("status").notNull().default("pending"),
1241
- attempt: integer("attempt").notNull().default(1),
1242
- renderedTitle: text("rendered_title"),
1243
- renderedBody: text("rendered_body"),
1244
- errorCode: text("error_code"),
1245
- errorMessage: text("error_message"),
1246
- sentAt: integer("sent_at"),
1247
- conversationId: text("conversation_id"),
1248
- messageId: text("message_id"),
1249
- conversationStrategy: text("conversation_strategy"),
1250
- threadAction: text("thread_action"),
1251
- threadTargetConversationId: text("thread_target_conversation_id"),
1252
- threadDecisionFallbackUsed: integer("thread_decision_fallback_used"),
1253
- clientDeliveryStatus: text("client_delivery_status"),
1254
- clientDeliveryError: text("client_delivery_error"),
1255
- clientDeliveryAt: integer("client_delivery_at"),
1256
- createdAt: integer("created_at").notNull(),
1257
- updatedAt: integer("updated_at").notNull(),
1258
- },
1259
- (table) => [
1260
- uniqueIndex("idx_notification_deliveries_decision_channel").on(
1261
- table.notificationDecisionId,
1262
- table.channel,
1263
- ),
1264
- ],
1265
- );
1266
-
1267
- // ── Conversation Attention ───────────────────────────────────────────
1268
-
1269
- export const conversationAttentionEvents = sqliteTable(
1270
- "conversation_attention_events",
1271
- {
1272
- id: text("id").primaryKey(),
1273
- conversationId: text("conversation_id")
1274
- .notNull()
1275
- .references(() => conversations.id, { onDelete: "cascade" }),
1276
- assistantId: text("assistant_id").notNull(),
1277
- sourceChannel: text("source_channel").notNull(),
1278
- signalType: text("signal_type").notNull(),
1279
- confidence: text("confidence").notNull(),
1280
- source: text("source").notNull(),
1281
- evidenceText: text("evidence_text"),
1282
- metadataJson: text("metadata_json").notNull().default("{}"),
1283
- observedAt: integer("observed_at").notNull(),
1284
- createdAt: integer("created_at").notNull(),
1285
- },
1286
- (table) => [
1287
- index("idx_conv_attn_events_conv_observed").on(
1288
- table.conversationId,
1289
- table.observedAt,
1290
- ),
1291
- index("idx_conv_attn_events_assistant_observed").on(
1292
- table.assistantId,
1293
- table.observedAt,
1294
- ),
1295
- index("idx_conv_attn_events_channel_observed").on(
1296
- table.sourceChannel,
1297
- table.observedAt,
1298
- ),
1299
- ],
1300
- );
1301
-
1302
- export const conversationAssistantAttentionState = sqliteTable(
1303
- "conversation_assistant_attention_state",
1304
- {
1305
- conversationId: text("conversation_id")
1306
- .primaryKey()
1307
- .references(() => conversations.id, { onDelete: "cascade" }),
1308
- assistantId: text("assistant_id").notNull(),
1309
- latestAssistantMessageId: text("latest_assistant_message_id"),
1310
- latestAssistantMessageAt: integer("latest_assistant_message_at"),
1311
- lastSeenAssistantMessageId: text("last_seen_assistant_message_id"),
1312
- lastSeenAssistantMessageAt: integer("last_seen_assistant_message_at"),
1313
- lastSeenEventAt: integer("last_seen_event_at"),
1314
- lastSeenConfidence: text("last_seen_confidence"),
1315
- lastSeenSignalType: text("last_seen_signal_type"),
1316
- lastSeenSourceChannel: text("last_seen_source_channel"),
1317
- lastSeenSource: text("last_seen_source"),
1318
- lastSeenEvidenceText: text("last_seen_evidence_text"),
1319
- createdAt: integer("created_at").notNull(),
1320
- updatedAt: integer("updated_at").notNull(),
1321
- },
1322
- (table) => [
1323
- index("idx_conv_attn_state_assistant_latest_msg").on(
1324
- table.assistantId,
1325
- table.latestAssistantMessageAt,
1326
- ),
1327
- index("idx_conv_attn_state_assistant_last_seen").on(
1328
- table.assistantId,
1329
- table.lastSeenAssistantMessageAt,
1330
- ),
1331
- ],
1332
- );
1333
-
1334
- // ── Actor Token Records ──────────────────────────────────────────────
1335
-
1336
- export const actorTokenRecords = sqliteTable("actor_token_records", {
1337
- id: text("id").primaryKey(),
1338
- tokenHash: text("token_hash").notNull(),
1339
- assistantId: text("assistant_id").notNull(),
1340
- guardianPrincipalId: text("guardian_principal_id").notNull(),
1341
- hashedDeviceId: text("hashed_device_id").notNull(),
1342
- platform: text("platform").notNull(),
1343
- status: text("status").notNull().default("active"),
1344
- issuedAt: integer("issued_at").notNull(),
1345
- expiresAt: integer("expires_at"),
1346
- createdAt: integer("created_at").notNull(),
1347
- updatedAt: integer("updated_at").notNull(),
1348
- });
1349
-
1350
- // ── Actor Refresh Token Records ──────────────────────────────────────
1351
-
1352
- export const actorRefreshTokenRecords = sqliteTable(
1353
- "actor_refresh_token_records",
1354
- {
1355
- id: text("id").primaryKey(),
1356
- tokenHash: text("token_hash").notNull(),
1357
- familyId: text("family_id").notNull(),
1358
- assistantId: text("assistant_id").notNull(),
1359
- guardianPrincipalId: text("guardian_principal_id").notNull(),
1360
- hashedDeviceId: text("hashed_device_id").notNull(),
1361
- platform: text("platform").notNull(),
1362
- status: text("status").notNull().default("active"),
1363
- issuedAt: integer("issued_at").notNull(),
1364
- absoluteExpiresAt: integer("absolute_expires_at").notNull(),
1365
- inactivityExpiresAt: integer("inactivity_expires_at").notNull(),
1366
- lastUsedAt: integer("last_used_at"),
1367
- createdAt: integer("created_at").notNull(),
1368
- updatedAt: integer("updated_at").notNull(),
1369
- },
1370
- );
1371
-
1372
- // ── Scoped Approval Grants ──────────────────────────────────────────
1373
-
1374
- export const scopedApprovalGrants = sqliteTable(
1375
- "scoped_approval_grants",
1376
- {
1377
- id: text("id").primaryKey(),
1378
- assistantId: text("assistant_id").notNull(),
1379
- scopeMode: text("scope_mode").notNull(), // 'request_id' | 'tool_signature'
1380
- requestId: text("request_id"),
1381
- toolName: text("tool_name"),
1382
- inputDigest: text("input_digest"),
1383
- requestChannel: text("request_channel").notNull(),
1384
- decisionChannel: text("decision_channel").notNull(),
1385
- executionChannel: text("execution_channel"), // null = any channel
1386
- conversationId: text("conversation_id"),
1387
- callSessionId: text("call_session_id"),
1388
- requesterExternalUserId: text("requester_external_user_id"),
1389
- guardianExternalUserId: text("guardian_external_user_id"),
1390
- status: text("status").notNull(), // 'active' | 'consumed' | 'expired' | 'revoked'
1391
- expiresAt: text("expires_at").notNull(),
1392
- consumedAt: text("consumed_at"),
1393
- consumedByRequestId: text("consumed_by_request_id"),
1394
- createdAt: text("created_at").notNull(),
1395
- updatedAt: text("updated_at").notNull(),
1396
- },
1397
- (table) => [
1398
- index("idx_scoped_grants_request_id").on(table.requestId),
1399
- index("idx_scoped_grants_tool_sig").on(table.toolName, table.inputDigest),
1400
- index("idx_scoped_grants_status_expires").on(table.status, table.expiresAt),
1401
- ],
1402
- );
1
+ export * from "./schema/index.js";