@vellumai/assistant 0.4.13 → 0.4.15

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.
Files changed (133) hide show
  1. package/ARCHITECTURE.md +77 -38
  2. package/README.md +10 -12
  3. package/package.json +1 -1
  4. package/src/__tests__/actor-token-service.test.ts +108 -522
  5. package/src/__tests__/channel-approval-routes.test.ts +92 -239
  6. package/src/__tests__/channel-approval.test.ts +100 -0
  7. package/src/__tests__/conversation-routes-guardian-reply.test.ts +13 -6
  8. package/src/__tests__/conversation-routes.test.ts +11 -4
  9. package/src/__tests__/guardian-actions-endpoint.test.ts +26 -19
  10. package/src/__tests__/mcp-health-check.test.ts +65 -0
  11. package/src/__tests__/permission-types.test.ts +33 -0
  12. package/src/__tests__/scan-result-store.test.ts +121 -0
  13. package/src/__tests__/session-agent-loop.test.ts +120 -0
  14. package/src/__tests__/session-approval-overrides.test.ts +205 -0
  15. package/src/__tests__/session-surfaces-task-progress.test.ts +38 -0
  16. package/src/amazon/client.ts +8 -5
  17. package/src/approvals/guardian-decision-primitive.ts +14 -9
  18. package/src/approvals/guardian-request-resolvers.ts +2 -2
  19. package/src/calls/call-controller.ts +2 -2
  20. package/src/calls/twilio-routes.ts +2 -2
  21. package/src/cli/mcp.ts +3 -3
  22. package/src/cli.ts +24 -0
  23. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +19 -130
  24. package/src/config/bundled-skills/doordash/__tests__/doordash-client.test.ts +8 -6
  25. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
  26. package/src/config/bundled-skills/messaging/SKILL.md +49 -14
  27. package/src/config/bundled-skills/messaging/TOOLS.json +52 -9
  28. package/src/config/bundled-skills/messaging/tools/gmail-batch-archive.ts +35 -11
  29. package/src/config/bundled-skills/messaging/tools/gmail-draft.ts +3 -1
  30. package/src/config/bundled-skills/messaging/tools/gmail-forward.ts +5 -6
  31. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +10 -2
  32. package/src/config/bundled-skills/messaging/tools/gmail-send-draft.ts +20 -0
  33. package/src/config/bundled-skills/messaging/tools/gmail-send-with-attachments.ts +3 -4
  34. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -8
  35. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +76 -0
  36. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +10 -0
  37. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +11 -3
  38. package/src/config/bundled-skills/messaging/tools/scan-result-store.ts +86 -0
  39. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  40. package/src/config/bundled-skills/skills-catalog/SKILL.md +31 -8
  41. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
  42. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
  43. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
  44. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
  45. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +79 -24
  46. package/src/config/bundled-skills/sms-setup/SKILL.md +1 -1
  47. package/src/config/bundled-skills/telegram-setup/SKILL.md +1 -1
  48. package/src/config/bundled-skills/twilio-setup/SKILL.md +1 -1
  49. package/src/daemon/approval-generators.ts +6 -3
  50. package/src/daemon/handlers/config-ingress.ts +2 -6
  51. package/src/daemon/handlers/guardian-actions.ts +1 -1
  52. package/src/daemon/handlers/sessions.ts +4 -1
  53. package/src/daemon/handlers/shared.ts +3 -0
  54. package/src/daemon/handlers/skills.ts +32 -0
  55. package/src/daemon/ipc-contract/messages.ts +3 -1
  56. package/src/daemon/ipc-handler.ts +24 -0
  57. package/src/daemon/ipc-validate.ts +1 -1
  58. package/src/daemon/lifecycle.ts +6 -8
  59. package/src/daemon/server.ts +8 -3
  60. package/src/daemon/session-agent-loop.ts +19 -1
  61. package/src/daemon/session-attachments.ts +2 -1
  62. package/src/daemon/session-history.ts +2 -2
  63. package/src/daemon/session-process.ts +5 -9
  64. package/src/daemon/session-surfaces.ts +17 -1
  65. package/src/daemon/session-tool-setup.ts +216 -69
  66. package/src/daemon/session.ts +24 -1
  67. package/src/events/domain-events.ts +1 -1
  68. package/src/events/tool-domain-event-publisher.ts +5 -10
  69. package/src/influencer/client.ts +8 -7
  70. package/src/messaging/providers/gmail/client.ts +33 -1
  71. package/src/messaging/providers/gmail/mime-builder.ts +5 -1
  72. package/src/messaging/providers/sms/adapter.ts +3 -7
  73. package/src/messaging/providers/telegram-bot/adapter.ts +3 -7
  74. package/src/messaging/providers/whatsapp/adapter.ts +3 -7
  75. package/src/notifications/adapters/sms.ts +2 -2
  76. package/src/notifications/adapters/telegram.ts +2 -2
  77. package/src/permissions/prompter.ts +2 -0
  78. package/src/permissions/types.ts +11 -1
  79. package/src/runtime/approval-conversation-turn.ts +4 -0
  80. package/src/runtime/auth/__tests__/context.test.ts +130 -0
  81. package/src/runtime/auth/__tests__/credential-service.test.ts +277 -0
  82. package/src/runtime/auth/__tests__/guard-tests.test.ts +289 -0
  83. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +71 -0
  84. package/src/runtime/auth/__tests__/middleware.test.ts +239 -0
  85. package/src/runtime/auth/__tests__/policy.test.ts +29 -0
  86. package/src/runtime/auth/__tests__/route-policy.test.ts +166 -0
  87. package/src/runtime/auth/__tests__/scopes.test.ts +109 -0
  88. package/src/runtime/auth/__tests__/subject.test.ts +149 -0
  89. package/src/runtime/auth/__tests__/token-service.test.ts +263 -0
  90. package/src/runtime/auth/context.ts +62 -0
  91. package/src/runtime/{actor-refresh-token-service.ts → auth/credential-service.ts} +112 -79
  92. package/src/runtime/auth/external-assistant-id.ts +69 -0
  93. package/src/runtime/auth/index.ts +37 -0
  94. package/src/runtime/auth/middleware.ts +127 -0
  95. package/src/runtime/auth/policy.ts +17 -0
  96. package/src/runtime/auth/route-policy.ts +261 -0
  97. package/src/runtime/auth/scopes.ts +64 -0
  98. package/src/runtime/auth/subject.ts +68 -0
  99. package/src/runtime/auth/token-service.ts +275 -0
  100. package/src/runtime/auth/types.ts +79 -0
  101. package/src/runtime/channel-approval-parser.ts +11 -5
  102. package/src/runtime/channel-approval-types.ts +1 -1
  103. package/src/runtime/channel-approvals.ts +22 -1
  104. package/src/runtime/guardian-action-followup-executor.ts +2 -2
  105. package/src/runtime/guardian-context-resolver.ts +15 -0
  106. package/src/runtime/guardian-decision-types.ts +23 -6
  107. package/src/runtime/guardian-outbound-actions.ts +4 -22
  108. package/src/runtime/guardian-reply-router.ts +5 -3
  109. package/src/runtime/http-server.ts +210 -182
  110. package/src/runtime/http-types.ts +11 -1
  111. package/src/runtime/local-actor-identity.ts +25 -0
  112. package/src/runtime/pending-interactions.ts +1 -0
  113. package/src/runtime/routes/approval-routes.ts +42 -59
  114. package/src/runtime/routes/channel-route-shared.ts +9 -41
  115. package/src/runtime/routes/channel-routes.ts +0 -2
  116. package/src/runtime/routes/conversation-routes.ts +39 -49
  117. package/src/runtime/routes/events-routes.ts +15 -22
  118. package/src/runtime/routes/guardian-action-routes.ts +46 -51
  119. package/src/runtime/routes/guardian-approval-interception.ts +6 -5
  120. package/src/runtime/routes/guardian-bootstrap-routes.ts +12 -8
  121. package/src/runtime/routes/guardian-refresh-routes.ts +2 -2
  122. package/src/runtime/routes/inbound-message-handler.ts +39 -45
  123. package/src/runtime/routes/pairing-routes.ts +9 -9
  124. package/src/runtime/routes/secret-routes.ts +90 -45
  125. package/src/runtime/routes/surface-action-routes.ts +12 -2
  126. package/src/runtime/routes/trust-rules-routes.ts +13 -0
  127. package/src/runtime/routes/twilio-routes.ts +3 -3
  128. package/src/runtime/session-approval-overrides.ts +86 -0
  129. package/src/security/keychain-to-encrypted-migration.ts +8 -1
  130. package/src/skills/frontmatter.ts +44 -1
  131. package/src/tools/permission-checker.ts +226 -74
  132. package/src/runtime/actor-token-service.ts +0 -234
  133. package/src/runtime/middleware/actor-token.ts +0 -265
