@vellumai/assistant 0.3.5 → 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 (486) hide show
  1. package/README.md +51 -0
  2. package/eslint.config.mjs +31 -0
  3. package/package.json +1 -1
  4. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  5. package/scripts/ipc/generate-swift.ts +18 -2
  6. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
  7. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  8. package/src/__tests__/browser-manager.test.ts +1 -0
  9. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  10. package/src/__tests__/call-orchestrator.test.ts +752 -271
  11. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  12. package/src/__tests__/call-recovery.test.ts +3 -0
  13. package/src/__tests__/call-routes-http.test.ts +5 -0
  14. package/src/__tests__/call-store.test.ts +3 -0
  15. package/src/__tests__/channel-approval-routes.test.ts +1260 -85
  16. package/src/__tests__/channel-approval.test.ts +37 -0
  17. package/src/__tests__/channel-approvals.test.ts +4 -65
  18. package/src/__tests__/channel-guardian.test.ts +556 -0
  19. package/src/__tests__/channel-readiness-service.test.ts +74 -7
  20. package/src/__tests__/checker.test.ts +14 -7
  21. package/src/__tests__/clarification-resolver.test.ts +44 -24
  22. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  23. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  24. package/src/__tests__/config-schema.test.ts +12 -7
  25. package/src/__tests__/context-window-manager.test.ts +30 -2
  26. package/src/__tests__/contradiction-checker.test.ts +20 -5
  27. package/src/__tests__/credential-security-invariants.test.ts +6 -2
  28. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  29. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  30. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  31. package/src/__tests__/guardian-action-store.test.ts +123 -0
  32. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  33. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  34. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  35. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  36. package/src/__tests__/handlers-twilio-config.test.ts +126 -0
  37. package/src/__tests__/intent-routing.test.ts +2 -0
  38. package/src/__tests__/ipc-snapshot.test.ts +228 -1
  39. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  40. package/src/__tests__/model-intents.test.ts +96 -0
  41. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  42. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  43. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  44. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  45. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  46. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  47. package/src/__tests__/qdrant-manager.test.ts +27 -20
  48. package/src/__tests__/relay-server.test.ts +779 -40
  49. package/src/__tests__/run-orchestrator-assistant-events.test.ts +2 -0
  50. package/src/__tests__/run-orchestrator.test.ts +20 -4
  51. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  52. package/src/__tests__/runtime-runs.test.ts +16 -0
  53. package/src/__tests__/schedule-store.test.ts +18 -4
  54. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  55. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  56. package/src/__tests__/session-agent-loop.test.ts +857 -0
  57. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  58. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  59. package/src/__tests__/session-profile-injection.test.ts +6 -0
  60. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  61. package/src/__tests__/session-queue.test.ts +6 -0
  62. package/src/__tests__/session-runtime-assembly.test.ts +237 -13
  63. package/src/__tests__/session-slash-known.test.ts +6 -0
  64. package/src/__tests__/session-slash-queue.test.ts +6 -0
  65. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  66. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  67. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  68. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  69. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  70. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  71. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  72. package/src/__tests__/skills.test.ts +2 -0
  73. package/src/__tests__/sms-messaging-provider.test.ts +2 -1
  74. package/src/__tests__/starter-task-flow.test.ts +2 -0
  75. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  76. package/src/__tests__/system-prompt.test.ts +2 -0
  77. package/src/__tests__/task-management-tools.test.ts +2 -2
  78. package/src/__tests__/task-runner.test.ts +14 -4
  79. package/src/__tests__/terminal-tools.test.ts +25 -19
  80. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  81. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  82. package/src/__tests__/tool-executor.test.ts +23 -24
  83. package/src/__tests__/trust-store.test.ts +3 -3
  84. package/src/__tests__/twilio-rest.test.ts +29 -0
  85. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  86. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  87. package/src/__tests__/twilio-routes.test.ts +141 -21
  88. package/src/__tests__/user-reference.test.ts +2 -0
  89. package/src/__tests__/voice-quality.test.ts +222 -0
  90. package/src/__tests__/web-search.test.ts +45 -29
  91. package/src/agent/loop.ts +1 -1
  92. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  93. package/src/amazon/client.ts +1418 -0
  94. package/src/amazon/request-extractor.ts +135 -0
  95. package/src/amazon/session.ts +109 -0
  96. package/src/autonomy/autonomy-store.ts +5 -5
  97. package/src/browser-extension-relay/client.ts +124 -0
  98. package/src/browser-extension-relay/protocol.ts +63 -0
  99. package/src/browser-extension-relay/server.ts +177 -0
  100. package/src/bundler/app-bundler.ts +3 -3
  101. package/src/bundler/bundle-signer.ts +1 -1
  102. package/src/bundler/signature-verifier.ts +1 -1
  103. package/src/calls/call-conversation-messages.ts +33 -0
  104. package/src/calls/call-domain.ts +106 -5
  105. package/src/calls/call-orchestrator.ts +252 -54
  106. package/src/calls/call-pointer-messages.ts +53 -0
  107. package/src/calls/call-recovery.ts +3 -8
  108. package/src/calls/call-store.ts +69 -87
  109. package/src/calls/elevenlabs-config.ts +3 -2
  110. package/src/calls/guardian-action-sweep.ts +105 -0
  111. package/src/calls/guardian-dispatch.ts +203 -0
  112. package/src/calls/guardian-question-copy.ts +133 -0
  113. package/src/calls/relay-server.ts +466 -8
  114. package/src/calls/speaker-identification.ts +1 -1
  115. package/src/calls/twilio-config.ts +7 -5
  116. package/src/calls/twilio-provider.ts +6 -4
  117. package/src/calls/twilio-rest.ts +40 -15
  118. package/src/calls/twilio-routes.ts +60 -45
  119. package/src/calls/types.ts +3 -1
  120. package/src/channels/types.ts +25 -0
  121. package/src/cli/amazon.ts +815 -0
  122. package/src/cli/config-commands.ts +2 -2
  123. package/src/cli/core-commands.ts +4 -3
  124. package/src/cli/influencer.ts +244 -0
  125. package/src/cli/map.ts +89 -6
  126. package/src/cli.ts +1 -1
  127. package/src/config/agent-schema.ts +171 -0
  128. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  129. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  130. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  131. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  132. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  133. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  134. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  135. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  136. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  137. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  138. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  139. package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
  140. package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
  141. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  142. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  143. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  144. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  145. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  146. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  147. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  148. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +7 -9
  149. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  150. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
  151. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
  152. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  153. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
  154. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
  155. package/src/config/bundled-skills/messaging/SKILL.md +12 -2
  156. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  157. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  158. package/src/config/bundled-skills/phone-calls/SKILL.md +86 -21
  159. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  160. package/src/config/bundled-tool-registry.ts +310 -0
  161. package/src/config/calls-schema.ts +181 -0
  162. package/src/config/core-schema.ts +309 -0
  163. package/src/config/defaults.ts +26 -2
  164. package/src/config/env-registry.ts +162 -0
  165. package/src/config/env.ts +175 -0
  166. package/src/config/loader.ts +6 -6
  167. package/src/config/memory-schema.ts +528 -0
  168. package/src/config/sandbox-schema.ts +55 -0
  169. package/src/config/schema.ts +156 -1137
  170. package/src/config/skill-state.ts +1 -1
  171. package/src/config/skills-schema.ts +32 -0
  172. package/src/config/skills.ts +35 -24
  173. package/src/config/system-prompt.ts +107 -56
  174. package/src/config/templates/SOUL.md +1 -1
  175. package/src/config/types.ts +1 -0
  176. package/src/config/user-reference.ts +4 -9
  177. package/src/config/vellum-skills/catalog.json +0 -7
  178. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  179. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
  180. package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
  181. package/src/context/window-manager.ts +27 -7
  182. package/src/daemon/approval-generators.ts +186 -0
  183. package/src/daemon/approved-devices-store.ts +140 -0
  184. package/src/daemon/assistant-attachments.ts +1 -1
  185. package/src/daemon/classifier.ts +35 -32
  186. package/src/daemon/config-watcher.ts +1 -1
  187. package/src/daemon/daemon-control.ts +217 -0
  188. package/src/daemon/handlers/apps.ts +2 -3
  189. package/src/daemon/handlers/config-channels.ts +158 -0
  190. package/src/daemon/handlers/config-inbox.ts +540 -0
  191. package/src/daemon/handlers/config-ingress.ts +231 -0
  192. package/src/daemon/handlers/config-integrations.ts +258 -0
  193. package/src/daemon/handlers/config-model.ts +143 -0
  194. package/src/daemon/handlers/config-parental.ts +163 -0
  195. package/src/daemon/handlers/config-scheduling.ts +172 -0
  196. package/src/daemon/handlers/config-slack.ts +92 -0
  197. package/src/daemon/handlers/config-telegram.ts +301 -0
  198. package/src/daemon/handlers/config-tools.ts +177 -0
  199. package/src/daemon/handlers/config-trust.ts +104 -0
  200. package/src/daemon/handlers/config-twilio.ts +1080 -0
  201. package/src/daemon/handlers/config.ts +53 -2463
  202. package/src/daemon/handlers/diagnostics.ts +1 -1
  203. package/src/daemon/handlers/dictation.ts +4 -6
  204. package/src/daemon/handlers/documents.ts +18 -32
  205. package/src/daemon/handlers/index.ts +9 -0
  206. package/src/daemon/handlers/misc.ts +3 -5
  207. package/src/daemon/handlers/pairing.ts +98 -0
  208. package/src/daemon/handlers/sessions.ts +54 -5
  209. package/src/daemon/handlers/shared.ts +3 -1
  210. package/src/daemon/handlers/skills.ts +1 -1
  211. package/src/daemon/handlers/twitter-auth.ts +2 -0
  212. package/src/daemon/handlers/work-items.ts +2 -2
  213. package/src/daemon/handlers/workspace-files.ts +4 -3
  214. package/src/daemon/install-cli-launchers.ts +113 -0
  215. package/src/daemon/ipc-contract/apps.ts +356 -0
  216. package/src/daemon/ipc-contract/browser.ts +74 -0
  217. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  218. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  219. package/src/daemon/ipc-contract/documents.ts +74 -0
  220. package/src/daemon/ipc-contract/inbox.ts +209 -0
  221. package/src/daemon/ipc-contract/integrations.ts +284 -0
  222. package/src/daemon/ipc-contract/memory.ts +48 -0
  223. package/src/daemon/ipc-contract/messages.ts +211 -0
  224. package/src/daemon/ipc-contract/pairing.ts +45 -0
  225. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  226. package/src/daemon/ipc-contract/schedules.ts +97 -0
  227. package/src/daemon/ipc-contract/sessions.ts +315 -0
  228. package/src/daemon/ipc-contract/shared.ts +42 -0
  229. package/src/daemon/ipc-contract/skills.ts +120 -0
  230. package/src/daemon/ipc-contract/subagents.ts +58 -0
  231. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  232. package/src/daemon/ipc-contract/trust.ts +60 -0
  233. package/src/daemon/ipc-contract/work-items.ts +225 -0
  234. package/src/daemon/ipc-contract/workspace.ts +113 -0
  235. package/src/daemon/ipc-contract-inventory.json +60 -0
  236. package/src/daemon/ipc-contract-inventory.ts +55 -29
  237. package/src/daemon/ipc-contract.ts +226 -2527
  238. package/src/daemon/ipc-protocol.ts +1 -1
  239. package/src/daemon/ipc-validate.ts +7 -0
  240. package/src/daemon/lifecycle.ts +97 -379
  241. package/src/daemon/pairing-store.ts +177 -0
  242. package/src/daemon/providers-setup.ts +43 -0
  243. package/src/daemon/ride-shotgun-handler.ts +67 -2
  244. package/src/daemon/server.ts +60 -44
  245. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  246. package/src/daemon/session-agent-loop.ts +113 -275
  247. package/src/daemon/session-dynamic-profile.ts +1 -1
  248. package/src/daemon/session-history.ts +1 -1
  249. package/src/daemon/session-media-retry.ts +1 -1
  250. package/src/daemon/session-messaging.ts +37 -2
  251. package/src/daemon/session-notifiers.ts +5 -25
  252. package/src/daemon/session-process.ts +99 -59
  253. package/src/daemon/session-queue-manager.ts +96 -4
  254. package/src/daemon/session-runtime-assembly.ts +149 -15
  255. package/src/daemon/session-surfaces.ts +19 -4
  256. package/src/daemon/session-tool-setup.ts +28 -30
  257. package/src/daemon/session-workspace.ts +1 -1
  258. package/src/daemon/session.ts +24 -1
  259. package/src/daemon/shutdown-handlers.ts +122 -0
  260. package/src/daemon/trace-emitter.ts +1 -1
  261. package/src/daemon/watch-handler.ts +36 -33
  262. package/src/doordash/cart-queries.ts +787 -0
  263. package/src/doordash/client.ts +144 -127
  264. package/src/doordash/order-queries.ts +85 -0
  265. package/src/doordash/queries.ts +10 -1308
  266. package/src/doordash/search-queries.ts +203 -0
  267. package/src/doordash/session.ts +3 -2
  268. package/src/doordash/store-queries.ts +246 -0
  269. package/src/doordash/types.ts +367 -0
  270. package/src/email/providers/agentmail.ts +2 -1
  271. package/src/email/providers/index.ts +3 -2
  272. package/src/email/service.ts +3 -2
  273. package/src/errors.ts +43 -0
  274. package/src/home-base/prebuilt/seed.ts +1 -1
  275. package/src/hooks/cli.ts +6 -5
  276. package/src/hooks/config.ts +6 -8
  277. package/src/hooks/discovery.ts +6 -5
  278. package/src/hooks/manager.ts +4 -3
  279. package/src/hooks/runner.ts +2 -2
  280. package/src/hooks/templates.ts +5 -5
  281. package/src/inbound/public-ingress-urls.ts +3 -1
  282. package/src/index.ts +4 -2
  283. package/src/influencer/client.ts +1104 -0
  284. package/src/instrument.ts +4 -3
  285. package/src/logfire.ts +4 -3
  286. package/src/memory/admin.ts +25 -35
  287. package/src/memory/attachments-store.ts +4 -7
  288. package/src/memory/channel-delivery-store.ts +30 -1
  289. package/src/memory/channel-guardian-store.ts +200 -1
  290. package/src/memory/clarification-resolver.ts +37 -33
  291. package/src/memory/conflict-store.ts +67 -61
  292. package/src/memory/contradiction-checker.ts +141 -117
  293. package/src/memory/conversation-store.ts +335 -51
  294. package/src/memory/db-connection.ts +27 -4
  295. package/src/memory/db-init.ts +121 -4
  296. package/src/memory/db.ts +14 -1
  297. package/src/memory/embedding-backend.ts +27 -5
  298. package/src/memory/embedding-ollama.ts +2 -1
  299. package/src/memory/entity-extractor.ts +38 -35
  300. package/src/memory/guardian-action-store.ts +430 -0
  301. package/src/memory/inbox-escalation-projection.ts +59 -0
  302. package/src/memory/inbox-thread-store.ts +218 -0
  303. package/src/memory/ingress-invite-store.ts +338 -0
  304. package/src/memory/ingress-member-store.ts +350 -0
  305. package/src/memory/items-extractor.ts +91 -97
  306. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  307. package/src/memory/job-handlers/media-processing.ts +11 -42
  308. package/src/memory/job-handlers/summarization.ts +32 -26
  309. package/src/memory/job-utils.ts +3 -10
  310. package/src/memory/jobs-store.ts +6 -9
  311. package/src/memory/jobs-worker.ts +51 -36
  312. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  313. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  314. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  315. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  316. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  317. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  318. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  319. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  320. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  321. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  322. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  323. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  324. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  325. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  326. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  327. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  328. package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
  329. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  330. package/src/memory/migrations/index.ts +24 -0
  331. package/src/memory/migrations/registry.ts +79 -0
  332. package/src/memory/migrations/validate-migration-state.ts +69 -0
  333. package/src/memory/qdrant-manager.ts +49 -8
  334. package/src/memory/query-builder.ts +1 -1
  335. package/src/memory/raw-query.ts +119 -0
  336. package/src/memory/recall-cache.ts +4 -1
  337. package/src/memory/retriever.ts +160 -47
  338. package/src/memory/schema-migration.ts +25 -984
  339. package/src/memory/schema.ts +130 -7
  340. package/src/memory/search/entity.ts +10 -19
  341. package/src/memory/search/lexical.ts +81 -52
  342. package/src/memory/search/ranking.ts +21 -22
  343. package/src/memory/search/semantic.ts +157 -19
  344. package/src/memory/shared-app-links-store.ts +4 -5
  345. package/src/memory/validation.ts +19 -0
  346. package/src/messaging/draft-store.ts +5 -6
  347. package/src/messaging/providers/sms/adapter.ts +3 -6
  348. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  349. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  350. package/src/messaging/providers/whatsapp/client.ts +67 -0
  351. package/src/messaging/style-analyzer.ts +5 -4
  352. package/src/messaging/thread-summarizer.ts +61 -69
  353. package/src/messaging/triage-engine.ts +62 -71
  354. package/src/migrations/config-merge.ts +53 -0
  355. package/src/migrations/data-layout.ts +68 -0
  356. package/src/migrations/data-merge.ts +33 -0
  357. package/src/migrations/hooks-merge.ts +90 -0
  358. package/src/migrations/index.ts +6 -0
  359. package/src/migrations/log.ts +23 -0
  360. package/src/migrations/skills-merge.ts +33 -0
  361. package/src/migrations/workspace-layout.ts +79 -0
  362. package/src/permissions/checker.ts +119 -11
  363. package/src/permissions/prompter.ts +14 -0
  364. package/src/permissions/shell-identity.ts +31 -1
  365. package/src/permissions/trust-store.ts +21 -1
  366. package/src/providers/anthropic/client.ts +4 -4
  367. package/src/providers/failover.ts +2 -2
  368. package/src/providers/model-intents.ts +70 -0
  369. package/src/providers/ollama/client.ts +2 -1
  370. package/src/providers/provider-send-message.ts +176 -0
  371. package/src/providers/registry.ts +71 -30
  372. package/src/providers/retry.ts +35 -1
  373. package/src/providers/types.ts +12 -1
  374. package/src/runtime/approval-conversation-turn.ts +97 -0
  375. package/src/runtime/approval-message-composer.ts +115 -5
  376. package/src/runtime/channel-approval-parser.ts +36 -2
  377. package/src/runtime/channel-approvals.ts +0 -21
  378. package/src/runtime/channel-guardian-service.ts +48 -7
  379. package/src/runtime/channel-readiness-service.ts +160 -34
  380. package/src/runtime/channel-readiness-types.ts +10 -4
  381. package/src/runtime/channel-retry-sweep.ts +184 -0
  382. package/src/runtime/guardian-context-resolver.ts +108 -0
  383. package/src/runtime/http-server.ts +275 -743
  384. package/src/runtime/http-types.ts +56 -3
  385. package/src/runtime/middleware/auth.ts +116 -0
  386. package/src/runtime/middleware/error-handler.ts +33 -0
  387. package/src/runtime/middleware/twilio-validation.ts +127 -0
  388. package/src/runtime/routes/app-routes.ts +1 -1
  389. package/src/runtime/routes/call-routes.ts +49 -6
  390. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  391. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  392. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  393. package/src/runtime/routes/channel-route-shared.ts +144 -0
  394. package/src/runtime/routes/channel-routes.ts +32 -1634
  395. package/src/runtime/routes/conversation-routes.ts +50 -7
  396. package/src/runtime/routes/events-routes.ts +2 -2
  397. package/src/runtime/routes/identity-routes.ts +126 -0
  398. package/src/runtime/routes/pairing-routes.ts +143 -0
  399. package/src/runtime/routes/run-routes.ts +15 -1
  400. package/src/runtime/run-orchestrator.ts +52 -34
  401. package/src/schedule/schedule-store.ts +36 -32
  402. package/src/schedule/scheduler.ts +3 -3
  403. package/src/security/encrypted-store.ts +5 -7
  404. package/src/security/oauth2.ts +45 -15
  405. package/src/security/parental-control-store.ts +183 -0
  406. package/src/security/secret-allowlist.ts +4 -3
  407. package/src/security/secret-scanner.ts +5 -5
  408. package/src/security/secure-keys.ts +1 -1
  409. package/src/security/token-manager.ts +3 -2
  410. package/src/services/vercel-deploy.ts +6 -2
  411. package/src/skills/tool-manifest.ts +3 -3
  412. package/src/skills/vellum-catalog-remote.ts +75 -16
  413. package/src/slack/slack-webhook.ts +2 -1
  414. package/src/swarm/orchestrator.ts +92 -1
  415. package/src/swarm/router-planner.ts +6 -9
  416. package/src/swarm/worker-prompts.ts +9 -12
  417. package/src/tasks/task-compiler.ts +19 -28
  418. package/src/tasks/task-runner.ts +1 -1
  419. package/src/tools/assets/search.ts +15 -14
  420. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  421. package/src/tools/browser/auto-navigate.ts +1 -0
  422. package/src/tools/browser/browser-execution.ts +10 -1
  423. package/src/tools/browser/browser-manager.ts +119 -4
  424. package/src/tools/browser/network-recorder.ts +5 -0
  425. package/src/tools/credentials/broker.ts +11 -2
  426. package/src/tools/credentials/metadata-store.ts +18 -14
  427. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  428. package/src/tools/credentials/vault.ts +49 -23
  429. package/src/tools/executor.ts +68 -9
  430. package/src/tools/host-terminal/cli-discover.ts +1 -1
  431. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  432. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  433. package/src/tools/network/script-proxy/server.ts +1 -1
  434. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  435. package/src/tools/network/web-fetch.ts +18 -2
  436. package/src/tools/network/web-search.ts +7 -3
  437. package/src/tools/reminder/reminder-store.ts +14 -15
  438. package/src/tools/schedule/create.ts +1 -0
  439. package/src/tools/schedule/list.ts +2 -1
  440. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  441. package/src/tools/skills/skill-script-runner.ts +24 -9
  442. package/src/tools/skills/skill-tool-factory.ts +1 -0
  443. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  444. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  445. package/src/tools/terminal/parser.ts +50 -0
  446. package/src/tools/watcher/delete.ts +6 -0
  447. package/src/tools/weather/service.ts +1 -1
  448. package/src/twitter/client.ts +190 -24
  449. package/src/twitter/session.ts +4 -3
  450. package/src/util/clipboard.ts +1 -1
  451. package/src/util/errors.ts +65 -8
  452. package/src/util/fs.ts +40 -0
  453. package/src/util/json.ts +10 -0
  454. package/src/util/log-redact.ts +189 -0
  455. package/src/util/logger.ts +19 -17
  456. package/src/util/object.ts +3 -0
  457. package/src/util/platform.ts +72 -365
  458. package/src/util/pricing.ts +1 -1
  459. package/src/util/promise-guard.ts +1 -1
  460. package/src/util/retry.ts +19 -0
  461. package/src/util/row-mapper.ts +79 -0
  462. package/src/util/silently.ts +21 -0
  463. package/src/watcher/engine.ts +5 -1
  464. package/src/watcher/provider-types.ts +20 -0
  465. package/src/watcher/providers/github.ts +156 -0
  466. package/src/watcher/providers/gmail.ts +1 -0
  467. package/src/watcher/providers/google-calendar.ts +1 -0
  468. package/src/watcher/providers/linear.ts +460 -0
  469. package/src/watcher/providers/slack.ts +1 -0
  470. package/src/work-items/work-item-runner.ts +1 -1
  471. package/src/workspace/git-service.ts +1 -1
  472. package/src/workspace/provider-commit-message-generator.ts +51 -22
  473. package/src/__tests__/call-bridge.test.ts +0 -517
  474. package/src/__tests__/session-process-bridge.test.ts +0 -244
  475. package/src/calls/call-bridge.ts +0 -168
  476. package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
  477. package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
  478. package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
  479. package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
  480. package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
  481. package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
  482. package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
  483. package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
  484. package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
  485. package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
  486. 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;
