@vellumai/assistant 0.4.49 → 0.4.51

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 (353) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/integrations.md +2 -2
  4. package/docs/architecture/keychain-broker.md +6 -6
  5. package/docs/architecture/memory.md +180 -119
  6. package/knip.json +32 -0
  7. package/package.json +3 -2
  8. package/src/__tests__/agent-loop.test.ts +3 -1
  9. package/src/__tests__/anthropic-provider.test.ts +114 -23
  10. package/src/__tests__/approval-cascade.test.ts +1 -15
  11. package/src/__tests__/approval-routes-http.test.ts +2 -0
  12. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  13. package/src/__tests__/btw-routes.test.ts +61 -5
  14. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  15. package/src/__tests__/checker.test.ts +13 -0
  16. package/src/__tests__/config-schema.test.ts +1 -68
  17. package/src/__tests__/config-watcher.test.ts +8 -0
  18. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  19. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  20. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  21. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  22. package/src/__tests__/credential-security-invariants.test.ts +8 -7
  23. package/src/__tests__/credential-vault-unit.test.ts +23 -18
  24. package/src/__tests__/credential-vault.test.ts +30 -18
  25. package/src/__tests__/credentials-cli.test.ts +257 -82
  26. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  27. package/src/__tests__/date-context.test.ts +93 -77
  28. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  29. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  30. package/src/__tests__/history-repair.test.ts +245 -0
  31. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  32. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  33. package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
  34. package/src/__tests__/integration-status.test.ts +31 -30
  35. package/src/__tests__/invite-redemption-service.test.ts +166 -13
  36. package/src/__tests__/invite-routes-http.test.ts +166 -5
  37. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  38. package/src/__tests__/list-messages-attachments.test.ts +193 -0
  39. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  40. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  41. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  42. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  43. package/src/__tests__/memory-regressions.test.ts +477 -2841
  44. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  45. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  46. package/src/__tests__/mime-builder.test.ts +28 -0
  47. package/src/__tests__/native-web-search.test.ts +1 -0
  48. package/src/__tests__/oauth-cli.test.ts +824 -31
  49. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  50. package/src/__tests__/oauth-store.test.ts +363 -17
  51. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  52. package/src/__tests__/registry.test.ts +0 -1
  53. package/src/__tests__/relay-server.test.ts +55 -1
  54. package/src/__tests__/schedule-tools.test.ts +32 -0
  55. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  56. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  57. package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
  58. package/src/__tests__/secure-keys.test.ts +78 -18
  59. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  60. package/src/__tests__/server-history-render.test.ts +2 -2
  61. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  62. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  63. package/src/__tests__/session-agent-loop.test.ts +19 -15
  64. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  65. package/src/__tests__/session-error.test.ts +124 -2
  66. package/src/__tests__/session-history-web-search.test.ts +918 -0
  67. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  68. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  69. package/src/__tests__/session-queue.test.ts +37 -27
  70. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  71. package/src/__tests__/session-slash-known.test.ts +1 -15
  72. package/src/__tests__/session-slash-queue.test.ts +1 -15
  73. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  74. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  75. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  76. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  77. package/src/__tests__/skills-install-extract.test.ts +93 -0
  78. package/src/__tests__/skills.test.ts +2 -2
  79. package/src/__tests__/skillssh-registry.test.ts +451 -0
  80. package/src/__tests__/slack-channel-config.test.ts +10 -8
  81. package/src/__tests__/trust-store.test.ts +15 -0
  82. package/src/__tests__/twilio-config.test.ts +11 -10
  83. package/src/__tests__/twilio-provider.test.ts +9 -4
  84. package/src/__tests__/voice-invite-redemption.test.ts +85 -5
  85. package/src/agent/ax-tree-compaction.test.ts +51 -0
  86. package/src/agent/loop.ts +39 -12
  87. package/src/approvals/AGENTS.md +1 -1
  88. package/src/approvals/guardian-request-resolvers.ts +14 -2
  89. package/src/bundler/compiler-tools.ts +66 -2
  90. package/src/calls/call-domain.ts +134 -3
  91. package/src/calls/call-store.ts +6 -0
  92. package/src/calls/relay-server.ts +44 -6
  93. package/src/calls/relay-setup-router.ts +17 -1
  94. package/src/calls/twilio-config.ts +5 -4
  95. package/src/calls/twilio-provider.ts +14 -9
  96. package/src/calls/twilio-rest.ts +10 -7
  97. package/src/calls/types.ts +3 -1
  98. package/src/cli/commands/config.ts +14 -9
  99. package/src/cli/commands/contacts.ts +3 -0
  100. package/src/cli/commands/credentials.ts +170 -174
  101. package/src/cli/commands/doctor.ts +11 -8
  102. package/src/cli/commands/keys.ts +9 -9
  103. package/src/cli/commands/mcp.ts +46 -59
  104. package/src/cli/commands/memory.ts +16 -165
  105. package/src/cli/commands/oauth/apps.ts +68 -10
  106. package/src/cli/commands/oauth/connections.ts +475 -105
  107. package/src/cli/commands/oauth/index.ts +3 -3
  108. package/src/cli/commands/oauth/providers.ts +18 -4
  109. package/src/cli/commands/sessions.ts +5 -2
  110. package/src/cli/commands/skills.ts +173 -1
  111. package/src/cli/http-client.ts +0 -20
  112. package/src/cli/main-screen.tsx +2 -2
  113. package/src/cli/program.ts +5 -6
  114. package/src/cli.ts +20 -22
  115. package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
  116. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  117. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  118. package/src/config/bundled-skills/contacts/SKILL.md +35 -11
  119. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  120. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  121. package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
  122. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
  123. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
  124. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
  125. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
  126. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
  127. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
  128. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
  129. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
  130. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
  131. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
  132. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
  133. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
  134. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
  135. package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
  136. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
  137. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
  138. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
  139. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
  140. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
  141. package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
  142. package/src/config/bundled-skills/messaging/SKILL.md +1 -1
  143. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  144. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  145. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
  146. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
  147. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
  148. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
  149. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
  150. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
  151. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
  152. package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
  153. package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
  154. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
  155. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
  156. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
  157. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
  158. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
  159. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
  160. package/src/config/bundled-tool-registry.ts +2 -5
  161. package/src/config/loader.ts +6 -42
  162. package/src/config/schema.ts +1 -12
  163. package/src/config/schemas/memory-lifecycle.ts +0 -9
  164. package/src/config/schemas/memory-processing.ts +0 -180
  165. package/src/config/schemas/memory-retrieval.ts +32 -104
  166. package/src/config/schemas/memory.ts +0 -10
  167. package/src/config/types.ts +0 -4
  168. package/src/contacts/contact-store.ts +39 -2
  169. package/src/contacts/contacts-write.ts +9 -0
  170. package/src/context/window-manager.ts +4 -1
  171. package/src/daemon/config-watcher.ts +55 -2
  172. package/src/daemon/daemon-control.ts +1 -1
  173. package/src/daemon/date-context.ts +114 -31
  174. package/src/daemon/handlers/config-ingress.ts +2 -2
  175. package/src/daemon/handlers/config-slack-channel.ts +59 -39
  176. package/src/daemon/handlers/config-telegram.ts +23 -14
  177. package/src/daemon/handlers/session-history.ts +1 -358
  178. package/src/daemon/handlers/sessions.ts +18 -13
  179. package/src/daemon/handlers/shared.ts +3 -17
  180. package/src/daemon/handlers/skills.ts +20 -1
  181. package/src/daemon/history-repair.ts +72 -8
  182. package/src/daemon/host-cu-proxy.ts +55 -26
  183. package/src/daemon/lifecycle.ts +39 -4
  184. package/src/daemon/mcp-reload-service.ts +2 -2
  185. package/src/daemon/message-types/computer-use.ts +1 -12
  186. package/src/daemon/message-types/memory.ts +4 -16
  187. package/src/daemon/message-types/messages.ts +1 -0
  188. package/src/daemon/message-types/sessions.ts +4 -42
  189. package/src/daemon/server.ts +6 -1
  190. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  191. package/src/daemon/session-agent-loop.ts +334 -48
  192. package/src/daemon/session-error.ts +89 -6
  193. package/src/daemon/session-history.ts +17 -7
  194. package/src/daemon/session-media-retry.ts +6 -2
  195. package/src/daemon/session-memory.ts +69 -149
  196. package/src/daemon/session-process.ts +10 -1
  197. package/src/daemon/session-runtime-assembly.ts +49 -19
  198. package/src/daemon/session-slash.ts +3 -5
  199. package/src/daemon/session-surfaces.ts +4 -1
  200. package/src/daemon/session-tool-setup.ts +7 -1
  201. package/src/daemon/session.ts +12 -2
  202. package/src/email/providers/index.ts +2 -2
  203. package/src/instrument.ts +61 -1
  204. package/src/media/avatar-router.ts +1 -1
  205. package/src/memory/admin.ts +2 -191
  206. package/src/memory/canonical-guardian-store.ts +38 -2
  207. package/src/memory/conversation-crud.ts +0 -33
  208. package/src/memory/conversation-queries.ts +25 -83
  209. package/src/memory/db-init.ts +32 -0
  210. package/src/memory/embedding-backend.ts +84 -8
  211. package/src/memory/embedding-types.ts +9 -1
  212. package/src/memory/indexer.ts +7 -46
  213. package/src/memory/invite-store.ts +19 -0
  214. package/src/memory/items-extractor.ts +274 -76
  215. package/src/memory/job-handlers/backfill.ts +2 -127
  216. package/src/memory/job-handlers/cleanup.ts +2 -16
  217. package/src/memory/job-handlers/extraction.ts +2 -138
  218. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  219. package/src/memory/job-handlers/summarization.ts +3 -148
  220. package/src/memory/job-utils.ts +21 -59
  221. package/src/memory/jobs-store.ts +1 -159
  222. package/src/memory/jobs-worker.ts +9 -52
  223. package/src/memory/migrations/104-core-indexes.ts +3 -3
  224. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  225. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  226. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  227. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  228. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  229. package/src/memory/migrations/154-drop-fts.ts +20 -0
  230. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  231. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  232. package/src/memory/migrations/157-invite-contact-id.ts +104 -0
  233. package/src/memory/migrations/index.ts +8 -0
  234. package/src/memory/migrations/registry.ts +6 -0
  235. package/src/memory/qdrant-client.ts +148 -51
  236. package/src/memory/raw-query.ts +1 -1
  237. package/src/memory/retriever.test.ts +294 -273
  238. package/src/memory/retriever.ts +421 -645
  239. package/src/memory/schema/calls.ts +2 -0
  240. package/src/memory/schema/contacts.ts +1 -0
  241. package/src/memory/schema/memory-core.ts +3 -48
  242. package/src/memory/schema/oauth.ts +2 -0
  243. package/src/memory/search/formatting.ts +263 -176
  244. package/src/memory/search/lexical.ts +1 -254
  245. package/src/memory/search/ranking.ts +0 -455
  246. package/src/memory/search/semantic.ts +100 -14
  247. package/src/memory/search/staleness.ts +47 -0
  248. package/src/memory/search/tier-classifier.ts +21 -0
  249. package/src/memory/search/types.ts +15 -77
  250. package/src/memory/task-memory-cleanup.ts +4 -6
  251. package/src/messaging/provider.ts +1 -1
  252. package/src/messaging/providers/gmail/adapter.ts +1 -1
  253. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  254. package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
  255. package/src/messaging/providers/whatsapp/adapter.ts +13 -9
  256. package/src/messaging/registry.ts +9 -5
  257. package/src/oauth/byo-connection.test.ts +40 -25
  258. package/src/oauth/connect-orchestrator.ts +4 -10
  259. package/src/oauth/connection-resolver.ts +20 -6
  260. package/src/oauth/manual-token-connection.ts +5 -5
  261. package/src/oauth/oauth-store.ts +183 -31
  262. package/src/oauth/platform-connection.test.ts +1 -1
  263. package/src/oauth/provider-behaviors.ts +503 -4
  264. package/src/oauth/seed-providers.ts +214 -8
  265. package/src/oauth/token-persistence.ts +31 -16
  266. package/src/permissions/defaults.ts +1 -0
  267. package/src/permissions/trust-store.ts +23 -1
  268. package/src/playbooks/playbook-compiler.ts +1 -1
  269. package/src/prompts/system-prompt.ts +18 -2
  270. package/src/providers/anthropic/client.ts +56 -126
  271. package/src/providers/types.ts +7 -1
  272. package/src/runtime/AGENTS.md +9 -0
  273. package/src/runtime/auth/route-policy.ts +6 -3
  274. package/src/runtime/channel-readiness-service.ts +48 -40
  275. package/src/runtime/guardian-reply-router.ts +24 -22
  276. package/src/runtime/http-server.ts +2 -2
  277. package/src/runtime/http-types.ts +2 -0
  278. package/src/runtime/invite-redemption-service.ts +72 -12
  279. package/src/runtime/invite-service.ts +43 -0
  280. package/src/runtime/middleware/twilio-validation.ts +1 -1
  281. package/src/runtime/pending-interactions.ts +2 -2
  282. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  283. package/src/runtime/routes/btw-routes.ts +10 -5
  284. package/src/runtime/routes/conversation-routes.ts +56 -11
  285. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  286. package/src/runtime/routes/integrations/slack/channel.ts +2 -2
  287. package/src/runtime/routes/integrations/telegram.ts +2 -2
  288. package/src/runtime/routes/integrations/twilio.ts +17 -17
  289. package/src/runtime/routes/invite-routes.ts +29 -4
  290. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  291. package/src/runtime/routes/memory-item-routes.ts +503 -0
  292. package/src/runtime/routes/secret-routes.ts +17 -0
  293. package/src/runtime/routes/session-management-routes.ts +3 -3
  294. package/src/runtime/routes/settings-routes.ts +3 -3
  295. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  296. package/src/runtime/routes/workspace-routes.ts +9 -4
  297. package/src/runtime/routes/workspace-utils.ts +8 -2
  298. package/src/schedule/integration-status.ts +26 -19
  299. package/src/security/keychain-broker-client.ts +17 -4
  300. package/src/security/oauth2.ts +6 -7
  301. package/src/security/secure-keys.ts +44 -19
  302. package/src/security/token-manager.ts +46 -39
  303. package/src/services/vercel-deploy.ts +0 -24
  304. package/src/signals/confirm.ts +78 -0
  305. package/src/signals/mcp-reload.ts +18 -0
  306. package/src/skills/catalog-install.ts +74 -18
  307. package/src/skills/skillssh-registry.ts +503 -0
  308. package/src/tools/assets/search.ts +5 -1
  309. package/src/tools/computer-use/definitions.ts +0 -10
  310. package/src/tools/computer-use/registry.ts +1 -1
  311. package/src/tools/credentials/vault.ts +22 -7
  312. package/src/tools/memory/definitions.ts +4 -13
  313. package/src/tools/memory/handlers.test.ts +83 -103
  314. package/src/tools/memory/handlers.ts +50 -85
  315. package/src/tools/network/script-proxy/session-manager.ts +8 -8
  316. package/src/tools/schedule/create.ts +10 -3
  317. package/src/tools/schedule/update.ts +8 -1
  318. package/src/tools/skills/load.ts +25 -2
  319. package/src/watcher/provider-types.ts +1 -1
  320. package/src/watcher/providers/github.ts +1 -1
  321. package/src/watcher/providers/gmail.ts +3 -3
  322. package/src/watcher/providers/google-calendar.ts +3 -3
  323. package/src/watcher/providers/linear.ts +1 -1
  324. package/src/__tests__/clarification-resolver.test.ts +0 -193
  325. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  326. package/src/__tests__/conflict-policy.test.ts +0 -269
  327. package/src/__tests__/conflict-store.test.ts +0 -372
  328. package/src/__tests__/contradiction-checker.test.ts +0 -361
  329. package/src/__tests__/entity-extractor.test.ts +0 -211
  330. package/src/__tests__/entity-search.test.ts +0 -1117
  331. package/src/__tests__/profile-compiler.test.ts +0 -392
  332. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  333. package/src/__tests__/session-profile-injection.test.ts +0 -557
  334. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  335. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  336. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  337. package/src/daemon/session-conflict-gate.ts +0 -167
  338. package/src/daemon/session-dynamic-profile.ts +0 -77
  339. package/src/memory/clarification-resolver.ts +0 -417
  340. package/src/memory/conflict-intent.ts +0 -205
  341. package/src/memory/conflict-policy.ts +0 -127
  342. package/src/memory/conflict-store.ts +0 -410
  343. package/src/memory/contradiction-checker.ts +0 -508
  344. package/src/memory/entity-extractor.ts +0 -535
  345. package/src/memory/format-recall.ts +0 -47
  346. package/src/memory/fts-reconciler.ts +0 -165
  347. package/src/memory/job-handlers/conflict.ts +0 -200
  348. package/src/memory/profile-compiler.ts +0 -195
  349. package/src/memory/recall-cache.ts +0 -117
  350. package/src/memory/search/entity.ts +0 -535
  351. package/src/memory/search/query-expansion.test.ts +0 -70
  352. package/src/memory/search/query-expansion.ts +0 -118
  353. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -33,10 +33,6 @@ const WEEKDAY_NAMES = [
33
33
  "Friday",
34
34
  "Saturday",
35
35
  ] as const;
