@vellumai/assistant 0.4.14 → 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 (130) 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-channel-details.ts +1 -1
  42. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +68 -24
  43. package/src/config/bundled-skills/sms-setup/SKILL.md +1 -1
  44. package/src/config/bundled-skills/telegram-setup/SKILL.md +1 -1
  45. package/src/config/bundled-skills/twilio-setup/SKILL.md +1 -1
  46. package/src/daemon/approval-generators.ts +6 -3
  47. package/src/daemon/handlers/config-ingress.ts +2 -6
  48. package/src/daemon/handlers/guardian-actions.ts +1 -1
  49. package/src/daemon/handlers/sessions.ts +4 -1
  50. package/src/daemon/handlers/shared.ts +3 -0
  51. package/src/daemon/handlers/skills.ts +32 -0
  52. package/src/daemon/ipc-contract/messages.ts +3 -1
  53. package/src/daemon/ipc-handler.ts +24 -0
  54. package/src/daemon/ipc-validate.ts +1 -1
  55. package/src/daemon/lifecycle.ts +6 -8
  56. package/src/daemon/server.ts +8 -3
  57. package/src/daemon/session-agent-loop.ts +19 -1
  58. package/src/daemon/session-attachments.ts +2 -1
  59. package/src/daemon/session-history.ts +2 -2
  60. package/src/daemon/session-process.ts +5 -9
  61. package/src/daemon/session-surfaces.ts +17 -1
  62. package/src/daemon/session-tool-setup.ts +216 -69
  63. package/src/daemon/session.ts +24 -1
  64. package/src/events/domain-events.ts +1 -1
  65. package/src/events/tool-domain-event-publisher.ts +5 -10
  66. package/src/influencer/client.ts +8 -7
  67. package/src/messaging/providers/gmail/client.ts +33 -1
  68. package/src/messaging/providers/gmail/mime-builder.ts +5 -1
  69. package/src/messaging/providers/sms/adapter.ts +3 -7
  70. package/src/messaging/providers/telegram-bot/adapter.ts +3 -7
  71. package/src/messaging/providers/whatsapp/adapter.ts +3 -7
  72. package/src/notifications/adapters/sms.ts +2 -2
  73. package/src/notifications/adapters/telegram.ts +2 -2
  74. package/src/permissions/prompter.ts +2 -0
  75. package/src/permissions/types.ts +11 -1
  76. package/src/runtime/approval-conversation-turn.ts +4 -0
  77. package/src/runtime/auth/__tests__/context.test.ts +130 -0
  78. package/src/runtime/auth/__tests__/credential-service.test.ts +277 -0
  79. package/src/runtime/auth/__tests__/guard-tests.test.ts +289 -0
  80. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +71 -0
  81. package/src/runtime/auth/__tests__/middleware.test.ts +239 -0
  82. package/src/runtime/auth/__tests__/policy.test.ts +29 -0
  83. package/src/runtime/auth/__tests__/route-policy.test.ts +166 -0
  84. package/src/runtime/auth/__tests__/scopes.test.ts +109 -0
  85. package/src/runtime/auth/__tests__/subject.test.ts +149 -0
  86. package/src/runtime/auth/__tests__/token-service.test.ts +263 -0
  87. package/src/runtime/auth/context.ts +62 -0
  88. package/src/runtime/{actor-refresh-token-service.ts → auth/credential-service.ts} +112 -79
  89. package/src/runtime/auth/external-assistant-id.ts +69 -0
  90. package/src/runtime/auth/index.ts +37 -0
  91. package/src/runtime/auth/middleware.ts +127 -0
  92. package/src/runtime/auth/policy.ts +17 -0
  93. package/src/runtime/auth/route-policy.ts +261 -0
  94. package/src/runtime/auth/scopes.ts +64 -0
  95. package/src/runtime/auth/subject.ts +68 -0
  96. package/src/runtime/auth/token-service.ts +275 -0
  97. package/src/runtime/auth/types.ts +79 -0
  98. package/src/runtime/channel-approval-parser.ts +11 -5
  99. package/src/runtime/channel-approval-types.ts +1 -1
  100. package/src/runtime/channel-approvals.ts +22 -1
  101. package/src/runtime/guardian-action-followup-executor.ts +2 -2
  102. package/src/runtime/guardian-context-resolver.ts +15 -0
  103. package/src/runtime/guardian-decision-types.ts +23 -6
  104. package/src/runtime/guardian-outbound-actions.ts +4 -22
  105. package/src/runtime/guardian-reply-router.ts +5 -3
  106. package/src/runtime/http-server.ts +210 -182
  107. package/src/runtime/http-types.ts +11 -1
  108. package/src/runtime/local-actor-identity.ts +25 -0
  109. package/src/runtime/pending-interactions.ts +1 -0
  110. package/src/runtime/routes/approval-routes.ts +42 -59
  111. package/src/runtime/routes/channel-route-shared.ts +9 -41
  112. package/src/runtime/routes/channel-routes.ts +0 -2
  113. package/src/runtime/routes/conversation-routes.ts +39 -49
  114. package/src/runtime/routes/events-routes.ts +15 -22
  115. package/src/runtime/routes/guardian-action-routes.ts +46 -51
  116. package/src/runtime/routes/guardian-approval-interception.ts +6 -5
  117. package/src/runtime/routes/guardian-bootstrap-routes.ts +12 -8
  118. package/src/runtime/routes/guardian-refresh-routes.ts +2 -2
  119. package/src/runtime/routes/inbound-message-handler.ts +39 -45
  120. package/src/runtime/routes/pairing-routes.ts +9 -9
  121. package/src/runtime/routes/secret-routes.ts +90 -45
  122. package/src/runtime/routes/surface-action-routes.ts +12 -2
  123. package/src/runtime/routes/trust-rules-routes.ts +13 -0
  124. package/src/runtime/routes/twilio-routes.ts +3 -3
  125. package/src/runtime/session-approval-overrides.ts +86 -0
  126. package/src/security/keychain-to-encrypted-migration.ts +8 -1
  127. package/src/skills/frontmatter.ts +44 -1
  128. package/src/tools/permission-checker.ts +226 -74
  129. package/src/runtime/actor-token-service.ts +0 -234
  130. package/src/runtime/middleware/actor-token.ts +0 -265
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Core auth types for the single-header JWT auth system.
3
+ *
4
+ * These types define the token claims, scope profiles, principal types,
5
+ * and the normalized AuthContext that downstream code consumes.
6
+ */
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // Scope profiles — named bundles of permissions
10
+ // ---------------------------------------------------------------------------
11
+
12
+ export type ScopeProfile =
13
+ | 'actor_client_v1'
14
+ | 'gateway_ingress_v1'
15
+ | 'gateway_service_v1'
16
+ | 'ipc_v1';
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Individual scope strings
20
+ // ---------------------------------------------------------------------------
21
+
22
+ export type Scope =
23
+ | 'chat.read'
24
+ | 'chat.write'
25
+ | 'approval.read'
26
+ | 'approval.write'
27
+ | 'settings.read'
28
+ | 'settings.write'
29
+ | 'attachments.read'
30
+ | 'attachments.write'
31
+ | 'calls.read'
32
+ | 'calls.write'
33
+ | 'ingress.write'
34
+ | 'internal.write'
35
+ | 'feature_flags.read'
36
+ | 'feature_flags.write'
37
+ | 'ipc.all';
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Principal types — derived from the sub pattern
41
+ // ---------------------------------------------------------------------------
42
+
43
+ export type PrincipalType = 'actor' | 'svc_gateway' | 'ipc';
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Token audience — which service the JWT is intended for
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export type TokenAudience = 'vellum-gateway' | 'vellum-daemon';
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // JWT claims — the payload inside the token
53
+ // ---------------------------------------------------------------------------
54
+
55
+ export interface TokenClaims {
56
+ iss: 'vellum-auth';
57
+ aud: TokenAudience;
58
+ sub: string;
59
+ scope_profile: ScopeProfile;
60
+ exp: number;
61
+ policy_epoch: number;
62
+ iat?: number;
63
+ jti?: string;
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // AuthContext — normalized auth state for downstream consumers
68
+ // ---------------------------------------------------------------------------
69
+
70
+ export interface AuthContext {
71
+ subject: string;
72
+ principalType: PrincipalType;
73
+ assistantId: string;
74
+ actorPrincipalId?: string;
75
+ sessionId?: string;
76
+ scopeProfile: ScopeProfile;
77
+ scopes: ReadonlySet<Scope>;
78
+ policyEpoch: number;
79
+ }
@@ -18,22 +18,28 @@ import type { ApprovalAction, ApprovalDecisionResult } from './channel-approval-
18
18
  // ---------------------------------------------------------------------------
19
19
 
20
20
  const APPROVE_ONCE_PHRASES = ['yes', 'approve', 'approve once', 'allow', 'go ahead'];
21
+ const APPROVE_10M_PHRASES = ['approve for 10 minutes', 'allow for 10 minutes', 'approve 10m', 'allow 10m', 'approve 10 min', 'allow 10 min'];
22
+ const APPROVE_THREAD_PHRASES = ['approve for thread', 'allow for thread', 'approve thread', 'allow thread'];
21
23
  const APPROVE_ALWAYS_PHRASES = ['always', 'approve always', 'allow always'];
22
24
  const REJECT_PHRASES = ['no', 'reject', 'deny', 'cancel'];
23
25
 
24
26
  /**
25
- * Build a Map from lowercased phrase to action. "Approve always" phrases
26
- * are checked first (longest-match-wins) because "approve" is a prefix
27
- * of "approve always".
27
+ * Build a Map from lowercased phrase to action. Longer phrases are
28
+ * inserted first so iteration order does not matter — we match on
29
+ * exact equality after normalising, not prefix matching.
28
30
  */
29
31
  function buildPhraseMap(): Map<string, ApprovalAction> {
30
32
  const map = new Map<string, ApprovalAction>();
31
33
 
32
- // Insert longer phrases first so iteration order does not matter —
33
- // we match on exact equality after normalising, not prefix matching.
34
34
  for (const phrase of APPROVE_ALWAYS_PHRASES) {
35
35
  map.set(phrase, 'approve_always');
36
36
  }
37
+ for (const phrase of APPROVE_10M_PHRASES) {
38
+ map.set(phrase, 'approve_10m');
39
+ }
40
+ for (const phrase of APPROVE_THREAD_PHRASES) {
41
+ map.set(phrase, 'approve_thread');
42
+ }
37
43
  for (const phrase of APPROVE_ONCE_PHRASES) {
38
44
  map.set(phrase, 'approve_once');
39
45
  }
@@ -14,7 +14,7 @@ import type { GuardianDecisionAction } from './guardian-decision-types.js';
14
14
  // ---------------------------------------------------------------------------
15
15
 
16
16
  /** The set of actions a user can take on an approval prompt. */
17
- export type ApprovalAction = 'approve_once' | 'approve_always' | 'reject';
17
+ export type ApprovalAction = 'approve_once' | 'approve_10m' | 'approve_thread' | 'approve_always' | 'reject';
18
18
 
19
19
  /** An action presented to the user as a tappable button or text option. */
20
20
  export interface ApprovalActionOption {
@@ -10,9 +10,11 @@
10
10
  */
11
11
 
12
12
  import { addRule } from '../permissions/trust-store.js';
13
+ import type { UserDecision } from '../permissions/types.js';
13
14
  import { getTool } from '../tools/registry.js';
14
15
  import { composeApprovalMessage } from './approval-message-composer.js';
15
16
  import type {
17
+ ApprovalAction,
16
18
  ApprovalDecisionResult,
17
19
  ApprovalUIMetadata,
18
20
  ChannelApprovalPrompt,
@@ -110,6 +112,25 @@ export function buildApprovalUIMetadata(
110
112
  };
111
113
  }
112
114
 
115
+ // ---------------------------------------------------------------------------
116
+ // 2.5. Action → UserDecision mapping
117
+ // ---------------------------------------------------------------------------
118
+
119
+ /**
120
+ * Map a channel-level `ApprovalAction` to the permission system's
121
+ * `UserDecision` type. Temporary approval modes (`approve_10m`,
122
+ * `approve_thread`) map directly to their `allow_*` counterparts so
123
+ * the permission pipeline can activate the appropriate override.
124
+ */
125
+ function mapApprovalActionToUserDecision(action: ApprovalAction): UserDecision {
126
+ switch (action) {
127
+ case 'reject': return 'deny';
128
+ case 'approve_10m': return 'allow_10m';
129
+ case 'approve_thread': return 'allow_thread';
130
+ default: return 'allow';
131
+ }
132
+ }
133
+
113
134
  // ---------------------------------------------------------------------------
114
135
  // 3. Consume a user decision and apply it to the session
115
136
  // ---------------------------------------------------------------------------
@@ -176,7 +197,7 @@ export function handleChannelDecision(
176
197
  if (!resolved) return { applied: false };
177
198
 
178
199
  // Map channel-level action to the permission system's UserDecision type.
179
- const userDecision = decision.action === 'reject' ? 'deny' as const : 'allow' as const;
200
+ const userDecision = mapApprovalActionToUserDecision(decision.action);
180
201
  if (decisionContext === undefined) {
181
202
  resolved.session.handleConfirmationResponse(info.requestId, userDecision);
182
203
  } else {
@@ -29,7 +29,7 @@ import {
29
29
  type GuardianActionRequest,
30
30
  } from '../memory/guardian-action-store.js';
31
31
  import { getLogger } from '../util/logger.js';
32
- import { readHttpToken } from '../util/platform.js';
32
+ import { mintDaemonDeliveryToken } from './auth/token-service.js';
33
33
  import { deliverChannelReply } from './gateway-client.js';
34
34
  import { composeGuardianActionMessageGenerative } from './guardian-action-message-composer.js';
35
35
  import type { GuardianActionCopyGenerator } from './http-types.js';
@@ -115,7 +115,7 @@ async function executeMessageBack(
115
115
 
116
116
  const gatewayBase = getGatewayInternalBaseUrl();
117
117
  const deliverUrl = `${gatewayBase}/deliver/sms`;
118
- const bearerToken = readHttpToken() ?? undefined;
118
+ const bearerToken = mintDaemonDeliveryToken();
119
119
 
120
120
  await deliverChannelReply(
121
121
  deliverUrl,
@@ -124,3 +124,18 @@ export function resolveRoutingState(ctx: Pick<GuardianRuntimeContext, 'trustClas
124
124
  export function resolveRoutingStateFromRuntime(ctx: GuardianRuntimeContext): RoutingState {
125
125
  return resolveRoutingState(ctx);
126
126
  }
127
+
128
+ /**
129
+ * Override the sourceChannel on a resolved GuardianRuntimeContext.
130
+ *
131
+ * The HTTP /messages endpoint resolves trust against a fixed internal
132
+ * channel ('vellum') but the request body carries the actual sourceChannel
133
+ * (e.g. the channel the gateway routed the request through). This helper
134
+ * copies the context with the caller-supplied sourceChannel.
135
+ */
136
+ export function toGuardianRuntimeContext(
137
+ sourceChannel: import('../channels/types.js').ChannelId,
138
+ ctx: GuardianRuntimeContext,
139
+ ): GuardianRuntimeContext {
140
+ return { ...ctx, sourceChannel };
141
+ }
@@ -44,6 +44,8 @@ export interface GuardianDecisionAction {
44
44
  /** Canonical set of all guardian decision actions with their labels. */
45
45
  export const GUARDIAN_DECISION_ACTIONS = {
46
46
  approve_once: { action: 'approve_once', label: 'Approve once' },
47
+ approve_10m: { action: 'approve_10m', label: 'Allow 10 min' },
48
+ approve_thread: { action: 'approve_thread', label: 'Allow thread' },
47
49
  approve_always: { action: 'approve_always', label: 'Approve always' },
48
50
  reject: { action: 'reject', label: 'Reject' },
49
51
  } as const satisfies Record<string, GuardianDecisionAction>;
@@ -54,16 +56,21 @@ export const GUARDIAN_DECISION_ACTIONS = {
54
56
  *
55
57
  * When `persistentDecisionsAllowed` is `false`, the `approve_always` action
56
58
  * is excluded. When `forGuardianOnBehalf` is `true` (guardian acting on behalf
57
- * of a requester), `approve_always` is also excluded since guardians cannot
58
- * permanently allowlist tools on behalf of others.
59
+ * of a requester), both `approve_always` and the temporary modes are excluded
60
+ * since guardians cannot grant broad delegated allow modes on behalf of others.
61
+ *
62
+ * Temporary modes (`approve_10m`, `approve_thread`) are included for
63
+ * requester-side standard approval flows when persistent decisions are allowed.
59
64
  */
60
65
  export function buildDecisionActions(opts?: {
61
66
  persistentDecisionsAllowed?: boolean;
62
67
  forGuardianOnBehalf?: boolean;
63
68
  }): GuardianDecisionAction[] {
64
69
  const showAlways = opts?.persistentDecisionsAllowed !== false && !opts?.forGuardianOnBehalf;
70
+ const showTemporary = opts?.persistentDecisionsAllowed !== false && !opts?.forGuardianOnBehalf;
65
71
  return [
66
72
  GUARDIAN_DECISION_ACTIONS.approve_once,
73
+ ...(showTemporary ? [GUARDIAN_DECISION_ACTIONS.approve_10m, GUARDIAN_DECISION_ACTIONS.approve_thread] : []),
67
74
  ...(showAlways ? [GUARDIAN_DECISION_ACTIONS.approve_always] : []),
68
75
  GUARDIAN_DECISION_ACTIONS.reject,
69
76
  ];
@@ -72,16 +79,26 @@ export function buildDecisionActions(opts?: {
72
79
  /**
73
80
  * Build the plain-text fallback instruction string that matches the given
74
81
  * set of decision actions. Ensures the text always includes parser-compatible
75
- * keywords (yes/always/no) so text-based fallback remains actionable.
82
+ * keywords so text-based fallback remains actionable.
76
83
  */
77
84
  export function buildPlainTextFallback(
78
85
  promptText: string,
79
86
  actions: GuardianDecisionAction[],
80
87
  ): string {
81
88
  const hasAlways = actions.some(a => a.action === 'approve_always');
82
- return hasAlways
83
- ? `${promptText}\n\nReply "yes" to approve once, "always" to approve always, or "no" to reject.`
84
- : `${promptText}\n\nReply "yes" to approve or "no" to reject.`;
89
+ const has10m = actions.some(a => a.action === 'approve_10m');
90
+ const hasThread = actions.some(a => a.action === 'approve_thread');
91
+
92
+ if (hasAlways && has10m && hasThread) {
93
+ return `${promptText}\n\nReply "yes" to approve once, "approve for 10 minutes", "approve for thread", "always" to approve always, or "no" to reject.`;
94
+ }
95
+ if (hasAlways) {
96
+ return `${promptText}\n\nReply "yes" to approve once, "always" to approve always, or "no" to reject.`;
97
+ }
98
+ if (has10m && hasThread) {
99
+ return `${promptText}\n\nReply "yes" to approve once, "approve for 10 minutes", "approve for thread", or "no" to reject.`;
100
+ }
101
+ return `${promptText}\n\nReply "yes" to approve or "no" to reject.`;
85
102
  }
86
103
 
87
104
  // ---------------------------------------------------------------------------
@@ -16,8 +16,8 @@ import { sendMessage as sendSms } from "../messaging/providers/sms/client.js";
16
16
  import { getCredentialMetadata } from "../tools/credentials/metadata-store.js";
17
17
  import { getLogger } from "../util/logger.js";
18
18
  import { normalizePhoneNumber } from "../util/phone.js";
19
- import { readHttpToken } from "../util/platform.js";
20
19
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "./assistant-scope.js";
20
+ import { mintDaemonDeliveryToken } from "./auth/token-service.js";
21
21
  import {
22
22
  countRecentSendsToDestination,
23
23
  createOutboundSession,
@@ -153,13 +153,7 @@ function deliverVerificationSms(
153
153
  (async () => {
154
154
  try {
155
155
  const gatewayUrl = getGatewayInternalBaseUrl();
156
- const bearerToken = readHttpToken();
157
- if (!bearerToken) {
158
- log.error(
159
- "Cannot deliver verification SMS: no runtime HTTP token available",
160
- );
161
- return;
162
- }
156
+ const bearerToken = mintDaemonDeliveryToken();
163
157
  await sendSms(gatewayUrl, bearerToken, to, text, assistantId);
164
158
  log.info({ to, assistantId }, "Verification SMS delivered");
165
159
  } catch (err) {
@@ -184,13 +178,7 @@ function deliverVerificationTelegram(
184
178
  (async () => {
185
179
  try {
186
180
  const gatewayUrl = getGatewayInternalBaseUrl();
187
- const bearerToken = readHttpToken();
188
- if (!bearerToken) {
189
- log.error(
190
- "Cannot deliver verification Telegram message: no runtime HTTP token available",
191
- );
192
- return;
193
- }
181
+ const bearerToken = mintDaemonDeliveryToken();
194
182
  const url = `${gatewayUrl}/deliver/telegram`;
195
183
  const resp = await fetch(url, {
196
184
  method: "POST",
@@ -646,13 +634,7 @@ function deliverVerificationSlack(
646
634
  (async () => {
647
635
  try {
648
636
  const gatewayUrl = getGatewayInternalBaseUrl();
649
- const bearerToken = readHttpToken();
650
- if (!bearerToken) {
651
- log.error(
652
- "Cannot deliver verification Slack DM: no runtime HTTP token available",
653
- );
654
- return;
655
- }
637
+ const bearerToken = mintDaemonDeliveryToken();
656
638
  const url = `${gatewayUrl}/deliver/slack`;
657
639
  const resp = await fetch(url, {
658
640
  method: "POST",
@@ -108,6 +108,8 @@ export interface GuardianReplyResult {
108
108
 
109
109
  const VALID_ACTIONS: ReadonlySet<string> = new Set([
110
110
  'approve_once',
111
+ 'approve_10m',
112
+ 'approve_thread',
111
113
  'approve_always',
112
114
  'reject',
113
115
  ]);
@@ -476,9 +478,9 @@ export async function routeGuardianReply(
476
478
  // Decision-bearing disposition from the engine
477
479
  let decisionAction = engineResult.disposition as ApprovalAction;
478
480
 
479
- // Guardians cannot approve_always — the canonical primitive enforces
480
- // this too, but enforce it here for clarity.
481
- if (decisionAction === 'approve_always') {
481
+ // Guardians cannot use broad allow modes — the canonical primitive
482
+ // enforces this too, but enforce it here for clarity.
483
+ if (decisionAction === 'approve_always' || decisionAction === 'approve_10m' || decisionAction === 'approve_thread') {
482
484
  decisionAction = 'approve_once';
483
485
  }
484
486