@vellumai/assistant 0.4.52 → 0.4.54

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 (380) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/bun.lock +62 -349
  3. package/docs/architecture/integrations.md +1 -1
  4. package/docs/architecture/keychain-broker.md +91 -40
  5. package/docs/architecture/memory.md +3 -3
  6. package/docs/architecture/security.md +2 -2
  7. package/knip.json +7 -29
  8. package/package.json +2 -9
  9. package/src/__tests__/agent-loop.test.ts +1 -1
  10. package/src/__tests__/app-git-history.test.ts +0 -2
  11. package/src/__tests__/app-git-service.test.ts +1 -6
  12. package/src/__tests__/approval-cascade.test.ts +3 -2
  13. package/src/__tests__/approval-routes-http.test.ts +0 -1
  14. package/src/__tests__/asset-materialize-tool.test.ts +0 -1
  15. package/src/__tests__/asset-search-tool.test.ts +0 -1
  16. package/src/__tests__/assistant-events-sse-hardening.test.ts +0 -1
  17. package/src/__tests__/attachments-store.test.ts +0 -1
  18. package/src/__tests__/avatar-e2e.test.ts +5 -1
  19. package/src/__tests__/browser-fill-credential.test.ts +4 -6
  20. package/src/__tests__/btw-routes.test.ts +39 -0
  21. package/src/__tests__/call-controller.test.ts +0 -1
  22. package/src/__tests__/call-domain.test.ts +1 -1
  23. package/src/__tests__/call-routes-http.test.ts +1 -3
  24. package/src/__tests__/canonical-guardian-store.test.ts +33 -2
  25. package/src/__tests__/channel-guardian.test.ts +4 -4
  26. package/src/__tests__/channel-readiness-routes.test.ts +0 -1
  27. package/src/__tests__/channel-readiness-service.test.ts +1 -1
  28. package/src/__tests__/checker.test.ts +13 -11
  29. package/src/__tests__/claude-code-skill-regression.test.ts +5 -2
  30. package/src/__tests__/claude-code-tool-profiles.test.ts +7 -3
  31. package/src/__tests__/config-loader-backfill.test.ts +1 -5
  32. package/src/__tests__/config-schema.test.ts +9 -46
  33. package/src/__tests__/config-watcher.test.ts +11 -3
  34. package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -1
  35. package/src/__tests__/credential-broker-browser-fill.test.ts +27 -24
  36. package/src/__tests__/credential-broker-server-use.test.ts +76 -40
  37. package/src/__tests__/credential-security-e2e.test.ts +1 -6
  38. package/src/__tests__/credential-security-invariants.test.ts +27 -8
  39. package/src/__tests__/credential-vault-unit.test.ts +32 -16
  40. package/src/__tests__/credential-vault.test.ts +40 -28
  41. package/src/__tests__/credentials-cli.test.ts +1 -21
  42. package/src/__tests__/email-invite-adapter.test.ts +0 -1
  43. package/src/__tests__/error-handler-friendly-messages.test.ts +4 -5
  44. package/src/__tests__/fixtures/credential-security-fixtures.ts +3 -3
  45. package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -79
  46. package/src/__tests__/gateway-only-enforcement.test.ts +1 -23
  47. package/src/__tests__/guardian-action-conversation-turn.test.ts +8 -8
  48. package/src/__tests__/guardian-action-late-reply.test.ts +13 -14
  49. package/src/__tests__/guardian-action-store.test.ts +0 -57
  50. package/src/__tests__/guardian-outbound-http.test.ts +1 -1
  51. package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -3
  52. package/src/__tests__/hooks-blocking.test.ts +1 -1
  53. package/src/__tests__/hooks-config.test.ts +5 -29
  54. package/src/__tests__/hooks-discovery.test.ts +1 -1
  55. package/src/__tests__/hooks-integration.test.ts +1 -1
  56. package/src/__tests__/hooks-manager.test.ts +1 -1
  57. package/src/__tests__/hooks-runner.test.ts +1 -23
  58. package/src/__tests__/hooks-settings.test.ts +1 -1
  59. package/src/__tests__/hooks-templates.test.ts +1 -1
  60. package/src/__tests__/host-shell-tool.test.ts +0 -1
  61. package/src/__tests__/http-user-message-parity.test.ts +19 -0
  62. package/src/__tests__/integration-status.test.ts +0 -1
  63. package/src/__tests__/invite-routes-http.test.ts +0 -3
  64. package/src/__tests__/list-messages-attachments.test.ts +0 -1
  65. package/src/__tests__/llm-usage-store.test.ts +50 -0
  66. package/src/__tests__/log-export-workspace.test.ts +233 -0
  67. package/src/__tests__/managed-proxy-context.test.ts +41 -41
  68. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
  69. package/src/__tests__/media-generate-image.test.ts +9 -4
  70. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -7
  71. package/src/__tests__/memory-regressions.experimental.test.ts +4 -4
  72. package/src/__tests__/memory-regressions.test.ts +27 -28
  73. package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
  74. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -4
  75. package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1
  76. package/src/__tests__/migration-export-http.test.ts +0 -1
  77. package/src/__tests__/migration-import-commit-http.test.ts +0 -1
  78. package/src/__tests__/migration-import-preflight-http.test.ts +0 -1
  79. package/src/__tests__/migration-validate-http.test.ts +0 -1
  80. package/src/__tests__/notification-decision-fallback.test.ts +1 -1
  81. package/src/__tests__/notification-schedule-dedup.test.ts +237 -0
  82. package/src/__tests__/oauth-cli.test.ts +2 -14
  83. package/src/__tests__/oauth-store.test.ts +3 -7
  84. package/src/__tests__/oauth2-gateway-transport.test.ts +5 -4
  85. package/src/__tests__/onboarding-starter-tasks.test.ts +1 -1
  86. package/src/__tests__/onboarding-template-contract.test.ts +1 -2
  87. package/src/__tests__/openai-provider.test.ts +7 -7
  88. package/src/__tests__/platform.test.ts +14 -4
  89. package/src/__tests__/pricing.test.ts +0 -234
  90. package/src/__tests__/provider-commit-message-generator.test.ts +19 -15
  91. package/src/__tests__/provider-fail-open-selection.test.ts +67 -62
  92. package/src/__tests__/provider-managed-proxy-integration.test.ts +88 -85
  93. package/src/__tests__/provider-registry-ollama.test.ts +10 -4
  94. package/src/__tests__/public-ingress-urls.test.ts +1 -1
  95. package/src/__tests__/recording-handler.test.ts +0 -1
  96. package/src/__tests__/registry.test.ts +3 -103
  97. package/src/__tests__/relay-server.test.ts +0 -1
  98. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  99. package/src/__tests__/runtime-events-sse-parity.test.ts +0 -1
  100. package/src/__tests__/runtime-events-sse.test.ts +0 -1
  101. package/src/__tests__/script-proxy-injection-runtime.test.ts +2 -7
  102. package/src/__tests__/secret-onetime-send.test.ts +1 -6
  103. package/src/__tests__/secret-routes-managed-proxy.test.ts +6 -14
  104. package/src/__tests__/secret-scanner-executor.test.ts +0 -1
  105. package/src/__tests__/secure-keys.test.ts +241 -229
  106. package/src/__tests__/send-endpoint-busy.test.ts +0 -1
  107. package/src/__tests__/session-abort-tool-results.test.ts +3 -2
  108. package/src/__tests__/session-agent-loop-overflow.test.ts +1012 -838
  109. package/src/__tests__/session-agent-loop.test.ts +2 -2
  110. package/src/__tests__/session-confirmation-signals.test.ts +3 -2
  111. package/src/__tests__/session-error.test.ts +5 -4
  112. package/src/__tests__/session-history-web-search.test.ts +34 -9
  113. package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -7
  114. package/src/__tests__/session-pre-run-repair.test.ts +3 -2
  115. package/src/__tests__/session-provider-retry-repair.test.ts +31 -27
  116. package/src/__tests__/session-queue.test.ts +5 -5
  117. package/src/__tests__/session-runtime-assembly.test.ts +118 -0
  118. package/src/__tests__/session-slash-known.test.ts +31 -14
  119. package/src/__tests__/session-slash-queue.test.ts +3 -2
  120. package/src/__tests__/session-slash-unknown.test.ts +3 -2
  121. package/src/__tests__/session-workspace-cache-state.test.ts +3 -1
  122. package/src/__tests__/session-workspace-injection.test.ts +3 -2
  123. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -2
  124. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
  125. package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
  126. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
  127. package/src/__tests__/skillssh-registry.test.ts +21 -0
  128. package/src/__tests__/slack-channel-config.test.ts +1 -7
  129. package/src/__tests__/slack-share-routes.test.ts +1 -1
  130. package/src/__tests__/swarm-recursion.test.ts +4 -1
  131. package/src/__tests__/swarm-session-integration.test.ts +24 -14
  132. package/src/__tests__/swarm-tool.test.ts +4 -2
  133. package/src/__tests__/task-compiler.test.ts +1 -1
  134. package/src/__tests__/telegram-bot-username-resolution.test.ts +2 -4
  135. package/src/__tests__/test-support/browser-skill-harness.ts +0 -18
  136. package/src/__tests__/test-support/computer-use-skill-harness.ts +0 -23
  137. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1521 -0
  138. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  139. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  140. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
  141. package/src/__tests__/tool-executor.test.ts +1 -2
  142. package/src/__tests__/trust-store.test.ts +8 -83
  143. package/src/__tests__/twilio-config.test.ts +0 -1
  144. package/src/__tests__/twilio-provider.test.ts +0 -5
  145. package/src/__tests__/twilio-routes.test.ts +2 -3
  146. package/src/__tests__/usage-cache-backfill-migration.test.ts +10 -10
  147. package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
  148. package/src/__tests__/voice-quality.test.ts +2 -1
  149. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
  150. package/src/__tests__/web-search.test.ts +1 -1
  151. package/src/agent/loop.ts +17 -1
  152. package/src/bundler/app-bundler.ts +40 -24
  153. package/src/calls/call-controller.ts +16 -0
  154. package/src/calls/guardian-question-copy.ts +1 -1
  155. package/src/calls/relay-server.ts +29 -13
  156. package/src/calls/voice-control-protocol.ts +1 -0
  157. package/src/calls/voice-quality.ts +1 -1
  158. package/src/calls/voice-session-bridge.ts +9 -3
  159. package/src/channels/types.ts +16 -0
  160. package/src/cli/commands/bash.ts +173 -0
  161. package/src/cli/commands/doctor.ts +15 -57
  162. package/src/cli/commands/memory.ts +3 -5
  163. package/src/cli/commands/oauth/connections.ts +4 -2
  164. package/src/cli/commands/oauth/providers.ts +1 -13
  165. package/src/cli/commands/sessions.ts +1 -1
  166. package/src/cli/commands/usage.ts +359 -0
  167. package/src/cli/http-client.ts +22 -12
  168. package/src/cli/program.ts +4 -0
  169. package/src/cli/reference.ts +2 -0
  170. package/src/cli.ts +251 -181
  171. package/src/config/assistant-feature-flags.ts +0 -7
  172. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  173. package/src/config/bundled-skills/claude-code/SKILL.md +1 -1
  174. package/src/config/bundled-skills/claude-code/TOOLS.json +1 -1
  175. package/src/config/bundled-skills/gmail/SKILL.md +0 -1
  176. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +4 -3
  177. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  178. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +3 -5
  179. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -3
  180. package/src/config/bundled-skills/messaging/SKILL.md +0 -1
  181. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +1 -1
  182. package/src/config/bundled-skills/sequences/SKILL.md +0 -1
  183. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -6
  184. package/src/config/env.ts +13 -0
  185. package/src/config/feature-flag-registry.json +15 -39
  186. package/src/config/loader.ts +7 -135
  187. package/src/config/schema.ts +0 -6
  188. package/src/config/schemas/channels.ts +1 -0
  189. package/src/config/schemas/elevenlabs.ts +2 -2
  190. package/src/config/schemas/security.ts +1 -2
  191. package/src/config/skills.ts +1 -1
  192. package/src/contacts/contact-store.ts +21 -75
  193. package/src/contacts/contacts-write.ts +6 -6
  194. package/src/contacts/types.ts +2 -0
  195. package/src/context/token-estimator.ts +35 -2
  196. package/src/context/window-manager.ts +16 -2
  197. package/src/daemon/approved-devices-store.ts +0 -44
  198. package/src/daemon/classifier.ts +1 -1
  199. package/src/daemon/config-watcher.ts +35 -11
  200. package/src/daemon/context-overflow-reducer.ts +13 -2
  201. package/src/daemon/handlers/config-ingress.ts +25 -8
  202. package/src/daemon/handlers/config-model.ts +22 -16
  203. package/src/daemon/handlers/config-telegram.ts +18 -6
  204. package/src/daemon/handlers/dictation.ts +0 -429
  205. package/src/daemon/handlers/sessions.ts +4 -116
  206. package/src/daemon/handlers/skills.ts +2 -201
  207. package/src/daemon/lifecycle.ts +21 -20
  208. package/src/daemon/message-types/contacts.ts +2 -0
  209. package/src/daemon/message-types/integrations.ts +1 -0
  210. package/src/daemon/message-types/sessions.ts +2 -0
  211. package/src/daemon/parse-actual-tokens-from-error.test.ts +75 -0
  212. package/src/daemon/providers-setup.ts +1 -1
  213. package/src/daemon/server.ts +42 -5
  214. package/src/daemon/session-agent-loop-handlers.ts +1 -1
  215. package/src/daemon/session-agent-loop.ts +27 -79
  216. package/src/daemon/session-error.ts +5 -4
  217. package/src/daemon/session-process.ts +17 -10
  218. package/src/daemon/session-runtime-assembly.ts +50 -0
  219. package/src/daemon/session-slash.ts +34 -22
  220. package/src/daemon/session.ts +1 -0
  221. package/src/daemon/shutdown-handlers.ts +15 -0
  222. package/src/daemon/watch-handler.ts +2 -2
  223. package/src/email/guardrails.ts +1 -1
  224. package/src/email/service.ts +0 -5
  225. package/src/events/domain-events.ts +1 -0
  226. package/src/hooks/templates.ts +1 -1
  227. package/src/media/app-icon-generator.ts +4 -3
  228. package/src/media/avatar-router.ts +5 -4
  229. package/src/media/gemini-image-service.ts +5 -5
  230. package/src/memory/admin.ts +2 -2
  231. package/src/memory/app-git-service.ts +0 -7
  232. package/src/memory/canonical-guardian-store.ts +25 -3
  233. package/src/memory/conversation-crud.ts +1 -1
  234. package/src/memory/conversation-title-service.ts +2 -2
  235. package/src/memory/db-init.ts +12 -0
  236. package/src/memory/embedding-backend.ts +46 -33
  237. package/src/memory/external-conversation-store.ts +0 -30
  238. package/src/memory/guardian-action-store.ts +0 -31
  239. package/src/memory/guardian-approvals.ts +1 -56
  240. package/src/memory/indexer.ts +4 -3
  241. package/src/memory/items-extractor.ts +1 -1
  242. package/src/memory/job-handlers/backfill.ts +5 -2
  243. package/src/memory/job-handlers/index-maintenance.ts +2 -2
  244. package/src/memory/job-handlers/media-processing.ts +2 -2
  245. package/src/memory/job-handlers/summarization.ts +1 -1
  246. package/src/memory/job-utils.ts +1 -2
  247. package/src/memory/jobs-worker.ts +2 -2
  248. package/src/memory/llm-usage-store.ts +57 -11
  249. package/src/memory/media-store.ts +4 -535
  250. package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +2 -2
  251. package/src/memory/migrations/110-channel-guardian.ts +0 -1
  252. package/src/memory/migrations/158-channel-interaction-columns.ts +18 -0
  253. package/src/memory/migrations/159-drop-contact-interaction-columns.ts +16 -0
  254. package/src/memory/migrations/160-drop-loopback-port-column.ts +13 -0
  255. package/src/memory/migrations/index.ts +3 -0
  256. package/src/memory/published-pages-store.ts +0 -83
  257. package/src/memory/qdrant-circuit-breaker.ts +0 -8
  258. package/src/memory/retriever.test.ts +19 -12
  259. package/src/memory/retriever.ts +1 -1
  260. package/src/memory/schema/contacts.ts +2 -2
  261. package/src/memory/schema/oauth.ts +0 -1
  262. package/src/memory/search/semantic.ts +1 -8
  263. package/src/memory/shared-app-links-store.ts +0 -15
  264. package/src/messaging/registry.ts +0 -5
  265. package/src/messaging/style-analyzer.ts +1 -1
  266. package/src/notifications/copy-composer.ts +5 -13
  267. package/src/notifications/decision-engine.ts +2 -2
  268. package/src/notifications/deliveries-store.ts +0 -39
  269. package/src/notifications/guardian-question-mode.ts +6 -10
  270. package/src/notifications/preference-extractor.ts +1 -1
  271. package/src/oauth/byo-connection.test.ts +29 -20
  272. package/src/oauth/connect-orchestrator.ts +5 -3
  273. package/src/oauth/connect-types.ts +9 -2
  274. package/src/oauth/manual-token-connection.ts +9 -7
  275. package/src/oauth/oauth-store.ts +2 -8
  276. package/src/oauth/provider-behaviors.ts +11 -1
  277. package/src/oauth/seed-providers.ts +13 -5
  278. package/src/permissions/checker.ts +21 -2
  279. package/src/permissions/shell-identity.ts +0 -5
  280. package/src/permissions/trust-store.ts +0 -37
  281. package/src/prompts/__tests__/build-cli-reference-section.test.ts +1 -1
  282. package/src/prompts/system-prompt.ts +5 -14
  283. package/src/prompts/templates/BOOTSTRAP.md +1 -3
  284. package/src/providers/anthropic/client.ts +16 -8
  285. package/src/providers/managed-proxy/constants.ts +9 -11
  286. package/src/providers/managed-proxy/context.ts +14 -9
  287. package/src/providers/provider-send-message.ts +4 -52
  288. package/src/providers/registry.ts +29 -57
  289. package/src/providers/types.ts +1 -1
  290. package/src/runtime/actor-token-store.ts +0 -23
  291. package/src/runtime/auth/route-policy.ts +4 -0
  292. package/src/runtime/channel-invite-transports/telegram.ts +12 -6
  293. package/src/runtime/channel-retry-sweep.ts +6 -0
  294. package/src/runtime/http-router.ts +5 -1
  295. package/src/runtime/http-server.ts +101 -4
  296. package/src/runtime/http-types.ts +1 -0
  297. package/src/runtime/invite-instruction-generator.ts +25 -51
  298. package/src/runtime/invite-service.ts +0 -20
  299. package/src/runtime/middleware/error-handler.ts +1 -2
  300. package/src/runtime/routes/app-management-routes.ts +1 -0
  301. package/src/runtime/routes/attachment-routes.ts +1 -1
  302. package/src/runtime/routes/brain-graph-routes.ts +1 -1
  303. package/src/runtime/routes/btw-routes.ts +20 -1
  304. package/src/runtime/routes/call-routes.ts +1 -1
  305. package/src/runtime/routes/conversation-routes.ts +64 -24
  306. package/src/runtime/routes/debug-routes.ts +1 -1
  307. package/src/runtime/routes/diagnostics-routes.ts +2 -2
  308. package/src/runtime/routes/documents-routes.ts +3 -3
  309. package/src/runtime/routes/global-search-routes.ts +1 -1
  310. package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -20
  311. package/src/runtime/routes/guardian-refresh-routes.ts +0 -20
  312. package/src/runtime/routes/inbound-message-handler.ts +10 -2
  313. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -0
  314. package/src/runtime/routes/inbound-stages/edit-intercept.ts +5 -5
  315. package/src/runtime/routes/integrations/slack/share.ts +5 -5
  316. package/src/runtime/routes/log-export-routes.ts +122 -10
  317. package/src/runtime/routes/secret-routes.ts +4 -4
  318. package/src/runtime/routes/session-query-routes.ts +3 -3
  319. package/src/runtime/routes/settings-routes.ts +53 -0
  320. package/src/runtime/routes/trust-rules-routes.ts +1 -1
  321. package/src/runtime/routes/workspace-routes.ts +3 -0
  322. package/src/runtime/verification-templates.ts +1 -1
  323. package/src/security/credential-backend.ts +148 -0
  324. package/src/security/oauth2.ts +5 -5
  325. package/src/security/secret-allowlist.ts +1 -1
  326. package/src/security/secure-keys.ts +98 -160
  327. package/src/security/token-manager.ts +0 -7
  328. package/src/sequence/guardrails.ts +0 -4
  329. package/src/sequence/store.ts +1 -20
  330. package/src/sequence/types.ts +1 -36
  331. package/src/signals/bash.ts +157 -0
  332. package/src/signals/cancel.ts +69 -0
  333. package/src/signals/conversation-undo.ts +127 -0
  334. package/src/signals/trust-rule.ts +174 -0
  335. package/src/skills/clawhub.ts +5 -5
  336. package/src/skills/managed-store.ts +4 -4
  337. package/src/skills/skillssh-registry.ts +6 -1
  338. package/src/swarm/backend-claude-code.ts +6 -6
  339. package/src/swarm/worker-backend.ts +1 -1
  340. package/src/swarm/worker-runner.ts +1 -1
  341. package/src/telegram/bot-username.ts +11 -0
  342. package/src/telemetry/usage-telemetry-reporter.test.ts +366 -0
  343. package/src/telemetry/usage-telemetry-reporter.ts +181 -0
  344. package/src/tools/claude-code/claude-code.ts +6 -6
  345. package/src/tools/credentials/broker.ts +7 -5
  346. package/src/tools/credentials/vault.ts +11 -6
  347. package/src/tools/memory/handlers.test.ts +24 -26
  348. package/src/tools/memory/handlers.ts +1 -13
  349. package/src/tools/network/__tests__/web-search.test.ts +18 -86
  350. package/src/tools/network/web-search.ts +9 -15
  351. package/src/tools/registry.ts +5 -100
  352. package/src/tools/terminal/parser.ts +34 -4
  353. package/src/tools/tool-manifest.ts +0 -10
  354. package/src/usage/actors.ts +0 -12
  355. package/src/util/canonicalize-identity.ts +0 -9
  356. package/src/util/errors.ts +0 -3
  357. package/src/util/platform.ts +31 -8
  358. package/src/util/pricing.ts +0 -39
  359. package/src/watcher/constants.ts +0 -7
  360. package/src/watcher/providers/linear.ts +1 -1
  361. package/src/work-items/work-item-store.ts +4 -4
  362. package/src/workspace/commit-message-provider.ts +1 -1
  363. package/src/workspace/git-service.ts +44 -1
  364. package/src/workspace/provider-commit-message-generator.ts +11 -7
  365. package/src/__tests__/fixtures/proxy-fixtures.ts +0 -147
  366. package/src/browser-extension-relay/client.ts +0 -155
  367. package/src/contacts/index.ts +0 -18
  368. package/src/daemon/tls-certs.ts +0 -270
  369. package/src/errors.ts +0 -41
  370. package/src/events/index.ts +0 -18
  371. package/src/followups/index.ts +0 -10
  372. package/src/playbooks/index.ts +0 -10
  373. package/src/runtime/auth/index.ts +0 -44
  374. package/src/tasks/candidate-store.ts +0 -95
  375. package/src/tools/browser/api-map.ts +0 -313
  376. package/src/tools/browser/auto-navigate.ts +0 -469
  377. package/src/tools/browser/headless-browser.ts +0 -590
  378. package/src/tools/browser/recording-store.ts +0 -75
  379. package/src/tools/computer-use/registry.ts +0 -21
  380. package/src/tools/tasks/index.ts +0 -27
