@vellumai/assistant 0.4.5 → 0.4.7
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 +27 -10
- package/README.md +6 -6
- package/bun.lock +57 -2
- package/docs/architecture/memory.md +4 -4
- package/docs/trusted-contact-access.md +8 -0
- package/package.json +3 -2
- package/src/__tests__/actor-token-service.test.ts +9 -6
- package/src/__tests__/assistant-feature-flags-integration.test.ts +1 -0
- package/src/__tests__/call-controller.test.ts +115 -0
- package/src/__tests__/call-domain.test.ts +148 -10
- package/src/__tests__/call-pointer-message-composer.test.ts +39 -49
- package/src/__tests__/call-pointer-messages.test.ts +105 -43
- package/src/__tests__/canonical-guardian-store.test.ts +44 -10
- package/src/__tests__/channel-approval-routes.test.ts +67 -65
- package/src/__tests__/channel-delivery-store.test.ts +2 -2
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +2 -2
- package/src/__tests__/deterministic-verification-control-plane.test.ts +6 -6
- package/src/__tests__/gateway-client-managed-outbound.test.ts +147 -0
- package/src/__tests__/guardian-actions-endpoint.test.ts +7 -6
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +57 -12
- package/src/__tests__/guardian-dispatch.test.ts +39 -1
- package/src/__tests__/guardian-grant-minting.test.ts +24 -24
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +205 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -25
- package/src/__tests__/guardian-routing-state.test.ts +10 -32
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -2
- package/src/__tests__/inbound-invite-redemption.test.ts +8 -8
- package/src/__tests__/memory-retrieval.benchmark.test.ts +22 -47
- package/src/__tests__/no-is-trusted-guard.test.ts +77 -0
- package/src/__tests__/non-member-access-request.test.ts +57 -47
- package/src/__tests__/notification-decision-fallback.test.ts +232 -0
- package/src/__tests__/notification-decision-strategy.test.ts +304 -8
- package/src/__tests__/notification-guardian-path.test.ts +38 -1
- package/src/__tests__/relay-server.test.ts +136 -5
- package/src/__tests__/send-endpoint-busy.test.ts +35 -1
- package/src/__tests__/session-tool-setup-tools-disabled.test.ts +155 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +66 -2
- package/src/__tests__/system-prompt.test.ts +1 -0
- package/src/__tests__/tool-approval-handler.test.ts +1 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +10 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +14 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +24 -24
- package/src/__tests__/trusted-contact-multichannel.test.ts +5 -5
- package/src/__tests__/trusted-contact-verification.test.ts +10 -10
- package/src/approvals/guardian-decision-primitive.ts +29 -25
- package/src/approvals/guardian-request-resolvers.ts +9 -5
- package/src/calls/call-controller.ts +15 -0
- package/src/calls/call-pointer-message-composer.ts +27 -85
- package/src/calls/call-pointer-messages.ts +54 -21
- package/src/calls/guardian-dispatch.ts +30 -0
- package/src/calls/relay-server.ts +58 -24
- package/src/calls/types.ts +1 -0
- package/src/config/system-prompt.ts +10 -3
- package/src/config/templates/BOOTSTRAP.md +6 -5
- package/src/config/templates/USER.md +1 -0
- package/src/config/user-reference.ts +44 -0
- package/src/daemon/handlers/guardian-actions.ts +5 -2
- package/src/daemon/handlers/sessions.ts +8 -3
- package/src/daemon/lifecycle.ts +109 -3
- package/src/daemon/providers-setup.ts +0 -8
- package/src/daemon/server.ts +32 -24
- package/src/daemon/session-agent-loop.ts +4 -3
- package/src/daemon/session-lifecycle.ts +1 -9
- package/src/daemon/session-process.ts +2 -2
- package/src/daemon/session-runtime-assembly.ts +2 -0
- package/src/daemon/session-slash.ts +35 -2
- package/src/daemon/session-tool-setup.ts +10 -0
- package/src/daemon/session.ts +1 -0
- package/src/memory/canonical-guardian-store.ts +40 -0
- package/src/memory/conversation-crud.ts +26 -0
- package/src/memory/conversation-store.ts +1 -0
- package/src/memory/db-init.ts +12 -0
- package/src/memory/guardian-bindings.ts +4 -0
- package/src/memory/job-handlers/backfill.ts +2 -9
- package/src/memory/migrations/039-actor-refresh-token-records.ts +51 -0
- package/src/memory/migrations/125-guardian-principal-id-columns.ts +19 -0
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +210 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/migrations/registry.ts +5 -0
- package/src/memory/schema.ts +22 -0
- package/src/notifications/README.md +8 -1
- package/src/notifications/copy-composer.ts +160 -30
- package/src/notifications/decision-engine.ts +98 -1
- package/src/runtime/access-request-helper.ts +43 -28
- package/src/runtime/actor-refresh-token-service.ts +309 -0
- package/src/runtime/actor-refresh-token-store.ts +157 -0
- package/src/runtime/actor-token-service.ts +3 -3
- package/src/runtime/actor-trust-resolver.ts +19 -14
- package/src/runtime/channel-guardian-service.ts +6 -0
- package/src/runtime/gateway-client.ts +239 -0
- package/src/runtime/guardian-context-resolver.ts +6 -2
- package/src/runtime/guardian-reply-router.ts +33 -16
- package/src/runtime/guardian-vellum-migration.ts +29 -5
- package/src/runtime/http-server.ts +2 -0
- package/src/runtime/http-types.ts +0 -13
- package/src/runtime/local-actor-identity.ts +19 -13
- package/src/runtime/middleware/actor-token.ts +2 -2
- package/src/runtime/routes/channel-delivery-routes.ts +5 -5
- package/src/runtime/routes/conversation-routes.ts +45 -35
- package/src/runtime/routes/guardian-action-routes.ts +7 -1
- package/src/runtime/routes/guardian-approval-interception.ts +52 -52
- package/src/runtime/routes/guardian-bootstrap-routes.ts +11 -24
- package/src/runtime/routes/guardian-refresh-routes.ts +53 -0
- package/src/runtime/routes/inbound-conversation.ts +7 -7
- package/src/runtime/routes/inbound-message-handler.ts +105 -94
- package/src/runtime/routes/pairing-routes.ts +60 -50
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/types/qrcode.d.ts +10 -0
- package/src/util/logger.ts +10 -0
- package/src/daemon/call-pointer-generators.ts +0 -59
|
@@ -41,6 +41,10 @@ import { getDb, initializeDb, resetDb } from '../memory/db.js';
|
|
|
41
41
|
|
|
42
42
|
initializeDb();
|
|
43
43
|
|
|
44
|
+
// All decisionable kinds (tool_approval, pending_question, access_request)
|
|
45
|
+
// require a guardianPrincipalId. Use a constant for test fixtures.
|
|
46
|
+
const TEST_PRINCIPAL = 'test-principal-id';
|
|
47
|
+
|
|
44
48
|
function resetTables(): void {
|
|
45
49
|
const db = getDb();
|
|
46
50
|
db.run('DELETE FROM canonical_guardian_deliveries');
|
|
@@ -71,6 +75,7 @@ describe('canonical-guardian-store', () => {
|
|
|
71
75
|
conversationId: 'conv-1',
|
|
72
76
|
requesterExternalUserId: 'user-1',
|
|
73
77
|
guardianExternalUserId: 'guardian-1',
|
|
78
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
74
79
|
callSessionId: 'session-1',
|
|
75
80
|
pendingQuestionId: 'pq-1',
|
|
76
81
|
questionText: 'Can I run this tool?',
|
|
@@ -94,6 +99,7 @@ describe('canonical-guardian-store', () => {
|
|
|
94
99
|
const req = createCanonicalGuardianRequest({
|
|
95
100
|
kind: 'access_request',
|
|
96
101
|
sourceType: 'channel',
|
|
102
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
97
103
|
});
|
|
98
104
|
|
|
99
105
|
expect(req.id).toBeTruthy();
|
|
@@ -111,6 +117,7 @@ describe('canonical-guardian-store', () => {
|
|
|
111
117
|
const created = createCanonicalGuardianRequest({
|
|
112
118
|
kind: 'tool_approval',
|
|
113
119
|
sourceType: 'voice',
|
|
120
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
114
121
|
});
|
|
115
122
|
|
|
116
123
|
const fetched = getCanonicalGuardianRequest(created.id);
|
|
@@ -127,16 +134,16 @@ describe('canonical-guardian-store', () => {
|
|
|
127
134
|
// ── listCanonicalGuardianRequests ─────────────────────────────────
|
|
128
135
|
|
|
129
136
|
test('lists all requests with no filters', () => {
|
|
130
|
-
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice' });
|
|
131
|
-
createCanonicalGuardianRequest({ kind: 'access_request', sourceType: 'channel' });
|
|
137
|
+
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
|
|
138
|
+
createCanonicalGuardianRequest({ kind: 'access_request', sourceType: 'channel', guardianPrincipalId: TEST_PRINCIPAL });
|
|
132
139
|
|
|
133
140
|
const all = listCanonicalGuardianRequests();
|
|
134
141
|
expect(all).toHaveLength(2);
|
|
135
142
|
});
|
|
136
143
|
|
|
137
144
|
test('filters by status', () => {
|
|
138
|
-
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice' });
|
|
139
|
-
const req2 = createCanonicalGuardianRequest({ kind: 'access_request', sourceType: 'channel' });
|
|
145
|
+
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
|
|
146
|
+
const req2 = createCanonicalGuardianRequest({ kind: 'access_request', sourceType: 'channel', guardianPrincipalId: TEST_PRINCIPAL });
|
|
140
147
|
updateCanonicalGuardianRequest(req2.id, { status: 'approved' });
|
|
141
148
|
|
|
142
149
|
const pending = listCanonicalGuardianRequests({ status: 'pending' });
|
|
@@ -153,11 +160,13 @@ describe('canonical-guardian-store', () => {
|
|
|
153
160
|
kind: 'tool_approval',
|
|
154
161
|
sourceType: 'voice',
|
|
155
162
|
guardianExternalUserId: 'guardian-A',
|
|
163
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
156
164
|
});
|
|
157
165
|
createCanonicalGuardianRequest({
|
|
158
166
|
kind: 'tool_approval',
|
|
159
167
|
sourceType: 'voice',
|
|
160
168
|
guardianExternalUserId: 'guardian-B',
|
|
169
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
161
170
|
});
|
|
162
171
|
|
|
163
172
|
const filtered = listCanonicalGuardianRequests({ guardianExternalUserId: 'guardian-A' });
|
|
@@ -170,11 +179,13 @@ describe('canonical-guardian-store', () => {
|
|
|
170
179
|
kind: 'tool_approval',
|
|
171
180
|
sourceType: 'voice',
|
|
172
181
|
conversationId: 'conv-X',
|
|
182
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
173
183
|
});
|
|
174
184
|
createCanonicalGuardianRequest({
|
|
175
185
|
kind: 'tool_approval',
|
|
176
186
|
sourceType: 'voice',
|
|
177
187
|
conversationId: 'conv-Y',
|
|
188
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
178
189
|
});
|
|
179
190
|
|
|
180
191
|
const filtered = listCanonicalGuardianRequests({ conversationId: 'conv-X' });
|
|
@@ -182,18 +193,18 @@ describe('canonical-guardian-store', () => {
|
|
|
182
193
|
});
|
|
183
194
|
|
|
184
195
|
test('filters by sourceType', () => {
|
|
185
|
-
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice' });
|
|
186
|
-
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'channel' });
|
|
187
|
-
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'desktop' });
|
|
196
|
+
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
|
|
197
|
+
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'channel', guardianPrincipalId: TEST_PRINCIPAL });
|
|
198
|
+
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'desktop', guardianPrincipalId: TEST_PRINCIPAL });
|
|
188
199
|
|
|
189
200
|
const voiceOnly = listCanonicalGuardianRequests({ sourceType: 'voice' });
|
|
190
201
|
expect(voiceOnly).toHaveLength(1);
|
|
191
202
|
});
|
|
192
203
|
|
|
193
204
|
test('filters by kind', () => {
|
|
194
|
-
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice' });
|
|
195
|
-
createCanonicalGuardianRequest({ kind: 'pending_question', sourceType: 'voice' });
|
|
196
|
-
createCanonicalGuardianRequest({ kind: 'access_request', sourceType: 'channel' });
|
|
205
|
+
createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
|
|
206
|
+
createCanonicalGuardianRequest({ kind: 'pending_question', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
|
|
207
|
+
createCanonicalGuardianRequest({ kind: 'access_request', sourceType: 'channel', guardianPrincipalId: TEST_PRINCIPAL });
|
|
197
208
|
|
|
198
209
|
const toolOnly = listCanonicalGuardianRequests({ kind: 'tool_approval' });
|
|
199
210
|
expect(toolOnly).toHaveLength(1);
|
|
@@ -204,16 +215,19 @@ describe('canonical-guardian-store', () => {
|
|
|
204
215
|
kind: 'tool_approval',
|
|
205
216
|
sourceType: 'voice',
|
|
206
217
|
guardianExternalUserId: 'guardian-A',
|
|
218
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
207
219
|
});
|
|
208
220
|
createCanonicalGuardianRequest({
|
|
209
221
|
kind: 'tool_approval',
|
|
210
222
|
sourceType: 'channel',
|
|
211
223
|
guardianExternalUserId: 'guardian-A',
|
|
224
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
212
225
|
});
|
|
213
226
|
createCanonicalGuardianRequest({
|
|
214
227
|
kind: 'access_request',
|
|
215
228
|
sourceType: 'voice',
|
|
216
229
|
guardianExternalUserId: 'guardian-A',
|
|
230
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
217
231
|
});
|
|
218
232
|
|
|
219
233
|
const filtered = listCanonicalGuardianRequests({
|
|
@@ -230,6 +244,7 @@ describe('canonical-guardian-store', () => {
|
|
|
230
244
|
const req = createCanonicalGuardianRequest({
|
|
231
245
|
kind: 'tool_approval',
|
|
232
246
|
sourceType: 'voice',
|
|
247
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
233
248
|
});
|
|
234
249
|
|
|
235
250
|
const updated = updateCanonicalGuardianRequest(req.id, {
|
|
@@ -260,6 +275,7 @@ describe('canonical-guardian-store', () => {
|
|
|
260
275
|
const req = createCanonicalGuardianRequest({
|
|
261
276
|
kind: 'tool_approval',
|
|
262
277
|
sourceType: 'voice',
|
|
278
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
263
279
|
});
|
|
264
280
|
|
|
265
281
|
const resolved = resolveCanonicalGuardianRequest(req.id, 'pending', {
|
|
@@ -278,6 +294,7 @@ describe('canonical-guardian-store', () => {
|
|
|
278
294
|
const req = createCanonicalGuardianRequest({
|
|
279
295
|
kind: 'tool_approval',
|
|
280
296
|
sourceType: 'channel',
|
|
297
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
281
298
|
});
|
|
282
299
|
|
|
283
300
|
const resolved = resolveCanonicalGuardianRequest(req.id, 'pending', {
|
|
@@ -293,6 +310,7 @@ describe('canonical-guardian-store', () => {
|
|
|
293
310
|
const req = createCanonicalGuardianRequest({
|
|
294
311
|
kind: 'tool_approval',
|
|
295
312
|
sourceType: 'voice',
|
|
313
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
296
314
|
});
|
|
297
315
|
|
|
298
316
|
// Try to resolve with wrong expected status
|
|
@@ -311,6 +329,7 @@ describe('canonical-guardian-store', () => {
|
|
|
311
329
|
const req = createCanonicalGuardianRequest({
|
|
312
330
|
kind: 'tool_approval',
|
|
313
331
|
sourceType: 'voice',
|
|
332
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
314
333
|
});
|
|
315
334
|
|
|
316
335
|
// First resolve succeeds
|
|
@@ -353,6 +372,7 @@ describe('canonical-guardian-store', () => {
|
|
|
353
372
|
sourceChannel: 'twilio',
|
|
354
373
|
conversationId: 'conv-voice-1',
|
|
355
374
|
guardianExternalUserId: 'guardian-phone',
|
|
375
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
356
376
|
callSessionId: 'call-123',
|
|
357
377
|
pendingQuestionId: 'pq-456',
|
|
358
378
|
questionText: 'What is the gate code?',
|
|
@@ -374,6 +394,7 @@ describe('canonical-guardian-store', () => {
|
|
|
374
394
|
conversationId: 'conv-tg-1',
|
|
375
395
|
requesterExternalUserId: 'requester-tg-user',
|
|
376
396
|
guardianExternalUserId: 'guardian-tg-user',
|
|
397
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
377
398
|
toolName: 'execute_code',
|
|
378
399
|
inputDigest: 'sha256:abcdef',
|
|
379
400
|
expiresAt: new Date(Date.now() + 120_000).toISOString(),
|
|
@@ -394,6 +415,7 @@ describe('canonical-guardian-store', () => {
|
|
|
394
415
|
sourceType: 'desktop',
|
|
395
416
|
conversationId: 'conv-desktop-1',
|
|
396
417
|
guardianExternalUserId: 'guardian-desktop',
|
|
418
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
397
419
|
questionText: 'User wants to access settings',
|
|
398
420
|
});
|
|
399
421
|
|
|
@@ -408,6 +430,7 @@ describe('canonical-guardian-store', () => {
|
|
|
408
430
|
const req = createCanonicalGuardianRequest({
|
|
409
431
|
kind: 'tool_approval',
|
|
410
432
|
sourceType: 'voice',
|
|
433
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
411
434
|
});
|
|
412
435
|
|
|
413
436
|
const d1 = createCanonicalGuardianDelivery({
|
|
@@ -436,6 +459,7 @@ describe('canonical-guardian-store', () => {
|
|
|
436
459
|
const req = createCanonicalGuardianRequest({
|
|
437
460
|
kind: 'tool_approval',
|
|
438
461
|
sourceType: 'voice',
|
|
462
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
439
463
|
});
|
|
440
464
|
|
|
441
465
|
const deliveries = listCanonicalGuardianDeliveries(req.id);
|
|
@@ -446,10 +470,12 @@ describe('canonical-guardian-store', () => {
|
|
|
446
470
|
const pendingReq = createCanonicalGuardianRequest({
|
|
447
471
|
kind: 'pending_question',
|
|
448
472
|
sourceType: 'voice',
|
|
473
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
449
474
|
});
|
|
450
475
|
const resolvedReq = createCanonicalGuardianRequest({
|
|
451
476
|
kind: 'pending_question',
|
|
452
477
|
sourceType: 'voice',
|
|
478
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
453
479
|
});
|
|
454
480
|
updateCanonicalGuardianRequest(resolvedReq.id, { status: 'approved' });
|
|
455
481
|
|
|
@@ -476,6 +502,7 @@ describe('canonical-guardian-store', () => {
|
|
|
476
502
|
const req = createCanonicalGuardianRequest({
|
|
477
503
|
kind: 'pending_question',
|
|
478
504
|
sourceType: 'voice',
|
|
505
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
479
506
|
});
|
|
480
507
|
|
|
481
508
|
createCanonicalGuardianDelivery({
|
|
@@ -498,6 +525,7 @@ describe('canonical-guardian-store', () => {
|
|
|
498
525
|
const req = createCanonicalGuardianRequest({
|
|
499
526
|
kind: 'tool_approval',
|
|
500
527
|
sourceType: 'voice',
|
|
528
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
501
529
|
});
|
|
502
530
|
const delivery = createCanonicalGuardianDelivery({
|
|
503
531
|
requestId: req.id,
|
|
@@ -525,6 +553,7 @@ describe('canonical-guardian-store', () => {
|
|
|
525
553
|
const req = createCanonicalGuardianRequest({
|
|
526
554
|
kind: 'pending_question',
|
|
527
555
|
sourceType: 'voice',
|
|
556
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
528
557
|
});
|
|
529
558
|
createCanonicalGuardianDelivery({
|
|
530
559
|
requestId: req.id,
|
|
@@ -544,10 +573,12 @@ describe('canonical-guardian-store', () => {
|
|
|
544
573
|
const pendingReq = createCanonicalGuardianRequest({
|
|
545
574
|
kind: 'pending_question',
|
|
546
575
|
sourceType: 'voice',
|
|
576
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
547
577
|
});
|
|
548
578
|
const resolvedReq = createCanonicalGuardianRequest({
|
|
549
579
|
kind: 'pending_question',
|
|
550
580
|
sourceType: 'voice',
|
|
581
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
551
582
|
});
|
|
552
583
|
updateCanonicalGuardianRequest(resolvedReq.id, { status: 'approved' });
|
|
553
584
|
|
|
@@ -574,6 +605,7 @@ describe('canonical-guardian-store', () => {
|
|
|
574
605
|
const req = createCanonicalGuardianRequest({
|
|
575
606
|
kind: 'pending_question',
|
|
576
607
|
sourceType: 'voice',
|
|
608
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
577
609
|
});
|
|
578
610
|
|
|
579
611
|
// Two delivery rows targeting the same chat for the same request
|
|
@@ -602,6 +634,7 @@ describe('canonical-guardian-store', () => {
|
|
|
602
634
|
const req = createCanonicalGuardianRequest({
|
|
603
635
|
kind: 'pending_question',
|
|
604
636
|
sourceType: 'voice',
|
|
637
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
605
638
|
});
|
|
606
639
|
createCanonicalGuardianDelivery({
|
|
607
640
|
requestId: req.id,
|
|
@@ -620,6 +653,7 @@ describe('canonical-guardian-store', () => {
|
|
|
620
653
|
const req = createCanonicalGuardianRequest({
|
|
621
654
|
kind: 'pending_question',
|
|
622
655
|
sourceType: 'voice',
|
|
656
|
+
guardianPrincipalId: TEST_PRINCIPAL,
|
|
623
657
|
});
|
|
624
658
|
createCanonicalGuardianDelivery({
|
|
625
659
|
requestId: req.id,
|
|
@@ -181,8 +181,8 @@ const TEST_BEARER_TOKEN = 'token';
|
|
|
181
181
|
function makeInboundRequest(overrides: Record<string, unknown> = {}): Request {
|
|
182
182
|
const body: Record<string, unknown> = {
|
|
183
183
|
sourceChannel: 'telegram',
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
conversationExternalId: 'chat-123',
|
|
185
|
+
actorExternalId: 'telegram-user-default',
|
|
186
186
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
187
187
|
content: 'hello',
|
|
188
188
|
replyCallbackUrl: 'https://gateway.test/deliver',
|
|
@@ -516,11 +516,11 @@ describe('empty content with callbackData bypasses validation', () => {
|
|
|
516
516
|
const reqBody = {
|
|
517
517
|
sourceChannel: 'telegram',
|
|
518
518
|
interface: 'telegram',
|
|
519
|
-
|
|
519
|
+
conversationExternalId: 'chat-123',
|
|
520
520
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
521
521
|
callbackData: 'apr:req-empty-2:approve_once',
|
|
522
522
|
replyCallbackUrl: 'https://gateway.test/deliver',
|
|
523
|
-
|
|
523
|
+
actorExternalId: 'telegram-user-default',
|
|
524
524
|
};
|
|
525
525
|
const req = new Request('http://localhost/channels/inbound', {
|
|
526
526
|
method: 'POST',
|
|
@@ -780,8 +780,8 @@ describe('SMS channel approval decisions', () => {
|
|
|
780
780
|
const body = {
|
|
781
781
|
sourceChannel: 'sms',
|
|
782
782
|
interface: 'sms',
|
|
783
|
-
|
|
784
|
-
|
|
783
|
+
conversationExternalId: 'sms-chat-123',
|
|
784
|
+
actorExternalId: 'sms-user-default',
|
|
785
785
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
786
786
|
content: 'hello',
|
|
787
787
|
replyCallbackUrl: 'https://gateway.test/deliver',
|
|
@@ -904,10 +904,10 @@ describe('SMS guardian verify intercept', () => {
|
|
|
904
904
|
body: JSON.stringify({
|
|
905
905
|
sourceChannel: 'sms',
|
|
906
906
|
interface: 'sms',
|
|
907
|
-
|
|
907
|
+
conversationExternalId: 'sms-chat-verify',
|
|
908
908
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
909
909
|
content: secret,
|
|
910
|
-
|
|
910
|
+
actorExternalId: 'sms-user-42',
|
|
911
911
|
replyCallbackUrl: 'https://gateway.test/deliver',
|
|
912
912
|
}),
|
|
913
913
|
});
|
|
@@ -945,10 +945,10 @@ describe('SMS guardian verify intercept', () => {
|
|
|
945
945
|
body: JSON.stringify({
|
|
946
946
|
sourceChannel: 'sms',
|
|
947
947
|
interface: 'sms',
|
|
948
|
-
|
|
948
|
+
conversationExternalId: 'sms-chat-verify-fail',
|
|
949
949
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
950
950
|
content: '000000',
|
|
951
|
-
|
|
951
|
+
actorExternalId: 'sms-user-43',
|
|
952
952
|
replyCallbackUrl: 'https://gateway.test/deliver',
|
|
953
953
|
}),
|
|
954
954
|
});
|
|
@@ -998,10 +998,10 @@ describe('SMS guardian verify intercept', () => {
|
|
|
998
998
|
body: JSON.stringify({
|
|
999
999
|
sourceChannel: 'sms',
|
|
1000
1000
|
interface: 'sms',
|
|
1001
|
-
|
|
1001
|
+
conversationExternalId: 'sms-chat-hex-message',
|
|
1002
1002
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
1003
1003
|
content: secret,
|
|
1004
|
-
|
|
1004
|
+
actorExternalId: 'sms-user-hex',
|
|
1005
1005
|
replyCallbackUrl: 'https://gateway.test/deliver',
|
|
1006
1006
|
}),
|
|
1007
1007
|
});
|
|
@@ -1067,9 +1067,9 @@ describe('guardian decision scoping — multiple pending approvals', () => {
|
|
|
1067
1067
|
// The guardian clicks the approval button for the OLDER request
|
|
1068
1068
|
const req = makeInboundRequest({
|
|
1069
1069
|
content: '',
|
|
1070
|
-
|
|
1070
|
+
conversationExternalId: 'guardian-scope-chat',
|
|
1071
1071
|
callbackData: 'apr:req-older:approve_once',
|
|
1072
|
-
|
|
1072
|
+
actorExternalId: 'guardian-scope-user',
|
|
1073
1073
|
});
|
|
1074
1074
|
|
|
1075
1075
|
const res = await handleChannelInbound(req, noopProcessMessage, 'token');
|
|
@@ -1145,8 +1145,8 @@ describe('ambiguous plain-text decision with multiple pending requests', () => {
|
|
|
1145
1145
|
// Guardian sends plain-text "yes" — ambiguous because two approvals are pending
|
|
1146
1146
|
const req = makeInboundRequest({
|
|
1147
1147
|
content: 'yes',
|
|
1148
|
-
|
|
1149
|
-
|
|
1148
|
+
conversationExternalId: 'guardian-ambig-chat',
|
|
1149
|
+
actorExternalId: 'guardian-ambig-user',
|
|
1150
1150
|
});
|
|
1151
1151
|
|
|
1152
1152
|
const res = await handleChannelInbound(
|
|
@@ -1307,7 +1307,7 @@ describe('assistant-scoped guardian verification via handleChannelInbound', () =
|
|
|
1307
1307
|
|
|
1308
1308
|
const req = makeInboundRequest({
|
|
1309
1309
|
content: secret,
|
|
1310
|
-
|
|
1310
|
+
actorExternalId: 'user-default-asst',
|
|
1311
1311
|
});
|
|
1312
1312
|
|
|
1313
1313
|
const res = await handleChannelInbound(req, noopProcessMessage, 'token');
|
|
@@ -1330,7 +1330,7 @@ describe('assistant-scoped guardian verification via handleChannelInbound', () =
|
|
|
1330
1330
|
|
|
1331
1331
|
const req = makeInboundRequest({
|
|
1332
1332
|
content: secret,
|
|
1333
|
-
|
|
1333
|
+
actorExternalId: 'user-for-asst-x',
|
|
1334
1334
|
});
|
|
1335
1335
|
|
|
1336
1336
|
const res = await handleChannelInbound(req, noopProcessMessage, 'token', 'asst-route-X');
|
|
@@ -1356,7 +1356,7 @@ describe('assistant-scoped guardian verification via handleChannelInbound', () =
|
|
|
1356
1356
|
|
|
1357
1357
|
const req = makeInboundRequest({
|
|
1358
1358
|
content: secret,
|
|
1359
|
-
|
|
1359
|
+
actorExternalId: 'user-cross-test',
|
|
1360
1360
|
});
|
|
1361
1361
|
|
|
1362
1362
|
const res = await handleChannelInbound(req, noopProcessMessage, 'token', 'asst-B-cross');
|
|
@@ -1384,7 +1384,7 @@ describe('assistant-scoped guardian verification via handleChannelInbound', () =
|
|
|
1384
1384
|
|
|
1385
1385
|
const req = makeInboundRequest({
|
|
1386
1386
|
content: 'hello from non-self assistant',
|
|
1387
|
-
|
|
1387
|
+
actorExternalId: 'incoming-user',
|
|
1388
1388
|
});
|
|
1389
1389
|
|
|
1390
1390
|
const res = await handleChannelInbound(req, noopProcessMessage, 'token', 'asst-non-self');
|
|
@@ -1467,7 +1467,7 @@ describe('handleChannelInbound gatewayOriginSecret integration', () => {
|
|
|
1467
1467
|
body: JSON.stringify({
|
|
1468
1468
|
sourceChannel: 'telegram',
|
|
1469
1469
|
interface: 'telegram',
|
|
1470
|
-
|
|
1470
|
+
conversationExternalId: 'chat-gw-secret-test',
|
|
1471
1471
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
1472
1472
|
content: 'hello',
|
|
1473
1473
|
}),
|
|
@@ -1494,10 +1494,10 @@ describe('handleChannelInbound gatewayOriginSecret integration', () => {
|
|
|
1494
1494
|
body: JSON.stringify({
|
|
1495
1495
|
sourceChannel: 'telegram',
|
|
1496
1496
|
interface: 'telegram',
|
|
1497
|
-
|
|
1497
|
+
conversationExternalId: 'chat-gw-secret-pass',
|
|
1498
1498
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
1499
1499
|
content: 'hello',
|
|
1500
|
-
|
|
1500
|
+
actorExternalId: 'telegram-user-default',
|
|
1501
1501
|
}),
|
|
1502
1502
|
});
|
|
1503
1503
|
|
|
@@ -1521,10 +1521,10 @@ describe('handleChannelInbound gatewayOriginSecret integration', () => {
|
|
|
1521
1521
|
body: JSON.stringify({
|
|
1522
1522
|
sourceChannel: 'telegram',
|
|
1523
1523
|
interface: 'telegram',
|
|
1524
|
-
|
|
1524
|
+
conversationExternalId: 'chat-gw-fallback',
|
|
1525
1525
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
1526
1526
|
content: 'hello',
|
|
1527
|
-
|
|
1527
|
+
actorExternalId: 'telegram-user-default',
|
|
1528
1528
|
}),
|
|
1529
1529
|
});
|
|
1530
1530
|
|
|
@@ -1745,8 +1745,8 @@ describe('guardian conversational approval via conversation engine', () => {
|
|
|
1745
1745
|
|
|
1746
1746
|
const req = makeInboundRequest({
|
|
1747
1747
|
content: 'hmm what does this do?',
|
|
1748
|
-
|
|
1749
|
-
|
|
1748
|
+
conversationExternalId: 'guardian-conv-chat',
|
|
1749
|
+
actorExternalId: 'guardian-conv-user',
|
|
1750
1750
|
});
|
|
1751
1751
|
|
|
1752
1752
|
const res = await handleChannelInbound(
|
|
@@ -1809,8 +1809,8 @@ describe('guardian conversational approval via conversation engine', () => {
|
|
|
1809
1809
|
|
|
1810
1810
|
const req = makeInboundRequest({
|
|
1811
1811
|
content: 'yes go ahead and run it',
|
|
1812
|
-
|
|
1813
|
-
|
|
1812
|
+
conversationExternalId: 'guardian-nlp-chat',
|
|
1813
|
+
actorExternalId: 'guardian-nlp-user',
|
|
1814
1814
|
});
|
|
1815
1815
|
|
|
1816
1816
|
const res = await handleChannelInbound(
|
|
@@ -1867,9 +1867,9 @@ describe('guardian conversational approval via conversation engine', () => {
|
|
|
1867
1867
|
// Guardian clicks approve_always via callback button
|
|
1868
1868
|
const req = makeInboundRequest({
|
|
1869
1869
|
content: '',
|
|
1870
|
-
|
|
1870
|
+
conversationExternalId: 'guardian-dg-chat',
|
|
1871
1871
|
callbackData: 'apr:req-gdg-1:approve_always',
|
|
1872
|
-
|
|
1872
|
+
actorExternalId: 'guardian-dg-user',
|
|
1873
1873
|
});
|
|
1874
1874
|
|
|
1875
1875
|
const res = await handleChannelInbound(
|
|
@@ -1937,8 +1937,8 @@ describe('guardian conversational approval via conversation engine', () => {
|
|
|
1937
1937
|
|
|
1938
1938
|
const req = makeInboundRequest({
|
|
1939
1939
|
content: 'approve it',
|
|
1940
|
-
|
|
1941
|
-
|
|
1940
|
+
conversationExternalId: 'guardian-multi-chat',
|
|
1941
|
+
actorExternalId: 'guardian-multi-user',
|
|
1942
1942
|
});
|
|
1943
1943
|
|
|
1944
1944
|
const res = await handleChannelInbound(
|
|
@@ -2058,8 +2058,8 @@ describe('keep_pending remains conversational — guardian path', () => {
|
|
|
2058
2058
|
|
|
2059
2059
|
const guardianReq = makeInboundRequest({
|
|
2060
2060
|
content: 'yes',
|
|
2061
|
-
|
|
2062
|
-
|
|
2061
|
+
conversationExternalId: 'guardian-chat-fb',
|
|
2062
|
+
actorExternalId: 'guardian-user-fb',
|
|
2063
2063
|
});
|
|
2064
2064
|
const res = await handleChannelInbound(
|
|
2065
2065
|
guardianReq, noopProcessMessage, 'token', 'self', undefined,
|
|
@@ -2100,8 +2100,8 @@ describe('requester cancel of guardian-gated pending request', () => {
|
|
|
2100
2100
|
// Create requester conversation
|
|
2101
2101
|
const initReq = makeInboundRequest({
|
|
2102
2102
|
content: 'init',
|
|
2103
|
-
|
|
2104
|
-
|
|
2103
|
+
conversationExternalId: 'requester-cancel-chat',
|
|
2104
|
+
actorExternalId: 'requester-cancel-user',
|
|
2105
2105
|
});
|
|
2106
2106
|
await handleChannelInbound(initReq, noopProcessMessage, 'token');
|
|
2107
2107
|
|
|
@@ -2135,8 +2135,8 @@ describe('requester cancel of guardian-gated pending request', () => {
|
|
|
2135
2135
|
|
|
2136
2136
|
const req = makeInboundRequest({
|
|
2137
2137
|
content: 'deny',
|
|
2138
|
-
|
|
2139
|
-
|
|
2138
|
+
conversationExternalId: 'requester-cancel-chat',
|
|
2139
|
+
actorExternalId: 'requester-cancel-user',
|
|
2140
2140
|
});
|
|
2141
2141
|
const res = await handleChannelInbound(
|
|
2142
2142
|
req, noopProcessMessage, 'token', 'self', undefined,
|
|
@@ -2168,8 +2168,8 @@ describe('requester cancel of guardian-gated pending request', () => {
|
|
|
2168
2168
|
|
|
2169
2169
|
const initReq = makeInboundRequest({
|
|
2170
2170
|
content: 'init',
|
|
2171
|
-
|
|
2172
|
-
|
|
2171
|
+
conversationExternalId: 'requester-cancel-chat',
|
|
2172
|
+
actorExternalId: 'requester-cancel-user',
|
|
2173
2173
|
});
|
|
2174
2174
|
await handleChannelInbound(initReq, noopProcessMessage, 'token');
|
|
2175
2175
|
|
|
@@ -2203,8 +2203,8 @@ describe('requester cancel of guardian-gated pending request', () => {
|
|
|
2203
2203
|
|
|
2204
2204
|
const req = makeInboundRequest({
|
|
2205
2205
|
content: 'actually never mind, cancel it',
|
|
2206
|
-
|
|
2207
|
-
|
|
2206
|
+
conversationExternalId: 'requester-cancel-chat',
|
|
2207
|
+
actorExternalId: 'requester-cancel-user',
|
|
2208
2208
|
});
|
|
2209
2209
|
const res = await handleChannelInbound(
|
|
2210
2210
|
req, noopProcessMessage, 'token', 'self', undefined,
|
|
@@ -2229,8 +2229,8 @@ describe('requester cancel of guardian-gated pending request', () => {
|
|
|
2229
2229
|
|
|
2230
2230
|
const initReq = makeInboundRequest({
|
|
2231
2231
|
content: 'init',
|
|
2232
|
-
|
|
2233
|
-
|
|
2232
|
+
conversationExternalId: 'requester-cancel-chat',
|
|
2233
|
+
actorExternalId: 'requester-cancel-user',
|
|
2234
2234
|
});
|
|
2235
2235
|
await handleChannelInbound(initReq, noopProcessMessage, 'token');
|
|
2236
2236
|
|
|
@@ -2264,8 +2264,8 @@ describe('requester cancel of guardian-gated pending request', () => {
|
|
|
2264
2264
|
|
|
2265
2265
|
const req = makeInboundRequest({
|
|
2266
2266
|
content: 'what is happening?',
|
|
2267
|
-
|
|
2268
|
-
|
|
2267
|
+
conversationExternalId: 'requester-cancel-chat',
|
|
2268
|
+
actorExternalId: 'requester-cancel-user',
|
|
2269
2269
|
});
|
|
2270
2270
|
const res = await handleChannelInbound(
|
|
2271
2271
|
req, noopProcessMessage, 'token', 'self', undefined,
|
|
@@ -2290,8 +2290,8 @@ describe('requester cancel of guardian-gated pending request', () => {
|
|
|
2290
2290
|
|
|
2291
2291
|
const initReq = makeInboundRequest({
|
|
2292
2292
|
content: 'init',
|
|
2293
|
-
|
|
2294
|
-
|
|
2293
|
+
conversationExternalId: 'requester-cancel-chat',
|
|
2294
|
+
actorExternalId: 'requester-cancel-user',
|
|
2295
2295
|
});
|
|
2296
2296
|
await handleChannelInbound(initReq, noopProcessMessage, 'token');
|
|
2297
2297
|
|
|
@@ -2321,8 +2321,8 @@ describe('requester cancel of guardian-gated pending request', () => {
|
|
|
2321
2321
|
// Requester tries to self-approve while guardian approval is pending.
|
|
2322
2322
|
const req = makeInboundRequest({
|
|
2323
2323
|
content: 'approve',
|
|
2324
|
-
|
|
2325
|
-
|
|
2324
|
+
conversationExternalId: 'requester-cancel-chat',
|
|
2325
|
+
actorExternalId: 'requester-cancel-user',
|
|
2326
2326
|
});
|
|
2327
2327
|
const res = await handleChannelInbound(req, noopProcessMessage, 'token');
|
|
2328
2328
|
const body = await res.json() as Record<string, unknown>;
|
|
@@ -2443,8 +2443,8 @@ describe('engine decision race condition — guardian path', () => {
|
|
|
2443
2443
|
|
|
2444
2444
|
const guardianReq = makeInboundRequest({
|
|
2445
2445
|
content: 'approve it',
|
|
2446
|
-
|
|
2447
|
-
|
|
2446
|
+
conversationExternalId: 'guardian-race-chat',
|
|
2447
|
+
actorExternalId: 'guardian-race-user',
|
|
2448
2448
|
});
|
|
2449
2449
|
const res = await handleChannelInbound(
|
|
2450
2450
|
guardianReq, noopProcessMessage, 'token', 'self', undefined,
|
|
@@ -2789,6 +2789,7 @@ describe('NL approval routing via destination-scoped canonical requests', () =>
|
|
|
2789
2789
|
sourceChannel: 'twilio',
|
|
2790
2790
|
conversationId: 'conv-voice-nl-1',
|
|
2791
2791
|
toolName: 'shell',
|
|
2792
|
+
guardianPrincipalId: 'test-principal-id',
|
|
2792
2793
|
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
2793
2794
|
// guardianExternalUserId intentionally omitted
|
|
2794
2795
|
});
|
|
@@ -2806,8 +2807,8 @@ describe('NL approval routing via destination-scoped canonical requests', () =>
|
|
|
2806
2807
|
// Send inbound guardian text reply "yes" from that chat
|
|
2807
2808
|
const req = makeInboundRequest({
|
|
2808
2809
|
sourceChannel: 'telegram',
|
|
2809
|
-
|
|
2810
|
-
|
|
2810
|
+
conversationExternalId: guardianChatId,
|
|
2811
|
+
actorExternalId: guardianUserId,
|
|
2811
2812
|
content: 'yes',
|
|
2812
2813
|
externalMessageId: `msg-nl-approve-${Date.now()}`,
|
|
2813
2814
|
});
|
|
@@ -2842,6 +2843,7 @@ describe('NL approval routing via destination-scoped canonical requests', () =>
|
|
|
2842
2843
|
sourceType: 'voice',
|
|
2843
2844
|
sourceChannel: 'twilio',
|
|
2844
2845
|
toolName: 'shell',
|
|
2846
|
+
guardianPrincipalId: 'test-principal-id',
|
|
2845
2847
|
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
2846
2848
|
});
|
|
2847
2849
|
|
|
@@ -2855,8 +2857,8 @@ describe('NL approval routing via destination-scoped canonical requests', () =>
|
|
|
2855
2857
|
// Send from differentChatId — delivery-scoped lookup should not match
|
|
2856
2858
|
const req = makeInboundRequest({
|
|
2857
2859
|
sourceChannel: 'telegram',
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
+
conversationExternalId: differentChatId,
|
|
2861
|
+
actorExternalId: guardianUserId,
|
|
2860
2862
|
content: 'approve',
|
|
2861
2863
|
externalMessageId: `msg-nl-mismatch-${Date.now()}`,
|
|
2862
2864
|
});
|
|
@@ -2897,8 +2899,8 @@ describe('trusted-contact self-approval blocked before guardian approval row exi
|
|
|
2897
2899
|
// Create the requester conversation (different user than guardian)
|
|
2898
2900
|
const initReq = makeInboundRequest({
|
|
2899
2901
|
content: 'init',
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
+
conversationExternalId: 'tc-selfapproval-chat',
|
|
2903
|
+
actorExternalId: 'tc-selfapproval-user',
|
|
2902
2904
|
});
|
|
2903
2905
|
await handleChannelInbound(initReq, noopProcessMessage, 'token');
|
|
2904
2906
|
|
|
@@ -2925,8 +2927,8 @@ describe('trusted-contact self-approval blocked before guardian approval row exi
|
|
|
2925
2927
|
// Trusted contact sends "yes" to try to self-approve
|
|
2926
2928
|
const req = makeInboundRequest({
|
|
2927
2929
|
content: 'yes',
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
+
conversationExternalId: 'tc-selfapproval-chat',
|
|
2931
|
+
actorExternalId: 'tc-selfapproval-user',
|
|
2930
2932
|
});
|
|
2931
2933
|
const res = await handleChannelInbound(
|
|
2932
2934
|
req, noopProcessMessage, 'token', 'self', undefined,
|
|
@@ -2953,8 +2955,8 @@ describe('trusted-contact self-approval blocked before guardian approval row exi
|
|
|
2953
2955
|
|
|
2954
2956
|
const initReq = makeInboundRequest({
|
|
2955
2957
|
content: 'init',
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
+
conversationExternalId: 'tc-selfapproval-chat',
|
|
2959
|
+
actorExternalId: 'tc-selfapproval-user',
|
|
2958
2960
|
});
|
|
2959
2961
|
await handleChannelInbound(initReq, noopProcessMessage, 'token');
|
|
2960
2962
|
|
|
@@ -2972,8 +2974,8 @@ describe('trusted-contact self-approval blocked before guardian approval row exi
|
|
|
2972
2974
|
// "approve" would normally be parsed as an approval decision.
|
|
2973
2975
|
const req = makeInboundRequest({
|
|
2974
2976
|
content: 'approve',
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
+
conversationExternalId: 'tc-selfapproval-chat',
|
|
2978
|
+
actorExternalId: 'tc-selfapproval-user',
|
|
2977
2979
|
});
|
|
2978
2980
|
const res = await handleChannelInbound(req, noopProcessMessage, 'token');
|
|
2979
2981
|
const body = await res.json() as Record<string, unknown>;
|