@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
@@ -5,6 +5,7 @@
5
5
  * before it is sent to the provider. They are pure (no side effects).
6
6
  */
7
7
 
8
+ import type { ChannelId, TurnChannelContext } from '../channels/types.js';
8
9
  import type { Message } from '../providers/types.js';
9
10
  import { listAppFiles, getAppsDir } from '../memory/app-store.js';
10
11
  import { statSync } from 'node:fs';
@@ -15,7 +16,7 @@ import { join } from 'node:path';
15
16
  * interacting. Used to gate UI-specific references and permission asks.
16
17
  */
17
18
  export interface ChannelCapabilities {
18
- /** The raw channel identifier (e.g. "dashboard", "telegram", "http-api"). */
19
+ /** The raw channel identifier (e.g. "macos", "telegram", "sms"). */
19
20
  channel: string;
20
21
  /** Whether this channel can render the dashboard UI (apps, dynamic pages). */
21
22
  dashboardCapable: boolean;
@@ -25,16 +26,49 @@ export interface ChannelCapabilities {
25
26
  supportsVoiceInput: boolean;
26
27
  }
27
28
 
29
+ /** Guardian identity/trust context for external chat channels. */
30
+ export interface GuardianRuntimeContext {
31
+ sourceChannel: ChannelId;
32
+ actorRole: 'guardian' | 'non-guardian' | 'unverified_channel';
33
+ guardianChatId?: string;
34
+ guardianExternalUserId?: string;
35
+ requesterIdentifier?: string;
36
+ requesterExternalUserId?: string;
37
+ requesterChatId?: string;
38
+ denialReason?: 'no_binding' | 'no_identity';
39
+ }
40
+
28
41
  /** Derive channel capabilities from a raw source channel identifier. */
29
42
  export function resolveChannelCapabilities(sourceChannel?: string | null): ChannelCapabilities {
30
- const channel = sourceChannel ?? 'dashboard';
31
- const isDashboard = channel === 'dashboard';
32
- return {
33
- channel,
34
- dashboardCapable: isDashboard,
35
- supportsDynamicUi: isDashboard,
36
- supportsVoiceInput: isDashboard,
37
- };
43
+ // Normalise legacy pseudo-channel IDs to canonical ChannelId values.
44
+ let channel: string;
45
+ switch (sourceChannel) {
46
+ case null:
47
+ case undefined:
48
+ case 'dashboard':
49
+ case 'http-api':
50
+ case 'mac':
51
+ channel = 'macos';
52
+ break;
53
+ default:
54
+ channel = sourceChannel;
55
+ }
56
+
57
+ switch (channel) {
58
+ case 'macos':
59
+ return { channel, dashboardCapable: true, supportsDynamicUi: true, supportsVoiceInput: true };
60
+ case 'ios':
61
+ return { channel, dashboardCapable: false, supportsDynamicUi: false, supportsVoiceInput: true };
62
+ case 'telegram':
63
+ case 'sms':
64
+ case 'voice':
65
+ case 'whatsapp':
66
+ case 'slack':
67
+ case 'email':
68
+ return { channel, dashboardCapable: false, supportsDynamicUi: false, supportsVoiceInput: false };
69
+ default:
70
+ return { channel, dashboardCapable: false, supportsDynamicUi: false, supportsVoiceInput: false };
71
+ }
38
72
  }
39
73
 
40
74
  /** Context about the active workspace surface, passed to applyRuntimeInjections. */
@@ -264,6 +298,110 @@ export function injectChannelCapabilityContext(message: Message, caps: ChannelCa
264
298
  };
265
299
  }
266
300
 
