@vellumai/assistant 0.5.16 → 0.6.1

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 (592) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +69 -16
  3. package/Dockerfile +2 -5
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +32 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/knip.json +2 -1
  9. package/openapi.yaml +1198 -83
  10. package/package.json +5 -1
  11. package/src/__tests__/actor-token-service.test.ts +68 -0
  12. package/src/__tests__/agent-loop.test.ts +0 -32
  13. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  14. package/src/__tests__/anthropic-provider.test.ts +217 -98
  15. package/src/__tests__/app-compiler.test.ts +120 -0
  16. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  17. package/src/__tests__/app-executors.test.ts +47 -1
  18. package/src/__tests__/app-source-watcher.test.ts +159 -0
  19. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  20. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  21. package/src/__tests__/call-domain.test.ts +2 -6
  22. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  23. package/src/__tests__/call-recovery.test.ts +2 -6
  24. package/src/__tests__/call-routes-http.test.ts +2 -6
  25. package/src/__tests__/call-store.test.ts +2 -6
  26. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  27. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  28. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  29. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  30. package/src/__tests__/checker.test.ts +63 -9
  31. package/src/__tests__/clawhub.test.ts +54 -24
  32. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  33. package/src/__tests__/config-schema.test.ts +6 -1
  34. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  35. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  36. package/src/__tests__/contacts-tools.test.ts +31 -0
  37. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  38. package/src/__tests__/context-token-estimator.test.ts +175 -10
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
  40. package/src/__tests__/conversation-agent-loop.test.ts +13 -51
  41. package/src/__tests__/conversation-attachments.test.ts +2 -6
  42. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  43. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  44. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  45. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  46. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  47. package/src/__tests__/conversation-error.test.ts +33 -2
  48. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  49. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  50. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  53. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  54. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  55. package/src/__tests__/conversation-store.test.ts +2 -6
  56. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  57. package/src/__tests__/conversation-usage.test.ts +2 -6
  58. package/src/__tests__/conversation-wipe.test.ts +13 -414
  59. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  60. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  61. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  62. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  63. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  64. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  65. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  66. package/src/__tests__/date-context.test.ts +76 -210
  67. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  68. package/src/__tests__/file-list-tool.test.ts +219 -0
  69. package/src/__tests__/first-greeting.test.ts +1 -1
  70. package/src/__tests__/followup-tools.test.ts +2 -6
  71. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  72. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  73. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  74. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  75. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  76. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  77. package/src/__tests__/guardian-action-store.test.ts +2 -6
  78. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  79. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  80. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  81. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  82. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  83. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  84. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  85. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  86. package/src/__tests__/heartbeat-service.test.ts +180 -3
  87. package/src/__tests__/identity-routes.test.ts +328 -0
  88. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  89. package/src/__tests__/injection-block.test.ts +178 -0
  90. package/src/__tests__/install-meta.test.ts +506 -0
  91. package/src/__tests__/install-skill-routing.test.ts +293 -0
  92. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  93. package/src/__tests__/invite-routes-http.test.ts +2 -6
  94. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
  95. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  96. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  97. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  98. package/src/__tests__/llm-context-route-provider.test.ts +103 -6
  99. package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
  100. package/src/__tests__/llm-usage-store.test.ts +2 -6
  101. package/src/__tests__/log-export-workspace.test.ts +74 -111
  102. package/src/__tests__/managed-store.test.ts +38 -11
  103. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  104. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  105. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  106. package/src/__tests__/memory-recall-log-store.test.ts +134 -6
  107. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  108. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  109. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  110. package/src/__tests__/mock-fetch.ts +87 -0
  111. package/src/__tests__/non-member-access-request.test.ts +2 -6
  112. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  113. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  114. package/src/__tests__/oauth-cli.test.ts +364 -2
  115. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  116. package/src/__tests__/onboarding-template-contract.test.ts +62 -14
  117. package/src/__tests__/outlook-attachments.test.ts +301 -0
  118. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  119. package/src/__tests__/outlook-categories.test.ts +212 -0
  120. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  121. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  122. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  123. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  124. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  125. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  126. package/src/__tests__/outlook-trash.test.ts +77 -0
  127. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  128. package/src/__tests__/parser.test.ts +32 -0
  129. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  130. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  131. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  132. package/src/__tests__/permission-mode-store.test.ts +277 -0
  133. package/src/__tests__/permission-mode.test.ts +101 -0
  134. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  135. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  136. package/src/__tests__/playbook-execution.test.ts +76 -80
  137. package/src/__tests__/playbook-tools.test.ts +5 -7
  138. package/src/__tests__/profiler-routes.test.ts +502 -0
  139. package/src/__tests__/profiler-run-store.test.ts +441 -0
  140. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  141. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  142. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  143. package/src/__tests__/registry.test.ts +3 -3
  144. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  145. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  146. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  147. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  148. package/src/__tests__/schedule-store.test.ts +2 -6
  149. package/src/__tests__/schedule-tools.test.ts +2 -6
  150. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  151. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  152. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  153. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  154. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  155. package/src/__tests__/search-skills-unified.test.ts +422 -0
  156. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  157. package/src/__tests__/send-endpoint-busy.test.ts +44 -9
  158. package/src/__tests__/sequence-store.test.ts +2 -6
  159. package/src/__tests__/server-history-render.test.ts +2 -6
  160. package/src/__tests__/set-permission-mode.test.ts +274 -0
  161. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  162. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  163. package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
  164. package/src/__tests__/skill-memory.test.ts +2 -741
  165. package/src/__tests__/skills-uninstall.test.ts +2 -2
  166. package/src/__tests__/skills.test.ts +1 -1
  167. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  168. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  169. package/src/__tests__/subagent-detail.test.ts +84 -0
  170. package/src/__tests__/subagent-disposal.test.ts +308 -0
  171. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  172. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  173. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  174. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  175. package/src/__tests__/subagent-tools.test.ts +464 -4
  176. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  177. package/src/__tests__/task-compiler.test.ts +2 -6
  178. package/src/__tests__/task-management-tools.test.ts +2 -6
  179. package/src/__tests__/task-memory-cleanup.test.ts +185 -241
  180. package/src/__tests__/task-runner.test.ts +2 -6
  181. package/src/__tests__/task-scheduler.test.ts +2 -6
  182. package/src/__tests__/terminal-tools.test.ts +17 -27
  183. package/src/__tests__/test-preload.ts +7 -0
  184. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  185. package/src/__tests__/tool-executor.test.ts +4 -26
  186. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  187. package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
  188. package/src/__tests__/top-level-renderer.test.ts +10 -13
  189. package/src/__tests__/trust-store.test.ts +1 -1
  190. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  191. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
  192. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  193. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  194. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  195. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  196. package/src/__tests__/usage-routes.test.ts +2 -6
  197. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  198. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  199. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  200. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  201. package/src/__tests__/volume-security-guard.test.ts +2 -0
  202. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  203. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  204. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  205. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  206. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  207. package/src/__tests__/workspace-policy.test.ts +1 -1
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +7 -15
  211. package/src/approvals/guardian-request-resolvers.ts +24 -0
  212. package/src/avatar/traits-png-sync.ts +3 -3
  213. package/src/bundler/app-compiler.ts +179 -2
  214. package/src/bundler/package-resolver.ts +3 -5
  215. package/src/cli/__tests__/notifications.test.ts +1 -2
  216. package/src/cli/__tests__/run-assistant-command.ts +29 -0
  217. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  218. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  219. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  220. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  221. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  222. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  223. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  224. package/src/cli/commands/avatar.ts +3 -3
  225. package/src/cli/commands/config.ts +26 -13
  226. package/src/cli/commands/conversations.ts +1 -8
  227. package/src/cli/commands/doctor.ts +2 -2
  228. package/src/cli/commands/email.ts +584 -835
  229. package/src/cli/commands/memory.ts +37 -84
  230. package/src/cli/commands/notifications.ts +7 -2
  231. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  232. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  233. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  234. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  235. package/src/cli/commands/oauth/connect.ts +25 -11
  236. package/src/cli/commands/oauth/mode.ts +7 -0
  237. package/src/cli/commands/oauth/shared.ts +39 -3
  238. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  239. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  240. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  241. package/src/cli/commands/platform/index.ts +16 -16
  242. package/src/cli/commands/routes.ts +396 -0
  243. package/src/cli/commands/skills.ts +218 -36
  244. package/src/cli/commands/trust.ts +2 -2
  245. package/src/cli/lib/daemon-credential-client.ts +2 -3
  246. package/src/cli/program.ts +2 -0
  247. package/src/cli.ts +1 -120
  248. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  249. package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
  250. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  251. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  252. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  253. package/src/config/bundled-skills/gmail/SKILL.md +4 -12
  254. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  255. package/src/config/bundled-skills/messaging/SKILL.md +17 -18
  256. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  257. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  258. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  259. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  260. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  261. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  262. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  263. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  264. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  265. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  266. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  267. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  268. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  269. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  270. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  271. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  272. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  273. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  274. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  275. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  276. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  277. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  278. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  279. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  280. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  281. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  282. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  283. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  284. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  285. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  286. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  287. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  288. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  289. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  290. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  291. package/src/config/bundled-skills/slack/SKILL.md +3 -7
  292. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  293. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  294. package/src/config/bundled-tool-registry.ts +56 -4
  295. package/src/config/env-registry.ts +78 -8
  296. package/src/config/feature-flag-registry.json +38 -125
  297. package/src/config/schema.ts +8 -0
  298. package/src/config/schemas/filing.ts +51 -0
  299. package/src/config/schemas/heartbeat.ts +15 -12
  300. package/src/config/schemas/memory-lifecycle.ts +12 -0
  301. package/src/config/schemas/platform.ts +8 -0
  302. package/src/config/schemas/security.ts +14 -0
  303. package/src/config/schemas/timeouts.ts +1 -1
  304. package/src/config/skills.ts +18 -7
  305. package/src/context/token-estimator.ts +25 -18
  306. package/src/context/window-manager.ts +6 -2
  307. package/src/credential-execution/process-manager.ts +3 -1
  308. package/src/daemon/app-source-watcher.ts +93 -0
  309. package/src/daemon/config-watcher.ts +79 -1
  310. package/src/daemon/context-overflow-reducer.ts +46 -2
  311. package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
  312. package/src/daemon/conversation-agent-loop.ts +236 -108
  313. package/src/daemon/conversation-error.ts +31 -8
  314. package/src/daemon/conversation-history.ts +4 -19
  315. package/src/daemon/conversation-lifecycle.ts +36 -9
  316. package/src/daemon/conversation-media-retry.ts +85 -7
  317. package/src/daemon/conversation-notifiers.ts +4 -1
  318. package/src/daemon/conversation-process.ts +13 -7
  319. package/src/daemon/conversation-runtime-assembly.ts +305 -306
  320. package/src/daemon/conversation-tool-setup.ts +44 -14
  321. package/src/daemon/conversation-workspace.ts +1 -2
  322. package/src/daemon/conversation.ts +59 -2
  323. package/src/daemon/daemon-control.ts +8 -2
  324. package/src/daemon/date-context.ts +26 -53
  325. package/src/daemon/first-greeting.ts +1 -1
  326. package/src/daemon/handlers/conversations.ts +4 -7
  327. package/src/daemon/handlers/shared.test.ts +143 -0
  328. package/src/daemon/handlers/shared.ts +85 -17
  329. package/src/daemon/handlers/skills.ts +416 -209
  330. package/src/daemon/lifecycle.ts +212 -131
  331. package/src/daemon/main.ts +5 -1
  332. package/src/daemon/message-types/conversations.ts +29 -7
  333. package/src/daemon/message-types/messages.ts +12 -2
  334. package/src/daemon/message-types/schedules.ts +1 -0
  335. package/src/daemon/message-types/settings.ts +6 -0
  336. package/src/daemon/message-types/skills.ts +97 -36
  337. package/src/daemon/profiler-run-store.ts +557 -0
  338. package/src/daemon/providers-setup.ts +5 -0
  339. package/src/daemon/server.ts +100 -11
  340. package/src/daemon/shutdown-handlers.ts +5 -0
  341. package/src/daemon/tool-side-effects.ts +50 -8
  342. package/src/export/transcript-formatter.ts +148 -0
  343. package/src/filing/filing-service.ts +228 -0
  344. package/src/heartbeat/heartbeat-service.ts +97 -7
  345. package/src/hooks/cli.ts +2 -2
  346. package/src/hooks/runner.ts +15 -38
  347. package/src/inbound/platform-callback-registration.ts +14 -14
  348. package/src/mcp/client.ts +6 -0
  349. package/src/mcp/mcp-oauth-provider.ts +149 -27
  350. package/src/memory/admin.ts +42 -75
  351. package/src/memory/app-store.ts +69 -0
  352. package/src/memory/conversation-bootstrap.ts +3 -1
  353. package/src/memory/conversation-crud.ts +211 -288
  354. package/src/memory/conversation-group-migration.ts +157 -0
  355. package/src/memory/conversation-queries.ts +61 -13
  356. package/src/memory/conversation-title-service.ts +1 -0
  357. package/src/memory/db-init.ts +194 -361
  358. package/src/memory/embed.ts +73 -0
  359. package/src/memory/embedding-backend.ts +8 -14
  360. package/src/memory/embedding-runtime-manager.ts +12 -114
  361. package/src/memory/fingerprint.ts +2 -2
  362. package/src/memory/graph/bootstrap.ts +521 -0
  363. package/src/memory/graph/capability-seed.ts +449 -0
  364. package/src/memory/graph/consolidation.ts +725 -0
  365. package/src/memory/graph/conversation-graph-memory.ts +659 -0
  366. package/src/memory/graph/decay.test.ts +208 -0
  367. package/src/memory/graph/decay.ts +195 -0
  368. package/src/memory/graph/extraction-job.ts +74 -0
  369. package/src/memory/graph/extraction.test.ts +936 -0
  370. package/src/memory/graph/extraction.ts +1297 -0
  371. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  372. package/src/memory/graph/graph-search.ts +280 -0
  373. package/src/memory/graph/image-ref-utils.ts +29 -0
  374. package/src/memory/graph/injection.test.ts +513 -0
  375. package/src/memory/graph/injection.ts +469 -0
  376. package/src/memory/graph/inspect.ts +543 -0
  377. package/src/memory/graph/narrative.ts +267 -0
  378. package/src/memory/graph/pattern-scan.ts +269 -0
  379. package/src/memory/graph/retriever.ts +1111 -0
  380. package/src/memory/graph/scoring.test.ts +548 -0
  381. package/src/memory/graph/scoring.ts +232 -0
  382. package/src/memory/graph/serendipity.ts +65 -0
  383. package/src/memory/graph/store.test.ts +1098 -0
  384. package/src/memory/graph/store.ts +838 -0
  385. package/src/memory/graph/tool-handlers.ts +301 -0
  386. package/src/memory/graph/tools.ts +97 -0
  387. package/src/memory/graph/triggers.test.ts +487 -0
  388. package/src/memory/graph/triggers.ts +223 -0
  389. package/src/memory/graph/types.ts +295 -0
  390. package/src/memory/group-crud.ts +191 -0
  391. package/src/memory/indexer.ts +37 -19
  392. package/src/memory/job-handlers/cleanup.ts +32 -42
  393. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  394. package/src/memory/job-handlers/embedding.ts +5 -31
  395. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  396. package/src/memory/job-handlers/summarization.ts +32 -17
  397. package/src/memory/job-utils.ts +1 -1
  398. package/src/memory/jobs-store.ts +21 -31
  399. package/src/memory/jobs-worker.ts +180 -129
  400. package/src/memory/llm-request-log-store.ts +96 -12
  401. package/src/memory/memory-recall-log-store.ts +49 -5
  402. package/src/memory/message-content.ts +1 -0
  403. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  404. package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
  405. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  406. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  407. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  408. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  409. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  410. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  411. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  412. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  413. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  414. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  415. package/src/memory/migrations/index.ts +12 -0
  416. package/src/memory/migrations/registry.ts +16 -0
  417. package/src/memory/qdrant-client.ts +44 -17
  418. package/src/memory/schema/conversations.ts +14 -0
  419. package/src/memory/schema/index.ts +1 -0
  420. package/src/memory/schema/infrastructure.ts +8 -1
  421. package/src/memory/schema/memory-core.ts +0 -51
  422. package/src/memory/schema/memory-graph.ts +154 -0
  423. package/src/memory/search/semantic.ts +47 -91
  424. package/src/memory/task-memory-cleanup.ts +58 -61
  425. package/src/messaging/providers/outlook/adapter.ts +8 -1
  426. package/src/messaging/providers/outlook/client.ts +299 -0
  427. package/src/messaging/providers/outlook/types.ts +118 -0
  428. package/src/notifications/adapters/macos.ts +1 -0
  429. package/src/notifications/copy-composer.ts +95 -0
  430. package/src/notifications/decision-engine.ts +35 -0
  431. package/src/notifications/signal.ts +16 -0
  432. package/src/oauth/seed-providers.ts +2 -1
  433. package/src/permissions/checker.ts +36 -4
  434. package/src/permissions/defaults.ts +4 -4
  435. package/src/permissions/permission-mode-store.ts +180 -0
  436. package/src/permissions/permission-mode.ts +31 -0
  437. package/src/permissions/workspace-policy.ts +10 -1
  438. package/src/playbooks/playbook-compiler.ts +19 -18
  439. package/src/playbooks/types.ts +4 -3
  440. package/src/prompts/system-prompt.ts +62 -36
  441. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  442. package/src/prompts/templates/BOOTSTRAP.md +70 -165
  443. package/src/prompts/templates/HEARTBEAT.md +3 -1
  444. package/src/prompts/templates/SOUL.md +25 -4
  445. package/src/prompts/templates/UPDATES.md +8 -0
  446. package/src/providers/anthropic/client.ts +136 -220
  447. package/src/providers/gemini/client.ts +1 -1
  448. package/src/providers/openai/client.ts +1 -1
  449. package/src/providers/registry.ts +1 -1
  450. package/src/providers/retry.ts +19 -3
  451. package/src/runtime/actor-trust-resolver.ts +5 -1
  452. package/src/runtime/auth/route-policy.ts +30 -0
  453. package/src/runtime/guardian-reply-router.ts +5 -1
  454. package/src/runtime/http-server.ts +55 -5
  455. package/src/runtime/http-types.ts +12 -1
  456. package/src/runtime/middleware/auth.ts +20 -0
  457. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  458. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  459. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  460. package/src/runtime/routes/app-management-routes.ts +1 -11
  461. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  462. package/src/runtime/routes/archive-utils.ts +29 -0
  463. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  464. package/src/runtime/routes/attachment-routes.ts +106 -16
  465. package/src/runtime/routes/avatar-routes.ts +2 -9
  466. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  467. package/src/runtime/routes/btw-routes.ts +22 -1
  468. package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
  469. package/src/runtime/routes/conversation-management-routes.ts +3 -14
  470. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  471. package/src/runtime/routes/conversation-routes.ts +264 -44
  472. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  473. package/src/runtime/routes/debug-routes.ts +1 -1
  474. package/src/runtime/routes/global-search-routes.ts +21 -19
  475. package/src/runtime/routes/group-routes.ts +207 -0
  476. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  477. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  478. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  479. package/src/runtime/routes/identity-routes.ts +53 -18
  480. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  481. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  482. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  483. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  484. package/src/runtime/routes/log-export-routes.ts +23 -275
  485. package/src/runtime/routes/memory-item-routes.test.ts +170 -247
  486. package/src/runtime/routes/memory-item-routes.ts +341 -388
  487. package/src/runtime/routes/migration-routes.ts +18 -7
  488. package/src/runtime/routes/profiler-routes.ts +350 -0
  489. package/src/runtime/routes/schedule-routes.ts +28 -11
  490. package/src/runtime/routes/settings-routes.ts +95 -8
  491. package/src/runtime/routes/skills-routes.ts +103 -37
  492. package/src/runtime/routes/subagents-routes.ts +28 -7
  493. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  494. package/src/runtime/routes/user-routes.ts +41 -0
  495. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  496. package/src/runtime/routes/workspace-routes.ts +0 -1
  497. package/src/schedule/schedule-store.ts +30 -0
  498. package/src/schedule/scheduler.ts +52 -18
  499. package/src/security/oauth2.ts +1 -1
  500. package/src/security/secure-keys.ts +4 -8
  501. package/src/shared/provider-env-vars.ts +19 -0
  502. package/src/skills/catalog-cache.ts +5 -0
  503. package/src/skills/catalog-install.ts +25 -16
  504. package/src/skills/clawhub.ts +134 -154
  505. package/src/skills/install-meta.ts +208 -0
  506. package/src/skills/managed-store.ts +29 -18
  507. package/src/skills/skill-memory.ts +12 -229
  508. package/src/skills/skillssh-registry.ts +19 -17
  509. package/src/subagent/index.ts +13 -3
  510. package/src/subagent/manager.ts +308 -29
  511. package/src/subagent/types.ts +68 -0
  512. package/src/tasks/task-runner.ts +7 -5
  513. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  514. package/src/tools/apps/executors.ts +29 -4
  515. package/src/tools/browser/runtime-check.ts +3 -1
  516. package/src/tools/filesystem/list.ts +93 -0
  517. package/src/tools/memory/register.ts +63 -46
  518. package/src/tools/permission-checker.ts +85 -1
  519. package/src/tools/registry.ts +4 -0
  520. package/src/tools/schedule/create.ts +3 -0
  521. package/src/tools/schedule/list.ts +1 -0
  522. package/src/tools/schedule/update.ts +6 -0
  523. package/src/tools/shared/filesystem/errors.ts +5 -0
  524. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  525. package/src/tools/shared/filesystem/image-read.ts +22 -85
  526. package/src/tools/shared/filesystem/types.ts +17 -0
  527. package/src/tools/shared/shell-output.ts +31 -2
  528. package/src/tools/subagent/abort.ts +12 -2
  529. package/src/tools/subagent/message.ts +9 -2
  530. package/src/tools/subagent/notify-parent.ts +79 -0
  531. package/src/tools/subagent/read.ts +29 -8
  532. package/src/tools/subagent/resolve.ts +21 -0
  533. package/src/tools/subagent/spawn.ts +2 -0
  534. package/src/tools/subagent/status.ts +11 -1
  535. package/src/tools/system/avatar-generator.ts +3 -3
  536. package/src/tools/system/register.ts +23 -0
  537. package/src/tools/system/set-permission-mode.ts +103 -0
  538. package/src/tools/terminal/parser.ts +30 -5
  539. package/src/tools/terminal/safe-env.ts +17 -1
  540. package/src/tools/tool-manifest.ts +9 -3
  541. package/src/tools/types.ts +2 -0
  542. package/src/util/browser.ts +25 -10
  543. package/src/util/bun-runtime.ts +172 -0
  544. package/src/util/logger.ts +1 -1
  545. package/src/util/platform.ts +50 -17
  546. package/src/watcher/providers/outlook-calendar.ts +343 -0
  547. package/src/watcher/providers/outlook.ts +198 -0
  548. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  549. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  550. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  551. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  552. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  553. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  554. package/src/workspace/migrations/029-seed-pkb.ts +84 -0
  555. package/src/workspace/migrations/registry.ts +10 -0
  556. package/src/workspace/top-level-renderer.ts +5 -9
  557. package/src/__tests__/cli-memory.test.ts +0 -372
  558. package/src/__tests__/clipboard.test.ts +0 -88
  559. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  560. package/src/__tests__/journal-context.test.ts +0 -268
  561. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  562. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  563. package/src/__tests__/memory-query-builder.test.ts +0 -59
  564. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  565. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  566. package/src/__tests__/memory-regressions.test.ts +0 -3696
  567. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  568. package/src/cli/cli-memory.ts +0 -176
  569. package/src/daemon/conversation-memory.ts +0 -207
  570. package/src/memory/conversation-starters-cadence.ts +0 -74
  571. package/src/memory/items-extractor.ts +0 -860
  572. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  573. package/src/memory/job-handlers/extraction.ts +0 -40
  574. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  575. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  576. package/src/memory/journal-memory.ts +0 -224
  577. package/src/memory/query-builder.ts +0 -47
  578. package/src/memory/query-expansion.ts +0 -83
  579. package/src/memory/retriever.test.ts +0 -1592
  580. package/src/memory/retriever.ts +0 -1331
  581. package/src/memory/search/formatting.test.ts +0 -140
  582. package/src/memory/search/formatting.ts +0 -262
  583. package/src/memory/search/mmr.ts +0 -139
  584. package/src/memory/search/ranking.ts +0 -15
  585. package/src/memory/search/staleness.ts +0 -40
  586. package/src/memory/search/tier-classifier.ts +0 -18
  587. package/src/memory/search/types.ts +0 -121
  588. package/src/prompts/journal-context.ts +0 -154
  589. package/src/tools/memory/definitions.ts +0 -69
  590. package/src/tools/memory/handlers.test.ts +0 -562
  591. package/src/tools/memory/handlers.ts +0 -434
  592. package/src/util/clipboard.ts +0 -34
