@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
@@ -0,0 +1,172 @@
1
+ import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
2
+
3
+ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../runtime/assistant-scope.js";
4
+ import { callPendingQuestions, callSessions } from "./calls.js";
5
+
6
+ export const guardianActionRequests = sqliteTable(
7
+ "guardian_action_requests",
8
+ {
9
+ id: text("id").primaryKey(),
10
+ assistantId: text("assistant_id")
11
+ .notNull()
12
+ .default(DAEMON_INTERNAL_ASSISTANT_ID),
13
+ kind: text("kind").notNull(), // 'ask_guardian'
14
+ sourceChannel: text("source_channel").notNull(), // 'voice'
15
+ sourceConversationId: text("source_conversation_id").notNull(),
16
+ callSessionId: text("call_session_id")
17
+ .notNull()
18
+ .references(() => callSessions.id, { onDelete: "cascade" }),
19
+ pendingQuestionId: text("pending_question_id")
20
+ .notNull()
21
+ .references(() => callPendingQuestions.id, { onDelete: "cascade" }),
22
+ questionText: text("question_text").notNull(),
23
+ requestCode: text("request_code").notNull(), // short human-readable code for routing replies
24
+ status: text("status").notNull().default("pending"), // pending | answered | expired | cancelled
25
+ answerText: text("answer_text"),
26
+ answeredByChannel: text("answered_by_channel"),
27
+ answeredByExternalUserId: text("answered_by_external_user_id"),
28
+ answeredAt: integer("answered_at"),
29
+ expiresAt: integer("expires_at").notNull(),
30
+ expiredReason: text("expired_reason"), // call_timeout | sweep_timeout | cancelled
31
+ followupState: text("followup_state").notNull().default("none"), // none | awaiting_guardian_choice | dispatching | completed | declined | failed
32
+ lateAnswerText: text("late_answer_text"),
33
+ lateAnsweredAt: integer("late_answered_at"),
34
+ followupAction: text("followup_action"), // call_back | message_back | decline
35
+ followupCompletedAt: integer("followup_completed_at"),
36
+ toolName: text("tool_name"), // tool identity for tool-approval requests
37
+ inputDigest: text("input_digest"), // canonical SHA-256 digest of tool input
38
+ supersededByRequestId: text("superseded_by_request_id"), // links to the request that replaced this one
39
+ supersededAt: integer("superseded_at"), // epoch ms when supersession occurred
40
+ createdAt: integer("created_at").notNull(),
41
+ updatedAt: integer("updated_at").notNull(),
42
+ },
43
+ (table) => [
44
+ index("idx_guardian_action_requests_session_status_created").on(
45
+ table.callSessionId,
46
+ table.status,
47
+ table.createdAt,
48
+ ),
49
+ ],
50
+ );
51
+
52
+ export const guardianActionDeliveries = sqliteTable(
53
+ "guardian_action_deliveries",
54
+ {
55
+ id: text("id").primaryKey(),
56
+ requestId: text("request_id")
57
+ .notNull()
58
+ .references(() => guardianActionRequests.id, { onDelete: "cascade" }),
59
+ destinationChannel: text("destination_channel").notNull(), // 'telegram' | 'sms' | 'vellum'
60
+ destinationConversationId: text("destination_conversation_id"),
61
+ destinationChatId: text("destination_chat_id"),
62
+ destinationExternalUserId: text("destination_external_user_id"),
63
+ status: text("status").notNull().default("pending"), // pending | sent | failed | answered | expired | cancelled
64
+ sentAt: integer("sent_at"),
65
+ respondedAt: integer("responded_at"),
66
+ lastError: text("last_error"),
67
+ createdAt: integer("created_at").notNull(),
68
+ updatedAt: integer("updated_at").notNull(),
69
+ },
70
+ (table) => [
71
+ index("idx_guardian_action_deliveries_dest_conversation").on(
72
+ table.destinationConversationId,
73
+ ),
74
+ ],
75
+ );
76
+
77
+ export const canonicalGuardianRequests = sqliteTable(
78
+ "canonical_guardian_requests",
79
+ {
80
+ id: text("id").primaryKey(),
81
+ kind: text("kind").notNull(),
82
+ sourceType: text("source_type").notNull(),
83
+ sourceChannel: text("source_channel"),
84
+ conversationId: text("conversation_id"),
85
+ requesterExternalUserId: text("requester_external_user_id"),
86
+ requesterChatId: text("requester_chat_id"),
87
+ guardianExternalUserId: text("guardian_external_user_id"),
88
+ guardianPrincipalId: text("guardian_principal_id"),
89
+ callSessionId: text("call_session_id"),
90
+ pendingQuestionId: text("pending_question_id"),
91
+ questionText: text("question_text"),
92
+ requestCode: text("request_code"),
93
+ toolName: text("tool_name"),
94
+ inputDigest: text("input_digest"),
95
+ status: text("status").notNull().default("pending"),
96
+ answerText: text("answer_text"),
97
+ decidedByExternalUserId: text("decided_by_external_user_id"),
98
+ decidedByPrincipalId: text("decided_by_principal_id"),
99
+ followupState: text("followup_state"),
100
+ expiresAt: text("expires_at"),
101
+ createdAt: text("created_at").notNull(),
102
+ updatedAt: text("updated_at").notNull(),
103
+ },
104
+ (table) => [
105
+ index("idx_canonical_guardian_requests_status").on(table.status),
106
+ index("idx_canonical_guardian_requests_guardian").on(
107
+ table.guardianExternalUserId,
108
+ table.status,
109
+ ),
110
+ index("idx_canonical_guardian_requests_conversation").on(
111
+ table.conversationId,
112
+ table.status,
113
+ ),
114
+ index("idx_canonical_guardian_requests_source").on(
115
+ table.sourceType,
116
+ table.status,
117
+ ),
118
+ index("idx_canonical_guardian_requests_kind").on(table.kind, table.status),
119
+ index("idx_canonical_guardian_requests_request_code").on(table.requestCode),
120
+ ],
121
+ );
122
+
123
+ export const canonicalGuardianDeliveries = sqliteTable(
124
+ "canonical_guardian_deliveries",
125
+ {
126
+ id: text("id").primaryKey(),
127
+ requestId: text("request_id")
128
+ .notNull()
129
+ .references(() => canonicalGuardianRequests.id, { onDelete: "cascade" }),
130
+ destinationChannel: text("destination_channel").notNull(),
131
+ destinationConversationId: text("destination_conversation_id"),
132
+ destinationChatId: text("destination_chat_id"),
133
+ destinationMessageId: text("destination_message_id"),
134
+ status: text("status").notNull().default("pending"),
135
+ createdAt: text("created_at").notNull(),
136
+ updatedAt: text("updated_at").notNull(),
137
+ },
138
+ (table) => [
139
+ index("idx_canonical_guardian_deliveries_request_id").on(table.requestId),
140
+ index("idx_canonical_guardian_deliveries_status").on(table.status),
141
+ ],
142
+ );
143
+
144
+ export const scopedApprovalGrants = sqliteTable(
145
+ "scoped_approval_grants",
146
+ {
147
+ id: text("id").primaryKey(),
148
+ assistantId: text("assistant_id").notNull(),
149
+ scopeMode: text("scope_mode").notNull(), // 'request_id' | 'tool_signature'
150
+ requestId: text("request_id"),
151
+ toolName: text("tool_name"),
152
+ inputDigest: text("input_digest"),
153
+ requestChannel: text("request_channel").notNull(),
154
+ decisionChannel: text("decision_channel").notNull(),
155
+ executionChannel: text("execution_channel"), // null = any channel
156
+ conversationId: text("conversation_id"),
157
+ callSessionId: text("call_session_id"),
158
+ requesterExternalUserId: text("requester_external_user_id"),
159
+ guardianExternalUserId: text("guardian_external_user_id"),
160
+ status: text("status").notNull(), // 'active' | 'consumed' | 'expired' | 'revoked'
161
+ expiresAt: text("expires_at").notNull(),
162
+ consumedAt: text("consumed_at"),
163
+ consumedByRequestId: text("consumed_by_request_id"),
164
+ createdAt: text("created_at").notNull(),
165
+ updatedAt: text("updated_at").notNull(),
166
+ },
167
+ (table) => [
168
+ index("idx_scoped_grants_request_id").on(table.requestId),
169
+ index("idx_scoped_grants_tool_sig").on(table.toolName, table.inputDigest),
170
+ index("idx_scoped_grants_status_expires").on(table.status, table.expiresAt),
171
+ ],
172
+ );
@@ -0,0 +1,8 @@
1
+ export * from "./calls.js";
2
+ export * from "./contacts.js";
3
+ export * from "./conversations.js";
4
+ export * from "./guardian.js";
5
+ export * from "./infrastructure.js";
6
+ export * from "./memory-core.js";
7
+ export * from "./notifications.js";
8
+ export * from "./tasks.js";
@@ -0,0 +1,205 @@
1
+ import {
2
+ blob,
3
+ index,
4
+ integer,
5
+ real,
6
+ sqliteTable,
7
+ text,
8
+ } from "drizzle-orm/sqlite-core";
9
+
10
+ export const accounts = sqliteTable("accounts", {
11
+ id: text("id").primaryKey(),
12
+ service: text("service").notNull(),
13
+ username: text("username"),
14
+ email: text("email"),
15
+ displayName: text("display_name"),
16
+ status: text("status").notNull().default("active"),
17
+ credentialRef: text("credential_ref"),
18
+ metadataJson: text("metadata_json"),
19
+ createdAt: integer("created_at").notNull(),
20
+ updatedAt: integer("updated_at").notNull(),
21
+ });
22
+
23
+ export const reminders = sqliteTable("reminders", {
24
+ id: text("id").primaryKey(),
25
+ label: text("label").notNull(),
26
+ message: text("message").notNull(),
27
+ fireAt: integer("fire_at").notNull(), // epoch ms, absolute timestamp
28
+ mode: text("mode").notNull(), // 'notify' | 'execute'
29
+ status: text("status").notNull(), // 'pending' | 'firing' | 'fired' | 'cancelled'
30
+ firedAt: integer("fired_at"),
31
+ conversationId: text("conversation_id"),
32
+ routingIntent: text("routing_intent").notNull().default("all_channels"), // 'single_channel' | 'multi_channel' | 'all_channels'
33
+ routingHintsJson: text("routing_hints_json").notNull().default("{}"),
34
+ createdAt: integer("created_at").notNull(),
35
+ updatedAt: integer("updated_at").notNull(),
36
+ });
37
+
38
+ export const cronJobs = sqliteTable("cron_jobs", {
39
+ id: text("id").primaryKey(),
40
+ name: text("name").notNull(),
41
+ enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
42
+ cronExpression: text("cron_expression").notNull(), // e.g. '0 9 * * 1-5'
43
+ scheduleSyntax: text("schedule_syntax").notNull().default("cron"), // 'cron' | 'rrule'
44
+ timezone: text("timezone"), // e.g. 'America/Los_Angeles'
45
+ message: text("message").notNull(),
46
+ nextRunAt: integer("next_run_at").notNull(),
47
+ lastRunAt: integer("last_run_at"),
48
+ lastStatus: text("last_status"), // 'ok' | 'error'
49
+ retryCount: integer("retry_count").notNull().default(0),
50
+ createdBy: text("created_by").notNull(), // 'agent' | 'user'
51
+ createdAt: integer("created_at").notNull(),
52
+ updatedAt: integer("updated_at").notNull(),
53
+ });
54
+
55
+ export const cronRuns = sqliteTable("cron_runs", {
56
+ id: text("id").primaryKey(),
57
+ jobId: text("job_id")
58
+ .notNull()
59
+ .references(() => cronJobs.id, { onDelete: "cascade" }),
60
+ status: text("status").notNull(), // 'ok' | 'error'
61
+ startedAt: integer("started_at").notNull(),
62
+ finishedAt: integer("finished_at"),
63
+ durationMs: integer("duration_ms"),
64
+ output: text("output"),
65
+ error: text("error"),
66
+ conversationId: text("conversation_id"),
67
+ createdAt: integer("created_at").notNull(),
68
+ });
69
+
70
+ // Recurrence-centric aliases — prefer these in new code.
71
+ // Physical table names remain `cron_jobs` / `cron_runs` for migration compatibility.
72
+ export const scheduleJobs = cronJobs;
73
+ export const scheduleRuns = cronRuns;
74
+
75
+ export const sharedAppLinks = sqliteTable("shared_app_links", {
76
+ id: text("id").primaryKey(),
77
+ shareToken: text("share_token").notNull().unique(),
78
+ bundleData: blob("bundle_data", { mode: "buffer" }).notNull(),
79
+ bundleSizeBytes: integer("bundle_size_bytes").notNull(),
80
+ manifestJson: text("manifest_json").notNull(),
81
+ downloadCount: integer("download_count").notNull().default(0),
82
+ createdAt: integer("created_at").notNull(),
83
+ expiresAt: integer("expires_at"),
84
+ });
85
+
86
+ export const homeBaseAppLinks = sqliteTable("home_base_app_links", {
87
+ id: text("id").primaryKey(),
88
+ appId: text("app_id").notNull(),
89
+ source: text("source").notNull(),
90
+ createdAt: integer("created_at").notNull(),
91
+ updatedAt: integer("updated_at").notNull(),
92
+ });
93
+
94
+ export const publishedPages = sqliteTable("published_pages", {
95
+ id: text("id").primaryKey(),
96
+ deploymentId: text("deployment_id").notNull().unique(),
97
+ publicUrl: text("public_url").notNull(),
98
+ pageTitle: text("page_title"),
99
+ htmlHash: text("html_hash").notNull(),
100
+ publishedAt: integer("published_at").notNull(),
101
+ status: text("status").notNull().default("active"),
102
+ appId: text("app_id"),
103
+ projectSlug: text("project_slug"),
104
+ });
105
+
106
+ export const watchers = sqliteTable("watchers", {
107
+ id: text("id").primaryKey(),
108
+ name: text("name").notNull(),
109
+ providerId: text("provider_id").notNull(),
110
+ enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
111
+ pollIntervalMs: integer("poll_interval_ms").notNull().default(60000),
112
+ actionPrompt: text("action_prompt").notNull(),
113
+ watermark: text("watermark"),
114
+ conversationId: text("conversation_id"),
115
+ status: text("status").notNull().default("idle"), // idle | polling | error | disabled
116
+ consecutiveErrors: integer("consecutive_errors").notNull().default(0),
117
+ lastError: text("last_error"),
118
+ lastPollAt: integer("last_poll_at"),
119
+ nextPollAt: integer("next_poll_at").notNull(),
120
+ configJson: text("config_json"),
121
+ credentialService: text("credential_service").notNull(),
122
+ createdAt: integer("created_at").notNull(),
123
+ updatedAt: integer("updated_at").notNull(),
124
+ });
125
+
126
+ export const watcherEvents = sqliteTable("watcher_events", {
127
+ id: text("id").primaryKey(),
128
+ watcherId: text("watcher_id")
129
+ .notNull()
130
+ .references(() => watchers.id, { onDelete: "cascade" }),
131
+ externalId: text("external_id").notNull(),
132
+ eventType: text("event_type").notNull(),
133
+ summary: text("summary").notNull(),
134
+ payloadJson: text("payload_json").notNull(),
135
+ disposition: text("disposition").notNull().default("pending"), // pending | silent | notify | escalate | error
136
+ llmAction: text("llm_action"),
137
+ processedAt: integer("processed_at"),
138
+ createdAt: integer("created_at").notNull(),
139
+ });
140
+
141
+ export const llmRequestLogs = sqliteTable("llm_request_logs", {
142
+ id: text("id").primaryKey(),
143
+ conversationId: text("conversation_id").notNull(),
144
+ requestPayload: text("request_payload").notNull(),
145
+ responsePayload: text("response_payload").notNull(),
146
+ createdAt: integer("created_at").notNull(),
147
+ });
148
+
149
+ export const llmUsageEvents = sqliteTable(
150
+ "llm_usage_events",
151
+ {
152
+ id: text("id").primaryKey(),
153
+ createdAt: integer("created_at").notNull(),
154
+ conversationId: text("conversation_id"),
155
+ runId: text("run_id"),
156
+ requestId: text("request_id"),
157
+ actor: text("actor").notNull(),
158
+ provider: text("provider").notNull(),
159
+ model: text("model").notNull(),
160
+ inputTokens: integer("input_tokens").notNull(),
161
+ outputTokens: integer("output_tokens").notNull(),
162
+ cacheCreationInputTokens: integer("cache_creation_input_tokens"),
163
+ cacheReadInputTokens: integer("cache_read_input_tokens"),
164
+ estimatedCostUsd: real("estimated_cost_usd"),
165
+ pricingStatus: text("pricing_status").notNull(),
166
+ metadataJson: text("metadata_json"),
167
+ },
168
+ (table) => [
169
+ index("idx_llm_usage_events_conversation_id").on(table.conversationId),
170
+ ],
171
+ );
172
+
173
+ export const actorTokenRecords = sqliteTable("actor_token_records", {
174
+ id: text("id").primaryKey(),
175
+ tokenHash: text("token_hash").notNull(),
176
+ assistantId: text("assistant_id").notNull(),
177
+ guardianPrincipalId: text("guardian_principal_id").notNull(),
178
+ hashedDeviceId: text("hashed_device_id").notNull(),
179
+ platform: text("platform").notNull(),
180
+ status: text("status").notNull().default("active"),
181
+ issuedAt: integer("issued_at").notNull(),
182
+ expiresAt: integer("expires_at"),
183
+ createdAt: integer("created_at").notNull(),
184
+ updatedAt: integer("updated_at").notNull(),
185
+ });
186
+
187
+ export const actorRefreshTokenRecords = sqliteTable(
188
+ "actor_refresh_token_records",
189
+ {
190
+ id: text("id").primaryKey(),
191
+ tokenHash: text("token_hash").notNull(),
192
+ familyId: text("family_id").notNull(),
193
+ assistantId: text("assistant_id").notNull(),
194
+ guardianPrincipalId: text("guardian_principal_id").notNull(),
195
+ hashedDeviceId: text("hashed_device_id").notNull(),
196
+ platform: text("platform").notNull(),
197
+ status: text("status").notNull().default("active"),
198
+ issuedAt: integer("issued_at").notNull(),
199
+ absoluteExpiresAt: integer("absolute_expires_at").notNull(),
200
+ inactivityExpiresAt: integer("inactivity_expires_at").notNull(),
201
+ lastUsedAt: integer("last_used_at"),
202
+ createdAt: integer("created_at").notNull(),
203
+ updatedAt: integer("updated_at").notNull(),
204
+ },
205
+ );
@@ -0,0 +1,196 @@
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 { conversations, messages } from "./conversations.js";
12
+
13
+ export const memorySegments = sqliteTable(
14
+ "memory_segments",
15
+ {
16
+ id: text("id").primaryKey(),
17
+ messageId: text("message_id")
18
+ .notNull()
19
+ .references(() => messages.id, { onDelete: "cascade" }),
20
+ conversationId: text("conversation_id")
21
+ .notNull()
22
+ .references(() => conversations.id, { onDelete: "cascade" }),
23
+ role: text("role").notNull(),
24
+ segmentIndex: integer("segment_index").notNull(),
25
+ text: text("text").notNull(),
26
+ tokenEstimate: integer("token_estimate").notNull(),
27
+ scopeId: text("scope_id").notNull().default("default"),
28
+ contentHash: text("content_hash"),
29
+ createdAt: integer("created_at").notNull(),
30
+ updatedAt: integer("updated_at").notNull(),
31
+ },
32
+ (table) => [index("idx_memory_segments_scope_id").on(table.scopeId)],
33
+ );
34
+
35
+ export const memoryItems = sqliteTable(
36
+ "memory_items",
37
+ {
38
+ id: text("id").primaryKey(),
39
+ kind: text("kind").notNull(),
40
+ subject: text("subject").notNull(),
41
+ statement: text("statement").notNull(),
42
+ status: text("status").notNull(),
43
+ confidence: real("confidence").notNull(),
44
+ importance: real("importance"),
45
+ accessCount: integer("access_count").notNull().default(0),
46
+ fingerprint: text("fingerprint").notNull(),
47
+ verificationState: text("verification_state")
48
+ .notNull()
49
+ .default("assistant_inferred"),
50
+ scopeId: text("scope_id").notNull().default("default"),
51
+ firstSeenAt: integer("first_seen_at").notNull(),
52
+ lastSeenAt: integer("last_seen_at").notNull(),
53
+ lastUsedAt: integer("last_used_at"),
54
+ validFrom: integer("valid_from"),
55
+ invalidAt: integer("invalid_at"),
56
+ },
57
+ (table) => [
58
+ index("idx_memory_items_scope_id").on(table.scopeId),
59
+ index("idx_memory_items_fingerprint").on(table.fingerprint),
60
+ ],
61
+ );
62
+
63
+ export const memoryItemSources = sqliteTable(
64
+ "memory_item_sources",
65
+ {
66
+ memoryItemId: text("memory_item_id")
67
+ .notNull()
68
+ .references(() => memoryItems.id, { onDelete: "cascade" }),
69
+ messageId: text("message_id")
70
+ .notNull()
71
+ .references(() => messages.id, { onDelete: "cascade" }),
72
+ evidence: text("evidence"),
73
+ createdAt: integer("created_at").notNull(),
74
+ },
75
+ (table) => [
76
+ index("idx_memory_item_sources_memory_item_id").on(table.memoryItemId),
77
+ ],
78
+ );
79
+
80
+ export const memoryItemConflicts = sqliteTable(
81
+ "memory_item_conflicts",
82
+ {
83
+ id: text("id").primaryKey(),
84
+ scopeId: text("scope_id").notNull().default("default"),
85
+ existingItemId: text("existing_item_id")
86
+ .notNull()
87
+ .references(() => memoryItems.id, { onDelete: "cascade" }),
88
+ candidateItemId: text("candidate_item_id")
89
+ .notNull()
90
+ .references(() => memoryItems.id, { onDelete: "cascade" }),
91
+ relationship: text("relationship").notNull(),
92
+ status: text("status").notNull(),
93
+ clarificationQuestion: text("clarification_question"),
94
+ resolutionNote: text("resolution_note"),
95
+ lastAskedAt: integer("last_asked_at"),
96
+ resolvedAt: integer("resolved_at"),
97
+ createdAt: integer("created_at").notNull(),
98
+ updatedAt: integer("updated_at").notNull(),
99
+ },
100
+ (table) => [index("idx_memory_item_conflicts_scope_id").on(table.scopeId)],
101
+ );
102
+
103
+ export const memorySummaries = sqliteTable(
104
+ "memory_summaries",
105
+ {
106
+ id: text("id").primaryKey(),
107
+ scope: text("scope").notNull(),
108
+ scopeKey: text("scope_key").notNull(),
109
+ summary: text("summary").notNull(),
110
+ tokenEstimate: integer("token_estimate").notNull(),
111
+ version: integer("version").notNull().default(1),
112
+ scopeId: text("scope_id").notNull().default("default"),
113
+ startAt: integer("start_at").notNull(),
114
+ endAt: integer("end_at").notNull(),
115
+ createdAt: integer("created_at").notNull(),
116
+ updatedAt: integer("updated_at").notNull(),
117
+ },
118
+ (table) => [
119
+ index("idx_memory_summaries_scope_id").on(table.scopeId),
120
+ uniqueIndex("idx_memory_summaries_scope_scope_key").on(
121
+ table.scope,
122
+ table.scopeKey,
123
+ ),
124
+ ],
125
+ );
126
+
127
+ export const memoryEmbeddings = sqliteTable(
128
+ "memory_embeddings",
129
+ {
130
+ id: text("id").primaryKey(),
131
+ targetType: text("target_type").notNull(),
132
+ targetId: text("target_id").notNull(),
133
+ provider: text("provider").notNull(),
134
+ model: text("model").notNull(),
135
+ dimensions: integer("dimensions").notNull(),
136
+ vectorJson: text("vector_json"),
137
+ vectorBlob: blob("vector_blob"),
138
+ contentHash: text("content_hash"),
139
+ createdAt: integer("created_at").notNull(),
140
+ updatedAt: integer("updated_at").notNull(),
141
+ },
142
+ (table) => [
143
+ uniqueIndex("idx_memory_embeddings_target_provider_model").on(
144
+ table.targetType,
145
+ table.targetId,
146
+ table.provider,
147
+ table.model,
148
+ ),
149
+ ],
150
+ );
151
+
152
+ export const memoryJobs = sqliteTable("memory_jobs", {
153
+ id: text("id").primaryKey(),
154
+ type: text("type").notNull(),
155
+ payload: text("payload").notNull(),
156
+ status: text("status").notNull(),
157
+ attempts: integer("attempts").notNull().default(0),
158
+ deferrals: integer("deferrals").notNull().default(0),
159
+ runAfter: integer("run_after").notNull(),
160
+ lastError: text("last_error"),
161
+ startedAt: integer("started_at"),
162
+ createdAt: integer("created_at").notNull(),
163
+ updatedAt: integer("updated_at").notNull(),
164
+ });
165
+
166
+ export const memoryCheckpoints = sqliteTable("memory_checkpoints", {
167
+ key: text("key").primaryKey(),
168
+ value: text("value").notNull(),
169
+ updatedAt: integer("updated_at").notNull(),
170
+ });
171
+
172
+ export const memoryEntities = sqliteTable("memory_entities", {
173
+ id: text("id").primaryKey(),
174
+ name: text("name").notNull(),
175
+ type: text("type").notNull(),
176
+ aliases: text("aliases"),
177
+ description: text("description"),
178
+ firstSeenAt: integer("first_seen_at").notNull(),
179
+ lastSeenAt: integer("last_seen_at").notNull(),
180
+ mentionCount: integer("mention_count").notNull().default(1),
181
+ });
182
+
183
+ export const memoryEntityRelations = sqliteTable("memory_entity_relations", {
184
+ id: text("id").primaryKey(),
185
+ sourceEntityId: text("source_entity_id").notNull(),
186
+ targetEntityId: text("target_entity_id").notNull(),
187
+ relation: text("relation").notNull(),
188
+ evidence: text("evidence"),
189
+ firstSeenAt: integer("first_seen_at").notNull(),
190
+ lastSeenAt: integer("last_seen_at").notNull(),
191
+ });
192
+
193
+ export const memoryItemEntities = sqliteTable("memory_item_entities", {
194
+ memoryItemId: text("memory_item_id").notNull(),
195
+ entityId: text("entity_id").notNull(),
196
+ });