@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,272 @@
1
+ import {
2
+ batchGetMessages,
3
+ listMessages,
4
+ searchMessages,
5
+ } from "../../../../messaging/providers/outlook/client.js";
6
+ import type { OutlookMessage } 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 { storeScanResult } from "../../gmail/tools/scan-result-store.js";
13
+ import { err, ok } from "./shared.js";
14
+
15
+ const MAX_MESSAGES_CAP = 10000;
16
+ const MAX_IDS_PER_SENDER = 5000;
17
+ const MAX_SAMPLE_SUBJECTS = 3;
18
+
19
+ interface SenderAggregation {
20
+ displayName: string;
21
+ email: string;
22
+ messageCount: number;
23
+ hasUnsubscribe: boolean;
24
+ newestMessageId: string;
25
+ newestUnsubscribableMessageId: string | null;
26
+ newestUnsubscribableEpoch: number;
27
+ oldestDate: string;
28
+ newestDate: string;
29
+ messageIds: string[];
30
+ hasMore: boolean;
31
+ sampleSubjects: string[];
32
+ }
33
+
34
+ export async function run(
35
+ input: Record<string, unknown>,
36
+ _context: ToolContext,
37
+ ): Promise<ToolExecutionResult> {
38
+ const account = input.account as string | undefined;
39
+ const userQuery = input.query as string | undefined;
40
+ const maxSenders = (input.max_senders as number) ?? 50;
41
+
42
+ try {
43
+ const connection = await resolveOAuthConnection("outlook", {
44
+ account,
45
+ });
46
+
47
+ // Build OData filter: inbox messages from last 90 days
48
+ const ninetyDaysAgo = new Date(
49
+ Date.now() - 90 * 24 * 60 * 60 * 1000,
50
+ ).toISOString();
51
+ const ninetyDaysAgoEpoch = new Date(ninetyDaysAgo).getTime();
52
+ const dateFilter = `receivedDateTime ge ${ninetyDaysAgo}`;
53
+
54
+ const allMessageIds: string[] = [];
55
+ const fetchPromises: Promise<OutlookMessage[]>[] = [];
56
+ let skip = 0;
57
+ let truncated = false;
58
+ let timeBudgetExceeded = false;
59
+ const startTime = Date.now();
60
+ const TIME_BUDGET_MS = 90_000;
61
+
62
+ // When userQuery is provided, use searchMessages (Microsoft Graph
63
+ // doesn't support combining $filter and $search). Date filtering is
64
+ // applied client-side instead.
65
+ const useSearch = Boolean(userQuery);
66
+
67
+ // Paginate through messages
68
+ while (allMessageIds.length < MAX_MESSAGES_CAP) {
69
+ if (Date.now() - startTime > TIME_BUDGET_MS) {
70
+ timeBudgetExceeded = true;
71
+ truncated = true;
72
+ break;
73
+ }
74
+ const pageSize = Math.min(100, MAX_MESSAGES_CAP - allMessageIds.length);
75
+
76
+ let messages: OutlookMessage[];
77
+
78
+ if (useSearch) {
79
+ const searchResp = await searchMessages(connection, userQuery!, {
80
+ top: pageSize,
81
+ skip,
82
+ });
83
+ messages = searchResp.value ?? [];
84
+ } else {
85
+ const listResp = await listMessages(connection, {
86
+ top: pageSize,
87
+ skip,
88
+ filter: dateFilter,
89
+ orderby: "receivedDateTime desc",
90
+ select: "id,from,receivedDateTime,hasAttachments,subject",
91
+ });
92
+ messages = listResp.value ?? [];
93
+ }
94
+
95
+ if (messages.length === 0) break;
96
+
97
+ // When using search, apply 90-day date filter client-side
98
+ const filtered = useSearch
99
+ ? messages.filter((m) => {
100
+ const received = m.receivedDateTime
101
+ ? new Date(m.receivedDateTime).getTime()
102
+ : 0;
103
+ return received >= ninetyDaysAgoEpoch;
104
+ })
105
+ : messages;
106
+
107
+ const ids = filtered.map((m) => m.id);
108
+ allMessageIds.push(...ids);
109
+
110
+ // Fetch internet message headers (List-Unsubscribe) for each batch
111
+ if (ids.length > 0) {
112
+ fetchPromises.push(
113
+ batchGetMessages(
114
+ connection,
115
+ ids,
116
+ "id,from,receivedDateTime,subject,internetMessageHeaders",
117
+ ),
118
+ );
119
+ }
120
+
121
+ skip += messages.length;
122
+
123
+ // If we received fewer messages than requested, there are no more pages
124
+ if (messages.length < pageSize) break;
125
+
126
+ // When using search, if all messages in a page are older than
127
+ // 90 days we've likely passed beyond the relevant window
128
+ if (useSearch && filtered.length === 0) break;
129
+ }
130
+
131
+ // Flag truncation if we hit the cap with more pages potentially available
132
+ if (allMessageIds.length >= MAX_MESSAGES_CAP) {
133
+ truncated = true;
134
+ }
135
+
136
+ if (allMessageIds.length === 0) {
137
+ return ok(
138
+ JSON.stringify({
139
+ senders: [],
140
+ total_scanned: 0,
141
+ message:
142
+ "No emails found matching the query. Try broadening the search (e.g. extend date range).",
143
+ }),
144
+ );
145
+ }
146
+
147
+ const fetchedMessages = (await Promise.all(fetchPromises)).flat();
148
+
149
+ // Group by sender email
150
+ const senderMap = new Map<string, SenderAggregation>();
151
+
152
+ for (const msg of fetchedMessages) {
153
+ const fromEmail = msg.from?.emailAddress?.address?.toLowerCase();
154
+ const fromName = msg.from?.emailAddress?.name ?? "";
155
+ const subject = msg.subject ?? "";
156
+ const dateStr = msg.receivedDateTime ?? "";
157
+
158
+ if (!fromEmail) continue;
159
+
160
+ // Check for List-Unsubscribe header
161
+ const listUnsub = msg.internetMessageHeaders?.find(
162
+ (h) => h.name.toLowerCase() === "list-unsubscribe",
163
+ )?.value;
164
+
165
+ let agg = senderMap.get(fromEmail);
166
+ if (!agg) {
167
+ agg = {
168
+ displayName: fromName,
169
+ email: fromEmail,
170
+ messageCount: 0,
171
+ hasUnsubscribe: false,
172
+ newestMessageId: msg.id,
173
+ newestUnsubscribableMessageId: null,
174
+ newestUnsubscribableEpoch: 0,
175
+ oldestDate: dateStr,
176
+ newestDate: dateStr,
177
+ messageIds: [],
178
+ hasMore: false,
179
+ sampleSubjects: [],
180
+ };
181
+ senderMap.set(fromEmail, agg);
182
+ }
183
+
184
+ agg.messageCount++;
185
+
186
+ if (listUnsub) agg.hasUnsubscribe = true;
187
+
188
+ // Use displayName from earliest message that has one
189
+ if (!agg.displayName && fromName) agg.displayName = fromName;
190
+
191
+ // Track message IDs (cap at MAX_IDS_PER_SENDER)
192
+ if (agg.messageIds.length < MAX_IDS_PER_SENDER) {
193
+ agg.messageIds.push(msg.id);
194
+ } else {
195
+ agg.hasMore = true;
196
+ }
197
+
198
+ // Track date range using ISO date strings
199
+ const msgEpoch = dateStr ? new Date(dateStr).getTime() : 0;
200
+ const oldestEpoch = agg.oldestDate
201
+ ? new Date(agg.oldestDate).getTime()
202
+ : Infinity;
203
+ const newestEpoch = agg.newestDate
204
+ ? new Date(agg.newestDate).getTime()
205
+ : 0;
206
+
207
+ if (msgEpoch > 0 && msgEpoch < oldestEpoch) {
208
+ agg.oldestDate = dateStr || agg.oldestDate;
209
+ }
210
+ if (msgEpoch > newestEpoch) {
211
+ agg.newestDate = dateStr || agg.newestDate;
212
+ agg.newestMessageId = msg.id;
213
+ }
214
+
215
+ // Track the newest message that has List-Unsubscribe so
216
+ // unsubscribe actions target a message that carries the header
217
+ if (listUnsub && msgEpoch >= agg.newestUnsubscribableEpoch) {
218
+ agg.newestUnsubscribableMessageId = msg.id;
219
+ agg.newestUnsubscribableEpoch = msgEpoch;
220
+ }
221
+
222
+ // Collect sample subjects
223
+ if (subject && agg.sampleSubjects.length < MAX_SAMPLE_SUBJECTS) {
224
+ agg.sampleSubjects.push(subject);
225
+ }
226
+ }
227
+
228
+ // Sort by message count descending, take top N
229
+ const sorted = [...senderMap.values()]
230
+ .sort((a, b) => b.messageCount - a.messageCount)
231
+ .slice(0, maxSenders);
232
+
233
+ const resultSenders = sorted.map((s) => ({
234
+ id: Buffer.from(s.email).toString("base64url"),
235
+ display_name: s.displayName || s.email.split("@")[0],
236
+ email: s.email,
237
+ message_count: s.messageCount,
238
+ has_unsubscribe: s.hasUnsubscribe,
239
+ newest_message_id:
240
+ s.hasUnsubscribe && s.newestUnsubscribableMessageId
241
+ ? s.newestUnsubscribableMessageId
242
+ : s.newestMessageId,
243
+ oldest_date: s.oldestDate,
244
+ newest_date: s.newestDate,
245
+ sample_subjects: s.sampleSubjects,
246
+ }));
247
+
248
+ // Store message IDs server-side to keep them out of LLM context
249
+ const scanId = storeScanResult(
250
+ sorted.map((s) => ({
251
+ id: Buffer.from(s.email).toString("base64url"),
252
+ messageIds: s.messageIds,
253
+ newestMessageId: s.newestMessageId,
254
+ newestUnsubscribableMessageId: s.newestUnsubscribableMessageId,
255
+ })),
256
+ );
257
+
258
+ return ok(
259
+ JSON.stringify({
260
+ scan_id: scanId,
261
+ senders: resultSenders,
262
+ total_scanned: allMessageIds.length,
263
+ query_used: userQuery ?? `inbox messages from last 90 days`,
264
+ ...(truncated ? { truncated: true } : {}),
265
+ ...(timeBudgetExceeded ? { time_budget_exceeded: true } : {}),
266
+ note: `message_count reflects emails found per sender within the ${allMessageIds.length} messages scanned. Use scan_id with outlook_archive to archive messages (pass scan_id + sender_ids instead of message_ids).`,
267
+ }),
268
+ );
269
+ } catch (e) {
270
+ return err(e instanceof Error ? e.message : String(e));
271
+ }
272
+ }
@@ -0,0 +1,29 @@
1
+ import { trashMessage } 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 messageId = input.message_id as string;
15
+
16
+ if (!messageId) {
17
+ return err("message_id is required.");
18
+ }
19
+
20
+ try {
21
+ const connection = await resolveOAuthConnection("outlook", {
22
+ account,
23
+ });
24
+ await trashMessage(connection, messageId);
25
+ return ok("Message moved to Deleted Items.");
26
+ } catch (e) {
27
+ return err(e instanceof Error ? e.message : String(e));
28
+ }
29
+ }
@@ -0,0 +1,129 @@
1
+ import {
2
+ getMessageWithHeaders,
3
+ sendMessage,
4
+ } from "../../../../messaging/providers/outlook/client.js";
5
+ import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
6
+ import {
7
+ isPrivateOrLocalHost,
8
+ resolveHostAddresses,
9
+ } from "../../../../tools/network/url-safety.js";
10
+ import type {
11
+ ToolContext,
12
+ ToolExecutionResult,
13
+ } from "../../../../tools/types.js";
14
+ import {
15
+ err,
16
+ ok,
17
+ pinnedHttpsRequest,
18
+ resolveRequestAddress,
19
+ } from "./shared.js";
20
+
21
+ export async function run(
22
+ input: Record<string, unknown>,
23
+ context: ToolContext,
24
+ ): Promise<ToolExecutionResult> {
25
+ const account = input.account as string | undefined;
26
+ if (!context.triggeredBySurfaceAction) {
27
+ return err(
28
+ "This tool requires user confirmation via a surface action. Present results in a selection table with action buttons and wait for the user to click before proceeding.",
29
+ );
30
+ }
31
+
32
+ const messageId = input.message_id as string;
33
+
34
+ if (!messageId) {
35
+ return err("message_id is required.");
36
+ }
37
+
38
+ try {
39
+ const connection = await resolveOAuthConnection("outlook", {
40
+ account,
41
+ });
42
+ const message = await getMessageWithHeaders(connection, messageId);
43
+ const headers = message.internetMessageHeaders ?? [];
44
+ const unsubHeader = headers.find(
45
+ (h) => h.name.toLowerCase() === "list-unsubscribe",
46
+ )?.value;
47
+
48
+ if (!unsubHeader) {
49
+ return err(
50
+ "No List-Unsubscribe header found. Manual unsubscribe may be required.",
51
+ );
52
+ }
53
+
54
+ const httpsMatch = unsubHeader.match(/<(https:\/\/[^>]+)>/);
55
+ const mailtoMatch = unsubHeader.match(/<mailto:([^>]+)>/);
56
+ const postHeader = headers.find(
57
+ (h) => h.name.toLowerCase() === "list-unsubscribe-post",
58
+ )?.value;
59
+
60
+ if (httpsMatch) {
61
+ const url = httpsMatch[1];
62
+ let parsed: URL;
63
+ let validatedAddresses: string[];
64
+ try {
65
+ parsed = new URL(url);
66
+ if (parsed.protocol !== "https:") {
67
+ return err("Unsubscribe URL must use HTTPS.");
68
+ }
69
+ if (isPrivateOrLocalHost(parsed.hostname)) {
70
+ return err("Unsubscribe URL points to a private or local address.");
71
+ }
72
+ const { addresses, blockedAddress } = await resolveRequestAddress(
73
+ parsed.hostname,
74
+ resolveHostAddresses,
75
+ false,
76
+ );
77
+ if (blockedAddress) {
78
+ return err("Unsubscribe URL resolves to a private or local address.");
79
+ }
80
+ if (addresses.length === 0) {
81
+ return err("Unable to resolve unsubscribe URL hostname.");
82
+ }
83
+ validatedAddresses = addresses;
84
+ } catch {
85
+ return err("Invalid unsubscribe URL.");
86
+ }
87
+
88
+ const method = postHeader ? "POST" : "GET";
89
+ const reqOpts = postHeader
90
+ ? {
91
+ method: "POST" as const,
92
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
93
+ body: postHeader,
94
+ }
95
+ : undefined;
96
+
97
+ let lastStatus = 0;
98
+ for (const address of validatedAddresses) {
99
+ try {
100
+ lastStatus = await pinnedHttpsRequest(parsed, address, reqOpts);
101
+ if (lastStatus >= 200 && lastStatus < 400) {
102
+ return ok(`Successfully unsubscribed via HTTPS ${method}.`);
103
+ }
104
+ } catch {
105
+ continue;
106
+ }
107
+ }
108
+ return err(`Unsubscribe request failed: ${lastStatus}`);
109
+ }
110
+
111
+ if (mailtoMatch) {
112
+ const mailtoAddr = mailtoMatch[1].split("?")[0];
113
+ await sendMessage(connection, {
114
+ message: {
115
+ subject: "Unsubscribe",
116
+ body: { contentType: "text", content: "" },
117
+ toRecipients: [{ emailAddress: { address: mailtoAddr } }],
118
+ },
119
+ });
120
+ return ok(`Unsubscribe email sent to ${mailtoAddr}.`);
121
+ }
122
+
123
+ return err(
124
+ "No supported unsubscribe method found (requires https: or mailto: URL).",
125
+ );
126
+ } catch (e) {
127
+ return err(e instanceof Error ? e.message : String(e));
128
+ }
129
+ }
@@ -0,0 +1,87 @@
1
+ import {
2
+ getAutoReplySettings,
3
+ updateAutoReplySettings,
4
+ } from "../../../../messaging/providers/outlook/client.js";
5
+ import type { OutlookAutoReplySettings } 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 { 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 (get, enable, or disable).");
22
+ }
23
+
24
+ try {
25
+ const connection = await resolveOAuthConnection("outlook", {
26
+ account,
27
+ });
28
+ switch (action) {
29
+ case "get": {
30
+ const settings = await getAutoReplySettings(connection);
31
+ return ok(JSON.stringify(settings, null, 2));
32
+ }
33
+
34
+ case "enable": {
35
+ const internalMessage = input.internal_message as string;
36
+ if (!internalMessage)
37
+ return err("internal_message is required when enabling auto-reply.");
38
+
39
+ const externalAudience = (input.external_audience as string) ?? "none";
40
+ const startDate = input.start_date as string | undefined;
41
+ const endDate = input.end_date as string | undefined;
42
+ const timeZone =
43
+ (input.time_zone as string) ??
44
+ Intl.DateTimeFormat().resolvedOptions().timeZone;
45
+
46
+ const isScheduled = !!(startDate && endDate);
47
+
48
+ const settings: OutlookAutoReplySettings = {
49
+ status: isScheduled ? "scheduled" : "alwaysEnabled",
50
+ externalAudience: externalAudience as "none" | "contactsOnly" | "all",
51
+ internalReplyMessage: internalMessage,
52
+ };
53
+
54
+ if (input.external_message) {
55
+ settings.externalReplyMessage = input.external_message as string;
56
+ }
57
+
58
+ if (isScheduled) {
59
+ settings.scheduledStartDateTime = {
60
+ dateTime: startDate!,
61
+ timeZone,
62
+ };
63
+ settings.scheduledEndDateTime = {
64
+ dateTime: endDate!,
65
+ timeZone,
66
+ };
67
+ }
68
+
69
+ await updateAutoReplySettings(connection, settings);
70
+ return ok("Auto-reply enabled.");
71
+ }
72
+
73
+ case "disable": {
74
+ await updateAutoReplySettings(connection, {
75
+ status: "disabled",
76
+ externalAudience: "none",
77
+ });
78
+ return ok("Auto-reply disabled.");
79
+ }
80
+
81
+ default:
82
+ return err(`Unknown action "${action}". Use get, enable, or disable.`);
83
+ }
84
+ } catch (e) {
85
+ return err(e instanceof Error ? e.message : String(e));
86
+ }
87
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Shared utilities for Outlook skill tools.
3
+ */
4
+
5
+ import type { ToolExecutionResult } from "../../../../tools/types.js";
6
+
7
+ export function ok(content: string): ToolExecutionResult {
8
+ return { content, isError: false };
9
+ }
10
+
11
+ export function err(message: string): ToolExecutionResult {
12
+ return { content: message, isError: true };
13
+ }
14
+
15
+ // Re-export DNS rebinding protection helpers from the Gmail shared module.
16
+ // These are provider-agnostic and should be reused, not duplicated.
17
+ export { pinnedHttpsRequest } from "../../gmail/tools/shared.js";
18
+
19
+ // resolveRequestAddress lives in the shared network-safety module (not Gmail-specific).
20
+ export { resolveRequestAddress } from "../../../../tools/network/url-safety.js";
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: outlook-calendar
3
+ description: View, create, and manage Outlook Calendar events and check availability
4
+ compatibility: "Designed for Vellum personal assistants"
5
+ metadata:
6
+ emoji: "📅"
7
+ vellum:
8
+ display-name: "Outlook Calendar"
9
+ ---
10
+
11
+ You are an Outlook Calendar assistant with full access to the user's calendar. Use the Outlook Calendar tools to help them view, create, and manage events.
12
+
13
+ ## Connection Setup
14
+
15
+ Before using any Outlook Calendar tool, verify that Outlook is connected by attempting a lightweight call (e.g., `outlook_calendar_list_events` with a narrow date range). If the call fails with a token/authorization error:
16
+
17
+ 1. **Try connecting directly first.** Run `assistant oauth status outlook`. This will show whether or not the user had previously connected their Outlook account. If so, they are ready to go.
18
+ 2. **If no connections are found:** Call `skill_load` with `skill: "vellum-oauth-integrations"`. The skill will evaluate whether managed or your-own mode is appropriate and guide the user accordingly.
19
+
20
+ ## Capabilities
21
+
22
+ - **List Events**: View upcoming events from any calendar within a date range.
23
+ - **Get Event**: Read full details of a specific calendar event.
24
+ - **Create Event**: Create new events with attendees, location, and description.
25
+ - **Check Availability**: Find free/busy times across calendars to identify open slots for scheduling.
26
+ - **RSVP**: Respond to event invitations (accepted, declined, tentative).
27
+
28
+ ## Scheduling Playbook
29
+
30
+ When the user wants to schedule something:
31
+
32
+ 1. **Always check availability first** before proposing times. Use `outlook_calendar_check_availability` to find free slots.
33
+ 2. Propose 2-3 available time options to the user.
34
+ 3. Once the user picks a time, create the event with `outlook_calendar_create_event`.
35
+ 4. If adding other attendees, mention that they'll receive an invitation email.
36
+
37
+ ## Date & Time Handling
38
+
39
+ - Always ask the user for their timezone if it's not already known from context or their profile.
40
+ - Use ISO 8601 format for dates and times (e.g., `2024-01-15T09:00:00-05:00`).
41
+ - For all-day events, use date-only format (e.g., `2024-01-15`).
42
+ - When listing events, display times in the user's local timezone.
43
+
44
+ ## Confidence Scores
45
+
46
+ Medium-risk tools (create event, RSVP) require a confidence score between 0 and 1. Set this based on how certain you are the action matches the user's intent:
47
+
48
+ - **0.9-1.0**: User explicitly requested this exact action
49
+ - **0.7-0.8**: Action is strongly implied by context
50
+ - **0.5-0.6**: Reasonable inference but some ambiguity
51
+ - **Below 0.5**: Ask the user to confirm before proceeding