@vellumai/assistant 0.3.18 → 0.3.20

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 (202) hide show
  1. package/ARCHITECTURE.md +155 -15
  2. package/Dockerfile +1 -0
  3. package/README.md +40 -4
  4. package/docs/architecture/integrations.md +7 -11
  5. package/docs/architecture/security.md +80 -0
  6. package/package.json +1 -1
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +58 -0
  8. package/src/__tests__/approval-primitive.test.ts +540 -0
  9. package/src/__tests__/assistant-feature-flag-guard.test.ts +206 -0
  10. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +198 -0
  11. package/src/__tests__/assistant-feature-flags-integration.test.ts +272 -0
  12. package/src/__tests__/call-controller.test.ts +605 -104
  13. package/src/__tests__/channel-invite-transport.test.ts +264 -0
  14. package/src/__tests__/checker.test.ts +60 -0
  15. package/src/__tests__/cli.test.ts +42 -1
  16. package/src/__tests__/config-schema.test.ts +11 -127
  17. package/src/__tests__/config-watcher.test.ts +0 -8
  18. package/src/__tests__/daemon-lifecycle.test.ts +1 -0
  19. package/src/__tests__/daemon-server-session-init.test.ts +8 -2
  20. package/src/__tests__/diff.test.ts +22 -0
  21. package/src/__tests__/guardian-action-copy-generator.test.ts +5 -0
  22. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +779 -0
  23. package/src/__tests__/guardian-action-late-reply.test.ts +546 -1
  24. package/src/__tests__/guardian-actions-endpoint.test.ts +774 -0
  25. package/src/__tests__/guardian-control-plane-policy.test.ts +36 -3
  26. package/src/__tests__/guardian-dispatch.test.ts +185 -1
  27. package/src/__tests__/guardian-grant-minting.test.ts +532 -0
  28. package/src/__tests__/inbound-invite-redemption.test.ts +367 -0
  29. package/src/__tests__/invite-redemption-service.test.ts +306 -0
  30. package/src/__tests__/ipc-snapshot.test.ts +58 -0
  31. package/src/__tests__/notification-decision-fallback.test.ts +88 -0
  32. package/src/__tests__/remote-skill-policy.test.ts +215 -0
  33. package/src/__tests__/sandbox-diagnostics.test.ts +6 -249
  34. package/src/__tests__/sandbox-host-parity.test.ts +6 -13
  35. package/src/__tests__/scoped-approval-grants.test.ts +521 -0
  36. package/src/__tests__/scoped-grant-security-matrix.test.ts +444 -0
  37. package/src/__tests__/script-proxy-session-manager.test.ts +1 -19
  38. package/src/__tests__/session-load-history-repair.test.ts +169 -2
  39. package/src/__tests__/session-runtime-assembly.test.ts +33 -5
  40. package/src/__tests__/skill-feature-flags-integration.test.ts +171 -0
  41. package/src/__tests__/skill-feature-flags.test.ts +188 -0
  42. package/src/__tests__/skill-load-feature-flag.test.ts +141 -0
  43. package/src/__tests__/skill-mirror-parity.test.ts +1 -0
  44. package/src/__tests__/skill-projection-feature-flag.test.ts +363 -0
  45. package/src/__tests__/system-prompt.test.ts +1 -1
  46. package/src/__tests__/terminal-sandbox.test.ts +142 -9
  47. package/src/__tests__/terminal-tools.test.ts +2 -93
  48. package/src/__tests__/thread-seed-composer.test.ts +18 -0
  49. package/src/__tests__/tool-approval-handler.test.ts +350 -0
  50. package/src/__tests__/trust-store.test.ts +2 -0
  51. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +8 -10
  52. package/src/__tests__/voice-scoped-grant-consumer.test.ts +533 -0
  53. package/src/agent/loop.ts +36 -1
  54. package/src/approvals/approval-primitive.ts +381 -0
  55. package/src/approvals/guardian-decision-primitive.ts +191 -0
  56. package/src/calls/call-controller.ts +276 -212
  57. package/src/calls/call-domain.ts +56 -6
  58. package/src/calls/guardian-dispatch.ts +56 -0
  59. package/src/calls/relay-server.ts +13 -0
  60. package/src/calls/types.ts +1 -1
  61. package/src/calls/voice-session-bridge.ts +59 -4
  62. package/src/cli/core-commands.ts +0 -4
  63. package/src/cli.ts +76 -34
  64. package/src/config/__tests__/feature-flag-registry-guard.test.ts +179 -0
  65. package/src/config/assistant-feature-flags.ts +162 -0
  66. package/src/config/bundled-skills/api-mapping/icon.svg +18 -0
  67. package/src/config/bundled-skills/messaging/TOOLS.json +30 -0
  68. package/src/config/bundled-skills/messaging/tools/slack-delete-message.ts +24 -0
  69. package/src/config/bundled-skills/notifications/SKILL.md +18 -0
  70. package/src/config/bundled-skills/reminder/SKILL.md +49 -2
  71. package/src/config/bundled-skills/time-based-actions/SKILL.md +49 -2
  72. package/src/config/bundled-skills/voice-setup/SKILL.md +122 -0
  73. package/src/config/core-schema.ts +1 -1
  74. package/src/config/env-registry.ts +10 -0
  75. package/src/config/feature-flag-registry.json +61 -0
  76. package/src/config/loader.ts +22 -1
  77. package/src/config/sandbox-schema.ts +0 -39
  78. package/src/config/schema.ts +12 -2
  79. package/src/config/skill-state.ts +34 -0
  80. package/src/config/skills-schema.ts +26 -0
  81. package/src/config/skills.ts +9 -0
  82. package/src/config/system-prompt.ts +110 -46
  83. package/src/config/templates/SOUL.md +1 -1
  84. package/src/config/types.ts +19 -1
  85. package/src/config/vellum-skills/catalog.json +1 -1
  86. package/src/config/vellum-skills/guardian-verify-setup/SKILL.md +1 -0
  87. package/src/config/vellum-skills/sms-setup/SKILL.md +1 -1
  88. package/src/config/vellum-skills/telegram-setup/SKILL.md +1 -1
  89. package/src/config/vellum-skills/trusted-contacts/SKILL.md +104 -3
  90. package/src/config/vellum-skills/twilio-setup/SKILL.md +1 -1
  91. package/src/daemon/config-watcher.ts +0 -1
  92. package/src/daemon/daemon-control.ts +1 -1
  93. package/src/daemon/guardian-invite-intent.ts +124 -0
  94. package/src/daemon/handlers/avatar.ts +68 -0
  95. package/src/daemon/handlers/browser.ts +2 -2
  96. package/src/daemon/handlers/config-channels.ts +18 -0
  97. package/src/daemon/handlers/guardian-actions.ts +120 -0
  98. package/src/daemon/handlers/index.ts +4 -0
  99. package/src/daemon/handlers/sessions.ts +19 -0
  100. package/src/daemon/handlers/shared.ts +3 -1
  101. package/src/daemon/handlers/skills.ts +45 -2
  102. package/src/daemon/install-cli-launchers.ts +58 -13
  103. package/src/daemon/ipc-contract/guardian-actions.ts +53 -0
  104. package/src/daemon/ipc-contract/sessions.ts +8 -2
  105. package/src/daemon/ipc-contract/settings.ts +25 -2
  106. package/src/daemon/ipc-contract/skills.ts +1 -0
  107. package/src/daemon/ipc-contract-inventory.json +10 -0
  108. package/src/daemon/ipc-contract.ts +4 -0
  109. package/src/daemon/lifecycle.ts +6 -2
  110. package/src/daemon/main.ts +1 -0
  111. package/src/daemon/server.ts +1 -0
  112. package/src/daemon/session-lifecycle.ts +52 -7
  113. package/src/daemon/session-memory.ts +45 -0
  114. package/src/daemon/session-process.ts +260 -422
  115. package/src/daemon/session-runtime-assembly.ts +12 -0
  116. package/src/daemon/session-skill-tools.ts +14 -1
  117. package/src/daemon/session-tool-setup.ts +5 -0
  118. package/src/daemon/session.ts +11 -0
  119. package/src/daemon/tool-side-effects.ts +35 -9
  120. package/src/index.ts +0 -2
  121. package/src/memory/conversation-display-order-migration.ts +44 -0
  122. package/src/memory/conversation-queries.ts +2 -0
  123. package/src/memory/conversation-store.ts +91 -0
  124. package/src/memory/db-init.ts +13 -1
  125. package/src/memory/embedding-local.ts +22 -8
  126. package/src/memory/guardian-action-store.ts +133 -2
  127. package/src/memory/guardian-verification.ts +1 -1
  128. package/src/memory/ingress-invite-store.ts +95 -1
  129. package/src/memory/migrations/033-scoped-approval-grants.ts +51 -0
  130. package/src/memory/migrations/034-guardian-action-tool-metadata.ts +12 -0
  131. package/src/memory/migrations/035-guardian-action-supersession.ts +23 -0
  132. package/src/memory/migrations/index.ts +3 -0
  133. package/src/memory/schema.ts +35 -1
  134. package/src/memory/scoped-approval-grants.ts +518 -0
  135. package/src/messaging/providers/slack/client.ts +12 -0
  136. package/src/messaging/providers/slack/types.ts +5 -0
  137. package/src/notifications/decision-engine.ts +49 -12
  138. package/src/notifications/emit-signal.ts +7 -0
  139. package/src/notifications/signal.ts +7 -0
  140. package/src/notifications/thread-seed-composer.ts +2 -1
  141. package/src/permissions/checker.ts +27 -0
  142. package/src/runtime/channel-approval-types.ts +16 -6
  143. package/src/runtime/channel-approvals.ts +19 -15
  144. package/src/runtime/channel-invite-transport.ts +85 -0
  145. package/src/runtime/channel-invite-transports/telegram.ts +105 -0
  146. package/src/runtime/guardian-action-grant-minter.ts +154 -0
  147. package/src/runtime/guardian-action-message-composer.ts +30 -0
  148. package/src/runtime/guardian-decision-types.ts +91 -0
  149. package/src/runtime/http-server.ts +23 -1
  150. package/src/runtime/ingress-service.ts +22 -0
  151. package/src/runtime/invite-redemption-service.ts +181 -0
  152. package/src/runtime/invite-redemption-templates.ts +39 -0
  153. package/src/runtime/routes/call-routes.ts +2 -1
  154. package/src/runtime/routes/guardian-action-routes.ts +206 -0
  155. package/src/runtime/routes/guardian-approval-interception.ts +66 -74
  156. package/src/runtime/routes/inbound-message-handler.ts +568 -409
  157. package/src/runtime/routes/pairing-routes.ts +4 -0
  158. package/src/security/encrypted-store.ts +31 -17
  159. package/src/security/keychain.ts +176 -2
  160. package/src/security/secure-keys.ts +97 -0
  161. package/src/security/tool-approval-digest.ts +67 -0
  162. package/src/skills/remote-skill-policy.ts +131 -0
  163. package/src/tools/browser/browser-execution.ts +2 -2
  164. package/src/tools/browser/browser-manager.ts +46 -32
  165. package/src/tools/browser/browser-screencast.ts +2 -2
  166. package/src/tools/calls/call-start.ts +1 -1
  167. package/src/tools/executor.ts +22 -17
  168. package/src/tools/network/script-proxy/session-manager.ts +1 -5
  169. package/src/tools/skills/load.ts +22 -8
  170. package/src/tools/system/avatar-generator.ts +119 -0
  171. package/src/tools/system/navigate-settings.ts +65 -0
  172. package/src/tools/system/open-system-settings.ts +75 -0
  173. package/src/tools/system/voice-config.ts +121 -32
  174. package/src/tools/terminal/backends/native.ts +40 -19
  175. package/src/tools/terminal/backends/types.ts +3 -3
  176. package/src/tools/terminal/parser.ts +1 -1
  177. package/src/tools/terminal/sandbox-diagnostics.ts +6 -87
  178. package/src/tools/terminal/sandbox.ts +1 -12
  179. package/src/tools/terminal/shell.ts +3 -31
  180. package/src/tools/tool-approval-handler.ts +141 -3
  181. package/src/tools/tool-manifest.ts +6 -0
  182. package/src/tools/types.ts +6 -0
  183. package/src/util/diff.ts +36 -13
  184. package/Dockerfile.sandbox +0 -5
  185. package/src/__tests__/doordash-client.test.ts +0 -187
  186. package/src/__tests__/doordash-session.test.ts +0 -154
  187. package/src/__tests__/signup-e2e.test.ts +0 -354
  188. package/src/__tests__/terminal-sandbox-docker.test.ts +0 -1065
  189. package/src/__tests__/terminal-sandbox.integration.test.ts +0 -180
  190. package/src/cli/doordash.ts +0 -1057
  191. package/src/config/bundled-skills/doordash/SKILL.md +0 -163
  192. package/src/config/templates/LOOKS.md +0 -25
  193. package/src/doordash/cart-queries.ts +0 -787
  194. package/src/doordash/client.ts +0 -1016
  195. package/src/doordash/order-queries.ts +0 -85
  196. package/src/doordash/queries.ts +0 -13
  197. package/src/doordash/query-extractor.ts +0 -94
  198. package/src/doordash/search-queries.ts +0 -203
  199. package/src/doordash/session.ts +0 -84
  200. package/src/doordash/store-queries.ts +0 -246
  201. package/src/doordash/types.ts +0 -367
  202. package/src/tools/terminal/backends/docker.ts +0 -379