36
- const TIMEZONE_SUBJECT_LINE_RE = /^\s*-\s*time\s*zone\s*:\s*(.+)$/i;
37
- const TIMEZONE_SUBJECT_COMPACT_RE = /^\s*-\s*timezone\s*:\s*(.+)$/i;
38
- const TIMEZONE_TOKEN_RE =
39
- /\b(?:[A-Za-z][A-Za-z0-9_+-]*(?:\/[A-Za-z0-9_+-]+)+|(?:UTC|GMT)(?:[+-]\d{1,2}(?::?\d{2})?)?)\b/gi;
40
36
  const UTC_GMT_OFFSET_TOKEN_RE = /^(?:UTC|GMT)([+-])(\d{1,2})(?::?(\d{2}))?$/i;
41
37
 
42
38
  function normalizeOffsetToken(offsetToken: string): string {
@@ -111,6 +107,17 @@ function canonicalizeTimeZone(timeZone: string): string | null {
111
107
  return null;
112
108
  }
113
109
  }
110
+ // Check abbreviation mapping before Intl (many abbreviations are not recognized by Intl)
111
+ const abbrIana = TIMEZONE_ABBREVIATIONS[trimmed.toUpperCase()];
112
+ if (abbrIana) {
113
+ try {
114
+ return new Intl.DateTimeFormat("en-US", {
115
+ timeZone: abbrIana,
116
+ }).resolvedOptions().timeZone;
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
114
121
  try {
115
122
  return new Intl.DateTimeFormat("en-US", {
116
123
  timeZone: trimmed,
@@ -120,47 +127,123 @@ function canonicalizeTimeZone(timeZone: string): string | null {
120
127
  }
121
128
  }
122
129
 
123
- function extractTimeZoneCandidates(text: string): string[] {
124
- const matches = (text.match(TIMEZONE_TOKEN_RE) ?? [])
125
- .map((token) => token.trim())
126
- .filter((token) => token.length > 0);
127
- const ianaTokens = matches.filter((token) => token.includes("/"));
128
- const offsetTokens = matches.filter((token) => !token.includes("/"));
129
- return [...ianaTokens, ...offsetTokens];
130
- }
130
+ /**
131
+ * Common timezone abbreviation → IANA identifier mapping.
132
+ * Used as a fallback when `Intl.DateTimeFormat` does not recognize the abbreviation.
133
+ */
134
+ const TIMEZONE_ABBREVIATIONS: Record<string, string> = {
135
+ // North America
136
+ PST: "America/Los_Angeles",
137
+ PDT: "America/Los_Angeles",
138
+ MST: "America/Denver",
139
+ MDT: "America/Denver",
140
+ CST: "America/Chicago",
141
+ CDT: "America/Chicago",
142
+ EST: "America/New_York",
143
+ EDT: "America/New_York",
144
+ AKST: "America/Anchorage",
145
+ AKDT: "America/Anchorage",
146
+ HST: "Pacific/Honolulu",
147
+ AST: "America/Puerto_Rico",
148
+ NST: "America/St_Johns",
149
+ NDT: "America/St_Johns",
150
+ // Europe
151
+ BST: "Europe/London",
152
+ CET: "Europe/Paris",
153
+ CEST: "Europe/Paris",
154
+ EET: "Europe/Athens",
155
+ EEST: "Europe/Athens",
156
+ WEST: "Europe/Lisbon",
157
+ MSK: "Europe/Moscow",
158
+ // Asia / Oceania
159
+ JST: "Asia/Tokyo",
160
+ KST: "Asia/Seoul",
161
+ HKT: "Asia/Hong_Kong",
162
+ SGT: "Asia/Singapore",
163
+ WIB: "Asia/Jakarta",
164
+ PHT: "Asia/Manila",
165
+ PKT: "Asia/Karachi",
166
+ NPT: "Asia/Kathmandu",
167
+ AEST: "Australia/Sydney",
168
+ AEDT: "Australia/Sydney",
169
+ ACST: "Australia/Adelaide",
170
+ ACDT: "Australia/Adelaide",
171
+ AWST: "Australia/Perth",
172
+ NZST: "Pacific/Auckland",
173
+ NZDT: "Pacific/Auckland",
174
+ // South America
175
+ BRT: "America/Sao_Paulo",
176
+ };
131
177
 
132
178
  /**
133
- * Extract a valid user timezone from compiled `<dynamic-user-profile>` text.
179
+ * Regex matching IANA timezone identifiers (e.g. "America/New_York"),
180
+ * UTC/GMT offset tokens (e.g. "UTC+5", "GMT-8:30"), and common
181
+ * timezone abbreviations (e.g. "PST", "EST", "JST").
134
182
  *
135
- * Prefers explicit `timezone:` profile lines, then falls back to scanning the
136
- * full profile body for valid IANA timezone identifiers.
183
+ * Abbreviation alternation is built from `TIMEZONE_ABBREVIATIONS` keys.
137
184
  */
138
- export function extractUserTimeZoneFromDynamicProfile(
139
- profileText: string,
185
+ const TIMEZONE_ABBR_ALTERNATION = Object.keys(TIMEZONE_ABBREVIATIONS).join("|");
186
+ const TIMEZONE_TOKEN_RE = new RegExp(
187
+ `\\b(?:[A-Za-z][A-Za-z0-9_+-]*(?:/[A-Za-z0-9_+-]+)+|(?:UTC|GMT)(?:[+-]\\d{1,2}(?::?\\d{2})?)?|(?:${TIMEZONE_ABBR_ALTERNATION}))\\b`,
188
+ "gi",
189
+ );
190
+
191
+ /**
192
+ * Extract the user's timezone from V2 memory recall injected text.
193
+ *
194
+ * Scans the `<user_identity>` section (if present) for lines containing
195
+ * "timezone" and tries to resolve an IANA identifier. Falls back to
196
+ * scanning the full text body.
197
+ */
198
+ export function extractUserTimeZoneFromRecall(
199
+ injectedText: string,
140
200
  ): string | null {
141
- const trimmed = profileText.trim();
142
- if (trimmed.length === 0) return null;
201
+ if (!injectedText || injectedText.trim().length === 0) return null;
143
202
 
144
- const candidateTexts: string[] = [];
145
- for (const line of trimmed.split("\n")) {
146
- const match =
147
- line.match(TIMEZONE_SUBJECT_LINE_RE) ??
148
- line.match(TIMEZONE_SUBJECT_COMPACT_RE);
149
- if (match) {
150
- candidateTexts.push(match[1]);
203
+ // Prefer lines inside <user_identity> that mention "timezone"
204
+ const identityMatch = injectedText.match(
205
+ /<user_identity>([\s\S]*?)<\/user_identity>/,
206
+ );
207
+ if (identityMatch) {
208
+ const identityBlock = identityMatch[1];
209
+ for (const line of identityBlock.split("\n")) {
210
+ if (/time\s*zone/i.test(line)) {
211
+ for (const token of extractTimeZoneCandidates(line)) {
212
+ const canonical = canonicalizeTimeZone(token);
213
+ if (canonical) return canonical;
214
+ }
215
+ }
151
216
  }
152
- }
153
- candidateTexts.push(trimmed);
154
-
155
- for (const text of candidateTexts) {
156
- for (const token of extractTimeZoneCandidates(text)) {
217
+ // Scan full identity block for any timezone token
218
+ for (const token of extractTimeZoneCandidates(identityBlock)) {
157
219
  const canonical = canonicalizeTimeZone(token);
158
220
  if (canonical) return canonical;
159
221
  }
160
222
  }
223
+
224
+ // Fallback: scan entire injected text for timezone tokens in
225
+ // lines that mention "timezone"
226
+ for (const line of injectedText.split("\n")) {
227
+ if (/time\s*zone/i.test(line)) {
228
+ for (const token of extractTimeZoneCandidates(line)) {
229
+ const canonical = canonicalizeTimeZone(token);
230
+ if (canonical) return canonical;
231
+ }
232
+ }
233
+ }
234
+
161
235
  return null;
162
236
  }
163
237
 
238
+ function extractTimeZoneCandidates(text: string): string[] {
239
+ const matches = (text.match(TIMEZONE_TOKEN_RE) ?? [])
240
+ .map((token) => token.trim())
241
+ .filter((token) => token.length > 0);
242
+ const ianaTokens = matches.filter((token) => token.includes("/"));
243
+ const offsetTokens = matches.filter((token) => !token.includes("/"));
244
+ return [...ianaTokens, ...offsetTokens];
245
+ }
246
+
164
247
  /**
165
248
  * Get the local date parts for a given instant in the specified timezone.
166
249
  */
@@ -161,7 +161,7 @@ export async function handleIngressConfig(
161
161
  // Best-effort Twilio webhook reconciliation: when ingress is being
162
162
  // enabled/updated and Twilio numbers are assigned with valid credentials,
163
163
  // push the new webhook URLs to Twilio so calls route correctly.
164
- if (isEnabled && hasTwilioCredentials()) {
164
+ if (isEnabled && (await hasTwilioCredentials())) {
165
165
  const currentConfig = loadRawConfig();
166
166
  const twilioConfig = (currentConfig?.twilio ?? {}) as Record<
167
167
  string,
@@ -188,7 +188,7 @@ export async function handleIngressConfig(
188
188
 
189
189
  if (assignedNumbers.size > 0) {
190
190
  const { accountSid: acctSid, authToken: acctToken } =
191
- getTwilioCredentials();
191
+ await getTwilioCredentials();
192
192
  // Fire-and-forget: webhook sync failure must not block the ingress save.
193
193
  // Reconcile every assigned number so assistant-scoped mappings do not
194
194
  // retain stale Twilio webhook URLs after ingress URL changes.
@@ -13,7 +13,7 @@ import { getConnectionByProvider } from "../../oauth/oauth-store.js";
13
13
  import { credentialKey } from "../../security/credential-key.js";
14
14
  import {
15
15
  deleteSecureKeyAsync,
16
- getSecureKey,
16
+ getSecureKeyAsync,
17
17
  setSecureKeyAsync,
18
18
  } from "../../security/secure-keys.js";
19
19
  import {
@@ -39,13 +39,13 @@ export interface SlackChannelConfigResult {
39
39
 
40
40
  // -- Business logic --
41
41
 
42
- export function getSlackChannelConfig(): SlackChannelConfigResult {
43
- const hasBotToken = !!getSecureKey(
42
+ export async function getSlackChannelConfig(): Promise<SlackChannelConfigResult> {
43
+ const hasBotToken = !!(await getSecureKeyAsync(
44
44
  credentialKey("slack_channel", "bot_token"),
45
- );
46
- const hasAppToken = !!getSecureKey(
45
+ ));
46
+ const hasAppToken = !!(await getSecureKeyAsync(
47
47
  credentialKey("slack_channel", "app_token"),
48
- );
48
+ ));
49
49
  const conn = getConnectionByProvider("slack_channel");
50
50
  const connected =
51
51
  !!(conn && conn.status === "active") && hasBotToken && hasAppToken;
@@ -91,13 +91,17 @@ export async function setSlackChannelConfig(
91
91
  user?: string;
92
92
  };
93
93
  if (!data.ok) {
94
- const errConn = getConnectionByProvider("slack_channel");
95
- const errConnected = !!(errConn && errConn.status === "active");
94
+ const errHasBotToken = !!(await getSecureKeyAsync(
95
+ credentialKey("slack_channel", "bot_token"),
96
+ ));
97
+ const errHasAppToken = !!(await getSecureKeyAsync(
98
+ credentialKey("slack_channel", "app_token"),
99
+ ));
96
100
  return {
97
101
  success: false,
98
- hasBotToken: errConnected,
99
- hasAppToken: errConnected,
100
- connected: errConnected,
102
+ hasBotToken: errHasBotToken,
103
+ hasAppToken: errHasAppToken,
104
+ connected: errHasBotToken && errHasAppToken,
101
105
  error: `Slack API validation failed: ${
102
106
  data.error ?? "unknown error"
103
107
  }`,
@@ -111,13 +115,17 @@ export async function setSlackChannelConfig(
111
115
  };
112
116
  } catch (err) {
113
117
  const message = err instanceof Error ? err.message : String(err);
114
- const errConn = getConnectionByProvider("slack_channel");
115
- const errConnected = !!(errConn && errConn.status === "active");
118
+ const errHasBotToken = !!(await getSecureKeyAsync(
119
+ credentialKey("slack_channel", "bot_token"),
120
+ ));
121
+ const errHasAppToken = !!(await getSecureKeyAsync(
122
+ credentialKey("slack_channel", "app_token"),
123
+ ));
116
124
  return {
117
125
  success: false,
118
- hasBotToken: errConnected,
119
- hasAppToken: errConnected,
120
- connected: errConnected,
126
+ hasBotToken: errHasBotToken,
127
+ hasAppToken: errHasAppToken,
128
+ connected: errHasBotToken && errHasAppToken,
121
129
  error: `Failed to validate bot token: ${message}`,
122
130
  };
123
131
  }
@@ -127,13 +135,17 @@ export async function setSlackChannelConfig(
127
135
  botToken,
128
136
  );
129
137
  if (!stored) {
130
- const errConn = getConnectionByProvider("slack_channel");
131
- const errConnected = !!(errConn && errConn.status === "active");
138
+ const errHasBotToken = !!(await getSecureKeyAsync(
139
+ credentialKey("slack_channel", "bot_token"),
140
+ ));
141
+ const errHasAppToken = !!(await getSecureKeyAsync(
142
+ credentialKey("slack_channel", "app_token"),
143
+ ));
132
144
  return {
133
145
  success: false,
134
- hasBotToken: errConnected,
135
- hasAppToken: errConnected,
136
- connected: errConnected,
146
+ hasBotToken: errHasBotToken,
147
+ hasAppToken: errHasAppToken,
148
+ connected: errHasBotToken && errHasAppToken,
137
149
  error: "Failed to store bot token in secure storage",
138
150
  };
139
151
  }
@@ -161,13 +173,17 @@ export async function setSlackChannelConfig(
161
173
  // Validate and store app token
162
174
  if (appToken) {
163
175
  if (!appToken.startsWith("xapp-")) {
164
- const errConn = getConnectionByProvider("slack_channel");
165
- const errConnected = !!(errConn && errConn.status === "active");
176
+ const errHasBotToken = !!(await getSecureKeyAsync(
177
+ credentialKey("slack_channel", "bot_token"),
178
+ ));
179
+ const errHasAppToken = !!(await getSecureKeyAsync(
180
+ credentialKey("slack_channel", "app_token"),
181
+ ));
166
182
  return {
167
183
  success: false,
168
- hasBotToken: errConnected,
169
- hasAppToken: errConnected,
170
- connected: errConnected,
184
+ hasBotToken: errHasBotToken,
185
+ hasAppToken: errHasAppToken,
186
+ connected: errHasBotToken && errHasAppToken,
171
187
  error: 'Invalid app token: must start with "xapp-"',
172
188
  };
173
189
  }
@@ -177,13 +193,17 @@ export async function setSlackChannelConfig(
177
193
  appToken,
178
194
  );
179
195
  if (!stored) {
180
- const errConn = getConnectionByProvider("slack_channel");
181
- const errConnected = !!(errConn && errConn.status === "active");
196
+ const errHasBotToken = !!(await getSecureKeyAsync(
197
+ credentialKey("slack_channel", "bot_token"),
198
+ ));
199
+ const errHasAppToken = !!(await getSecureKeyAsync(
200
+ credentialKey("slack_channel", "app_token"),
201
+ ));
182
202
  return {
183
203
  success: false,
184
- hasBotToken: errConnected,
185
- hasAppToken: errConnected,
186
- connected: errConnected,
204
+ hasBotToken: errHasBotToken,
205
+ hasAppToken: errHasAppToken,
206
+ connected: errHasBotToken && errHasAppToken,
187
207
  error: "Failed to store app token in secure storage",
188
208
  };
189
209
  }
@@ -191,12 +211,12 @@ export async function setSlackChannelConfig(
191
211
  upsertCredentialMetadata("slack_channel", "app_token", {});
192
212
  }
193
213
 
194
- const hasBotToken = !!getSecureKey(
214
+ const hasBotToken = !!(await getSecureKeyAsync(
195
215
  credentialKey("slack_channel", "bot_token"),
196
- );
197
- const hasAppToken = !!getSecureKey(
216
+ ));
217
+ const hasAppToken = !!(await getSecureKeyAsync(
198
218
  credentialKey("slack_channel", "app_token"),
199
- );
219
+ ));
200
220
 
201
221
  if (hasBotToken && !hasAppToken) {
202
222
  warning =
@@ -237,12 +257,12 @@ export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResul
237
257
 
238
258
  if (r1 === "error" || r2 === "error") {
239
259
  // Check each key individually so partial deletions report accurate status.
240
- const hasBotToken = !!getSecureKey(
260
+ const hasBotToken = !!(await getSecureKeyAsync(
241
261
  credentialKey("slack_channel", "bot_token"),
242
- );
243
- const hasAppToken = !!getSecureKey(
262
+ ));
263
+ const hasAppToken = !!(await getSecureKeyAsync(
244
264
  credentialKey("slack_channel", "app_token"),
245
- );
265
+ ));
246
266
  return {
247
267
  success: false,
248
268
  hasBotToken,
@@ -16,7 +16,7 @@ import { getConnectionByProvider } from "../../oauth/oauth-store.js";
16
16
  import { credentialKey } from "../../security/credential-key.js";
17
17
  import {
18
18
  deleteSecureKeyAsync,
19
- getSecureKey,
19
+ getSecureKeyAsync,
20
20
  setSecureKeyAsync,
21
21
  } from "../../security/secure-keys.js";
22
22
  import { getTelegramBotUsername } from "../../telegram/bot-username.js";
@@ -65,11 +65,13 @@ export type TelegramConfigResult = Omit<TelegramConfigResponse, "type">;
65
65
 
66
66
  // -- Extracted business logic functions --
67
67
 
68
- export function getTelegramConfig(): TelegramConfigResult {
69
- const hasBotToken = !!getSecureKey(credentialKey("telegram", "bot_token"));
70
- const hasWebhookSecret = !!getSecureKey(
68
+ export async function getTelegramConfig(): Promise<TelegramConfigResult> {
69
+ const hasBotToken = !!(await getSecureKeyAsync(
70
+ credentialKey("telegram", "bot_token"),
71
+ ));
72
+ const hasWebhookSecret = !!(await getSecureKeyAsync(
71
73
  credentialKey("telegram", "webhook_secret"),
72
- );
74
+ ));
73
75
  const conn = getConnectionByProvider("telegram");
74
76
  const connected = !!(conn && conn.status === "active");
75
77
  const botUsername = getTelegramBotUsername();
@@ -89,7 +91,8 @@ export async function setTelegramConfig(
89
91
  // Track provenance so we only rollback tokens that were freshly provided.
90
92
  const isNewToken = !!botToken;
91
93
  const resolvedToken =
92
- botToken || getSecureKey(credentialKey("telegram", "bot_token"));
94
+ botToken ||
95
+ (await getSecureKeyAsync(credentialKey("telegram", "bot_token")));
93
96
  if (!resolvedToken) {
94
97
  return {
95
98
  success: false,
@@ -166,9 +169,9 @@ export async function setTelegramConfig(
166
169
  invalidateConfigCache();
167
170
 
168
171
  // Ensure webhook secret exists (generate if missing)
169
- let hasWebhookSecret = !!getSecureKey(
172
+ let hasWebhookSecret = !!(await getSecureKeyAsync(
170
173
  credentialKey("telegram", "webhook_secret"),
171
- );
174
+ ));
172
175
  if (!hasWebhookSecret) {
173
176
  const { randomUUID } = await import("node:crypto");
174
177
  const webhookSecret = randomUUID();
@@ -241,7 +244,9 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
241
244
  // The gateway reconcile short-circuits when credentials are absent,
242
245
  // so we must call the Telegram API directly while the token is still
243
246
  // available.
244
- const botToken = getSecureKey(credentialKey("telegram", "bot_token"));
247
+ const botToken = await getSecureKeyAsync(
248
+ credentialKey("telegram", "bot_token"),
249
+ );
245
250
  if (botToken) {
246
251
  try {
247
252
  await fetch(`https://api.telegram.org/bot${botToken}/deleteWebhook`);
@@ -260,10 +265,12 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
260
265
 
261
266
  if (r1 === "error" || r2 === "error") {
262
267
  // Check each key individually so partial deletions report accurate status.
263
- const hasBotToken = !!getSecureKey(credentialKey("telegram", "bot_token"));
264
- const hasWebhookSecret = !!getSecureKey(
268
+ const hasBotToken = !!(await getSecureKeyAsync(
269
+ credentialKey("telegram", "bot_token"),
270
+ ));
271
+ const hasWebhookSecret = !!(await getSecureKeyAsync(
265
272
  credentialKey("telegram", "webhook_secret"),
266
- );
273
+ ));
267
274
  return {
268
275
  success: false,
269
276
  hasBotToken,
@@ -297,7 +304,9 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
297
304
  export async function setTelegramCommands(
298
305
  commands?: Array<{ command: string; description: string }>,
299
306
  ): Promise<TelegramConfigResult> {
300
- const storedToken = getSecureKey(credentialKey("telegram", "bot_token"));
307
+ const storedToken = await getSecureKeyAsync(
308
+ credentialKey("telegram", "bot_token"),
309
+ );
301
310
  if (!storedToken) {
302
311
  return {
303
312
  success: false,
@@ -398,7 +407,7 @@ export async function handleTelegramConfig(
398
407
  let result: TelegramConfigResult;
399
408
 
400
409
  if (msg.action === "get") {
401
- result = getTelegramConfig();
410
+ result = await getTelegramConfig();
402
411
  } else if (msg.action === "set") {
403
412
  result = await setTelegramConfig(msg.botToken);
404
413
  } else if (msg.action === "clear") {