@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,221 @@
1
+ {
2
+ "version": 1,
3
+ "tools": [
4
+ {
5
+ "name": "outlook_calendar_list_events",
6
+ "description": "List upcoming Outlook Calendar events within a date range. Returns event summaries, times, and metadata.",
7
+ "category": "calendar",
8
+ "risk": "low",
9
+ "input_schema": {
10
+ "type": "object",
11
+ "properties": {
12
+ "calendar_id": {
13
+ "type": "string",
14
+ "description": "Calendar ID to query. If omitted, uses the default calendar."
15
+ },
16
+ "time_min": {
17
+ "type": "string",
18
+ "description": "Start of time range in ISO 8601 format (e.g. \"2024-01-15T00:00:00Z\"). Defaults to now."
19
+ },
20
+ "time_max": {
21
+ "type": "string",
22
+ "description": "End of time range in ISO 8601 format (e.g. \"2024-01-22T00:00:00Z\"). Defaults to 7 days from now."
23
+ },
24
+ "max_results": {
25
+ "type": "number",
26
+ "description": "Maximum number of events to return (default 25, max 250)"
27
+ },
28
+ "query": {
29
+ "type": "string",
30
+ "description": "OData $filter expression to filter events"
31
+ },
32
+ "order_by": {
33
+ "type": "string",
34
+ "description": "OData $orderby expression (default \"start/dateTime\")"
35
+ },
36
+ "account": {
37
+ "type": "string",
38
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
39
+ },
40
+ "activity": {
41
+ "type": "string",
42
+ "description": "Brief non-technical explanation of why this tool is being called"
43
+ }
44
+ }
45
+ },
46
+ "executor": "tools/outlook-calendar-list-events.ts",
47
+ "execution_target": "host"
48
+ },
49
+ {
50
+ "name": "outlook_calendar_get_event",
51
+ "description": "Get the full details of a single Outlook Calendar event by its ID.",
52
+ "category": "calendar",
53
+ "risk": "low",
54
+ "input_schema": {
55
+ "type": "object",
56
+ "properties": {
57
+ "event_id": {
58
+ "type": "string",
59
+ "description": "The Outlook Calendar event ID"
60
+ },
61
+ "calendar_id": {
62
+ "type": "string",
63
+ "description": "Calendar ID. If omitted, uses the default calendar."
64
+ },
65
+ "account": {
66
+ "type": "string",
67
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
68
+ },
69
+ "activity": {
70
+ "type": "string",
71
+ "description": "Brief non-technical explanation of why this tool is being called"
72
+ }
73
+ },
74
+ "required": ["event_id"]
75
+ },
76
+ "executor": "tools/outlook-calendar-get-event.ts",
77
+ "execution_target": "host"
78
+ },
79
+ {
80
+ "name": "outlook_calendar_create_event",
81
+ "description": "Create a new Outlook Calendar event. Include a confidence score (0-1) indicating how certain you are this action is correct.",
82
+ "category": "calendar",
83
+ "risk": "medium",
84
+ "input_schema": {
85
+ "type": "object",
86
+ "properties": {
87
+ "summary": {
88
+ "type": "string",
89
+ "description": "Event title (maps to the Outlook subject field)"
90
+ },
91
+ "start": {
92
+ "type": "string",
93
+ "description": "Start time in ISO 8601 format (e.g. \"2024-01-15T09:00:00-05:00\") or date for all-day events (\"2024-01-15\")"
94
+ },
95
+ "end": {
96
+ "type": "string",
97
+ "description": "End time in ISO 8601 format or date for all-day events"
98
+ },
99
+ "description": {
100
+ "type": "string",
101
+ "description": "Event description or notes"
102
+ },
103
+ "location": {
104
+ "type": "string",
105
+ "description": "Event location"
106
+ },
107
+ "attendees": {
108
+ "type": "array",
109
+ "items": {
110
+ "type": "string"
111
+ },
112
+ "description": "Email addresses of attendees to invite"
113
+ },
114
+ "timezone": {
115
+ "type": "string",
116
+ "description": "IANA timezone (e.g. \"America/New_York\"). Required when start/end don't include timezone offset."
117
+ },
118
+ "calendar_id": {
119
+ "type": "string",
120
+ "description": "Calendar ID. If omitted, uses the default calendar."
121
+ },
122
+ "confidence": {
123
+ "type": "number",
124
+ "description": "Confidence score (0-1) for this action"
125
+ },
126
+ "account": {
127
+ "type": "string",
128
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
129
+ },
130
+ "activity": {
131
+ "type": "string",
132
+ "description": "Brief non-technical explanation of why this tool is being called"
133
+ }
134
+ },
135
+ "required": ["summary", "start", "end", "confidence"]
136
+ },
137
+ "executor": "tools/outlook-calendar-create-event.ts",
138
+ "execution_target": "host"
139
+ },
140
+ {
141
+ "name": "outlook_calendar_check_availability",
142
+ "description": "Check free/busy status across one or more schedules to find available time slots.",
143
+ "category": "calendar",
144
+ "risk": "low",
145
+ "input_schema": {
146
+ "type": "object",
147
+ "properties": {
148
+ "time_min": {
149
+ "type": "string",
150
+ "description": "Start of time range in ISO 8601 format"
151
+ },
152
+ "time_max": {
153
+ "type": "string",
154
+ "description": "End of time range in ISO 8601 format"
155
+ },
156
+ "schedules": {
157
+ "type": "array",
158
+ "items": {
159
+ "type": "string"
160
+ },
161
+ "description": "Email addresses of schedules to check. Defaults to the authenticated user."
162
+ },
163
+ "timezone": {
164
+ "type": "string",
165
+ "description": "IANA timezone for interpreting results (e.g. \"America/New_York\")"
166
+ },
167
+ "account": {
168
+ "type": "string",
169
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
170
+ },
171
+ "activity": {
172
+ "type": "string",
173
+ "description": "Brief non-technical explanation of why this tool is being called"
174
+ }
175
+ },
176
+ "required": ["time_min", "time_max"]
177
+ },
178
+ "executor": "tools/outlook-calendar-check-availability.ts",
179
+ "execution_target": "host"
180
+ },
181
+ {
182
+ "name": "outlook_calendar_rsvp",
183
+ "description": "Respond to an Outlook Calendar event invitation (accept, decline, or tentative). Include a confidence score (0-1).",
184
+ "category": "calendar",
185
+ "risk": "medium",
186
+ "input_schema": {
187
+ "type": "object",
188
+ "properties": {
189
+ "event_id": {
190
+ "type": "string",
191
+ "description": "The Outlook Calendar event ID"
192
+ },
193
+ "response": {
194
+ "type": "string",
195
+ "enum": ["accepted", "declined", "tentative"],
196
+ "description": "RSVP response"
197
+ },
198
+ "confidence": {
199
+ "type": "number",
200
+ "description": "Confidence score (0-1) for this action"
201
+ },
202
+ "calendar_id": {
203
+ "type": "string",
204
+ "description": "Calendar ID. If omitted, uses the default calendar."
205
+ },
206
+ "account": {
207
+ "type": "string",
208
+ "description": "Email address of the Outlook account to use. Required when multiple Outlook accounts are connected. If omitted, uses the most recently connected account."
209
+ },
210
+ "activity": {
211
+ "type": "string",
212
+ "description": "Brief non-technical explanation of why this tool is being called"
213
+ }
214
+ },
215
+ "required": ["event_id", "response", "confidence"]
216
+ },
217
+ "executor": "tools/outlook-calendar-rsvp.ts",
218
+ "execution_target": "host"
219
+ }
220
+ ]
221
+ }
@@ -0,0 +1,252 @@
1
+ import type {
2
+ OAuthConnection,
3
+ OAuthConnectionResponse,
4
+ } from "../../../oauth/connection.js";
5
+ import type {
6
+ OutlookCalendarEvent,
7
+ OutlookCalendarEventListResponse,
8
+ OutlookCalendarListResponse,
9
+ OutlookDateTimeZone,
10
+ OutlookScheduleResponse,
11
+ } from "./types.js";
12
+
13
+ export class OutlookCalendarApiError extends Error {
14
+ constructor(
15
+ public readonly status: number,
16
+ public readonly statusText: string,
17
+ message: string,
18
+ ) {
19
+ super(message);
20
+ this.name = "OutlookCalendarApiError";
21
+ }
22
+ }
23
+
24
+ const MAX_RETRIES = 3;
25
+ const INITIAL_BACKOFF_MS = 1000;
26
+
27
+ function isRetryable(status: number): boolean {
28
+ return status === 429 || (status >= 500 && status < 600);
29
+ }
30
+
31
+ const IDEMPOTENT_METHODS = new Set([
32
+ "GET",
33
+ "HEAD",
34
+ "PUT",
35
+ "DELETE",
36
+ "OPTIONS",
37
+ "PATCH",
38
+ ]);
39
+
40
+ function isIdempotent(method: string): boolean {
41
+ return IDEMPOTENT_METHODS.has(method.toUpperCase());
42
+ }
43
+
44
+ /**
45
+ * Make an authenticated request to the Microsoft Graph API with retry logic.
46
+ *
47
+ * The OAuth provider's baseUrl is `https://graph.microsoft.com`, so all paths
48
+ * must include the full API version and resource prefix (e.g. `/v1.0/me/events`).
49
+ */
50
+ async function request<T>(
51
+ connection: OAuthConnection,
52
+ path: string,
53
+ options?: RequestInit,
54
+ query?: Record<string, string | string[]>,
55
+ ): Promise<T> {
56
+ const method = (options?.method ?? "GET").toUpperCase();
57
+ const canRetry = isIdempotent(method);
58
+
59
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
60
+ let resp: OAuthConnectionResponse;
61
+ try {
62
+ const extraHeaders =
63
+ options?.headers &&
64
+ typeof options.headers === "object" &&
65
+ !Array.isArray(options.headers)
66
+ ? (options.headers as Record<string, string>)
67
+ : {};
68
+ resp = await connection.request({
69
+ method,
70
+ path,
71
+ query,
72
+ headers: {
73
+ "Content-Type": "application/json",
74
+ ...extraHeaders,
75
+ },
76
+ body: options?.body ? JSON.parse(options.body as string) : undefined,
77
+ });
78
+ } catch (err) {
79
+ // Network-level errors from connection.request() are not retryable
80
+ throw err;
81
+ }
82
+
83
+ if (resp.status < 200 || resp.status >= 300) {
84
+ if (canRetry && isRetryable(resp.status) && attempt < MAX_RETRIES) {
85
+ const retryAfter =
86
+ resp.headers["retry-after"] ?? resp.headers["Retry-After"];
87
+ const delayMs = retryAfter
88
+ ? parseInt(retryAfter, 10) * 1000
89
+ : INITIAL_BACKOFF_MS * Math.pow(2, attempt);
90
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
91
+ continue;
92
+ }
93
+ const bodyStr =
94
+ typeof resp.body === "string"
95
+ ? resp.body
96
+ : JSON.stringify(resp.body ?? "");
97
+ throw new OutlookCalendarApiError(
98
+ resp.status,
99
+ "",
100
+ `Microsoft Graph API ${resp.status}: ${bodyStr}`,
101
+ );
102
+ }
103
+
104
+ // Success
105
+ if (resp.status === 204 || resp.body === undefined) {
106
+ return undefined as T;
107
+ }
108
+ return resp.body as T;
109
+ }
110
+
111
+ throw new Error(
112
+ "Unreachable: retry loop exited without returning or throwing",
113
+ );
114
+ }
115
+
116
+ /** List calendar events, optionally within a specific calendar. */
117
+ export async function listEvents(
118
+ connection: OAuthConnection,
119
+ calendarId?: string,
120
+ options?: {
121
+ top?: number;
122
+ skip?: number;
123
+ filter?: string;
124
+ orderby?: string;
125
+ select?: string;
126
+ },
127
+ ): Promise<OutlookCalendarEventListResponse> {
128
+ const path =
129
+ calendarId && calendarId !== "primary"
130
+ ? `/v1.0/me/calendars/${encodeURIComponent(calendarId)}/events`
131
+ : "/v1.0/me/events";
132
+
133
+ const query: Record<string, string> = {};
134
+ if (options?.top !== undefined) query["$top"] = String(options.top);
135
+ if (options?.skip !== undefined) query["$skip"] = String(options.skip);
136
+ if (options?.filter) query["$filter"] = options.filter;
137
+ query["$orderby"] = options?.orderby ?? "start/dateTime";
138
+ if (options?.select) query["$select"] = options.select;
139
+
140
+ return request<OutlookCalendarEventListResponse>(
141
+ connection,
142
+ path,
143
+ undefined,
144
+ Object.keys(query).length > 0 ? query : undefined,
145
+ );
146
+ }
147
+
148
+ /** Get a single calendar event by ID. */
149
+ export async function getEvent(
150
+ connection: OAuthConnection,
151
+ eventId: string,
152
+ select?: string,
153
+ ): Promise<OutlookCalendarEvent> {
154
+ const query: Record<string, string> = {};
155
+ if (select) query["$select"] = select;
156
+
157
+ return request<OutlookCalendarEvent>(
158
+ connection,
159
+ `/v1.0/me/events/${encodeURIComponent(eventId)}`,
160
+ undefined,
161
+ Object.keys(query).length > 0 ? query : undefined,
162
+ );
163
+ }
164
+
165
+ /** Create a new calendar event. */
166
+ export async function createEvent(
167
+ connection: OAuthConnection,
168
+ event: Partial<OutlookCalendarEvent>,
169
+ calendarId?: string,
170
+ ): Promise<OutlookCalendarEvent> {
171
+ const path =
172
+ calendarId && calendarId !== "primary"
173
+ ? `/v1.0/me/calendars/${encodeURIComponent(calendarId)}/events`
174
+ : "/v1.0/me/events";
175
+
176
+ return request<OutlookCalendarEvent>(connection, path, {
177
+ method: "POST",
178
+ body: JSON.stringify(event),
179
+ });
180
+ }
181
+
182
+ /** Update (patch) an existing calendar event. */
183
+ export async function patchEvent(
184
+ connection: OAuthConnection,
185
+ eventId: string,
186
+ updates: Partial<OutlookCalendarEvent>,
187
+ ): Promise<OutlookCalendarEvent> {
188
+ return request<OutlookCalendarEvent>(
189
+ connection,
190
+ `/v1.0/me/events/${encodeURIComponent(eventId)}`,
191
+ {
192
+ method: "PATCH",
193
+ body: JSON.stringify(updates),
194
+ },
195
+ );
196
+ }
197
+
198
+ /** RSVP to a calendar event (accept, decline, or tentatively accept). */
199
+ export async function rsvpEvent(
200
+ connection: OAuthConnection,
201
+ eventId: string,
202
+ response: "accepted" | "declined" | "tentative",
203
+ sendResponse?: boolean,
204
+ comment?: string,
205
+ ): Promise<void> {
206
+ const endpointMap: Record<string, string> = {
207
+ accepted: "accept",
208
+ declined: "decline",
209
+ tentative: "tentativelyAccept",
210
+ };
211
+
212
+ const action = endpointMap[response];
213
+ const body: Record<string, unknown> = {};
214
+ if (sendResponse !== undefined) body.sendResponse = sendResponse;
215
+ if (comment !== undefined) body.comment = comment;
216
+
217
+ await request<void>(
218
+ connection,
219
+ `/v1.0/me/events/${encodeURIComponent(eventId)}/${action}`,
220
+ {
221
+ method: "POST",
222
+ body: JSON.stringify(body),
223
+ },
224
+ );
225
+ }
226
+
227
+ /** Get free/busy schedule for one or more users by email address. */
228
+ export async function getSchedule(
229
+ connection: OAuthConnection,
230
+ query: {
231
+ schedules: string[];
232
+ startTime: OutlookDateTimeZone;
233
+ endTime: OutlookDateTimeZone;
234
+ availabilityViewInterval?: number;
235
+ },
236
+ ): Promise<OutlookScheduleResponse> {
237
+ return request<OutlookScheduleResponse>(
238
+ connection,
239
+ "/v1.0/me/calendar/getSchedule",
240
+ {
241
+ method: "POST",
242
+ body: JSON.stringify(query),
243
+ },
244
+ );
245
+ }
246
+
247
+ /** List all calendars in the user's mailbox. */
248
+ export async function listCalendars(
249
+ connection: OAuthConnection,
250
+ ): Promise<OutlookCalendarListResponse> {
251
+ return request<OutlookCalendarListResponse>(connection, "/v1.0/me/calendars");
252
+ }
@@ -0,0 +1,53 @@
1
+ import type {
2
+ ToolContext,
3
+ ToolExecutionResult,
4
+ } from "../../../../tools/types.js";
5
+ import * as calendar from "../calendar-client.js";
6
+ import { err, getCalendarConnection, ok } from "./shared.js";
7
+
8
+ export async function run(
9
+ input: Record<string, unknown>,
10
+ _context: ToolContext,
11
+ ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
13
+ const timeMin = input.time_min as string;
14
+ const timeMax = input.time_max as string;
15
+ let schedules = (input.schedules as string[] | undefined) ?? [];
16
+ const timezone = (input.timezone as string | undefined) ?? "UTC";
17
+
18
+ const connection = await getCalendarConnection(account);
19
+
20
+ if (schedules.length === 0) {
21
+ const resp = await connection.request({
22
+ method: "GET",
23
+ path: "/v1.0/me",
24
+ });
25
+ if (resp.status < 200 || resp.status >= 300) {
26
+ return err(
27
+ "Failed to retrieve your Microsoft profile. Cannot determine your email for availability lookup.",
28
+ );
29
+ }
30
+ const body = resp.body as Record<string, unknown>;
31
+ const email =
32
+ (body.mail as string | undefined) ??
33
+ (body.userPrincipalName as string | undefined);
34
+ if (email) {
35
+ schedules = [email];
36
+ }
37
+ }
38
+
39
+ if (schedules.length === 0) {
40
+ return err(
41
+ "No schedules provided and could not determine your email address from your Microsoft profile. " +
42
+ "Please provide at least one email address in the schedules parameter.",
43
+ );
44
+ }
45
+
46
+ const result = await calendar.getSchedule(connection, {
47
+ schedules,
48
+ startTime: { dateTime: timeMin, timeZone: timezone },
49
+ endTime: { dateTime: timeMax, timeZone: timezone },
50
+ });
51
+
52
+ return ok(JSON.stringify(result, null, 2));
53
+ }
@@ -0,0 +1,74 @@
1
+ import type {
2
+ ToolContext,
3
+ ToolExecutionResult,
4
+ } from "../../../../tools/types.js";
5
+ import * as calendar from "../calendar-client.js";
6
+ import { getCalendarConnection, ok } from "./shared.js";
7
+
8
+ export async function run(
9
+ input: Record<string, unknown>,
10
+ _context: ToolContext,
11
+ ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
13
+ const summary = input.summary as string;
14
+ const startRaw = input.start as string;
15
+ const endRaw = input.end as string;
16
+ const description = input.description as string | undefined;
17
+ const location = input.location as string | undefined;
18
+ const attendees = input.attendees as string[] | undefined;
19
+ const timezone = input.timezone as string | undefined;
20
+ const calendarId = input.calendar_id as string | undefined;
21
+
22
+ // Detect all-day events: if start string does not contain "T", treat as all-day
23
+ const isAllDay = !startRaw.includes("T");
24
+
25
+ // Determine the timeZone to send. If the caller provided an explicit IANA
26
+ // timezone, always use it. Otherwise, only fall back to UTC when the
27
+ // dateTime string does NOT already carry an offset (e.g. "-05:00" or "Z").
28
+ // Sending timeZone: "UTC" alongside a dateTime that contains a different
29
+ // offset would cause Microsoft Graph to ignore the offset and interpret the
30
+ // time as UTC, creating events at the wrong wall-clock time.
31
+ const hasOffset = (dt: string) => /[Zz]|[+-]\d{2}:\d{2}$/.test(dt);
32
+ const resolveTimeZone = (dt: string) =>
33
+ timezone ?? (hasOffset(dt) ? undefined : "UTC");
34
+
35
+ const start = isAllDay
36
+ ? {
37
+ dateTime: `${startRaw}T00:00:00`,
38
+ timeZone: timezone ?? "UTC",
39
+ }
40
+ : { dateTime: startRaw, timeZone: resolveTimeZone(startRaw) };
41
+
42
+ const end = isAllDay
43
+ ? {
44
+ dateTime: `${endRaw}T00:00:00`,
45
+ timeZone: timezone ?? "UTC",
46
+ }
47
+ : { dateTime: endRaw, timeZone: resolveTimeZone(endRaw) };
48
+
49
+ const eventBody: Parameters<typeof calendar.createEvent>[1] = {
50
+ subject: summary,
51
+ start,
52
+ end,
53
+ isAllDay,
54
+ };
55
+
56
+ if (description) {
57
+ eventBody.body = { contentType: "text", content: description };
58
+ }
59
+ if (location) {
60
+ eventBody.location = { displayName: location };
61
+ }
62
+ if (attendees?.length) {
63
+ eventBody.attendees = attendees.map((email) => ({
64
+ emailAddress: { address: email },
65
+ type: "required" as const,
66
+ }));
67
+ }
68
+
69
+ const connection = await getCalendarConnection(account);
70
+ const event = await calendar.createEvent(connection, eventBody, calendarId);
71
+ return ok(
72
+ `Event created (ID: ${event.id}).${event.webLink ? ` View it here: ${event.webLink}` : ""}`,
73
+ );
74
+ }
@@ -0,0 +1,18 @@
1
+ import type {
2
+ ToolContext,
3
+ ToolExecutionResult,
4
+ } from "../../../../tools/types.js";
5
+ import * as calendar from "../calendar-client.js";
6
+ import { getCalendarConnection, ok } from "./shared.js";
7
+
8
+ export async function run(
9
+ input: Record<string, unknown>,
10
+ _context: ToolContext,
11
+ ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
13
+ const eventId = input.event_id as string;
14
+
15
+ const connection = await getCalendarConnection(account);
16
+ const event = await calendar.getEvent(connection, eventId);
17
+ return ok(JSON.stringify(event, null, 2));
18
+ }
@@ -0,0 +1,46 @@
1
+ import type {
2
+ ToolContext,
3
+ ToolExecutionResult,
4
+ } from "../../../../tools/types.js";
5
+ import * as calendar from "../calendar-client.js";
6
+ import { getCalendarConnection, ok } from "./shared.js";
7
+
8
+ export async function run(
9
+ input: Record<string, unknown>,
10
+ _context: ToolContext,
11
+ ): Promise<ToolExecutionResult> {
12
+ const account = input.account as string | undefined;
13
+ const calendarId = input.calendar_id as string | undefined;
14
+ const timeMin = (input.time_min as string) ?? new Date().toISOString();
15
+ const timeMax = input.time_max as string | undefined;
16
+ const maxResults = Math.min((input.max_results as number) ?? 25, 250);
17
+ const query = input.query as string | undefined;
18
+ const orderBy = input.order_by as string | undefined;
19
+
20
+ const connection = await getCalendarConnection(account);
21
+
22
+ // Build OData $filter from time range and optional user query
23
+ const filterParts: string[] = [];
24
+ if (timeMin) {
25
+ filterParts.push(`start/dateTime ge '${timeMin}'`);
26
+ }
27
+ if (timeMax) {
28
+ filterParts.push(`start/dateTime le '${timeMax}'`);
29
+ }
30
+ if (query) {
31
+ filterParts.push(query);
32
+ }
33
+ const filter = filterParts.length > 0 ? filterParts.join(" and ") : undefined;
34
+
35
+ const result = await calendar.listEvents(connection, calendarId, {
36
+ filter,
37
+ top: maxResults,
38
+ orderby: orderBy,
39
+ });
40
+
41
+ if (!result.value?.length) {
42
+ return ok("No events found in the specified time range.");
43
+ }
44
+
45
+ return ok(JSON.stringify(result, null, 2));
46
+ }