301
+ /** Channel command intent metadata (e.g. Telegram /start). */
302
+ export interface ChannelCommandContext {
303
+ type: string;
304
+ payload?: string;
305
+ languageCode?: string;
306
+ }
307
+
308
+ /**
309
+ * Prepend channel command context to the last user message so the
310
+ * model knows this turn was triggered by a channel command (e.g. /start).
311
+ */
312
+ export function injectChannelCommandContext(message: Message, ctx: ChannelCommandContext): Message {
313
+ const lines: string[] = ['<channel_command_context>'];
314
+ lines.push(`command_type: ${ctx.type}`);
315
+ if (ctx.payload) {
316
+ lines.push(`payload: ${ctx.payload}`);
317
+ }
318
+ if (ctx.languageCode) {
319
+ lines.push(`language_code: ${ctx.languageCode}`);
320
+ }
321
+ lines.push('</channel_command_context>');
322
+
323
+ const block = lines.join('\n');
324
+ return {
325
+ ...message,
326
+ content: [
327
+ { type: 'text', text: block },
328
+ ...message.content,
329
+ ],
330
+ };
331
+ }
332
+
333
+ // ---------------------------------------------------------------------------
334
+ // Channel turn context injection
335
+ // ---------------------------------------------------------------------------
336
+
337
+ /** Parameters for building the channel turn context block. */
338
+ export interface ChannelTurnContextParams {
339
+ turnContext: TurnChannelContext;
340
+ conversationOriginChannel: ChannelId | null;
341
+ }
342
+
343
+ /**
344
+ * Build the `<channel_turn_context>` text block that informs the model
345
+ * which channels are active for the current turn and the conversation's
346
+ * origin channel.
347
+ */
348
+ export function buildChannelTurnContextBlock(params: ChannelTurnContextParams): string {
349
+ const { turnContext, conversationOriginChannel } = params;
350
+ const lines: string[] = ['<channel_turn_context>'];
351
+ lines.push(`user_message_channel: ${turnContext.userMessageChannel}`);
352
+ lines.push(`assistant_message_channel: ${turnContext.assistantMessageChannel}`);
353
+ lines.push(`conversation_origin_channel: ${conversationOriginChannel ?? 'unknown'}`);
354
+ lines.push('</channel_turn_context>');
355
+ return lines.join('\n');
356
+ }
357
+
358
+ /**
359
+ * Prepend channel turn context to the last user message so the model
360
+ * knows which channels are involved in this turn.
361
+ */
362
+ export function injectChannelTurnContext(message: Message, params: ChannelTurnContextParams): Message {
363
+ const block = buildChannelTurnContextBlock(params);
364
+ return {
365
+ ...message,
366
+ content: [
367
+ { type: 'text', text: block },
368
+ ...message.content,
369
+ ],
370
+ };
371
+ }
372
+
373
+ /**
374
+ * Build the `<guardian_context>` text block used for model grounding.
375
+ */
376
+ export function buildGuardianContextBlock(ctx: GuardianRuntimeContext): string {
377
+ const lines: string[] = ['<guardian_context>'];
378
+ lines.push(`source_channel: ${ctx.sourceChannel}`);
379
+ lines.push(`actor_role: ${ctx.actorRole}`);
380
+ lines.push(`guardian_external_user_id: ${ctx.guardianExternalUserId ?? 'unknown'}`);
381
+ lines.push(`guardian_chat_id: ${ctx.guardianChatId ?? 'unknown'}`);
382
+ lines.push(`requester_identifier: ${ctx.requesterIdentifier ?? 'unknown'}`);
383
+ lines.push(`requester_external_user_id: ${ctx.requesterExternalUserId ?? 'unknown'}`);
384
+ lines.push(`requester_chat_id: ${ctx.requesterChatId ?? 'unknown'}`);
385
+ lines.push(`denial_reason: ${ctx.denialReason ?? 'none'}`);
386
+ lines.push('</guardian_context>');
387
+ return lines.join('\n');
388
+ }
389
+
390
+ /**
391
+ * Prepend guardian trust/identity facts to the last user message so the
392
+ * model can reason about guardian status from deterministic runtime facts.
393
+ */
394
+ export function injectGuardianContext(message: Message, ctx: GuardianRuntimeContext): Message {
395
+ const block = buildGuardianContextBlock(ctx);
396
+ return {
397
+ ...message,
398
+ content: [
399
+ { type: 'text', text: block },
400
+ ...message.content,
401
+ ],
402
+ };
403
+ }
404
+
267
405
  // ---------------------------------------------------------------------------
268
406
  // Prefix-based stripping primitive
269
407
  // ---------------------------------------------------------------------------
@@ -286,7 +424,7 @@ export function stripUserTextBlocksByPrefix(messages: Message[], prefixes: strin
286
424
  if (nextContent.length === message.content.length) return message;
287
425
  if (nextContent.length === 0) return null;
288
426
  return { ...message, content: nextContent };
289
- }).filter((message): message is NonNullable<typeof message> => message !== null);
427
+ }).filter((message): message is NonNullable<typeof message> => message != null);
290
428
  }