@@ -0,0 +1,53 @@
1
+ // Guardian action decision IPC types.
2
+ // Enables desktop clients to fetch pending guardian prompts and submit
3
+ // button decisions deterministically (without text parsing).
4
+
5
+ // === Client -> Server ===
6
+
7
+ export interface GuardianActionsPendingRequest {
8
+ type: 'guardian_actions_pending_request';
9
+ conversationId: string;
10
+ }
11
+
12
+ export interface GuardianActionDecision {
13
+ type: 'guardian_action_decision';
14
+ requestId: string;
15
+ action: string;
16
+ conversationId?: string;
17
+ }
18
+
19
+ // === Server -> Client ===
20
+
21
+ export interface GuardianActionsPendingResponse {
22
+ type: 'guardian_actions_pending_response';
23
+ conversationId: string;
24
+ prompts: Array<{
25
+ requestId: string;
26
+ requestCode: string;
27
+ state: string;
28
+ questionText: string;
29
+ toolName: string | null;
30
+ actions: Array<{ action: string; label: string }>;
31
+ expiresAt: number;
32
+ conversationId: string;
33
+ callSessionId: string | null;
34
+ }>;
35
+ }
36
+
37
+ export interface GuardianActionDecisionResponse {
38
+ type: 'guardian_action_decision_response';
39
+ applied: boolean;
40
+ reason?: string;
41
+ requestId?: string;
42
+ userText?: string;
43
+ }
44
+
45
+ // --- Domain-level union aliases (consumed by the barrel file) ---
46
+
47
+ export type _GuardianActionsClientMessages =
48
+ | GuardianActionsPendingRequest
49
+ | GuardianActionDecision;
50
+
51
+ export type _GuardianActionsServerMessages =
52
+ | GuardianActionsPendingResponse
53
+ | GuardianActionDecisionResponse;
@@ -156,6 +156,11 @@ export interface ConversationSearchRequest {
156
156
  maxMessagesPerConversation?: number;
157
157
  }
