@vellumai/assistant 0.4.5 → 0.4.6
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 +4 -4
- package/README.md +6 -6
- package/bun.lock +6 -2
- package/docs/architecture/memory.md +4 -4
- package/package.json +2 -2
- package/src/__tests__/actor-token-service.test.ts +5 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +1 -0
- package/src/__tests__/call-controller.test.ts +78 -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__/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__/guardian-actions-endpoint.test.ts +7 -6
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +57 -12
- 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 +4 -4
- 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 +50 -47
- package/src/__tests__/relay-server.test.ts +71 -0
- package/src/__tests__/send-endpoint-busy.test.ts +6 -0
- 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 +9 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +8 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +22 -22
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -4
- 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-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 +13 -13
- 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/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-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 +8 -0
- package/src/memory/guardian-bindings.ts +4 -0
- package/src/memory/job-handlers/backfill.ts +2 -9
- 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 +2 -0
- package/src/memory/migrations/registry.ts +5 -0
- package/src/memory/schema.ts +3 -0
- package/src/notifications/copy-composer.ts +2 -2
- package/src/runtime/access-request-helper.ts +43 -28
- package/src/runtime/actor-trust-resolver.ts +19 -14
- package/src/runtime/channel-guardian-service.ts +6 -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-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 +1 -0
- package/src/runtime/routes/inbound-conversation.ts +7 -7
- package/src/runtime/routes/inbound-message-handler.ts +105 -94
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/util/logger.ts +10 -0
- package/src/daemon/call-pointer-generators.ts +0 -59
|
@@ -25,8 +25,8 @@ mock.module('../util/logger.js', () => ({
|
|
|
25
25
|
}),
|
|
26
26
|
}));
|
|
27
27
|
|
|
28
|
-
import { addPointerMessage, formatDuration,
|
|
29
|
-
import { getMessages } from '../memory/conversation-store.js';
|
|
28
|
+
import { addPointerMessage, formatDuration, resetPointerMessageProcessor, setPointerMessageProcessor } from '../calls/call-pointer-messages.js';
|
|
29
|
+
import { addMessage, getMessages } from '../memory/conversation-store.js';
|
|
30
30
|
import { getDb, initializeDb, resetDb } from '../memory/db.js';
|
|
31
31
|
import { conversations } from '../memory/schema.js';
|
|
32
32
|
|
|
@@ -95,7 +95,7 @@ describe('addPointerMessage', () => {
|
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
afterEach(() => {
|
|
98
|
-
|
|
98
|
+
resetPointerMessageProcessor();
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
afterAll(() => {
|
|
@@ -192,87 +192,149 @@ describe('addPointerMessage', () => {
|
|
|
192
192
|
expect(text).toContain('failed: Max attempts exceeded');
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
-
// Trust-aware tests
|
|
196
|
-
// short-circuits to fallback), so these validate the trust gating path
|
|
197
|
-
// while still receiving deterministic text.
|
|
195
|
+
// Trust-aware tests
|
|
198
196
|
|
|
199
|
-
test('untrusted audience uses deterministic fallback even with
|
|
197
|
+
test('untrusted audience uses deterministic fallback even with processor set', () => {
|
|
200
198
|
const convId = 'conv-ptr-untrusted';
|
|
201
|
-
// standard threadType + no origin channel = untrusted
|
|
202
199
|
ensureConversation(convId, { threadType: 'standard' });
|
|
203
200
|
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return 'generated text';
|
|
201
|
+
const processorCalled = { value: false };
|
|
202
|
+
setPointerMessageProcessor(async () => {
|
|
203
|
+
processorCalled.value = true;
|
|
208
204
|
});
|
|
209
205
|
|
|
210
206
|
addPointerMessage(convId, 'started', '+15551234567');
|
|
211
207
|
const text = getLatestAssistantText(convId);
|
|
212
|
-
// In test env, deterministic fallback is always used regardless of trust
|
|
213
208
|
expect(text).toContain('Call to +15551234567 started');
|
|
209
|
+
// processor not called because standard threadType + no origin = untrusted
|
|
210
|
+
expect(processorCalled.value).toBe(false);
|
|
214
211
|
});
|
|
215
212
|
|
|
216
|
-
test('explicit untrusted audience mode skips
|
|
213
|
+
test('explicit untrusted audience mode skips processor', () => {
|
|
217
214
|
const convId = 'conv-ptr-explicit-untrusted';
|
|
218
215
|
ensureConversation(convId, { threadType: 'private' });
|
|
219
216
|
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return 'generated text';
|
|
217
|
+
const processorCalled = { value: false };
|
|
218
|
+
setPointerMessageProcessor(async () => {
|
|
219
|
+
processorCalled.value = true;
|
|
224
220
|
});
|
|
225
221
|
|
|
226
222
|
addPointerMessage(convId, 'started', '+15551234567', undefined, 'untrusted');
|
|
227
223
|
const text = getLatestAssistantText(convId);
|
|
228
224
|
expect(text).toContain('Call to +15551234567 started');
|
|
229
|
-
|
|
230
|
-
expect(generatorCalled.value).toBe(false);
|
|
225
|
+
expect(processorCalled.value).toBe(false);
|
|
231
226
|
});
|
|
232
227
|
|
|
233
|
-
test('
|
|
234
|
-
const convId = 'conv-ptr-
|
|
228
|
+
test('trusted audience routes through daemon processor with required facts', async () => {
|
|
229
|
+
const convId = 'conv-ptr-trusted';
|
|
235
230
|
ensureConversation(convId, { threadType: 'private' });
|
|
236
231
|
|
|
237
|
-
|
|
232
|
+
let capturedInstruction = '';
|
|
233
|
+
let capturedFacts: string[] = [];
|
|
234
|
+
setPointerMessageProcessor(async (_convId, instruction, requiredFacts) => {
|
|
235
|
+
capturedInstruction = instruction;
|
|
236
|
+
capturedFacts = requiredFacts ?? [];
|
|
237
|
+
});
|
|
238
238
|
|
|
239
239
|
await addPointerMessage(convId, 'completed', '+15559876543', { duration: '1m' });
|
|
240
|
+
// Processor was called with a structured instruction
|
|
241
|
+
expect(capturedInstruction).toContain('[CALL_STATUS_EVENT]');
|
|
242
|
+
expect(capturedInstruction).toContain('+15559876543');
|
|
243
|
+
expect(capturedInstruction).toContain('completed');
|
|
244
|
+
expect(capturedInstruction).toContain('1m');
|
|
245
|
+
// Required facts include phone number, duration, and outcome keyword
|
|
246
|
+
expect(capturedFacts).toContain('+15559876543');
|
|
247
|
+
expect(capturedFacts).toContain('1m');
|
|
248
|
+
expect(capturedFacts).toContain('completed');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test('trusted audience falls back to deterministic on processor failure', async () => {
|
|
252
|
+
const convId = 'conv-ptr-processor-fail';
|
|
253
|
+
ensureConversation(convId, { threadType: 'private' });
|
|
254
|
+
|
|
255
|
+
setPointerMessageProcessor(async () => {
|
|
256
|
+
throw new Error('Daemon unavailable');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
await addPointerMessage(convId, 'failed', '+15559876543', { reason: 'busy' });
|
|
260
|
+
// Falls back to deterministic — written directly to conversation store
|
|
240
261
|
const text = getLatestAssistantText(convId);
|
|
241
|
-
|
|
242
|
-
expect(text).toContain('Call to +15559876543 completed (1m)');
|
|
262
|
+
expect(text).toContain('failed: busy');
|
|
243
263
|
});
|
|
244
264
|
|
|
245
265
|
test('vellum origin channel is detected as trusted audience', async () => {
|
|
246
266
|
const convId = 'conv-ptr-vellum';
|
|
247
267
|
ensureConversation(convId, { originChannel: 'vellum' });
|
|
248
268
|
|
|
249
|
-
|
|
269
|
+
let processorCalled = false;
|
|
270
|
+
setPointerMessageProcessor(async () => {
|
|
271
|
+
processorCalled = true;
|
|
272
|
+
});
|
|
250
273
|
|
|
251
274
|
await addPointerMessage(convId, 'failed', '+15559876543', { reason: 'busy' });
|
|
252
|
-
|
|
253
|
-
expect(text).toContain('failed: busy');
|
|
275
|
+
expect(processorCalled).toBe(true);
|
|
254
276
|
});
|
|
255
277
|
|
|
256
278
|
test('missing conversation defaults to untrusted', () => {
|
|
257
|
-
const
|
|
258
|
-
|
|
279
|
+
const convId = 'conv-ptr-no-signals';
|
|
280
|
+
ensureConversation(convId);
|
|
281
|
+
|
|
282
|
+
const processorCalled = { value: false };
|
|
283
|
+
setPointerMessageProcessor(async () => {
|
|
284
|
+
processorCalled.value = true;
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
addPointerMessage(convId, 'started', '+15551234567');
|
|
288
|
+
const text = getLatestAssistantText(convId);
|
|
289
|
+
expect(text).toContain('Call to +15551234567 started');
|
|
290
|
+
expect(processorCalled.value).toBe(false);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Provenance trust class tests
|
|
294
|
+
|
|
295
|
+
test('guardian provenance trust class is detected as trusted audience', async () => {
|
|
296
|
+
const convId = 'conv-ptr-guardian-provenance';
|
|
297
|
+
ensureConversation(convId, { threadType: 'standard' });
|
|
298
|
+
// Add a user message with guardian provenance metadata
|
|
299
|
+
await addMessage(convId, 'user', 'hello', { provenanceTrustClass: 'guardian' });
|
|
300
|
+
|
|
301
|
+
let processorCalled = false;
|
|
302
|
+
setPointerMessageProcessor(async () => {
|
|
303
|
+
processorCalled = true;
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
await addPointerMessage(convId, 'completed', '+15559876543');
|
|
307
|
+
expect(processorCalled).toBe(true);
|
|
308
|
+
});
|
|
259
309
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
310
|
+
test('trusted_contact provenance trust class is detected as trusted audience', async () => {
|
|
311
|
+
const convId = 'conv-ptr-tc-provenance';
|
|
312
|
+
ensureConversation(convId, { threadType: 'standard' });
|
|
313
|
+
// Add a user message with trusted_contact provenance metadata
|
|
314
|
+
await addMessage(convId, 'user', 'hello', { provenanceTrustClass: 'trusted_contact' });
|
|
315
|
+
|
|
316
|
+
let processorCalled = false;
|
|
317
|
+
setPointerMessageProcessor(async () => {
|
|
318
|
+
processorCalled = true;
|
|
264
319
|
});
|
|
265
320
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
321
|
+
await addPointerMessage(convId, 'completed', '+15559876543');
|
|
322
|
+
expect(processorCalled).toBe(true);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test('unknown provenance trust class does not grant trusted audience', () => {
|
|
326
|
+
const convId = 'conv-ptr-unknown-provenance';
|
|
327
|
+
ensureConversation(convId, { threadType: 'standard' });
|
|
328
|
+
addMessage(convId, 'user', 'hello', { provenanceTrustClass: 'unknown' });
|
|
271
329
|
|
|
272
|
-
|
|
273
|
-
|
|
330
|
+
const processorCalled = { value: false };
|
|
331
|
+
setPointerMessageProcessor(async () => {
|
|
332
|
+
processorCalled.value = true;
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
addPointerMessage(convId, 'started', '+15551234567');
|
|
336
|
+
const text = getLatestAssistantText(convId);
|
|
274
337
|
expect(text).toContain('Call to +15551234567 started');
|
|
275
|
-
|
|
276
|
-
expect(generatorCalled.value).toBe(false);
|
|
338
|
+
expect(processorCalled.value).toBe(false);
|
|
277
339
|
});
|
|
278
340
|
});
|
|
@@ -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,
|