@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.
- package/ARCHITECTURE.md +1 -1
- package/Dockerfile +14 -8
- package/README.md +2 -2
- package/docs/architecture/memory.md +28 -29
- package/docs/runbook-trusted-contacts.md +1 -4
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -7
- package/src/__tests__/anthropic-provider.test.ts +86 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/checker.test.ts +37 -98
- package/src/__tests__/commit-message-enrichment-service.test.ts +15 -4
- package/src/__tests__/config-schema.test.ts +6 -14
- package/src/__tests__/conflict-policy.test.ts +76 -0
- package/src/__tests__/conflict-store.test.ts +14 -20
- package/src/__tests__/contacts-tools.test.ts +8 -61
- package/src/__tests__/contradiction-checker.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/daemon-server-session-init.test.ts +1 -19
- package/src/__tests__/followup-tools.test.ts +0 -30
- package/src/__tests__/gemini-provider.test.ts +79 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-routing-invariants.test.ts +6 -4
- package/src/__tests__/ipc-snapshot.test.ts +0 -4
- package/src/__tests__/managed-proxy-context.test.ts +163 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +13 -12
- package/src/__tests__/memory-regressions.test.ts +6 -6
- package/src/__tests__/openai-provider.test.ts +82 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +134 -1
- package/src/__tests__/provider-managed-proxy-integration.test.ts +269 -0
- package/src/__tests__/recurrence-types.test.ts +0 -15
- package/src/__tests__/registry.test.ts +0 -10
- package/src/__tests__/schedule-tools.test.ts +28 -44
- package/src/__tests__/script-proxy-session-runtime.test.ts +6 -1
- package/src/__tests__/session-agent-loop.test.ts +0 -2
- package/src/__tests__/session-conflict-gate.test.ts +243 -388
- package/src/__tests__/session-profile-injection.test.ts +0 -2
- package/src/__tests__/session-runtime-assembly.test.ts +2 -3
- package/src/__tests__/session-skill-tools.test.ts +0 -49
- package/src/__tests__/session-workspace-cache-state.test.ts +0 -1
- package/src/__tests__/session-workspace-injection.test.ts +0 -1
- package/src/__tests__/session-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/task-management-tools.test.ts +111 -0
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -1
- package/src/__tests__/twilio-config.test.ts +0 -3
- package/src/amazon/session.ts +30 -91
- package/src/approvals/guardian-decision-primitive.ts +11 -7
- package/src/approvals/guardian-request-resolvers.ts +5 -3
- package/src/calls/call-controller.ts +423 -571
- package/src/calls/finalize-call.ts +20 -0
- package/src/calls/relay-access-wait.ts +340 -0
- package/src/calls/relay-server.ts +269 -899
- package/src/calls/relay-setup-router.ts +307 -0
- package/src/calls/relay-verification.ts +280 -0
- package/src/calls/twilio-config.ts +1 -8
- package/src/calls/voice-control-protocol.ts +184 -0
- package/src/calls/voice-session-bridge.ts +1 -8
- package/src/config/agent-schema.ts +1 -1
- package/src/config/bundled-skills/contacts/SKILL.md +7 -18
- package/src/config/bundled-skills/contacts/TOOLS.json +4 -20
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +2 -4
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +6 -12
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +3 -24
- package/src/config/bundled-skills/followups/TOOLS.json +0 -4
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -10
- package/src/config/bundled-tool-registry.ts +0 -5
- package/src/config/core-schema.ts +1 -1
- package/src/config/env.ts +0 -10
- package/src/config/feature-flag-registry.json +1 -1
- package/src/config/loader.ts +19 -0
- package/src/config/memory-schema.ts +0 -10
- package/src/config/schema.ts +2 -2
- package/src/config/system-prompt.ts +6 -0
- package/src/contacts/contact-store.ts +36 -62
- package/src/contacts/contacts-write.ts +14 -3
- package/src/contacts/types.ts +9 -4
- package/src/daemon/handlers/config-heartbeat.ts +1 -2
- package/src/daemon/handlers/contacts.ts +2 -2
- package/src/daemon/handlers/guardian-actions.ts +1 -1
- package/src/daemon/handlers/session-history.ts +398 -0
- package/src/daemon/handlers/session-user-message.ts +982 -0
- package/src/daemon/handlers/sessions.ts +9 -1337
- package/src/daemon/ipc-contract/contacts.ts +2 -2
- package/src/daemon/ipc-contract/sessions.ts +0 -6
- package/src/daemon/ipc-contract-inventory.json +0 -1
- package/src/daemon/lifecycle.ts +0 -29
- package/src/daemon/session-agent-loop.ts +1 -45
- package/src/daemon/session-conflict-gate.ts +21 -82
- package/src/daemon/session-memory.ts +7 -52
- package/src/daemon/session-process.ts +3 -1
- package/src/daemon/session-runtime-assembly.ts +18 -35
- package/src/heartbeat/heartbeat-service.ts +5 -1
- package/src/home-base/app-link-store.ts +0 -7
- package/src/memory/conflict-intent.ts +3 -6
- package/src/memory/conflict-policy.ts +34 -0
- package/src/memory/conflict-store.ts +10 -18
- package/src/memory/contradiction-checker.ts +2 -2
- package/src/memory/conversation-attention-store.ts +1 -1
- package/src/memory/conversation-store.ts +0 -51
- package/src/memory/db-init.ts +8 -0
- package/src/memory/job-handlers/conflict.ts +24 -7
- package/src/memory/migrations/105-contacts-and-triage.ts +4 -7
- package/src/memory/migrations/134-contacts-notes-column.ts +68 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +31 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/recall-cache.ts +0 -5
- package/src/memory/schema/calls.ts +274 -0
- package/src/memory/schema/contacts.ts +125 -0
- package/src/memory/schema/conversations.ts +129 -0
- package/src/memory/schema/guardian.ts +172 -0
- package/src/memory/schema/index.ts +8 -0
- package/src/memory/schema/infrastructure.ts +205 -0
- package/src/memory/schema/memory-core.ts +196 -0
- package/src/memory/schema/notifications.ts +191 -0
- package/src/memory/schema/tasks.ts +78 -0
- package/src/memory/schema.ts +1 -1402
- package/src/memory/slack-thread-store.ts +0 -69
- package/src/messaging/index.ts +0 -1
- package/src/messaging/types.ts +0 -38
- package/src/notifications/decisions-store.ts +2 -105
- package/src/notifications/deliveries-store.ts +0 -11
- package/src/notifications/preferences-store.ts +1 -58
- package/src/permissions/checker.ts +6 -17
- package/src/providers/anthropic/client.ts +6 -2
- package/src/providers/gemini/client.ts +13 -2
- package/src/providers/managed-proxy/constants.ts +55 -0
- package/src/providers/managed-proxy/context.ts +77 -0
- package/src/providers/registry.ts +112 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +51 -23
- package/src/runtime/guardian-action-service.ts +3 -2
- package/src/runtime/guardian-outbound-actions.ts +3 -3
- package/src/runtime/guardian-reply-router.ts +4 -4
- package/src/runtime/http-server.ts +83 -710
- package/src/runtime/http-types.ts +0 -16
- package/src/runtime/middleware/auth.ts +0 -12
- package/src/runtime/routes/app-routes.ts +33 -0
- package/src/runtime/routes/approval-routes.ts +32 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +6 -3
- package/src/runtime/routes/attachment-routes.ts +32 -0
- package/src/runtime/routes/brain-graph-routes.ts +27 -0
- package/src/runtime/routes/call-routes.ts +41 -0
- package/src/runtime/routes/channel-readiness-routes.ts +20 -0
- package/src/runtime/routes/channel-routes.ts +70 -0
- package/src/runtime/routes/contact-routes.ts +371 -29
- package/src/runtime/routes/conversation-attention-routes.ts +15 -0
- package/src/runtime/routes/conversation-routes.ts +192 -194
- package/src/runtime/routes/debug-routes.ts +15 -0
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/global-search-routes.ts +17 -2
- package/src/runtime/routes/guardian-action-routes.ts +23 -1
- package/src/runtime/routes/guardian-approval-interception.ts +2 -1
- package/src/runtime/routes/guardian-bootstrap-routes.ts +26 -1
- package/src/runtime/routes/guardian-refresh-routes.ts +20 -0
- package/src/runtime/routes/identity-routes.ts +20 -0
- package/src/runtime/routes/inbound-message-handler.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +5 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +83 -0
- package/src/runtime/routes/invite-routes.ts +31 -0
- package/src/runtime/routes/migration-routes.ts +47 -17
- package/src/runtime/routes/pairing-routes.ts +18 -0
- package/src/runtime/routes/secret-routes.ts +20 -0
- package/src/runtime/routes/surface-action-routes.ts +26 -0
- package/src/runtime/routes/trust-rules-routes.ts +31 -0
- package/src/runtime/routes/twilio-routes.ts +79 -0
- package/src/schedule/recurrence-types.ts +1 -11
- package/src/tools/followups/followup_create.ts +9 -3
- package/src/tools/mcp/mcp-tool-factory.ts +0 -17
- package/src/tools/memory/definitions.ts +0 -6
- package/src/tools/network/script-proxy/session-manager.ts +38 -3
- package/src/tools/schedule/create.ts +1 -3
- package/src/tools/schedule/update.ts +9 -6
- package/src/twitter/session.ts +29 -77
- package/src/util/cookie-session.ts +114 -0
- package/src/workspace/git-service.ts +6 -4
- package/src/__tests__/conversation-routes.test.ts +0 -99
- package/src/__tests__/get-weather.test.ts +0 -393
- package/src/__tests__/task-tools.test.ts +0 -685
- package/src/__tests__/weather-skill-regression.test.ts +0 -276
- package/src/autonomy/autonomy-resolver.ts +0 -62
- package/src/autonomy/autonomy-store.ts +0 -138
- package/src/autonomy/disposition-mapper.ts +0 -31
- package/src/autonomy/index.ts +0 -11
- package/src/autonomy/types.ts +0 -43
- package/src/config/bundled-skills/weather/SKILL.md +0 -38
- package/src/config/bundled-skills/weather/TOOLS.json +0 -36
- package/src/config/bundled-skills/weather/icon.svg +0 -24
- package/src/config/bundled-skills/weather/tools/get-weather.ts +0 -12
- package/src/contacts/startup-migration.ts +0 -21
- package/src/messaging/triage-engine.ts +0 -344
- package/src/tools/weather/service.ts +0 -712
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import {
|
|
2
|
+
index,
|
|
3
|
+
integer,
|
|
4
|
+
real,
|
|
5
|
+
sqliteTable,
|
|
6
|
+
text,
|
|
7
|
+
} from "drizzle-orm/sqlite-core";
|
|
8
|
+
|
|
9
|
+
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../runtime/assistant-scope.js";
|
|
10
|
+
import { conversations } from "./conversations.js";
|
|
11
|
+
|
|
12
|
+
export const callSessions = sqliteTable(
|
|
13
|
+
"call_sessions",
|
|
14
|
+
{
|
|
15
|
+
id: text("id").primaryKey(),
|
|
16
|
+
conversationId: text("conversation_id")
|
|
17
|
+
.notNull()
|
|
18
|
+
.references(() => conversations.id, { onDelete: "cascade" }),
|
|
19
|
+
provider: text("provider").notNull(),
|
|
20
|
+
providerCallSid: text("provider_call_sid"),
|
|
21
|
+
fromNumber: text("from_number").notNull(),
|
|
22
|
+
toNumber: text("to_number").notNull(),
|
|
23
|
+
task: text("task"),
|
|
24
|
+
status: text("status").notNull().default("initiated"),
|
|
25
|
+
callMode: text("call_mode"),
|
|
26
|
+
guardianVerificationSessionId: text("guardian_verification_session_id"),
|
|
27
|
+
callerIdentityMode: text("caller_identity_mode"),
|
|
28
|
+
callerIdentitySource: text("caller_identity_source"),
|
|
29
|
+
assistantId: text("assistant_id"),
|
|
30
|
+
initiatedFromConversationId: text("initiated_from_conversation_id"),
|
|
31
|
+
startedAt: integer("started_at"),
|
|
32
|
+
endedAt: integer("ended_at"),
|
|
33
|
+
lastError: text("last_error"),
|
|
34
|
+
createdAt: integer("created_at").notNull(),
|
|
35
|
+
updatedAt: integer("updated_at").notNull(),
|
|
36
|
+
},
|
|
37
|
+
(table) => [index("idx_call_sessions_status").on(table.status)],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const callEvents = sqliteTable("call_events", {
|
|
41
|
+
id: text("id").primaryKey(),
|
|
42
|
+
callSessionId: text("call_session_id")
|
|
43
|
+
.notNull()
|
|
44
|
+
.references(() => callSessions.id, { onDelete: "cascade" }),
|
|
45
|
+
eventType: text("event_type").notNull(),
|
|
46
|
+
payloadJson: text("payload_json").notNull().default("{}"),
|
|
47
|
+
createdAt: integer("created_at").notNull(),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const callPendingQuestions = sqliteTable("call_pending_questions", {
|
|
51
|
+
id: text("id").primaryKey(),
|
|
52
|
+
callSessionId: text("call_session_id")
|
|
53
|
+
.notNull()
|
|
54
|
+
.references(() => callSessions.id, { onDelete: "cascade" }),
|
|
55
|
+
questionText: text("question_text").notNull(),
|
|
56
|
+
status: text("status").notNull().default("pending"),
|
|
57
|
+
askedAt: integer("asked_at").notNull(),
|
|
58
|
+
answeredAt: integer("answered_at"),
|
|
59
|
+
answerText: text("answer_text"),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export const processedCallbacks = sqliteTable("processed_callbacks", {
|
|
63
|
+
id: text("id").primaryKey(),
|
|
64
|
+
dedupeKey: text("dedupe_key").notNull().unique(),
|
|
65
|
+
callSessionId: text("call_session_id")
|
|
66
|
+
.notNull()
|
|
67
|
+
.references(() => callSessions.id, { onDelete: "cascade" }),
|
|
68
|
+
claimId: text("claim_id"),
|
|
69
|
+
createdAt: integer("created_at").notNull(),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export const externalConversationBindings = sqliteTable(
|
|
73
|
+
"external_conversation_bindings",
|
|
74
|
+
{
|
|
75
|
+
conversationId: text("conversation_id")
|
|
76
|
+
.primaryKey()
|
|
77
|
+
.references(() => conversations.id, { onDelete: "cascade" }),
|
|
78
|
+
sourceChannel: text("source_channel").notNull(),
|
|
79
|
+
externalChatId: text("external_chat_id").notNull(),
|
|
80
|
+
externalUserId: text("external_user_id"),
|
|
81
|
+
displayName: text("display_name"),
|
|
82
|
+
username: text("username"),
|
|
83
|
+
createdAt: integer("created_at").notNull(),
|
|
84
|
+
updatedAt: integer("updated_at").notNull(),
|
|
85
|
+
lastInboundAt: integer("last_inbound_at"),
|
|
86
|
+
lastOutboundAt: integer("last_outbound_at"),
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
export const channelGuardianVerificationChallenges = sqliteTable(
|
|
91
|
+
"channel_guardian_verification_challenges",
|
|
92
|
+
{
|
|
93
|
+
id: text("id").primaryKey(),
|
|
94
|
+
assistantId: text("assistant_id").notNull(),
|
|
95
|
+
channel: text("channel").notNull(),
|
|
96
|
+
challengeHash: text("challenge_hash").notNull(),
|
|
97
|
+
expiresAt: integer("expires_at").notNull(),
|
|
98
|
+
status: text("status").notNull().default("pending"),
|
|
99
|
+
createdBySessionId: text("created_by_session_id"),
|
|
100
|
+
consumedByExternalUserId: text("consumed_by_external_user_id"),
|
|
101
|
+
consumedByChatId: text("consumed_by_chat_id"),
|
|
102
|
+
// Outbound session: expected-identity binding
|
|
103
|
+
expectedExternalUserId: text("expected_external_user_id"),
|
|
104
|
+
expectedChatId: text("expected_chat_id"),
|
|
105
|
+
expectedPhoneE164: text("expected_phone_e164"),
|
|
106
|
+
identityBindingStatus: text("identity_binding_status").default("bound"),
|
|
107
|
+
// Outbound session: delivery tracking
|
|
108
|
+
destinationAddress: text("destination_address"),
|
|
109
|
+
lastSentAt: integer("last_sent_at"),
|
|
110
|
+
sendCount: integer("send_count").default(0),
|
|
111
|
+
nextResendAt: integer("next_resend_at"),
|
|
112
|
+
// Session configuration
|
|
113
|
+
codeDigits: integer("code_digits").default(6),
|
|
114
|
+
maxAttempts: integer("max_attempts").default(3),
|
|
115
|
+
// Distinguishes guardian verification from trusted contact verification
|
|
116
|
+
verificationPurpose: text("verification_purpose").default("guardian"),
|
|
117
|
+
// Telegram bootstrap deep-link token hash
|
|
118
|
+
bootstrapTokenHash: text("bootstrap_token_hash"),
|
|
119
|
+
createdAt: integer("created_at").notNull(),
|
|
120
|
+
updatedAt: integer("updated_at").notNull(),
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
export const channelGuardianApprovalRequests = sqliteTable(
|
|
125
|
+
"channel_guardian_approval_requests",
|
|
126
|
+
{
|
|
127
|
+
id: text("id").primaryKey(),
|
|
128
|
+
runId: text("run_id").notNull(),
|
|
129
|
+
requestId: text("request_id"),
|
|
130
|
+
conversationId: text("conversation_id").notNull(),
|
|
131
|
+
assistantId: text("assistant_id")
|
|
132
|
+
.notNull()
|
|
133
|
+
.default(DAEMON_INTERNAL_ASSISTANT_ID),
|
|
134
|
+
channel: text("channel").notNull(),
|
|
135
|
+
requesterExternalUserId: text("requester_external_user_id").notNull(),
|
|
136
|
+
requesterChatId: text("requester_chat_id").notNull(),
|
|
137
|
+
guardianExternalUserId: text("guardian_external_user_id").notNull(),
|
|
138
|
+
guardianChatId: text("guardian_chat_id").notNull(),
|
|
139
|
+
toolName: text("tool_name").notNull(),
|
|
140
|
+
riskLevel: text("risk_level"),
|
|
141
|
+
reason: text("reason"),
|
|
142
|
+
status: text("status").notNull().default("pending"),
|
|
143
|
+
decidedByExternalUserId: text("decided_by_external_user_id"),
|
|
144
|
+
expiresAt: integer("expires_at").notNull(),
|
|
145
|
+
createdAt: integer("created_at").notNull(),
|
|
146
|
+
updatedAt: integer("updated_at").notNull(),
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
export const channelGuardianRateLimits = sqliteTable(
|
|
151
|
+
"channel_guardian_rate_limits",
|
|
152
|
+
{
|
|
153
|
+
id: text("id").primaryKey(),
|
|
154
|
+
assistantId: text("assistant_id").notNull(),
|
|
155
|
+
channel: text("channel").notNull(),
|
|
156
|
+
actorExternalUserId: text("actor_external_user_id").notNull(),
|
|
157
|
+
actorChatId: text("actor_chat_id").notNull(),
|
|
158
|
+
// Legacy columns kept with defaults for backward compatibility with upgraded databases
|
|
159
|
+
// that still have the old NOT NULL columns without DEFAULT. Not read by app logic.
|
|
160
|
+
invalidAttempts: integer("invalid_attempts").notNull().default(0),
|
|
161
|
+
windowStartedAt: integer("window_started_at").notNull().default(0),
|
|
162
|
+
attemptTimestampsJson: text("attempt_timestamps_json")
|
|
163
|
+
.notNull()
|
|
164
|
+
.default("[]"),
|
|
165
|
+
lockedUntil: integer("locked_until"),
|
|
166
|
+
createdAt: integer("created_at").notNull(),
|
|
167
|
+
updatedAt: integer("updated_at").notNull(),
|
|
168
|
+
},
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
export const mediaAssets = sqliteTable("media_assets", {
|
|
172
|
+
id: text("id").primaryKey(),
|
|
173
|
+
title: text("title").notNull(),
|
|
174
|
+
filePath: text("file_path").notNull(),
|
|
175
|
+
mimeType: text("mime_type").notNull(),
|
|
176
|
+
durationSeconds: real("duration_seconds"),
|
|
177
|
+
fileHash: text("file_hash").notNull(),
|
|
178
|
+
status: text("status").notNull().default("registered"), // registered | processing | indexed | failed
|
|
179
|
+
mediaType: text("media_type").notNull(), // video | audio | image
|
|
180
|
+
metadata: text("metadata"), // JSON
|
|
181
|
+
createdAt: integer("created_at").notNull(),
|
|
182
|
+
updatedAt: integer("updated_at").notNull(),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
export const processingStages = sqliteTable("processing_stages", {
|
|
186
|
+
id: text("id").primaryKey(),
|
|
187
|
+
assetId: text("asset_id")
|
|
188
|
+
.notNull()
|
|
189
|
+
.references(() => mediaAssets.id, { onDelete: "cascade" }),
|
|
190
|
+
stage: text("stage").notNull(),
|
|
191
|
+
status: text("status").notNull().default("pending"), // pending | running | completed | failed
|
|
192
|
+
progress: integer("progress").notNull().default(0), // 0-100
|
|
193
|
+
lastError: text("last_error"),
|
|
194
|
+
startedAt: integer("started_at"),
|
|
195
|
+
completedAt: integer("completed_at"),
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
export const mediaKeyframes = sqliteTable("media_keyframes", {
|
|
199
|
+
id: text("id").primaryKey(),
|
|
200
|
+
assetId: text("asset_id")
|
|
201
|
+
.notNull()
|
|
202
|
+
.references(() => mediaAssets.id, { onDelete: "cascade" }),
|
|
203
|
+
timestamp: real("timestamp").notNull(),
|
|
204
|
+
filePath: text("file_path").notNull(),
|
|
205
|
+
metadata: text("metadata"), // JSON
|
|
206
|
+
createdAt: integer("created_at").notNull(),
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
export const mediaVisionOutputs = sqliteTable("media_vision_outputs", {
|
|
210
|
+
id: text("id").primaryKey(),
|
|
211
|
+
assetId: text("asset_id")
|
|
212
|
+
.notNull()
|
|
213
|
+
.references(() => mediaAssets.id, { onDelete: "cascade" }),
|
|
214
|
+
keyframeId: text("keyframe_id")
|
|
215
|
+
.notNull()
|
|
216
|
+
.references(() => mediaKeyframes.id, { onDelete: "cascade" }),
|
|
217
|
+
analysisType: text("analysis_type").notNull(),
|
|
218
|
+
output: text("output").notNull(), // JSON
|
|
219
|
+
confidence: real("confidence"),
|
|
220
|
+
createdAt: integer("created_at").notNull(),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
export const mediaTimelines = sqliteTable("media_timelines", {
|
|
224
|
+
id: text("id").primaryKey(),
|
|
225
|
+
assetId: text("asset_id")
|
|
226
|
+
.notNull()
|
|
227
|
+
.references(() => mediaAssets.id, { onDelete: "cascade" }),
|
|
228
|
+
startTime: real("start_time").notNull(),
|
|
229
|
+
endTime: real("end_time").notNull(),
|
|
230
|
+
segmentType: text("segment_type").notNull(),
|
|
231
|
+
attributes: text("attributes"), // JSON
|
|
232
|
+
confidence: real("confidence"),
|
|
233
|
+
createdAt: integer("created_at").notNull(),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
export const mediaEvents = sqliteTable("media_events", {
|
|
237
|
+
id: text("id").primaryKey(),
|
|
238
|
+
assetId: text("asset_id")
|
|
239
|
+
.notNull()
|
|
240
|
+
.references(() => mediaAssets.id, { onDelete: "cascade" }),
|
|
241
|
+
eventType: text("event_type").notNull(),
|
|
242
|
+
startTime: real("start_time").notNull(),
|
|
243
|
+
endTime: real("end_time").notNull(),
|
|
244
|
+
confidence: real("confidence").notNull(),
|
|
245
|
+
reasons: text("reasons").notNull(), // JSON array
|
|
246
|
+
metadata: text("metadata"), // JSON
|
|
247
|
+
createdAt: integer("created_at").notNull(),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
export const mediaTrackingProfiles = sqliteTable("media_tracking_profiles", {
|
|
251
|
+
id: text("id").primaryKey(),
|
|
252
|
+
assetId: text("asset_id")
|
|
253
|
+
.notNull()
|
|
254
|
+
.references(() => mediaAssets.id, { onDelete: "cascade" }),
|
|
255
|
+
capabilities: text("capabilities").notNull(), // JSON: { [capName]: { enabled, tier } }
|
|
256
|
+
createdAt: integer("created_at").notNull(),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
export const mediaEventFeedback = sqliteTable("media_event_feedback", {
|
|
260
|
+
id: text("id").primaryKey(),
|
|
261
|
+
assetId: text("asset_id")
|
|
262
|
+
.notNull()
|
|
263
|
+
.references(() => mediaAssets.id, { onDelete: "cascade" }),
|
|
264
|
+
eventId: text("event_id")
|
|
265
|
+
.notNull()
|
|
266
|
+
.references(() => mediaEvents.id, { onDelete: "cascade" }),
|
|
267
|
+
feedbackType: text("feedback_type").notNull(), // correct | incorrect | boundary_edit | missed
|
|
268
|
+
originalStartTime: real("original_start_time"),
|
|
269
|
+
originalEndTime: real("original_end_time"),
|
|
270
|
+
correctedStartTime: real("corrected_start_time"),
|
|
271
|
+
correctedEndTime: real("corrected_end_time"),
|
|
272
|
+
notes: text("notes"),
|
|
273
|
+
createdAt: integer("created_at").notNull(),
|
|
274
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
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 { conversations } from "./conversations.js";
|
|
5
|
+
|
|
6
|
+
export const contacts = sqliteTable("contacts", {
|
|
7
|
+
id: text("id").primaryKey(),
|
|
8
|
+
displayName: text("display_name").notNull(),
|
|
9
|
+
notes: text("notes"),
|
|
10
|
+
lastInteraction: integer("last_interaction"), // epoch ms
|
|
11
|
+
interactionCount: integer("interaction_count").notNull().default(0),
|
|
12
|
+
createdAt: integer("created_at").notNull(),
|
|
13
|
+
updatedAt: integer("updated_at").notNull(),
|
|
14
|
+
role: text("role").notNull().default("contact"), // 'guardian' | 'contact'
|
|
15
|
+
principalId: text("principal_id"), // internal auth principal (nullable)
|
|
16
|
+
assistantId: text("assistant_id"), // which assistant this guardian is for (nullable, daemon default is DAEMON_INTERNAL_ASSISTANT_ID)
|
|
17
|
+
contactType: text("contact_type").notNull().default("human"), // 'human' | 'assistant'
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const contactChannels = sqliteTable(
|
|
21
|
+
"contact_channels",
|
|
22
|
+
{
|
|
23
|
+
id: text("id").primaryKey(),
|
|
24
|
+
contactId: text("contact_id")
|
|
25
|
+
.notNull()
|
|
26
|
+
.references(() => contacts.id, { onDelete: "cascade" }),
|
|
27
|
+
type: text("type").notNull(), // 'email', 'slack', 'whatsapp', 'phone', etc.
|
|
28
|
+
address: text("address").notNull(), // the actual identifier on that channel
|
|
29
|
+
isPrimary: integer("is_primary", { mode: "boolean" })
|
|
30
|
+
.notNull()
|
|
31
|
+
.default(false),
|
|
32
|
+
externalUserId: text("external_user_id"), // channel-native user ID (e.g., Telegram numeric ID, E.164 phone)
|
|
33
|
+
externalChatId: text("external_chat_id"), // delivery/notification routing address (e.g., Telegram chat ID)
|
|
34
|
+
status: text("status").notNull().default("unverified"), // 'active' | 'pending' | 'revoked' | 'blocked' | 'unverified'
|
|
35
|
+
policy: text("policy").notNull().default("allow"), // 'allow' | 'deny' | 'escalate'
|
|
36
|
+
verifiedAt: integer("verified_at"), // epoch ms
|
|
37
|
+
verifiedVia: text("verified_via"), // 'challenge' | 'invite' | 'bootstrap' | etc.
|
|
38
|
+
inviteId: text("invite_id"), // reference to invite that onboarded
|
|
39
|
+
revokedReason: text("revoked_reason"),
|
|
40
|
+
blockedReason: text("blocked_reason"),
|
|
41
|
+
lastSeenAt: integer("last_seen_at"), // epoch ms
|
|
42
|
+
updatedAt: integer("updated_at"), // epoch ms
|
|
43
|
+
createdAt: integer("created_at").notNull(),
|
|
44
|
+
},
|
|
45
|
+
(table) => [
|
|
46
|
+
index("idx_contact_channels_type_ext_user").on(
|
|
47
|
+
table.type,
|
|
48
|
+
table.externalUserId,
|
|
49
|
+
),
|
|
50
|
+
index("idx_contact_channels_type_ext_chat").on(
|
|
51
|
+
table.type,
|
|
52
|
+
table.externalChatId,
|
|
53
|
+
),
|
|
54
|
+
],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
export const assistantContactMetadata = sqliteTable(
|
|
58
|
+
"assistant_contact_metadata",
|
|
59
|
+
{
|
|
60
|
+
contactId: text("contact_id")
|
|
61
|
+
.primaryKey()
|
|
62
|
+
.references(() => contacts.id, { onDelete: "cascade" }),
|
|
63
|
+
species: text("species").notNull(), // 'vellum' | 'openclaw'
|
|
64
|
+
metadata: text("metadata"), // JSON blob for species-specific fields
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
export const assistantIngressInvites = sqliteTable(
|
|
69
|
+
"assistant_ingress_invites",
|
|
70
|
+
{
|
|
71
|
+
id: text("id").primaryKey(),
|
|
72
|
+
assistantId: text("assistant_id")
|
|
73
|
+
.notNull()
|
|
74
|
+
.default(DAEMON_INTERNAL_ASSISTANT_ID),
|
|
75
|
+
sourceChannel: text("source_channel").notNull(),
|
|
76
|
+
tokenHash: text("token_hash").notNull(),
|
|
77
|
+
createdBySessionId: text("created_by_session_id"),
|
|
78
|
+
note: text("note"),
|
|
79
|
+
maxUses: integer("max_uses").notNull().default(1),
|
|
80
|
+
useCount: integer("use_count").notNull().default(0),
|
|
81
|
+
expiresAt: integer("expires_at").notNull(),
|
|
82
|
+
status: text("status").notNull().default("active"),
|
|
83
|
+
redeemedByExternalUserId: text("redeemed_by_external_user_id"),
|
|
84
|
+
redeemedByExternalChatId: text("redeemed_by_external_chat_id"),
|
|
85
|
+
redeemedAt: integer("redeemed_at"),
|
|
86
|
+
// Voice invite fields (nullable — non-voice invites leave these NULL)
|
|
87
|
+
expectedExternalUserId: text("expected_external_user_id"),
|
|
88
|
+
voiceCodeHash: text("voice_code_hash"),
|
|
89
|
+
voiceCodeDigits: integer("voice_code_digits"),
|
|
90
|
+
// Display metadata for personalized voice prompts (nullable — non-voice invites leave these NULL)
|
|
91
|
+
friendName: text("friend_name"),
|
|
92
|
+
guardianName: text("guardian_name"),
|
|
93
|
+
createdAt: integer("created_at").notNull(),
|
|
94
|
+
updatedAt: integer("updated_at").notNull(),
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
export const assistantInboxThreadState = sqliteTable(
|
|
99
|
+
"assistant_inbox_thread_state",
|
|
100
|
+
{
|
|
101
|
+
conversationId: text("conversation_id")
|
|
102
|
+
.primaryKey()
|
|
103
|
+
.references(() => conversations.id, { onDelete: "cascade" }),
|
|
104
|
+
assistantId: text("assistant_id")
|
|
105
|
+
.notNull()
|
|
106
|
+
.default(DAEMON_INTERNAL_ASSISTANT_ID),
|
|
107
|
+
sourceChannel: text("source_channel").notNull(),
|
|
108
|
+
externalChatId: text("external_chat_id").notNull(),
|
|
109
|
+
externalUserId: text("external_user_id"),
|
|
110
|
+
displayName: text("display_name"),
|
|
111
|
+
username: text("username"),
|
|
112
|
+
lastInboundAt: integer("last_inbound_at"),
|
|
113
|
+
lastOutboundAt: integer("last_outbound_at"),
|
|
114
|
+
lastMessageAt: integer("last_message_at"),
|
|
115
|
+
unreadCount: integer("unread_count").notNull().default(0),
|
|
116
|
+
pendingEscalationCount: integer("pending_escalation_count")
|
|
117
|
+
.notNull()
|
|
118
|
+
.default(0),
|
|
119
|
+
hasPendingEscalation: integer("has_pending_escalation")
|
|
120
|
+
.notNull()
|
|
121
|
+
.default(0),
|
|
122
|
+
createdAt: integer("created_at").notNull(),
|
|
123
|
+
updatedAt: integer("updated_at").notNull(),
|
|
124
|
+
},
|
|
125
|
+
);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
index,
|
|
3
|
+
integer,
|
|
4
|
+
real,
|
|
5
|
+
sqliteTable,
|
|
6
|
+
text,
|
|
7
|
+
} from "drizzle-orm/sqlite-core";
|
|
8
|
+
|
|
9
|
+
export const conversations = sqliteTable(
|
|
10
|
+
"conversations",
|
|
11
|
+
{
|
|
12
|
+
id: text("id").primaryKey(),
|
|
13
|
+
title: text("title"),
|
|
14
|
+
createdAt: integer("created_at").notNull(),
|
|
15
|
+
updatedAt: integer("updated_at").notNull(),
|
|
16
|
+
totalInputTokens: integer("total_input_tokens").notNull().default(0),
|
|
17
|
+
totalOutputTokens: integer("total_output_tokens").notNull().default(0),
|
|
18
|
+
totalEstimatedCost: real("total_estimated_cost").notNull().default(0),
|
|
19
|
+
contextSummary: text("context_summary"),
|
|
20
|
+
contextCompactedMessageCount: integer("context_compacted_message_count")
|
|
21
|
+
.notNull()
|
|
22
|
+
.default(0),
|
|
23
|
+
contextCompactedAt: integer("context_compacted_at"),
|
|
24
|
+
threadType: text("thread_type").notNull().default("standard"),
|
|
25
|
+
source: text("source").notNull().default("user"),
|
|
26
|
+
memoryScopeId: text("memory_scope_id").notNull().default("default"),
|
|
27
|
+
originChannel: text("origin_channel"),
|
|
28
|
+
originInterface: text("origin_interface"),
|
|
29
|
+
isAutoTitle: integer("is_auto_title").notNull().default(1),
|
|
30
|
+
scheduleJobId: text("schedule_job_id"),
|
|
31
|
+
},
|
|
32
|
+
(table) => [
|
|
33
|
+
index("idx_conversations_updated_at").on(table.updatedAt),
|
|
34
|
+
index("idx_conversations_thread_type").on(table.threadType),
|
|
35
|
+
],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export const messages = sqliteTable(
|
|
39
|
+
"messages",
|
|
40
|
+
{
|
|
41
|
+
id: text("id").primaryKey(),
|
|
42
|
+
conversationId: text("conversation_id")
|
|
43
|
+
.notNull()
|
|
44
|
+
.references(() => conversations.id, { onDelete: "cascade" }),
|
|
45
|
+
role: text("role").notNull(),
|
|
46
|
+
content: text("content").notNull(),
|
|
47
|
+
createdAt: integer("created_at").notNull(),
|
|
48
|
+
metadata: text("metadata"),
|
|
49
|
+
},
|
|
50
|
+
(table) => [index("idx_messages_conversation_id").on(table.conversationId)],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
export const toolInvocations = sqliteTable(
|
|
54
|
+
"tool_invocations",
|
|
55
|
+
{
|
|
56
|
+
id: text("id").primaryKey(),
|
|
57
|
+
conversationId: text("conversation_id")
|
|
58
|
+
.notNull()
|
|
59
|
+
.references(() => conversations.id),
|
|
60
|
+
toolName: text("tool_name").notNull(),
|
|
61
|
+
input: text("input").notNull(),
|
|
62
|
+
result: text("result").notNull(),
|
|
63
|
+
decision: text("decision").notNull(),
|
|
64
|
+
riskLevel: text("risk_level").notNull(),
|
|
65
|
+
durationMs: integer("duration_ms").notNull(),
|
|
66
|
+
createdAt: integer("created_at").notNull(),
|
|
67
|
+
},
|
|
68
|
+
(table) => [
|
|
69
|
+
index("idx_tool_invocations_conversation_id").on(table.conversationId),
|
|
70
|
+
],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
export const conversationKeys = sqliteTable("conversation_keys", {
|
|
74
|
+
id: text("id").primaryKey(),
|
|
75
|
+
conversationKey: text("conversation_key").notNull(),
|
|
76
|
+
conversationId: text("conversation_id")
|
|
77
|
+
.notNull()
|
|
78
|
+
.references(() => conversations.id, { onDelete: "cascade" }),
|
|
79
|
+
createdAt: integer("created_at").notNull(),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
export const attachments = sqliteTable("attachments", {
|
|
83
|
+
id: text("id").primaryKey(),
|
|
84
|
+
originalFilename: text("original_filename").notNull(),
|
|
85
|
+
mimeType: text("mime_type").notNull(),
|
|
86
|
+
sizeBytes: integer("size_bytes").notNull(),
|
|
87
|
+
kind: text("kind").notNull(),
|
|
88
|
+
dataBase64: text("data_base64").notNull(),
|
|
89
|
+
contentHash: text("content_hash"),
|
|
90
|
+
thumbnailBase64: text("thumbnail_base64"),
|
|
91
|
+
createdAt: integer("created_at").notNull(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const messageAttachments = sqliteTable("message_attachments", {
|
|
95
|
+
id: text("id").primaryKey(),
|
|
96
|
+
messageId: text("message_id")
|
|
97
|
+
.notNull()
|
|
98
|
+
.references(() => messages.id, { onDelete: "cascade" }),
|
|
99
|
+
attachmentId: text("attachment_id")
|
|
100
|
+
.notNull()
|
|
101
|
+
.references(() => attachments.id, { onDelete: "cascade" }),
|
|
102
|
+
position: integer("position").notNull().default(0),
|
|
103
|
+
createdAt: integer("created_at").notNull(),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
export const channelInboundEvents = sqliteTable("channel_inbound_events", {
|
|
107
|
+
id: text("id").primaryKey(),
|
|
108
|
+
sourceChannel: text("source_channel").notNull(),
|
|
109
|
+
externalChatId: text("external_chat_id").notNull(),
|
|
110
|
+
externalMessageId: text("external_message_id").notNull(),
|
|
111
|
+
sourceMessageId: text("source_message_id"),
|
|
112
|
+
conversationId: text("conversation_id")
|
|
113
|
+
.notNull()
|
|
114
|
+
.references(() => conversations.id, { onDelete: "cascade" }),
|
|
115
|
+
messageId: text("message_id").references(() => messages.id, {
|
|
116
|
+
onDelete: "cascade",
|
|
117
|
+
}),
|
|
118
|
+
deliveryStatus: text("delivery_status").notNull().default("pending"),
|
|
119
|
+
processingStatus: text("processing_status").notNull().default("pending"),
|
|
120
|
+
processingAttempts: integer("processing_attempts").notNull().default(0),
|
|
121
|
+
lastProcessingError: text("last_processing_error"),
|
|
122
|
+
retryAfter: integer("retry_after"),
|
|
123
|
+
rawPayload: text("raw_payload"),
|
|
124
|
+
deliveredSegmentCount: integer("delivered_segment_count")
|
|
125
|
+
.notNull()
|
|
126
|
+
.default(0),
|
|
127
|
+
createdAt: integer("created_at").notNull(),
|
|
128
|
+
updatedAt: integer("updated_at").notNull(),
|
|
129
|
+
});
|