@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
@@ -41,6 +41,10 @@
41
41
  "reason": {
42
42
  "type": "string",
43
43
  "description": "Brief non-technical explanation of why this tool is being called"
44
+ },
45
+ "account": {
46
+ "type": "string",
47
+ "description": "Email address of the Google account to use. Required when multiple Google accounts are connected. If omitted, uses the most recently connected account."
44
48
  }
45
49
  }
46
50
  },
@@ -66,6 +70,10 @@
66
70
  "reason": {
67
71
  "type": "string",
68
72
  "description": "Brief non-technical explanation of why this tool is being called"
73
+ },
74
+ "account": {
75
+ "type": "string",
76
+ "description": "Email address of the Google account to use. Required when multiple Google accounts are connected. If omitted, uses the most recently connected account."
69
77
  }
70
78
  },
71
79
  "required": ["event_id"]
@@ -123,6 +131,10 @@
123
131
  "reason": {
124
132
  "type": "string",
125
133
  "description": "Brief non-technical explanation of why this tool is being called"
134
+ },
135
+ "account": {
136
+ "type": "string",
137
+ "description": "Email address of the Google account to use. Required when multiple Google accounts are connected. If omitted, uses the most recently connected account."
126
138
  }
127
139
  },
128
140
  "required": ["summary", "start", "end", "confidence"]
@@ -160,6 +172,10 @@
160
172
  "reason": {
161
173
  "type": "string",
162
174
  "description": "Brief non-technical explanation of why this tool is being called"
175
+ },
176
+ "account": {
177
+ "type": "string",
178
+ "description": "Email address of the Google account to use. Required when multiple Google accounts are connected. If omitted, uses the most recently connected account."
163
179
  }
164
180
  },
165
181
  "required": ["time_min", "time_max"]
@@ -195,6 +211,10 @@
195
211
  "reason": {
196
212
  "type": "string",
197
213
  "description": "Brief non-technical explanation of why this tool is being called"
214
+ },
215
+ "account": {
216
+ "type": "string",
217
+ "description": "Email address of the Google account to use. Required when multiple Google accounts are connected. If omitted, uses the most recently connected account."
198
218
  }
199
219
  },
200
220
  "required": ["event_id", "response", "confidence"]