158
158
 
159
+ export interface ReorderThreadsRequest {
160
+ type: 'reorder_threads';
161
+ updates: Array<{ sessionId: string; displayOrder: number | null; isPinned: boolean }>;
162
+ }
163
+
159
164
  // === Server → Client ===
160
165
 
161
166
  export interface ConversationSearchMatchingMessage {
@@ -213,7 +218,7 @@ export interface AssistantAttention {
213
218
 
214
219
  export interface SessionListResponse {
215
220
  type: 'session_list_response';
216
- sessions: Array<{ id: string; title: string; createdAt?: number; updatedAt: number; threadType?: ThreadType; source?: string; channelBinding?: ChannelBinding; conversationOriginChannel?: ChannelId; conversationOriginInterface?: InterfaceId; assistantAttention?: AssistantAttention }>;
221
+ sessions: Array<{ id: string; title: string; createdAt?: number; updatedAt: number; threadType?: ThreadType; source?: string; channelBinding?: ChannelBinding; conversationOriginChannel?: ChannelId; conversationOriginInterface?: InterfaceId; assistantAttention?: AssistantAttention; displayOrder?: number; isPinned?: boolean }>;
217
222
  /** Whether more sessions exist beyond the returned page. */
218
223
  hasMore?: boolean;
219
224
  }
@@ -392,7 +397,8 @@ export type _SessionsClientMessages =
392
397
  | SessionRenameRequest
393
398
  | SessionsClearRequest
394
399
  | ConversationSearchRequest
395
- | MessageContentRequest;
400
+ | MessageContentRequest
401
+ | ReorderThreadsRequest;
396
402
 
397
403
  export type _SessionsServerMessages =
398
404
  | AuthResult
@@ -9,6 +9,13 @@ export interface VoiceConfigUpdateRequest {
9
9
  activationKey: string;
10
10
  }
11
11
 
12
+ /** Request from the client to generate a custom avatar via Gemini. */
13
+ export interface GenerateAvatarRequest {
14
+ type: 'generate_avatar';
15
+ /** Text description of the desired avatar appearance. */
16
+ description: string;
17
+ }
18
+
12
19
  // === Server → Client ===
13
20
 
14
21
  /** Sent by the daemon to update a client-side setting (e.g. activation key). */
@@ -20,7 +27,23 @@ export interface ClientSettingsUpdate {
20
27
  value: string;
21
28
  }
22
29
 
30
+ /** Sent by the daemon after the avatar image has been regenerated and saved to disk. */
31
+ export interface AvatarUpdated {
32
+ type: 'avatar_updated';
33
+ /** Absolute path to the updated avatar image file. */
34
+ avatarPath: string;
35
+ }
36
+
37
+ /** Response to a generate_avatar request indicating success or failure. */
38
+ export interface GenerateAvatarResponse {
39
+ type: 'generate_avatar_response';
40
+ /** Whether the avatar was generated successfully. */
41
+ success: boolean;
42
+ /** Error message when success is false. */
43
+ error?: string;
44
+ }
45
+
23
46
  // --- Domain-level union aliases (consumed by the barrel file) ---
24
47
 
25
- export type _SettingsClientMessages = VoiceConfigUpdateRequest;
26
- export type _SettingsServerMessages = ClientSettingsUpdate;
48
+ export type _SettingsClientMessages = VoiceConfigUpdateRequest | GenerateAvatarRequest;
49
+ export type _SettingsServerMessages = ClientSettingsUpdate | AvatarUpdated | GenerateAvatarResponse;
@@ -95,6 +95,7 @@ export interface SkillsListResponse {
95
95
  updateAvailable: boolean;
96
96
  userInvocable: boolean;
97
97
  clawhub?: { author: string; stars: number; installs: number; reports: number; publishedAt: string };
98
+ provenance?: { kind: 'first-party' | 'third-party' | 'local'; provider?: string; originId?: string; sourceUrl?: string };
98
99
  }>;
99
100
  }
100
101
 
@@ -5,6 +5,7 @@
5
5
  "_ComputerUseClientMessages",
6
6
  "_DiagnosticsClientMessages",
7
7
  "_DocumentsClientMessages",
8
+ "_GuardianActionsClientMessages",
8
9
  "_InboxClientMessages",
9
10
  "_IntegrationsClientMessages",
10
11
  "_MessagesClientMessages",
@@ -28,6 +29,7 @@
28
29
  "_ComputerUseServerMessages",
29
30
  "_DiagnosticsServerMessages",
30
31
  "_DocumentsServerMessages",
32
+ "_GuardianActionsServerMessages",
31
33
  "_InboxServerMessages",
32
34
  "_IntegrationsServerMessages",
33
35
  "_MemoryServerMessages",
@@ -86,7 +88,10 @@
86
88
  "fork_shared_app",
87
89
  "gallery_install",
88
90
  "gallery_list",
91
+ "generate_avatar",
89
92
  "get_signing_identity_response",
93
+ "guardian_action_decision",
94
+ "guardian_actions_pending_request",
90
95
  "guardian_verification",
91
96
  "heartbeat_checklist_read",
92
97
  "heartbeat_checklist_write",
@@ -124,6 +129,7 @@
124
129
  "reminder_cancel",
125
130
  "reminders_list",
126
131
  "remove_trust_rule",
132
+ "reorder_threads",
127
133
  "ride_shotgun_start",
128
134
  "ride_shotgun_stop",
129
135
  "sandbox_set",
@@ -210,6 +216,7 @@
210
216
  "assistant_text_delta",
211
217
  "assistant_thinking_delta",
212
218
  "auth_result",
219
+ "avatar_updated",
213
220
  "browser_cdp_request",
214
221
  "browser_frame",
215
222
  "browser_handoff_request",
@@ -236,9 +243,12 @@
236
243
  "fork_shared_app_response",
237
244
  "gallery_install_response",
238
245
  "gallery_list_response",
246
+ "generate_avatar_response",
239
247
  "generation_cancelled",
240
248
  "generation_handoff",
241
249
  "get_signing_identity",
250
+ "guardian_action_decision_response",
251
+ "guardian_actions_pending_response",
242
252
  "guardian_verification_response",
243
253
  "heartbeat_alert",
244
254
  "heartbeat_checklist_response",
@@ -18,6 +18,7 @@ export * from './ipc-contract/browser.js';
18
18
  export * from './ipc-contract/computer-use.js';
19
19
  export * from './ipc-contract/diagnostics.js';
20
20
  export * from './ipc-contract/documents.js';
21
+ export * from './ipc-contract/guardian-actions.js';
21
22
  export * from './ipc-contract/inbox.js';
22
23
  export * from './ipc-contract/integrations.js';
23
24
  export * from './ipc-contract/memory.js';
@@ -42,6 +43,7 @@ import type { _BrowserClientMessages, _BrowserServerMessages } from './ipc-contr
42
43
  import type { _ComputerUseClientMessages, _ComputerUseServerMessages } from './ipc-contract/computer-use.js';
43
44
  import type { _DiagnosticsClientMessages, _DiagnosticsServerMessages } from './ipc-contract/diagnostics.js';
44
45
  import type { _DocumentsClientMessages, _DocumentsServerMessages } from './ipc-contract/documents.js';
46
+ import type { _GuardianActionsClientMessages, _GuardianActionsServerMessages } from './ipc-contract/guardian-actions.js';
45
47
  import type { _InboxClientMessages, _InboxServerMessages } from './ipc-contract/inbox.js';
46
48
  import type { _IntegrationsClientMessages, _IntegrationsServerMessages } from './ipc-contract/integrations.js';
47
49
  import type { _MemoryServerMessages } from './ipc-contract/memory.js';
@@ -83,6 +85,7 @@ export type ClientMessage =
83
85
  | _BrowserClientMessages
84
86
  | _SubagentsClientMessages
85
87
  | _DocumentsClientMessages
88
+ | _GuardianActionsClientMessages
86
89
  | _WorkspaceClientMessages
87
90
  | _SchedulesClientMessages
88
91
  | _DiagnosticsClientMessages
@@ -107,6 +110,7 @@ export type ServerMessage =
107
110
  | _BrowserServerMessages
108
111
  | _SubagentsServerMessages
109
112
  | _DocumentsServerMessages
113
+ | _GuardianActionsServerMessages
110
114
  | _MemoryServerMessages
111
115
  | _WorkspaceServerMessages
112
116
  | _SchedulesServerMessages
@@ -57,7 +57,7 @@ import type { ServerMessage } from './ipc-protocol.js';
57
57
  import { initializeProvidersAndTools, registerMessagingProviders,registerWatcherProviders } from './providers-setup.js';
58
58
  import { seedInterfaceFiles } from './seed-files.js';
59
59
  import { DaemonServer } from './server.js';
60
- import { setGuardianActionCopyGenerator, setGuardianFollowUpConversationGenerator } from './session-process.js';
60
+ import { setApprovalConversationGenerator, setGuardianActionCopyGenerator, setGuardianFollowUpConversationGenerator } from './session-process.js';
61
61
  import { initSlashPairingContext } from './session-slash.js';
62
62
  import { installShutdownHandlers } from './shutdown-handlers.js';
63
63
 
@@ -307,7 +307,11 @@ export async function runDaemon(): Promise<void> {
307
307
  server.persistAndProcessMessage(conversationId, content, attachmentIds, options, sourceChannel, sourceInterface),
308
308
  interfacesDir: getInterfacesDir(),
309
309
  approvalCopyGenerator: createApprovalCopyGenerator(),
310
- approvalConversationGenerator: createApprovalConversationGenerator(),
310
+ approvalConversationGenerator: (() => {
311
+ const gen = createApprovalConversationGenerator();
312
+ setApprovalConversationGenerator(gen);
313
+ return gen;
314
+ })(),
311
315
  guardianActionCopyGenerator: (() => {
312
316
  const gen = createGuardianActionCopyGenerator();
313
317
  setGuardianActionCopyGenerator(gen);
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bun
2
+ process.title = 'vellum-daemon';
2
3
  import * as Sentry from '@sentry/node';
3
4
 
4
5
  import { getLogger } from '../util/logger.js';
@@ -795,6 +795,7 @@ export class DaemonServer {
795
795
  const resolvedInterface = resolveTurnInterface(sourceInterface);
796
796
  session.setAssistantId(options?.assistantId ?? 'self');
797
797
  session.setGuardianContext(options?.guardianContext ?? null);
798
+ await session.ensureActorScopedHistory();
798
799
  session.setChannelCapabilities(resolveChannelCapabilities(sourceChannel, sourceInterface));
799
800
  session.setCommandIntent(options?.commandIntent ?? null);
800
801
  session.setTurnChannelContext({
@@ -19,10 +19,38 @@ import { repairHistory } from './history-repair.js';
19
19
  import type { SurfaceData,SurfaceType, UsageStats } from './ipc-protocol.js';
20
20
  import { unregisterCallNotifiers,unregisterWatchNotifiers } from './session-notifiers.js';
21
21
  import type { MessageQueue } from './session-queue-manager.js';
22
+ import type { GuardianRuntimeContext } from './session-runtime-assembly.js';
22
23
  import { resetSkillToolProjection } from './session-skill-tools.js';
23
24
 
24
25
  const log = getLogger('session-lifecycle');
25
26
 
27
+ type GuardianActorRole = GuardianRuntimeContext['actorRole'];
28
+
29
+ function parseProvenanceActorRole(metadata: string | null): GuardianActorRole | undefined {
30
+ if (!metadata) return undefined;
31
+ try {
32
+ const parsed = JSON.parse(metadata) as { provenanceActorRole?: unknown };
33
+ const role = parsed?.provenanceActorRole;
34
+ if (role === 'guardian' || role === 'non-guardian' || role === 'unverified_channel') {
35
+ return role;
36
+ }
37
+ } catch {
38
+ // Ignore malformed metadata and treat as unknown provenance.
39
+ }
40
+ return undefined;
41
+ }
42
+
43
+ function isUntrustedActorRole(role: GuardianActorRole | undefined): boolean {
44
+ return role === 'non-guardian' || role === 'unverified_channel';
45
+ }
46
+
47
+ function filterMessagesForUntrustedActor(messages: conversationStore.MessageRow[]): conversationStore.MessageRow[] {
48
+ return messages.filter((m) => {
49
+ const provenanceRole = parseProvenanceActorRole(m.metadata);
50
+ return provenanceRole === 'non-guardian' || provenanceRole === 'unverified_channel';
51
+ });
52
+ }
53
+
26
54
  // ── Context Interfaces ───────────────────────────────────────────────
27
55
 
28
56
  export interface LoadFromDbContext {
@@ -31,6 +59,8 @@ export interface LoadFromDbContext {
31
59
  usageStats: UsageStats;
32
60
  contextCompactedMessageCount: number;
33
61
  contextCompactedAt: number | null;
62
+ guardianContext?: { actorRole: GuardianActorRole };
63
+ loadedHistoryActorRole?: GuardianActorRole;
34
64
  }
35
65
 
36
66
  export interface AbortContext {
@@ -59,15 +89,28 @@ export interface DisposeContext extends AbortContext {
59
89
  // ── loadFromDb ───────────────────────────────────────────────────────
60
90
 
61
91
  export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
62
- const dbMessages = conversationStore.getMessages(ctx.conversationId);
92
+ const actorRole = ctx.guardianContext?.actorRole;
93
+ const allDbMessages = conversationStore.getMessages(ctx.conversationId);
94
+ const dbMessages = isUntrustedActorRole(actorRole)
95
+ ? filterMessagesForUntrustedActor(allDbMessages)
96
+ : allDbMessages;
63
97
 
64
98
  const conv = conversationStore.getConversation(ctx.conversationId);
65
- const contextSummary = conv?.contextSummary?.trim() || null;
66
- ctx.contextCompactedMessageCount = Math.max(
67
- 0,
68
- Math.min(conv?.contextCompactedMessageCount ?? 0, dbMessages.length),
69
- );
70
- ctx.contextCompactedAt = conv?.contextCompactedAt ?? null;
99
+ const contextSummary = !isUntrustedActorRole(actorRole)
100
+ ? conv?.contextSummary?.trim() || null
101
+ : null;
102
+ if (isUntrustedActorRole(actorRole)) {
103
+ // Compacted summaries may include trusted/guardian-only details, so we
104
+ // disable summary-based context for untrusted actor views.
105
+ ctx.contextCompactedMessageCount = 0;
106
+ ctx.contextCompactedAt = null;
107
+ } else {
108
+ ctx.contextCompactedMessageCount = Math.max(
109
+ 0,
110
+ Math.min(conv?.contextCompactedMessageCount ?? 0, dbMessages.length),
111
+ );
112
+ ctx.contextCompactedAt = conv?.contextCompactedAt ?? null;
113
+ }
71
114
 
72
115
  const parsedMessages: Message[] = dbMessages
73
116
  .slice(ctx.contextCompactedMessageCount)
@@ -102,6 +145,8 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
102
145
  };
103
146
  }
104
147
 
148
+ ctx.loadedHistoryActorRole = actorRole;
149
+
105
150
  log.info({ conversationId: ctx.conversationId, count: ctx.messages.length }, 'Loaded messages from DB');
106
151
  }
107
152
 
@@ -39,6 +39,16 @@ export interface MemoryPrepareContext {
39
39
  isInteractive?: boolean;
40
40
  }
41
41
 
42
+ /**
43
+ * Returns true when the latest user turn is an internal tool-result-only
44
+ * message (no user-authored text/image content).
45
+ */
46
+ function isToolResultOnlyUserTurn(message: Message | undefined): boolean {
47
+ return message?.role === 'user'
48
+ && message.content.length > 0
49
+ && message.content.every((block) => block.type === 'tool_result');
50
+ }
51
+
42
52
  /**
43
53
  * Build memory recall, dynamic profile, and conflict gate evaluation
44
54
  * for a single agent loop turn. Returns the augmented run messages and
@@ -86,6 +96,41 @@ export async function prepareMemoryContext(
86
96
  };
87
97
  }
88
98
 
99
+ // Internal tool-result turns (assistant tool loop) should not trigger
100
+ // memory retrieval/profile injection. Injecting memory here repeats the
101
+ // same long recall block on every tool step and dramatically inflates
102
+ // per-step prompt size/latency.
103
+ const latestMessage = ctx.messages[ctx.messages.length - 1];
104
+ if (isToolResultOnlyUserTurn(latestMessage)) {
105
+ return {
106
+ runMessages: ctx.messages,
107
+ recall: {
108
+ enabled: false,
109
+ degraded: false,
110
+ injectedText: '',
111
+ lexicalHits: 0,
112
+ semanticHits: 0,
113
+ recencyHits: 0,
114
+ entityHits: 0,
115
+ relationSeedEntityCount: 0,
116
+ relationTraversedEdgeCount: 0,
117
+ relationNeighborEntityCount: 0,
118
+ relationExpandedItemCount: 0,
119
+ earlyTerminated: false,
120
+ mergedCount: 0,
121
+ selectedCount: 0,
122
+ rerankApplied: false,
123
+ injectedTokens: 0,
124
+ latencyMs: 0,
125
+ topCandidates: [],
126
+ } as Awaited<ReturnType<typeof buildMemoryRecall>>,
127
+ dynamicProfile: { text: '' },
128
+ softConflictInstruction: null,
129
+ recallInjectionStrategy: 'prepend_user_block',
130
+ conflictClarification: null,
131
+ };
132
+ }
133
+
89
134
  const runtimeConfig = getConfig();
90
135
  const memoryEnabled = runtimeConfig.memory?.enabled !== false;
91
136