@vellumai/assistant 0.5.16 → 0.6.0

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 (407) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +0 -3
  3. package/knip.json +2 -1
  4. package/openapi.yaml +660 -80
  5. package/package.json +1 -1
  6. package/src/__tests__/actor-token-service.test.ts +68 -0
  7. package/src/__tests__/agent-loop.test.ts +0 -32
  8. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  9. package/src/__tests__/anthropic-provider.test.ts +57 -3
  10. package/src/__tests__/app-compiler.test.ts +120 -0
  11. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  12. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  13. package/src/__tests__/call-domain.test.ts +2 -6
  14. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  15. package/src/__tests__/call-recovery.test.ts +2 -6
  16. package/src/__tests__/call-routes-http.test.ts +2 -6
  17. package/src/__tests__/call-store.test.ts +2 -6
  18. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  19. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  20. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  21. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  22. package/src/__tests__/checker.test.ts +25 -3
  23. package/src/__tests__/clawhub.test.ts +54 -24
  24. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  25. package/src/__tests__/cli-memory.test.ts +74 -69
  26. package/src/__tests__/config-schema.test.ts +1 -1
  27. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  28. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  29. package/src/__tests__/contacts-tools.test.ts +31 -0
  30. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  31. package/src/__tests__/context-token-estimator.test.ts +175 -10
  32. package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
  33. package/src/__tests__/conversation-agent-loop.test.ts +9 -0
  34. package/src/__tests__/conversation-attachments.test.ts +2 -6
  35. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  36. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  37. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  38. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  39. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  40. package/src/__tests__/conversation-error.test.ts +33 -2
  41. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  42. package/src/__tests__/conversation-history-web-search.test.ts +5 -0
  43. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  44. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  45. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  46. package/src/__tests__/conversation-store.test.ts +2 -6
  47. package/src/__tests__/conversation-usage.test.ts +2 -6
  48. package/src/__tests__/conversation-wipe.test.ts +11 -408
  49. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  50. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  51. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  52. package/src/__tests__/followup-tools.test.ts +2 -6
  53. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  54. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  55. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  56. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  57. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  58. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  59. package/src/__tests__/guardian-action-store.test.ts +2 -6
  60. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  61. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  62. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  63. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  64. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  65. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  66. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  67. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  68. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  69. package/src/__tests__/injection-block.test.ts +154 -0
  70. package/src/__tests__/install-meta.test.ts +506 -0
  71. package/src/__tests__/install-skill-routing.test.ts +292 -0
  72. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  73. package/src/__tests__/invite-routes-http.test.ts +2 -6
  74. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
  75. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  76. package/src/__tests__/llm-context-route-provider.test.ts +2 -6
  77. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
  78. package/src/__tests__/llm-usage-store.test.ts +2 -6
  79. package/src/__tests__/log-export-workspace.test.ts +2 -6
  80. package/src/__tests__/managed-store.test.ts +38 -11
  81. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  82. package/src/__tests__/memory-recall-log-store.test.ts +2 -6
  83. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  84. package/src/__tests__/non-member-access-request.test.ts +2 -6
  85. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  86. package/src/__tests__/oauth-cli.test.ts +364 -2
  87. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  88. package/src/__tests__/outlook-attachments.test.ts +301 -0
  89. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  90. package/src/__tests__/outlook-categories.test.ts +212 -0
  91. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  92. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  93. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  94. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  95. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  96. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  97. package/src/__tests__/outlook-trash.test.ts +77 -0
  98. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  99. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  100. package/src/__tests__/playbook-execution.test.ts +76 -80
  101. package/src/__tests__/playbook-tools.test.ts +5 -7
  102. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  103. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  104. package/src/__tests__/registry.test.ts +2 -2
  105. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  106. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  107. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  108. package/src/__tests__/schedule-store.test.ts +2 -6
  109. package/src/__tests__/schedule-tools.test.ts +2 -6
  110. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  111. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  112. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  113. package/src/__tests__/search-skills-unified.test.ts +421 -0
  114. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  115. package/src/__tests__/send-endpoint-busy.test.ts +2 -6
  116. package/src/__tests__/sequence-store.test.ts +2 -6
  117. package/src/__tests__/server-history-render.test.ts +2 -6
  118. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  119. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  120. package/src/__tests__/skill-load-feature-flag.test.ts +11 -11
  121. package/src/__tests__/skill-memory.test.ts +140 -98
  122. package/src/__tests__/skills-uninstall.test.ts +2 -2
  123. package/src/__tests__/skills.test.ts +1 -1
  124. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  125. package/src/__tests__/task-compiler.test.ts +2 -6
  126. package/src/__tests__/task-management-tools.test.ts +2 -6
  127. package/src/__tests__/task-memory-cleanup.test.ts +173 -229
  128. package/src/__tests__/task-runner.test.ts +2 -6
  129. package/src/__tests__/task-scheduler.test.ts +2 -6
  130. package/src/__tests__/test-preload.ts +3 -0
  131. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  132. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  133. package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
  134. package/src/__tests__/trust-store.test.ts +1 -1
  135. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  136. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -6
  137. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  138. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  139. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  140. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  141. package/src/__tests__/usage-routes.test.ts +2 -6
  142. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  143. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  144. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  145. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  146. package/src/__tests__/volume-security-guard.test.ts +2 -0
  147. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  148. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  149. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  150. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  151. package/src/__tests__/workspace-policy.test.ts +1 -1
  152. package/src/agent/attachments.ts +7 -2
  153. package/src/agent/image-optimize.ts +165 -0
  154. package/src/agent/loop.ts +1 -15
  155. package/src/bundler/app-compiler.ts +179 -2
  156. package/src/bundler/package-resolver.ts +3 -5
  157. package/src/cli/__tests__/notifications.test.ts +1 -2
  158. package/src/cli/cli-memory.ts +67 -64
  159. package/src/cli/commands/avatar.ts +3 -3
  160. package/src/cli/commands/config.ts +26 -13
  161. package/src/cli/commands/doctor.ts +2 -2
  162. package/src/cli/commands/memory.ts +41 -55
  163. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  164. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  165. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  166. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  167. package/src/cli/commands/oauth/connect.ts +11 -6
  168. package/src/cli/commands/oauth/mode.ts +7 -0
  169. package/src/cli/commands/oauth/shared.ts +39 -3
  170. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  171. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  172. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  173. package/src/cli/commands/platform/index.ts +16 -16
  174. package/src/cli/commands/skills.ts +88 -16
  175. package/src/cli/commands/trust.ts +2 -2
  176. package/src/cli/lib/daemon-credential-client.ts +2 -3
  177. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  178. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  179. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  180. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  181. package/src/config/bundled-skills/gmail/SKILL.md +2 -10
  182. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  183. package/src/config/bundled-skills/messaging/SKILL.md +10 -18
  184. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  185. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  186. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  187. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  188. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  189. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  190. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  191. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  192. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  193. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  194. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  195. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  196. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  197. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  198. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  199. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  200. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  201. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  202. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  203. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  204. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  205. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  206. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  207. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  208. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  209. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  210. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  211. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  212. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  213. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  214. package/src/config/bundled-skills/slack/SKILL.md +1 -7
  215. package/src/config/bundled-tool-registry.ts +56 -4
  216. package/src/config/env-registry.ts +15 -8
  217. package/src/config/feature-flag-registry.json +21 -124
  218. package/src/config/schemas/platform.ts +8 -0
  219. package/src/config/schemas/timeouts.ts +1 -1
  220. package/src/config/skills.ts +18 -7
  221. package/src/context/token-estimator.ts +25 -18
  222. package/src/context/window-manager.ts +6 -2
  223. package/src/credential-execution/process-manager.ts +3 -1
  224. package/src/daemon/context-overflow-reducer.ts +46 -2
  225. package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
  226. package/src/daemon/conversation-agent-loop.ts +96 -61
  227. package/src/daemon/conversation-error.ts +31 -8
  228. package/src/daemon/conversation-lifecycle.ts +33 -0
  229. package/src/daemon/conversation-media-retry.ts +85 -7
  230. package/src/daemon/conversation-notifiers.ts +4 -1
  231. package/src/daemon/conversation-runtime-assembly.ts +5 -0
  232. package/src/daemon/conversation.ts +41 -2
  233. package/src/daemon/daemon-control.ts +8 -2
  234. package/src/daemon/handlers/shared.ts +22 -12
  235. package/src/daemon/handlers/skills.ts +416 -202
  236. package/src/daemon/lifecycle.ts +40 -1
  237. package/src/daemon/main.ts +5 -1
  238. package/src/daemon/message-types/conversations.ts +4 -1
  239. package/src/daemon/message-types/messages.ts +3 -1
  240. package/src/daemon/message-types/skills.ts +97 -36
  241. package/src/daemon/providers-setup.ts +5 -0
  242. package/src/daemon/server.ts +11 -2
  243. package/src/daemon/tool-side-effects.ts +27 -5
  244. package/src/heartbeat/heartbeat-service.ts +1 -0
  245. package/src/hooks/cli.ts +2 -2
  246. package/src/hooks/runner.ts +15 -38
  247. package/src/inbound/platform-callback-registration.ts +14 -14
  248. package/src/memory/admin.ts +11 -45
  249. package/src/memory/conversation-bootstrap.ts +2 -0
  250. package/src/memory/conversation-crud.ts +242 -348
  251. package/src/memory/conversation-group-migration.ts +157 -0
  252. package/src/memory/conversation-queries.ts +4 -2
  253. package/src/memory/db-init.ts +30 -3
  254. package/src/memory/embed.ts +73 -0
  255. package/src/memory/embedding-backend.ts +8 -14
  256. package/src/memory/embedding-runtime-manager.ts +12 -114
  257. package/src/memory/fingerprint.ts +2 -2
  258. package/src/memory/graph/bootstrap.ts +512 -0
  259. package/src/memory/graph/capability-seed.ts +297 -0
  260. package/src/memory/graph/consolidation.ts +691 -0
  261. package/src/memory/graph/conversation-graph-memory.ts +630 -0
  262. package/src/memory/graph/decay.test.ts +208 -0
  263. package/src/memory/graph/decay.ts +195 -0
  264. package/src/memory/graph/extraction-job.ts +69 -0
  265. package/src/memory/graph/extraction.test.ts +936 -0
  266. package/src/memory/graph/extraction.ts +1254 -0
  267. package/src/memory/graph/graph-search.ts +266 -0
  268. package/src/memory/graph/image-ref-utils.ts +29 -0
  269. package/src/memory/graph/injection.test.ts +513 -0
  270. package/src/memory/graph/injection.ts +439 -0
  271. package/src/memory/graph/inspect.ts +534 -0
  272. package/src/memory/graph/narrative.ts +267 -0
  273. package/src/memory/graph/pattern-scan.ts +269 -0
  274. package/src/memory/graph/retriever.ts +1008 -0
  275. package/src/memory/graph/scoring.test.ts +548 -0
  276. package/src/memory/graph/scoring.ts +232 -0
  277. package/src/memory/graph/serendipity.ts +65 -0
  278. package/src/memory/graph/store.test.ts +1050 -0
  279. package/src/memory/graph/store.ts +699 -0
  280. package/src/memory/graph/tool-handlers.ts +426 -0
  281. package/src/memory/graph/tools.ts +141 -0
  282. package/src/memory/graph/triggers.test.ts +487 -0
  283. package/src/memory/graph/triggers.ts +223 -0
  284. package/src/memory/graph/types.ts +271 -0
  285. package/src/memory/group-crud.ts +191 -0
  286. package/src/memory/indexer.ts +37 -19
  287. package/src/memory/job-handlers/cleanup.ts +0 -53
  288. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  289. package/src/memory/job-handlers/embedding.ts +5 -31
  290. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  291. package/src/memory/job-handlers/summarization.ts +32 -17
  292. package/src/memory/job-utils.ts +1 -1
  293. package/src/memory/jobs-store.ts +50 -70
  294. package/src/memory/jobs-worker.ts +147 -112
  295. package/src/memory/message-content.ts +1 -0
  296. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  297. package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
  298. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  299. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  300. package/src/memory/migrations/index.ts +4 -0
  301. package/src/memory/migrations/registry.ts +8 -0
  302. package/src/memory/qdrant-client.ts +44 -17
  303. package/src/memory/schema/index.ts +1 -0
  304. package/src/memory/schema/memory-graph.ts +139 -0
  305. package/src/memory/search/semantic.ts +47 -91
  306. package/src/memory/task-memory-cleanup.ts +28 -50
  307. package/src/messaging/providers/outlook/adapter.ts +8 -1
  308. package/src/messaging/providers/outlook/client.ts +299 -0
  309. package/src/messaging/providers/outlook/types.ts +118 -0
  310. package/src/notifications/adapters/macos.ts +1 -0
  311. package/src/notifications/copy-composer.ts +9 -0
  312. package/src/notifications/signal.ts +16 -0
  313. package/src/oauth/seed-providers.ts +2 -1
  314. package/src/permissions/checker.ts +24 -3
  315. package/src/permissions/defaults.ts +4 -4
  316. package/src/permissions/workspace-policy.ts +1 -1
  317. package/src/playbooks/playbook-compiler.ts +19 -18
  318. package/src/playbooks/types.ts +4 -3
  319. package/src/prompts/system-prompt.ts +3 -29
  320. package/src/providers/anthropic/client.ts +47 -19
  321. package/src/providers/gemini/client.ts +1 -1
  322. package/src/providers/openai/client.ts +1 -1
  323. package/src/providers/registry.ts +1 -1
  324. package/src/providers/retry.ts +19 -3
  325. package/src/runtime/actor-trust-resolver.ts +5 -1
  326. package/src/runtime/auth/route-policy.ts +7 -0
  327. package/src/runtime/guardian-reply-router.ts +5 -1
  328. package/src/runtime/http-server.ts +23 -3
  329. package/src/runtime/middleware/auth.ts +20 -0
  330. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  331. package/src/runtime/routes/attachment-routes.ts +106 -16
  332. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  333. package/src/runtime/routes/btw-routes.ts +8 -0
  334. package/src/runtime/routes/conversation-management-routes.ts +2 -0
  335. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  336. package/src/runtime/routes/debug-routes.ts +1 -1
  337. package/src/runtime/routes/global-search-routes.ts +21 -19
  338. package/src/runtime/routes/group-routes.ts +207 -0
  339. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  340. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  341. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  342. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  343. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  344. package/src/runtime/routes/memory-item-routes.test.ts +2 -14
  345. package/src/runtime/routes/memory-item-routes.ts +341 -388
  346. package/src/runtime/routes/schedule-routes.ts +2 -0
  347. package/src/runtime/routes/skills-routes.ts +103 -37
  348. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  349. package/src/schedule/scheduler.ts +8 -1
  350. package/src/security/oauth2.ts +1 -1
  351. package/src/security/secure-keys.ts +4 -8
  352. package/src/shared/provider-env-vars.ts +19 -0
  353. package/src/skills/catalog-cache.ts +5 -0
  354. package/src/skills/catalog-install.ts +15 -14
  355. package/src/skills/clawhub.ts +134 -154
  356. package/src/skills/install-meta.ts +208 -0
  357. package/src/skills/managed-store.ts +27 -16
  358. package/src/skills/skill-memory.ts +152 -77
  359. package/src/skills/skillssh-registry.ts +19 -17
  360. package/src/tasks/task-runner.ts +3 -1
  361. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  362. package/src/tools/browser/runtime-check.ts +3 -1
  363. package/src/tools/memory/register.ts +63 -46
  364. package/src/tools/permission-checker.ts +7 -1
  365. package/src/tools/shared/filesystem/image-read.ts +22 -85
  366. package/src/tools/terminal/safe-env.ts +1 -0
  367. package/src/tools/tool-manifest.ts +3 -3
  368. package/src/util/browser.ts +25 -10
  369. package/src/util/bun-runtime.ts +172 -0
  370. package/src/watcher/providers/outlook-calendar.ts +343 -0
  371. package/src/watcher/providers/outlook.ts +198 -0
  372. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  373. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  374. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  375. package/src/workspace/migrations/registry.ts +6 -0
  376. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  377. package/src/__tests__/journal-context.test.ts +0 -268
  378. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  379. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  380. package/src/__tests__/memory-query-builder.test.ts +0 -59
  381. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  382. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  383. package/src/__tests__/memory-regressions.test.ts +0 -3696
  384. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  385. package/src/daemon/conversation-memory.ts +0 -207
  386. package/src/memory/conversation-starters-cadence.ts +0 -74
  387. package/src/memory/items-extractor.ts +0 -860
  388. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  389. package/src/memory/job-handlers/extraction.ts +0 -40
  390. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  391. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  392. package/src/memory/journal-memory.ts +0 -224
  393. package/src/memory/query-builder.ts +0 -47
  394. package/src/memory/query-expansion.ts +0 -83
  395. package/src/memory/retriever.test.ts +0 -1592
  396. package/src/memory/retriever.ts +0 -1331
  397. package/src/memory/search/formatting.test.ts +0 -140
  398. package/src/memory/search/formatting.ts +0 -262
  399. package/src/memory/search/mmr.ts +0 -139
  400. package/src/memory/search/ranking.ts +0 -15
  401. package/src/memory/search/staleness.ts +0 -40
  402. package/src/memory/search/tier-classifier.ts +0 -18
  403. package/src/memory/search/types.ts +0 -121
  404. package/src/prompts/journal-context.ts +0 -154
  405. package/src/tools/memory/definitions.ts +0 -69
  406. package/src/tools/memory/handlers.test.ts +0 -562
  407. package/src/tools/memory/handlers.ts +0 -434
