@vellumai/assistant 0.5.13 → 0.5.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 (425) hide show
  1. package/.env.example +1 -6
  2. package/AGENTS.md +4 -0
  3. package/ARCHITECTURE.md +0 -1
  4. package/bunfig.toml +1 -0
  5. package/docs/architecture/memory.md +3 -3
  6. package/openapi.yaml +127 -22
  7. package/package.json +1 -1
  8. package/src/__tests__/access-request-decision.test.ts +2 -32
  9. package/src/__tests__/actor-token-service.test.ts +1 -31
  10. package/src/__tests__/anthropic-provider.test.ts +53 -40
  11. package/src/__tests__/app-git-history.test.ts +9 -17
  12. package/src/__tests__/app-git-service.test.ts +14 -20
  13. package/src/__tests__/app-store-dir-names.test.ts +10 -20
  14. package/src/__tests__/approval-cascade.test.ts +2 -19
  15. package/src/__tests__/approval-primitive.test.ts +2 -27
  16. package/src/__tests__/approval-routes-http.test.ts +2 -30
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
  19. package/src/__tests__/attachments-store.test.ts +5 -32
  20. package/src/__tests__/audit-log-rotation.test.ts +5 -36
  21. package/src/__tests__/avatar-e2e.test.ts +1 -9
  22. package/src/__tests__/avatar-generator.test.ts +1 -7
  23. package/src/__tests__/browser-fill-credential.test.ts +0 -4
  24. package/src/__tests__/browser-manager.test.ts +0 -6
  25. package/src/__tests__/call-controller.test.ts +1 -22
  26. package/src/__tests__/call-conversation-messages.test.ts +0 -21
  27. package/src/__tests__/call-domain.test.ts +0 -25
  28. package/src/__tests__/call-pointer-messages.test.ts +0 -21
  29. package/src/__tests__/call-recovery.test.ts +0 -22
  30. package/src/__tests__/call-routes-http.test.ts +0 -24
  31. package/src/__tests__/call-store.test.ts +0 -21
  32. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
  33. package/src/__tests__/canonical-guardian-store.test.ts +48 -21
  34. package/src/__tests__/channel-approval-routes.test.ts +6 -26
  35. package/src/__tests__/channel-approvals.test.ts +1 -38
  36. package/src/__tests__/channel-delivery-store.test.ts +0 -21
  37. package/src/__tests__/channel-guardian.test.ts +0 -26
  38. package/src/__tests__/channel-reply-delivery.test.ts +5 -0
  39. package/src/__tests__/channel-retry-sweep.test.ts +0 -21
  40. package/src/__tests__/checker.test.ts +26 -61
  41. package/src/__tests__/clawhub.test.ts +9 -25
  42. package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
  43. package/src/__tests__/config-loader-backfill.test.ts +9 -28
  44. package/src/__tests__/config-schema-cmd.test.ts +5 -25
  45. package/src/__tests__/config-schema.test.ts +21 -40
  46. package/src/__tests__/config-watcher.test.ts +4 -91
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
  48. package/src/__tests__/contacts-tools.test.ts +0 -21
  49. package/src/__tests__/context-memory-e2e.test.ts +0 -21
  50. package/src/__tests__/context-window-manager.test.ts +130 -3
  51. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
  52. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
  53. package/src/__tests__/conversation-agent-loop.test.ts +0 -4
  54. package/src/__tests__/conversation-attachments.test.ts +1 -24
  55. package/src/__tests__/conversation-attention-store.test.ts +0 -21
  56. package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
  57. package/src/__tests__/conversation-clear-safety.test.ts +0 -22
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
  59. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
  60. package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
  61. package/src/__tests__/conversation-disk-view.test.ts +5 -27
  62. package/src/__tests__/conversation-error.test.ts +1 -1
  63. package/src/__tests__/conversation-fork-crud.test.ts +1 -33
  64. package/src/__tests__/conversation-fork-route.test.ts +0 -27
  65. package/src/__tests__/conversation-history-web-search.test.ts +23 -16
  66. package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
  67. package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
  68. package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
  69. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
  70. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
  71. package/src/__tests__/conversation-queue.test.ts +8 -8
  72. package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
  74. package/src/__tests__/conversation-slash-commands.test.ts +5 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +0 -4
  76. package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
  77. package/src/__tests__/conversation-speed-override.test.ts +326 -0
  78. package/src/__tests__/conversation-starter-routes.test.ts +0 -23
  79. package/src/__tests__/conversation-store.test.ts +0 -21
  80. package/src/__tests__/conversation-unread-route.test.ts +0 -24
  81. package/src/__tests__/conversation-usage.test.ts +56 -21
  82. package/src/__tests__/conversation-wipe.test.ts +0 -21
  83. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  84. package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
  85. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
  86. package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
  87. package/src/__tests__/credential-vault-unit.test.ts +9 -428
  88. package/src/__tests__/credentials-cli.test.ts +10 -10
  89. package/src/__tests__/daemon-assistant-events.test.ts +0 -19
  90. package/src/__tests__/date-context.test.ts +77 -97
  91. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
  92. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
  93. package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
  94. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
  95. package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
  96. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
  97. package/src/__tests__/email-cli.test.ts +6 -6
  98. package/src/__tests__/ephemeral-permissions.test.ts +5 -17
  99. package/src/__tests__/first-greeting.test.ts +4 -32
  100. package/src/__tests__/followup-tools.test.ts +0 -21
  101. package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
  102. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
  103. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
  104. package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
  105. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
  106. package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
  107. package/src/__tests__/guardian-action-store.test.ts +0 -21
  108. package/src/__tests__/guardian-action-sweep.test.ts +0 -21
  109. package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
  110. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
  111. package/src/__tests__/guardian-dispatch.test.ts +0 -21
  112. package/src/__tests__/guardian-grant-minting.test.ts +0 -22
  113. package/src/__tests__/guardian-outbound-http.test.ts +0 -22
  114. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
  115. package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
  116. package/src/__tests__/guardian-routing-state.test.ts +0 -22
  117. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
  118. package/src/__tests__/headless-browser-interactions.test.ts +0 -4
  119. package/src/__tests__/headless-browser-navigate.test.ts +0 -4
  120. package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
  121. package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
  122. package/src/__tests__/heartbeat-service.test.ts +99 -26
  123. package/src/__tests__/hooks-blocking.test.ts +3 -3
  124. package/src/__tests__/hooks-config.test.ts +7 -7
  125. package/src/__tests__/hooks-discovery.test.ts +3 -3
  126. package/src/__tests__/hooks-integration.test.ts +5 -5
  127. package/src/__tests__/hooks-manager.test.ts +3 -3
  128. package/src/__tests__/hooks-runner.test.ts +5 -23
  129. package/src/__tests__/hooks-settings.test.ts +3 -3
  130. package/src/__tests__/hooks-templates.test.ts +3 -3
  131. package/src/__tests__/http-conversation-lineage.test.ts +0 -27
  132. package/src/__tests__/identity-intro-cache.test.ts +0 -4
  133. package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
  134. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
  135. package/src/__tests__/intent-routing.test.ts +2 -55
  136. package/src/__tests__/invite-redemption-service.test.ts +0 -21
  137. package/src/__tests__/invite-routes-http.test.ts +0 -21
  138. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
  139. package/src/__tests__/journal-context.test.ts +8 -75
  140. package/src/__tests__/list-messages-attachments.test.ts +0 -22
  141. package/src/__tests__/llm-context-route-provider.test.ts +0 -21
  142. package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
  143. package/src/__tests__/llm-usage-store.test.ts +0 -21
  144. package/src/__tests__/log-export-workspace.test.ts +1 -1
  145. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
  146. package/src/__tests__/managed-store.test.ts +1 -1
  147. package/src/__tests__/mcp-cli.test.ts +7 -10
  148. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
  149. package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
  150. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
  151. package/src/__tests__/memory-recall-log-store.test.ts +0 -27
  152. package/src/__tests__/memory-recall-quality.test.ts +0 -21
  153. package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
  154. package/src/__tests__/memory-regressions.test.ts +282 -70
  155. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
  156. package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
  157. package/src/__tests__/messaging-send-tool.test.ts +201 -0
  158. package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
  159. package/src/__tests__/migration-export-http.test.ts +7 -1
  160. package/src/__tests__/migration-import-commit-http.test.ts +16 -14
  161. package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
  162. package/src/__tests__/migration-validate-http.test.ts +1 -28
  163. package/src/__tests__/native-web-search.test.ts +25 -22
  164. package/src/__tests__/non-member-access-request.test.ts +0 -22
  165. package/src/__tests__/notification-guardian-path.test.ts +0 -21
  166. package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
  167. package/src/__tests__/oauth-apps-routes.test.ts +103 -2
  168. package/src/__tests__/oauth-cli.test.ts +52 -0
  169. package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
  170. package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
  171. package/src/__tests__/oauth-providers-routes.test.ts +257 -0
  172. package/src/__tests__/oauth-store.test.ts +0 -21
  173. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  174. package/src/__tests__/openai-provider.test.ts +261 -0
  175. package/src/__tests__/pairing-concurrent.test.ts +6 -6
  176. package/src/__tests__/pairing-routes.test.ts +7 -1
  177. package/src/__tests__/path-policy.test.ts +1 -1
  178. package/src/__tests__/platform.test.ts +64 -88
  179. package/src/__tests__/playbook-execution.test.ts +0 -21
  180. package/src/__tests__/playbook-tools.test.ts +0 -21
  181. package/src/__tests__/pricing.test.ts +100 -0
  182. package/src/__tests__/relay-server.test.ts +1 -25
  183. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
  184. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
  185. package/src/__tests__/runtime-events-sse.test.ts +0 -24
  186. package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
  187. package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
  188. package/src/__tests__/schedule-store.test.ts +0 -21
  189. package/src/__tests__/schedule-tools.test.ts +0 -21
  190. package/src/__tests__/scheduler-recurrence.test.ts +0 -21
  191. package/src/__tests__/scoped-approval-grants.test.ts +0 -21
  192. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
  193. package/src/__tests__/secret-allowlist.test.ts +1 -1
  194. package/src/__tests__/secret-ingress-channel.test.ts +0 -5
  195. package/src/__tests__/secret-ingress-cli.test.ts +0 -6
  196. package/src/__tests__/secret-ingress-http.test.ts +0 -5
  197. package/src/__tests__/secret-ingress.test.ts +0 -5
  198. package/src/__tests__/send-endpoint-busy.test.ts +0 -24
  199. package/src/__tests__/sequence-store.test.ts +0 -21
  200. package/src/__tests__/server-history-render.test.ts +0 -24
  201. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
  202. package/src/__tests__/skill-load-inline-command.test.ts +9 -0
  203. package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
  204. package/src/__tests__/skill-load-tool.test.ts +11 -0
  205. package/src/__tests__/skills-uninstall.test.ts +10 -8
  206. package/src/__tests__/skills.test.ts +1 -1
  207. package/src/__tests__/slack-channel-config.test.ts +1 -1
  208. package/src/__tests__/slack-inbound-verification.test.ts +0 -22
  209. package/src/__tests__/starter-bundle.test.ts +4 -1
  210. package/src/__tests__/suggestion-routes.test.ts +2 -0
  211. package/src/__tests__/system-prompt.test.ts +1 -1
  212. package/src/__tests__/terminal-tools.test.ts +1 -1
  213. package/src/__tests__/test-preload.ts +31 -0
  214. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
  215. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  216. package/src/__tests__/tool-executor.test.ts +0 -20
  217. package/src/__tests__/tool-input-summary.test.ts +124 -0
  218. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
  219. package/src/__tests__/trust-store.test.ts +7 -1
  220. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
  221. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
  222. package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
  223. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  224. package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
  225. package/src/__tests__/twilio-routes.test.ts +1 -1
  226. package/src/__tests__/update-bulletin.test.ts +1 -1
  227. package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
  228. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
  229. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
  230. package/src/__tests__/voice-session-bridge.test.ts +1 -1
  231. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
  232. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
  233. package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
  234. package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
  235. package/src/agent/loop.ts +6 -9
  236. package/src/approvals/guardian-decision-primitive.ts +46 -18
  237. package/src/approvals/guardian-request-resolvers.ts +19 -2
  238. package/src/calls/active-call-lease.ts +2 -2
  239. package/src/cli/AGENTS.md +1 -1
  240. package/src/cli/commands/doctor.ts +9 -9
  241. package/src/cli/commands/memory.ts +142 -0
  242. package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
  243. package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
  244. package/src/cli/commands/oauth/connect.ts +13 -12
  245. package/src/cli/commands/oauth/index.ts +1 -1
  246. package/src/cli/commands/oauth/providers.ts +47 -62
  247. package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
  248. package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
  249. package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
  250. package/src/cli/commands/platform/connect.ts +17 -7
  251. package/src/cli/commands/platform/disconnect.ts +28 -3
  252. package/src/cli/commands/platform/index.ts +3 -3
  253. package/src/cli.ts +1 -299
  254. package/src/config/assistant-feature-flags.ts +23 -15
  255. package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
  256. package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
  257. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
  258. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
  259. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
  260. package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
  261. package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
  262. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
  263. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
  264. package/src/config/bundled-skills/messaging/SKILL.md +7 -7
  265. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
  266. package/src/config/bundled-skills/slack/SKILL.md +18 -0
  267. package/src/config/env-registry.ts +15 -11
  268. package/src/config/env.ts +1 -11
  269. package/src/config/feature-flag-registry.json +16 -0
  270. package/src/config/schema.ts +4 -0
  271. package/src/config/schemas/heartbeat.ts +6 -1
  272. package/src/config/schemas/inference.ts +14 -3
  273. package/src/config/schemas/memory-processing.ts +16 -8
  274. package/src/config/schemas/memory-retrieval.ts +3 -3
  275. package/src/config/skills.ts +1 -1
  276. package/src/context/window-manager.ts +174 -51
  277. package/src/credential-execution/executable-discovery.ts +2 -2
  278. package/src/daemon/approved-devices-store.ts +2 -2
  279. package/src/daemon/assistant-attachments.ts +2 -0
  280. package/src/daemon/config-watcher.ts +4 -50
  281. package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
  282. package/src/daemon/conversation-agent-loop.ts +12 -0
  283. package/src/daemon/conversation-error.ts +3 -5
  284. package/src/daemon/conversation-history.ts +7 -3
  285. package/src/daemon/conversation-lifecycle.ts +16 -0
  286. package/src/daemon/conversation-messaging.ts +1 -0
  287. package/src/daemon/conversation-notifiers.ts +67 -30
  288. package/src/daemon/conversation-process.ts +161 -2
  289. package/src/daemon/conversation-queue-manager.ts +2 -0
  290. package/src/daemon/conversation-runtime-assembly.ts +33 -11
  291. package/src/daemon/conversation-slash.ts +14 -3
  292. package/src/daemon/conversation-tool-setup.ts +2 -0
  293. package/src/daemon/conversation-usage.ts +32 -4
  294. package/src/daemon/conversation.ts +33 -1
  295. package/src/daemon/daemon-control.ts +32 -16
  296. package/src/daemon/date-context.ts +47 -45
  297. package/src/daemon/dictation-profile-store.ts +2 -2
  298. package/src/daemon/handlers/conversations.ts +19 -0
  299. package/src/daemon/handlers/shared.ts +14 -21
  300. package/src/daemon/lifecycle.ts +5 -7
  301. package/src/daemon/message-types/conversations.ts +2 -0
  302. package/src/daemon/message-types/guardian-actions.ts +3 -17
  303. package/src/daemon/message-types/integrations.ts +11 -1
  304. package/src/daemon/message-types/messages.ts +1 -0
  305. package/src/daemon/pairing-store.ts +2 -79
  306. package/src/daemon/server.ts +154 -8
  307. package/src/daemon/watch-handler.ts +65 -21
  308. package/src/email/guardrails.ts +3 -3
  309. package/src/heartbeat/heartbeat-service.ts +14 -7
  310. package/src/hooks/cli.ts +2 -2
  311. package/src/hooks/config.ts +2 -2
  312. package/src/hooks/discovery.ts +2 -2
  313. package/src/hooks/manager.ts +2 -2
  314. package/src/hooks/runner.ts +5 -2
  315. package/src/hooks/templates.ts +2 -2
  316. package/src/memory/admin.ts +181 -2
  317. package/src/memory/app-git-service.ts +61 -4
  318. package/src/memory/attachments-store.ts +2 -0
  319. package/src/memory/canonical-guardian-store.ts +16 -0
  320. package/src/memory/db-init.ts +8 -0
  321. package/src/memory/embedding-local.ts +5 -2
  322. package/src/memory/indexer.ts +44 -26
  323. package/src/memory/items-extractor.ts +34 -82
  324. package/src/memory/job-handlers/batch-extraction.ts +741 -0
  325. package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
  326. package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
  327. package/src/memory/jobs-store.ts +28 -0
  328. package/src/memory/jobs-worker.ts +56 -9
  329. package/src/memory/lifecycle-events-store.ts +4 -2
  330. package/src/memory/llm-request-log-store.ts +40 -2
  331. package/src/memory/llm-usage-store.ts +4 -3
  332. package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
  333. package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
  334. package/src/memory/migrations/index.ts +2 -0
  335. package/src/memory/query-expansion.ts +83 -0
  336. package/src/memory/retriever.test.ts +119 -0
  337. package/src/memory/retriever.ts +513 -105
  338. package/src/memory/schema/guardian.ts +4 -0
  339. package/src/memory/schema/infrastructure.ts +1 -0
  340. package/src/memory/search/formatting.test.ts +140 -0
  341. package/src/memory/search/formatting.ts +143 -198
  342. package/src/memory/search/mmr.ts +136 -0
  343. package/src/memory/search/staleness.ts +0 -15
  344. package/src/memory/search/tier-classifier.ts +10 -21
  345. package/src/memory/search/types.ts +17 -0
  346. package/src/messaging/providers/slack/adapter.ts +51 -5
  347. package/src/notifications/broadcaster.ts +13 -0
  348. package/src/notifications/copy-composer.ts +8 -0
  349. package/src/oauth/connect-orchestrator.ts +1 -1
  350. package/src/oauth/connection-resolver.ts +2 -2
  351. package/src/oauth/provider-serializer.ts +116 -0
  352. package/src/permissions/trust-store.ts +24 -7
  353. package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
  354. package/src/prompts/journal-context.ts +50 -35
  355. package/src/prompts/persona-resolver.ts +1 -1
  356. package/src/prompts/system-prompt.ts +27 -28
  357. package/src/prompts/templates/BOOTSTRAP.md +14 -1
  358. package/src/prompts/templates/HEARTBEAT.md +10 -0
  359. package/src/prompts/templates/NOW.md +19 -25
  360. package/src/prompts/templates/SOUL.md +13 -1
  361. package/src/prompts/templates/UPDATES.md +12 -0
  362. package/src/prompts/update-bulletin.ts +1 -1
  363. package/src/providers/anthropic/client.ts +89 -18
  364. package/src/providers/model-catalog.ts +22 -2
  365. package/src/providers/model-intents.ts +2 -2
  366. package/src/providers/openai/client.ts +40 -1
  367. package/src/providers/retry.ts +23 -4
  368. package/src/providers/types.ts +2 -0
  369. package/src/runtime/assistant-scope.ts +1 -1
  370. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
  371. package/src/runtime/auth/route-policy.ts +1 -0
  372. package/src/runtime/auth/token-service.ts +51 -29
  373. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
  374. package/src/runtime/guardian-decision-types.ts +16 -10
  375. package/src/runtime/http-server.ts +3 -14
  376. package/src/runtime/http-types.ts +1 -0
  377. package/src/runtime/migrations/vbundle-builder.ts +7 -4
  378. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
  379. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  380. package/src/runtime/routes/conversation-query-routes.ts +40 -8
  381. package/src/runtime/routes/conversation-routes.ts +125 -3
  382. package/src/runtime/routes/guardian-action-routes.ts +9 -3
  383. package/src/runtime/routes/identity-routes.ts +25 -4
  384. package/src/runtime/routes/llm-context-normalization.ts +1 -0
  385. package/src/runtime/routes/log-export-routes.ts +34 -12
  386. package/src/runtime/routes/migration-routes.ts +6 -10
  387. package/src/runtime/routes/oauth-apps.ts +2 -9
  388. package/src/runtime/routes/oauth-providers.ts +60 -0
  389. package/src/runtime/routes/pairing-routes.ts +0 -8
  390. package/src/runtime/routes/settings-routes.ts +0 -1
  391. package/src/runtime/routes/telemetry-routes.ts +16 -4
  392. package/src/security/encrypted-store.ts +2 -2
  393. package/src/security/secret-allowlist.ts +3 -3
  394. package/src/signals/emit-event.ts +42 -0
  395. package/src/signals/user-message.ts +37 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
  397. package/src/telemetry/usage-telemetry-reporter.ts +23 -17
  398. package/src/tools/browser/runtime-check.ts +2 -2
  399. package/src/tools/credentials/vault.ts +2 -249
  400. package/src/tools/memory/definitions.ts +1 -1
  401. package/src/tools/memory/handlers.test.ts +50 -8
  402. package/src/tools/memory/handlers.ts +3 -1
  403. package/src/tools/side-effects.ts +1 -6
  404. package/src/tools/terminal/safe-env.ts +3 -2
  405. package/src/tools/terminal/shell.ts +11 -14
  406. package/src/tools/tool-approval-handler.ts +20 -1
  407. package/src/tools/tool-input-summary.ts +66 -0
  408. package/src/tools/types.ts +4 -0
  409. package/src/usage/types.ts +4 -0
  410. package/src/util/device-id.ts +10 -10
  411. package/src/util/platform.ts +71 -33
  412. package/src/util/pricing.ts +19 -6
  413. package/src/util/strip-comment-lines.ts +28 -0
  414. package/src/workspace/git-service.ts +8 -18
  415. package/src/workspace/migrations/003-seed-device-id.ts +6 -4
  416. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
  417. package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
  418. package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
  419. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
  420. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
  421. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
  422. package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
  423. package/src/workspace/migrations/registry.ts +8 -0
  424. package/src/signals/confirm.ts +0 -82
  425. package/src/signals/trust-rule.ts +0 -174