@@ -0,0 +1,84 @@
1
+ import {
2
+ createDraft,
3
+ createReplyDraft,
4
+ patchMessage,
5
+ } from "../../../../messaging/providers/outlook/client.js";
6
+ import type { OutlookRecipient } from "../../../../messaging/providers/outlook/types.js";
7
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
8
+ import type {
9
+ ToolContext,
10
+ ToolExecutionResult,
11
+ } from "../../../../tools/types.js";
12
+ import { err, ok } from "./shared.js";
13
+
14
+ function toRecipients(csv: string | undefined): OutlookRecipient[] | undefined {
15
+ if (!csv) return undefined;
16
+ return csv
17
+ .split(",")
18
+ .map((s) => s.trim())
19
+ .filter(Boolean)
20
+ .map((address) => ({ emailAddress: { address } }));
21
+ }
22
+
23
+ export async function run(
24
+ input: Record<string, unknown>,
25
+ _context: ToolContext,
26
+ ): Promise<ToolExecutionResult> {
27
+ const account = input.account as string | undefined;
28
+ const to = input.to as string;
29
+ const subject = input.subject as string;
30
+ const body = input.body as string;
31
+ const inReplyTo = input.in_reply_to as string | undefined;
32
+ const cc = input.cc as string | undefined;
33
+ const bcc = input.bcc as string | undefined;
34
+
35
+ if (!to) return err("to is required.");
36
+ if (!subject) return err("subject is required.");
37
+ if (!body) return err("body is required.");
38
+
39
+ try {
40
+ const connection = await resolveOAuthConnection("outlook", {
41
+ account,
42
+ });
43
+
44
+ if (inReplyTo) {
45
+ // Create a reply draft, then optionally patch recipients
46
+ const draft = await createReplyDraft(connection, inReplyTo, body);
47
+
48
+ const toList = toRecipients(to);
49
+ const ccList = toRecipients(cc);
50
+ const bccList = toRecipients(bcc);
51
+
52
+ const patch: Record<string, unknown> = {};
53
+ if (toList) patch.toRecipients = toList;
54
+ if (ccList) patch.ccRecipients = ccList;
55
+ if (bccList) patch.bccRecipients = bccList;
56
+
57
+ if (Object.keys(patch).length > 0) {
58
+ await patchMessage(connection, draft.id, patch);
59
+ }
60
+
61
+ return ok(
62
+ `Draft created (ID: ${draft.id}). It will appear in your Outlook Drafts folder. Tell me to send it when ready.`,
63
+ );
64
+ }
65
+
66
+ const toRecipientsList = toRecipients(to) ?? [];
67
+ const ccRecipientsList = toRecipients(cc);
68
+ const bccRecipientsList = toRecipients(bcc);
69
+
70
+ const draft = await createDraft(connection, {
71
+ subject,
72
+ body: { contentType: "text", content: body },
73
+ toRecipients: toRecipientsList,
74
+ ccRecipients: ccRecipientsList,
75
+ bccRecipients: bccRecipientsList,
76
+ });
77
+
78
+ return ok(
79
+ `Draft created (ID: ${draft.id}). It will appear in your Outlook Drafts folder. Tell me to send it when ready.`,
80
+ );
81
+ } catch (e) {
82
+ return err(e instanceof Error ? e.message : String(e));
83
+ }
84
+ }
@@ -0,0 +1,94 @@
1
+ import {
2
+ listMessages,
3
+ updateMessageFlag,
4
+ } from "../../../../messaging/providers/outlook/client.js";
5
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
6
+ import type {
7
+ ToolContext,
8
+ ToolExecutionResult,
9
+ } from "../../../../tools/types.js";
10
+ import { err, ok } from "./shared.js";
11
+
12
+ export async function run(
13
+ input: Record<string, unknown>,
14
+ _context: ToolContext,
15
+ ): Promise<ToolExecutionResult> {
16
+ const account = input.account as string | undefined;
17
+ const action = input.action as string;
18
+
19
+ if (!action) {
20
+ return err("action is required (track, list, untrack, or complete).");
21
+ }
22
+
23
+ try {
24
+ const connection = await resolveOAuthConnection("outlook", {
25
+ account,
26
+ });
27
+
28
+ switch (action) {
29
+ case "track": {
30
+ const messageId = input.message_id as string;
31
+ if (!messageId) return err("message_id is required for track action.");
32
+
33
+ await updateMessageFlag(connection, messageId, {
34
+ flagStatus: "flagged",
35
+ });
36
+ return ok("Message flagged for follow-up.");
37
+ }
38
+
39
+ case "complete": {
40
+ const messageId = input.message_id as string;
41
+ if (!messageId)
42
+ return err("message_id is required for complete action.");
43
+
44
+ await updateMessageFlag(connection, messageId, {
45
+ flagStatus: "complete",
46
+ });
47
+ return ok("Follow-up marked complete.");
48
+ }
49
+
50
+ case "untrack": {
51
+ const messageId = input.message_id as string;
52
+ if (!messageId)
53
+ return err("message_id is required for untrack action.");
54
+
55
+ await updateMessageFlag(connection, messageId, {
56
+ flagStatus: "notFlagged",
57
+ });
58
+ return ok("Follow-up flag removed.");
59
+ }
60
+
61
+ case "list": {
62
+ const resp = await listMessages(connection, {
63
+ filter: "flag/flagStatus eq 'flagged'",
64
+ top: 50,
65
+ select:
66
+ "id,conversationId,subject,bodyPreview,body,from,toRecipients,receivedDateTime,isRead,hasAttachments,parentFolderId,categories,flag",
67
+ orderby: "receivedDateTime desc",
68
+ });
69
+
70
+ const messages = resp.value ?? [];
71
+ if (messages.length === 0) {
72
+ return ok("No messages are currently flagged for follow-up.");
73
+ }
74
+
75
+ const items = messages.map((m) => ({
76
+ id: m.id,
77
+ conversationId: m.conversationId,
78
+ subject: m.subject,
79
+ from: m.from?.emailAddress?.address ?? "",
80
+ date: m.receivedDateTime,
81
+ }));
82
+
83
+ return ok(JSON.stringify(items, null, 2));
84
+ }
85
+
86
+ default:
87
+ return err(
88
+ `Unknown action "${action}". Use track, list, untrack, or complete.`,
89
+ );
90
+ }
91
+ } catch (e) {
92
+ return err(e instanceof Error ? e.message : String(e));
93
+ }
94
+ }
@@ -0,0 +1,49 @@
1
+ import { createForwardDraft } from "../../../../messaging/providers/outlook/client.js";
2
+ import type { OutlookRecipient } from "../../../../messaging/providers/outlook/types.js";
3
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
4
+ import type {
5
+ ToolContext,
6
+ ToolExecutionResult,
7
+ } from "../../../../tools/types.js";
8
+ import { err, ok } from "./shared.js";
9
+
10
+ function toRecipients(csv: string): OutlookRecipient[] {
11
+ return csv
12
+ .split(",")
13
+ .map((s) => s.trim())
14
+ .filter(Boolean)
15
+ .map((address) => ({ emailAddress: { address } }));
16
+ }
17
+
18
+ export async function run(
19
+ input: Record<string, unknown>,
20
+ _context: ToolContext,
21
+ ): Promise<ToolExecutionResult> {
22
+ const account = input.account as string | undefined;
23
+ const messageId = input.message_id as string;
24
+ const to = input.to as string;
25
+ const comment = input.comment as string | undefined;
26
+
27
+ if (!messageId) return err("message_id is required.");
28
+ if (!to) return err("to is required.");
29
+
30
+ try {
31
+ const connection = await resolveOAuthConnection("outlook", {
32
+ account,
33
+ });
34
+
35
+ const toRecipientsList = toRecipients(to);
36
+ const draft = await createForwardDraft(
37
+ connection,
38
+ messageId,
39
+ toRecipientsList,
40
+ comment,
41
+ );
42
+
43
+ return ok(
44
+ `Forward draft created (ID: ${draft.id}). Review in Outlook Drafts, then tell me to send it.`,
45
+ );
46
+ } catch (e) {
47
+ return err(e instanceof Error ? e.message : String(e));
48
+ }
49
+ }
@@ -0,0 +1,237 @@
1
+ import {
2
+ batchGetMessages,
3
+ listMessages,
4
+ } from "../../../../messaging/providers/outlook/client.js";
5
+ import type { OutlookMessage } from "../../../../messaging/providers/outlook/types.js";
6
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
7
+ import type {
8
+ ToolContext,
9
+ ToolExecutionResult,
10
+ } from "../../../../tools/types.js";
11
+ import { storeScanResult } from "../../gmail/tools/scan-result-store.js";
12
+ import { err, ok } from "./shared.js";
13
+
14
+ const MAX_MESSAGES_CAP = 5000;
15
+ const MAX_IDS_PER_SENDER = 5000;
16
+ const MAX_SAMPLE_SUBJECTS = 3;
17
+
18
+ interface OutreachSenderAggregation {
19
+ displayName: string;
20
+ email: string;
21
+ messageCount: number;
22
+ newestMessageId: string;
23
+ oldestDate: string;
24
+ newestDate: string;
25
+ messageIds: string[];
26
+ hasMore: boolean;
27
+ sampleSubjects: string[];
28
+ }
29
+
30
+ /** Parse a time range string like "90d" or "30d" into milliseconds. */
31
+ function parseTimeRange(timeRange: string): number {
32
+ const match = timeRange.match(/^(\d+)([dhm])$/);
33
+ if (!match) return 90 * 24 * 60 * 60 * 1000; // default 90 days
34
+ const value = parseInt(match[1], 10);
35
+ switch (match[2]) {
36
+ case "d":
37
+ return value * 24 * 60 * 60 * 1000;
38
+ case "h":
39
+ return value * 60 * 60 * 1000;
40
+ case "m":
41
+ return value * 60 * 1000;
42
+ default:
43
+ return 90 * 24 * 60 * 60 * 1000;
44
+ }
45
+ }
46
+
47
+ export async function run(
48
+ input: Record<string, unknown>,
49
+ _context: ToolContext,
50
+ ): Promise<ToolExecutionResult> {
51
+ const account = input.account as string | undefined;
52
+ const maxSenders = (input.max_senders as number) ?? 30;
53
+ const timeRange = (input.time_range as string) ?? "90d";
54
+
55
+ try {
56
+ const connection = await resolveOAuthConnection("outlook", {
57
+ account,
58
+ });
59
+
60
+ // Build OData filter: inbox messages from the specified time range
61
+ const sinceDate = new Date(
62
+ Date.now() - parseTimeRange(timeRange),
63
+ ).toISOString();
64
+ const dateFilter = `receivedDateTime ge ${sinceDate}`;
65
+
66
+ const allMessageIds: string[] = [];
67
+ const fetchPromises: Promise<OutlookMessage[]>[] = [];
68
+ let skip = 0;
69
+ let truncated = false;
70
+ let timeBudgetExceeded = false;
71
+ const startTime = Date.now();
72
+ const TIME_BUDGET_MS = 90_000;
73
+
74
+ // Paginate through messages
75
+ while (allMessageIds.length < MAX_MESSAGES_CAP) {
76
+ if (Date.now() - startTime > TIME_BUDGET_MS) {
77
+ timeBudgetExceeded = true;
78
+ truncated = true;
79
+ break;
80
+ }
81
+ const pageSize = Math.min(100, MAX_MESSAGES_CAP - allMessageIds.length);
82
+
83
+ const listResp = await listMessages(connection, {
84
+ top: pageSize,
85
+ skip,
86
+ filter: dateFilter,
87
+ orderby: "receivedDateTime desc",
88
+ select: "id,from,receivedDateTime,subject",
89
+ });
90
+
91
+ const messages = listResp.value ?? [];
92
+ if (messages.length === 0) break;
93
+
94
+ const ids = messages.map((m) => m.id);
95
+ allMessageIds.push(...ids);
96
+
97
+ // Fetch internet message headers to check for List-Unsubscribe
98
+ fetchPromises.push(
99
+ batchGetMessages(
100
+ connection,
101
+ ids,
102
+ "id,from,receivedDateTime,subject,internetMessageHeaders",
103
+ ),
104
+ );
105
+
106
+ skip += messages.length;
107
+ if (messages.length < pageSize) break;
108
+ }
109
+
110
+ if (allMessageIds.length >= MAX_MESSAGES_CAP) {
111
+ truncated = true;
112
+ }
113
+
114
+ if (allMessageIds.length === 0) {
115
+ return ok(
116
+ JSON.stringify({
117
+ senders: [],
118
+ total_scanned: 0,
119
+ note: "No emails found matching the query.",
120
+ }),
121
+ );
122
+ }
123
+
124
+ const fetchedMessages = (await Promise.all(fetchPromises)).flat();
125
+
126
+ // First pass: track which senders have ANY messages with List-Unsubscribe
127
+ const sendersWithUnsubscribe = new Set<string>();
128
+ for (const msg of fetchedMessages) {
129
+ const fromEmail = msg.from?.emailAddress?.address?.toLowerCase();
130
+ if (!fromEmail) continue;
131
+ const hasUnsub = msg.internetMessageHeaders?.some(
132
+ (h) => h.name.toLowerCase() === "list-unsubscribe",
133
+ );
134
+ if (hasUnsub) sendersWithUnsubscribe.add(fromEmail);
135
+ }
136
+
137
+ // Second pass: aggregate only senders WITHOUT List-Unsubscribe
138
+ const senderMap = new Map<string, OutreachSenderAggregation>();
139
+
140
+ for (const msg of fetchedMessages) {
141
+ const fromEmail = msg.from?.emailAddress?.address?.toLowerCase();
142
+ const fromName = msg.from?.emailAddress?.name ?? "";
143
+ const subject = msg.subject ?? "";
144
+ const dateStr = msg.receivedDateTime ?? "";
145
+
146
+ if (!fromEmail) continue;
147
+ // Skip senders that have any messages with List-Unsubscribe
148
+ if (sendersWithUnsubscribe.has(fromEmail)) continue;
149
+
150
+ let agg = senderMap.get(fromEmail);
151
+ if (!agg) {
152
+ agg = {
153
+ displayName: fromName,
154
+ email: fromEmail,
155
+ messageCount: 0,
156
+ newestMessageId: msg.id,
157
+ oldestDate: dateStr,
158
+ newestDate: dateStr,
159
+ messageIds: [],
160
+ hasMore: false,
161
+ sampleSubjects: [],
162
+ };
163
+ senderMap.set(fromEmail, agg);
164
+ }
165
+
166
+ agg.messageCount++;
167
+
168
+ if (!agg.displayName && fromName) agg.displayName = fromName;
169
+
170
+ if (agg.messageIds.length < MAX_IDS_PER_SENDER) {
171
+ agg.messageIds.push(msg.id);
172
+ } else {
173
+ agg.hasMore = true;
174
+ }
175
+
176
+ // Track date range
177
+ const msgEpoch = dateStr ? new Date(dateStr).getTime() : 0;
178
+ const oldestEpoch = agg.oldestDate
179
+ ? new Date(agg.oldestDate).getTime()
180
+ : Infinity;
181
+ const newestEpoch = agg.newestDate
182
+ ? new Date(agg.newestDate).getTime()
183
+ : 0;
184
+
185
+ if (msgEpoch > 0 && msgEpoch < oldestEpoch) {
186
+ agg.oldestDate = dateStr || agg.oldestDate;
187
+ }
188
+ if (msgEpoch > newestEpoch) {
189
+ agg.newestDate = dateStr || agg.newestDate;
190
+ agg.newestMessageId = msg.id;
191
+ }
192
+
193
+ if (subject && agg.sampleSubjects.length < MAX_SAMPLE_SUBJECTS) {
194
+ agg.sampleSubjects.push(subject);
195
+ }
196
+ }
197
+
198
+ // Sort by message count desc, take top N
199
+ const sorted = [...senderMap.values()]
200
+ .sort((a, b) => b.messageCount - a.messageCount)
201
+ .slice(0, maxSenders);
202
+
203
+ const senders = sorted.map((s) => ({
204
+ id: Buffer.from(s.email).toString("base64url"),
205
+ display_name: s.displayName || s.email.split("@")[0],
206
+ email: s.email,
207
+ message_count: s.messageCount,
208
+ newest_message_id: s.newestMessageId,
209
+ oldest_date: s.oldestDate,
210
+ newest_date: s.newestDate,
211
+ sample_subjects: s.sampleSubjects,
212
+ }));
213
+
214
+ // Store message IDs server-side to keep them out of LLM context
215
+ const scanId = storeScanResult(
216
+ sorted.map((s) => ({
217
+ id: Buffer.from(s.email).toString("base64url"),
218
+ messageIds: s.messageIds,
219
+ newestMessageId: s.newestMessageId,
220
+ newestUnsubscribableMessageId: null,
221
+ })),
222
+ );
223
+
224
+ return ok(
225
+ JSON.stringify({
226
+ scan_id: scanId,
227
+ senders,
228
+ total_scanned: allMessageIds.length,
229
+ ...(truncated ? { truncated: true } : {}),
230
+ ...(timeBudgetExceeded ? { time_budget_exceeded: true } : {}),
231
+ note: "Scanned inbox for senders without List-Unsubscribe headers (potential cold outreach). Use outlook_archive and outlook_mail_rules for cleanup.",
232
+ }),
233
+ );
234
+ } catch (e) {
235
+ return err(e instanceof Error ? e.message : String(e));
236
+ }
237
+ }
@@ -0,0 +1,161 @@
1
+ import {
2
+ createMailRule,
3
+ deleteMailRule,
4
+ listMailRules,
5
+ } from "../../../../messaging/providers/outlook/client.js";
6
+ import type {
7
+ OutlookMessageRuleActions,
8
+ OutlookMessageRulePredicates,
9
+ } from "../../../../messaging/providers/outlook/types.js";
10
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
11
+ import type {
12
+ ToolContext,
13
+ ToolExecutionResult,
14
+ } from "../../../../tools/types.js";
15
+ import { err, ok } from "./shared.js";
16
+
17
+ export async function run(
18
+ input: Record<string, unknown>,
19
+ _context: ToolContext,
20
+ ): Promise<ToolExecutionResult> {
21
+ const account = input.account as string | undefined;
22
+ const action = input.action as string;
23
+
24
+ if (!action) {
25
+ return err("action is required (list, create, or delete).");
26
+ }
27
+
28
+ try {
29
+ const connection = await resolveOAuthConnection("outlook", {
30
+ account,
31
+ });
32
+ switch (action) {
33
+ case "list": {
34
+ const resp = await listMailRules(connection);
35
+ const rules = resp.value ?? [];
36
+ if (rules.length === 0) {
37
+ return ok("No inbox rules configured.");
38
+ }
39
+ const summary = rules.map((r) => ({
40
+ id: r.id,
41
+ displayName: r.displayName,
42
+ isEnabled: r.isEnabled,
43
+ conditions: summarizeConditions(r.conditions),
44
+ actions: summarizeActions(r.actions),
45
+ }));
46
+ return ok(JSON.stringify(summary, null, 2));
47
+ }
48
+
49
+ case "create": {
50
+ const displayName = input.display_name as string;
51
+ if (!displayName)
52
+ return err("display_name is required for create action.");
53
+
54
+ const conditions: OutlookMessageRulePredicates = {};
55
+ if (input.from_contains)
56
+ conditions.senderContains = Array.isArray(input.from_contains)
57
+ ? (input.from_contains as string[])
58
+ : [input.from_contains as string];
59
+ if (input.subject_contains)
60
+ conditions.subjectContains = Array.isArray(input.subject_contains)
61
+ ? (input.subject_contains as string[])
62
+ : [input.subject_contains as string];
63
+ if (input.body_contains)
64
+ conditions.bodyContains = Array.isArray(input.body_contains)
65
+ ? (input.body_contains as string[])
66
+ : [input.body_contains as string];
67
+ if (input.has_attachment !== undefined)
68
+ conditions.hasAttachments = input.has_attachment as boolean;
69
+ if (input.importance)
70
+ conditions.importance = input.importance as "low" | "normal" | "high";
71
+
72
+ if (Object.keys(conditions).length === 0) {
73
+ return err(
74
+ "At least one condition is required (from_contains, subject_contains, body_contains, has_attachment, or importance).",
75
+ );
76
+ }
77
+
78
+ const actions: OutlookMessageRuleActions = {};
79
+ if (input.move_to_folder)
80
+ actions.moveToFolder = input.move_to_folder as string;
81
+ if (input.delete !== undefined)
82
+ actions.delete = input.delete as boolean;
83
+ if (input.mark_as_read !== undefined)
84
+ actions.markAsRead = input.mark_as_read as boolean;
85
+ if (input.mark_importance)
86
+ actions.markImportance = input.mark_importance as
87
+ | "low"
88
+ | "normal"
89
+ | "high";
90
+ if (input.forward_to) {
91
+ const emails = Array.isArray(input.forward_to)
92
+ ? (input.forward_to as string[])
93
+ : [input.forward_to as string];
94
+ actions.forwardTo = emails.map((addr) => ({
95
+ emailAddress: { address: addr },
96
+ }));
97
+ }
98
+ if (input.stop_processing !== undefined)
99
+ actions.stopProcessingRules = input.stop_processing as boolean;
100
+
101
+ const rule = await createMailRule(connection, {
102
+ displayName,
103
+ sequence: (input.sequence as number) ?? 1,
104
+ isEnabled: true,
105
+ conditions,
106
+ actions,
107
+ });
108
+ return ok(`Rule created (ID: ${rule.id}).`);
109
+ }
110
+
111
+ case "delete": {
112
+ const ruleId = input.rule_id as string;
113
+ if (!ruleId) return err("rule_id is required for delete action.");
114
+
115
+ await deleteMailRule(connection, ruleId);
116
+ return ok("Rule deleted.");
117
+ }
118
+
119
+ default:
120
+ return err(`Unknown action "${action}". Use list, create, or delete.`);
121
+ }
122
+ } catch (e) {
123
+ return err(e instanceof Error ? e.message : String(e));
124
+ }
125
+ }
126
+
127
+ function summarizeConditions(
128
+ c: OutlookMessageRulePredicates | undefined,
129
+ ): string {
130
+ if (!c) return "none";
131
+ const parts: string[] = [];
132
+ if (c.senderContains?.length)
133
+ parts.push(`sender contains: ${c.senderContains.join(", ")}`);
134
+ if (c.subjectContains?.length)
135
+ parts.push(`subject contains: ${c.subjectContains.join(", ")}`);
136
+ if (c.bodyContains?.length)
137
+ parts.push(`body contains: ${c.bodyContains.join(", ")}`);
138
+ if (c.fromAddresses?.length)
139
+ parts.push(
140
+ `from: ${c.fromAddresses.map((a) => a.emailAddress.address).join(", ")}`,
141
+ );
142
+ if (c.hasAttachments !== undefined)
143
+ parts.push(`has attachments: ${c.hasAttachments}`);
144
+ if (c.importance) parts.push(`importance: ${c.importance}`);
145
+ return parts.length > 0 ? parts.join("; ") : "none";
146
+ }
147
+
148
+ function summarizeActions(c: OutlookMessageRuleActions | undefined): string {
149
+ if (!c) return "none";
150
+ const parts: string[] = [];
151
+ if (c.moveToFolder) parts.push(`move to folder: ${c.moveToFolder}`);
152
+ if (c.delete) parts.push("delete");
153
+ if (c.markAsRead) parts.push("mark as read");
154
+ if (c.markImportance) parts.push(`mark importance: ${c.markImportance}`);
155
+ if (c.forwardTo?.length)
156
+ parts.push(
157
+ `forward to: ${c.forwardTo.map((r) => r.emailAddress.address).join(", ")}`,
158
+ );
159
+ if (c.stopProcessingRules) parts.push("stop processing rules");
160
+ return parts.length > 0 ? parts.join("; ") : "none";
161
+ }
@@ -0,0 +1,32 @@
1
+ import { sendDraft } from "../../../../messaging/providers/outlook/client.js";
2
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
3
+ import type {
4
+ ToolContext,
5
+ ToolExecutionResult,
6
+ } from "../../../../tools/types.js";
7
+ import { err, ok } from "./shared.js";
8
+
9
+ export async function run(
10
+ input: Record<string, unknown>,
11
+ context: ToolContext,
12
+ ): Promise<ToolExecutionResult> {
13
+ const account = input.account as string | undefined;
14
+ const draftId = input.draft_id as string;
15
+ if (!draftId) return err("draft_id is required.");
16
+
17
+ if (!context.triggeredBySurfaceAction) {
18
+ return err(
19
+ "This tool requires user confirmation via a surface action. Present the draft details with a send button and wait for the user to click before proceeding.",
20
+ );
21
+ }
22
+
23
+ try {
24
+ const connection = await resolveOAuthConnection("outlook", {
25
+ account,
26
+ });
27
+ await sendDraft(connection, draftId);
28
+ return ok("Draft sent.");
29
+ } catch (e) {
30
+ return err(e instanceof Error ? e.message : String(e));
31
+ }
32
+ }