@@ -27,7 +28,7 @@ export interface ChannelCapabilities {
27
28
 
28
29
  /** Guardian identity/trust context for external chat channels. */
29
30
  export interface GuardianRuntimeContext {
30
- sourceChannel: string;
31
+ sourceChannel: ChannelId;
31
32
  actorRole: 'guardian' | 'non-guardian' | 'unverified_channel';
32
33
  guardianChatId?: string;
33
34
  guardianExternalUserId?: string;
@@ -39,14 +40,35 @@ export interface GuardianRuntimeContext {
39
40
 
40
41
  /** Derive channel capabilities from a raw source channel identifier. */
41
42
  export function resolveChannelCapabilities(sourceChannel?: string | null): ChannelCapabilities {
42
- const channel = sourceChannel ?? 'dashboard';
43
- const isDashboard = channel === 'dashboard';
44
- return {
45
- channel,
46
- dashboardCapable: isDashboard,
47
- supportsDynamicUi: isDashboard,
48
- supportsVoiceInput: isDashboard,
49
- };
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
+ }
50
72
  }
51
73
 
52
74
  /** Context about the active workspace surface, passed to applyRuntimeInjections. */
@@ -276,11 +298,82 @@ export function injectChannelCapabilityContext(message: Message, caps: ChannelCa
276
298
  };
277
299
  }
278
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
+
279
308
  /**
280
- * Prepend guardian trust/identity facts to the last user message so the
281
- * model can reason about guardian status from deterministic runtime facts.
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).
282
311
  */
283
- export function injectGuardianContext(message: Message, ctx: GuardianRuntimeContext): Message {
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 {
284
377
  const lines: string[] = ['<guardian_context>'];
285
378
  lines.push(`source_channel: ${ctx.sourceChannel}`);
286
379
  lines.push(`actor_role: ${ctx.actorRole}`);
@@ -291,8 +384,15 @@ export function injectGuardianContext(message: Message, ctx: GuardianRuntimeCont
291
384
  lines.push(`requester_chat_id: ${ctx.requesterChatId ?? 'unknown'}`);
292
385
  lines.push(`denial_reason: ${ctx.denialReason ?? 'none'}`);
293
386
  lines.push('</guardian_context>');
387
+ return lines.join('\n');
388
+ }
294
389
 
295
- const block = lines.join('\n');
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);
296
396
  return {
297
397
  ...message,
298
398
  content: [
@@ -324,7 +424,7 @@ export function stripUserTextBlocksByPrefix(messages: Message[], prefixes: strin
324
424
  if (nextContent.length === message.content.length) return message;
325
425
  if (nextContent.length === 0) return null;
326
426
  return { ...message, content: nextContent };
327
- }).filter((message): message is NonNullable<typeof message> => message !== null);
427
+ }).filter((message): message is NonNullable<typeof message> => message != null);
328
428
  }