@@ -2,21 +2,23 @@ import { execSync, spawn } from "node:child_process";
2
2
  import {
3
3
  closeSync,
4
4
  existsSync,
5
- mkdirSync,
6
5
  openSync,
7
6
  readFileSync,
8
7
  unlinkSync,
9
8
  writeFileSync,
10
9
  } from "node:fs";
11
- import { join, resolve } from "node:path";
10
+ import { resolve } from "node:path";
12
11
 
13
12
  import { getRuntimeHttpHost, getRuntimeHttpPort } from "../config/env.js";
14
13
  import { getIsContainerized } from "../config/env-registry.js";
14
+ import { loadOrCreateSigningKey } from "../runtime/auth/token-service.js";
15
15
  import { DaemonError } from "../util/errors.js";
16
16
  import { getLogger } from "../util/logger.js";
17
17
  import {
18
+ ensureDataDir,
19
+ getDaemonStartupLockPath,
20
+ getDaemonStderrLogPath,
18
21
  getPidPath,
19
- getRootDir,
20
22
  getWorkspaceConfigPath,
21
23
  } from "../util/platform.js";
22
24
 
@@ -66,7 +68,7 @@ function readDaemonTimeouts(): typeof DAEMON_TIMEOUT_DEFAULTS {
66
68
  /**
67
69
  * Kill the stale daemon recorded in this workspace's PID file, if any.
68
70
  * Only targets the exact PID from our PID file — never scans globally —
69
- * so isolated daemons (e.g., dev instances with a different BASE_DATA_DIR)
71
+ * so isolated daemons (e.g., dev instances with a different VELLUM_WORKSPACE_DIR)
70
72
  * are never affected.
71
73
  */
72
74
  function killStaleDaemon(): void {
@@ -240,7 +242,7 @@ export async function getDaemonStatus(): Promise<{
240
242
  }
241
243
 
242
244
  function getStartupLockPath(): string {
243
- return join(getRootDir(), "daemon-startup.lock");
245
+ return getDaemonStartupLockPath();
244
246
  }
245
247
 
246
248
  /** Attempt to acquire a startup lock. Returns true on success. Stale locks
@@ -253,7 +255,7 @@ function acquireStartupLock(): boolean {
253
255
  // On a first-time run, getRootDir() may not exist yet, and writeFileSync
254
256
  // with 'wx' would throw ENOENT — which the catch block misinterprets as
255
257
  // "lock already held."
256
- mkdirSync(getRootDir(), { recursive: true });
258
+ ensureDataDir();
257
259
  // O_CREAT | O_EXCL — fails atomically if the file already exists.
258
260
  writeFileSync(lockPath, String(Date.now()), { flag: "wx" });
259
261
  return true;
@@ -343,29 +345,43 @@ async function startDaemonLocked(): Promise<{
343
345
  // a crash where the process is alive but non-responsive).
344
346
  killStaleDaemon();
345
347
 
346
- // Only create the root dir for PID files the daemon process itself
347
- // handles migration + full ensureDataDir() in runDaemon(). Calling
348
- // ensureDataDir() here would pre-create workspace destination dirs
349
- // and cause migration moves to no-op.
350
- const rootDir = getRootDir();
351
- if (!existsSync(rootDir)) {
352
- mkdirSync(rootDir, { recursive: true });
353
- }
348
+ // Ensure root + workspace dirs exist before spawning. The daemon itself
349
+ // handles full ensureDataDir() during runDaemon(), but we need at least
350
+ // the root dir for the PID file and stderr log.
351
+ ensureDataDir();
354
352
 
355
353
  // Spawn the daemon as a detached child process
356
354
  const mainPath = resolve(import.meta.dirname ?? __dirname, "main.ts");
357
355
 
356
+ // Pre-load the signing key so the daemon receives it via env var and
357
+ // never needs to access the protected directory for key material.
358
+ // Done before opening stderrFd to avoid leaking the file descriptor if
359
+ // loadOrCreateSigningKey throws.
360
+ const spawnEnv = { ...process.env };
361
+ if (!spawnEnv.ACTOR_TOKEN_SIGNING_KEY) {
362
+ try {
363
+ const key = loadOrCreateSigningKey();
364
+ spawnEnv.ACTOR_TOKEN_SIGNING_KEY = key.toString("hex");
365
+ } catch (err) {
366
+ throw new DaemonError(
367
+ `Failed to pre-load signing key for daemon: ${
368
+ err instanceof Error ? err.message : String(err)
369
+ }`,
370
+ );
371
+ }
372
+ }
373
+
358
374
  // Redirect the child's stderr to a file instead of piping it back to the
359
375
  // parent. A pipe's read end is destroyed when the parent exits, leaving
360
376
  // fd 2 broken in the child. Bun (unlike Node.js) does not ignore SIGPIPE,
361
377
  // so any later stderr write would silently kill the daemon.
362
- const stderrPath = join(rootDir, "daemon-stderr.log");
378
+ const stderrPath = getDaemonStderrLogPath();
363
379
  const stderrFd = openSync(stderrPath, "w");
364
380
 
365
381
  const child = spawn("bun", ["run", mainPath], {
366
382
  detached: true,
367
383
  stdio: ["ignore", "ignore", stderrFd],
368
- env: { ...process.env },
384
+ env: spawnEnv,
369
385
  });
370
386
 
371
387
  // The child inherited the fd; close the parent's copy.
@@ -18,14 +18,14 @@ export interface TemporalContextOptions {
18
18
  userTimeZone?: string | null;
19
19
  }
20
20
 
21
- const WEEKDAY_NAMES = [
22
- "Sunday",
23
- "Monday",
24
- "Tuesday",
25
- "Wednesday",
26
- "Thursday",
27
- "Friday",
28
- "Saturday",
21
+ const WEEKDAY_SHORT = [
22
+ "Sun",
23
+ "Mon",
24
+ "Tue",
25
+ "Wed",
26
+ "Thu",
27
+ "Fri",
28
+ "Sat",
29
29
  ] as const;
30
30
  const UTC_GMT_OFFSET_TOKEN_RE = /^(?:UTC|GMT)([+-])(\d{1,2})(?::?(\d{2}))?$/i;
31
31
 
@@ -183,35 +183,41 @@ const TIMEZONE_TOKEN_RE = new RegExp(
183
183
  );
184
184
 
185
185
  /**
186
- * Extract the user's timezone from V2 memory recall injected text.
186
+ * Extract the user's timezone from memory recall injected text.
187
187
  *
188
- * Scans the `<user_identity>` section (if present) for lines containing
189
- * "timezone" and tries to resolve an IANA identifier. Falls back to
190
- * scanning the full text body.
188
+ * Prefers identity items (`<item kind="identity" ...>`) rendered inside the
189
+ * `<recalled>` section of `<memory_context>`. Falls back to scanning the
190
+ * full injected text for lines mentioning "timezone".
191
191
  */
192
192
  export function extractUserTimeZoneFromRecall(
193
193
  injectedText: string,
194
194
  ): string | null {
195
195
  if (!injectedText || injectedText.trim().length === 0) return null;
196
196
 
197
- // Prefer lines inside <user_identity> that mention "timezone"
198
- const identityMatch = injectedText.match(
199
- /<user_identity>([\s\S]*?)<\/user_identity>/,
200
- );
201
- if (identityMatch) {
202
- const identityBlock = identityMatch[1];
203
- for (const line of identityBlock.split("\n")) {
204
- if (/time\s*zone/i.test(line)) {
205
- for (const token of extractTimeZoneCandidates(line)) {
197
+ // Prefer identity items: <item ... kind="identity" ...>content</item>
198
+ const identityItemRe = /<item\s[^>]*kind="identity"[^>]*>([\s\S]*?)<\/item>/g;
199
+ let match: RegExpExecArray | null;
200
+ const identityTexts: string[] = [];
201
+ while ((match = identityItemRe.exec(injectedText)) !== null) {
202
+ identityTexts.push(match[1]);
203
+ }
204
+
205
+ if (identityTexts.length > 0) {
206
+ // First pass: identity items whose text mentions "timezone"
207
+ for (const text of identityTexts) {
208
+ if (/time\s*zone/i.test(text)) {
209
+ for (const token of extractTimeZoneCandidates(text)) {
206
210
  const canonical = canonicalizeTimeZone(token);
207
211
  if (canonical) return canonical;
208
212
  }
209
213
  }
210
214
  }
211
- // Scan full identity block for any timezone token
212
- for (const token of extractTimeZoneCandidates(identityBlock)) {
213
- const canonical = canonicalizeTimeZone(token);
214
- if (canonical) return canonical;
215
+ // Second pass: any timezone token in any identity item
216
+ for (const text of identityTexts) {
217
+ for (const token of extractTimeZoneCandidates(text)) {
218
+ const canonical = canonicalizeTimeZone(token);
219
+ if (canonical) return canonical;
220
+ }
215
221
  }
216
222
  }
217
223
 
@@ -284,30 +290,25 @@ function formatLocalDate(date: Date, timeZone: string): string {
284
290
  }
285
291
 
286
292
  /**
287
- * Format a Date as local ISO 8601 with timezone offset in the given timezone.
293
+ * Format HH:MM and UTC offset for the given instant in the given timezone.
288
294
  */
289
- function formatLocalIsoWithOffset(date: Date, timeZone: string): string {
295
+ function formatCompactTimeAndOffset(
296
+ date: Date,
297
+ timeZone: string,
298
+ ): { time: string; offset: string } {
290
299
  const fmt = new Intl.DateTimeFormat("en-US", {
291
300
  timeZone,
292
- year: "numeric",
293
- month: "2-digit",
294
- day: "2-digit",
295
301
  hour: "2-digit",
296
302
  minute: "2-digit",
297
- second: "2-digit",
298
303
  hourCycle: "h23",
299
304
  timeZoneName: "shortOffset",
300
305
  });
301
306
  const parts = fmt.formatToParts(date);
302
307
  const get = (t: string) => parts.find((p) => p.type === t)?.value ?? "";
303
- const offset = normalizeOffsetToken(get("timeZoneName"));
304
- const year = get("year");
305
- const month = get("month");
306
- const day = get("day");
307
308
  const hour = get("hour");
308
309
  const minute = get("minute");
309
- const second = get("second");
310
- return `${year}-${month}-${day}T${hour}:${minute}:${second}${offset}`;
310
+ const offset = normalizeOffsetToken(get("timeZoneName"));
311
+ return { time: `${hour}:${minute}`, offset };
311
312
  }
312
313
 
313
314
  /**
@@ -345,22 +346,23 @@ export function buildTemporalContext(
345
346
  : "assistant_host_fallback";
346
347
  const todayParts = localDateParts(now, timeZone);
347
348
  const todayStr = formatLocalDate(now, timeZone);
348
- const todayWeekday = WEEKDAY_NAMES[todayParts.weekday];
349
+ const todayWeekday = WEEKDAY_SHORT[todayParts.weekday];
350
+ const { time, offset } = formatCompactTimeAndOffset(now, timeZone);
351
+
352
+ const tzSuffix =
353
+ timeZoneSource === "assistant_host_fallback" ? " (host fallback)" : "";
349
354
 
350
355
  const lines = [
351
356
  `<temporal_context>`,
352
- `Today: ${todayStr} (${todayWeekday})`,
353
- `Timezone: ${timeZone}`,
354
- `Current local time: ${formatLocalIsoWithOffset(now, timeZone)}`,
355
- `Current UTC time: ${now.toISOString()}`,
356
- `Timezone source: ${timeZoneSource}`,
357
+ `Today: ${todayStr} (${todayWeekday}) ${time} ${offset}`,
358
+ `TZ: ${timeZone}${tzSuffix}`,
357
359
  ];
358
360
 
359
361
  if (userTimeZone && userTimeZone !== timeZone) {
360
- lines.push(`User timezone: ${userTimeZone}`);
362
+ lines.push(`User TZ: ${userTimeZone}`);
361
363
  }
362
364
  if (resolvedHostTimeZone !== timeZone) {
363
- lines.push(`Assistant host timezone: ${resolvedHostTimeZone}`);
365
+ lines.push(`Host TZ: ${resolvedHostTimeZone}`);
364
366
  }
365
367
 
366
368
  lines.push(`</temporal_context>`);
@@ -16,7 +16,7 @@ import {
16
16
  import { dirname, join } from "node:path";
17
17
 
18
18
  import { getLogger } from "../util/logger.js";
19
- import { getRootDir } from "../util/platform.js";
19
+ import { getWorkspaceDir } from "../util/platform.js";
20
20
 
21
21
  const log = getLogger("dictation-profile-store");
22
22
 
@@ -97,7 +97,7 @@ let storePathOverride: string | null = null;
97
97
 
98
98
  function getStorePath(): string {
99
99
  if (storePathOverride) return storePathOverride;
100
- return join(getRootDir(), "dictation-profiles.json");
100
+ return join(getWorkspaceDir(), "dictation-profiles.json");
101
101
  }
102
102
 
103
103
  let cachedConfig: DictationProfilesConfig | null = null;
@@ -24,7 +24,9 @@ import {
24
24
  UNTITLED_FALLBACK,
25
25
  } from "../../memory/conversation-title-service.js";
26
26
  import * as pendingInteractions from "../../runtime/pending-interactions.js";
27
+ import { redactSecrets } from "../../security/secret-scanner.js";
27
28
  import { getSubagentManager } from "../../subagent/index.js";
29
+ import { summarizeToolInput } from "../../tools/tool-input-summary.js";
28
30
  import { truncate } from "../../util/truncate.js";
29
31
  import type { Conversation } from "../conversation.js";
30
32
  import { HostBashProxy } from "../host-bash-proxy.js";
@@ -84,6 +86,14 @@ export function makeEventSender(params: {
84
86
 
85
87
  try {
86
88
  const trustContext = conversation.trustContext;
89
+ const inputRecord = event.input as Record<string, unknown>;
90
+ const activityRaw =
91
+ (typeof inputRecord.activity === "string"
92
+ ? inputRecord.activity
93
+ : undefined) ??
94
+ (typeof inputRecord.reason === "string"
95
+ ? inputRecord.reason
96
+ : undefined);
87
97
  createCanonicalGuardianRequest({
88
98
  id: event.requestId,
89
99
  kind: "tool_approval",
@@ -92,6 +102,15 @@ export function makeEventSender(params: {
92
102
  conversationId,
93
103
  guardianPrincipalId: trustContext?.guardianPrincipalId ?? undefined,
94
104
  toolName: event.toolName,
105
+ commandPreview:
106
+ redactSecrets(
107
+ summarizeToolInput(event.toolName, inputRecord),
108
+ ) || undefined,
109
+ riskLevel: event.riskLevel,
110
+ activityText: activityRaw
111
+ ? redactSecrets(activityRaw)
112
+ : undefined,
113
+ executionTarget: event.executionTarget,
95
114
  status: "pending",
96
115
  requestCode: generateCanonicalRequestCode(),
97
116
  expiresAt: Date.now() + 5 * 60 * 1000,
@@ -1,6 +1,7 @@
1
1
  import { v4 as uuid } from "uuid";
2
2
 
3
3
  import { getConfig } from "../../config/loader.js";
4
+ import type { Speed } from "../../config/schemas/inference.js";
4
5
  import type { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
5
6
  import type { SecretPromptResult } from "../../permissions/secret-prompter.js";
6
7
  import type { AuthContext } from "../../runtime/auth/types.js";
@@ -91,27 +92,8 @@ export interface RenderedHistoryContent {
91
92
  contentOrder: string[];
92
93
  /** UI surfaces (widgets) embedded in the message. */
93
94
  surfaces: HistorySurface[];
94
- }
95
-
96
- export interface SubagentNotificationData {
97
- subagentId: string;
98
- label: string;
99
- status: "completed" | "failed" | "aborted";
100
- error?: string;
101
- conversationId?: string;
102
- }
103
-
104
- export interface ParsedHistoryMessage {
105
- id?: string;
106
- role: string;
107
- text: string;
108
- timestamp: number;
109
- toolCalls: HistoryToolCall[];
110
- toolCallsBeforeText: boolean;
111
- textSegments: string[];
112
- contentOrder: string[];
113
- surfaces: HistorySurface[];
114
- subagentNotification?: SubagentNotificationData;
95
+ /** Thinking segments extracted from thinking blocks. */
96
+ thinkingSegments: string[];
115
97
  }
116
98
 
117
99
  /**
@@ -120,6 +102,7 @@ export interface ParsedHistoryMessage {
120
102
  export interface ConversationCreateOptions {
121
103
  systemPromptOverride?: string;
122
104
  maxResponseTokens?: number;
105
+ speed?: Speed;
123
106
  transport?: ConversationTransportMetadata;
124
107
  assistantId?: string;
125
108
  trustContext?: TrustContext;
@@ -220,6 +203,7 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
220
203
  textSegments: text ? [text] : [],
221
204
  contentOrder: text ? ["text:0"] : [],
222
205
  surfaces: [],
206
+ thinkingSegments: [],
223
207
  };
224
208
  }
225
209
 
@@ -227,6 +211,7 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
227
211
  const attachmentParts: string[] = [];
228
212
  const toolCalls: HistoryToolCall[] = [];
229
213
  const surfaces: HistorySurface[] = [];
214
+ const thinkingSegments: string[] = [];
230
215
  const pendingToolUses = new Map<string, HistoryToolCall>();
231
216
  let seenText = false;
232
217
  let seenToolUse = false;
@@ -300,6 +285,13 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
300
285
  continue;
301
286
  }
302
287
 
288
+ if (block.type === "thinking" && typeof block.thinking === "string") {
289
+ finalizeSegment();
290
+ thinkingSegments.push(block.thinking);
291
+ contentOrder.push(`thinking:${thinkingSegments.length - 1}`);
292
+ continue;
293
+ }
294
+
303
295
  if (block.type === "text" && typeof block.text === "string") {
304
296
  textParts.push(block.text);
305
297
  ensureSegment();
@@ -410,6 +402,7 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
410
402
  textSegments,
411
403
  contentOrder,
412
404
  surfaces,
405
+ thinkingSegments,
413
406
  };
414
407
  }
415
408
 
@@ -1,5 +1,3 @@
1
- import { join } from "node:path";
2
-
3
1
  import { config as dotenvConfig } from "dotenv";
4
2
 
5
3
  import { setPointerMessageProcessor } from "../calls/call-pointer-messages.js";
@@ -89,8 +87,8 @@ import { getDeviceId } from "../util/device-id.js";
89
87
  import { getLogger, initLogger } from "../util/logger.js";
90
88
  import {
91
89
  ensureDataDir,
90
+ getDotEnvPath,
92
91
  getInterfacesDir,
93
- getRootDir,
94
92
  getWorkspaceDir,
95
93
  } from "../util/platform.js";
96
94
  import {
@@ -149,7 +147,7 @@ export {
149
147
  const log = getLogger("lifecycle");
150
148
 
151
149
  function loadDotEnv(): void {
152
- dotenvConfig({ path: join(getRootDir(), ".env"), quiet: true });
150
+ dotenvConfig({ path: getDotEnvPath(), quiet: true });
153
151
  }
154
152
 
155
153
  export interface CesStartupResult {
@@ -266,8 +264,7 @@ export async function runDaemon(): Promise<void> {
266
264
  ensureDataDir();
267
265
 
268
266
  // Load (or generate + persist) the auth signing key so tokens survive
269
- // daemon restarts. Must happen after ensureDataDir() creates the
270
- // protected directory.
267
+ // daemon restarts.
271
268
  const signingKey = resolveSigningKey();
272
269
  initAuthSigningKey(signingKey);
273
270
 
@@ -1155,12 +1152,13 @@ export async function runDaemon(): Promise<void> {
1155
1152
 
1156
1153
  const heartbeatConfig = config.heartbeat;
1157
1154
  const heartbeat = new HeartbeatService({
1158
- processMessage: (conversationId, content) =>
1155
+ processMessage: (conversationId, content, options) =>
1159
1156
  server.processMessage(conversationId, content, undefined, {
1160
1157
  trustContext: {
1161
1158
  sourceChannel: "vellum",
1162
1159
  trustClass: "guardian",
1163
1160
  },
1161
+ ...options,
1164
1162
  }),
1165
1163
  alerter: (alert) => server.broadcast(alert),
1166
1164
  onConversationCreated: (info) =>
@@ -341,6 +341,8 @@ export interface UsageUpdate {
341
341
  totalOutputTokens: number;
342
342
  estimatedCost: number;
343
343
  model: string;
344
+ contextWindowTokens?: number;
345
+ contextWindowMaxTokens?: number;
344
346
  }
345
347
 
346
348
  export interface UsageResponse {
@@ -2,6 +2,8 @@
2
2
  // Enables desktop clients to fetch pending guardian prompts and submit
3
3
  // button decisions deterministically (without text parsing).
4
4
 
5
+ import type { GuardianDecisionPrompt } from "../../runtime/guardian-decision-types.js";
6
+
5
7
  // === Client -> Server ===
6
8
 
7
9
  export interface GuardianActionsPendingRequest {
@@ -21,23 +23,7 @@ export interface GuardianActionDecision {
21
23
  export interface GuardianActionsPendingResponse {
22
24
  type: "guardian_actions_pending_response";
23
25
  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
- * Canonical request kind (e.g. 'tool_approval', 'pending_question').
36
- * Present when the prompt originates from the canonical guardian request
37
- * store. Absent for legacy-only prompts.
38
- */
39
- kind?: string;
40
- }>;
26
+ prompts: GuardianDecisionPrompt[];
41
27
  }
42
28
 
43
29
  export interface GuardianActionDecisionResponse {
@@ -211,6 +211,14 @@ export interface NavigateSettings {
211
211
  tab: string;
212
212
  }
213
213
 
214
+ export interface ShowPlatformLogin {
215
+ type: "show_platform_login";
216
+ }
217
+
218
+ export interface PlatformDisconnected {
219
+ type: "platform_disconnected";
220
+ }
221
+
214
222
  // --- Domain-level union aliases (consumed by the barrel file) ---
215
223
 
216
224
  export type _IntegrationsClientMessages =
@@ -237,4 +245,6 @@ export type _IntegrationsServerMessages =
237
245
  | IntegrationConnectResult
238
246
  | OAuthConnectResultResponse
239
247
  | OpenUrl
240
- | NavigateSettings;
248
+ | NavigateSettings
249
+ | ShowPlatformLogin
250
+ | PlatformDisconnected;
@@ -71,6 +71,7 @@ export interface AssistantTextDelta {
71
71
  export interface AssistantThinkingDelta {
72
72
  type: "assistant_thinking_delta";
73
73
  thinking: string;
74
+ conversationId?: string;
74
75
  }
75
76
 
76
77
  export interface ToolUseStart {
@@ -1,28 +1,14 @@
1
1
  /**
2
- * Pairing request store with TTL and disk persistence.
2
+ * In-memory pairing request store with TTL.
3
3
  *
4
4
  * Each pairing request lives for at most TTL_MS (5 minutes) before
5
5
  * being swept as expired. Status transitions:
6
6
  * registered → pending → approved | denied | expired
7
- *
8
- * Entries are persisted to ~/.vellum/protected/pairing-requests.json
9
- * using the same atomic-write pattern as approved-devices-store.ts
10
- * so that device bindings survive daemon restarts.
11
7
  */
12
8
 
13
9
  import { createHash, timingSafeEqual } from "node:crypto";
14
- import {
15
- chmodSync,
16
- existsSync,
17
- mkdirSync,
18
- readFileSync,
19
- renameSync,
20
- writeFileSync,
21
- } from "node:fs";
22
- import { dirname, join } from "node:path";
23
10
 
24
11
  import { getLogger } from "../util/logger.js";
25
- import { getRootDir } from "../util/platform.js";
26
12
 
27
13
  const log = getLogger("pairing-store");
28
14
 
@@ -59,60 +45,11 @@ function timingSafeCompare(a: string, b: string): boolean {
59
45
  return timingSafeEqual(bufA, bufB);
60
46
  }
61
47
 
62
- interface PairingStoreFile {
63
- version: 1;
64
- requests: PairingRequest[];
65
- }
66
-
67
- function getStorePath(): string {
68
- return join(getRootDir(), "protected", "pairing-requests.json");
69
- }
70
-
71
- function loadFromDisk(): Map<string, PairingRequest> {
72
- const path = getStorePath();
73
- if (!existsSync(path)) {
74
- return new Map();
75
- }
76
- try {
77
- const raw = readFileSync(path, "utf-8");
78
- const data = JSON.parse(raw) as PairingStoreFile;
79
- if (data.version !== 1 || !Array.isArray(data.requests)) {
80
- log.warn("Invalid pairing-requests.json format, starting fresh");
81
- return new Map();
82
- }
83
- const map = new Map<string, PairingRequest>();
84
- for (const entry of data.requests) {
85
- map.set(entry.pairingRequestId, entry);
86
- }
87
- return map;
88
- } catch (err) {
89
- log.error({ err }, "Failed to load pairing-requests.json");
90
- return new Map();
91
- }
92
- }
93
-
94
- function saveToDisk(requests: Map<string, PairingRequest>): void {
95
- const path = getStorePath();
96
- const dir = dirname(path);
97
- if (!existsSync(dir)) {
98
- mkdirSync(dir, { recursive: true });
99
- }
100
- const data: PairingStoreFile = {
101
- version: 1,
102
- requests: Array.from(requests.values()),
103
- };
104
- const tmpPath = path + ".tmp." + process.pid;
105
- writeFileSync(tmpPath, JSON.stringify(data, null, 2), { mode: 0o600 });
106
- renameSync(tmpPath, path);
107
- chmodSync(path, 0o600);
108
- }
109
-
110
48
  export class PairingStore {
111
49
  private requests = new Map<string, PairingRequest>();
112
50
  private sweepTimer: ReturnType<typeof setInterval> | null = null;
113
51
 
114
52
  start(): void {
115
- this.requests = loadFromDisk();
116
53
  this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
117
54
  }
118
55
 
@@ -168,8 +105,6 @@ export class PairingStore {
168
105
  localLanUrl: params.localLanUrl ?? null,
169
106
  createdAt: Date.now(),
170
107
  });
171
- this.persist();
172
-
173
108
  log.info(
174
109
  { pairingRequestId: params.pairingRequestId },
175
110
  "Pairing request registered",
@@ -226,8 +161,6 @@ export class PairingStore {
226
161
  if (entry.status === "registered") {
227
162
  entry.status = "pending";
228
163
  }
229
- this.persist();
230
-
231
164
  return { ok: true, entry };
232
165
  }
233
166
 
@@ -242,7 +175,6 @@ export class PairingStore {
242
175
  if (!entry) return null;
243
176
  entry.status = "approved";
244
177
  entry.bearerToken = bearerToken;
245
- this.persist();
246
178
  return entry;
247
179
  }
248
180
 
@@ -253,7 +185,6 @@ export class PairingStore {
253
185
  const entry = this.requests.get(pairingRequestId);
254
186
  if (!entry) return null;
255
187
  entry.status = "denied";
256
- this.persist();
257
188
  return entry;
258
189
  }
259
190
 
@@ -274,14 +205,6 @@ export class PairingStore {
274
205
  return timingSafeCompare(entry.hashedPairingSecret, hashedSecret);
275
206
  }
276
207
 
277
- private persist(): void {
278
- try {
279
- saveToDisk(this.requests);
280
- } catch (err) {
281
- log.error({ err }, "Failed to persist pairing requests to disk");
282
- }
283
- }
284
-
285
208
  private sweep(): void {
286
209
  const now = Date.now();
287
210
  let changed = false;
@@ -300,7 +223,7 @@ export class PairingStore {
300
223
  }
301
224
  }
302
225
  if (changed) {
303
- this.persist();
226
+ log.debug("Sweep completed with changes");
304
227
  }
305
228
  }
306
229
  }