@vellumai/assistant 0.4.4 → 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
|
@@ -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>;
|
|
@@ -118,6 +118,7 @@ function makeCanonicalRequest(overrides: Record<string, unknown> = {}) {
|
|
|
118
118
|
conversationId: 'conv-1',
|
|
119
119
|
requesterExternalUserId: 'requester-1',
|
|
120
120
|
guardianExternalUserId: 'guardian-1',
|
|
121
|
+
guardianPrincipalId: 'test-principal-id',
|
|
121
122
|
toolName: 'bash',
|
|
122
123
|
status: 'pending',
|
|
123
124
|
requestCode: generateCanonicalRequestCode(),
|
|
@@ -119,8 +119,8 @@ function makeInboundRequest(overrides: Record<string, unknown> = {}): Request {
|
|
|
119
119
|
const body = {
|
|
120
120
|
sourceChannel: 'telegram',
|
|
121
121
|
interface: 'telegram',
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
conversationExternalId: 'chat-123',
|
|
123
|
+
actorExternalId: 'telegram-user-default',
|
|
124
124
|
externalMessageId: `msg-${Date.now()}-${Math.random()}`,
|
|
125
125
|
content: 'hello',
|
|
126
126
|
replyCallbackUrl: 'https://gateway.test/deliver',
|
|
@@ -257,11 +257,11 @@ describe('Verification control messages are deterministic (guard)', () => {
|
|
|
257
257
|
body: JSON.stringify({
|
|
258
258
|
sourceChannel: 'telegram',
|
|
259
259
|
interface: 'telegram',
|
|
260
|
-
|
|
260
|
+
conversationExternalId: 'chat-123',
|
|
261
261
|
externalMessageId: `msg-guard-${Date.now()}`,
|
|
262
262
|
content: secret,
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
actorExternalId: 'user-123',
|
|
264
|
+
actorDisplayName: 'Test User',
|
|
265
265
|
replyCallbackUrl: 'http://localhost/callback',
|
|
266
266
|
}),
|
|
267
267
|
});
|
|
@@ -330,11 +330,11 @@ describe('Verification control messages are deterministic (guard)', () => {
|
|
|
330
330
|
body: JSON.stringify({
|
|
331
331
|
sourceChannel: 'telegram',
|
|
332
332
|
interface: 'telegram',
|
|
333
|
-
|
|
333
|
+
conversationExternalId: 'chat-bootstrap-123',
|
|
334
334
|
externalMessageId: `msg-bootstrap-${Date.now()}`,
|
|
335
335
|
content: `/start gv_${bootstrapToken}`,
|
|
336
|
-
|
|
337
|
-
|
|
336
|
+
actorExternalId: 'user-bootstrap-123',
|
|
337
|
+
actorDisplayName: 'Bootstrap User',
|
|
338
338
|
replyCallbackUrl: 'http://localhost/callback',
|
|
339
339
|
sourceMetadata: {
|
|
340
340
|
commandIntent: { type: 'start', payload: `gv_${bootstrapToken}` },
|
|
@@ -94,6 +94,7 @@ function createTestCanonicalRequest(overrides: {
|
|
|
94
94
|
kind?: string;
|
|
95
95
|
toolName?: string;
|
|
96
96
|
guardianExternalUserId?: string;
|
|
97
|
+
guardianPrincipalId?: string;
|
|
97
98
|
questionText?: string;
|
|
98
99
|
expiresAt?: string;
|
|
99
100
|
}) {
|
|
@@ -105,6 +106,7 @@ function createTestCanonicalRequest(overrides: {
|
|
|
105
106
|
sourceChannel: 'vellum',
|
|
106
107
|
conversationId: overrides.conversationId,
|
|
107
108
|
guardianExternalUserId: overrides.guardianExternalUserId,
|
|
109
|
+
guardianPrincipalId: overrides.guardianPrincipalId ?? 'test-principal',
|
|
108
110
|
toolName: overrides.toolName ?? 'bash',
|
|
109
111
|
questionText: overrides.questionText,
|
|
110
112
|
requestCode: generateCanonicalRequestCode(),
|
|
@@ -304,7 +306,7 @@ describe('HTTP handleGuardianActionDecision', () => {
|
|
|
304
306
|
expect(body.requestId).toBe('req-stale-1');
|
|
305
307
|
});
|
|
306
308
|
|
|
307
|
-
test('passes actorContext with
|
|
309
|
+
test('passes actorContext with vellum channel and guardianPrincipalId', async () => {
|
|
308
310
|
createTestCanonicalRequest({ conversationId: 'conv-actor', requestId: 'req-actor-1' });
|
|
309
311
|
mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({ applied: true, requestId: 'req-actor-1', grantMinted: false });
|
|
310
312
|
|
|
@@ -315,9 +317,8 @@ describe('HTTP handleGuardianActionDecision', () => {
|
|
|
315
317
|
await handleGuardianActionDecision(req, mockLoopbackServer);
|
|
316
318
|
const call = mockApplyCanonicalGuardianDecision.mock.calls[0]![0] as Record<string, unknown>;
|
|
317
319
|
const actorContext = call.actorContext as Record<string, unknown>;
|
|
318
|
-
expect(actorContext.externalUserId).toBeUndefined();
|
|
319
320
|
expect(actorContext.channel).toBe('vellum');
|
|
320
|
-
expect(actorContext.
|
|
321
|
+
expect(actorContext.guardianPrincipalId).toBeDefined();
|
|
321
322
|
});
|
|
322
323
|
});
|
|
323
324
|
|
|
@@ -373,6 +374,7 @@ describe('listGuardianDecisionPrompts', () => {
|
|
|
373
374
|
sourceType: 'desktop',
|
|
374
375
|
sourceChannel: 'vellum',
|
|
375
376
|
conversationId: 'conv-expired',
|
|
377
|
+
guardianPrincipalId: 'test-principal',
|
|
376
378
|
toolName: 'bash',
|
|
377
379
|
requestCode: generateCanonicalRequestCode(),
|
|
378
380
|
status: 'pending',
|
|
@@ -597,7 +599,7 @@ describe('IPC guardian_action_decision', () => {
|
|
|
597
599
|
expect(sent[0].reason).toBe('already_resolved');
|
|
598
600
|
});
|
|
599
601
|
|
|
600
|
-
test('passes actorContext with
|
|
602
|
+
test('passes actorContext with vellum channel and guardianPrincipalId', async () => {
|
|
601
603
|
createTestCanonicalRequest({ conversationId: 'conv-ipc-actor', requestId: 'req-ipc-actor' });
|
|
602
604
|
mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({ applied: true, requestId: 'req-ipc-actor', grantMinted: false });
|
|
603
605
|
|
|
@@ -609,9 +611,8 @@ describe('IPC guardian_action_decision', () => {
|
|
|
609
611
|
);
|
|
610
612
|
const call = mockApplyCanonicalGuardianDecision.mock.calls[0]![0] as Record<string, unknown>;
|
|
611
613
|
const actorContext = call.actorContext as Record<string, unknown>;
|
|
612
|
-
expect(actorContext.externalUserId).toBeUndefined();
|
|
613
614
|
expect(actorContext.channel).toBe('vellum');
|
|
614
|
-
expect(actorContext.
|
|
615
|
+
expect(actorContext.guardianPrincipalId).toBeDefined();
|
|
615
616
|
});
|
|
616
617
|
});
|
|
617
618
|
|