@@ -0,0 +1,530 @@
1
+ {
2
+ "version": 1,
3
+ "tools": [
4
+ {
5
+ "name": "outlook_rules",
6
+ "description": "List, create, or delete Outlook inbox message rules for automatic mail processing. Include a confidence score (0-1) for create and delete actions.",
7
+ "category": "outlook",
8
+ "risk": "medium",
9
+ "input_schema": {
10
+ "type": "object",
11
+ "properties": {
12
+ "action": {
13
+ "type": "string",
14
+ "enum": ["list", "create", "delete"],
15
+ "description": "Rule action"
16
+ },
17
+ "display_name": {
18
+ "type": "string",
19
+ "description": "Display name for the rule (required for create)"
20
+ },
21
+ "from_contains": {
22
+ "oneOf": [
23
+ {
24
+ "type": "string"
25
+ },
26
+ {
27
+ "type": "array",
28
+ "items": {
29
+ "type": "string"
30
+ }
31
+ }
32
+ ],
33
+ "description": "Condition: sender contains (for create)"
34
+ },
35
+ "subject_contains": {
36
+ "oneOf": [
37
+ {
38
+ "type": "string"
39
+ },
40
+ {
41
+ "type": "array",
42
+ "items": {
43
+ "type": "string"
44
+ }
45
+ }
46
+ ],
47
+ "description": "Condition: subject contains (for create)"
48
+ },
49
+ "body_contains": {
50
+ "oneOf": [
51
+ {
52
+ "type": "string"
53
+ },
54
+ {
55
+ "type": "array",
56
+ "items": {
57
+ "type": "string"
58
+ }
59
+ }
60
+ ],
61
+ "description": "Condition: body contains (for create)"
62
+ },
63
+ "has_attachment": {
64
+ "type": "boolean",
65
+ "description": "Condition: message has attachment (for create)"
66
+ },
67
+ "importance": {
68
+ "type": "string",
69
+ "enum": ["low", "normal", "high"],
70
+ "description": "Condition: message importance level (for create)"
71
+ },
72
+ "move_to_folder": {
73
+ "type": "string",
74
+ "description": "Action: move to folder ID (for create)"
75
+ },
76
+ "delete": {
77
+ "type": "boolean",
78
+ "description": "Action: delete message (for create)"
79
+ },
80
+ "mark_as_read": {
81
+ "type": "boolean",
82
+ "description": "Action: mark as read (for create)"
83
+ },
84
+ "mark_importance": {
85
+ "type": "string",
86
+ "enum": ["low", "normal", "high"],
87
+ "description": "Action: set importance level (for create)"
88
+ },
89
+ "forward_to": {
90
+ "oneOf": [
91
+ {
92
+ "type": "string"
93
+ },
94
+ {
95
+ "type": "array",
96
+ "items": {
97
+ "type": "string"
98
+ }
99
+ }
100
+ ],
101
+ "description": "Action: forward to email address(es) (for create)"
102
+ },
103
+ "stop_processing": {
104
+ "type": "boolean",
105
+ "description": "Action: stop processing subsequent rules (for create)"
106
+ },
107
+ "rule_id": {
108
+ "type": "string",
109
+ "description": "Rule ID (required for delete)"
110
+ },
111
+ "confidence": {
112
+ "type": "number",
113
+ "description": "Confidence score (0-1) for this action"
114
+ },
115
+ "activity": {
116
+ "type": "string",
117
+ "description": "Brief non-technical explanation of why this tool is being called"
118
+ },
119
+ "account": {
120
+ "type": "string",
121
+ "description": "Email address of the Outlook account to use. Required when multiple Microsoft accounts are connected. If omitted, uses the most recently connected account."
122
+ }
123
+ },
124
+ "required": ["action"]
125
+ },
126
+ "executor": "tools/outlook-rules.ts",
127
+ "execution_target": "host"
128
+ },
129
+ {
130
+ "name": "outlook_vacation",
131
+ "description": "Get, enable, or disable Outlook automatic reply (out-of-office) settings. Include a confidence score (0-1) for enable and disable actions.",
132
+ "category": "outlook",
133
+ "risk": "medium",
134
+ "input_schema": {
135
+ "type": "object",
136
+ "properties": {
137
+ "action": {
138
+ "type": "string",
139
+ "enum": ["get", "enable", "disable"],
140
+ "description": "Vacation action"
141
+ },
142
+ "internal_message": {
143
+ "type": "string",
144
+ "description": "Auto-reply message for internal (same organization) senders (required for enable)"
145
+ },
146
+ "external_message": {
147
+ "type": "string",
148
+ "description": "Auto-reply message for external senders (for enable)"
149
+ },
150
+ "external_audience": {
151
+ "type": "string",
152
+ "enum": ["none", "contactsOnly", "all"],
153
+ "description": "Who receives the external auto-reply: none, contactsOnly, or all (default none)"
154
+ },
155
+ "start_date": {
156
+ "type": "string",
157
+ "description": "Scheduled start date/time in ISO 8601 format (for enable with schedule)"
158
+ },
159
+ "end_date": {
160
+ "type": "string",
161
+ "description": "Scheduled end date/time in ISO 8601 format (for enable with schedule)"
162
+ },
163
+ "time_zone": {
164
+ "type": "string",
165
+ "description": "IANA time zone for scheduled dates (default: system timezone)"
166
+ },
167
+ "confidence": {
168
+ "type": "number",
169
+ "description": "Confidence score (0-1) for this action"
170
+ },
171
+ "activity": {
172
+ "type": "string",
173
+ "description": "Brief non-technical explanation of why this tool is being called"
174
+ },
175
+ "account": {
176
+ "type": "string",
177
+ "description": "Email address of the Outlook account to use. Required when multiple Microsoft accounts are connected. If omitted, uses the most recently connected account."
178
+ }
179
+ },
180
+ "required": ["action"]
181
+ },
182
+ "executor": "tools/outlook-vacation.ts",
183
+ "execution_target": "host"
184
+ },
185
+ {
186
+ "name": "outlook_sender_digest",
187
+ "description": "Scan Outlook inbox and group messages by sender to identify high-volume senders (e.g. newsletters). Returns top senders sorted by message count with metadata for bulk cleanup.",
188
+ "category": "outlook",
189
+ "risk": "low",
190
+ "input_schema": {
191
+ "type": "object",
192
+ "properties": {
193
+ "query": {
194
+ "type": "string",
195
+ "description": "Optional search query to narrow results. If omitted, scans all inbox messages from the last 90 days."
196
+ },
197
+ "max_senders": {
198
+ "type": "number",
199
+ "description": "Maximum senders to return (default 50)"
200
+ },
201
+ "activity": {
202
+ "type": "string",
203
+ "description": "Brief non-technical explanation of why this tool is being called"
204
+ },
205
+ "account": {
206
+ "type": "string",
207
+ "description": "Email address of the Outlook account to use. Required when multiple Microsoft accounts are connected. If omitted, uses the most recently connected account."
208
+ }
209
+ }
210
+ },
211
+ "executor": "tools/outlook-sender-digest.ts",
212
+ "execution_target": "host"
213
+ },
214
+ {
215
+ "name": "outlook_outreach_scan",
216
+ "description": "Scan Outlook inbox for senders without List-Unsubscribe headers (potential cold outreach). Returns top senders with message counts and sample subjects. Read-only - use outlook_archive and outlook_mail_rules for cleanup.",
217
+ "category": "outlook",
218
+ "risk": "low",
219
+ "input_schema": {
220
+ "type": "object",
221
+ "properties": {
222
+ "max_senders": {
223
+ "type": "number",
224
+ "description": "Maximum senders to return (default 30)"
225
+ },
226
+ "time_range": {
227
+ "type": "string",
228
+ "description": "Time range filter (default '90d'). Accepts values like '30d', '7d', '90d'."
229
+ },
230
+ "activity": {
231
+ "type": "string",
232
+ "description": "Brief non-technical explanation of why this tool is being called"
233
+ },
234
+ "account": {
235
+ "type": "string",
236
+ "description": "Email address of the Outlook account to use. Required when multiple Microsoft accounts are connected. If omitted, uses the most recently connected account."
237
+ }
238
+ }
239
+ },
240
+ "executor": "tools/outlook-outreach-scan.ts",
241
+ "execution_target": "host"
242
+ },
243
+ {
244
+ "name": "outlook_draft",
245
+ "description": "Create a draft email in Outlook. The draft will appear in Outlook Drafts for user review. Use in_reply_to with an Outlook message ID to create a reply draft.",
246
+ "category": "outlook",
247
+ "risk": "low",
248
+ "input_schema": {
249
+ "type": "object",
250
+ "properties": {
251
+ "to": {
252
+ "type": "string",
253
+ "description": "Recipient email address (comma-separated for multiple)"
254
+ },
255
+ "subject": {
256
+ "type": "string",
257
+ "description": "Email subject line"
258
+ },
259
+ "body": {
260
+ "type": "string",
261
+ "description": "Email body (plain text)"
262
+ },
263
+ "in_reply_to": {
264
+ "type": "string",
265
+ "description": "Outlook message ID to reply to. Creates a reply draft that preserves the conversation thread."
266
+ },
267
+ "cc": {
268
+ "type": "string",
269
+ "description": "CC recipients (comma-separated email addresses)"
270
+ },
271
+ "bcc": {
272
+ "type": "string",
273
+ "description": "BCC recipients (comma-separated email addresses)"
274
+ },
275
+ "activity": {
276
+ "type": "string",
277
+ "description": "Brief non-technical explanation of why this tool is being called"
278
+ },
279
+ "account": {
280
+ "type": "string",
281
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
282
+ }
283
+ },
284
+ "required": ["to", "subject", "body"]
285
+ },
286
+ "executor": "tools/outlook-draft.ts",
287
+ "execution_target": "host"
288
+ },
289
+ {
290
+ "name": "outlook_send_draft",
291
+ "description": "Send an existing Outlook draft. Only use when the user has reviewed and explicitly approved sending.",
292
+ "category": "outlook",
293
+ "risk": "high",
294
+ "input_schema": {
295
+ "type": "object",
296
+ "properties": {
297
+ "draft_id": {
298
+ "type": "string",
299
+ "description": "Outlook draft message ID to send"
300
+ },
301
+ "confidence": {
302
+ "type": "number",
303
+ "description": "Confidence score (0-1) for this action"
304
+ },
305
+ "activity": {
306
+ "type": "string",
307
+ "description": "Brief non-technical explanation of why this tool is being called"
308
+ },
309
+ "account": {
310
+ "type": "string",
311
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
312
+ }
313
+ },
314
+ "required": ["draft_id", "confidence"]
315
+ },
316
+ "executor": "tools/outlook-send-draft.ts",
317
+ "execution_target": "host"
318
+ },
319
+ {
320
+ "name": "outlook_forward",
321
+ "description": "Create a draft forwarding an Outlook message to another recipient, preserving attachments. Microsoft Graph handles attachment forwarding natively.",
322
+ "category": "outlook",
323
+ "risk": "low",
324
+ "input_schema": {
325
+ "type": "object",
326
+ "properties": {
327
+ "message_id": {
328
+ "type": "string",
329
+ "description": "Outlook message ID to forward"
330
+ },
331
+ "to": {
332
+ "type": "string",
333
+ "description": "Recipient email address (comma-separated for multiple)"
334
+ },
335
+ "comment": {
336
+ "type": "string",
337
+ "description": "Optional text to include with the forwarded message"
338
+ },
339
+ "confidence": {
340
+ "type": "number",
341
+ "description": "Confidence score (0-1) for this action"
342
+ },
343
+ "activity": {
344
+ "type": "string",
345
+ "description": "Brief non-technical explanation of why this tool is being called"
346
+ },
347
+ "account": {
348
+ "type": "string",
349
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
350
+ }
351
+ },
352
+ "required": ["message_id", "to", "confidence"]
353
+ },
354
+ "executor": "tools/outlook-forward.ts",
355
+ "execution_target": "host"
356
+ },
357
+ {
358
+ "name": "outlook_trash",
359
+ "description": "Move an Outlook message to Deleted Items. Include a confidence score (0-1).",
360
+ "category": "outlook",
361
+ "risk": "medium",
362
+ "input_schema": {
363
+ "type": "object",
364
+ "properties": {
365
+ "message_id": {
366
+ "type": "string",
367
+ "description": "Outlook message ID to move to Deleted Items"
368
+ },
369
+ "confidence": {
370
+ "type": "number",
371
+ "description": "Confidence score (0-1) for this action"
372
+ },
373
+ "activity": {
374
+ "type": "string",
375
+ "description": "Brief non-technical explanation of why this tool is being called"
376
+ },
377
+ "account": {
378
+ "type": "string",
379
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
380
+ }
381
+ },
382
+ "required": ["message_id", "confidence"]
383
+ },
384
+ "executor": "tools/outlook-trash.ts",
385
+ "execution_target": "host"
386
+ },
387
+ {
388
+ "name": "outlook_categories",
389
+ "description": "Add, remove, or list available categories on Outlook messages. Include a confidence score (0-1) for add/remove actions.",
390
+ "category": "outlook",
391
+ "risk": "medium",
392
+ "input_schema": {
393
+ "type": "object",
394
+ "properties": {
395
+ "action": {
396
+ "type": "string",
397
+ "enum": ["add", "remove", "list_available"],
398
+ "description": "Category action"
399
+ },
400
+ "message_id": {
401
+ "type": "string",
402
+ "description": "Outlook message ID (required for add/remove)"
403
+ },
404
+ "categories": {
405
+ "type": "array",
406
+ "items": {
407
+ "type": "string"
408
+ },
409
+ "description": "Category names to add or remove"
410
+ },
411
+ "confidence": {
412
+ "type": "number",
413
+ "description": "Confidence score (0-1) for this action"
414
+ },
415
+ "activity": {
416
+ "type": "string",
417
+ "description": "Brief non-technical explanation of why this tool is being called"
418
+ },
419
+ "account": {
420
+ "type": "string",
421
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
422
+ }
423
+ },
424
+ "required": ["action", "confidence"]
425
+ },
426
+ "executor": "tools/outlook-categories.ts",
427
+ "execution_target": "host"
428
+ },
429
+ {
430
+ "name": "outlook_follow_up",
431
+ "description": "Track, list, untrack, or complete follow-up flags on Outlook messages using the native flag system. Include a confidence score (0-1) for track/complete/untrack actions.",
432
+ "category": "outlook",
433
+ "risk": "medium",
434
+ "input_schema": {
435
+ "type": "object",
436
+ "properties": {
437
+ "action": {
438
+ "type": "string",
439
+ "enum": ["track", "list", "untrack", "complete"],
440
+ "description": "Follow-up action"
441
+ },
442
+ "message_id": {
443
+ "type": "string",
444
+ "description": "Outlook message ID (required for track/complete/untrack)"
445
+ },
446
+ "confidence": {
447
+ "type": "number",
448
+ "description": "Confidence score (0-1) for this action"
449
+ },
450
+ "activity": {
451
+ "type": "string",
452
+ "description": "Brief non-technical explanation of why this tool is being called"
453
+ },
454
+ "account": {
455
+ "type": "string",
456
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
457
+ }
458
+ },
459
+ "required": ["action", "confidence"]
460
+ },
461
+ "executor": "tools/outlook-follow-up.ts",
462
+ "execution_target": "host"
463
+ },
464
+ {
465
+ "name": "outlook_unsubscribe",
466
+ "description": "Unsubscribe from a mailing list by following the List-Unsubscribe header on an Outlook message. Include a confidence score (0-1).",
467
+ "category": "outlook",
468
+ "risk": "medium",
469
+ "input_schema": {
470
+ "type": "object",
471
+ "properties": {
472
+ "message_id": {
473
+ "type": "string",
474
+ "description": "An Outlook message ID from the mailing list to unsubscribe from"
475
+ },
476
+ "confidence": {
477
+ "type": "number",
478
+ "description": "Confidence score (0-1) for this action"
479
+ },
480
+ "activity": {
481
+ "type": "string",
482
+ "description": "Brief non-technical explanation of why this tool is being called"
483
+ },
484
+ "account": {
485
+ "type": "string",
486
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
487
+ }
488
+ },
489
+ "required": ["message_id", "confidence"]
490
+ },
491
+ "executor": "tools/outlook-unsubscribe.ts",
492
+ "execution_target": "host"
493
+ },
494
+ {
495
+ "name": "outlook_attachments",
496
+ "description": "List or download Outlook email attachments. Use action 'list' to enumerate attachments on a message, then 'download' to save a specific attachment to disk.",
497
+ "category": "outlook",
498
+ "risk": "low",
499
+ "input_schema": {
500
+ "type": "object",
501
+ "properties": {
502
+ "action": {
503
+ "type": "string",
504
+ "enum": ["list", "download"],
505
+ "description": "List attachments on a message, or download a specific attachment"
506
+ },
507
+ "message_id": {
508
+ "type": "string",
509
+ "description": "Outlook message ID"
510
+ },
511
+ "attachment_id": {
512
+ "type": "string",
513
+ "description": "Attachment ID (required for download, from a prior list call)"
514
+ },
515
+ "activity": {
516
+ "type": "string",
517
+ "description": "Brief non-technical explanation of why this tool is being called"
518
+ },
519
+ "account": {
520
+ "type": "string",
521
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
522
+ }
523
+ },
524
+ "required": ["action", "message_id"]
525
+ },
526
+ "executor": "tools/outlook-attachments.ts",
527
+ "execution_target": "host"
528
+ }
529
+ ]
530
+ }
@@ -0,0 +1,85 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { basename, resolve } from "node:path";
3
+
4
+ import {
5
+ getAttachment,
6
+ listAttachments,
7
+ } from "../../../../messaging/providers/outlook/client.js";
8
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
9
+ import type {
10
+ ToolContext,
11
+ ToolExecutionResult,
12
+ } from "../../../../tools/types.js";
13
+ import { err, ok } from "./shared.js";
14
+
15
+ export async function run(
16
+ input: Record<string, unknown>,
17
+ context: ToolContext,
18
+ ): Promise<ToolExecutionResult> {
19
+ const account = input.account as string | undefined;
20
+ const action = input.action as string;
21
+ const messageId = input.message_id as string;
22
+
23
+ if (!action) return err("action is required.");
24
+ if (!messageId) return err("message_id is required.");
25
+
26
+ if (action === "list") {
27
+ try {
28
+ const connection = await resolveOAuthConnection("outlook", {
29
+ account,
30
+ });
31
+ const response = await listAttachments(connection, messageId);
32
+ const attachments = response.value ?? [];
33
+
34
+ if (attachments.length === 0) {
35
+ return ok("No attachments found on this message.");
36
+ }
37
+
38
+ const result = attachments.map((a) => ({
39
+ attachmentId: a.id,
40
+ name: a.name,
41
+ contentType: a.contentType,
42
+ size: a.size,
43
+ isInline: a.isInline,
44
+ }));
45
+
46
+ return ok(JSON.stringify(result, null, 2));
47
+ } catch (e) {
48
+ return err(e instanceof Error ? e.message : String(e));
49
+ }
50
+ }
51
+
52
+ if (action === "download") {
53
+ const attachmentId = input.attachment_id as string;
54
+
55
+ if (!attachmentId) return err("attachment_id is required for download.");
56
+
57
+ try {
58
+ const connection = await resolveOAuthConnection("outlook", {
59
+ account,
60
+ });
61
+ const attachment = await getAttachment(
62
+ connection,
63
+ messageId,
64
+ attachmentId,
65
+ );
66
+
67
+ // Outlook returns standard base64 in contentBytes
68
+ const buffer = Buffer.from(attachment.contentBytes, "base64");
69
+
70
+ const outputDir = context.workingDir ?? process.cwd();
71
+ // Sanitize filename: strip path separators to prevent traversal attacks
72
+ const safeName = basename(attachment.name).replace(/\.\./g, "_");
73
+ const outputPath = resolve(outputDir, safeName);
74
+ if (!outputPath.startsWith(outputDir))
75
+ return err("Invalid filename: path traversal detected.");
76
+ await writeFile(outputPath, buffer);
77
+
78
+ return ok(`Attachment saved to ${outputPath} (${buffer.length} bytes).`);
79
+ } catch (e) {
80
+ return err(e instanceof Error ? e.message : String(e));
81
+ }
82
+ }
83
+
84
+ return err(`Unknown action: ${action}. Expected "list" or "download".`);
85
+ }
@@ -0,0 +1,77 @@
1
+ import {
2
+ getMessage,
3
+ listMasterCategories,
4
+ updateMessageCategories,
5
+ } from "../../../../messaging/providers/outlook/client.js";
6
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
7
+ import type {
8
+ ToolContext,
9
+ ToolExecutionResult,
10
+ } from "../../../../tools/types.js";
11
+ import { err, ok } from "./shared.js";
12
+
13
+ export async function run(
14
+ input: Record<string, unknown>,
15
+ _context: ToolContext,
16
+ ): Promise<ToolExecutionResult> {
17
+ const account = input.account as string | undefined;
18
+ const action = input.action as string;
19
+
20
+ if (!action) {
21
+ return err("action is required (add, remove, or list_available).");
22
+ }
23
+
24
+ try {
25
+ const connection = await resolveOAuthConnection("outlook", {
26
+ account,
27
+ });
28
+
29
+ switch (action) {
30
+ case "add": {
31
+ const messageId = input.message_id as string;
32
+ if (!messageId) return err("message_id is required for add action.");
33
+
34
+ const categories = input.categories as string[] | undefined;
35
+ if (!categories || categories.length === 0) {
36
+ return err("categories is required for add action.");
37
+ }
38
+
39
+ const message = await getMessage(connection, messageId, "categories");
40
+ const existing = message.categories ?? [];
41
+ const merged = [...new Set([...existing, ...categories])];
42
+ await updateMessageCategories(connection, messageId, merged);
43
+ return ok("Categories updated.");
44
+ }
45
+
46
+ case "remove": {
47
+ const messageId = input.message_id as string;
48
+ if (!messageId) return err("message_id is required for remove action.");
49
+
50
+ const categories = input.categories as string[] | undefined;
51
+ if (!categories || categories.length === 0) {
52
+ return err("categories is required for remove action.");
53
+ }
54
+
55
+ const removeSet = new Set(categories);
56
+ const message = await getMessage(connection, messageId, "categories");
57
+ const existing = message.categories ?? [];
58
+ const filtered = existing.filter((c) => !removeSet.has(c));
59
+ await updateMessageCategories(connection, messageId, filtered);
60
+ return ok("Categories updated.");
61
+ }
62
+
63
+ case "list_available": {
64
+ const resp = await listMasterCategories(connection);
65
+ const categories = resp.value ?? [];
66
+ return ok(JSON.stringify(categories, null, 2));
67
+ }
68
+
69
+ default:
70
+ return err(
71
+ `Unknown action "${action}". Use add, remove, or list_available.`,
72
+ );
73
+ }
74
+ } catch (e) {
75
+ return err(e instanceof Error ? e.message : String(e));
76
+ }
77
+ }