@@ -80,16 +80,17 @@ import {
80
80
  } from '../memory/channel-guardian-store.js';
81
81
  import { getDb, initializeDb, resetDb } from '../memory/db.js';
82
82
  import { conversations, externalConversationBindings } from '../memory/schema.js';
83
+ import { initAuthSigningKey } from '../runtime/auth/token-service.js';
83
84
  import * as gatewayClient from '../runtime/gateway-client.js';
84
85
  import * as pendingInteractions from '../runtime/pending-interactions.js';
85
86
  import {
86
87
  _setTestPollMaxWait,
87
88
  handleChannelInbound,
88
89
  sweepExpiredGuardianApprovals,
89
- verifyGatewayOrigin,
90
90
  } from '../runtime/routes/channel-routes.js';
91
91
 
92
92
  initializeDb();
93
+ initAuthSigningKey(Buffer.from('test-signing-key-at-least-32-bytes-long'));
93
94
 
94
95
  afterAll(() => {
95
96
  resetDb();
@@ -174,8 +175,9 @@ function registerPendingInteraction(
174
175
  return handleConfirmationResponse;
175
176
  }
176
177
 
177
- /** Default bearer token used by tests. Include the X-Gateway-Origin header
178
- * so that verifyGatewayOrigin does not reject the request. */
178
+ /** Legacy bearer token constant retained for use in X-Gateway-Origin
179
+ * headers in test request construction. Gateway-origin proof is now
180
+ * handled by JWT auth; these headers are ignored by the handler. */
179
181
  const TEST_BEARER_TOKEN = 'token';
180
182
 
181
183
  function makeInboundRequest(overrides: Record<string, unknown> = {}): Request {
@@ -255,7 +257,7 @@ describe('inbound callback metadata triggers decision handling', () => {
255
257
 
256
258
  // Establish the conversation to get a conversationId mapping
257
259
  const initReq = makeInboundRequest({ content: 'init' });
258
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
260
+ await handleChannelInbound(initReq, noopProcessMessage);
259
261
 
260
262
  const db = getDb();
261
263
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -272,7 +274,7 @@ describe('inbound callback metadata triggers decision handling', () => {
272
274
  callbackData: 'apr:req-cb-1:approve_once',
273
275
  });
274
276
 
275
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
277
+ const res = await handleChannelInbound(req, noopProcessMessage);
276
278
  const body = await res.json() as Record<string, unknown>;
277
279
 
278
280
  expect(body.accepted).toBe(true);
@@ -286,7 +288,7 @@ describe('inbound callback metadata triggers decision handling', () => {
286
288
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
287
289
 
288
290
  const initReq = makeInboundRequest({ content: 'init' });
289
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
291
+ await handleChannelInbound(initReq, noopProcessMessage);
290
292
 
291
293
  const db = getDb();
292
294
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -300,7 +302,7 @@ describe('inbound callback metadata triggers decision handling', () => {
300
302
  callbackData: 'apr:req-cb-2:reject',
301
303
  });
302
304
 
303
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
305
+ const res = await handleChannelInbound(req, noopProcessMessage);
304
306
  const body = await res.json() as Record<string, unknown>;
305
307
 
306
308
  expect(body.accepted).toBe(true);
@@ -330,7 +332,7 @@ describe('inbound text matching approval phrases triggers decision handling', ()
330
332
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
331
333
 
332
334
  const initReq = makeInboundRequest({ content: 'init' });
333
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
335
+ await handleChannelInbound(initReq, noopProcessMessage);
334
336
 
335
337
  const db = getDb();
336
338
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -340,7 +342,7 @@ describe('inbound text matching approval phrases triggers decision handling', ()
340
342
  const sessionMock = registerPendingInteraction('req-txt-1', conversationId!, 'shell');
341
343
 
342
344
  const req = makeInboundRequest({ content: 'approve' });
343
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
345
+ const res = await handleChannelInbound(req, noopProcessMessage);
344
346
  const body = await res.json() as Record<string, unknown>;
345
347
 
346
348
  expect(body.accepted).toBe(true);
@@ -354,7 +356,7 @@ describe('inbound text matching approval phrases triggers decision handling', ()
354
356
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
355
357
 
356
358
  const initReq = makeInboundRequest({ content: 'init' });
357
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
359
+ await handleChannelInbound(initReq, noopProcessMessage);
358
360
 
359
361
  const db = getDb();
360
362
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -364,7 +366,7 @@ describe('inbound text matching approval phrases triggers decision handling', ()
364
366
  const sessionMock = registerPendingInteraction('req-txt-2', conversationId!, 'shell');
365
367
 
366
368
  const req = makeInboundRequest({ content: 'always' });
367
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
369
+ const res = await handleChannelInbound(req, noopProcessMessage);
368
370
  const body = await res.json() as Record<string, unknown>;
369
371
 
370
372
  expect(body.accepted).toBe(true);
@@ -394,7 +396,7 @@ describe('non-decision messages during pending approval (legacy fallback)', () =
394
396
  const replySpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
395
397
 
396
398
  const initReq = makeInboundRequest({ content: 'init' });
397
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
399
+ await handleChannelInbound(initReq, noopProcessMessage);
398
400
 
399
401
  const db = getDb();
400
402
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -405,7 +407,7 @@ describe('non-decision messages during pending approval (legacy fallback)', () =
405
407
 
406
408
  // Send a message that is NOT a decision
407
409
  const req = makeInboundRequest({ content: 'what is the weather?' });
408
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
410
+ const res = await handleChannelInbound(req, noopProcessMessage);
409
411
  const body = await res.json() as Record<string, unknown>;
410
412
 
411
413
  expect(body.accepted).toBe(true);
@@ -432,7 +434,7 @@ describe('non-decision messages during pending approval (legacy fallback)', () =
432
434
  describe('messages without pending approval proceed normally', () => {
433
435
  test('proceeds to normal processing when no pending approval exists', async () => {
434
436
  const req = makeInboundRequest({ content: 'hello world' });
435
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
437
+ const res = await handleChannelInbound(req, noopProcessMessage);
436
438
  const body = await res.json() as Record<string, unknown>;
437
439
 
438
440
  expect(body.accepted).toBe(true);
@@ -441,7 +443,7 @@ describe('messages without pending approval proceed normally', () => {
441
443
 
442
444
  test('text "approve" is processed normally when no pending approval exists', async () => {
443
445
  const req = makeInboundRequest({ content: 'approve' });
444
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
446
+ const res = await handleChannelInbound(req, noopProcessMessage);
445
447
  const body = await res.json() as Record<string, unknown>;
446
448
 
447
449
  expect(body.accepted).toBe(true);
@@ -476,7 +478,7 @@ describe('empty content with callbackData bypasses validation', () => {
476
478
  test('allows empty content when callbackData is present', async () => {
477
479
  // Establish the conversation first
478
480
  const initReq = makeInboundRequest({ content: 'init' });
479
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
481
+ await handleChannelInbound(initReq, noopProcessMessage);
480
482
 
481
483
  const db = getDb();
482
484
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -492,7 +494,7 @@ describe('empty content with callbackData bypasses validation', () => {
492
494
  callbackData: 'apr:req-empty-1:approve_once',
493
495
  });
494
496
 
495
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
497
+ const res = await handleChannelInbound(req, noopProcessMessage);
496
498
  expect(res.status).toBe(200);
497
499
  const body = await res.json() as Record<string, unknown>;
498
500
  expect(body.accepted).toBe(true);
@@ -505,7 +507,7 @@ describe('empty content with callbackData bypasses validation', () => {
505
507
  test('allows undefined content when callbackData is present', async () => {
506
508
  // Establish the conversation first
507
509
  const initReq = makeInboundRequest({ content: 'init' });
508
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
510
+ await handleChannelInbound(initReq, noopProcessMessage);
509
511
 
510
512
  const db = getDb();
511
513
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -535,7 +537,7 @@ describe('empty content with callbackData bypasses validation', () => {
535
537
  body: JSON.stringify(reqBody),
536
538
  });
537
539
 
538
- const res = await handleChannelInbound(req, noopProcessMessage, TEST_BEARER_TOKEN);
540
+ const res = await handleChannelInbound(req, noopProcessMessage);
539
541
  expect(res.status).toBe(200);
540
542
  const resBody = await res.json() as Record<string, unknown>;
541
543
  expect(resBody.accepted).toBe(true);
@@ -563,7 +565,7 @@ describe('callback requestId validation', () => {
563
565
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
564
566
 
565
567
  const initReq = makeInboundRequest({ content: 'init' });
566
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
568
+ await handleChannelInbound(initReq, noopProcessMessage);
567
569
 
568
570
  const db = getDb();
569
571
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -579,7 +581,7 @@ describe('callback requestId validation', () => {
579
581
  callbackData: 'apr:stale-request-id:approve_once',
580
582
  });
581
583
 
582
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
584
+ const res = await handleChannelInbound(req, noopProcessMessage);
583
585
  const body = await res.json() as Record<string, unknown>;
584
586
 
585
587
  expect(body.accepted).toBe(true);
@@ -594,7 +596,7 @@ describe('callback requestId validation', () => {
594
596
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
595
597
 
596
598
  const initReq = makeInboundRequest({ content: 'init' });
597
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
599
+ await handleChannelInbound(initReq, noopProcessMessage);
598
600
 
599
601
  const db = getDb();
600
602
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -609,7 +611,7 @@ describe('callback requestId validation', () => {
609
611
  callbackData: 'apr:req-match:approve_once',
610
612
  });
611
613
 
612
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
614
+ const res = await handleChannelInbound(req, noopProcessMessage);
613
615
  const body = await res.json() as Record<string, unknown>;
614
616
 
615
617
  expect(body.accepted).toBe(true);
@@ -623,7 +625,7 @@ describe('callback requestId validation', () => {
623
625
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
624
626
 
625
627
  const initReq = makeInboundRequest({ content: 'init' });
626
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
628
+ await handleChannelInbound(initReq, noopProcessMessage);
627
629
 
628
630
  const db = getDb();
629
631
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -634,7 +636,7 @@ describe('callback requestId validation', () => {
634
636
 
635
637
  // Send plain text "yes" — no requestId in the parsed result
636
638
  const req = makeInboundRequest({ content: 'yes' });
637
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
639
+ const res = await handleChannelInbound(req, noopProcessMessage);
638
640
  const body = await res.json() as Record<string, unknown>;
639
641
 
640
642
  expect(body.accepted).toBe(true);
@@ -664,7 +666,7 @@ describe('no immediate reply after approval decision', () => {
664
666
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
665
667
 
666
668
  const initReq = makeInboundRequest({ content: 'init' });
667
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
669
+ await handleChannelInbound(initReq, noopProcessMessage);
668
670
 
669
671
  const db = getDb();
670
672
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -682,7 +684,7 @@ describe('no immediate reply after approval decision', () => {
682
684
  callbackData: 'apr:req-noreply-1:approve_once',
683
685
  });
684
686
 
685
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
687
+ const res = await handleChannelInbound(req, noopProcessMessage);
686
688
  const body = await res.json() as Record<string, unknown>;
687
689
 
688
690
  expect(body.approval).toBe('decision_applied');
@@ -698,7 +700,7 @@ describe('no immediate reply after approval decision', () => {
698
700
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
699
701
 
700
702
  const initReq = makeInboundRequest({ content: 'init' });
701
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
703
+ await handleChannelInbound(initReq, noopProcessMessage);
702
704
 
703
705
  const db = getDb();
704
706
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -711,7 +713,7 @@ describe('no immediate reply after approval decision', () => {
711
713
 
712
714
  // Send a plain-text approval
713
715
  const req = makeInboundRequest({ content: 'approve' });
714
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
716
+ const res = await handleChannelInbound(req, noopProcessMessage);
715
717
  const body = await res.json() as Record<string, unknown>;
716
718
 
717
719
  expect(body.approval).toBe('decision_applied');
@@ -733,7 +735,7 @@ describe('stale callback handling', () => {
733
735
  callbackData: 'apr:stale-req:approve_once',
734
736
  });
735
737
 
736
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
738
+ const res = await handleChannelInbound(req, noopProcessMessage);
737
739
  const body = await res.json() as Record<string, unknown>;
738
740
 
739
741
  expect(body.accepted).toBe(true);
@@ -748,7 +750,7 @@ describe('stale callback handling', () => {
748
750
  callbackData: 'apr:stale-req:approve_once',
749
751
  });
750
752
 
751
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
753
+ const res = await handleChannelInbound(req, noopProcessMessage);
752
754
  const body = await res.json() as Record<string, unknown>;
753
755
 
754
756
  expect(body.accepted).toBe(true);
@@ -759,7 +761,7 @@ describe('stale callback handling', () => {
759
761
  // Regular text message (no callbackData) should proceed normally
760
762
  const req = makeInboundRequest({ content: 'hello world' });
761
763
 
762
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
764
+ const res = await handleChannelInbound(req, noopProcessMessage);
763
765
  const body = await res.json() as Record<string, unknown>;
764
766
 
765
767
  expect(body.accepted).toBe(true);
@@ -809,7 +811,7 @@ describe('SMS channel approval decisions', () => {
809
811
 
810
812
  // Establish the conversation via SMS
811
813
  const initReq = makeSmsInboundRequest({ content: 'init' });
812
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
814
+ await handleChannelInbound(initReq, noopProcessMessage);
813
815
 
814
816
  const db = getDb();
815
817
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -819,7 +821,7 @@ describe('SMS channel approval decisions', () => {
819
821
  const sessionMock = registerPendingInteraction('req-sms-1', conversationId!, 'shell');
820
822
 
821
823
  const req = makeSmsInboundRequest({ content: 'yes' });
822
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
824
+ const res = await handleChannelInbound(req, noopProcessMessage);
823
825
  const body = await res.json() as Record<string, unknown>;
824
826
 
825
827
  expect(body.accepted).toBe(true);
@@ -833,7 +835,7 @@ describe('SMS channel approval decisions', () => {
833
835
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
834
836
 
835
837
  const initReq = makeSmsInboundRequest({ content: 'init' });
836
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
838
+ await handleChannelInbound(initReq, noopProcessMessage);
837
839
 
838
840
  const db = getDb();
839
841
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -843,7 +845,7 @@ describe('SMS channel approval decisions', () => {
843
845
  const sessionMock = registerPendingInteraction('req-sms-2', conversationId!, 'shell');
844
846
 
845
847
  const req = makeSmsInboundRequest({ content: 'no' });
846
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
848
+ const res = await handleChannelInbound(req, noopProcessMessage);
847
849
  const body = await res.json() as Record<string, unknown>;
848
850
 
849
851
  expect(body.accepted).toBe(true);
@@ -858,7 +860,7 @@ describe('SMS channel approval decisions', () => {
858
860
  const approvalSpy = spyOn(gatewayClient, 'deliverApprovalPrompt').mockResolvedValue(undefined);
859
861
 
860
862
  const initReq = makeSmsInboundRequest({ content: 'init' });
861
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
863
+ await handleChannelInbound(initReq, noopProcessMessage);
862
864
 
863
865
  const db = getDb();
864
866
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -868,7 +870,7 @@ describe('SMS channel approval decisions', () => {
868
870
  registerPendingInteraction('req-sms-3', conversationId!, 'shell');
869
871
 
870
872
  const req = makeSmsInboundRequest({ content: 'what is happening?' });
871
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
873
+ const res = await handleChannelInbound(req, noopProcessMessage);
872
874
  const body = await res.json() as Record<string, unknown>;
873
875
 
874
876
  expect(body.accepted).toBe(true);
@@ -919,7 +921,7 @@ describe('SMS guardian verify intercept', () => {
919
921
  }),
920
922
  });
921
923
 
922
- const res = await handleChannelInbound(req, noopProcessMessage, TEST_BEARER_TOKEN);
924
+ const res = await handleChannelInbound(req, noopProcessMessage);
923
925
  const body = await res.json() as Record<string, unknown>;
924
926
 
925
927
  expect(body.accepted).toBe(true);
@@ -960,7 +962,7 @@ describe('SMS guardian verify intercept', () => {
960
962
  }),
961
963
  });
962
964
 
963
- const res = await handleChannelInbound(req, noopProcessMessage, TEST_BEARER_TOKEN);
965
+ const res = await handleChannelInbound(req, noopProcessMessage);
964
966
  const body = await res.json() as Record<string, unknown>;
965
967
 
966
968
  expect(body.accepted).toBe(true);
@@ -1013,7 +1015,7 @@ describe('SMS guardian verify intercept', () => {
1013
1015
  }),
1014
1016
  });
1015
1017
 
1016
- const res = await handleChannelInbound(req, processMessage, TEST_BEARER_TOKEN);
1018
+ const res = await handleChannelInbound(req, processMessage);
1017
1019
  const body = await res.json() as Record<string, unknown>;
1018
1020
 
1019
1021
  expect(body.accepted).toBe(true);
@@ -1080,7 +1082,7 @@ describe('guardian decision scoping — multiple pending approvals', () => {
1080
1082
  actorExternalId: 'guardian-scope-user',
1081
1083
  });
1082
1084
 
1083
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
1085
+ const res = await handleChannelInbound(req, noopProcessMessage);
1084
1086
  const body = await res.json() as Record<string, unknown>;
1085
1087
 
1086
1088
  expect(body.accepted).toBe(true);
@@ -1159,8 +1161,7 @@ describe('ambiguous plain-text decision with multiple pending requests', () => {
1159
1161
  });
1160
1162
 
1161
1163
  const res = await handleChannelInbound(
1162
- req, noopProcessMessage, 'token', 'self', undefined,
1163
- undefined, mockConversationGenerator,
1164
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
1164
1165
  );
1165
1166
  const body = await res.json() as Record<string, unknown>;
1166
1167
 
@@ -1319,7 +1320,7 @@ describe('assistant-scoped guardian verification via handleChannelInbound', () =
1319
1320
  actorExternalId: 'user-default-asst',
1320
1321
  });
1321
1322
 
1322
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
1323
+ const res = await handleChannelInbound(req, noopProcessMessage);
1323
1324
  const body = await res.json() as Record<string, unknown>;
1324
1325
 
1325
1326
  expect(body.accepted).toBe(true);
@@ -1342,7 +1343,7 @@ describe('assistant-scoped guardian verification via handleChannelInbound', () =
1342
1343
  actorExternalId: 'user-for-asst-x',
1343
1344
  });
1344
1345
 
1345
- const res = await handleChannelInbound(req, noopProcessMessage, 'token', 'asst-route-X');
1346
+ const res = await handleChannelInbound(req, noopProcessMessage, 'asst-route-X');
1346
1347
  const body = await res.json() as Record<string, unknown>;
1347
1348
 
1348
1349
  expect(body.accepted).toBe(true);
@@ -1368,7 +1369,7 @@ describe('assistant-scoped guardian verification via handleChannelInbound', () =
1368
1369
  actorExternalId: 'user-cross-test',
1369
1370
  });
1370
1371
 
1371
- const res = await handleChannelInbound(req, noopProcessMessage, 'token', 'asst-B-cross');
1372
+ const res = await handleChannelInbound(req, noopProcessMessage, 'asst-B-cross');
1372
1373
  const body = await res.json() as Record<string, unknown>;
1373
1374
 
1374
1375
  expect(body.accepted).toBe(true);
@@ -1396,7 +1397,7 @@ describe('assistant-scoped guardian verification via handleChannelInbound', () =
1396
1397
  actorExternalId: 'incoming-user',
1397
1398
  });
1398
1399
 
1399
- const res = await handleChannelInbound(req, noopProcessMessage, 'token', 'asst-non-self');
1400
+ const res = await handleChannelInbound(req, noopProcessMessage, 'asst-non-self');
1400
1401
  expect(res.status).toBe(200);
1401
1402
 
1402
1403
  const binding = db
@@ -1409,142 +1410,9 @@ describe('assistant-scoped guardian verification via handleChannelInbound', () =
1409
1410
  });
1410
1411
  });
1411
1412
 
1412
- // ═══════════════════════════════════════════════════════════════════════════
1413
- // 28. Gateway-origin proof hardeningdedicated secret support
1414
- // ═══════════════════════════════════════════════════════════════════════════
1415
-
1416
- describe('verifyGatewayOrigin with dedicated gateway-origin secret', () => {
1417
- function makeReqWithHeader(value?: string): Request {
1418
- const headers: Record<string, string> = { 'Content-Type': 'application/json' };
1419
- if (value !== undefined) {
1420
- headers['X-Gateway-Origin'] = value;
1421
- }
1422
- return new Request('http://localhost/channels/inbound', {
1423
- method: 'POST',
1424
- headers,
1425
- body: '{}',
1426
- });
1427
- }
1428
-
1429
- test('returns true when no secrets configured (local dev)', () => {
1430
- expect(verifyGatewayOrigin(makeReqWithHeader(), undefined, undefined)).toBe(true);
1431
- });
1432
-
1433
- test('falls back to bearerToken when no dedicated secret is set', () => {
1434
- expect(verifyGatewayOrigin(makeReqWithHeader('my-bearer'), 'my-bearer', undefined)).toBe(true);
1435
- expect(verifyGatewayOrigin(makeReqWithHeader('wrong'), 'my-bearer', undefined)).toBe(false);
1436
- expect(verifyGatewayOrigin(makeReqWithHeader(), 'my-bearer', undefined)).toBe(false);
1437
- });
1438
-
1439
- test('uses dedicated secret when set, ignoring bearer token', () => {
1440
- expect(verifyGatewayOrigin(makeReqWithHeader('dedicated-secret'), 'bearer-token', 'dedicated-secret')).toBe(true);
1441
- expect(verifyGatewayOrigin(makeReqWithHeader('bearer-token'), 'bearer-token', 'dedicated-secret')).toBe(false);
1442
- });
1443
-
1444
- test('validates dedicated secret even when bearer token is not configured', () => {
1445
- expect(verifyGatewayOrigin(makeReqWithHeader('my-secret'), undefined, 'my-secret')).toBe(true);
1446
- expect(verifyGatewayOrigin(makeReqWithHeader('wrong'), undefined, 'my-secret')).toBe(false);
1447
- });
1448
-
1449
- test('rejects missing header when any secret is configured', () => {
1450
- expect(verifyGatewayOrigin(makeReqWithHeader(), 'bearer', undefined)).toBe(false);
1451
- expect(verifyGatewayOrigin(makeReqWithHeader(), undefined, 'secret')).toBe(false);
1452
- expect(verifyGatewayOrigin(makeReqWithHeader(), 'bearer', 'secret')).toBe(false);
1453
- });
1454
-
1455
- test('rejects mismatched length headers (constant-time comparison guard)', () => {
1456
- expect(verifyGatewayOrigin(makeReqWithHeader('short'), 'a-much-longer-secret', undefined)).toBe(false);
1457
- expect(verifyGatewayOrigin(makeReqWithHeader('a-much-longer-secret'), 'short', undefined)).toBe(false);
1458
- });
1459
- });
1460
-
1461
- // ═══════════════════════════════════════════════════════════════════════════
1462
- // 29. handleChannelInbound passes gatewayOriginSecret to verifyGatewayOrigin
1463
- // ═══════════════════════════════════════════════════════════════════════════
1464
-
1465
- describe('handleChannelInbound gatewayOriginSecret integration', () => {
1466
- test('rejects request when bearer token matches but dedicated secret does not', async () => {
1467
- const bearerToken = 'my-bearer';
1468
- const gwOriginToken = 'dedicated-gw-test-value';
1469
-
1470
- const req = new Request('http://localhost/channels/inbound', {
1471
- method: 'POST',
1472
- headers: {
1473
- 'Content-Type': 'application/json',
1474
- 'X-Gateway-Origin': bearerToken,
1475
- },
1476
- body: JSON.stringify({
1477
- sourceChannel: 'telegram',
1478
- interface: 'telegram',
1479
- conversationExternalId: 'chat-gw-secret-test',
1480
- externalMessageId: `msg-${Date.now()}-${Math.random()}`,
1481
- content: 'hello',
1482
- }),
1483
- });
1484
-
1485
- const res = await handleChannelInbound(
1486
- req, noopProcessMessage, bearerToken, 'self', gwOriginToken,
1487
- );
1488
- expect(res.status).toBe(403);
1489
- const body = await res.json() as { code: string };
1490
- expect(body.code).toBe('GATEWAY_ORIGIN_REQUIRED');
1491
- });
1492
-
1493
- test('accepts request when dedicated secret matches', async () => {
1494
- const bearerToken = 'my-bearer';
1495
- const gwOriginToken = 'dedicated-gw-test-value';
1496
-
1497
- const req = new Request('http://localhost/channels/inbound', {
1498
- method: 'POST',
1499
- headers: {
1500
- 'Content-Type': 'application/json',
1501
- 'X-Gateway-Origin': gwOriginToken,
1502
- },
1503
- body: JSON.stringify({
1504
- sourceChannel: 'telegram',
1505
- interface: 'telegram',
1506
- conversationExternalId: 'chat-gw-secret-pass',
1507
- externalMessageId: `msg-${Date.now()}-${Math.random()}`,
1508
- content: 'hello',
1509
- actorExternalId: 'telegram-user-default',
1510
- }),
1511
- });
1512
-
1513
- const res = await handleChannelInbound(
1514
- req, noopProcessMessage, bearerToken, 'self', gwOriginToken,
1515
- );
1516
- expect(res.status).toBe(200);
1517
- const body = await res.json() as Record<string, unknown>;
1518
- expect(body.accepted).toBe(true);
1519
- });
1520
-
1521
- test('falls back to bearer token when no dedicated secret is set', async () => {
1522
- const bearerToken = 'my-bearer';
1523
-
1524
- const req = new Request('http://localhost/channels/inbound', {
1525
- method: 'POST',
1526
- headers: {
1527
- 'Content-Type': 'application/json',
1528
- 'X-Gateway-Origin': bearerToken,
1529
- },
1530
- body: JSON.stringify({
1531
- sourceChannel: 'telegram',
1532
- interface: 'telegram',
1533
- conversationExternalId: 'chat-gw-fallback',
1534
- externalMessageId: `msg-${Date.now()}-${Math.random()}`,
1535
- content: 'hello',
1536
- actorExternalId: 'telegram-user-default',
1537
- }),
1538
- });
1539
-
1540
- const res = await handleChannelInbound(
1541
- req, noopProcessMessage, bearerToken, 'self',
1542
- );
1543
- expect(res.status).toBe(200);
1544
- const body = await res.json() as Record<string, unknown>;
1545
- expect(body.accepted).toBe(true);
1546
- });
1547
- });
1413
+ // Sections 28-29 (verifyGatewayOrigin / gatewayOriginSecret integration) removed:
1414
+ // gateway-origin proof is now handled by JWT auth the gateway proves its
1415
+ // identity by minting a daemon-audience token with the shared signing key.
1548
1416
 
1549
1417
  // ═══════════════════════════════════════════════════════════════════════════
1550
1418
  // Conversational approval engine — standard path
@@ -1565,7 +1433,7 @@ describe('conversational approval engine — standard path', () => {
1565
1433
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
1566
1434
 
1567
1435
  const initReq = makeInboundRequest({ content: 'init' });
1568
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
1436
+ await handleChannelInbound(initReq, noopProcessMessage);
1569
1437
 
1570
1438
  const db = getDb();
1571
1439
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -1583,8 +1451,7 @@ describe('conversational approval engine — standard path', () => {
1583
1451
 
1584
1452
  const req = makeInboundRequest({ content: 'what does this command do?' });
1585
1453
  const res = await handleChannelInbound(
1586
- req, noopProcessMessage, 'token', 'self', undefined,
1587
- undefined, mockConversationGenerator,
1454
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
1588
1455
  );
1589
1456
  const body = await res.json() as Record<string, unknown>;
1590
1457
 
@@ -1608,7 +1475,7 @@ describe('conversational approval engine — standard path', () => {
1608
1475
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
1609
1476
 
1610
1477
  const initReq = makeInboundRequest({ content: 'init' });
1611
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
1478
+ await handleChannelInbound(initReq, noopProcessMessage);
1612
1479
 
1613
1480
  const db = getDb();
1614
1481
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -1626,8 +1493,7 @@ describe('conversational approval engine — standard path', () => {
1626
1493
 
1627
1494
  const req = makeInboundRequest({ content: 'yeah go ahead and run it' });
1628
1495
  const res = await handleChannelInbound(
1629
- req, noopProcessMessage, 'token', 'self', undefined,
1630
- undefined, mockConversationGenerator,
1496
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
1631
1497
  );
1632
1498
  const body = await res.json() as Record<string, unknown>;
1633
1499
 
@@ -1644,7 +1510,7 @@ describe('conversational approval engine — standard path', () => {
1644
1510
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
1645
1511
 
1646
1512
  const initReq = makeInboundRequest({ content: 'init' });
1647
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
1513
+ await handleChannelInbound(initReq, noopProcessMessage);
1648
1514
 
1649
1515
  const db = getDb();
1650
1516
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -1662,8 +1528,7 @@ describe('conversational approval engine — standard path', () => {
1662
1528
 
1663
1529
  const req = makeInboundRequest({ content: 'nevermind, don\'t run that' });
1664
1530
  const res = await handleChannelInbound(
1665
- req, noopProcessMessage, 'token', 'self', undefined,
1666
- undefined, mockConversationGenerator,
1531
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
1667
1532
  );
1668
1533
  const body = await res.json() as Record<string, unknown>;
1669
1534
 
@@ -1679,7 +1544,7 @@ describe('conversational approval engine — standard path', () => {
1679
1544
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
1680
1545
 
1681
1546
  const initReq = makeInboundRequest({ content: 'init' });
1682
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
1547
+ await handleChannelInbound(initReq, noopProcessMessage);
1683
1548
 
1684
1549
  const db = getDb();
1685
1550
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -1700,8 +1565,7 @@ describe('conversational approval engine — standard path', () => {
1700
1565
  });
1701
1566
 
1702
1567
  const res = await handleChannelInbound(
1703
- req, noopProcessMessage, 'token', 'self', undefined,
1704
- undefined, mockConversationGenerator,
1568
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
1705
1569
  );
1706
1570
  const body = await res.json() as Record<string, unknown>;
1707
1571
 
@@ -1761,8 +1625,7 @@ describe('guardian conversational approval via conversation engine', () => {
1761
1625
  });
1762
1626
 
1763
1627
  const res = await handleChannelInbound(
1764
- req, noopProcessMessage, 'token', 'self', undefined,
1765
- undefined, mockConversationGenerator,
1628
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
1766
1629
  );
1767
1630
  const body = await res.json() as Record<string, unknown>;
1768
1631
 
@@ -1826,8 +1689,7 @@ describe('guardian conversational approval via conversation engine', () => {
1826
1689
  });
1827
1690
 
1828
1691
  const res = await handleChannelInbound(
1829
- req, noopProcessMessage, 'token', 'self', undefined,
1830
- undefined, mockConversationGenerator,
1692
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
1831
1693
  );
1832
1694
  const body = await res.json() as Record<string, unknown>;
1833
1695
 
@@ -1886,7 +1748,7 @@ describe('guardian conversational approval via conversation engine', () => {
1886
1748
  });
1887
1749
 
1888
1750
  const res = await handleChannelInbound(
1889
- req, noopProcessMessage, 'token', 'self',
1751
+ req, noopProcessMessage, 'self',
1890
1752
  );
1891
1753
  const body = await res.json() as Record<string, unknown>;
1892
1754
 
@@ -1956,8 +1818,7 @@ describe('guardian conversational approval via conversation engine', () => {
1956
1818
  });
1957
1819
 
1958
1820
  const res = await handleChannelInbound(
1959
- req, noopProcessMessage, 'token', 'self', undefined,
1960
- undefined, mockConversationGenerator,
1821
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
1961
1822
  );
1962
1823
  const body = await res.json() as Record<string, unknown>;
1963
1824
 
@@ -2003,7 +1864,7 @@ describe('keep_pending remains conversational — standard path', () => {
2003
1864
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
2004
1865
 
2005
1866
  const initReq = makeInboundRequest({ content: 'init' });
2006
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
1867
+ await handleChannelInbound(initReq, noopProcessMessage);
2007
1868
 
2008
1869
  const db = getDb();
2009
1870
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -2019,8 +1880,7 @@ describe('keep_pending remains conversational — standard path', () => {
2019
1880
 
2020
1881
  const req = makeInboundRequest({ content: 'approve' });
2021
1882
  const res = await handleChannelInbound(
2022
- req, noopProcessMessage, 'token', 'self', undefined,
2023
- undefined, mockConversationGenerator,
1883
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
2024
1884
  );
2025
1885
  const body = await res.json() as Record<string, unknown>;
2026
1886
 
@@ -2078,8 +1938,7 @@ describe('keep_pending remains conversational — guardian path', () => {
2078
1938
  actorExternalId: 'guardian-user-fb',
2079
1939
  });
2080
1940
  const res = await handleChannelInbound(
2081
- guardianReq, noopProcessMessage, 'token', 'self', undefined,
2082
- undefined, mockConversationGenerator,
1941
+ guardianReq, noopProcessMessage, 'self', undefined, mockConversationGenerator,
2083
1942
  );
2084
1943
  const body = await res.json() as Record<string, unknown>;
2085
1944
 
@@ -2120,7 +1979,7 @@ describe('requester cancel of guardian-gated pending request', () => {
2120
1979
  conversationExternalId: 'requester-cancel-chat',
2121
1980
  actorExternalId: 'requester-cancel-user',
2122
1981
  });
2123
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
1982
+ await handleChannelInbound(initReq, noopProcessMessage);
2124
1983
 
2125
1984
  const db = getDb();
2126
1985
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -2156,8 +2015,7 @@ describe('requester cancel of guardian-gated pending request', () => {
2156
2015
  actorExternalId: 'requester-cancel-user',
2157
2016
  });
2158
2017
  const res = await handleChannelInbound(
2159
- req, noopProcessMessage, 'token', 'self', undefined,
2160
- undefined, mockConversationGenerator,
2018
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
2161
2019
  );
2162
2020
  const body = await res.json() as Record<string, unknown>;
2163
2021
 
@@ -2188,7 +2046,7 @@ describe('requester cancel of guardian-gated pending request', () => {
2188
2046
  conversationExternalId: 'requester-cancel-chat',
2189
2047
  actorExternalId: 'requester-cancel-user',
2190
2048
  });
2191
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
2049
+ await handleChannelInbound(initReq, noopProcessMessage);
2192
2050
 
2193
2051
  const db = getDb();
2194
2052
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -2224,8 +2082,7 @@ describe('requester cancel of guardian-gated pending request', () => {
2224
2082
  actorExternalId: 'requester-cancel-user',
2225
2083
  });
2226
2084
  const res = await handleChannelInbound(
2227
- req, noopProcessMessage, 'token', 'self', undefined,
2228
- undefined, mockConversationGenerator,
2085
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
2229
2086
  );
2230
2087
  const body = await res.json() as Record<string, unknown>;
2231
2088
 
@@ -2249,7 +2106,7 @@ describe('requester cancel of guardian-gated pending request', () => {
2249
2106
  conversationExternalId: 'requester-cancel-chat',
2250
2107
  actorExternalId: 'requester-cancel-user',
2251
2108
  });
2252
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
2109
+ await handleChannelInbound(initReq, noopProcessMessage);
2253
2110
 
2254
2111
  const db = getDb();
2255
2112
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -2285,8 +2142,7 @@ describe('requester cancel of guardian-gated pending request', () => {
2285
2142
  actorExternalId: 'requester-cancel-user',
2286
2143
  });
2287
2144
  const res = await handleChannelInbound(
2288
- req, noopProcessMessage, 'token', 'self', undefined,
2289
- undefined, mockConversationGenerator,
2145
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
2290
2146
  );
2291
2147
  const body = await res.json() as Record<string, unknown>;
2292
2148
 
@@ -2310,7 +2166,7 @@ describe('requester cancel of guardian-gated pending request', () => {
2310
2166
  conversationExternalId: 'requester-cancel-chat',
2311
2167
  actorExternalId: 'requester-cancel-user',
2312
2168
  });
2313
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
2169
+ await handleChannelInbound(initReq, noopProcessMessage);
2314
2170
 
2315
2171
  const db = getDb();
2316
2172
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -2341,7 +2197,7 @@ describe('requester cancel of guardian-gated pending request', () => {
2341
2197
  conversationExternalId: 'requester-cancel-chat',
2342
2198
  actorExternalId: 'requester-cancel-user',
2343
2199
  });
2344
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
2200
+ const res = await handleChannelInbound(req, noopProcessMessage);
2345
2201
  const body = await res.json() as Record<string, unknown>;
2346
2202
 
2347
2203
  expect(body.accepted).toBe(true);
@@ -2371,7 +2227,7 @@ describe('engine decision race condition — standard path', () => {
2371
2227
  const deliverSpy = spyOn(gatewayClient, 'deliverChannelReply').mockResolvedValue(undefined);
2372
2228
 
2373
2229
  const initReq = makeInboundRequest({ content: 'init' });
2374
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
2230
+ await handleChannelInbound(initReq, noopProcessMessage);
2375
2231
 
2376
2232
  const db = getDb();
2377
2233
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -2394,8 +2250,7 @@ describe('engine decision race condition — standard path', () => {
2394
2250
 
2395
2251
  const req = makeInboundRequest({ content: 'go ahead' });
2396
2252
  const res = await handleChannelInbound(
2397
- req, noopProcessMessage, 'token', 'self', undefined,
2398
- undefined, mockConversationGenerator,
2253
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
2399
2254
  );
2400
2255
  const body = await res.json() as Record<string, unknown>;
2401
2256
 
@@ -2466,8 +2321,7 @@ describe('engine decision race condition — guardian path', () => {
2466
2321
  actorExternalId: 'guardian-race-user',
2467
2322
  });
2468
2323
  const res = await handleChannelInbound(
2469
- guardianReq, noopProcessMessage, 'token', 'self', undefined,
2470
- undefined, mockConversationGenerator,
2324
+ guardianReq, noopProcessMessage, 'self', undefined, mockConversationGenerator,
2471
2325
  );
2472
2326
  const body = await res.json() as Record<string, unknown>;
2473
2327
 
@@ -2517,7 +2371,7 @@ describe('non-decision status reply for different channels', () => {
2517
2371
 
2518
2372
  // Establish the conversation using sms (non-rich channel)
2519
2373
  const initReq = makeInboundRequest({ content: 'init', sourceChannel: 'sms' });
2520
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
2374
+ await handleChannelInbound(initReq, noopProcessMessage);
2521
2375
 
2522
2376
  const db = getDb();
2523
2377
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -2528,7 +2382,7 @@ describe('non-decision status reply for different channels', () => {
2528
2382
 
2529
2383
  // Send a non-decision message
2530
2384
  const req = makeInboundRequest({ content: 'what is happening?', sourceChannel: 'sms' });
2531
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
2385
+ const res = await handleChannelInbound(req, noopProcessMessage);
2532
2386
  const body = await res.json() as Record<string, unknown>;
2533
2387
 
2534
2388
  expect(body.accepted).toBe(true);
@@ -2551,7 +2405,7 @@ describe('non-decision status reply for different channels', () => {
2551
2405
 
2552
2406
  // Establish the conversation using telegram (rich channel)
2553
2407
  const initReq = makeInboundRequest({ content: 'init', sourceChannel: 'telegram' });
2554
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
2408
+ await handleChannelInbound(initReq, noopProcessMessage);
2555
2409
 
2556
2410
  const db = getDb();
2557
2411
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -2562,7 +2416,7 @@ describe('non-decision status reply for different channels', () => {
2562
2416
 
2563
2417
  // Send a non-decision message
2564
2418
  const req = makeInboundRequest({ content: 'what is happening?', sourceChannel: 'telegram' });
2565
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
2419
+ const res = await handleChannelInbound(req, noopProcessMessage);
2566
2420
  const body = await res.json() as Record<string, unknown>;
2567
2421
 
2568
2422
  expect(body.accepted).toBe(true);
@@ -2623,7 +2477,7 @@ describe('background channel processing approval prompts', () => {
2623
2477
  externalMessageId: 'msg-bg-1',
2624
2478
  });
2625
2479
 
2626
- const res = await handleChannelInbound(req, processMessage as unknown as typeof noopProcessMessage, 'token');
2480
+ const res = await handleChannelInbound(req, processMessage as unknown as typeof noopProcessMessage);
2627
2481
  const body = await res.json() as Record<string, unknown>;
2628
2482
  expect(body.accepted).toBe(true);
2629
2483
 
@@ -2677,7 +2531,7 @@ describe('background channel processing approval prompts', () => {
2677
2531
  externalMessageId: 'msg-bg-format-1',
2678
2532
  });
2679
2533
 
2680
- const res = await handleChannelInbound(req, processMessage as unknown as typeof noopProcessMessage, 'token');
2534
+ const res = await handleChannelInbound(req, processMessage as unknown as typeof noopProcessMessage);
2681
2535
  const body = await res.json() as Record<string, unknown>;
2682
2536
  expect(body.accepted).toBe(true);
2683
2537
 
@@ -2722,7 +2576,7 @@ describe('background channel processing approval prompts', () => {
2722
2576
  externalMessageId: 'msg-ng-1',
2723
2577
  });
2724
2578
 
2725
- const res = await handleChannelInbound(req, processMessage as unknown as typeof noopProcessMessage, 'token');
2579
+ const res = await handleChannelInbound(req, processMessage as unknown as typeof noopProcessMessage);
2726
2580
  const body = await res.json() as Record<string, unknown>;
2727
2581
  expect(body.accepted).toBe(true);
2728
2582
 
@@ -2765,7 +2619,7 @@ describe('background channel processing approval prompts', () => {
2765
2619
  externalMessageId: 'msg-bg-unverified-1',
2766
2620
  });
2767
2621
 
2768
- const res = await handleChannelInbound(req, processMessage as unknown as typeof noopProcessMessage, 'token');
2622
+ const res = await handleChannelInbound(req, processMessage as unknown as typeof noopProcessMessage);
2769
2623
  const body = await res.json() as Record<string, unknown>;
2770
2624
  expect(body.accepted).toBe(true);
2771
2625
 
@@ -2838,7 +2692,7 @@ describe('NL approval routing via destination-scoped canonical requests', () =>
2838
2692
  content: 'yes',
2839
2693
  externalMessageId: `msg-nl-approve-${Date.now()}`,
2840
2694
  });
2841
- const res = await handleChannelInbound(req, noopProcessMessage as any, TEST_BEARER_TOKEN);
2695
+ const res = await handleChannelInbound(req, noopProcessMessage as any);
2842
2696
  const body = await res.json() as Record<string, unknown>;
2843
2697
 
2844
2698
  expect(body.accepted).toBe(true);
@@ -2890,7 +2744,7 @@ describe('NL approval routing via destination-scoped canonical requests', () =>
2890
2744
  content: 'approve',
2891
2745
  externalMessageId: `msg-nl-mismatch-${Date.now()}`,
2892
2746
  });
2893
- const res = await handleChannelInbound(req, noopProcessMessage as any, TEST_BEARER_TOKEN);
2747
+ const res = await handleChannelInbound(req, noopProcessMessage as any);
2894
2748
  const body = await res.json() as Record<string, unknown>;
2895
2749
 
2896
2750
  expect(body.accepted).toBe(true);
@@ -2931,7 +2785,7 @@ describe('trusted-contact self-approval blocked before guardian approval row exi
2931
2785
  conversationExternalId: 'tc-selfapproval-chat',
2932
2786
  actorExternalId: 'tc-selfapproval-user',
2933
2787
  });
2934
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
2788
+ await handleChannelInbound(initReq, noopProcessMessage);
2935
2789
 
2936
2790
  const db = getDb();
2937
2791
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -2960,8 +2814,7 @@ describe('trusted-contact self-approval blocked before guardian approval row exi
2960
2814
  actorExternalId: 'tc-selfapproval-user',
2961
2815
  });
2962
2816
  const res = await handleChannelInbound(
2963
- req, noopProcessMessage, 'token', 'self', undefined,
2964
- undefined, mockConversationGenerator,
2817
+ req, noopProcessMessage, 'self', undefined, mockConversationGenerator,
2965
2818
  );
2966
2819
  const body = await res.json() as Record<string, unknown>;
2967
2820
 
@@ -2987,7 +2840,7 @@ describe('trusted-contact self-approval blocked before guardian approval row exi
2987
2840
  conversationExternalId: 'tc-selfapproval-chat',
2988
2841
  actorExternalId: 'tc-selfapproval-user',
2989
2842
  });
2990
- await handleChannelInbound(initReq, noopProcessMessage, 'token');
2843
+ await handleChannelInbound(initReq, noopProcessMessage);
2991
2844
 
2992
2845
  const db = getDb();
2993
2846
  const events = db.$client.prepare('SELECT conversation_id FROM channel_inbound_events').all() as Array<{ conversation_id: string }>;
@@ -3006,7 +2859,7 @@ describe('trusted-contact self-approval blocked before guardian approval row exi
3006
2859
  conversationExternalId: 'tc-selfapproval-chat',
3007
2860
  actorExternalId: 'tc-selfapproval-user',
3008
2861
  });
3009
- const res = await handleChannelInbound(req, noopProcessMessage, 'token');
2862
+ const res = await handleChannelInbound(req, noopProcessMessage);
3010
2863
  const body = await res.json() as Record<string, unknown>;
3011
2864
 
3012
2865
  expect(body.accepted).toBe(true);