291
429
 
292
430
  // ---------------------------------------------------------------------------
@@ -298,6 +436,11 @@ export function stripChannelCapabilityContext(messages: Message[]): Message[] {
298
436
  return stripUserTextBlocksByPrefix(messages, ['<channel_capabilities>']);
299
437
  }
300
438
 
439
+ /** Strip `<guardian_context>` blocks injected by `injectGuardianContext`. */
440
+ export function stripGuardianContext(messages: Message[]): Message[] {
441
+ return stripUserTextBlocksByPrefix(messages, ['<guardian_context>']);
442
+ }
443
+
301
444
  /**
302
445
  * Prepend workspace top-level directory context to a user message.
303
446
  */
@@ -355,9 +498,22 @@ export function stripActiveSurfaceContext(messages: Message[]): Message[] {
355
498
  // Declarative strip pipeline
356
499
  // ---------------------------------------------------------------------------
357
500
 
501
+ /** Strip `<channel_command_context>` blocks injected by `injectChannelCommandContext`. */
502
+ export function stripChannelCommandContext(messages: Message[]): Message[] {
503
+ return stripUserTextBlocksByPrefix(messages, ['<channel_command_context>']);
504
+ }
505
+
506
+ /** Strip `<channel_turn_context>` blocks injected by `injectChannelTurnContext`. */
507
+ export function stripChannelTurnContext(messages: Message[]): Message[] {
508
+ return stripUserTextBlocksByPrefix(messages, ['<channel_turn_context>']);
509
+ }
510
+
358
511
  /** Prefixes stripped by the pipeline (order doesn't matter — single pass). */
359
512
  const RUNTIME_INJECTION_PREFIXES = [
360
513
  '<channel_capabilities>',
514
+ '<channel_command_context>',
515
+ '<channel_turn_context>',
516
+ '<guardian_context>',
361
517
  '<workspace_top_level>',
362
518
  TEMPORAL_INJECTED_PREFIX,
363
519
  '<active_workspace>',
@@ -398,6 +554,9 @@ export function applyRuntimeInjections(
398
554
  activeSurface?: ActiveSurfaceContext | null;
399
555
  workspaceTopLevelContext?: string | null;
400
556
  channelCapabilities?: ChannelCapabilities | null;
557
+ channelCommandContext?: ChannelCommandContext | null;
558
+ channelTurnContext?: ChannelTurnContextParams | null;
559
+ guardianContext?: GuardianRuntimeContext | null;
401
560
  temporalContext?: string | null;
402
561
  },
403
562
  ): Message[] {
@@ -433,6 +592,36 @@ export function applyRuntimeInjections(
433
592
  }
434
593
  }
435
594
 
595
+ if (options.channelCommandContext) {
596
+ const userTail = result[result.length - 1];
597
+ if (userTail && userTail.role === 'user') {
598
+ result = [
599
+ ...result.slice(0, -1),
600
+ injectChannelCommandContext(userTail, options.channelCommandContext),
601
+ ];
602
+ }
603
+ }
604
+
605
+ if (options.channelTurnContext) {
606
+ const userTail = result[result.length - 1];
607
+ if (userTail && userTail.role === 'user') {
608
+ result = [
609
+ ...result.slice(0, -1),
610
+ injectChannelTurnContext(userTail, options.channelTurnContext),
611
+ ];
612
+ }
613
+ }
614
+
615
+ if (options.guardianContext) {
616
+ const userTail = result[result.length - 1];
617
+ if (userTail && userTail.role === 'user') {
618
+ result = [
619
+ ...result.slice(0, -1),
620
+ injectGuardianContext(userTail, options.guardianContext),
621
+ ];
622
+ }
623
+ }
624
+
436
625
  // Temporal context is injected before workspace top-level so it
437
626
  // appears after workspace context in the final message content
438
627
  // (both are prepended, so later injections appear first).
@@ -17,16 +17,13 @@ import {
17
17
  getPrebuiltHomeBasePreview,
18
18
  findSeededHomeBaseApp,
19
19
  } from '../home-base/prebuilt/seed.js';
20
+ import { isPlainObject } from '../util/object.js';
20
21
 
21
22
  const log = getLogger('session-surfaces');
22
23
 
23
24
  const MAX_UNDO_DEPTH = 10;
24
25
  const TASK_PROGRESS_TEMPLATE_FIELDS = ['title', 'status', 'steps'] as const;
25
26
 
26
- function isPlainObject(value: unknown): value is Record<string, unknown> {
27
- return typeof value === 'object' && value !== null && !Array.isArray(value);
28
- }
29
-
30
27
  function normalizeCardShowData(input: Record<string, unknown>, rawData: Record<string, unknown>): CardSurfaceData {
31
28
  const normalized: Record<string, unknown> = { ...rawData };
32
29
 
@@ -128,6 +125,24 @@ export interface SurfaceSessionContext {
128
125
  onEvent: (msg: ServerMessage) => void,
129
126
  requestId?: string,
130
127
  ): Promise<string>;
128
+ /** Serialize operations on a given surface to prevent read-modify-write races. */
129
+ withSurface<T>(surfaceId: string, fn: () => T | Promise<T>): Promise<T>;
130
+ }
131
+
132
+ /**
133
+ * Per-surface async mutex using Promise chaining.
134
+ * Operations on the same surfaceId are serialized; different surfaces run concurrently.
135
+ */
136
+ export function createSurfaceMutex(): <T>(surfaceId: string, fn: () => T | Promise<T>) => Promise<T> {
137
+ const chains = new Map<string, Promise<void>>();
138
+
139
+ return <T>(surfaceId: string, fn: () => T | Promise<T>): Promise<T> => {
140
+ const prev = chains.get(surfaceId) ?? Promise.resolve();
141
+ const next = prev.then(fn, fn);
142
+ // Keep the chain alive but swallow errors so one failure doesn't block subsequent ops
143
+ chains.set(surfaceId, next.then(() => {}, () => {}));
144
+ return next;
145
+ };
131
146
  }
132
147
 
133
148
  /**
@@ -15,6 +15,7 @@ import type { SecretPrompter } from '../permissions/secret-prompter.js';
15
15
  import { addRule, findHighestPriorityRule } from '../permissions/trust-store.js';
16
16
  import { generateAllowlistOptions, generateScopeOptions, normalizeWebFetchUrl } from '../permissions/checker.js';
17
17
  import { getLogger } from '../util/logger.js';
18
+ import { isPlainObject } from '../util/object.js';
18
19
 
19
20
  const log = getLogger('session-tool-setup');
20
21
  import { getAllToolDefinitions } from '../tools/registry.js';
@@ -41,6 +42,7 @@ import { projectSkillTools, type SkillProjectionCache } from './session-skill-to
41
42
  */
42
43
  export interface ToolSetupContext extends SurfaceSessionContext {
43
44
  readonly conversationId: string;
45
+ assistantId?: string;
44
46
  currentRequestId?: string;
45
47
  workingDir: string;
46
48
  sandboxOverride?: boolean;
@@ -76,22 +78,21 @@ export function buildToolDefinitions(): ToolDefinition[] {
76
78
 
77
79
  // ── DoorDash task_progress auto-update ────────────────────────────────
78
80
 
79
- function isPlainObject(value: unknown): value is Record<string, unknown> {
80
- return typeof value === 'object' && value !== null && !Array.isArray(value);
81
- }
82
-
83
81
  interface DoordashStep { label: string; status: string; detail?: string }
84
82
 
85
83
  /**
86
- * Map a `vellum doordash <subcommand>` to the step label it corresponds to.
84
+ * Map a `doordash <subcommand>` (or `vellum doordash <subcommand>`) to the
85
+ * step label it corresponds to.
87
86
  */
88
87
  function doordashCommandToStep(cmd: string): string | null {
89
- if (/vellum doordash status\b/.test(cmd) || /vellum doordash refresh\b/.test(cmd) || /vellum doordash login\b/.test(cmd)) return 'Check session';
90
- if (/vellum doordash search\b/.test(cmd) || /vellum doordash search-items\b/.test(cmd)) return 'Search restaurants';
91
- if (/vellum doordash menu\b/.test(cmd) || /vellum doordash item\b/.test(cmd) || /vellum doordash store-search\b/.test(cmd)) return 'Browse menu';
92
- if (/vellum doordash cart\b/.test(cmd)) return 'Add to cart';
93
- if (/vellum doordash checkout\b/.test(cmd) || /vellum doordash payment-methods\b/.test(cmd)) return 'Add to cart';
94
- if (/vellum doordash order\b/.test(cmd)) return 'Place order';
88
+ // Match both standalone `doordash` and legacy `vellum doordash` prefixes
89
+ const dd = /(?:vellum )?doordash /;
90
+ if (new RegExp(dd.source + 'status\\b').test(cmd) || new RegExp(dd.source + 'refresh\\b').test(cmd) || new RegExp(dd.source + 'login\\b').test(cmd)) return 'Check session';
91
+ if (new RegExp(dd.source + 'search\\b').test(cmd) || new RegExp(dd.source + 'search-items\\b').test(cmd)) return 'Search restaurants';
92
+ if (new RegExp(dd.source + 'menu\\b').test(cmd) || new RegExp(dd.source + 'item\\b').test(cmd) || new RegExp(dd.source + 'store-search\\b').test(cmd)) return 'Browse menu';
93
+ if (new RegExp(dd.source + 'cart\\b').test(cmd)) return 'Add to cart';
94
+ if (new RegExp(dd.source + 'checkout\\b').test(cmd) || new RegExp(dd.source + 'payment-methods\\b').test(cmd)) return 'Add to cart';
95
+ if (new RegExp(dd.source + 'order\\b').test(cmd)) return 'Place order';
95
96
  return null;
96
97
  }
97
98
 
@@ -150,7 +151,8 @@ export function createToolExecutor(
150
151
  // Pre-execution: mark the current DoorDash step as in_progress when command starts
151
152
  if (name === 'bash' || name === 'host_bash') {
152
153
  const preCmd = input.command as string | undefined;
153
- if (preCmd?.includes('vellum doordash')) {
154
+ const preStepLabel = preCmd ? doordashCommandToStep(preCmd) : null;
155
+ if (preStepLabel) {
154
156
  const surfaceId = 'doordash-progress';
155
157
  const stored = ctx.surfaceState.get(surfaceId);
156
158
  if (stored && stored.surfaceType === 'card') {
@@ -158,23 +160,20 @@ export function createToolExecutor(
158
160
  if (card.template === 'task_progress' && isPlainObject(card.templateData)) {
159
161
  const steps = (card.templateData as Record<string, unknown>).steps;
160
162
  if (Array.isArray(steps)) {
161
- const stepLabel = doordashCommandToStep(preCmd);
162
- if (stepLabel) {
163
- const stepIndex = (steps as DoordashStep[]).findIndex(s => s.label === stepLabel);
164
- if (stepIndex >= 0 && (steps as DoordashStep[])[stepIndex].status !== 'in_progress') {
165
- const updatedSteps = (steps as DoordashStep[]).map((s, i) =>
166
- i === stepIndex ? { ...s, status: 'in_progress' } : s
167
- );
168
- const updatedTemplateData = { ...card.templateData as Record<string, unknown>, steps: updatedSteps };
169
- const updatedData = { ...card, templateData: updatedTemplateData };
170
- stored.data = updatedData as import('./ipc-contract.js').CardSurfaceData;
171
- ctx.sendToClient({
172
- type: 'ui_surface_update',
173
- sessionId: ctx.conversationId,
174
- surfaceId,
175
- data: updatedData,
176
- });
177
- }
163
+ const stepIndex = (steps as DoordashStep[]).findIndex(s => s.label === preStepLabel);
164
+ if (stepIndex >= 0 && (steps as DoordashStep[])[stepIndex].status !== 'in_progress') {
165
+ const updatedSteps = (steps as DoordashStep[]).map((s, i) =>
166
+ i === stepIndex ? { ...s, status: 'in_progress' } : s
167
+ );
168
+ const updatedTemplateData = { ...card.templateData as Record<string, unknown>, steps: updatedSteps };
169
+ const updatedData = { ...card, templateData: updatedTemplateData };
170
+ stored.data = updatedData as import('./ipc-contract.js').CardSurfaceData;
171
+ ctx.sendToClient({
172
+ type: 'ui_surface_update',
173
+ sessionId: ctx.conversationId,
174
+ surfaceId,
175
+ data: updatedData,
176
+ });
178
177
  }
179
178
  }
180
179
  }
@@ -186,6 +185,7 @@ export function createToolExecutor(
186
185
  workingDir: ctx.workingDir,
187
186
  sessionId: ctx.conversationId,
188
187
  conversationId: ctx.conversationId,
188
+ assistantId: ctx.assistantId,
189
189
  requestId: ctx.currentRequestId,
190
190
  taskRunId: ctx.taskRunId,
191
191
  onOutput,
@@ -310,7 +310,7 @@ export function createToolExecutor(
310
310
  // Auto-emit task_progress card on first DoorDash CLI command
311
311
  if (name === 'bash' || name === 'host_bash') {
312
312
  const cmd = input.command as string | undefined;
313
- if (cmd?.includes('vellum doordash')) {
313
+ if (cmd && doordashCommandToStep(cmd)) {
314
314
  const surfaceId = 'doordash-progress';
315
315
 
316
316
  if (!ctx.surfaceState.has(surfaceId)) {
@@ -12,7 +12,7 @@ export interface WorkspaceSessionContext {
12
12
 
13
13
  /** Refresh workspace top-level directory context if needed. */
14
14
  export function refreshWorkspaceTopLevelContextIfNeeded(ctx: WorkspaceSessionContext): void {
15
- if (!ctx.workspaceTopLevelDirty && ctx.workspaceTopLevelContext !== null) return;
15
+ if (!ctx.workspaceTopLevelDirty && ctx.workspaceTopLevelContext != null) return;
16
16
  const snapshot = scanTopLevelDirectories(ctx.workingDir);
17
17
  ctx.workspaceTopLevelContext = renderWorkspaceTopLevelContext(snapshot);
18
18
  ctx.workspaceTopLevelDirty = false;
@@ -16,6 +16,7 @@
16
16
  */
17
17
 
18
18
  import type { Message } from '../providers/types.js';
19
+ import type { TurnChannelContext } from '../channels/types.js';
19
20
  import type { ServerMessage, UsageStats, UserMessageAttachment, SurfaceType, SurfaceData } from './ipc-protocol.js';
20
21
  import { AgentLoop } from '../agent/loop.js';
21
22
  import type { Provider } from '../providers/types.js';
@@ -37,12 +38,13 @@ import { ContextWindowManager } from '../context/window-manager.js';
37
38
  import { getHookManager } from '../hooks/manager.js';
38
39
  import { ConflictGate } from './session-conflict-gate.js';
39
40
  import { MessageQueue } from './session-queue-manager.js';
40
- import type { QueueDrainReason } from './session-queue-manager.js';
41
- import type { ChannelCapabilities } from './session-runtime-assembly.js';
41
+ import type { QueueDrainReason, QueueMetrics } from './session-queue-manager.js';
42
+ import type { ChannelCapabilities, GuardianRuntimeContext } from './session-runtime-assembly.js';
42
43
  import type { AssistantAttachmentDraft } from './assistant-attachments.js';
43
44
  import {
44
45
  handleSurfaceAction as handleSurfaceActionImpl,
45
46
  handleSurfaceUndo as handleSurfaceUndoImpl,
47
+ createSurfaceMutex,
46
48
  } from './session-surfaces.js';
47
49
  import {
48
50
  undo as undoImpl,
@@ -127,19 +129,25 @@ export class Session {
127
129
  /** @internal */ currentActiveSurfaceId?: string;
128
130
  /** @internal */ currentPage?: string;
129
131
  /** @internal */ channelCapabilities?: ChannelCapabilities;
132
+ /** @internal */ guardianContext?: GuardianRuntimeContext;
133
+ /** @internal */ assistantId?: string;
134
+ /** @internal */ commandIntent?: { type: string; payload?: string; languageCode?: string };
130
135
  /** @internal */ pendingSurfaceActions = new Map<string, { surfaceType: SurfaceType }>();
131
136
  /** @internal */ lastSurfaceAction = new Map<string, { actionId: string; data?: Record<string, unknown> }>();
132
137
  /** @internal */ surfaceState = new Map<string, { surfaceType: SurfaceType; data: SurfaceData }>();
133
138
  /** @internal */ surfaceUndoStacks = new Map<string, string[]>();
139
+ /** @internal */ withSurface = createSurfaceMutex();
134
140
  /** @internal */ currentTurnSurfaces: Array<{ surfaceId: string; surfaceType: SurfaceType; title?: string; data: SurfaceData; actions?: Array<{ id: string; label: string; style?: string }>; display?: string }> = [];
135
141
  /** @internal */ onEscalateToComputerUse?: (task: string, sourceSessionId: string) => boolean;
136
142
  /** @internal */ workspaceTopLevelContext: string | null = null;
137
143
  /** @internal */ workspaceTopLevelDirty = true;
138
144
  public readonly traceEmitter: TraceEmitter;
139
145
  public memoryPolicy: SessionMemoryPolicy;
146
+ /** @internal */ streamThinking: boolean;
140
147
  /** @internal */ turnCount = 0;
141
148
  public lastAssistantAttachments: AssistantAttachmentDraft[] = [];
142
149
  public lastAttachmentWarnings: string[] = [];
150
+ /** @internal */ currentTurnChannelContext: TurnChannelContext | null = null;
143
151
 
144
152
  constructor(
145
153
  conversationId: string,
@@ -190,6 +198,7 @@ export class Session {
190
198
  );
191
199
 
192
200
  const config = getConfig();
201
+ this.streamThinking = config.thinking.streamThinking ?? false;
193
202
  const resolveTools = createResolveToolsCallback(toolDefs, this);
194
203
 
195
204
  this.agentLoop = new AgentLoop(
@@ -283,6 +292,10 @@ export class Session {
283
292
  return this.queue.length;
284
293
  }
285
294
 
295
+ getQueueMetrics(): QueueMetrics {
296
+ return this.queue.getMetrics();
297
+ }
298
+
286
299
  hasQueuedMessages(): boolean {
287
300
  return !this.queue.isEmpty;
288
301
  }
@@ -327,6 +340,26 @@ export class Session {
327
340
  this.channelCapabilities = caps ?? undefined;
328
341
  }
329
342
 
343
+ setGuardianContext(ctx: GuardianRuntimeContext | null): void {
344
+ this.guardianContext = ctx ?? undefined;
345
+ }
346
+
347
+ setAssistantId(assistantId: string | null): void {
348
+ this.assistantId = assistantId ?? undefined;
349
+ }
350
+
351
+ setCommandIntent(intent: { type: string; payload?: string; languageCode?: string } | null): void {
352
+ this.commandIntent = intent ?? undefined;
353
+ }
354
+
355
+ setTurnChannelContext(ctx: TurnChannelContext): void {
356
+ this.currentTurnChannelContext = ctx;
357
+ }
358
+
359
+ getTurnChannelContext(): TurnChannelContext | null {
360
+ return this.currentTurnChannelContext;
361
+ }
362
+
330
363
  persistUserMessage(
331
364
  content: string,
332
365
  attachments: UserMessageAttachment[],
@@ -0,0 +1,122 @@
1
+ import * as Sentry from '@sentry/node';
2
+ import { getSqlite, resetDb } from '../memory/db.js';
3
+ import { browserManager } from '../tools/browser/browser-manager.js';
4
+ import { getEnrichmentService } from '../workspace/commit-message-enrichment-service.js';
5
+ import { getLogger } from '../util/logger.js';
6
+ import type { DaemonServer } from './server.js';
7
+ import type { RuntimeHttpServer } from '../runtime/http-server.js';
8
+ import type { HeartbeatService } from '../workspace/heartbeat-service.js';
9
+ import type { AgentHeartbeatService } from '../agent-heartbeat/agent-heartbeat-service.js';
10
+ import type { QdrantManager } from '../memory/qdrant-manager.js';
11
+ import type { HookManager } from '../hooks/manager.js';
12
+
13
+ const log = getLogger('lifecycle');
14
+
15
+ export interface ShutdownDeps {
16
+ server: DaemonServer;
17
+ heartbeat: HeartbeatService;
18
+ agentHeartbeat: AgentHeartbeatService;
19
+ hookManager: HookManager;
20
+ runtimeHttp: RuntimeHttpServer | null;
21
+ scheduler: { stop(): void };
22
+ memoryWorker: { stop(): void };
23
+ qdrantManager: QdrantManager;
24
+ cleanupPidFile: () => void;
25
+ }
26
+
27
+ export function installShutdownHandlers(deps: ShutdownDeps): void {
28
+ let shuttingDown = false;
29
+
30
+ const shutdown = async () => {
31
+ if (shuttingDown) return;
32
+ shuttingDown = true;
33
+ log.info('Shutting down daemon...');
34
+
35
+ deps.hookManager.stopWatching();
36
+
37
+ // Force exit if graceful shutdown takes too long.
38
+ // Set this BEFORE awaiting heartbeat stop and triggering daemon-stop hooks
39
+ // so it covers all potentially-blocking async shutdown work.
40
+ const forceTimer = setTimeout(() => {
41
+ log.warn('Graceful shutdown timed out, forcing exit');
42
+ deps.cleanupPidFile();
43
+ process.exit(1);
44
+ }, 10_000);
45
+ forceTimer.unref();
46
+
47
+ await deps.heartbeat.stop();
48
+ await deps.agentHeartbeat.stop();
49
+
50
+ try {
51
+ await deps.hookManager.trigger('daemon-stop', { pid: process.pid });
52
+ } catch {
53
+ // Don't let hook failures block shutdown
54
+ }
55
+
56
+ // Commit any uncommitted workspace changes before stopping the server.
57
+ // This ensures no workspace state is lost during graceful shutdown.
58
+ try {
59
+ log.info({ phase: 'pre_stop' }, 'Committing pending workspace changes');
60
+ await deps.heartbeat.commitAllPending();
61
+ } catch (err) {
62
+ log.warn({ err, phase: 'pre_stop' }, 'Shutdown workspace commit failed');
63
+ }
64
+
65
+ await deps.server.stop();
66
+
67
+ // Final commit sweep: catch any writes that occurred during server.stop()
68
+ // (e.g. in-flight tool executions completing during drain).
69
+ try {
70
+ log.info({ phase: 'post_stop' }, 'Final workspace commit sweep');
71
+ await deps.heartbeat.commitAllPending();
72
+ } catch (err) {
73
+ log.warn({ err, phase: 'post_stop' }, 'Post-stop workspace commit failed');
74
+ }
75
+
76
+ // Flush in-flight enrichment jobs so shutdown commit notes are not dropped.
77
+ // The enrichment service's shutdown() drains active jobs and discards pending ones.
78
+ try {
79
+ await getEnrichmentService().shutdown();
80
+ } catch (err) {
81
+ log.warn({ err }, 'Enrichment service shutdown failed (non-fatal)');
82
+ }
83
+
84
+ if (deps.runtimeHttp) await deps.runtimeHttp.stop();
85
+ await browserManager.closeAllPages();
86
+ deps.scheduler.stop();
87
+ deps.memoryWorker.stop();
88
+ await deps.qdrantManager.stop();
89
+
90
+ // Checkpoint WAL and close SQLite so no writes are lost on exit.
91
+ // Checkpoint and close are in separate try blocks so that close()
92
+ // always runs even if checkpointing throws (e.g. SQLITE_BUSY).
93
+ try {
94
+ getSqlite().exec('PRAGMA wal_checkpoint(TRUNCATE)');
95
+ } catch (err) {
96
+ log.warn({ err }, 'WAL checkpoint failed (non-fatal)');
97
+ }
98
+ try {
99
+ resetDb();
100
+ } catch (err) {
101
+ log.warn({ err }, 'Database close failed (non-fatal)');
102
+ }
103
+
104
+ await Sentry.flush(2000);
105
+ clearTimeout(forceTimer);
106
+ deps.cleanupPidFile();
107
+ process.exit(0);
108
+ };
109
+
110
+ process.on('SIGTERM', shutdown);
111
+ process.on('SIGINT', shutdown);
112
+
113
+ process.on('unhandledRejection', (reason) => {
114
+ log.error({ err: reason }, 'Unhandled promise rejection');
115
+ Sentry.captureException(reason);
116
+ });
117
+
118
+ process.on('uncaughtException', (err) => {
119
+ log.error({ err }, 'Uncaught exception');
120
+ Sentry.captureException(err);
121
+ });
122
+ }
@@ -69,7 +69,7 @@ function normalizeAttributes(
69
69
  }
70
70
 
71
71
  function normalizeValue(value: unknown): string | number | boolean | null {
72
- if (value === null || value === undefined) return null;
72
+ if (value == null) return null;
73
73
  if (typeof value === 'boolean' || typeof value === 'number') return value;
74
74
  if (typeof value === 'string') return truncate(value, ATTRIBUTE_VALUE_MAX_LENGTH);
75
75
  // Coerce non-primitives to string