329
429
 
330
430
  // ---------------------------------------------------------------------------
@@ -398,9 +498,21 @@ export function stripActiveSurfaceContext(messages: Message[]): Message[] {
398
498
  // Declarative strip pipeline
399
499
  // ---------------------------------------------------------------------------
400
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
+
401
511
  /** Prefixes stripped by the pipeline (order doesn't matter — single pass). */
402
512
  const RUNTIME_INJECTION_PREFIXES = [
403
513
  '<channel_capabilities>',
514
+ '<channel_command_context>',
515
+ '<channel_turn_context>',
404
516
  '<guardian_context>',
405
517
  '<workspace_top_level>',
406
518
  TEMPORAL_INJECTED_PREFIX,
@@ -442,6 +554,8 @@ export function applyRuntimeInjections(
442
554
  activeSurface?: ActiveSurfaceContext | null;
443
555
  workspaceTopLevelContext?: string | null;
444
556
  channelCapabilities?: ChannelCapabilities | null;
557
+ channelCommandContext?: ChannelCommandContext | null;
558
+ channelTurnContext?: ChannelTurnContextParams | null;
445
559
  guardianContext?: GuardianRuntimeContext | null;
446
560
  temporalContext?: string | null;
447
561
  },
@@ -478,6 +592,26 @@ export function applyRuntimeInjections(
478
592
  }
479
593
  }
480
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
+
481
615
  if (options.guardianContext) {
482
616
  const userTail = result[result.length - 1];
483
617
  if (userTail && userTail.role === 'user') {
@@ -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';
@@ -77,22 +78,21 @@ export function buildToolDefinitions(): ToolDefinition[] {
77
78
 
78
79
  // ── DoorDash task_progress auto-update ────────────────────────────────
79
80
 
80
- function isPlainObject(value: unknown): value is Record<string, unknown> {
81
- return typeof value === 'object' && value !== null && !Array.isArray(value);
82
- }
83
-
84
81
  interface DoordashStep { label: string; status: string; detail?: string }
85
82
 
86
83
  /**
87
- * 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.
88
86
  */
89
87
  function doordashCommandToStep(cmd: string): string | null {
90
- if (/vellum doordash status\b/.test(cmd) || /vellum doordash refresh\b/.test(cmd) || /vellum doordash login\b/.test(cmd)) return 'Check session';
91
- if (/vellum doordash search\b/.test(cmd) || /vellum doordash search-items\b/.test(cmd)) return 'Search restaurants';
92
- if (/vellum doordash menu\b/.test(cmd) || /vellum doordash item\b/.test(cmd) || /vellum doordash store-search\b/.test(cmd)) return 'Browse menu';
93
- if (/vellum doordash cart\b/.test(cmd)) return 'Add to cart';
94
- if (/vellum doordash checkout\b/.test(cmd) || /vellum doordash payment-methods\b/.test(cmd)) return 'Add to cart';
95
- 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';
96
96
  return null;
97
97
  }
98
98
 
@@ -151,7 +151,8 @@ export function createToolExecutor(
151
151
  // Pre-execution: mark the current DoorDash step as in_progress when command starts
152
152
  if (name === 'bash' || name === 'host_bash') {
153
153
  const preCmd = input.command as string | undefined;
154
- if (preCmd?.includes('vellum doordash')) {
154
+ const preStepLabel = preCmd ? doordashCommandToStep(preCmd) : null;
155
+ if (preStepLabel) {
155
156
  const surfaceId = 'doordash-progress';
156
157
  const stored = ctx.surfaceState.get(surfaceId);
157
158
  if (stored && stored.surfaceType === 'card') {
@@ -159,23 +160,20 @@ export function createToolExecutor(
159
160
  if (card.template === 'task_progress' && isPlainObject(card.templateData)) {
160
161
  const steps = (card.templateData as Record<string, unknown>).steps;
161
162
  if (Array.isArray(steps)) {
162
- const stepLabel = doordashCommandToStep(preCmd);
163
- if (stepLabel) {
164
- const stepIndex = (steps as DoordashStep[]).findIndex(s => s.label === stepLabel);
165
- if (stepIndex >= 0 && (steps as DoordashStep[])[stepIndex].status !== 'in_progress') {
166
- const updatedSteps = (steps as DoordashStep[]).map((s, i) =>
167
- i === stepIndex ? { ...s, status: 'in_progress' } : s
168
- );
169
- const updatedTemplateData = { ...card.templateData as Record<string, unknown>, steps: updatedSteps };
170
- const updatedData = { ...card, templateData: updatedTemplateData };
171
- stored.data = updatedData as import('./ipc-contract.js').CardSurfaceData;
172
- ctx.sendToClient({
173
- type: 'ui_surface_update',
174
- sessionId: ctx.conversationId,
175
- surfaceId,
176
- data: updatedData,
177
- });
178
- }
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
+ });
179
177
  }
180
178
  }
181
179
  }
@@ -312,7 +310,7 @@ export function createToolExecutor(
312
310
  // Auto-emit task_progress card on first DoorDash CLI command
313
311
  if (name === 'bash' || name === 'host_bash') {
314
312
  const cmd = input.command as string | undefined;
315
- if (cmd?.includes('vellum doordash')) {
313
+ if (cmd && doordashCommandToStep(cmd)) {
316
314
  const surfaceId = 'doordash-progress';
317
315
 
318
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 { QueueDrainReason, QueueMetrics } from './session-queue-manager.js';
41
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,
@@ -129,19 +131,23 @@ export class Session {
129
131
  /** @internal */ channelCapabilities?: ChannelCapabilities;
130
132
  /** @internal */ guardianContext?: GuardianRuntimeContext;
131
133
  /** @internal */ assistantId?: string;
134
+ /** @internal */ commandIntent?: { type: string; payload?: string; languageCode?: string };
132
135
  /** @internal */ pendingSurfaceActions = new Map<string, { surfaceType: SurfaceType }>();
133
136
  /** @internal */ lastSurfaceAction = new Map<string, { actionId: string; data?: Record<string, unknown> }>();
134
137
  /** @internal */ surfaceState = new Map<string, { surfaceType: SurfaceType; data: SurfaceData }>();
135
138
  /** @internal */ surfaceUndoStacks = new Map<string, string[]>();
139
+ /** @internal */ withSurface = createSurfaceMutex();
136
140
  /** @internal */ currentTurnSurfaces: Array<{ surfaceId: string; surfaceType: SurfaceType; title?: string; data: SurfaceData; actions?: Array<{ id: string; label: string; style?: string }>; display?: string }> = [];
137
141
  /** @internal */ onEscalateToComputerUse?: (task: string, sourceSessionId: string) => boolean;
138
142
  /** @internal */ workspaceTopLevelContext: string | null = null;
139
143
  /** @internal */ workspaceTopLevelDirty = true;
140
144
  public readonly traceEmitter: TraceEmitter;
141
145
  public memoryPolicy: SessionMemoryPolicy;
146
+ /** @internal */ streamThinking: boolean;
142
147
  /** @internal */ turnCount = 0;
143
148
  public lastAssistantAttachments: AssistantAttachmentDraft[] = [];
144
149
  public lastAttachmentWarnings: string[] = [];
150
+ /** @internal */ currentTurnChannelContext: TurnChannelContext | null = null;
145
151
 
146
152
  constructor(
147
153
  conversationId: string,
@@ -192,6 +198,7 @@ export class Session {
192
198
  );
193
199
 
194
200
  const config = getConfig();
201
+ this.streamThinking = config.thinking.streamThinking ?? false;
195
202
  const resolveTools = createResolveToolsCallback(toolDefs, this);
196
203
 
197
204
  this.agentLoop = new AgentLoop(
@@ -285,6 +292,10 @@ export class Session {
285
292
  return this.queue.length;
286
293
  }
287
294
 
295
+ getQueueMetrics(): QueueMetrics {
296
+ return this.queue.getMetrics();
297
+ }
298
+
288
299
  hasQueuedMessages(): boolean {
289
300
  return !this.queue.isEmpty;
290
301
  }
@@ -337,6 +348,18 @@ export class Session {
337
348
  this.assistantId = assistantId ?? undefined;
338
349
  }
339
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
+
340
363
  persistUserMessage(
341
364
  content: string,
342
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