@@ -9,12 +9,13 @@ export async function run(
9
9
  input: Record<string, unknown>,
10
10
  _context: ToolContext,
11
11
  ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
12
13
  const timeMin = input.time_min as string;
13
14
  const timeMax = input.time_max as string;
14
15
  const calendarIds = (input.calendar_ids as string[]) ?? ["primary"];
15
16
  const timezone = input.timezone as string | undefined;
16
17
 
17
- const connection = getCalendarConnection();
18
+ const connection = await getCalendarConnection(account, calendarIds[0]);
18
19
  const result = await calendar.freeBusy(connection, {
19
20
  timeMin,
20
21
  timeMax,
@@ -9,6 +9,7 @@ export async function run(
9
9
  input: Record<string, unknown>,
10
10
  _context: ToolContext,
11
11
  ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
12
13
  const summary = input.summary as string;
13
14
  const startRaw = input.start as string;
14
15
  const endRaw = input.end as string;
@@ -40,7 +41,7 @@ export async function run(
40
41
  eventBody.attendees = attendees.map((email) => ({ email }));
41
42
  }
42
43
 
43
- const connection = getCalendarConnection();
44
+ const connection = await getCalendarConnection(account, calendarId);
44
45
  const event = await calendar.createEvent(connection, eventBody, calendarId);
45
46
  const link = event.htmlLink ? ` View it here: ${event.htmlLink}` : "";
46
47
  return ok(`Event created (ID: ${event.id}).${link}`);
@@ -9,10 +9,11 @@ export async function run(
9
9
  input: Record<string, unknown>,
10
10
  _context: ToolContext,
11
11
  ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
12
13
  const eventId = input.event_id as string;
13
14
  const calendarId = (input.calendar_id as string) ?? "primary";
14
15
 
15
- const connection = getCalendarConnection();
16
+ const connection = await getCalendarConnection(account, calendarId);
16
17
  const event = await calendar.getEvent(connection, eventId, calendarId);
17
18
  return ok(JSON.stringify(event, null, 2));
18
19
  }
@@ -9,6 +9,7 @@ export async function run(
9
9
  input: Record<string, unknown>,
10
10
  _context: ToolContext,
11
11
  ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
12
13
  const calendarId = (input.calendar_id as string) ?? "primary";
13
14
  const timeMin = (input.time_min as string) ?? new Date().toISOString();
14
15
  const timeMax = input.time_max as string | undefined;
@@ -17,7 +18,7 @@ export async function run(
17
18
  const singleEvents = (input.single_events as boolean) ?? true;
18
19
  const orderBy = input.order_by as "startTime" | "updated" | undefined;
19
20
 
20
- const connection = getCalendarConnection();
21
+ const connection = await getCalendarConnection(account, calendarId);
21
22
  const result = await calendar.listEvents(connection, calendarId, {
22
23
  timeMin,
23
24
  timeMax,
@@ -9,11 +9,12 @@ export async function run(
9
9
  input: Record<string, unknown>,
10
10
  _context: ToolContext,
11
11
  ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
12
13
  const eventId = input.event_id as string;
13
14
  const response = input.response as "accepted" | "declined" | "tentative";
14
15
  const calendarId = (input.calendar_id as string) ?? "primary";
15
16
 
16
- const connection = getCalendarConnection();
17
+ const connection = await getCalendarConnection(account, calendarId);
17
18
 
18
19
  // First get the event to find the user's attendee entry
19
20
  const event = await calendar.getEvent(connection, eventId, calendarId);
@@ -10,6 +10,12 @@ export function ok(content: string): ToolExecutionResult {
10
10
  * Calendar uses the same OAuth credential service as Gmail since both
11
11
  * scopes are granted in a single OAuth consent flow.
12
12
  */
13
- export function getCalendarConnection(): OAuthConnection {
14
- return resolveOAuthConnection("integration:gmail");
13
+ export async function getCalendarConnection(
14
+ account?: string,
15
+ calendarId?: string,
16
+ ): Promise<OAuthConnection> {
17
+ // If no explicit account but calendar_id looks like an email, use it as the account hint
18
+ const resolved =
19
+ account ?? (calendarId?.includes("@") ? calendarId : undefined);
20
+ return resolveOAuthConnection("integration:google", resolved);
15
21
  }
@@ -23,7 +23,7 @@ Do not offer AgentMail as an option or mention it unless the user specifically a
23
23
  ## Communication Style
24
24
 
25
25
  - **Be action-oriented.** When the user asks to do something ("declutter", "check my email"), start doing it immediately. Don't ask for permission to read their inbox — that's obviously what they want.
26
- - **Keep it human.** Never mention OAuth, tokens, APIs, sandboxes, credential proxies, or other technical internals. If something isn't working, say "Gmail needs to be reconnected" — not "the OAuth2 access token for integration:gmail has expired."
26
+ - **Keep it human.** Never mention OAuth, tokens, APIs, sandboxes, credential proxies, or other technical internals. If something isn't working, say "Gmail needs to be reconnected" — not "the OAuth2 access token for integration:google has expired."
27
27
  - **Show progress.** When running a tool that scans many emails, tell the user what you're doing: "Scanning your inbox for clutter..." Don't go silent.
28
28
  - **Be brief and warm.** One or two sentences per update is plenty. Don't over-explain what you're about to do — just do it and narrate lightly.
29
29
 
@@ -90,8 +90,8 @@ export async function run(
90
90
  const queryFilter = input.query_filter as string | undefined;
91
91
 
92
92
  try {
93
- const provider = resolveProvider(platform);
94
- const conn = getProviderConnection(provider);
93
+ const provider = await resolveProvider(platform);
94
+ const conn = await getProviderConnection(provider);
95
95
  // Search for sent messages using the platform's search
96
96
  const query =
97
97
  queryFilter ?? (provider.id === "gmail" ? "in:sent" : "from:me");
@@ -22,7 +22,7 @@ export async function run(
22
22
  }
23
23
 
24
24
  try {
25
- const provider = resolveProvider(platform);
25
+ const provider = await resolveProvider(platform);
26
26
 
27
27
  if (!provider.archiveByQuery) {
28
28
  return err(
@@ -30,7 +30,7 @@ export async function run(
30
30
  );
31
31
  }
32
32
 
33
- const conn = getProviderConnection(provider);
33
+ const conn = await getProviderConnection(provider);
34
34
  const result = await provider.archiveByQuery!(conn, query);
35
35
 
36
36
  if (result.archived === 0) {
@@ -11,8 +11,8 @@ export async function run(
11
11
  const platform = input.platform as string | undefined;
12
12
 
13
13
  try {
14
- const provider = resolveProvider(platform);
15
- const conn = getProviderConnection(provider);
14
+ const provider = await resolveProvider(platform);
15
+ const conn = await getProviderConnection(provider);
16
16
  const info = await provider.testConnection(conn);
17
17
  return ok(JSON.stringify(info, null, 2));
18
18
  } catch (e) {
@@ -13,8 +13,8 @@ export async function run(
13
13
  const limit = input.limit as number | undefined;
14
14
 
15
15
  try {
16
- const provider = resolveProvider(platform);
17
- const conn = getProviderConnection(provider);
16
+ const provider = await resolveProvider(platform);
17
+ const conn = await getProviderConnection(provider);
18
18
  const conversations = await provider.listConversations(conn, {
19
19
  types: types as Array<"channel" | "dm" | "group" | "inbox"> | undefined,
20
20
  limit,
@@ -17,13 +17,13 @@ export async function run(
17
17
  }
18
18
 
19
19
  try {
20
- const provider = resolveProvider(platform);
20
+ const provider = await resolveProvider(platform);
21
21
  if (!provider.markRead) {
22
22
  return err(
23
23
  `${provider.displayName} does not support marking messages as read.`,
24
24
  );
25
25
  }
26
- const conn = getProviderConnection(provider);
26
+ const conn = await getProviderConnection(provider);
27
27
  await provider.markRead(conn, conversationId, messageId);
28
28
  return ok("Marked as read.");
29
29
  } catch (e) {
@@ -18,8 +18,8 @@ export async function run(
18
18
  }
19
19
 
20
20
  try {
21
- const provider = resolveProvider(platform);
22
- const conn = getProviderConnection(provider);
21
+ const provider = await resolveProvider(platform);
22
+ const conn = await getProviderConnection(provider);
23
23
  let messages;
24
24
  if (threadId && provider.getThreadReplies) {
25
25
  messages = await provider.getThreadReplies(
@@ -17,8 +17,8 @@ export async function run(
17
17
  }
18
18
 
19
19
  try {
20
- const provider = resolveProvider(platform);
21
- const conn = getProviderConnection(provider);
20
+ const provider = await resolveProvider(platform);
21
+ const conn = await getProviderConnection(provider);
22
22
  const result = await provider.search(conn, query, { count: maxResults });
23
23
  return ok(JSON.stringify(result, null, 2));
24
24
  } catch (e) {
@@ -45,14 +45,14 @@ export async function run(
45
45
  }
46
46
 
47
47
  try {
48
- const provider = resolveProvider(platform);
48
+ const provider = await resolveProvider(platform);
49
49
 
50
50
  // Non-Gmail platforms: reject attachment_paths
51
51
  if (provider.id !== "gmail" && attachmentPaths?.length) {
52
52
  return err("Attachments are only supported on Gmail.");
53
53
  }
54
54
 
55
- const conn = getProviderConnection(provider);
55
+ const conn = await getProviderConnection(provider);
56
56
 
57
57
  // Gmail: create a draft instead of sending directly
58
58
  if (provider.id === "gmail") {
@@ -15,7 +15,7 @@ export async function run(
15
15
  const pageToken = input.page_token as string | undefined;
16
16
 
17
17
  try {
18
- const provider = resolveProvider(platform);
18
+ const provider = await resolveProvider(platform);
19
19
 
20
20
  if (!provider.senderDigest) {
21
21
  return err(
@@ -23,7 +23,7 @@ export async function run(
23
23
  );
24
24
  }
25
25
 
26
- const conn = getProviderConnection(provider);
26
+ const conn = await getProviderConnection(provider);
27
27
  const result = await provider.senderDigest!(conn, query, {
28
28
  maxMessages,
29
29
  maxSenders,
@@ -106,10 +106,12 @@ export function extractEmail(address: string): string {
106
106
  * If only one provider is connected, auto-select it.
107
107
  * Otherwise, throw asking the user to specify.
108
108
  */
109
- export function resolveProvider(platformInput?: string): MessagingProvider {
109
+ export async function resolveProvider(
110
+ platformInput?: string,
111
+ ): Promise<MessagingProvider> {
110
112
  if (platformInput) return getMessagingProvider(platformInput);
111
113
 
112
- const connected = getConnectedProviders();
114
+ const connected = await getConnectedProviders();
113
115
  if (connected.length === 1) return connected[0];
114
116
  if (connected.length === 0) {
115
117
  throw new Error(
@@ -130,9 +132,9 @@ export function resolveProvider(platformInput?: string): MessagingProvider {
130
132
  * Non-OAuth providers (e.g. Telegram) use isConnected() and don't need
131
133
  * tokens — they receive an empty string which the string overload handles.
132
134
  */
133
- export function getProviderConnection(
135
+ export async function getProviderConnection(
134
136
  provider: MessagingProvider,
135
- ): OAuthConnection | string {
136
- if (provider.isConnected?.()) return "";
137
+ ): Promise<OAuthConnection | string> {
138
+ if (await provider.isConnected?.()) return "";
137
139
  return resolveOAuthConnection(provider.credentialService);
138
140
  }
@@ -14,6 +14,6 @@ export function err(message: string): ToolExecutionResult {
14
14
  return { content: message, isError: true };
15
15
  }
16
16
 
17
- export function getSlackConnection(): OAuthConnection {
17
+ export async function getSlackConnection(): Promise<OAuthConnection> {
18
18
  return resolveOAuthConnection("integration:slack");
19
19
  }
@@ -18,7 +18,7 @@ export async function run(
18
18
  }
19
19
 
20
20
  try {
21
- const connection = getSlackConnection();
21
+ const connection = await getSlackConnection();
22
22
  await addReaction(connection, channel, timestamp, emoji);
23
23
  return ok(`Added :${emoji}: reaction.`);
24
24
  } catch (e) {
@@ -16,7 +16,7 @@ export async function run(
16
16
  }
17
17
 
18
18
  try {
19
- const connection = getSlackConnection();
19
+ const connection = await getSlackConnection();
20
20
  const resp = await slack.conversationInfo(connection, channelId);
21
21
  const conv = resp.channel;
22
22
 
@@ -17,7 +17,7 @@ export async function run(
17
17
  }
18
18
 
19
19
  try {
20
- const connection = getSlackConnection();
20
+ const connection = await getSlackConnection();
21
21
  await deleteMessage(connection, channel, timestamp);
22
22
  return ok(`Message deleted.`);
23
23
  } catch (e) {
@@ -18,7 +18,7 @@ export async function run(
18
18
  }
19
19
 
20
20
  try {
21
- const connection = getSlackConnection();
21
+ const connection = await getSlackConnection();
22
22
  await updateMessage(connection, channel, timestamp, text);
23
23
  return ok(`Message updated.`);
24
24
  } catch (e) {
@@ -16,7 +16,7 @@ export async function run(
16
16
  }
17
17
 
18
18
  try {
19
- const connection = getSlackConnection();
19
+ const connection = await getSlackConnection();
20
20
  await leaveConversation(connection, channel);
21
21
  return ok("Left channel.");
22
22
  } catch (e) {
@@ -264,7 +264,7 @@ export async function run(
264
264
  const format = (input.format as string) ?? "text";
265
265
 
266
266
  try {
267
- const connection = getSlackConnection();
267
+ const connection = await getSlackConnection();
268
268
  const oldestTs = String((Date.now() - hoursBack * 60 * 60 * 1000) / 1000);
269
269
 
270
270
  let channelsToScan: SlackConversation[];
@@ -48,6 +48,7 @@ import * as computerUseClick from "./bundled-skills/computer-use/tools/computer-
48
48
  import * as computerUseDone from "./bundled-skills/computer-use/tools/computer-use-done.js";
49
49
  import * as computerUseDrag from "./bundled-skills/computer-use/tools/computer-use-drag.js";
50
50
  import * as computerUseKey from "./bundled-skills/computer-use/tools/computer-use-key.js";
51
+ import * as computerUseObserve from "./bundled-skills/computer-use/tools/computer-use-observe.js";
51
52
  import * as computerUseOpenApp from "./bundled-skills/computer-use/tools/computer-use-open-app.js";
52
53
  import * as computerUseRespond from "./bundled-skills/computer-use/tools/computer-use-respond.js";
53
54
  import * as computerUseRunApplescript from "./bundled-skills/computer-use/tools/computer-use-run-applescript.js";
@@ -88,8 +89,6 @@ import * as calendarListEvents from "./bundled-skills/google-calendar/tools/cale
88
89
  import * as calendarRsvp from "./bundled-skills/google-calendar/tools/calendar-rsvp.js";
89
90
  // ── image-studio ───────────────────────────────────────────────────────────────
90
91
  import * as mediaGenerateImage from "./bundled-skills/image-studio/tools/media-generate-image.js";
91
- // ── knowledge-graph ────────────────────────────────────────────────────────────
92
- import * as graphQuery from "./bundled-skills/knowledge-graph/tools/graph-query.js";
93
92
  // ── media-processing ───────────────────────────────────────────────────────────
94
93
  import * as analyzeKeyframes from "./bundled-skills/media-processing/tools/analyze-keyframes.js";
95
94
  import * as extractKeyframes from "./bundled-skills/media-processing/tools/extract-keyframes.js";
@@ -219,6 +218,7 @@ export const bundledToolRegistry = new Map<string, SkillToolScript>([
219
218
  ["claude-code:tools/claude-code.ts", claudeCode],
220
219
 
221
220
  // computer-use
221
+ ["computer-use:tools/computer-use-observe.ts", computerUseObserve],
222
222
  ["computer-use:tools/computer-use-click.ts", computerUseClick],
223
223
  ["computer-use:tools/computer-use-type-text.ts", computerUseTypeText],
224
224
  ["computer-use:tools/computer-use-key.ts", computerUseKey],
@@ -276,9 +276,6 @@ export const bundledToolRegistry = new Map<string, SkillToolScript>([
276
276
  // image-studio
277
277
  ["image-studio:tools/media-generate-image.ts", mediaGenerateImage],
278
278
 
279
- // knowledge-graph
280
- ["knowledge-graph:tools/graph-query.ts", graphQuery],
281
-
282
279
  // media-processing
283
280
  ["media-processing:tools/ingest-media.ts", ingestMedia],
284
281
  ["media-processing:tools/media-status.ts", mediaStatus],
@@ -405,8 +405,9 @@ export function invalidateConfigCache(): void {
405
405
  }
406
406
 
407
407
  /**
408
- * Load the raw config from disk, merging API keys from secure storage.
408
+ * Load the raw config from disk without any secure-storage merging.
409
409
  * Used by CLI config commands to read/write the file directly.
410
+ * API keys in secure storage are managed via `assistant keys` commands.
410
411
  */
411
412
  export function loadRawConfig(): Record<string, unknown> {
412
413
  ensureMigratedDataDir();
@@ -420,25 +421,6 @@ export function loadRawConfig(): Record<string, unknown> {
420
421
  }
421
422
  }
422
423
 
423
- // Merge secure keys into apiKeys so `config get apiKeys.*` works
424
- try {
425
- const apiKeys =
426
- raw.apiKeys &&
427
- typeof raw.apiKeys === "object" &&
428
- !Array.isArray(raw.apiKeys)
429
- ? { ...(raw.apiKeys as Record<string, unknown>) }
430
- : {};
431
- for (const provider of API_KEY_PROVIDERS) {
432
- const value = getSecureKey(provider);
433
- if (value) apiKeys[provider] = value;
434
- }
435
- if (Object.keys(apiKeys).length > 0) {
436
- raw.apiKeys = apiKeys;
437
- }
438
- } catch (err) {
439
- log.debug({ err }, "Failed to merge secure keys into raw config");
440
- }
441
-
442
424
  return raw;
443
425
  }
444
426
 
@@ -446,28 +428,10 @@ export function saveRawConfig(config: Record<string, unknown>): void {
446
428
  ensureMigratedDataDir();
447
429
  const configPath = getConfigPath();
448
430
 
449
- // Route apiKeys to secure storage and strip from plaintext file
450
- const apiKeys = config.apiKeys;
451
- if (apiKeys && typeof apiKeys === "object" && !Array.isArray(apiKeys)) {
452
- for (const [provider, value] of Object.entries(
453
- apiKeys as Record<string, unknown>,
454
- )) {
455
- if (typeof value === "string" && value.length > 0) {
456
- if (!setSecureKey(provider, value)) {
457
- throw new ConfigError(
458
- `Failed to save API key for "${provider}" to secure storage. Key not removed from config to prevent data loss.`,
459
- );
460
- }
461
- } else if (value == null || value === "") {
462
- deleteSecureKey(provider);
463
- }
464
- }
465
- // Remove apiKeys from plaintext config
466
- const { apiKeys: _, ...rest } = config;
467
- writeFileSync(configPath, JSON.stringify(rest, null, 2) + "\n");
468
- } else {
469
- writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
470
- }
431
+ // Strip apiKeys from plaintext config secure storage is managed
432
+ // by saveConfig() and `assistant keys` commands, not here.
433
+ const { apiKeys: _, ...rest } = config;
434
+ writeFileSync(configPath, JSON.stringify(rest, null, 2) + "\n");
471
435
 
472
436
  cached = null; // invalidate cache
473
437
  }
@@ -91,27 +91,16 @@ export {
91
91
  MemoryRetentionConfigSchema,
92
92
  } from "./schemas/memory-lifecycle.js";
93
93
  export type {
94
- MemoryConflictsConfig,
95
- MemoryEntityConfig,
96
94
  MemoryExtractionConfig,
97
- MemoryProfileConfig,
98
95
  MemorySummarizationConfig,
99
96
  } from "./schemas/memory-processing.js";
100
97
  export {
101
- MemoryConflictsConfigSchema,
102
- MemoryEntityConfigSchema,
103
98
  MemoryExtractionConfigSchema,
104
- MemoryProfileConfigSchema,
105
99
  MemorySummarizationConfigSchema,
106
100
  } from "./schemas/memory-processing.js";
107
- export type {
108
- MemoryRerankingConfig,
109
- MemoryRetrievalConfig,
110
- } from "./schemas/memory-retrieval.js";
101
+ export type { MemoryRetrievalConfig } from "./schemas/memory-retrieval.js";
111
102
  export {
112
103
  MemoryDynamicBudgetConfigSchema,
113
- MemoryEarlyTerminationConfigSchema,
114
- MemoryRerankingConfigSchema,
115
104
  MemoryRetrievalConfigSchema,
116
105
  } from "./schemas/memory-retrieval.js";
117
106
  export type {
@@ -33,15 +33,6 @@ export const MemoryCleanupConfigSchema = z.object({
33
33
  .int("memory.cleanup.enqueueIntervalMs must be an integer")
34
34
  .positive("memory.cleanup.enqueueIntervalMs must be a positive integer")
35
35
  .default(6 * 60 * 60 * 1000),
36
- resolvedConflictRetentionMs: z
37
- .number({
38
- error: "memory.cleanup.resolvedConflictRetentionMs must be a number",
39
- })
40
- .int("memory.cleanup.resolvedConflictRetentionMs must be an integer")
41
- .positive(
42
- "memory.cleanup.resolvedConflictRetentionMs must be a positive integer",
43
- )
44
- .default(30 * 24 * 60 * 60 * 1000),
45
36
  supersededItemRetentionMs: z
46
37
  .number({
47
38
  error: "memory.cleanup.supersededItemRetentionMs must be a number",