@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
|
@@ -89,7 +89,6 @@ function grantParams(
|
|
|
89
89
|
): CreateScopedApprovalGrantParams {
|
|
90
90
|
const futureExpiry = new Date(Date.now() + 60_000).toISOString();
|
|
91
91
|
return {
|
|
92
|
-
assistantId: "self",
|
|
93
92
|
scopeMode: "tool_signature",
|
|
94
93
|
toolName: "bash",
|
|
95
94
|
inputDigest: computeToolApprovalDigest("bash", { cmd: "ls" }),
|
|
@@ -338,35 +337,6 @@ describe("security matrix: persistence and fail-closed behavior", () => {
|
|
|
338
337
|
describe("security matrix: cross-scope invariants", () => {
|
|
339
338
|
beforeEach(() => clearTables());
|
|
340
339
|
|
|
341
|
-
test("grant for one assistant cannot be consumed by another assistant", () => {
|
|
342
|
-
const digest = computeToolApprovalDigest("bash", { cmd: "ls" });
|
|
343
|
-
createScopedApprovalGrant(
|
|
344
|
-
grantParams({
|
|
345
|
-
toolName: "bash",
|
|
346
|
-
inputDigest: digest,
|
|
347
|
-
assistantId: "assistant-alpha",
|
|
348
|
-
}),
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
// Attempt consumption from a different assistant
|
|
352
|
-
const wrongAssistant = consumeScopedApprovalGrantByToolSignature({
|
|
353
|
-
toolName: "bash",
|
|
354
|
-
inputDigest: digest,
|
|
355
|
-
consumingRequestId: "c1",
|
|
356
|
-
assistantId: "assistant-beta",
|
|
357
|
-
});
|
|
358
|
-
expect(wrongAssistant.ok).toBe(false);
|
|
359
|
-
|
|
360
|
-
// Correct assistant succeeds
|
|
361
|
-
const correctAssistant = consumeScopedApprovalGrantByToolSignature({
|
|
362
|
-
toolName: "bash",
|
|
363
|
-
inputDigest: digest,
|
|
364
|
-
consumingRequestId: "c2",
|
|
365
|
-
assistantId: "assistant-alpha",
|
|
366
|
-
});
|
|
367
|
-
expect(correctAssistant.ok).toBe(true);
|
|
368
|
-
});
|
|
369
|
-
|
|
370
340
|
test("all scope fields must match simultaneously for consumption", () => {
|
|
371
341
|
const digest = computeToolApprovalDigest("bash", { cmd: "ls" });
|
|
372
342
|
|
|
@@ -375,7 +345,6 @@ describe("security matrix: cross-scope invariants", () => {
|
|
|
375
345
|
grantParams({
|
|
376
346
|
toolName: "bash",
|
|
377
347
|
inputDigest: digest,
|
|
378
|
-
assistantId: "self",
|
|
379
348
|
executionChannel: "voice",
|
|
380
349
|
conversationId: "conv-123",
|
|
381
350
|
callSessionId: "call-456",
|
|
@@ -391,7 +360,6 @@ describe("security matrix: cross-scope invariants", () => {
|
|
|
391
360
|
toolName: "bash",
|
|
392
361
|
inputDigest: digest,
|
|
393
362
|
consumingRequestId: "c-chan",
|
|
394
|
-
assistantId: "self",
|
|
395
363
|
executionChannel: "sms",
|
|
396
364
|
conversationId: "conv-123",
|
|
397
365
|
callSessionId: "call-456",
|
|
@@ -405,7 +373,6 @@ describe("security matrix: cross-scope invariants", () => {
|
|
|
405
373
|
toolName: "bash",
|
|
406
374
|
inputDigest: digest,
|
|
407
375
|
consumingRequestId: "c-conv",
|
|
408
|
-
assistantId: "self",
|
|
409
376
|
executionChannel: "voice",
|
|
410
377
|
conversationId: "conv-999",
|
|
411
378
|
callSessionId: "call-456",
|
|
@@ -419,7 +386,6 @@ describe("security matrix: cross-scope invariants", () => {
|
|
|
419
386
|
toolName: "bash",
|
|
420
387
|
inputDigest: digest,
|
|
421
388
|
consumingRequestId: "c-call",
|
|
422
|
-
assistantId: "self",
|
|
423
389
|
executionChannel: "voice",
|
|
424
390
|
conversationId: "conv-123",
|
|
425
391
|
callSessionId: "call-999",
|
|
@@ -433,7 +399,6 @@ describe("security matrix: cross-scope invariants", () => {
|
|
|
433
399
|
toolName: "bash",
|
|
434
400
|
inputDigest: digest,
|
|
435
401
|
consumingRequestId: "c-user",
|
|
436
|
-
assistantId: "self",
|
|
437
402
|
executionChannel: "voice",
|
|
438
403
|
conversationId: "conv-123",
|
|
439
404
|
callSessionId: "call-456",
|
|
@@ -447,7 +412,6 @@ describe("security matrix: cross-scope invariants", () => {
|
|
|
447
412
|
toolName: "bash",
|
|
448
413
|
inputDigest: digest,
|
|
449
414
|
consumingRequestId: "c-all",
|
|
450
|
-
assistantId: "self",
|
|
451
415
|
executionChannel: "voice",
|
|
452
416
|
conversationId: "conv-123",
|
|
453
417
|
callSessionId: "call-456",
|
|
@@ -295,7 +295,6 @@ describe("POST /v1/messages — queue-if-busy and hub publishing", () => {
|
|
|
295
295
|
pendingInteractions.clear();
|
|
296
296
|
|
|
297
297
|
createGuardianBinding({
|
|
298
|
-
assistantId: "self",
|
|
299
298
|
channel: "vellum",
|
|
300
299
|
guardianExternalUserId: "dev-bypass",
|
|
301
300
|
guardianDeliveryChatId: "vellum",
|
|
@@ -176,7 +176,7 @@ describe("Slack inbound trusted contact verification", () => {
|
|
|
176
176
|
await handleChannelInbound(req, undefined, TEST_BEARER_TOKEN);
|
|
177
177
|
|
|
178
178
|
// An active outbound session should exist for the slack channel
|
|
179
|
-
const session = findActiveSession("
|
|
179
|
+
const session = findActiveSession("slack");
|
|
180
180
|
expect(session).not.toBeNull();
|
|
181
181
|
expect(session!.expectedExternalUserId).toBe("U0123UNKNOWN");
|
|
182
182
|
expect(session!.expectedChatId).toBe("U0123UNKNOWN");
|
|
@@ -187,7 +187,6 @@ describe("Slack inbound trusted contact verification", () => {
|
|
|
187
187
|
test("guardian is notified of the access attempt alongside verification", async () => {
|
|
188
188
|
// Set up a guardian binding so the notification can target it
|
|
189
189
|
createGuardianBinding({
|
|
190
|
-
assistantId: "self",
|
|
191
190
|
channel: "slack",
|
|
192
191
|
guardianExternalUserId: "U_GUARDIAN",
|
|
193
192
|
guardianDeliveryChatId: "D_GUARDIAN_DM",
|
|
@@ -285,7 +284,7 @@ describe("Slack inbound trusted contact verification", () => {
|
|
|
285
284
|
const req = buildSlackInboundRequest();
|
|
286
285
|
await handleChannelInbound(req, undefined, TEST_BEARER_TOKEN);
|
|
287
286
|
|
|
288
|
-
const session = findActiveSession("
|
|
287
|
+
const session = findActiveSession("slack");
|
|
289
288
|
expect(session).not.toBeNull();
|
|
290
289
|
|
|
291
290
|
// The challenge hash is stored in the session — extract the secret
|
|
@@ -317,7 +316,6 @@ describe("Slack inbound trusted contact verification", () => {
|
|
|
317
316
|
await import("../runtime/channel-guardian-service.js");
|
|
318
317
|
|
|
319
318
|
const outboundSession = createOutboundSession({
|
|
320
|
-
assistantId: "self",
|
|
321
319
|
channel: "slack",
|
|
322
320
|
expectedExternalUserId: "U0123UNKNOWN",
|
|
323
321
|
expectedChatId: "U0123UNKNOWN",
|
|
@@ -90,7 +90,6 @@ afterAll(() => {
|
|
|
90
90
|
function mintParams(overrides: Partial<MintGrantParams> = {}): MintGrantParams {
|
|
91
91
|
const futureExpiry = new Date(Date.now() + 60_000).toISOString();
|
|
92
92
|
return {
|
|
93
|
-
assistantId: "self",
|
|
94
93
|
scopeMode: "tool_signature",
|
|
95
94
|
requestChannel: "telegram",
|
|
96
95
|
decisionChannel: "telegram",
|
|
@@ -684,7 +684,6 @@ describe("inline wait-and-resume", () => {
|
|
|
684
684
|
toolName: "bash",
|
|
685
685
|
inputDigest: "sha256:waitgrant",
|
|
686
686
|
consumingRequestId: "consume-1",
|
|
687
|
-
assistantId: "self",
|
|
688
687
|
conversationId: "conv-1",
|
|
689
688
|
requesterExternalUserId: "requester-1",
|
|
690
689
|
executionChannel: "telegram",
|
|
@@ -727,7 +726,6 @@ describe("inline wait-and-resume", () => {
|
|
|
727
726
|
toolName: "bash",
|
|
728
727
|
inputDigest: "sha256:denywait",
|
|
729
728
|
consumingRequestId: "consume-1",
|
|
730
|
-
assistantId: "self",
|
|
731
729
|
},
|
|
732
730
|
{ maxWaitMs: 2_000, intervalMs: 20 },
|
|
733
731
|
);
|
|
@@ -759,7 +757,6 @@ describe("inline wait-and-resume", () => {
|
|
|
759
757
|
toolName: "bash",
|
|
760
758
|
inputDigest: "sha256:timeoutwait",
|
|
761
759
|
consumingRequestId: "consume-1",
|
|
762
|
-
assistantId: "self",
|
|
763
760
|
},
|
|
764
761
|
{ maxWaitMs: 100, intervalMs: 20 },
|
|
765
762
|
);
|
|
@@ -797,7 +794,6 @@ describe("inline wait-and-resume", () => {
|
|
|
797
794
|
toolName: "bash",
|
|
798
795
|
inputDigest: "sha256:abortwait",
|
|
799
796
|
consumingRequestId: "consume-1",
|
|
800
|
-
assistantId: "self",
|
|
801
797
|
},
|
|
802
798
|
{ maxWaitMs: 5_000, intervalMs: 20, signal: controller.signal },
|
|
803
799
|
);
|
|
@@ -217,10 +217,7 @@ async function simulateNotifierPoll(params: {
|
|
|
217
217
|
notifiedRequestIds.set(info.requestId, conversationId);
|
|
218
218
|
|
|
219
219
|
// Resolve guardian name via the contacts-based approach
|
|
220
|
-
const guardian = findGuardianForChannel(
|
|
221
|
-
params.sourceChannel,
|
|
222
|
-
params.assistantId ?? "self",
|
|
223
|
-
);
|
|
220
|
+
const guardian = findGuardianForChannel(params.sourceChannel);
|
|
224
221
|
const guardianName = resolveGuardianName(guardian?.contact.displayName);
|
|
225
222
|
|
|
226
223
|
const waitingText = `Waiting for ${guardianName}'s approval...`;
|
|
@@ -286,7 +283,6 @@ describe("trusted-contact pending-approval notifier", () => {
|
|
|
286
283
|
guardianExternalUserId: "guardian-1",
|
|
287
284
|
replyCallbackUrl: "http://localhost:3000/deliver/telegram",
|
|
288
285
|
bearerToken: "test-token",
|
|
289
|
-
assistantId: "self",
|
|
290
286
|
notifiedRequestIds: notified,
|
|
291
287
|
});
|
|
292
288
|
|
|
@@ -168,7 +168,6 @@ describe("trusted contact lifecycle notification signals", () => {
|
|
|
168
168
|
test("guardian deny emits guardian_decision and denied signals", async () => {
|
|
169
169
|
// Set up guardian binding and member record (guardians must pass ACL)
|
|
170
170
|
createGuardianBinding({
|
|
171
|
-
assistantId: "self",
|
|
172
171
|
channel: "telegram",
|
|
173
172
|
guardianExternalUserId: "guardian-user-789",
|
|
174
173
|
guardianDeliveryChatId: "guardian-chat-789",
|
|
@@ -176,7 +175,6 @@ describe("trusted contact lifecycle notification signals", () => {
|
|
|
176
175
|
verifiedVia: "test",
|
|
177
176
|
});
|
|
178
177
|
upsertMember({
|
|
179
|
-
assistantId: "self",
|
|
180
178
|
sourceChannel: "telegram",
|
|
181
179
|
externalUserId: "guardian-user-789",
|
|
182
180
|
externalChatId: "guardian-chat-789",
|
|
@@ -191,7 +189,6 @@ describe("trusted contact lifecycle notification signals", () => {
|
|
|
191
189
|
runId: `ingress-access-request-${Date.now()}`,
|
|
192
190
|
requestId: testRequestId,
|
|
193
191
|
conversationId: "access-req-telegram-requester-user-456",
|
|
194
|
-
assistantId: "self",
|
|
195
192
|
channel: "telegram",
|
|
196
193
|
requesterExternalUserId: "requester-user-456",
|
|
197
194
|
requesterChatId: "requester-chat-456",
|
|
@@ -250,7 +247,6 @@ describe("trusted contact lifecycle notification signals", () => {
|
|
|
250
247
|
test("guardian approve emits guardian_decision and verification_sent signals", async () => {
|
|
251
248
|
// Set up guardian binding and member record (guardians must pass ACL)
|
|
252
249
|
createGuardianBinding({
|
|
253
|
-
assistantId: "self",
|
|
254
250
|
channel: "telegram",
|
|
255
251
|
guardianExternalUserId: "guardian-user-789",
|
|
256
252
|
guardianDeliveryChatId: "guardian-chat-789",
|
|
@@ -258,7 +254,6 @@ describe("trusted contact lifecycle notification signals", () => {
|
|
|
258
254
|
verifiedVia: "test",
|
|
259
255
|
});
|
|
260
256
|
upsertMember({
|
|
261
|
-
assistantId: "self",
|
|
262
257
|
sourceChannel: "telegram",
|
|
263
258
|
externalUserId: "guardian-user-789",
|
|
264
259
|
externalChatId: "guardian-chat-789",
|
|
@@ -273,7 +268,6 @@ describe("trusted contact lifecycle notification signals", () => {
|
|
|
273
268
|
runId: `ingress-access-request-${Date.now()}`,
|
|
274
269
|
requestId: testRequestId,
|
|
275
270
|
conversationId: "access-req-telegram-requester-user-456",
|
|
276
|
-
assistantId: "self",
|
|
277
271
|
channel: "telegram",
|
|
278
272
|
requesterExternalUserId: "requester-user-456",
|
|
279
273
|
requesterChatId: "requester-chat-456",
|
|
@@ -328,7 +322,6 @@ describe("trusted contact lifecycle notification signals", () => {
|
|
|
328
322
|
test("deduplication keys prevent duplicate signals", async () => {
|
|
329
323
|
// Set up guardian binding and member record (guardians must pass ACL)
|
|
330
324
|
createGuardianBinding({
|
|
331
|
-
assistantId: "self",
|
|
332
325
|
channel: "telegram",
|
|
333
326
|
guardianExternalUserId: "guardian-user-789",
|
|
334
327
|
guardianDeliveryChatId: "guardian-chat-789",
|
|
@@ -336,7 +329,6 @@ describe("trusted contact lifecycle notification signals", () => {
|
|
|
336
329
|
verifiedVia: "test",
|
|
337
330
|
});
|
|
338
331
|
upsertMember({
|
|
339
|
-
assistantId: "self",
|
|
340
332
|
sourceChannel: "telegram",
|
|
341
333
|
externalUserId: "guardian-user-789",
|
|
342
334
|
externalChatId: "guardian-chat-789",
|
|
@@ -350,7 +342,6 @@ describe("trusted contact lifecycle notification signals", () => {
|
|
|
350
342
|
runId: `ingress-access-request-${Date.now()}`,
|
|
351
343
|
requestId: testRequestId,
|
|
352
344
|
conversationId: "access-req-telegram-requester-user-456",
|
|
353
|
-
assistantId: "self",
|
|
354
345
|
channel: "telegram",
|
|
355
346
|
requesterExternalUserId: "requester-user-456",
|
|
356
347
|
requesterChatId: "requester-chat-456",
|
|
@@ -395,7 +386,6 @@ describe("trusted contact activated notification signal", () => {
|
|
|
395
386
|
test("successful trusted contact verification emits activated signal", async () => {
|
|
396
387
|
// Set up a guardian binding so the verification path allows bypass
|
|
397
388
|
createGuardianBinding({
|
|
398
|
-
assistantId: "self",
|
|
399
389
|
channel: "telegram",
|
|
400
390
|
guardianExternalUserId: "guardian-user-789",
|
|
401
391
|
guardianDeliveryChatId: "guardian-chat-789",
|
|
@@ -405,7 +395,6 @@ describe("trusted contact activated notification signal", () => {
|
|
|
405
395
|
|
|
406
396
|
// Create an identity-bound outbound session (simulates M3 approval flow)
|
|
407
397
|
const session = createOutboundSession({
|
|
408
|
-
assistantId: "self",
|
|
409
398
|
channel: "telegram",
|
|
410
399
|
expectedExternalUserId: "requester-user-456",
|
|
411
400
|
expectedChatId: "chat-123",
|
|
@@ -452,7 +441,6 @@ describe("trusted contact activated notification signal", () => {
|
|
|
452
441
|
|
|
453
442
|
test("re-verification preserves an existing guardian-managed member display name", async () => {
|
|
454
443
|
createGuardianBinding({
|
|
455
|
-
assistantId: "self",
|
|
456
444
|
channel: "telegram",
|
|
457
445
|
guardianExternalUserId: "guardian-user-789",
|
|
458
446
|
guardianDeliveryChatId: "guardian-chat-789",
|
|
@@ -461,7 +449,6 @@ describe("trusted contact activated notification signal", () => {
|
|
|
461
449
|
});
|
|
462
450
|
|
|
463
451
|
upsertMember({
|
|
464
|
-
assistantId: "self",
|
|
465
452
|
sourceChannel: "telegram",
|
|
466
453
|
externalUserId: "requester-user-456",
|
|
467
454
|
externalChatId: "chat-123",
|
|
@@ -471,7 +458,6 @@ describe("trusted contact activated notification signal", () => {
|
|
|
471
458
|
});
|
|
472
459
|
|
|
473
460
|
const session = createOutboundSession({
|
|
474
|
-
assistantId: "self",
|
|
475
461
|
channel: "telegram",
|
|
476
462
|
expectedExternalUserId: "requester-user-456",
|
|
477
463
|
expectedChatId: "chat-123",
|
|
@@ -502,7 +488,7 @@ describe("trusted contact activated notification signal", () => {
|
|
|
502
488
|
// Create an inbound challenge (guardian flow, not trusted contact)
|
|
503
489
|
const { createVerificationChallenge } =
|
|
504
490
|
await import("../runtime/channel-guardian-service.js");
|
|
505
|
-
const { secret } = createVerificationChallenge("
|
|
491
|
+
const { secret } = createVerificationChallenge("telegram");
|
|
506
492
|
|
|
507
493
|
// "Guardian" enters the verification code
|
|
508
494
|
const verifyReq = buildInboundRequest({
|
|
@@ -533,7 +519,6 @@ describe("trusted contact activated notification signal", () => {
|
|
|
533
519
|
test("member is persisted BEFORE activated signal is emitted", async () => {
|
|
534
520
|
// Set up a guardian binding
|
|
535
521
|
createGuardianBinding({
|
|
536
|
-
assistantId: "self",
|
|
537
522
|
channel: "telegram",
|
|
538
523
|
guardianExternalUserId: "guardian-user-789",
|
|
539
524
|
guardianDeliveryChatId: "guardian-chat-789",
|
|
@@ -542,7 +527,6 @@ describe("trusted contact activated notification signal", () => {
|
|
|
542
527
|
});
|
|
543
528
|
|
|
544
529
|
const session = createOutboundSession({
|
|
545
|
-
assistantId: "self",
|
|
546
530
|
channel: "telegram",
|
|
547
531
|
expectedExternalUserId: "requester-user-456",
|
|
548
532
|
expectedChatId: "chat-123",
|
|
@@ -230,7 +230,6 @@ for (const config of CHANNEL_CONFIGS) {
|
|
|
230
230
|
|
|
231
231
|
test("guardian is notified when a non-member messages", async () => {
|
|
232
232
|
createGuardianBinding({
|
|
233
|
-
assistantId: "self",
|
|
234
233
|
channel: config.channel,
|
|
235
234
|
guardianExternalUserId: config.guardianExternalUserId,
|
|
236
235
|
guardianDeliveryChatId: config.guardianChatId,
|
|
@@ -258,7 +257,6 @@ for (const config of CHANNEL_CONFIGS) {
|
|
|
258
257
|
|
|
259
258
|
test("verification creates active member for channel", () => {
|
|
260
259
|
const session = createOutboundSession({
|
|
261
|
-
assistantId: "self",
|
|
262
260
|
channel: config.channel,
|
|
263
261
|
expectedExternalUserId: config.senderExternalUserId,
|
|
264
262
|
expectedChatId: config.externalChatId,
|
|
@@ -268,7 +266,6 @@ for (const config of CHANNEL_CONFIGS) {
|
|
|
268
266
|
});
|
|
269
267
|
|
|
270
268
|
const challengeResult = validateAndConsumeChallenge(
|
|
271
|
-
"self",
|
|
272
269
|
config.channel,
|
|
273
270
|
session.secret,
|
|
274
271
|
config.senderExternalUserId,
|
|
@@ -283,7 +280,6 @@ for (const config of CHANNEL_CONFIGS) {
|
|
|
283
280
|
}
|
|
284
281
|
|
|
285
282
|
upsertMember({
|
|
286
|
-
assistantId: "self",
|
|
287
283
|
sourceChannel: config.channel,
|
|
288
284
|
externalUserId: config.senderExternalUserId,
|
|
289
285
|
externalChatId: config.externalChatId,
|
|
@@ -307,7 +303,6 @@ for (const config of CHANNEL_CONFIGS) {
|
|
|
307
303
|
test("no cross-channel leakage between member records", () => {
|
|
308
304
|
// Create a member for this channel
|
|
309
305
|
upsertMember({
|
|
310
|
-
assistantId: "self",
|
|
311
306
|
sourceChannel: config.channel,
|
|
312
307
|
externalUserId: config.senderExternalUserId,
|
|
313
308
|
externalChatId: config.externalChatId,
|
|
@@ -345,7 +340,6 @@ describe("SMS identity binding with E.164 phone numbers", () => {
|
|
|
345
340
|
test("SMS verification session binds to phone E.164", () => {
|
|
346
341
|
const phone = "+15551234567";
|
|
347
342
|
const session = createOutboundSession({
|
|
348
|
-
assistantId: "self",
|
|
349
343
|
channel: "sms",
|
|
350
344
|
expectedExternalUserId: phone,
|
|
351
345
|
expectedPhoneE164: phone,
|
|
@@ -357,7 +351,6 @@ describe("SMS identity binding with E.164 phone numbers", () => {
|
|
|
357
351
|
|
|
358
352
|
// Verify with matching phone identity
|
|
359
353
|
const result = validateAndConsumeChallenge(
|
|
360
|
-
"self",
|
|
361
354
|
"sms",
|
|
362
355
|
session.secret,
|
|
363
356
|
phone,
|
|
@@ -374,7 +367,6 @@ describe("SMS identity binding with E.164 phone numbers", () => {
|
|
|
374
367
|
const wrongPhone = "+15559999999";
|
|
375
368
|
|
|
376
369
|
const session = createOutboundSession({
|
|
377
|
-
assistantId: "self",
|
|
378
370
|
channel: "sms",
|
|
379
371
|
expectedExternalUserId: expectedPhone,
|
|
380
372
|
expectedPhoneE164: expectedPhone,
|
|
@@ -385,7 +377,6 @@ describe("SMS identity binding with E.164 phone numbers", () => {
|
|
|
385
377
|
|
|
386
378
|
// Try to verify with a different phone (anti-oracle: same error message)
|
|
387
379
|
const result = validateAndConsumeChallenge(
|
|
388
|
-
"self",
|
|
389
380
|
"sms",
|
|
390
381
|
session.secret,
|
|
391
382
|
wrongPhone,
|
|
@@ -407,7 +398,6 @@ describe("cross-channel isolation", () => {
|
|
|
407
398
|
test("verification sessions are scoped per channel", () => {
|
|
408
399
|
// Create sessions on both channels
|
|
409
400
|
const telegramSession = createOutboundSession({
|
|
410
|
-
assistantId: "self",
|
|
411
401
|
channel: "telegram",
|
|
412
402
|
expectedExternalUserId: "user-123",
|
|
413
403
|
expectedChatId: "chat-123",
|
|
@@ -416,7 +406,6 @@ describe("cross-channel isolation", () => {
|
|
|
416
406
|
});
|
|
417
407
|
|
|
418
408
|
const smsSession = createOutboundSession({
|
|
419
|
-
assistantId: "self",
|
|
420
409
|
channel: "sms",
|
|
421
410
|
expectedExternalUserId: "+15551234567",
|
|
422
411
|
expectedPhoneE164: "+15551234567",
|
|
@@ -427,7 +416,6 @@ describe("cross-channel isolation", () => {
|
|
|
427
416
|
|
|
428
417
|
// Telegram code should not work on SMS channel
|
|
429
418
|
const wrongChannelResult = validateAndConsumeChallenge(
|
|
430
|
-
"self",
|
|
431
419
|
"sms",
|
|
432
420
|
telegramSession.secret,
|
|
433
421
|
"+15551234567",
|
|
@@ -437,7 +425,6 @@ describe("cross-channel isolation", () => {
|
|
|
437
425
|
|
|
438
426
|
// SMS code should work on SMS channel
|
|
439
427
|
const correctChannelResult = validateAndConsumeChallenge(
|
|
440
|
-
"self",
|
|
441
428
|
"sms",
|
|
442
429
|
smsSession.secret,
|
|
443
430
|
"+15551234567",
|
|
@@ -91,7 +91,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
91
91
|
test("successful verification creates active member with allow policy", () => {
|
|
92
92
|
// Simulate M3: guardian approves, outbound session created for the requester
|
|
93
93
|
const session = createOutboundSession({
|
|
94
|
-
assistantId: "self",
|
|
95
94
|
channel: "telegram",
|
|
96
95
|
expectedExternalUserId: "requester-user-123",
|
|
97
96
|
expectedChatId: "requester-chat-123",
|
|
@@ -102,7 +101,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
102
101
|
|
|
103
102
|
// Requester enters the 6-digit code
|
|
104
103
|
const result = validateAndConsumeChallenge(
|
|
105
|
-
"self",
|
|
106
104
|
"telegram",
|
|
107
105
|
session.secret,
|
|
108
106
|
"requester-user-123",
|
|
@@ -237,7 +235,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
237
235
|
test("post-verify message is accepted (ACL check passes)", () => {
|
|
238
236
|
// Create and verify a trusted contact
|
|
239
237
|
const session = createOutboundSession({
|
|
240
|
-
assistantId: "self",
|
|
241
238
|
channel: "telegram",
|
|
242
239
|
expectedExternalUserId: "requester-user-456",
|
|
243
240
|
expectedChatId: "requester-chat-456",
|
|
@@ -247,7 +244,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
247
244
|
});
|
|
248
245
|
|
|
249
246
|
validateAndConsumeChallenge(
|
|
250
|
-
"self",
|
|
251
247
|
"telegram",
|
|
252
248
|
session.secret,
|
|
253
249
|
"requester-user-456",
|
|
@@ -278,7 +274,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
278
274
|
test("member lookup is scoped by channel type", () => {
|
|
279
275
|
// Create member on the telegram channel
|
|
280
276
|
const session = createOutboundSession({
|
|
281
|
-
assistantId: "self",
|
|
282
277
|
channel: "telegram",
|
|
283
278
|
expectedExternalUserId: "user-cross-test",
|
|
284
279
|
expectedChatId: "chat-cross-test",
|
|
@@ -288,7 +283,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
288
283
|
});
|
|
289
284
|
|
|
290
285
|
validateAndConsumeChallenge(
|
|
291
|
-
"self",
|
|
292
286
|
"telegram",
|
|
293
287
|
session.secret,
|
|
294
288
|
"user-cross-test",
|
|
@@ -345,7 +339,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
345
339
|
|
|
346
340
|
// Guardian re-approves, new outbound session created
|
|
347
341
|
const session = createOutboundSession({
|
|
348
|
-
assistantId: "self",
|
|
349
342
|
channel: "telegram",
|
|
350
343
|
expectedExternalUserId: "user-revoked",
|
|
351
344
|
expectedChatId: "chat-revoked",
|
|
@@ -356,7 +349,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
356
349
|
|
|
357
350
|
// Requester enters the new code
|
|
358
351
|
const result = validateAndConsumeChallenge(
|
|
359
|
-
"self",
|
|
360
352
|
"telegram",
|
|
361
353
|
session.secret,
|
|
362
354
|
"user-revoked",
|
|
@@ -389,7 +381,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
389
381
|
test("trusted contact verification does NOT create a guardian binding", () => {
|
|
390
382
|
// Ensure there's an existing guardian binding we want to preserve
|
|
391
383
|
createGuardianBinding({
|
|
392
|
-
assistantId: "self",
|
|
393
384
|
channel: "telegram",
|
|
394
385
|
guardianExternalUserId: "guardian-user-original",
|
|
395
386
|
guardianDeliveryChatId: "guardian-chat-original",
|
|
@@ -400,7 +391,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
400
391
|
|
|
401
392
|
// Create an outbound session for a requester (different user than guardian)
|
|
402
393
|
const session = createOutboundSession({
|
|
403
|
-
assistantId: "self",
|
|
404
394
|
channel: "telegram",
|
|
405
395
|
expectedExternalUserId: "requester-user-789",
|
|
406
396
|
expectedChatId: "requester-chat-789",
|
|
@@ -410,7 +400,6 @@ describe("trusted contact verification → member activation", () => {
|
|
|
410
400
|
});
|
|
411
401
|
|
|
412
402
|
const result = validateAndConsumeChallenge(
|
|
413
|
-
"self",
|
|
414
403
|
"telegram",
|
|
415
404
|
session.secret,
|
|
416
405
|
"requester-user-789",
|
|
@@ -425,7 +414,7 @@ describe("trusted contact verification → member activation", () => {
|
|
|
425
414
|
}
|
|
426
415
|
|
|
427
416
|
// The original guardian binding should remain intact
|
|
428
|
-
const guardianResult = findGuardianForChannel("telegram"
|
|
417
|
+
const guardianResult = findGuardianForChannel("telegram");
|
|
429
418
|
expect(guardianResult).not.toBeNull();
|
|
430
419
|
expect(guardianResult!.channel.externalUserId).toBe(
|
|
431
420
|
"guardian-user-original",
|
|
@@ -437,10 +426,9 @@ describe("trusted contact verification → member activation", () => {
|
|
|
437
426
|
|
|
438
427
|
const { createVerificationChallenge } =
|
|
439
428
|
await import("../runtime/channel-guardian-service.js");
|
|
440
|
-
const { secret } = createVerificationChallenge("
|
|
429
|
+
const { secret } = createVerificationChallenge("telegram");
|
|
441
430
|
|
|
442
431
|
const result = validateAndConsumeChallenge(
|
|
443
|
-
"self",
|
|
444
432
|
"telegram",
|
|
445
433
|
secret,
|
|
446
434
|
"guardian-user",
|
|
@@ -452,7 +440,7 @@ describe("trusted contact verification → member activation", () => {
|
|
|
452
440
|
expect(result.verificationType).toBe("guardian");
|
|
453
441
|
}
|
|
454
442
|
|
|
455
|
-
const guardianResult = findGuardianForChannel("telegram"
|
|
443
|
+
const guardianResult = findGuardianForChannel("telegram");
|
|
456
444
|
expect(guardianResult).toBeNull();
|
|
457
445
|
});
|
|
458
446
|
});
|
|
@@ -817,8 +817,8 @@ describe("twilio webhook routes", () => {
|
|
|
817
817
|
expect(res.status).toBe(200);
|
|
818
818
|
const session = getCallSessionByCallSid("CA_inbound_assist_1");
|
|
819
819
|
expect(session).not.toBeNull();
|
|
820
|
-
//
|
|
821
|
-
expect(session!.
|
|
820
|
+
// Session was created for the inbound call.
|
|
821
|
+
expect(session!.status).toBe("initiated");
|
|
822
822
|
});
|
|
823
823
|
|
|
824
824
|
test("outbound call flow remains non-regressed with callSessionId present", async () => {
|
|
@@ -147,7 +147,6 @@ describe("redeemVoiceInviteCode", () => {
|
|
|
147
147
|
const codeHash = hashVoiceCode(code);
|
|
148
148
|
|
|
149
149
|
const { invite } = createInvite({
|
|
150
|
-
assistantId: opts.assistantId ?? "self",
|
|
151
150
|
sourceChannel: "voice",
|
|
152
151
|
maxUses: opts.maxUses ?? 1,
|
|
153
152
|
expiresInMs: opts.expiresInMs,
|
|
@@ -254,7 +254,6 @@ function grantParams(
|
|
|
254
254
|
): CreateScopedApprovalGrantParams {
|
|
255
255
|
const futureExpiry = new Date(Date.now() + 60_000).toISOString();
|
|
256
256
|
return {
|
|
257
|
-
assistantId: ASSISTANT_ID,
|
|
258
257
|
scopeMode: "tool_signature",
|
|
259
258
|
toolName: TOOL_NAME,
|
|
260
259
|
inputDigest: computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT),
|
|
@@ -422,42 +421,6 @@ describe("voice bridge confirmation handling (grant consumption via primitive)",
|
|
|
422
421
|
expect(decision!.reason).toContain("guardian voice call");
|
|
423
422
|
});
|
|
424
423
|
|
|
425
|
-
test("non-guardian with grant for different assistantId: auto-denied", async () => {
|
|
426
|
-
// Create a grant scoped to a different assistant
|
|
427
|
-
createScopedApprovalGrant(
|
|
428
|
-
grantParams({
|
|
429
|
-
assistantId: "other-assistant",
|
|
430
|
-
}),
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
const mockData = createMockSession();
|
|
434
|
-
setupBridgeDeps(() => mockData.session);
|
|
435
|
-
|
|
436
|
-
const trustContext: TrustContext = {
|
|
437
|
-
sourceChannel: "voice",
|
|
438
|
-
trustClass: "trusted_contact",
|
|
439
|
-
requesterExternalUserId: "caller-123",
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
await startVoiceTurn({
|
|
443
|
-
conversationId: CONVERSATION_ID,
|
|
444
|
-
callSessionId: CALL_SESSION_ID,
|
|
445
|
-
content: "test utterance",
|
|
446
|
-
assistantId: ASSISTANT_ID,
|
|
447
|
-
trustContext,
|
|
448
|
-
isInbound: true,
|
|
449
|
-
onTextDelta: () => {},
|
|
450
|
-
onComplete: () => {},
|
|
451
|
-
onError: () => {},
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
455
|
-
|
|
456
|
-
const decision = mockData.getConfirmationDecision();
|
|
457
|
-
expect(decision).not.toBeNull();
|
|
458
|
-
expect(decision!.decision).toBe("deny");
|
|
459
|
-
});
|
|
460
|
-
|
|
461
424
|
test("grants revoked when revokeScopedApprovalGrantsForContext is called with callSessionId", () => {
|
|
462
425
|
const db = getDb();
|
|
463
426
|
const testCallSessionId = "call-session-revoke-test";
|