@vellumai/assistant 0.4.33 → 0.4.34
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/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -3
- package/src/__tests__/actor-token-service.test.ts +4 -11
- package/src/__tests__/approval-primitive.test.ts +0 -45
- package/src/__tests__/assistant-id-boundary-guard.test.ts +150 -0
- package/src/__tests__/callback-handoff-copy.test.ts +0 -1
- package/src/__tests__/channel-approval-routes.test.ts +5 -45
- package/src/__tests__/channel-guardian.test.ts +122 -345
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +4 -3
- package/src/__tests__/contacts-tools.test.ts +4 -5
- package/src/__tests__/conversation-attention-store.test.ts +2 -65
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -2
- package/src/__tests__/conversation-pairing.test.ts +0 -1
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -2
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -7
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -74
- package/src/__tests__/guardian-action-late-reply.test.ts +1 -8
- package/src/__tests__/guardian-grant-minting.test.ts +0 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -3
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -3
- package/src/__tests__/non-member-access-request.test.ts +0 -7
- package/src/__tests__/notification-broadcaster.test.ts +1 -2
- package/src/__tests__/notification-decision-fallback.test.ts +0 -2
- package/src/__tests__/notification-decision-strategy.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +11 -83
- package/src/__tests__/scoped-approval-grants.test.ts +9 -40
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -36
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/send-notification-tool.test.ts +0 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -4
- package/src/__tests__/thread-seed-composer.test.ts +0 -1
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -4
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -5
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -17
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -13
- package/src/__tests__/trusted-contact-verification.test.ts +3 -15
- package/src/__tests__/twilio-routes.test.ts +2 -2
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -37
- package/src/approvals/approval-primitive.ts +0 -15
- package/src/approvals/guardian-decision-primitive.ts +0 -3
- package/src/approvals/guardian-request-resolvers.ts +0 -5
- package/src/calls/call-domain.ts +0 -3
- package/src/calls/call-store.ts +0 -3
- package/src/calls/guardian-action-sweep.ts +2 -1
- package/src/calls/guardian-dispatch.ts +1 -2
- package/src/calls/relay-access-wait.ts +0 -4
- package/src/calls/relay-server.ts +3 -11
- package/src/calls/relay-setup-router.ts +1 -2
- package/src/calls/relay-verification.ts +0 -1
- package/src/calls/twilio-routes.ts +0 -3
- package/src/calls/types.ts +0 -1
- package/src/calls/voice-session-bridge.ts +0 -1
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -1
- package/src/contacts/contact-store.ts +13 -88
- package/src/contacts/contacts-write.ts +3 -11
- package/src/contacts/types.ts +0 -1
- package/src/daemon/handlers/config-channels.ts +16 -42
- package/src/daemon/handlers/config-inbox.ts +6 -6
- package/src/daemon/handlers/contacts.ts +3 -11
- package/src/daemon/handlers/index.ts +0 -2
- package/src/daemon/session-process.ts +0 -4
- package/src/memory/conversation-attention-store.ts +4 -19
- package/src/memory/conversation-crud.ts +0 -2
- package/src/memory/db-init.ts +4 -0
- package/src/memory/guardian-action-store.ts +0 -12
- package/src/memory/guardian-approvals.ts +35 -80
- package/src/memory/guardian-rate-limits.ts +1 -14
- package/src/memory/guardian-verification.ts +6 -34
- package/src/memory/invite-store.ts +5 -14
- package/src/memory/migrations/134-contacts-notes-column.ts +64 -45
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +263 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/migrations/registry.ts +14 -1
- package/src/memory/schema/calls.ts +0 -7
- package/src/memory/schema/contacts.ts +0 -8
- package/src/memory/schema/guardian.ts +0 -5
- package/src/memory/schema/infrastructure.ts +0 -2
- package/src/memory/schema/notifications.ts +3 -17
- package/src/memory/scoped-approval-grants.ts +2 -24
- package/src/notifications/adapters/sms.ts +2 -1
- package/src/notifications/broadcaster.ts +1 -6
- package/src/notifications/decision-engine.ts +3 -4
- package/src/notifications/deliveries-store.ts +0 -4
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/deterministic-checks.ts +1 -6
- package/src/notifications/emit-signal.ts +4 -11
- package/src/notifications/events-store.ts +7 -17
- package/src/notifications/preference-summary.ts +2 -2
- package/src/notifications/preferences-store.ts +2 -9
- package/src/notifications/signal.ts +0 -1
- package/src/notifications/thread-candidates.ts +1 -11
- package/src/notifications/types.ts +0 -3
- package/src/runtime/access-request-helper.ts +3 -10
- package/src/runtime/actor-refresh-token-store.ts +0 -6
- package/src/runtime/actor-token-store.ts +3 -16
- package/src/runtime/actor-trust-resolver.ts +1 -4
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -9
- package/src/runtime/auth/credential-service.ts +1 -15
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/channel-guardian-service.ts +15 -46
- package/src/runtime/channel-invite-transport.ts +8 -0
- package/src/runtime/channel-invite-transports/email.ts +4 -0
- package/src/runtime/channel-invite-transports/slack.ts +6 -0
- package/src/runtime/channel-invite-transports/sms.ts +4 -0
- package/src/runtime/channel-invite-transports/telegram.ts +6 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +0 -1
- package/src/runtime/guardian-action-followup-executor.ts +3 -2
- package/src/runtime/guardian-action-grant-minter.ts +0 -1
- package/src/runtime/guardian-outbound-actions.ts +2 -12
- package/src/runtime/guardian-vellum-migration.ts +2 -3
- package/src/runtime/http-server.ts +0 -1
- package/src/runtime/invite-redemption-service.ts +1 -14
- package/src/runtime/local-actor-identity.ts +2 -5
- package/src/runtime/routes/access-request-decision.ts +0 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +0 -9
- package/src/runtime/routes/channel-readiness-routes.ts +29 -18
- package/src/runtime/routes/contact-routes.ts +15 -40
- package/src/runtime/routes/conversation-attention-routes.ts +0 -2
- package/src/runtime/routes/global-search-routes.ts +0 -2
- package/src/runtime/routes/guardian-bootstrap-routes.ts +5 -6
- package/src/runtime/routes/guardian-expiry-sweep.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +0 -3
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -43
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +1 -4
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +0 -1
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +0 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +3 -7
- package/src/runtime/routes/pairing-routes.ts +4 -4
- package/src/runtime/tool-grant-request-helper.ts +0 -1
- package/src/tools/browser/browser-manager.ts +22 -21
- package/src/tools/browser/runtime-check.ts +111 -6
- package/src/tools/calls/call-start.ts +1 -3
- package/src/tools/followups/followup_create.ts +1 -2
- package/src/tools/tool-approval-handler.ts +0 -2
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
import { and, count, desc, eq, gt, lte } from "drizzle-orm";
|
|
10
10
|
import { v4 as uuid } from "uuid";
|
|
11
11
|
|
|
12
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
13
12
|
import { getDb } from "./db.js";
|
|
14
13
|
import { channelGuardianApprovalRequests } from "./schema.js";
|
|
15
14
|
|
|
@@ -29,7 +28,6 @@ export interface GuardianApprovalRequest {
|
|
|
29
28
|
runId: string;
|
|
30
29
|
requestId: string;
|
|
31
30
|
conversationId: string;
|
|
32
|
-
assistantId: string;
|
|
33
31
|
channel: string;
|
|
34
32
|
requesterExternalUserId: string;
|
|
35
33
|
requesterChatId: string;
|
|
@@ -57,7 +55,6 @@ function rowToApprovalRequest(
|
|
|
57
55
|
runId: row.runId,
|
|
58
56
|
requestId: row.requestId ?? row.runId,
|
|
59
57
|
conversationId: row.conversationId,
|
|
60
|
-
assistantId: row.assistantId,
|
|
61
58
|
channel: row.channel,
|
|
62
59
|
requesterExternalUserId: row.requesterExternalUserId,
|
|
63
60
|
requesterChatId: row.requesterChatId,
|
|
@@ -88,7 +85,6 @@ export function createApprovalRequest(params: {
|
|
|
88
85
|
runId: string;
|
|
89
86
|
requestId?: string;
|
|
90
87
|
conversationId: string;
|
|
91
|
-
assistantId?: string;
|
|
92
88
|
channel: string;
|
|
93
89
|
requesterExternalUserId: string;
|
|
94
90
|
requesterChatId: string;
|
|
@@ -108,7 +104,6 @@ export function createApprovalRequest(params: {
|
|
|
108
104
|
runId: params.runId,
|
|
109
105
|
requestId: params.requestId ?? null,
|
|
110
106
|
conversationId: params.conversationId,
|
|
111
|
-
assistantId: params.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
112
107
|
channel: params.channel,
|
|
113
108
|
requesterExternalUserId: params.requesterExternalUserId,
|
|
114
109
|
requesterChatId: params.requesterChatId,
|
|
@@ -172,34 +167,25 @@ export function getUnresolvedApprovalForRequest(
|
|
|
172
167
|
/**
|
|
173
168
|
* Find a pending guardian approval request by the guardian's chat ID.
|
|
174
169
|
* Used when the guardian sends a decision from their chat.
|
|
175
|
-
*
|
|
176
|
-
* When `assistantId` is provided, the lookup is scoped to that assistant,
|
|
177
|
-
* preventing cross-assistant approval consumption in shared guardian chats.
|
|
178
170
|
*/
|
|
179
171
|
export function getPendingApprovalByGuardianChat(
|
|
180
172
|
channel: string,
|
|
181
173
|
guardianChatId: string,
|
|
182
|
-
assistantId?: string,
|
|
183
174
|
): GuardianApprovalRequest | null {
|
|
184
175
|
const db = getDb();
|
|
185
176
|
const now = Date.now();
|
|
186
177
|
|
|
187
|
-
const conditions = [
|
|
188
|
-
eq(channelGuardianApprovalRequests.channel, channel),
|
|
189
|
-
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
190
|
-
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
191
|
-
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
192
|
-
];
|
|
193
|
-
if (assistantId) {
|
|
194
|
-
conditions.push(
|
|
195
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
178
|
const row = db
|
|
200
179
|
.select()
|
|
201
180
|
.from(channelGuardianApprovalRequests)
|
|
202
|
-
.where(
|
|
181
|
+
.where(
|
|
182
|
+
and(
|
|
183
|
+
eq(channelGuardianApprovalRequests.channel, channel),
|
|
184
|
+
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
185
|
+
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
186
|
+
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
187
|
+
),
|
|
188
|
+
)
|
|
203
189
|
.orderBy(desc(channelGuardianApprovalRequests.createdAt))
|
|
204
190
|
.get();
|
|
205
191
|
|
|
@@ -216,28 +202,22 @@ export function getPendingApprovalByRequestAndGuardianChat(
|
|
|
216
202
|
requestId: string,
|
|
217
203
|
channel: string,
|
|
218
204
|
guardianChatId: string,
|
|
219
|
-
assistantId?: string,
|
|
220
205
|
): GuardianApprovalRequest | null {
|
|
221
206
|
const db = getDb();
|
|
222
207
|
const now = Date.now();
|
|
223
208
|
|
|
224
|
-
const conditions = [
|
|
225
|
-
eq(channelGuardianApprovalRequests.requestId, requestId),
|
|
226
|
-
eq(channelGuardianApprovalRequests.channel, channel),
|
|
227
|
-
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
228
|
-
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
229
|
-
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
230
|
-
];
|
|
231
|
-
if (assistantId) {
|
|
232
|
-
conditions.push(
|
|
233
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
209
|
const row = db
|
|
238
210
|
.select()
|
|
239
211
|
.from(channelGuardianApprovalRequests)
|
|
240
|
-
.where(
|
|
212
|
+
.where(
|
|
213
|
+
and(
|
|
214
|
+
eq(channelGuardianApprovalRequests.requestId, requestId),
|
|
215
|
+
eq(channelGuardianApprovalRequests.channel, channel),
|
|
216
|
+
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
217
|
+
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
218
|
+
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
219
|
+
),
|
|
220
|
+
)
|
|
241
221
|
.get();
|
|
242
222
|
|
|
243
223
|
return row ? rowToApprovalRequest(row) : null;
|
|
@@ -247,34 +227,25 @@ export function getPendingApprovalByRequestAndGuardianChat(
|
|
|
247
227
|
* Return all pending (non-expired) guardian approval requests for a given
|
|
248
228
|
* guardian chat and channel. Used to detect ambiguity when a guardian sends
|
|
249
229
|
* a plain-text decision while multiple approvals are pending.
|
|
250
|
-
*
|
|
251
|
-
* When `assistantId` is provided, the results are scoped to that assistant
|
|
252
|
-
* to prevent cross-assistant approval consumption.
|
|
253
230
|
*/
|
|
254
231
|
export function getAllPendingApprovalsByGuardianChat(
|
|
255
232
|
channel: string,
|
|
256
233
|
guardianChatId: string,
|
|
257
|
-
assistantId?: string,
|
|
258
234
|
): GuardianApprovalRequest[] {
|
|
259
235
|
const db = getDb();
|
|
260
236
|
const now = Date.now();
|
|
261
237
|
|
|
262
|
-
const conditions = [
|
|
263
|
-
eq(channelGuardianApprovalRequests.channel, channel),
|
|
264
|
-
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
265
|
-
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
266
|
-
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
267
|
-
];
|
|
268
|
-
if (assistantId) {
|
|
269
|
-
conditions.push(
|
|
270
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
238
|
const rows = db
|
|
275
239
|
.select()
|
|
276
240
|
.from(channelGuardianApprovalRequests)
|
|
277
|
-
.where(
|
|
241
|
+
.where(
|
|
242
|
+
and(
|
|
243
|
+
eq(channelGuardianApprovalRequests.channel, channel),
|
|
244
|
+
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
245
|
+
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
246
|
+
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
247
|
+
),
|
|
248
|
+
)
|
|
278
249
|
.orderBy(desc(channelGuardianApprovalRequests.createdAt))
|
|
279
250
|
.all();
|
|
280
251
|
|
|
@@ -326,11 +297,10 @@ export function updateApprovalDecision(
|
|
|
326
297
|
// ---------------------------------------------------------------------------
|
|
327
298
|
|
|
328
299
|
/**
|
|
329
|
-
* List approval requests filtered by
|
|
330
|
-
*
|
|
300
|
+
* List approval requests optionally filtered by channel, conversation, and
|
|
301
|
+
* status. Returns a paginated list of escalations.
|
|
331
302
|
*/
|
|
332
303
|
export function listPendingApprovalRequests(params: {
|
|
333
|
-
assistantId?: string;
|
|
334
304
|
channel?: string;
|
|
335
305
|
conversationId?: string;
|
|
336
306
|
status?: ApprovalRequestStatus;
|
|
@@ -339,12 +309,7 @@ export function listPendingApprovalRequests(params: {
|
|
|
339
309
|
}): GuardianApprovalRequest[] {
|
|
340
310
|
const db = getDb();
|
|
341
311
|
|
|
342
|
-
const conditions = [
|
|
343
|
-
eq(
|
|
344
|
-
channelGuardianApprovalRequests.assistantId,
|
|
345
|
-
params.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
346
|
-
),
|
|
347
|
-
];
|
|
312
|
+
const conditions: ReturnType<typeof eq>[] = [];
|
|
348
313
|
if (params.channel) {
|
|
349
314
|
conditions.push(
|
|
350
315
|
eq(channelGuardianApprovalRequests.channel, params.channel),
|
|
@@ -447,26 +412,18 @@ export function resolveApprovalRequest(
|
|
|
447
412
|
* Count pending approval requests for a given conversation.
|
|
448
413
|
* Used by thread state projection to compute `pending_escalation_count`.
|
|
449
414
|
*/
|
|
450
|
-
export function countPendingByConversation(
|
|
451
|
-
conversationId: string,
|
|
452
|
-
assistantId?: string,
|
|
453
|
-
): number {
|
|
415
|
+
export function countPendingByConversation(conversationId: string): number {
|
|
454
416
|
const db = getDb();
|
|
455
417
|
|
|
456
|
-
const conditions = [
|
|
457
|
-
eq(channelGuardianApprovalRequests.conversationId, conversationId),
|
|
458
|
-
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
459
|
-
];
|
|
460
|
-
if (assistantId) {
|
|
461
|
-
conditions.push(
|
|
462
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
463
|
-
);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
418
|
const result = db
|
|
467
419
|
.select({ count: count() })
|
|
468
420
|
.from(channelGuardianApprovalRequests)
|
|
469
|
-
.where(
|
|
421
|
+
.where(
|
|
422
|
+
and(
|
|
423
|
+
eq(channelGuardianApprovalRequests.conversationId, conversationId),
|
|
424
|
+
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
425
|
+
),
|
|
426
|
+
)
|
|
470
427
|
.get();
|
|
471
428
|
|
|
472
429
|
return result?.count ?? 0;
|
|
@@ -478,7 +435,6 @@ export function countPendingByConversation(
|
|
|
478
435
|
* Retained for existing test fixtures that check legacy approval dedup.
|
|
479
436
|
*/
|
|
480
437
|
export function findPendingAccessRequestForRequester(
|
|
481
|
-
assistantId: string,
|
|
482
438
|
channel: string,
|
|
483
439
|
requesterExternalUserId: string,
|
|
484
440
|
toolName: string,
|
|
@@ -491,7 +447,6 @@ export function findPendingAccessRequestForRequester(
|
|
|
491
447
|
.from(channelGuardianApprovalRequests)
|
|
492
448
|
.where(
|
|
493
449
|
and(
|
|
494
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
495
450
|
eq(channelGuardianApprovalRequests.channel, channel),
|
|
496
451
|
eq(
|
|
497
452
|
channelGuardianApprovalRequests.requesterExternalUserId,
|
|
@@ -17,7 +17,6 @@ import { channelGuardianRateLimits } from "./schema.js";
|
|
|
17
17
|
|
|
18
18
|
export interface VerificationRateLimit {
|
|
19
19
|
id: string;
|
|
20
|
-
assistantId: string;
|
|
21
20
|
channel: string;
|
|
22
21
|
actorExternalUserId: string;
|
|
23
22
|
actorChatId: string;
|
|
@@ -49,7 +48,6 @@ function rowToRateLimit(
|
|
|
49
48
|
const timestamps = parseTimestamps(row.attemptTimestampsJson);
|
|
50
49
|
return {
|
|
51
50
|
id: row.id,
|
|
52
|
-
assistantId: row.assistantId,
|
|
53
51
|
channel: row.channel,
|
|
54
52
|
actorExternalUserId: row.actorExternalUserId,
|
|
55
53
|
actorChatId: row.actorChatId,
|
|
@@ -69,7 +67,6 @@ function rowToRateLimit(
|
|
|
69
67
|
* Get the rate-limit record for a given actor on a specific channel.
|
|
70
68
|
*/
|
|
71
69
|
export function getRateLimit(
|
|
72
|
-
assistantId: string,
|
|
73
70
|
channel: string,
|
|
74
71
|
actorExternalUserId: string,
|
|
75
72
|
actorChatId: string,
|
|
@@ -80,7 +77,6 @@ export function getRateLimit(
|
|
|
80
77
|
.from(channelGuardianRateLimits)
|
|
81
78
|
.where(
|
|
82
79
|
and(
|
|
83
|
-
eq(channelGuardianRateLimits.assistantId, assistantId),
|
|
84
80
|
eq(channelGuardianRateLimits.channel, channel),
|
|
85
81
|
eq(channelGuardianRateLimits.actorExternalUserId, actorExternalUserId),
|
|
86
82
|
eq(channelGuardianRateLimits.actorChatId, actorChatId),
|
|
@@ -101,7 +97,6 @@ export function getRateLimit(
|
|
|
101
97
|
* accumulate indefinitely.
|
|
102
98
|
*/
|
|
103
99
|
export function recordInvalidAttempt(
|
|
104
|
-
assistantId: string,
|
|
105
100
|
channel: string,
|
|
106
101
|
actorExternalUserId: string,
|
|
107
102
|
actorChatId: string,
|
|
@@ -113,12 +108,7 @@ export function recordInvalidAttempt(
|
|
|
113
108
|
const now = Date.now();
|
|
114
109
|
const cutoff = now - windowMs;
|
|
115
110
|
|
|
116
|
-
const existing = getRateLimit(
|
|
117
|
-
assistantId,
|
|
118
|
-
channel,
|
|
119
|
-
actorExternalUserId,
|
|
120
|
-
actorChatId,
|
|
121
|
-
);
|
|
111
|
+
const existing = getRateLimit(channel, actorExternalUserId, actorChatId);
|
|
122
112
|
|
|
123
113
|
if (existing) {
|
|
124
114
|
// Keep only timestamps within the sliding window, then add the new one
|
|
@@ -158,7 +148,6 @@ export function recordInvalidAttempt(
|
|
|
158
148
|
const lockedUntil = 1 >= maxAttempts ? now + lockoutMs : null;
|
|
159
149
|
const row = {
|
|
160
150
|
id,
|
|
161
|
-
assistantId,
|
|
162
151
|
channel,
|
|
163
152
|
actorExternalUserId,
|
|
164
153
|
actorChatId,
|
|
@@ -181,7 +170,6 @@ export function recordInvalidAttempt(
|
|
|
181
170
|
* successful verification).
|
|
182
171
|
*/
|
|
183
172
|
export function resetRateLimit(
|
|
184
|
-
assistantId: string,
|
|
185
173
|
channel: string,
|
|
186
174
|
actorExternalUserId: string,
|
|
187
175
|
actorChatId: string,
|
|
@@ -197,7 +185,6 @@ export function resetRateLimit(
|
|
|
197
185
|
})
|
|
198
186
|
.where(
|
|
199
187
|
and(
|
|
200
|
-
eq(channelGuardianRateLimits.assistantId, assistantId),
|
|
201
188
|
eq(channelGuardianRateLimits.channel, channel),
|
|
202
189
|
eq(channelGuardianRateLimits.actorExternalUserId, actorExternalUserId),
|
|
203
190
|
eq(channelGuardianRateLimits.actorChatId, actorChatId),
|
|
@@ -30,7 +30,6 @@ export type VerificationPurpose = "guardian" | "trusted_contact";
|
|
|
30
30
|
|
|
31
31
|
export interface VerificationChallenge {
|
|
32
32
|
id: string;
|
|
33
|
-
assistantId: string;
|
|
34
33
|
channel: string;
|
|
35
34
|
challengeHash: string;
|
|
36
35
|
expiresAt: number;
|
|
@@ -68,7 +67,6 @@ function rowToChallenge(
|
|
|
68
67
|
): VerificationChallenge {
|
|
69
68
|
return {
|
|
70
69
|
id: row.id,
|
|
71
|
-
assistantId: row.assistantId,
|
|
72
70
|
channel: row.channel,
|
|
73
71
|
challengeHash: row.challengeHash,
|
|
74
72
|
expiresAt: row.expiresAt,
|
|
@@ -101,7 +99,6 @@ function rowToChallenge(
|
|
|
101
99
|
|
|
102
100
|
export function createChallenge(params: {
|
|
103
101
|
id: string;
|
|
104
|
-
assistantId: string;
|
|
105
102
|
channel: string;
|
|
106
103
|
challengeHash: string;
|
|
107
104
|
expiresAt: number;
|
|
@@ -110,16 +107,12 @@ export function createChallenge(params: {
|
|
|
110
107
|
const db = getDb();
|
|
111
108
|
const now = Date.now();
|
|
112
109
|
|
|
113
|
-
// Revoke any prior pending challenges for the same
|
|
110
|
+
// Revoke any prior pending challenges for the same channel
|
|
114
111
|
// to close the replay window — only the latest challenge should be valid.
|
|
115
112
|
db.update(channelGuardianVerificationChallenges)
|
|
116
113
|
.set({ status: "revoked", updatedAt: now })
|
|
117
114
|
.where(
|
|
118
115
|
and(
|
|
119
|
-
eq(
|
|
120
|
-
channelGuardianVerificationChallenges.assistantId,
|
|
121
|
-
params.assistantId,
|
|
122
|
-
),
|
|
123
116
|
eq(channelGuardianVerificationChallenges.channel, params.channel),
|
|
124
117
|
eq(channelGuardianVerificationChallenges.status, "pending"),
|
|
125
118
|
),
|
|
@@ -128,7 +121,6 @@ export function createChallenge(params: {
|
|
|
128
121
|
|
|
129
122
|
const row = {
|
|
130
123
|
id: params.id,
|
|
131
|
-
assistantId: params.assistantId,
|
|
132
124
|
channel: params.channel,
|
|
133
125
|
challengeHash: params.challengeHash,
|
|
134
126
|
expiresAt: params.expiresAt,
|
|
@@ -157,16 +149,12 @@ export function createChallenge(params: {
|
|
|
157
149
|
return rowToChallenge(row);
|
|
158
150
|
}
|
|
159
151
|
|
|
160
|
-
export function revokePendingChallenges(
|
|
161
|
-
assistantId: string,
|
|
162
|
-
channel: string,
|
|
163
|
-
): void {
|
|
152
|
+
export function revokePendingChallenges(channel: string): void {
|
|
164
153
|
const db = getDb();
|
|
165
154
|
db.update(channelGuardianVerificationChallenges)
|
|
166
155
|
.set({ status: "revoked", updatedAt: Date.now() })
|
|
167
156
|
.where(
|
|
168
157
|
and(
|
|
169
|
-
eq(channelGuardianVerificationChallenges.assistantId, assistantId),
|
|
170
158
|
eq(channelGuardianVerificationChallenges.channel, channel),
|
|
171
159
|
eq(channelGuardianVerificationChallenges.status, "pending"),
|
|
172
160
|
),
|
|
@@ -175,7 +163,6 @@ export function revokePendingChallenges(
|
|
|
175
163
|
}
|
|
176
164
|
|
|
177
165
|
export function findPendingChallengeByHash(
|
|
178
|
-
assistantId: string,
|
|
179
166
|
channel: string,
|
|
180
167
|
challengeHash: string,
|
|
181
168
|
): VerificationChallenge | null {
|
|
@@ -188,7 +175,6 @@ export function findPendingChallengeByHash(
|
|
|
188
175
|
.from(channelGuardianVerificationChallenges)
|
|
189
176
|
.where(
|
|
190
177
|
and(
|
|
191
|
-
eq(channelGuardianVerificationChallenges.assistantId, assistantId),
|
|
192
178
|
eq(channelGuardianVerificationChallenges.channel, channel),
|
|
193
179
|
eq(channelGuardianVerificationChallenges.challengeHash, challengeHash),
|
|
194
180
|
inArray(channelGuardianVerificationChallenges.status, [
|
|
@@ -205,7 +191,7 @@ export function findPendingChallengeByHash(
|
|
|
205
191
|
}
|
|
206
192
|
|
|
207
193
|
/**
|
|
208
|
-
* Find any pending inbound (non-expired) challenge for a given
|
|
194
|
+
* Find any pending inbound (non-expired) challenge for a given channel.
|
|
209
195
|
* Scoped to 'pending' status only — this is the inbound verification path used by
|
|
210
196
|
* the relay-server to gate incoming voice calls. Outbound session states
|
|
211
197
|
* (pending_bootstrap, awaiting_response) are excluded so that an active outbound
|
|
@@ -213,7 +199,6 @@ export function findPendingChallengeByHash(
|
|
|
213
199
|
* guardian verification flow.
|
|
214
200
|
*/
|
|
215
201
|
export function findPendingChallengeForChannel(
|
|
216
|
-
assistantId: string,
|
|
217
202
|
channel: string,
|
|
218
203
|
): VerificationChallenge | null {
|
|
219
204
|
const db = getDb();
|
|
@@ -224,7 +209,6 @@ export function findPendingChallengeForChannel(
|
|
|
224
209
|
.from(channelGuardianVerificationChallenges)
|
|
225
210
|
.where(
|
|
226
211
|
and(
|
|
227
|
-
eq(channelGuardianVerificationChallenges.assistantId, assistantId),
|
|
228
212
|
eq(channelGuardianVerificationChallenges.channel, channel),
|
|
229
213
|
eq(channelGuardianVerificationChallenges.status, "pending"),
|
|
230
214
|
gt(channelGuardianVerificationChallenges.expiresAt, now),
|
|
@@ -261,11 +245,10 @@ export function consumeChallenge(
|
|
|
261
245
|
/**
|
|
262
246
|
* Create an outbound verification session with expected-identity binding.
|
|
263
247
|
* Auto-revokes prior pending/awaiting_response sessions for the same
|
|
264
|
-
*
|
|
248
|
+
* channel to close the replay window.
|
|
265
249
|
*/
|
|
266
250
|
export function createVerificationSession(params: {
|
|
267
251
|
id: string;
|
|
268
|
-
assistantId: string;
|
|
269
252
|
channel: string;
|
|
270
253
|
challengeHash: string;
|
|
271
254
|
expiresAt: number;
|
|
@@ -284,15 +267,11 @@ export function createVerificationSession(params: {
|
|
|
284
267
|
const db = getDb();
|
|
285
268
|
const now = Date.now();
|
|
286
269
|
|
|
287
|
-
// Revoke any prior pending/awaiting_response sessions for the same
|
|
270
|
+
// Revoke any prior pending/awaiting_response sessions for the same channel
|
|
288
271
|
db.update(channelGuardianVerificationChallenges)
|
|
289
272
|
.set({ status: "revoked", updatedAt: now })
|
|
290
273
|
.where(
|
|
291
274
|
and(
|
|
292
|
-
eq(
|
|
293
|
-
channelGuardianVerificationChallenges.assistantId,
|
|
294
|
-
params.assistantId,
|
|
295
|
-
),
|
|
296
275
|
eq(channelGuardianVerificationChallenges.channel, params.channel),
|
|
297
276
|
inArray(channelGuardianVerificationChallenges.status, [
|
|
298
277
|
"pending",
|
|
@@ -305,7 +284,6 @@ export function createVerificationSession(params: {
|
|
|
305
284
|
|
|
306
285
|
const row = {
|
|
307
286
|
id: params.id,
|
|
308
|
-
assistantId: params.assistantId,
|
|
309
287
|
channel: params.channel,
|
|
310
288
|
challengeHash: params.challengeHash,
|
|
311
289
|
expiresAt: params.expiresAt,
|
|
@@ -336,10 +314,9 @@ export function createVerificationSession(params: {
|
|
|
336
314
|
|
|
337
315
|
/**
|
|
338
316
|
* Find the most recent pending_bootstrap or awaiting_response session
|
|
339
|
-
* for a given
|
|
317
|
+
* for a given channel.
|
|
340
318
|
*/
|
|
341
319
|
export function findActiveSession(
|
|
342
|
-
assistantId: string,
|
|
343
320
|
channel: string,
|
|
344
321
|
): VerificationChallenge | null {
|
|
345
322
|
const db = getDb();
|
|
@@ -350,7 +327,6 @@ export function findActiveSession(
|
|
|
350
327
|
.from(channelGuardianVerificationChallenges)
|
|
351
328
|
.where(
|
|
352
329
|
and(
|
|
353
|
-
eq(channelGuardianVerificationChallenges.assistantId, assistantId),
|
|
354
330
|
eq(channelGuardianVerificationChallenges.channel, channel),
|
|
355
331
|
inArray(channelGuardianVerificationChallenges.status, [
|
|
356
332
|
"pending_bootstrap",
|
|
@@ -370,7 +346,6 @@ export function findActiveSession(
|
|
|
370
346
|
* Used by the Telegram /start gv_<token> bootstrap flow.
|
|
371
347
|
*/
|
|
372
348
|
export function findSessionByBootstrapTokenHash(
|
|
373
|
-
assistantId: string,
|
|
374
349
|
channel: string,
|
|
375
350
|
tokenHash: string,
|
|
376
351
|
): VerificationChallenge | null {
|
|
@@ -382,7 +357,6 @@ export function findSessionByBootstrapTokenHash(
|
|
|
382
357
|
.from(channelGuardianVerificationChallenges)
|
|
383
358
|
.where(
|
|
384
359
|
and(
|
|
385
|
-
eq(channelGuardianVerificationChallenges.assistantId, assistantId),
|
|
386
360
|
eq(channelGuardianVerificationChallenges.channel, channel),
|
|
387
361
|
eq(channelGuardianVerificationChallenges.bootstrapTokenHash, tokenHash),
|
|
388
362
|
eq(channelGuardianVerificationChallenges.status, "pending_bootstrap"),
|
|
@@ -399,7 +373,6 @@ export function findSessionByBootstrapTokenHash(
|
|
|
399
373
|
* given identity fields with an active status.
|
|
400
374
|
*/
|
|
401
375
|
export function findSessionByIdentity(
|
|
402
|
-
assistantId: string,
|
|
403
376
|
channel: string,
|
|
404
377
|
externalUserId?: string,
|
|
405
378
|
chatId?: string,
|
|
@@ -415,7 +388,6 @@ export function findSessionByIdentity(
|
|
|
415
388
|
const now = Date.now();
|
|
416
389
|
|
|
417
390
|
const conditions = [
|
|
418
|
-
eq(channelGuardianVerificationChallenges.assistantId, assistantId),
|
|
419
391
|
eq(channelGuardianVerificationChallenges.channel, channel),
|
|
420
392
|
inArray(channelGuardianVerificationChallenges.status, [
|
|
421
393
|
"pending_bootstrap",
|
|
@@ -10,7 +10,6 @@ import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
|
10
10
|
|
|
11
11
|
import { and, desc, eq, gt } from "drizzle-orm";
|
|
12
12
|
|
|
13
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
14
13
|
import { getDb } from "./db.js";
|
|
15
14
|
import { assistantIngressInvites } from "./schema.js";
|
|
16
15
|
|
|
@@ -22,7 +21,6 @@ export type InviteStatus = "active" | "redeemed" | "revoked" | "expired";
|
|
|
22
21
|
|
|
23
22
|
export interface IngressInvite {
|
|
24
23
|
id: string;
|
|
25
|
-
assistantId: string;
|
|
26
24
|
sourceChannel: string;
|
|
27
25
|
tokenHash: string;
|
|
28
26
|
createdBySessionId: string | null;
|
|
@@ -71,7 +69,6 @@ function rowToInvite(
|
|
|
71
69
|
): IngressInvite {
|
|
72
70
|
return {
|
|
73
71
|
id: row.id,
|
|
74
|
-
assistantId: row.assistantId,
|
|
75
72
|
sourceChannel: row.sourceChannel,
|
|
76
73
|
tokenHash: row.tokenHash,
|
|
77
74
|
createdBySessionId: row.createdBySessionId,
|
|
@@ -99,7 +96,6 @@ function rowToInvite(
|
|
|
99
96
|
// ---------------------------------------------------------------------------
|
|
100
97
|
|
|
101
98
|
export function createInvite(params: {
|
|
102
|
-
assistantId?: string;
|
|
103
99
|
sourceChannel: string;
|
|
104
100
|
createdBySessionId?: string;
|
|
105
101
|
note?: string;
|
|
@@ -122,7 +118,6 @@ export function createInvite(params: {
|
|
|
122
118
|
|
|
123
119
|
const row = {
|
|
124
120
|
id,
|
|
125
|
-
assistantId: params.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
126
121
|
sourceChannel: params.sourceChannel,
|
|
127
122
|
tokenHash: tokenH,
|
|
128
123
|
createdBySessionId: params.createdBySessionId ?? null,
|
|
@@ -154,16 +149,15 @@ export function createInvite(params: {
|
|
|
154
149
|
// ---------------------------------------------------------------------------
|
|
155
150
|
|
|
156
151
|
export function listInvites(params: {
|
|
157
|
-
assistantId?: string;
|
|
158
152
|
sourceChannel?: string;
|
|
159
153
|
status?: InviteStatus;
|
|
160
154
|
limit?: number;
|
|
161
155
|
offset?: number;
|
|
162
156
|
}): IngressInvite[] {
|
|
163
157
|
const db = getDb();
|
|
164
|
-
const assistantId = params.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID;
|
|
165
158
|
|
|
166
|
-
|
|
159
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
160
|
+
const conditions: any[] = [];
|
|
167
161
|
|
|
168
162
|
if (params.sourceChannel) {
|
|
169
163
|
conditions.push(
|
|
@@ -174,10 +168,9 @@ export function listInvites(params: {
|
|
|
174
168
|
conditions.push(eq(assistantIngressInvites.status, params.status));
|
|
175
169
|
}
|
|
176
170
|
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
.where(and(...conditions))
|
|
171
|
+
const query = db.select().from(assistantIngressInvites);
|
|
172
|
+
|
|
173
|
+
const rows = (conditions.length > 0 ? query.where(and(...conditions)) : query)
|
|
181
174
|
.orderBy(desc(assistantIngressInvites.createdAt))
|
|
182
175
|
.limit(params.limit ?? 100)
|
|
183
176
|
.offset(params.offset ?? 0)
|
|
@@ -329,7 +322,6 @@ export function findByTokenHash(tokenHash: string): IngressInvite | null {
|
|
|
329
322
|
* before code hash matching.
|
|
330
323
|
*/
|
|
331
324
|
export function findActiveVoiceInvites(params: {
|
|
332
|
-
assistantId: string;
|
|
333
325
|
expectedExternalUserId: string;
|
|
334
326
|
}): IngressInvite[] {
|
|
335
327
|
const db = getDb();
|
|
@@ -339,7 +331,6 @@ export function findActiveVoiceInvites(params: {
|
|
|
339
331
|
.from(assistantIngressInvites)
|
|
340
332
|
.where(
|
|
341
333
|
and(
|
|
342
|
-
eq(assistantIngressInvites.assistantId, params.assistantId),
|
|
343
334
|
eq(assistantIngressInvites.sourceChannel, "voice"),
|
|
344
335
|
eq(assistantIngressInvites.status, "active"),
|
|
345
336
|
eq(
|