@vellumai/assistant 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/Dockerfile +2 -0
  2. package/README.md +88 -2
  3. package/eslint.config.mjs +31 -0
  4. package/package.json +1 -1
  5. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  6. package/scripts/ipc/generate-swift.ts +31 -2
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
  8. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  9. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
  10. package/src/__tests__/approval-message-composer.test.ts +253 -0
  11. package/src/__tests__/browser-manager.test.ts +1 -0
  12. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  13. package/src/__tests__/call-domain.test.ts +12 -2
  14. package/src/__tests__/call-orchestrator.test.ts +799 -249
  15. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  16. package/src/__tests__/call-recovery.test.ts +3 -0
  17. package/src/__tests__/call-routes-http.test.ts +32 -2
  18. package/src/__tests__/call-store.test.ts +3 -0
  19. package/src/__tests__/channel-approval-routes.test.ts +1277 -98
  20. package/src/__tests__/channel-approval.test.ts +37 -0
  21. package/src/__tests__/channel-approvals.test.ts +36 -50
  22. package/src/__tests__/channel-guardian.test.ts +630 -22
  23. package/src/__tests__/channel-readiness-service.test.ts +324 -0
  24. package/src/__tests__/checker.test.ts +14 -7
  25. package/src/__tests__/clarification-resolver.test.ts +44 -24
  26. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  27. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  28. package/src/__tests__/config-schema.test.ts +14 -8
  29. package/src/__tests__/context-window-manager.test.ts +30 -2
  30. package/src/__tests__/contradiction-checker.test.ts +20 -5
  31. package/src/__tests__/credential-security-invariants.test.ts +7 -2
  32. package/src/__tests__/daemon-lifecycle.test.ts +13 -12
  33. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  34. package/src/__tests__/dictation-mode-detection.test.ts +63 -0
  35. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  36. package/src/__tests__/entity-search.test.ts +615 -0
  37. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  38. package/src/__tests__/guardian-action-store.test.ts +123 -0
  39. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  40. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  41. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  42. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  43. package/src/__tests__/handlers-twilio-config.test.ts +533 -0
  44. package/src/__tests__/intent-routing.test.ts +2 -0
  45. package/src/__tests__/ipc-snapshot.test.ts +291 -1
  46. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  47. package/src/__tests__/messaging-send-tool.test.ts +65 -0
  48. package/src/__tests__/model-intents.test.ts +96 -0
  49. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  50. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  51. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  52. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  53. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  54. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  55. package/src/__tests__/qdrant-manager.test.ts +27 -20
  56. package/src/__tests__/relay-server.test.ts +779 -40
  57. package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
  58. package/src/__tests__/run-orchestrator.test.ts +42 -4
  59. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  60. package/src/__tests__/runtime-runs.test.ts +16 -0
  61. package/src/__tests__/schedule-store.test.ts +18 -4
  62. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  63. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  64. package/src/__tests__/session-agent-loop.test.ts +857 -0
  65. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  66. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  67. package/src/__tests__/session-profile-injection.test.ts +6 -0
  68. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  69. package/src/__tests__/session-queue.test.ts +6 -0
  70. package/src/__tests__/session-runtime-assembly.test.ts +321 -13
  71. package/src/__tests__/session-slash-known.test.ts +6 -0
  72. package/src/__tests__/session-slash-queue.test.ts +6 -0
  73. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  74. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  75. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  76. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  77. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  78. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  79. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  80. package/src/__tests__/skills.test.ts +2 -0
  81. package/src/__tests__/sms-messaging-provider.test.ts +126 -0
  82. package/src/__tests__/starter-task-flow.test.ts +2 -0
  83. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  84. package/src/__tests__/system-prompt.test.ts +2 -0
  85. package/src/__tests__/task-management-tools.test.ts +2 -2
  86. package/src/__tests__/task-runner.test.ts +14 -4
  87. package/src/__tests__/terminal-tools.test.ts +25 -19
  88. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  89. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  90. package/src/__tests__/tool-executor.test.ts +23 -24
  91. package/src/__tests__/trust-store.test.ts +3 -3
  92. package/src/__tests__/twilio-rest.test.ts +29 -0
  93. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  94. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  95. package/src/__tests__/twilio-routes.test.ts +167 -11
  96. package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
  97. package/src/__tests__/user-reference.test.ts +2 -0
  98. package/src/__tests__/voice-quality.test.ts +222 -0
  99. package/src/__tests__/web-search.test.ts +46 -30
  100. package/src/__tests__/work-item-output.test.ts +110 -0
  101. package/src/agent/loop.ts +1 -1
  102. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  103. package/src/amazon/client.ts +1418 -0
  104. package/src/amazon/request-extractor.ts +135 -0
  105. package/src/amazon/session.ts +109 -0
  106. package/src/autonomy/autonomy-store.ts +5 -5
  107. package/src/browser-extension-relay/client.ts +124 -0
  108. package/src/browser-extension-relay/protocol.ts +63 -0
  109. package/src/browser-extension-relay/server.ts +177 -0
  110. package/src/bundler/app-bundler.ts +3 -3
  111. package/src/bundler/bundle-signer.ts +1 -1
  112. package/src/bundler/signature-verifier.ts +1 -1
  113. package/src/calls/call-conversation-messages.ts +33 -0
  114. package/src/calls/call-domain.ts +114 -10
  115. package/src/calls/call-orchestrator.ts +268 -59
  116. package/src/calls/call-pointer-messages.ts +53 -0
  117. package/src/calls/call-recovery.ts +3 -8
  118. package/src/calls/call-store.ts +69 -87
  119. package/src/calls/elevenlabs-config.ts +3 -2
  120. package/src/calls/guardian-action-sweep.ts +105 -0
  121. package/src/calls/guardian-dispatch.ts +203 -0
  122. package/src/calls/guardian-question-copy.ts +133 -0
  123. package/src/calls/relay-server.ts +466 -8
  124. package/src/calls/speaker-identification.ts +1 -1
  125. package/src/calls/twilio-config.ts +22 -14
  126. package/src/calls/twilio-provider.ts +6 -4
  127. package/src/calls/twilio-rest.ts +308 -7
  128. package/src/calls/twilio-routes.ts +65 -12
  129. package/src/calls/types.ts +3 -1
  130. package/src/channels/types.ts +25 -0
  131. package/src/cli/amazon.ts +815 -0
  132. package/src/cli/config-commands.ts +2 -2
  133. package/src/cli/core-commands.ts +4 -3
  134. package/src/cli/influencer.ts +244 -0
  135. package/src/cli/map.ts +89 -6
  136. package/src/cli.ts +1 -1
  137. package/src/config/agent-schema.ts +171 -0
  138. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  139. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  140. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  141. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  142. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  143. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  144. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  145. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  146. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  147. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  148. package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
  149. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
  150. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
  151. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  152. package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
  153. package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
  154. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  155. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  156. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  157. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  158. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  159. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  160. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  161. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
  162. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  163. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
  164. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
  165. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
  166. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
  167. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
  168. package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
  169. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
  170. package/src/config/bundled-skills/messaging/SKILL.md +33 -8
  171. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  172. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
  174. package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
  175. package/src/config/bundled-skills/twitter/SKILL.md +19 -3
  176. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  177. package/src/config/bundled-tool-registry.ts +310 -0
  178. package/src/config/calls-schema.ts +181 -0
  179. package/src/config/core-schema.ts +309 -0
  180. package/src/config/defaults.ts +28 -3
  181. package/src/config/env-registry.ts +162 -0
  182. package/src/config/env.ts +175 -0
  183. package/src/config/loader.ts +6 -6
  184. package/src/config/memory-schema.ts +528 -0
  185. package/src/config/sandbox-schema.ts +55 -0
  186. package/src/config/schema.ts +158 -1133
  187. package/src/config/skill-state.ts +1 -1
  188. package/src/config/skills-schema.ts +32 -0
  189. package/src/config/skills.ts +35 -24
  190. package/src/config/system-prompt.ts +131 -56
  191. package/src/config/templates/IDENTITY.md +2 -2
  192. package/src/config/templates/SOUL.md +1 -1
  193. package/src/config/types.ts +1 -0
  194. package/src/config/user-reference.ts +4 -9
  195. package/src/config/vellum-skills/catalog.json +6 -7
  196. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  197. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
  198. package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
  199. package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
  200. package/src/context/window-manager.ts +27 -7
  201. package/src/daemon/approval-generators.ts +186 -0
  202. package/src/daemon/approved-devices-store.ts +140 -0
  203. package/src/daemon/assistant-attachments.ts +1 -1
  204. package/src/daemon/classifier.ts +35 -32
  205. package/src/daemon/config-watcher.ts +1 -1
  206. package/src/daemon/daemon-control.ts +217 -0
  207. package/src/daemon/handlers/apps.ts +2 -3
  208. package/src/daemon/handlers/config-channels.ts +158 -0
  209. package/src/daemon/handlers/config-inbox.ts +540 -0
  210. package/src/daemon/handlers/config-ingress.ts +231 -0
  211. package/src/daemon/handlers/config-integrations.ts +258 -0
  212. package/src/daemon/handlers/config-model.ts +143 -0
  213. package/src/daemon/handlers/config-parental.ts +163 -0
  214. package/src/daemon/handlers/config-scheduling.ts +172 -0
  215. package/src/daemon/handlers/config-slack.ts +92 -0
  216. package/src/daemon/handlers/config-telegram.ts +301 -0
  217. package/src/daemon/handlers/config-tools.ts +177 -0
  218. package/src/daemon/handlers/config-trust.ts +104 -0
  219. package/src/daemon/handlers/config-twilio.ts +1080 -0
  220. package/src/daemon/handlers/config.ts +53 -1689
  221. package/src/daemon/handlers/diagnostics.ts +1 -1
  222. package/src/daemon/handlers/dictation.ts +180 -0
  223. package/src/daemon/handlers/documents.ts +18 -32
  224. package/src/daemon/handlers/identity.ts +14 -23
  225. package/src/daemon/handlers/index.ts +11 -0
  226. package/src/daemon/handlers/misc.ts +3 -5
  227. package/src/daemon/handlers/pairing.ts +98 -0
  228. package/src/daemon/handlers/sessions.ts +56 -5
  229. package/src/daemon/handlers/shared.ts +6 -1
  230. package/src/daemon/handlers/skills.ts +1 -1
  231. package/src/daemon/handlers/twitter-auth.ts +2 -0
  232. package/src/daemon/handlers/work-items.ts +17 -9
  233. package/src/daemon/handlers/workspace-files.ts +4 -3
  234. package/src/daemon/install-cli-launchers.ts +113 -0
  235. package/src/daemon/ipc-contract/apps.ts +356 -0
  236. package/src/daemon/ipc-contract/browser.ts +74 -0
  237. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  238. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  239. package/src/daemon/ipc-contract/documents.ts +74 -0
  240. package/src/daemon/ipc-contract/inbox.ts +209 -0
  241. package/src/daemon/ipc-contract/integrations.ts +284 -0
  242. package/src/daemon/ipc-contract/memory.ts +48 -0
  243. package/src/daemon/ipc-contract/messages.ts +211 -0
  244. package/src/daemon/ipc-contract/pairing.ts +45 -0
  245. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  246. package/src/daemon/ipc-contract/schedules.ts +97 -0
  247. package/src/daemon/ipc-contract/sessions.ts +315 -0
  248. package/src/daemon/ipc-contract/shared.ts +42 -0
  249. package/src/daemon/ipc-contract/skills.ts +120 -0
  250. package/src/daemon/ipc-contract/subagents.ts +58 -0
  251. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  252. package/src/daemon/ipc-contract/trust.ts +60 -0
  253. package/src/daemon/ipc-contract/work-items.ts +225 -0
  254. package/src/daemon/ipc-contract/workspace.ts +113 -0
  255. package/src/daemon/ipc-contract-inventory.json +70 -0
  256. package/src/daemon/ipc-contract-inventory.ts +55 -29
  257. package/src/daemon/ipc-contract.ts +229 -2426
  258. package/src/daemon/ipc-protocol.ts +1 -1
  259. package/src/daemon/ipc-validate.ts +7 -0
  260. package/src/daemon/lifecycle.ts +97 -377
  261. package/src/daemon/pairing-store.ts +177 -0
  262. package/src/daemon/providers-setup.ts +43 -0
  263. package/src/daemon/ride-shotgun-handler.ts +68 -3
  264. package/src/daemon/server.ts +66 -46
  265. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  266. package/src/daemon/session-agent-loop.ts +117 -275
  267. package/src/daemon/session-dynamic-profile.ts +1 -1
  268. package/src/daemon/session-history.ts +1 -1
  269. package/src/daemon/session-media-retry.ts +1 -1
  270. package/src/daemon/session-messaging.ts +37 -2
  271. package/src/daemon/session-notifiers.ts +5 -25
  272. package/src/daemon/session-process.ts +99 -59
  273. package/src/daemon/session-queue-manager.ts +96 -4
  274. package/src/daemon/session-runtime-assembly.ts +199 -10
  275. package/src/daemon/session-surfaces.ts +19 -4
  276. package/src/daemon/session-tool-setup.ts +30 -30
  277. package/src/daemon/session-workspace.ts +1 -1
  278. package/src/daemon/session.ts +35 -2
  279. package/src/daemon/shutdown-handlers.ts +122 -0
  280. package/src/daemon/trace-emitter.ts +1 -1
  281. package/src/daemon/watch-handler.ts +36 -33
  282. package/src/doordash/cart-queries.ts +787 -0
  283. package/src/doordash/client.ts +144 -127
  284. package/src/doordash/order-queries.ts +85 -0
  285. package/src/doordash/queries.ts +10 -1308
  286. package/src/doordash/search-queries.ts +203 -0
  287. package/src/doordash/session.ts +3 -2
  288. package/src/doordash/store-queries.ts +246 -0
  289. package/src/doordash/types.ts +367 -0
  290. package/src/email/providers/agentmail.ts +2 -1
  291. package/src/email/providers/index.ts +3 -2
  292. package/src/email/service.ts +3 -2
  293. package/src/errors.ts +43 -0
  294. package/src/home-base/prebuilt/seed.ts +1 -1
  295. package/src/hooks/cli.ts +6 -5
  296. package/src/hooks/config.ts +6 -8
  297. package/src/hooks/discovery.ts +6 -5
  298. package/src/hooks/manager.ts +4 -3
  299. package/src/hooks/runner.ts +2 -2
  300. package/src/hooks/templates.ts +5 -5
  301. package/src/inbound/public-ingress-urls.ts +6 -4
  302. package/src/index.ts +4 -2
  303. package/src/influencer/client.ts +1104 -0
  304. package/src/instrument.ts +4 -3
  305. package/src/logfire.ts +4 -3
  306. package/src/memory/admin.ts +25 -35
  307. package/src/memory/attachments-store.ts +4 -7
  308. package/src/memory/channel-delivery-store.ts +30 -1
  309. package/src/memory/channel-guardian-store.ts +202 -2
  310. package/src/memory/clarification-resolver.ts +37 -33
  311. package/src/memory/conflict-store.ts +67 -61
  312. package/src/memory/contradiction-checker.ts +141 -117
  313. package/src/memory/conversation-store.ts +335 -51
  314. package/src/memory/db-connection.ts +27 -4
  315. package/src/memory/db-init.ts +265 -4
  316. package/src/memory/db.ts +14 -1
  317. package/src/memory/embedding-backend.ts +27 -5
  318. package/src/memory/embedding-ollama.ts +2 -1
  319. package/src/memory/entity-extractor.ts +38 -35
  320. package/src/memory/guardian-action-store.ts +430 -0
  321. package/src/memory/inbox-escalation-projection.ts +59 -0
  322. package/src/memory/inbox-thread-store.ts +218 -0
  323. package/src/memory/ingress-invite-store.ts +338 -0
  324. package/src/memory/ingress-member-store.ts +350 -0
  325. package/src/memory/items-extractor.ts +91 -97
  326. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  327. package/src/memory/job-handlers/media-processing.ts +69 -0
  328. package/src/memory/job-handlers/summarization.ts +32 -26
  329. package/src/memory/job-utils.ts +3 -10
  330. package/src/memory/jobs-store.ts +8 -10
  331. package/src/memory/jobs-worker.ts +55 -36
  332. package/src/memory/media-store.ts +759 -0
  333. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  334. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  335. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  336. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  337. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  338. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  339. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  340. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  341. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  342. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  343. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  344. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  345. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  346. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  347. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  348. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  349. package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
  350. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  351. package/src/memory/migrations/index.ts +24 -0
  352. package/src/memory/migrations/registry.ts +79 -0
  353. package/src/memory/migrations/validate-migration-state.ts +69 -0
  354. package/src/memory/qdrant-manager.ts +49 -8
  355. package/src/memory/query-builder.ts +1 -1
  356. package/src/memory/raw-query.ts +119 -0
  357. package/src/memory/recall-cache.ts +4 -1
  358. package/src/memory/retriever.ts +165 -47
  359. package/src/memory/schema-migration.ts +25 -984
  360. package/src/memory/schema.ts +228 -7
  361. package/src/memory/search/entity.ts +205 -31
  362. package/src/memory/search/lexical.ts +81 -52
  363. package/src/memory/search/ranking.ts +27 -23
  364. package/src/memory/search/semantic.ts +157 -19
  365. package/src/memory/search/types.ts +24 -0
  366. package/src/memory/shared-app-links-store.ts +4 -5
  367. package/src/memory/validation.ts +19 -0
  368. package/src/messaging/draft-store.ts +5 -6
  369. package/src/messaging/provider-types.ts +2 -0
  370. package/src/messaging/providers/sms/adapter.ts +201 -0
  371. package/src/messaging/providers/sms/client.ts +93 -0
  372. package/src/messaging/providers/sms/types.ts +7 -0
  373. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  374. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  375. package/src/messaging/providers/whatsapp/client.ts +67 -0
  376. package/src/messaging/style-analyzer.ts +5 -4
  377. package/src/messaging/thread-summarizer.ts +61 -69
  378. package/src/messaging/triage-engine.ts +62 -71
  379. package/src/migrations/config-merge.ts +53 -0
  380. package/src/migrations/data-layout.ts +68 -0
  381. package/src/migrations/data-merge.ts +33 -0
  382. package/src/migrations/hooks-merge.ts +90 -0
  383. package/src/migrations/index.ts +6 -0
  384. package/src/migrations/log.ts +23 -0
  385. package/src/migrations/skills-merge.ts +33 -0
  386. package/src/migrations/workspace-layout.ts +79 -0
  387. package/src/permissions/checker.ts +133 -11
  388. package/src/permissions/prompter.ts +14 -0
  389. package/src/permissions/shell-identity.ts +31 -1
  390. package/src/permissions/trust-store.ts +21 -1
  391. package/src/providers/anthropic/client.ts +4 -4
  392. package/src/providers/failover.ts +2 -2
  393. package/src/providers/model-intents.ts +70 -0
  394. package/src/providers/ollama/client.ts +2 -1
  395. package/src/providers/provider-send-message.ts +176 -0
  396. package/src/providers/registry.ts +71 -30
  397. package/src/providers/retry.ts +35 -1
  398. package/src/providers/types.ts +12 -1
  399. package/src/runtime/approval-conversation-turn.ts +97 -0
  400. package/src/runtime/approval-message-composer.ts +253 -0
  401. package/src/runtime/channel-approval-parser.ts +36 -2
  402. package/src/runtime/channel-approvals.ts +11 -24
  403. package/src/runtime/channel-guardian-service.ts +88 -21
  404. package/src/runtime/channel-readiness-service.ts +418 -0
  405. package/src/runtime/channel-readiness-types.ts +35 -0
  406. package/src/runtime/channel-retry-sweep.ts +184 -0
  407. package/src/runtime/guardian-context-resolver.ts +108 -0
  408. package/src/runtime/http-server.ts +275 -717
  409. package/src/runtime/http-types.ts +59 -3
  410. package/src/runtime/middleware/auth.ts +116 -0
  411. package/src/runtime/middleware/error-handler.ts +33 -0
  412. package/src/runtime/middleware/twilio-validation.ts +127 -0
  413. package/src/runtime/routes/app-routes.ts +1 -1
  414. package/src/runtime/routes/call-routes.ts +51 -7
  415. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  416. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  417. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  418. package/src/runtime/routes/channel-route-shared.ts +144 -0
  419. package/src/runtime/routes/channel-routes.ts +32 -1588
  420. package/src/runtime/routes/conversation-routes.ts +50 -7
  421. package/src/runtime/routes/events-routes.ts +2 -2
  422. package/src/runtime/routes/identity-routes.ts +126 -0
  423. package/src/runtime/routes/pairing-routes.ts +143 -0
  424. package/src/runtime/routes/run-routes.ts +15 -1
  425. package/src/runtime/run-orchestrator.ts +86 -35
  426. package/src/schedule/schedule-store.ts +36 -32
  427. package/src/schedule/scheduler.ts +3 -3
  428. package/src/security/encrypted-store.ts +5 -7
  429. package/src/security/oauth2.ts +45 -15
  430. package/src/security/parental-control-store.ts +183 -0
  431. package/src/security/secret-allowlist.ts +4 -3
  432. package/src/security/secret-scanner.ts +5 -5
  433. package/src/security/secure-keys.ts +1 -1
  434. package/src/security/token-manager.ts +3 -2
  435. package/src/services/vercel-deploy.ts +6 -2
  436. package/src/skills/tool-manifest.ts +3 -3
  437. package/src/skills/vellum-catalog-remote.ts +75 -16
  438. package/src/slack/slack-webhook.ts +2 -1
  439. package/src/swarm/orchestrator.ts +92 -1
  440. package/src/swarm/router-planner.ts +6 -9
  441. package/src/swarm/worker-prompts.ts +9 -12
  442. package/src/tasks/task-compiler.ts +19 -28
  443. package/src/tasks/task-runner.ts +1 -1
  444. package/src/tools/assets/materialize.ts +2 -2
  445. package/src/tools/assets/search.ts +15 -14
  446. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  447. package/src/tools/browser/auto-navigate.ts +1 -0
  448. package/src/tools/browser/browser-execution.ts +10 -1
  449. package/src/tools/browser/browser-manager.ts +119 -4
  450. package/src/tools/browser/network-recorder.ts +5 -0
  451. package/src/tools/calls/call-start.ts +1 -0
  452. package/src/tools/credentials/broker.ts +11 -2
  453. package/src/tools/credentials/metadata-store.ts +18 -14
  454. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  455. package/src/tools/credentials/vault.ts +49 -23
  456. package/src/tools/execution-target.ts +11 -1
  457. package/src/tools/executor.ts +68 -9
  458. package/src/tools/host-terminal/cli-discover.ts +1 -1
  459. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  460. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  461. package/src/tools/network/script-proxy/server.ts +1 -1
  462. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  463. package/src/tools/network/web-fetch.ts +18 -2
  464. package/src/tools/network/web-search.ts +8 -4
  465. package/src/tools/reminder/reminder-store.ts +14 -15
  466. package/src/tools/schedule/create.ts +1 -0
  467. package/src/tools/schedule/list.ts +2 -1
  468. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  469. package/src/tools/skills/skill-script-runner.ts +24 -9
  470. package/src/tools/skills/skill-tool-factory.ts +1 -0
  471. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  472. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  473. package/src/tools/terminal/parser.ts +50 -0
  474. package/src/tools/types.ts +2 -0
  475. package/src/tools/watcher/delete.ts +6 -0
  476. package/src/tools/weather/service.ts +1 -1
  477. package/src/twitter/client.ts +190 -24
  478. package/src/twitter/router.ts +1 -1
  479. package/src/twitter/session.ts +4 -3
  480. package/src/util/clipboard.ts +1 -1
  481. package/src/util/errors.ts +65 -8
  482. package/src/util/fs.ts +40 -0
  483. package/src/util/json.ts +10 -0
  484. package/src/util/log-redact.ts +189 -0
  485. package/src/util/logger.ts +19 -17
  486. package/src/util/object.ts +3 -0
  487. package/src/util/platform.ts +105 -363
  488. package/src/util/pricing.ts +1 -1
  489. package/src/util/promise-guard.ts +1 -1
  490. package/src/util/retry.ts +19 -0
  491. package/src/util/row-mapper.ts +79 -0
  492. package/src/util/silently.ts +21 -0
  493. package/src/watcher/engine.ts +5 -1
  494. package/src/watcher/provider-types.ts +20 -0
  495. package/src/watcher/providers/github.ts +156 -0
  496. package/src/watcher/providers/gmail.ts +1 -0
  497. package/src/watcher/providers/google-calendar.ts +1 -0
  498. package/src/watcher/providers/linear.ts +460 -0
  499. package/src/watcher/providers/slack.ts +1 -0
  500. package/src/work-items/work-item-runner.ts +1 -1
  501. package/src/workspace/git-service.ts +1 -1
  502. package/src/workspace/provider-commit-message-generator.ts +51 -22
  503. package/src/__tests__/call-bridge.test.ts +0 -517
  504. package/src/__tests__/session-process-bridge.test.ts +0 -244
  505. package/src/calls/call-bridge.ts +0 -168
  506. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -61,7 +61,7 @@ const SENSITIVE_KEYS = new Set([
61
61
  function redactDeep(value: unknown): unknown {
62
62
  if (typeof value === 'string') return redact(value);
63
63
  if (Array.isArray(value)) return value.map(redactDeep);
64
- if (value !== null && typeof value === 'object') {
64
+ if (value != null && typeof value === 'object') {
65
65
  const out: Record<string, unknown> = {};
66
66
  for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
67
67
  if (SENSITIVE_KEYS.has(k.toLowerCase())) {
@@ -0,0 +1,180 @@
1
+ import * as net from 'node:net';
2
+ import { getConfiguredProvider } from '../../providers/provider-send-message.js';
3
+ import type { DictationRequest } from '../ipc-protocol.js';
4
+ import { log, defineHandlers, type HandlerContext } from './shared.js';
5
+
6
+ // Action verbs that signal the user wants a full agent session rather than inline text
7
+ const ACTION_VERBS = ['slack', 'email', 'send', 'create', 'open', 'search', 'find'];
8
+
9
+ const MAX_WINDOW_TITLE_LENGTH = 100;
10
+
11
+ /** Sanitize window title to mitigate prompt injection from attacker-controlled titles (e.g. browser tabs, Slack conversations). */
12
+ function sanitizeWindowTitle(title: string | undefined): string {
13
+ if (!title) return '';
14
+ return title
15
+ .replace(/[<>]/g, '') // strip angle brackets to prevent tag injection
16
+ .slice(0, MAX_WINDOW_TITLE_LENGTH);
17
+ }
18
+
19
+ /** Build a delimited app metadata block so the LLM treats it as contextual data, not instructions. */
20
+ function buildAppMetadataBlock(msg: DictationRequest): string {
21
+ const windowTitle = sanitizeWindowTitle(msg.context.windowTitle);
22
+ return [
23
+ '<app_metadata>',
24
+ `App: ${msg.context.appName} (${msg.context.bundleIdentifier})`,
25
+ `Window: ${windowTitle}`,
26
+ '</app_metadata>',
27
+ ].join('\n');
28
+ }
29
+
30
+ type DictationMode = 'dictation' | 'command' | 'action';
31
+
32
+ export function detectDictationMode(msg: DictationRequest): DictationMode {
33
+ // Command mode: selected text present — treat transcription as a transformation instruction
34
+ if (msg.context.selectedText && msg.context.selectedText.trim().length > 0) {
35
+ return 'command';
36
+ }
37
+
38
+ // Action mode: transcription starts with an action verb
39
+ const firstWord = msg.transcription.trim().split(/\s+/)[0]?.toLowerCase() ?? '';
40
+ if (ACTION_VERBS.includes(firstWord)) {
41
+ return 'action';
42
+ }
43
+
44
+ // Dictation mode: cursor is in a text field with no selection — clean up for typing
45
+ if (msg.context.cursorInTextField) {
46
+ return 'dictation';
47
+ }
48
+
49
+ // AX focus-role detection in browser editors (for example Gmail compose)
50
+ // is occasionally incomplete. If we default to action here, normal dictation
51
+ // gets misrouted into a new chat task. Treat ambiguous context as dictation.
52
+ return 'dictation';
53
+ }
54
+
55
+ function buildDictationPrompt(msg: DictationRequest): string {
56
+ return [
57
+ 'You are a dictation assistant. Clean up the following speech transcription for direct insertion into a text field.',
58
+ '',
59
+ '## Rules',
60
+ '- Fix grammar, punctuation, and capitalization',
61
+ '- Remove filler words (um, uh, like, you know)',
62
+ "- Maintain the speaker's intent and meaning",
63
+ '- Do NOT add explanations or commentary',
64
+ '- Return ONLY the cleaned text, nothing else',
65
+ '',
66
+ '## Tone Adaptation',
67
+ 'Adapt your output tone based on the active application:',
68
+ '- Email apps (Gmail, Mail): Professional but warm. Use proper greetings and sign-offs if appropriate.',
69
+ '- Slack: Casual and conversational. Match typical chat style.',
70
+ '- Code editors (VS Code, Xcode): Technical and concise. Code comments style.',
71
+ '- Terminal: Command-like, terse.',
72
+ '- Messages/iMessage: Very casual, texting style. Short sentences.',
73
+ '- Notes/Docs: Neutral, clear writing.',
74
+ '- Default: Match the user\'s natural voice.',
75
+ '',
76
+ '## Context Clues',
77
+ '- Window title may contain recipient name (Slack DMs, email compose)',
78
+ '- If you can identify a recipient, adapt formality to the apparent relationship',
79
+ '- Maintain the user\'s natural voice — don\'t over-formalize casual speech',
80
+ '- The user\'s writing patterns and preferences may be available from memory context — follow those when present',
81
+ '',
82
+ buildAppMetadataBlock(msg),
83
+ ].join('\n');
84
+ }
85
+
86
+ function buildCommandPrompt(msg: DictationRequest): string {
87
+ return [
88
+ 'You are a text transformation assistant. The user has selected text and given a voice command to transform it.',
89
+ '',
90
+ '## Rules',
91
+ '- Apply the instruction to the selected text',
92
+ '- Return ONLY the transformed text, nothing else',
93
+ '- Do NOT add explanations or commentary',
94
+ '',
95
+ '## Tone Adaptation',
96
+ 'Match the tone to the active application context:',
97
+ '- Email apps (Gmail, Mail): Professional but warm.',
98
+ '- Slack: Casual and conversational.',
99
+ '- Code editors (VS Code, Xcode): Technical and concise.',
100
+ '- Terminal: Command-like, terse.',
101
+ '- Messages/iMessage: Very casual, texting style.',
102
+ '- Notes/Docs: Neutral, clear writing.',
103
+ '- Default: Match the user\'s natural voice.',
104
+ '',
105
+ '## Context Clues',
106
+ '- Window title may contain recipient name (Slack DMs, email compose)',
107
+ '- If you can identify a recipient, adapt formality to the apparent relationship',
108
+ '- Maintain the user\'s natural voice — don\'t over-formalize casual speech',
109
+ '- The user\'s writing patterns and preferences may be available from memory context — follow those when present',
110
+ '',
111
+ buildAppMetadataBlock(msg),
112
+ '',
113
+ 'Selected text:',
114
+ msg.context.selectedText ?? '',
115
+ '',
116
+ `Instruction: ${msg.transcription}`,
117
+ ].join('\n');
118
+ }
119
+
120
+ export async function handleDictationRequest(
121
+ msg: DictationRequest,
122
+ socket: net.Socket,
123
+ ctx: HandlerContext,
124
+ ): Promise<void> {
125
+ const mode = detectDictationMode(msg);
126
+ log.info({ mode, transcriptionLength: msg.transcription.length }, 'Dictation request received');
127
+
128
+ // Action mode: return immediately — the client will route to a full agent session
129
+ if (mode === 'action') {
130
+ ctx.send(socket, {
131
+ type: 'dictation_response',
132
+ text: msg.transcription,
133
+ mode: 'action',
134
+ actionPlan: `User wants to: ${msg.transcription}`,
135
+ });
136
+ return;
137
+ }
138
+
139
+ // Dictation / command mode: make a single-turn LLM call for text cleanup or transformation
140
+ const systemPrompt = mode === 'dictation'
141
+ ? buildDictationPrompt(msg)
142
+ : buildCommandPrompt(msg);
143
+
144
+ const userText = mode === 'dictation'
145
+ ? msg.transcription
146
+ : msg.transcription; // command prompt already embeds the selected text and instruction
147
+
148
+ try {
149
+ const provider = getConfiguredProvider();
150
+ if (!provider) {
151
+ log.warn('Dictation: no provider available, returning raw transcription');
152
+ const fallbackText = mode === 'command' ? (msg.context.selectedText ?? msg.transcription) : msg.transcription;
153
+ ctx.send(socket, { type: 'dictation_response', text: fallbackText, mode });
154
+ return;
155
+ }
156
+
157
+ const response = await provider.sendMessage(
158
+ [{ role: 'user', content: [{ type: 'text', text: userText }] }],
159
+ [], // no tools
160
+ systemPrompt,
161
+ { config: { max_tokens: 1024 } },
162
+ );
163
+
164
+ const textBlock = response.content.find((b) => b.type === 'text');
165
+ const inlineFallback = mode === 'command' ? (msg.context.selectedText ?? msg.transcription) : msg.transcription;
166
+ const cleanedText = textBlock && 'text' in textBlock ? textBlock.text.trim() : inlineFallback;
167
+
168
+ ctx.send(socket, { type: 'dictation_response', text: cleanedText, mode });
169
+ } catch (err) {
170
+ const message = err instanceof Error ? err.message : String(err);
171
+ log.error({ err }, 'Dictation LLM call failed, returning raw transcription');
172
+ const fallbackText = mode === 'command' ? (msg.context.selectedText ?? msg.transcription) : msg.transcription;
173
+ ctx.send(socket, { type: 'dictation_response', text: fallbackText, mode });
174
+ ctx.send(socket, { type: 'error', message: `Dictation cleanup failed: ${message}` });
175
+ }
176
+ }
177
+
178
+ export const dictationHandlers = defineHandlers({
179
+ dictation_request: handleDictationRequest,
180
+ });
@@ -2,12 +2,23 @@ import { defineHandlers } from './shared.js';
2
2
  import type { HandlerContext } from './shared.js';
3
3
  import type { DocumentSaveRequest, DocumentLoadRequest, DocumentListRequest } from '../ipc-protocol.js';
4
4
  import type * as net from 'node:net';
5
- import type { Database } from 'bun:sqlite';
6
- import { getDb } from '../../memory/db.js';
5
+ import { rawRun, rawGet, rawAll } from '../../memory/db.js';
7
6
  import { getLogger } from '../../util/logger.js';
8
7
 
9
8
  const log = getLogger('documents');
10
9
 
10
+ interface DocumentRow {
11
+ surface_id: string;
12
+ conversation_id: string;
13
+ title: string;
14
+ content: string;
15
+ word_count: number;
16
+ created_at: number;
17
+ updated_at: number;
18
+ }
19
+
20
+ type DocumentListRow = Omit<DocumentRow, 'content'>;
21
+
11
22
  export function handleDocumentSave(msg: DocumentSaveRequest, socket: net.Socket, ctx: HandlerContext): void {
12
23
  log.info({
13
24
  surfaceId: msg.surfaceId,
@@ -18,13 +29,9 @@ export function handleDocumentSave(msg: DocumentSaveRequest, socket: net.Socket,
18
29
  }, 'Received save request');
19
30
 
20
31
  try {
21
- const db = getDb();
22
- // Get the raw SQLite client from Drizzle
23
- const sqlite = (db as unknown as { $client: Database }).$client;
24
32
  const now = Date.now();
25
33
 
26
- // Upsert document (insert or update if exists)
27
- sqlite.run(
34
+ rawRun(
28
35
  `INSERT INTO documents (surface_id, conversation_id, title, content, word_count, created_at, updated_at)
29
36
  VALUES (?, ?, ?, ?, ?, ?, ?)
30
37
  ON CONFLICT(surface_id) DO UPDATE SET
@@ -32,7 +39,7 @@ export function handleDocumentSave(msg: DocumentSaveRequest, socket: net.Socket,
32
39
  content = excluded.content,
33
40
  word_count = excluded.word_count,
34
41
  updated_at = excluded.updated_at`,
35
- [msg.surfaceId, msg.conversationId, msg.title, msg.content, msg.wordCount, now, now]
42
+ msg.surfaceId, msg.conversationId, msg.title, msg.content, msg.wordCount, now, now,
36
43
  );
37
44
 
38
45
  ctx.send(socket, {
@@ -55,22 +62,11 @@ export function handleDocumentSave(msg: DocumentSaveRequest, socket: net.Socket,
55
62
 
56
63
  export function handleDocumentLoad(msg: DocumentLoadRequest, socket: net.Socket, ctx: HandlerContext): void {
57
64
  try {
58
- const db = getDb();
59
- const sqlite = (db as unknown as { $client: Database }).$client;
60
-
61
- const result = sqlite.prepare(/*sql*/ `
65
+ const result = rawGet<DocumentRow>(/*sql*/ `
62
66
  SELECT surface_id, conversation_id, title, content, word_count, created_at, updated_at
63
67
  FROM documents
64
68
  WHERE surface_id = ?
65
- `).get(msg.surfaceId) as {
66
- surface_id: string;
67
- conversation_id: string;
68
- title: string;
69
- content: string;
70
- word_count: number;
71
- created_at: number;
72
- updated_at: number;
73
- } | undefined;
69
+ `, msg.surfaceId);
74
70
 
75
71
  if (result) {
76
72
  ctx.send(socket, {
@@ -119,9 +115,6 @@ export function handleDocumentLoad(msg: DocumentLoadRequest, socket: net.Socket,
119
115
 
120
116
  export function handleDocumentList(msg: DocumentListRequest, socket: net.Socket, ctx: HandlerContext): void {
121
117
  try {
122
- const db = getDb();
123
- const sqlite = (db as unknown as { $client: Database }).$client;
124
-
125
118
  let query = /*sql*/ `
126
119
  SELECT surface_id, conversation_id, title, word_count, created_at, updated_at
127
120
  FROM documents
@@ -135,14 +128,7 @@ export function handleDocumentList(msg: DocumentListRequest, socket: net.Socket,
135
128
 
136
129
  query += ' ORDER BY updated_at DESC';
137
130
 
138
- const results = sqlite.prepare(query).all(...params) as Array<{
139
- surface_id: string;
140
- conversation_id: string;
141
- title: string;
142
- word_count: number;
143
- created_at: number;
144
- updated_at: number;
145
- }>;
131
+ const results = rawAll<DocumentListRow>(query, ...params);
146
132
 
147
133
  ctx.send(socket, {
148
134
  type: 'document_list_response',
@@ -2,7 +2,7 @@ import * as net from 'node:net';
2
2
  import { existsSync, readFileSync, statSync } from 'node:fs';
3
3
  import { join, dirname } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
- import { getWorkspacePromptPath } from '../../util/platform.js';
5
+ import { getWorkspacePromptPath, readLockfile } from '../../util/platform.js';
6
6
  import { log, defineHandlers, type HandlerContext } from './shared.js';
7
7
 
8
8
  function handleIdentityGet(socket: net.Socket, ctx: HandlerContext): void {
@@ -68,28 +68,19 @@ function handleIdentityGet(socket: net.Socket, ctx: HandlerContext): void {
68
68
  let cloud: string | undefined;
69
69
  let originSystem: string | undefined;
70
70
  try {
71
- const homedir = process.env.HOME ?? process.env.USERPROFILE ?? '';
72
- const lockfilePaths = [
73
- join(homedir, '.vellum.lock.json'),
74
- join(homedir, '.vellum.lockfile.json'),
75
- ];
76
- for (const lockPath of lockfilePaths) {
77
- if (!existsSync(lockPath)) continue;
78
- const lockData = JSON.parse(readFileSync(lockPath, 'utf-8'));
79
- const assistants = lockData.assistants as Array<Record<string, unknown>> | undefined;
80
- if (assistants && assistants.length > 0) {
81
- // Use the most recently hatched assistant
82
- const sorted = [...assistants].sort((a, b) => {
83
- const dateA = new Date(a.hatchedAt as string || 0).getTime();
84
- const dateB = new Date(b.hatchedAt as string || 0).getTime();
85
- return dateB - dateA;
86
- });
87
- const latest = sorted[0];
88
- assistantId = latest.assistantId as string | undefined;
89
- cloud = latest.cloud as string | undefined;
90
- originSystem = cloud === 'local' ? 'local' : cloud;
91
- }
92
- break;
71
+ const lockData = readLockfile();
72
+ const assistants = lockData?.assistants as Array<Record<string, unknown>> | undefined;
73
+ if (assistants && assistants.length > 0) {
74
+ // Use the most recently hatched assistant
75
+ const sorted = [...assistants].sort((a, b) => {
76
+ const dateA = new Date(a.hatchedAt as string || 0).getTime();
77
+ const dateB = new Date(b.hatchedAt as string || 0).getTime();
78
+ return dateB - dateA;
79
+ });
80
+ const latest = sorted[0];
81
+ assistantId = latest.assistantId as string | undefined;
82
+ cloud = latest.cloud as string | undefined;
83
+ originSystem = cloud === 'local' ? 'local' : cloud;
93
84
  }
94
85
  } catch {
95
86
  // ignore — lockfile may not exist
@@ -22,6 +22,9 @@ import { signingHandlers } from './signing.js';
22
22
  import { twitterAuthHandlers } from './twitter-auth.js';
23
23
  import { workspaceFileHandlers } from './workspace-files.js';
24
24
  import { identityHandlers } from './identity.js';
25
+ import { dictationHandlers } from './dictation.js';
26
+ import { inboxInviteHandlers } from './config-inbox.js';
27
+ import { pairingHandlers } from './pairing.js';
25
28
 
26
29
  // Re-export types and utilities for backwards compatibility
27
30
  export type {
@@ -87,6 +90,11 @@ const inlineHandlers = defineHandlers({
87
90
  },
88
91
  integration_disconnect: () => { /* no-op — integration registry removed */ },
89
92
 
93
+ // Stub handler: assistant_inbox — real implementation will be added in a follow-up PR.
94
+ assistant_inbox: (_msg, socket, ctx) => {
95
+ ctx.send(socket, { type: 'assistant_inbox_response', success: false, error: 'Not yet implemented' });
96
+ },
97
+
90
98
  });
91
99
 
92
100
  const handlers = {
@@ -107,6 +115,9 @@ const handlers = {
107
115
  ...twitterAuthHandlers,
108
116
  ...workspaceFileHandlers,
109
117
  ...identityHandlers,
118
+ ...dictationHandlers,
119
+ ...inboxInviteHandlers,
120
+ ...pairingHandlers,
110
121
  ...inlineHandlers,
111
122
  } satisfies DispatchMap;
112
123
 
@@ -3,8 +3,7 @@ import { v4 as uuid } from 'uuid';
3
3
  import { readFileSync } from 'node:fs';
4
4
  import { createHash } from 'node:crypto';
5
5
  import * as conversationStore from '../../memory/conversation-store.js';
6
- import { getConfig } from '../../config/loader.js';
7
- import { getFailoverProvider, listProviders } from '../../providers/registry.js';
6
+ import { getConfiguredProvider } from '../../providers/provider-send-message.js';
8
7
  import type { Provider } from '../../providers/types.js';
9
8
  import { classifyInteraction } from '../classifier.js';
10
9
  import { checkIngressForSecrets } from '../../security/secret-ingress.js';
@@ -172,10 +171,9 @@ export async function handleSuggestionRequest(
172
171
  }
173
172
 
174
173
  // Try LLM suggestion using the configured provider
175
- const config = getConfig();
176
- if (listProviders().includes(config.provider)) {
174
+ const provider = getConfiguredProvider();
175
+ if (provider) {
177
176
  try {
178
- const provider = getFailoverProvider(config.provider, config.providerOrder);
179
177
  let promise = suggestionInFlight.get(m.id);
180
178
  if (!promise) {
181
179
  promise = generateSuggestion(provider, text);
@@ -0,0 +1,98 @@
1
+ import * as net from 'node:net';
2
+ import type {
3
+ PairingApprovalResponse,
4
+ ApprovedDeviceRemove,
5
+ } from '../ipc-protocol.js';
6
+ import { log, defineHandlers, type HandlerContext } from './shared.js';
7
+ import {
8
+ approveDevice,
9
+ removeDevice,
10
+ clearAllDevices,
11
+ listDevices,
12
+ } from '../approved-devices-store.js';
13
+ import type { PairingStore } from '../pairing-store.js';
14
+
15
+ /** Module-level reference set by the daemon server at startup. */
16
+ let pairingStoreRef: PairingStore | null = null;
17
+ let bearerTokenRef: string | undefined;
18
+
19
+ export function initPairingHandlers(store: PairingStore, bearerToken: string | undefined): void {
20
+ pairingStoreRef = store;
21
+ bearerTokenRef = bearerToken;
22
+ }
23
+
24
+ function handlePairingApprovalResponse(
25
+ msg: PairingApprovalResponse,
26
+ _socket: net.Socket,
27
+ _ctx: HandlerContext,
28
+ ): void {
29
+ if (!pairingStoreRef) {
30
+ log.warn('Pairing store not initialized');
31
+ return;
32
+ }
33
+
34
+ const entry = pairingStoreRef.get(msg.pairingRequestId);
35
+ if (!entry) {
36
+ log.warn({ pairingRequestId: msg.pairingRequestId }, 'Pairing request not found for approval response');
37
+ return;
38
+ }
39
+
40
+ // Idempotent: if already approved/denied, just re-broadcast the current status
41
+ if (entry.status === 'approved' || entry.status === 'denied') {
42
+ log.info({ pairingRequestId: msg.pairingRequestId, status: entry.status }, 'Duplicate approval response, no-op');
43
+ return;
44
+ }
45
+
46
+ if (msg.decision === 'deny') {
47
+ pairingStoreRef.deny(msg.pairingRequestId);
48
+ log.info({ pairingRequestId: msg.pairingRequestId }, 'Pairing request denied');
49
+ return;
50
+ }
51
+
52
+ // approve_once or always_allow
53
+ if (!bearerTokenRef) {
54
+ log.error('Cannot approve pairing: no bearer token configured');
55
+ return;
56
+ }
57
+
58
+ pairingStoreRef.approve(msg.pairingRequestId, bearerTokenRef);
59
+ log.info({ pairingRequestId: msg.pairingRequestId, decision: msg.decision }, 'Pairing request approved');
60
+
61
+ // If always_allow, persist the device to the allowlist
62
+ if (msg.decision === 'always_allow' && entry.hashedDeviceId) {
63
+ approveDevice(entry.hashedDeviceId, entry.deviceName ?? 'Unknown Device');
64
+ }
65
+ }
66
+
67
+ function handleApprovedDevicesList(socket: net.Socket, ctx: HandlerContext): void {
68
+ const devices = listDevices();
69
+ ctx.send(socket, {
70
+ type: 'approved_devices_list_response',
71
+ devices,
72
+ });
73
+ }
74
+
75
+ function handleApprovedDeviceRemove(
76
+ msg: ApprovedDeviceRemove,
77
+ socket: net.Socket,
78
+ ctx: HandlerContext,
79
+ ): void {
80
+ const success = removeDevice(msg.hashedDeviceId);
81
+ ctx.send(socket, {
82
+ type: 'approved_device_remove_response',
83
+ success,
84
+ });
85
+ log.info({ hashedDeviceId: msg.hashedDeviceId, success }, 'Device removal requested via IPC');
86
+ }
87
+
88
+ function handleApprovedDevicesClear(_socket: net.Socket, _ctx: HandlerContext): void {
89
+ clearAllDevices();
90
+ log.info('All approved devices cleared via IPC');
91
+ }
92
+
93
+ export const pairingHandlers = defineHandlers({
94
+ pairing_approval_response: handlePairingApprovalResponse,
95
+ approved_devices_list: (_msg, socket, ctx) => handleApprovedDevicesList(socket, ctx),
96
+ approved_device_remove: handleApprovedDeviceRemove,
97
+ approved_devices_clear: (_msg, socket, ctx) => handleApprovedDevicesClear(socket, ctx),
98
+ });
@@ -1,4 +1,6 @@
1
1
  import * as net from 'node:net';
2
+ import { isChannelId, parseChannelId } from '../../channels/types.js';
3
+ import { silentlyWithLog } from '../../util/silently.js';
2
4
  import { v4 as uuid } from 'uuid';
3
5
  import * as conversationStore from '../../memory/conversation-store.js';
4
6
  import * as externalConversationStore from '../../memory/external-conversation-store.js';
@@ -22,6 +24,7 @@ import type {
22
24
  UsageRequest,
23
25
  SandboxSetRequest,
24
26
  ServerMessage,
27
+ ConversationSearchRequest,
25
28
  } from '../ipc-protocol.js';
26
29
  import { getConfig } from '../../config/loader.js';
27
30
  import { getSubagentManager } from '../../subagent/index.js';
@@ -80,7 +83,20 @@ export async function handleUserMessage(
80
83
  attributes: { source: 'user_message' },
81
84
  });
82
85
 
83
- const result = session.enqueueMessage(msg.content ?? '', msg.attachments ?? [], sendEvent, requestId, msg.activeSurfaceId, msg.currentPage);
86
+ const ipcChannel = parseChannelId(msg.channel) ?? 'macos';
87
+ const queuedChannelMetadata = {
88
+ userMessageChannel: ipcChannel,
89
+ assistantMessageChannel: ipcChannel,
90
+ };
91
+ const result = session.enqueueMessage(
92
+ msg.content ?? '',
93
+ msg.attachments ?? [],
94
+ sendEvent,
95
+ requestId,
96
+ msg.activeSurfaceId,
97
+ msg.currentPage,
98
+ queuedChannelMetadata,
99
+ );
84
100
  if (result.rejected) {
85
101
  rlog.warn('Message rejected — queue is full');
86
102
  session.traceEmitter.emit('request_error', 'Message rejected — queue is full', {
@@ -114,6 +130,13 @@ export async function handleUserMessage(
114
130
  }
115
131
 
116
132
  rlog.info('Processing user message');
133
+ session.setTurnChannelContext({
134
+ userMessageChannel: ipcChannel,
135
+ assistantMessageChannel: ipcChannel,
136
+ });
137
+ session.setAssistantId('self');
138
+ session.setGuardianContext(null);
139
+ session.setCommandIntent(null);
117
140
  // Fire-and-forget: don't block the IPC handler so the connection can
118
141
  // continue receiving messages (e.g. cancel, confirmations, or
119
142
  // additional user_message that will be queued by the session).
@@ -208,12 +231,14 @@ export function handleSessionList(socket: net.Socket, ctx: HandlerContext, offse
208
231
  type: 'session_list_response',
209
232
  sessions: conversations.map((c) => {
210
233
  const binding = bindings.get(c.id);
234
+ const originChannel = parseChannelId(c.originChannel);
211
235
  return {
212
236
  id: c.id,
213
237
  title: c.title ?? 'Untitled',
214
238
  updatedAt: c.updatedAt,
215
239
  threadType: normalizeThreadType(c.threadType),
216
- ...(binding ? {
240
+ source: c.source ?? 'user',
241
+ ...(binding && isChannelId(binding.sourceChannel) ? {
217
242
  channelBinding: {
218
243
  sourceChannel: binding.sourceChannel,
219
244
  externalChatId: binding.externalChatId,
@@ -222,6 +247,7 @@ export function handleSessionList(socket: net.Socket, ctx: HandlerContext, offse
222
247
  username: binding.username,
223
248
  },
224
249
  } : {}),
250
+ ...(originChannel ? { conversationOriginChannel: originChannel } : {}),
225
251
  };
226
252
  }),
227
253
  hasMore: offset + conversations.length < totalCount,
@@ -274,6 +300,11 @@ export async function handleSessionCreate(
274
300
  ctx.socketToSession.set(socket, conversation.id);
275
301
  const sendEvent = (event: ServerMessage) => ctx.send(socket, event);
276
302
  const requestId = uuid();
303
+ const transportChannel = parseChannelId(msg.transport?.channelId) ?? 'macos';
304
+ session.setTurnChannelContext({
305
+ userMessageChannel: transportChannel,
306
+ assistantMessageChannel: transportChannel,
307
+ });
277
308
  session.processMessage(msg.initialMessage, [], sendEvent, requestId).catch((err) => {
278
309
  const message = err instanceof Error ? err.message : String(err);
279
310
  log.error({ err, sessionId: conversation.id }, 'Error processing initial message');
@@ -401,9 +432,12 @@ export function handleHistoryRequest(
401
432
  if (a.mimeType.startsWith('video/') && !a.thumbnailBase64) {
402
433
  const attachmentId = a.id;
403
434
  const base64 = a.dataBase64;
404
- generateVideoThumbnail(base64).then((thumb) => {
405
- if (thumb) setAttachmentThumbnail(attachmentId, thumb);
406
- }).catch(() => {});
435
+ silentlyWithLog(
436
+ generateVideoThumbnail(base64).then((thumb) => {
437
+ if (thumb) setAttachmentThumbnail(attachmentId, thumb);
438
+ }),
439
+ 'video thumbnail generation',
440
+ );
407
441
  }
408
442
 
409
443
  return {
@@ -539,6 +573,22 @@ export function handleDeleteQueuedMessage(
539
573
  }
540
574
  }
541
575
 
576
+ export function handleConversationSearch(
577
+ msg: ConversationSearchRequest,
578
+ socket: net.Socket,
579
+ ctx: HandlerContext,
580
+ ): void {
581
+ const results = conversationStore.searchConversations(msg.query, {
582
+ limit: msg.limit,
583
+ maxMessagesPerConversation: msg.maxMessagesPerConversation,
584
+ });
585
+ ctx.send(socket, {
586
+ type: 'conversation_search_response',
587
+ query: msg.query,
588
+ results,
589
+ });
590
+ }
591
+
542
592
  export const sessionHandlers = defineHandlers({
543
593
  user_message: handleUserMessage,
544
594
  confirmation_response: handleConfirmationResponse,
@@ -554,4 +604,5 @@ export const sessionHandlers = defineHandlers({
554
604
  regenerate: handleRegenerate,
555
605
  usage_request: handleUsageRequest,
556
606
  sandbox_set: handleSandboxSet,
607
+ conversation_search: handleConversationSearch,
557
608
  });
@@ -9,6 +9,7 @@ import type { ClientMessage, CuSessionCreate, ServerMessage, SessionTransportMet
9
9
  import type { SecretPromptResult } from '../../permissions/secret-prompter.js';
10
10
  import { getConfig } from '../../config/loader.js';
11
11
  import type { DebouncerMap } from '../../util/debounce.js';
12
+ import type { GuardianRuntimeContext } from '../session-runtime-assembly.js';
12
13
 
13
14
  const log = getLogger('handlers');
14
15
 
@@ -99,9 +100,13 @@ export interface SessionCreateOptions {
99
100
  systemPromptOverride?: string;
100
101
  maxResponseTokens?: number;
101
102
  transport?: SessionTransportMetadata;
103
+ assistantId?: string;
104
+ guardianContext?: GuardianRuntimeContext;
102
105
  memoryScopeId?: string;
103
106
  isPrivateThread?: boolean;
104
107
  strictPrivateSideEffects?: boolean;
108
+ /** Channel command intent metadata (e.g. Telegram /start). */
109
+ commandIntent?: { type: string; payload?: string; languageCode?: string };
105
110
  }
106
111
 
107
112
  /**
@@ -249,7 +254,7 @@ export function wireEscalationHandler(
249
254
  }
250
255
 
251
256
  export function isRecord(value: unknown): value is Record<string, unknown> {
252
- return typeof value === 'object' && value !== null;
257
+ return typeof value === 'object' && value != null;
253
258
  }
254
259
 
255
260
  export function formatBytes(sizeBytes: number): string {
@@ -423,7 +423,7 @@ export async function handleSkillsSearch(
423
423
  ctx: HandlerContext,
424
424
  ): Promise<void> {
425
425
  try {
426
- // Search vellum-skills catalog (remote with bundled fallback)
426
+ // Search vellum-skills catalog (platform API with bundled fallback)
427
427
  const catalogEntries = await listCatalogEntries();
428
428
  const query = (msg.query ?? '').toLowerCase();
429
429
  const matchingCatalog = catalogEntries.filter((e) => {