@@ -1,19 +1,4 @@
1
- import {
2
- createTimeout,
3
- extractToolUse,
4
- getConfiguredProvider,
5
- userMessage,
6
- } from "../../providers/provider-send-message.js";
7
- import {
8
- type ProfileResolution,
9
- resolveProfile,
10
- } from "../dictation-profile-store.js";
11
- import {
12
- applyDictionary,
13
- expandSnippets,
14
- } from "../dictation-text-processing.js";
15
1
  import type { DictationRequest } from "../message-protocol.js";
16
- import { type HandlerContext, log } from "./shared.js";
17
2
 
18
3
  // Action verbs for fast heuristic fallback (used when LLM classifier is unavailable)
19
4
  const ACTION_VERBS = [
@@ -32,29 +17,6 @@ const ACTION_VERBS = [
32
17
  "navigate",
33
18
  ];
34
19
 
35
- const DICTATION_CLASSIFICATION_TIMEOUT_MS = 5000;
36
-
37
- const MAX_WINDOW_TITLE_LENGTH = 100;
38
-
39
- /** Sanitize window title to mitigate prompt injection from attacker-controlled titles (e.g. browser tabs, Slack conversations). */
40
- function sanitizeWindowTitle(title: string | undefined): string {
41
- if (!title) return "";
42
- return title
43
- .replace(/[<>]/g, "") // strip angle brackets to prevent tag injection
44
- .slice(0, MAX_WINDOW_TITLE_LENGTH);
45
- }
46
-
47
- /** Build a delimited app metadata block so the LLM treats it as contextual data, not instructions. */
48
- function buildAppMetadataBlock(msg: DictationRequest): string {
49
- const windowTitle = sanitizeWindowTitle(msg.context.windowTitle);
50
- return [
51
- "<app_metadata>",
52
- `App: ${msg.context.appName} (${msg.context.bundleIdentifier})`,
53
- `Window: ${windowTitle}`,
54
- "</app_metadata>",
55
- ].join("\n");
56
- }
57
-
58
20
  type DictationMode = "dictation" | "command" | "action";
59
21
 
60
22
  /** Fast heuristic fallback — used when LLM classifier is unavailable or fails. */
@@ -80,394 +42,3 @@ export function detectDictationModeHeuristic(
80
42
 
81
43
  return "dictation";
82
44
  }
83
-
84
- /** Build a combined system prompt that classifies AND cleans dictation in a single LLM call. */
85
- function buildCombinedDictationPrompt(
86
- msg: DictationRequest,
87
- stylePrompt?: string,
88
- ): string {
89
- const sections = [
90
- "You are a voice input assistant. You will receive a speech transcription and must:",
91
- '1. Classify it as "dictation" (text to insert) or "action" (task for an assistant to execute)',
92
- "2. If dictation, clean up the text. If action, return the raw transcription.",
93
- "",
94
- "## Classification",
95
- 'DICTATION examples: "Hey how are you doing", "I think we should move forward with the proposal", "Dear team comma please review the attached document"',
96
- 'ACTION examples: "Message Aaron on Slack saying hey what\'s up", "Send an email to the team about the meeting", "Open Spotify and play my playlist", "Search for flights to Denver", "Create a new document in Google Docs"',
97
- "",
98
- "Key signals for ACTION: the user is addressing an assistant and asking it to DO something (send, message, open, search, create, schedule, etc.)",
99
- "Key signals for DICTATION: the user is composing text content that should be typed out as-is",
100
- `Cursor in text field: ${msg.context.cursorInTextField ? "yes" : "no"} — if yes, lean toward dictation unless the intent to command is clear.`,
101
- "",
102
- "## Cleanup Rules (for dictation mode only)",
103
- "- Fix grammar, punctuation, and capitalization",
104
- "- Remove filler words (um, uh, like, you know)",
105
- '- Rewrite vague or hedging language ("so yeah probably", "I guess maybe") into clear, confident statements',
106
- "- Maintain the speaker's intent and meaning",
107
- ];
108
-
109
- if (stylePrompt) {
110
- sections.push(
111
- "",
112
- "## User Style (HIGHEST PRIORITY)",
113
- "The user has configured these style preferences. They OVERRIDE the default tone adaptation below.",
114
- "Follow these instructions precisely — they reflect the user's personal writing voice and preferences.",
115
- "",
116
- stylePrompt,
117
- );
118
- }
119
-
120
- sections.push("", "## Tone Adaptation");
121
-
122
- if (stylePrompt) {
123
- sections.push(
124
- "Use these as fallback guidance only when the User Style above does not cover a specific aspect:",
125
- );
126
- } else {
127
- sections.push("Adapt your output tone based on the active application:");
128
- }
129
-
130
- sections.push(
131
- "- Email apps (Gmail, Mail): Professional but warm. Use proper greetings and sign-offs if appropriate.",
132
- "- Slack: Casual and conversational. Match typical chat style.",
133
- "- Code editors (VS Code, Xcode): Technical and concise. Code comments style.",
134
- "- Terminal: Command-like, terse.",
135
- "- Messages/iMessage: Very casual, texting style. Short sentences.",
136
- "- Notes/Docs: Neutral, clear writing.",
137
- "- Default: Match the user's natural voice.",
138
- "",
139
- "## Context Clues",
140
- "- Window title may contain recipient name (Slack DMs, email compose)",
141
- "- If you can identify a recipient, adapt formality to the apparent relationship",
142
- "- Maintain the user's natural voice — don't over-formalize casual speech",
143
- "- The user's writing patterns and preferences may be available from memory context — follow those when present",
144
- "",
145
- buildAppMetadataBlock(msg),
146
- );
147
-
148
- return sections.join("\n");
149
- }
150
-
151
- function buildCommandPrompt(
152
- msg: DictationRequest,
153
- stylePrompt?: string,
154
- ): string {
155
- const sections = [
156
- "You are a text transformation assistant. The user has selected text and given a voice command to transform it.",
157
- "",
158
- "## Rules",
159
- "- Apply the instruction to the selected text",
160
- "- Return ONLY the transformed text, nothing else",
161
- "- Do NOT add explanations or commentary",
162
- ];
163
-
164
- if (stylePrompt) {
165
- sections.push(
166
- "",
167
- "## User Style (HIGHEST PRIORITY)",
168
- "The user has configured these style preferences. They OVERRIDE the default tone adaptation below.",
169
- "Follow these instructions precisely — they reflect the user's personal writing voice and preferences.",
170
- "",
171
- stylePrompt,
172
- );
173
- }
174
-
175
- sections.push("", "## Tone Adaptation");
176
-
177
- if (stylePrompt) {
178
- sections.push(
179
- "Use these as fallback guidance only when the User Style above does not cover a specific aspect:",
180
- );
181
- } else {
182
- sections.push("Match the tone to the active application context:");
183
- }
184
-
185
- sections.push(
186
- "- Email apps (Gmail, Mail): Professional but warm.",
187
- "- Slack: Casual and conversational.",
188
- "- Code editors (VS Code, Xcode): Technical and concise.",
189
- "- Terminal: Command-like, terse.",
190
- "- Messages/iMessage: Very casual, texting style.",
191
- "- Notes/Docs: Neutral, clear writing.",
192
- "- Default: Match the user's natural voice.",
193
- "",
194
- "## Context Clues",
195
- "- Window title may contain recipient name (Slack DMs, email compose)",
196
- "- If you can identify a recipient, adapt formality to the apparent relationship",
197
- "- Maintain the user's natural voice — don't over-formalize casual speech",
198
- "- The user's writing patterns and preferences may be available from memory context — follow those when present",
199
- "",
200
- buildAppMetadataBlock(msg),
201
- "",
202
- "Selected text:",
203
- msg.context.selectedText ?? "",
204
- "",
205
- `Instruction: ${msg.transcription}`,
206
- );
207
-
208
- return sections.join("\n");
209
- }
210
-
211
- /** Compute dynamic max_tokens based on input length to avoid waste and truncation. */
212
- function computeMaxTokens(inputLength: number): number {
213
- const estimatedInputTokens = Math.ceil(inputLength / 3);
214
- return Math.max(256, estimatedInputTokens + 128);
215
- }
216
-
217
- export async function handleDictationRequest(
218
- msg: DictationRequest,
219
- ctx: HandlerContext,
220
- ): Promise<void> {
221
- log.info(
222
- { transcriptionLength: msg.transcription.length },
223
- "Dictation request received",
224
- );
225
-
226
- // Resolve profile for all modes (metadata is included in response)
227
- const resolution = resolveProfile(
228
- msg.context.bundleIdentifier,
229
- msg.context.appName,
230
- msg.profileId,
231
- );
232
- const { profile, source: profileSource } = resolution;
233
- log.info(
234
- { profileId: profile.id, profileSource },
235
- "Resolved dictation profile",
236
- );
237
-
238
- const profileMeta = {
239
- resolvedProfileId: profile.id,
240
- profileSource,
241
- };
242
-
243
- const stylePrompt = profile.stylePrompt || undefined;
244
-
245
- // Command mode: selected text present — deterministic, no classification needed
246
- if (msg.context.selectedText && msg.context.selectedText.trim().length > 0) {
247
- log.info({ mode: "command" }, "Command mode (selected text present)");
248
- await handleCommandMode(msg, ctx, profile, profileMeta, stylePrompt);
249
- return;
250
- }
251
-
252
- // Non-command: single LLM call that classifies AND cleans in one shot
253
- const transcription = expandSnippets(msg.transcription, profile.snippets);
254
-
255
- try {
256
- const provider = getConfiguredProvider();
257
- if (!provider) {
258
- log.warn(
259
- "Dictation: no provider available, using heuristic + raw transcription",
260
- );
261
- const mode = detectDictationModeHeuristic(msg);
262
- const normalizedText = applyDictionary(transcription, profile.dictionary);
263
- if (mode === "action") {
264
- ctx.send({
265
- type: "dictation_response",
266
- text: msg.transcription,
267
- mode: "action",
268
- actionPlan: `User wants to: ${msg.transcription}`,
269
- ...profileMeta,
270
- });
271
- } else {
272
- ctx.send({
273
- type: "dictation_response",
274
- text: normalizedText,
275
- mode,
276
- ...profileMeta,
277
- });
278
- }
279
- return;
280
- }
281
-
282
- const systemPrompt = buildCombinedDictationPrompt(msg, stylePrompt);
283
- const maxTokens = computeMaxTokens(transcription.length);
284
- const { signal, cleanup } = createTimeout(
285
- DICTATION_CLASSIFICATION_TIMEOUT_MS,
286
- );
287
-
288
- try {
289
- const response = await provider.sendMessage(
290
- [userMessage(`Transcription: "${transcription}"`)],
291
- [
292
- {
293
- name: "process_dictation",
294
- description: "Classify the voice input and return cleaned text",
295
- input_schema: {
296
- type: "object" as const,
297
- properties: {
298
- mode: {
299
- type: "string",
300
- enum: ["dictation", "action"],
301
- description:
302
- "dictation = user wants text inserted/cleaned up for typing. action = user wants the assistant to perform a task (send a message, open an app, search, navigate, control something).",
303
- },
304
- text: {
305
- type: "string",
306
- description:
307
- "If dictation: the cleaned/formatted text ready for insertion. If action: the raw transcription unchanged.",
308
- },
309
- reasoning: {
310
- type: "string",
311
- description: "Brief reasoning for the classification",
312
- },
313
- },
314
- required: ["mode", "text", "reasoning"],
315
- },
316
- },
317
- ],
318
- systemPrompt,
319
- {
320
- config: {
321
- modelIntent: "latency-optimized",
322
- max_tokens: maxTokens,
323
- tool_choice: { type: "tool" as const, name: "process_dictation" },
324
- },
325
- signal,
326
- },
327
- );
328
- cleanup();
329
-
330
- const toolBlock = extractToolUse(response);
331
- if (toolBlock) {
332
- const input = toolBlock.input as {
333
- mode?: string;
334
- text?: string;
335
- reasoning?: string;
336
- };
337
- const mode: DictationMode =
338
- input.mode === "action" ? "action" : "dictation";
339
- log.info(
340
- { mode, reasoning: input.reasoning },
341
- "LLM dictation classify+clean",
342
- );
343
-
344
- if (mode === "action") {
345
- ctx.send({
346
- type: "dictation_response",
347
- text: msg.transcription,
348
- mode: "action",
349
- actionPlan: `User wants to: ${msg.transcription}`,
350
- ...profileMeta,
351
- });
352
- } else {
353
- const cleanedText = input.text?.trim() || transcription;
354
- const normalizedText = applyDictionary(
355
- cleanedText,
356
- profile.dictionary,
357
- );
358
- ctx.send({
359
- type: "dictation_response",
360
- text: normalizedText,
361
- mode: "dictation",
362
- ...profileMeta,
363
- });
364
- }
365
- return;
366
- }
367
-
368
- // No tool_use block — fall through to heuristic
369
- log.warn("No tool_use block in combined dictation call, using heuristic");
370
- } finally {
371
- cleanup();
372
- }
373
- } catch (err) {
374
- const message = err instanceof Error ? err.message : String(err);
375
- log.warn(
376
- { err: message },
377
- "Combined dictation LLM call failed, using heuristic",
378
- );
379
- }
380
-
381
- // Heuristic fallback
382
- const fallbackMode = detectDictationModeHeuristic(msg);
383
- log.info({ mode: fallbackMode }, "Using heuristic fallback");
384
- if (fallbackMode === "action") {
385
- ctx.send({
386
- type: "dictation_response",
387
- text: msg.transcription,
388
- mode: "action",
389
- actionPlan: `User wants to: ${msg.transcription}`,
390
- ...profileMeta,
391
- });
392
- } else {
393
- const normalizedText = applyDictionary(transcription, profile.dictionary);
394
- ctx.send({
395
- type: "dictation_response",
396
- text: normalizedText,
397
- mode: fallbackMode,
398
- ...profileMeta,
399
- });
400
- }
401
- }
402
-
403
- /** Handle command mode (selected text) — separate code path, latency-optimized. */
404
- async function handleCommandMode(
405
- msg: DictationRequest,
406
- ctx: HandlerContext,
407
- profile: ReturnType<typeof resolveProfile>["profile"],
408
- profileMeta: {
409
- resolvedProfileId: string;
410
- profileSource: ProfileResolution["source"];
411
- },
412
- stylePrompt: string | undefined,
413
- ): Promise<void> {
414
- const systemPrompt = buildCommandPrompt(msg, stylePrompt);
415
- const inputLength =
416
- (msg.context.selectedText ?? "").length + msg.transcription.length;
417
- const maxTokens = Math.max(1024, computeMaxTokens(inputLength));
418
-
419
- try {
420
- const provider = getConfiguredProvider();
421
- if (!provider) {
422
- log.warn("Command mode: no provider available, returning selected text");
423
- const normalizedText = applyDictionary(
424
- msg.context.selectedText ?? msg.transcription,
425
- profile.dictionary,
426
- );
427
- ctx.send({
428
- type: "dictation_response",
429
- text: normalizedText,
430
- mode: "command",
431
- ...profileMeta,
432
- });
433
- return;
434
- }
435
-
436
- const response = await provider.sendMessage(
437
- [userMessage(msg.transcription)],
438
- [], // no tools
439
- systemPrompt,
440
- { config: { modelIntent: "latency-optimized", max_tokens: maxTokens } },
441
- );
442
-
443
- const textBlock = response.content.find((b) => b.type === "text");
444
- const cleanedText =
445
- textBlock && "text" in textBlock
446
- ? textBlock.text.trim()
447
- : (msg.context.selectedText ?? msg.transcription);
448
- const normalizedText = applyDictionary(cleanedText, profile.dictionary);
449
- ctx.send({
450
- type: "dictation_response",
451
- text: normalizedText,
452
- mode: "command",
453
- ...profileMeta,
454
- });
455
- } catch (err) {
456
- const message = err instanceof Error ? err.message : String(err);
457
- log.error({ err }, "Command mode LLM call failed, returning selected text");
458
- const normalizedText = applyDictionary(
459
- msg.context.selectedText ?? msg.transcription,
460
- profile.dictionary,
461
- );
462
- ctx.send({
463
- type: "dictation_response",
464
- text: normalizedText,
465
- mode: "command",
466
- ...profileMeta,
467
- });
468
- ctx.send({
469
- type: "error",
470
- message: `Dictation cleanup failed: ${message}`,
471
- });
472
- }
473
- }
@@ -2,7 +2,6 @@ import { v4 as uuid } from "uuid";
2
2
 
3
3
  import {
4
4
  type InterfaceId,
5
- isChannelId,
6
5
  parseChannelId,
7
6
  parseInterfaceId,
8
7
  } from "../../channels/types.js";
@@ -12,25 +11,18 @@ import {
12
11
  generateCanonicalRequestCode,
13
12
  resolveCanonicalGuardianRequest,
14
13
  } from "../../memory/canonical-guardian-store.js";
15
- import { getAttentionStateByConversationIds } from "../../memory/conversation-attention-store.js";
16
14
  import {
17
15
  batchSetDisplayOrders,
18
16
  clearAll,
19
17
  createConversation,
20
18
  getConversation,
21
- getDisplayMetaForConversations,
22
19
  updateConversationTitle,
23
20
  } from "../../memory/conversation-crud.js";
24
- import {
25
- countConversations,
26
- listConversations,
27
- } from "../../memory/conversation-queries.js";
28
21
  import {
29
22
  GENERATING_TITLE,
30
23
  queueGenerateConversationTitle,
31
24
  UNTITLED_FALLBACK,
32
25
  } from "../../memory/conversation-title-service.js";
33
- import * as externalConversationStore from "../../memory/external-conversation-store.js";
34
26
  import * as pendingInteractions from "../../runtime/pending-interactions.js";
35
27
  import { getSubagentManager } from "../../subagent/index.js";
36
28
  import { truncate } from "../../util/truncate.js";
@@ -38,7 +30,6 @@ import { HostBashProxy } from "../host-bash-proxy.js";
38
30
  import { HostCuProxy } from "../host-cu-proxy.js";
39
31
  import { HostFileProxy } from "../host-file-proxy.js";
40
32
  import type {
41
- CancelRequest,
42
33
  ConfirmationResponse,
43
34
  DeleteQueuedMessage,
44
35
  RegenerateRequest,
@@ -63,23 +54,6 @@ import {
63
54
  pendingStandaloneSecrets,
64
55
  } from "./shared.js";
65
56
 
66
- /**
67
- * Extract a valid ChannelId from a binding's sourceChannel, which may carry a
68
- * `notification:` namespace prefix (e.g. `"notification:telegram"` -> `"telegram"`).
69
- * Returns the ChannelId if valid, or null otherwise.
70
- */
71
- function parseBindingSourceChannel(
72
- sourceChannel: string,
73
- ): import("../../channels/types.js").ChannelId | null {
74
- if (isChannelId(sourceChannel)) return sourceChannel;
75
- const NOTIFICATION_PREFIX = "notification:";
76
- if (sourceChannel.startsWith(NOTIFICATION_PREFIX)) {
77
- const inner = sourceChannel.slice(NOTIFICATION_PREFIX.length);
78
- if (isChannelId(inner)) return inner;
79
- }
80
- return null;
81
- }
82
-
83
57
  export function syncCanonicalStatusFromConfirmationDecision(
84
58
  requestId: string,
85
59
  decision: ConfirmationResponse["decision"],
@@ -242,83 +216,6 @@ export function handleSecretResponse(
242
216
  );
243
217
  }
244
218
 
245
- export function handleSessionList(
246
- ctx: HandlerContext,
247
- offset = 0,
248
- limit = 50,
249
- ): void {
250
- const conversations = listConversations(limit, false, offset);
251
- const totalCount = countConversations();
252
- const conversationIds = conversations.map((c) => c.id);
253
- const bindings =
254
- externalConversationStore.getBindingsForConversations(conversationIds);
255
- const attentionStates = getAttentionStateByConversationIds(conversationIds);
256
- const displayMetas = getDisplayMetaForConversations(conversationIds);
257
- ctx.send({
258
- type: "session_list_response",
259
- sessions: conversations.map((c) => {
260
- const binding = bindings.get(c.id);
261
- const originChannel = parseChannelId(c.originChannel);
262
- const originInterface = parseInterfaceId(c.originInterface);
263
- const attn = attentionStates.get(c.id);
264
- const displayMeta = displayMetas.get(c.id);
265
- const assistantAttention = attn
266
- ? {
267
- hasUnseenLatestAssistantMessage:
268
- attn.latestAssistantMessageAt != null &&
269
- (attn.lastSeenAssistantMessageAt == null ||
270
- attn.lastSeenAssistantMessageAt <
271
- attn.latestAssistantMessageAt),
272
- ...(attn.latestAssistantMessageAt != null
273
- ? { latestAssistantMessageAt: attn.latestAssistantMessageAt }
274
- : {}),
275
- ...(attn.lastSeenAssistantMessageAt != null
276
- ? { lastSeenAssistantMessageAt: attn.lastSeenAssistantMessageAt }
277
- : {}),
278
- ...(attn.lastSeenConfidence != null
279
- ? { lastSeenConfidence: attn.lastSeenConfidence }
280
- : {}),
281
- ...(attn.lastSeenSignalType != null
282
- ? { lastSeenSignalType: attn.lastSeenSignalType }
283
- : {}),
284
- }
285
- : undefined;
286
- return {
287
- id: c.id,
288
- title: c.title ?? "Untitled",
289
- createdAt: c.createdAt,
290
- updatedAt: c.updatedAt,
291
- threadType: normalizeThreadType(c.threadType),
292
- source: c.source ?? "user",
293
- ...(binding && parseBindingSourceChannel(binding.sourceChannel)
294
- ? {
295
- channelBinding: {
296
- sourceChannel: parseBindingSourceChannel(
297
- binding.sourceChannel,
298
- )!,
299
- externalChatId: binding.externalChatId,
300
- externalUserId: binding.externalUserId,
301
- displayName: binding.displayName,
302
- username: binding.username,
303
- },
304
- }
305
- : {}),
306
- ...(c.scheduleJobId ? { scheduleJobId: c.scheduleJobId } : {}),
307
- ...(originChannel ? { conversationOriginChannel: originChannel } : {}),
308
- ...(originInterface
309
- ? { conversationOriginInterface: originInterface }
310
- : {}),
311
- ...(assistantAttention ? { assistantAttention } : {}),
312
- ...(displayMeta?.displayOrder != null
313
- ? { displayOrder: displayMeta.displayOrder }
314
- : {}),
315
- ...(displayMeta?.isPinned ? { isPinned: displayMeta.isPinned } : {}),
316
- };
317
- }),
318
- hasMore: offset + conversations.length < totalCount,
319
- });
320
- }
321
-
322
219
  /**
323
220
  * Clear all sessions and DB conversations. Returns the number of sessions cleared.
324
221
  */
@@ -332,11 +229,6 @@ export function clearAllSessions(ctx: HandlerContext): number {
332
229
  return cleared;
333
230
  }
334
231
 
335
- export function handleSessionsClear(ctx: HandlerContext): void {
336
- const cleared = clearAllSessions(ctx);
337
- ctx.send({ type: "sessions_clear_response", cleared });
338
- }
339
-
340
232
  export async function handleSessionCreate(
341
233
  msg: SessionCreateRequest,
342
234
  ctx: HandlerContext,
@@ -570,13 +462,6 @@ export function cancelGeneration(
570
462
  return true;
571
463
  }
572
464
 
573
- export function handleCancel(msg: CancelRequest, ctx: HandlerContext): void {
574
- const sessionId = msg.sessionId;
575
- if (sessionId) {
576
- cancelGeneration(sessionId, ctx);
577
- }
578
- }
579
-
580
465
  /**
581
466
  * Undo the last message in a session. Returns the removed count, or null if
582
467
  * the conversation does not exist. Restores evicted sessions from the database.
@@ -594,7 +479,10 @@ export async function undoLastMessage(
594
479
  return { removedCount };
595
480
  }
596
481
 
597
- export async function handleUndo(msg: UndoRequest, ctx: HandlerContext): Promise<void> {
482
+ export async function handleUndo(
483
+ msg: UndoRequest,
484
+ ctx: HandlerContext,
485
+ ): Promise<void> {
598
486
  const result = await undoLastMessage(msg.sessionId, ctx);
599
487
  if (!result) {
600
488
  ctx.send({ type: "error", message: "No active session" });