@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,343 @@
1
+ /**
2
+ * Outlook Calendar watcher provider — uses Microsoft Graph delta queries
3
+ * for efficient change detection.
4
+ *
5
+ * On first poll, performs a delta query to capture the initial @odata.deltaLink
6
+ * as the watermark (start from "now"). Subsequent polls use the deltaLink to
7
+ * detect new/updated events. Falls back to a fresh delta query if the delta
8
+ * token has expired (410 Gone).
9
+ */
10
+
11
+ import { OutlookCalendarApiError } from "../../config/bundled-skills/outlook-calendar/calendar-client.js";
12
+ import type { OutlookCalendarEvent } from "../../config/bundled-skills/outlook-calendar/types.js";
13
+ import type { OAuthConnection } from "../../oauth/connection.js";
14
+ import { resolveOAuthConnection } from "../../oauth/connection-resolver.js";
15
+ import { getLogger } from "../../util/logger.js";
16
+ import type {
17
+ FetchResult,
18
+ WatcherItem,
19
+ WatcherProvider,
20
+ } from "../provider-types.js";
21
+
22
+ const CREDENTIAL_SERVICE = "outlook";
23
+ const log = getLogger("watcher:outlook-calendar");
24
+
25
+ const DELTA_SELECT_FIELDS = [
26
+ "subject",
27
+ "start",
28
+ "end",
29
+ "location",
30
+ "bodyPreview",
31
+ "isAllDay",
32
+ "showAs",
33
+ "organizer",
34
+ "attendees",
35
+ "webLink",
36
+ "createdDateTime",
37
+ "lastModifiedDateTime",
38
+ "isCancelled",
39
+ "type",
40
+ ].join(",");
41
+
42
+ function eventToItem(
43
+ event: OutlookCalendarEvent,
44
+ eventType: string,
45
+ ): WatcherItem {
46
+ const start = event.start?.dateTime ?? "";
47
+ const end = event.end?.dateTime ?? "";
48
+
49
+ // Include lastModifiedDateTime in the dedup key so subsequent edits to the
50
+ // same event aren't silently dropped by the watcher_id + external_id constraint.
51
+ const version = event.lastModifiedDateTime ?? "";
52
+ const externalId = version ? `${event.id}@${version}` : event.id;
53
+
54
+ return {
55
+ externalId,
56
+ eventType,
57
+ summary: `Calendar event: ${event.subject ?? "(no title)"} — ${start}`,
58
+ payload: {
59
+ id: event.id,
60
+ subject: event.subject ?? "",
61
+ start,
62
+ end,
63
+ startTimeZone: event.start?.timeZone ?? "",
64
+ endTimeZone: event.end?.timeZone ?? "",
65
+ location: event.location?.displayName ?? "",
66
+ bodyPreview: event.bodyPreview ?? "",
67
+ isAllDay: event.isAllDay ?? false,
68
+ showAs: event.showAs ?? "busy",
69
+ organizer: event.organizer?.emailAddress?.address ?? "",
70
+ attendees:
71
+ event.attendees?.map((a) => ({
72
+ email: a.emailAddress.address,
73
+ response: a.status?.response,
74
+ })) ?? [],
75
+ webLink: event.webLink ?? "",
76
+ },
77
+ timestamp: event.lastModifiedDateTime
78
+ ? new Date(event.lastModifiedDateTime).getTime()
79
+ : Date.now(),
80
+ };
81
+ }
82
+
83
+ /** Thrown when Microsoft Graph returns 410 Gone (delta token expired). */
84
+ class DeltaSyncExpiredError extends Error {
85
+ constructor(message: string) {
86
+ super(message);
87
+ this.name = "DeltaSyncExpiredError";
88
+ }
89
+ }
90
+
91
+ interface DeltaSyncResult {
92
+ items: OutlookCalendarEvent[];
93
+ deltaLink: string;
94
+ }
95
+
96
+ /**
97
+ * Perform an incremental delta sync using a stored @odata.deltaLink.
98
+ * Follows pagination (@odata.nextLink) until the final page returns
99
+ * @odata.deltaLink. Returns all accumulated events and the new deltaLink.
100
+ */
101
+ async function deltaSync(
102
+ connection: OAuthConnection,
103
+ deltaLink: string,
104
+ ): Promise<DeltaSyncResult> {
105
+ const allItems: OutlookCalendarEvent[] = [];
106
+ let currentUrl = deltaLink;
107
+ let newDeltaLink: string | undefined;
108
+
109
+ do {
110
+ const parsed = new URL(currentUrl);
111
+ const path = parsed.pathname;
112
+ const query: Record<string, string> = {};
113
+ parsed.searchParams.forEach((v, k) => {
114
+ query[k] = v;
115
+ });
116
+
117
+ const resp = await connection.request({
118
+ method: "GET",
119
+ path,
120
+ query: Object.keys(query).length > 0 ? query : undefined,
121
+ });
122
+
123
+ if (resp.status < 200 || resp.status >= 300) {
124
+ const bodyStr =
125
+ typeof resp.body === "string"
126
+ ? resp.body
127
+ : JSON.stringify(resp.body ?? "");
128
+ if (resp.status === 410) {
129
+ throw new DeltaSyncExpiredError(bodyStr);
130
+ }
131
+ throw new OutlookCalendarApiError(
132
+ resp.status,
133
+ "",
134
+ `Microsoft Graph Calendar Delta API ${resp.status}: ${bodyStr}`,
135
+ );
136
+ }
137
+
138
+ const page = resp.body as {
139
+ value?: OutlookCalendarEvent[];
140
+ "@odata.nextLink"?: string;
141
+ "@odata.deltaLink"?: string;
142
+ };
143
+
144
+ if (page.value) {
145
+ allItems.push(...page.value);
146
+ }
147
+
148
+ newDeltaLink = page["@odata.deltaLink"];
149
+ currentUrl = page["@odata.nextLink"] ?? "";
150
+ } while (currentUrl && !newDeltaLink);
151
+
152
+ if (!newDeltaLink) {
153
+ throw new Error(
154
+ "Outlook Calendar delta query completed without returning a deltaLink",
155
+ );
156
+ }
157
+
158
+ return { items: allItems, deltaLink: newDeltaLink };
159
+ }
160
+
161
+ /**
162
+ * Perform the initial delta query to capture the current position.
163
+ * Queries calendarView/delta from now to 30 days out with the selected fields,
164
+ * paginating through all pages until reaching the final @odata.deltaLink.
165
+ */
166
+ async function initialDeltaQuery(
167
+ connection: OAuthConnection,
168
+ ): Promise<DeltaSyncResult> {
169
+ const now = new Date();
170
+ const thirtyDaysOut = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
171
+
172
+ const allItems: OutlookCalendarEvent[] = [];
173
+ let nextLink: string | undefined;
174
+ let newDeltaLink: string | undefined;
175
+
176
+ // First request — initial delta query with parameters
177
+ const initialQuery: Record<string, string> = {
178
+ startDateTime: now.toISOString(),
179
+ endDateTime: thirtyDaysOut.toISOString(),
180
+ $select: DELTA_SELECT_FIELDS,
181
+ };
182
+
183
+ const firstResp = await connection.request({
184
+ method: "GET",
185
+ path: "/v1.0/me/calendarView/delta",
186
+ query: initialQuery,
187
+ });
188
+
189
+ if (firstResp.status < 200 || firstResp.status >= 300) {
190
+ const bodyStr =
191
+ typeof firstResp.body === "string"
192
+ ? firstResp.body
193
+ : JSON.stringify(firstResp.body ?? "");
194
+ throw new OutlookCalendarApiError(
195
+ firstResp.status,
196
+ "",
197
+ `Microsoft Graph Calendar Delta API ${firstResp.status}: ${bodyStr}`,
198
+ );
199
+ }
200
+
201
+ const firstPage = firstResp.body as {
202
+ value?: OutlookCalendarEvent[];
203
+ "@odata.nextLink"?: string;
204
+ "@odata.deltaLink"?: string;
205
+ };
206
+
207
+ if (firstPage.value) {
208
+ allItems.push(...firstPage.value);
209
+ }
210
+
211
+ newDeltaLink = firstPage["@odata.deltaLink"];
212
+ nextLink = firstPage["@odata.nextLink"];
213
+
214
+ // Follow pagination until we get a deltaLink
215
+ while (nextLink && !newDeltaLink) {
216
+ const parsed = new URL(nextLink);
217
+ const path = parsed.pathname;
218
+ const query: Record<string, string> = {};
219
+ parsed.searchParams.forEach((v, k) => {
220
+ query[k] = v;
221
+ });
222
+
223
+ const resp = await connection.request({
224
+ method: "GET",
225
+ path,
226
+ query: Object.keys(query).length > 0 ? query : undefined,
227
+ });
228
+
229
+ if (resp.status < 200 || resp.status >= 300) {
230
+ const bodyStr =
231
+ typeof resp.body === "string"
232
+ ? resp.body
233
+ : JSON.stringify(resp.body ?? "");
234
+ throw new OutlookCalendarApiError(
235
+ resp.status,
236
+ "",
237
+ `Microsoft Graph Calendar Delta API ${resp.status}: ${bodyStr}`,
238
+ );
239
+ }
240
+
241
+ const page = resp.body as {
242
+ value?: OutlookCalendarEvent[];
243
+ "@odata.nextLink"?: string;
244
+ "@odata.deltaLink"?: string;
245
+ };
246
+
247
+ if (page.value) {
248
+ allItems.push(...page.value);
249
+ }
250
+
251
+ newDeltaLink = page["@odata.deltaLink"];
252
+ nextLink = page["@odata.nextLink"];
253
+ }
254
+
255
+ if (!newDeltaLink) {
256
+ throw new Error(
257
+ "Outlook Calendar initial delta query completed without returning a deltaLink",
258
+ );
259
+ }
260
+
261
+ return { items: allItems, deltaLink: newDeltaLink };
262
+ }
263
+
264
+ export const outlookCalendarProvider: WatcherProvider = {
265
+ id: "outlook-calendar",
266
+ displayName: "Outlook Calendar",
267
+ requiredCredentialService: CREDENTIAL_SERVICE,
268
+
269
+ async getInitialWatermark(credentialService: string): Promise<string> {
270
+ const connection = await resolveOAuthConnection(credentialService);
271
+ const { deltaLink } = await initialDeltaQuery(connection);
272
+ return deltaLink;
273
+ },
274
+
275
+ async fetchNew(
276
+ credentialService: string,
277
+ watermark: string | null,
278
+ _config: Record<string, unknown>,
279
+ _watcherKey: string,
280
+ ): Promise<FetchResult> {
281
+ const connection = await resolveOAuthConnection(credentialService);
282
+
283
+ if (!watermark) {
284
+ // No watermark — get initial position, return no items
285
+ const { deltaLink } = await initialDeltaQuery(connection);
286
+ return { items: [], watermark: deltaLink };
287
+ }
288
+
289
+ try {
290
+ const { items: events, deltaLink: newDeltaLink } = await deltaSync(
291
+ connection,
292
+ watermark,
293
+ );
294
+
295
+ if (events.length === 0) {
296
+ return { items: [], watermark: newDeltaLink };
297
+ }
298
+
299
+ // Filter out cancelled events and convert to watcher items
300
+ const items: WatcherItem[] = [];
301
+ for (const event of events) {
302
+ if (event.isCancelled) continue;
303
+
304
+ const eventType =
305
+ event.createdDateTime === event.lastModifiedDateTime
306
+ ? "new_calendar_event"
307
+ : "updated_calendar_event";
308
+ items.push(eventToItem(event, eventType));
309
+ }
310
+
311
+ log.info(
312
+ { count: items.length, watermark: newDeltaLink },
313
+ "Outlook Calendar: fetched event changes",
314
+ );
315
+
316
+ return { items, watermark: newDeltaLink };
317
+ } catch (err) {
318
+ if (err instanceof DeltaSyncExpiredError) {
319
+ log.warn(
320
+ "Outlook Calendar delta token expired, falling back to fresh query",
321
+ );
322
+ return fallbackFetch(connection);
323
+ }
324
+ throw err;
325
+ }
326
+ },
327
+ };
328
+
329
+ /**
330
+ * Fallback when delta token expires (410 Gone): perform a fresh initial
331
+ * delta query to get current events and a new deltaLink.
332
+ */
333
+ async function fallbackFetch(
334
+ connection: OAuthConnection,
335
+ ): Promise<FetchResult> {
336
+ const { items: events, deltaLink } = await initialDeltaQuery(connection);
337
+
338
+ const items = events
339
+ .filter((event) => !event.isCancelled)
340
+ .map((event) => eventToItem(event, "new_calendar_event"));
341
+
342
+ return { items, watermark: deltaLink };
343
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Outlook watcher provider — uses Microsoft Graph delta queries for efficient
3
+ * change detection.
4
+ *
5
+ * On first poll, captures the initial deltaLink as the watermark (start from "now").
6
+ * Subsequent polls use the deltaLink to detect new messages.
7
+ * Falls back to listing recent inbox messages if the sync state has expired (410 Gone).
8
+ */
9
+
10
+ import {
11
+ listMessages,
12
+ listMessagesDelta,
13
+ OutlookApiError,
14
+ } from "../../messaging/providers/outlook/client.js";
15
+ import type {
16
+ OutlookDeltaResponse,
17
+ OutlookMessage,
18
+ } from "../../messaging/providers/outlook/types.js";
19
+ import type { OAuthConnection } from "../../oauth/connection.js";
20
+ import { resolveOAuthConnection } from "../../oauth/connection-resolver.js";
21
+ import { getLogger } from "../../util/logger.js";
22
+ import type {
23
+ FetchResult,
24
+ WatcherItem,
25
+ WatcherProvider,
26
+ } from "../provider-types.js";
27
+
28
+ const log = getLogger("watcher:outlook");
29
+
30
+ /** Thrown when Microsoft Graph returns 410 Gone (delta sync state expired). */
31
+ export class DeltaSyncExpiredError extends Error {
32
+ constructor(message: string) {
33
+ super(message);
34
+ this.name = "DeltaSyncExpiredError";
35
+ }
36
+ }
37
+
38
+ function messageToItem(msg: OutlookMessage): WatcherItem {
39
+ const from =
40
+ msg.from?.emailAddress?.name ||
41
+ msg.from?.emailAddress?.address ||
42
+ "Unknown";
43
+ const subject = msg.subject ?? "(no subject)";
44
+
45
+ return {
46
+ externalId: msg.id,
47
+ eventType: "new_email",
48
+ summary: `Email from ${from}: ${subject}`,
49
+ payload: {
50
+ id: msg.id,
51
+ conversationId: msg.conversationId,
52
+ from,
53
+ fromAddress: msg.from?.emailAddress?.address ?? "",
54
+ subject,
55
+ receivedDateTime: msg.receivedDateTime,
56
+ bodyPreview: msg.bodyPreview ?? "",
57
+ isRead: msg.isRead ?? false,
58
+ hasAttachments: msg.hasAttachments ?? false,
59
+ },
60
+ timestamp: msg.receivedDateTime
61
+ ? new Date(msg.receivedDateTime).getTime()
62
+ : Date.now(),
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Fetch all pages of a delta response, following @odata.nextLink until
68
+ * a @odata.deltaLink is returned.
69
+ */
70
+ async function fetchAllDeltaPages(
71
+ connection: OAuthConnection,
72
+ folderId: string,
73
+ deltaLink?: string,
74
+ ): Promise<{ messages: OutlookMessage[]; newDeltaLink: string }> {
75
+ const messages: OutlookMessage[] = [];
76
+
77
+ let resp: OutlookDeltaResponse<OutlookMessage>;
78
+ try {
79
+ resp = await listMessagesDelta(connection, folderId, deltaLink);
80
+ } catch (err) {
81
+ if (err instanceof OutlookApiError && err.status === 410) {
82
+ throw new DeltaSyncExpiredError(err.message);
83
+ }
84
+ throw err;
85
+ }
86
+
87
+ if (resp.value) {
88
+ messages.push(...resp.value);
89
+ }
90
+
91
+ // Follow pagination until we get a deltaLink
92
+ while (resp["@odata.nextLink"] && !resp["@odata.deltaLink"]) {
93
+ try {
94
+ resp = await listMessagesDelta(
95
+ connection,
96
+ folderId,
97
+ resp["@odata.nextLink"],
98
+ );
99
+ } catch (err) {
100
+ if (err instanceof OutlookApiError && err.status === 410) {
101
+ throw new DeltaSyncExpiredError(
102
+ err instanceof Error ? err.message : String(err),
103
+ );
104
+ }
105
+ throw err;
106
+ }
107
+ if (resp.value) {
108
+ messages.push(...resp.value);
109
+ }
110
+ }
111
+
112
+ const newDeltaLink = resp["@odata.deltaLink"];
113
+ if (!newDeltaLink) {
114
+ throw new Error(
115
+ "Outlook delta query completed without returning a deltaLink",
116
+ );
117
+ }
118
+
119
+ return { messages, newDeltaLink };
120
+ }
121
+
122
+ export const outlookProvider: WatcherProvider = {
123
+ id: "outlook",
124
+ displayName: "Outlook",
125
+ requiredCredentialService: "outlook",
126
+
127
+ async getInitialWatermark(credentialService: string): Promise<string> {
128
+ const connection = await resolveOAuthConnection(credentialService);
129
+ const { newDeltaLink } = await fetchAllDeltaPages(connection, "inbox");
130
+ return newDeltaLink;
131
+ },
132
+
133
+ async fetchNew(
134
+ credentialService: string,
135
+ watermark: string | null,
136
+ _config: Record<string, unknown>,
137
+ _watcherKey: string,
138
+ ): Promise<FetchResult> {
139
+ const connection = await resolveOAuthConnection(credentialService);
140
+
141
+ if (!watermark) {
142
+ // No watermark — get initial position, return no items
143
+ const { newDeltaLink } = await fetchAllDeltaPages(connection, "inbox");
144
+ return { items: [], watermark: newDeltaLink };
145
+ }
146
+
147
+ try {
148
+ const { messages, newDeltaLink } = await fetchAllDeltaPages(
149
+ connection,
150
+ "inbox",
151
+ watermark,
152
+ );
153
+
154
+ if (messages.length === 0) {
155
+ return { items: [], watermark: newDeltaLink };
156
+ }
157
+
158
+ const items = messages.map(messageToItem);
159
+ log.info(
160
+ { count: items.length, watermark: newDeltaLink },
161
+ "Outlook: fetched new messages",
162
+ );
163
+
164
+ return { items, watermark: newDeltaLink };
165
+ } catch (err) {
166
+ if (err instanceof DeltaSyncExpiredError) {
167
+ log.warn(
168
+ "Outlook delta sync state expired, falling back to recent inbox messages",
169
+ );
170
+ return fallbackFetch(connection);
171
+ }
172
+ throw err;
173
+ }
174
+ },
175
+ };
176
+
177
+ /**
178
+ * Fallback when sync state expires (410 Gone): list recent inbox messages
179
+ * from the last day, then get a fresh deltaLink.
180
+ */
181
+ async function fallbackFetch(
182
+ connection: OAuthConnection,
183
+ ): Promise<FetchResult> {
184
+ const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
185
+ const resp = await listMessages(connection, {
186
+ folderId: "inbox",
187
+ top: 20,
188
+ filter: `receivedDateTime ge ${oneDayAgo}`,
189
+ orderby: "receivedDateTime desc",
190
+ });
191
+
192
+ const items = (resp.value ?? []).map(messageToItem);
193
+
194
+ // Get a fresh deltaLink for the new watermark
195
+ const { newDeltaLink } = await fetchAllDeltaPages(connection, "inbox");
196
+
197
+ return { items, watermark: newDeltaLink };
198
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Workspace migration 025: Remove standalone OAuth app setup skill directories
3
+ * and their SKILLS.md entries from user workspaces.
4
+ *
5
+ * These skills have been consolidated into vellum-oauth-integrations and are
6
+ * no longer shipped as standalone skills.
7
+ *
8
+ * Idempotent: safe to re-run after interruption at any point.
9
+ */
10
+
11
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+
14
+ import type { WorkspaceMigration } from "./types.js";
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Skills to remove
18
+ // ---------------------------------------------------------------------------
19
+
20
+ const DELETED_SKILLS = [
21
+ "airtable-oauth-app-setup",
22
+ "asana-oauth-app-setup",
23
+ "discord-oauth-app-setup",
24
+ "dropbox-oauth-app-setup",
25
+ "figma-oauth-app-setup",
26
+ "github-oauth-app-setup",
27
+ "google-oauth-app-setup",
28
+ "hubspot-oauth-app-setup",
29
+ "linear-oauth-app-setup",
30
+ "notion-oauth-app-setup",
31
+ "spotify-oauth-app-setup",
32
+ "todoist-oauth-app-setup",
33
+ "twitter-oauth-app-setup",
34
+ ];
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Migration
38
+ // ---------------------------------------------------------------------------
39
+
40
+ export const removeOauthAppSetupSkillsMigration: WorkspaceMigration = {
41
+ id: "025-remove-oauth-app-setup-skills",
42
+ description:
43
+ "Remove standalone OAuth app setup skill directories consolidated into vellum-oauth-integrations",
44
+
45
+ run(workspaceDir: string): void {
46
+ const skillsDir = join(workspaceDir, "skills");
47
+ if (!existsSync(skillsDir)) return;
48
+
49
+ // 1. Remove skill directories
50
+ for (const name of DELETED_SKILLS) {
51
+ const dir = join(skillsDir, name);
52
+ if (existsSync(dir)) {
53
+ rmSync(dir, { recursive: true, force: true });
54
+ }
55
+ }
56
+
57
+ // 2. Update SKILLS.md to remove entries referencing deleted skills
58
+ const indexPath = join(skillsDir, "SKILLS.md");
59
+ if (existsSync(indexPath)) {
60
+ let content = readFileSync(indexPath, "utf-8");
61
+ for (const name of DELETED_SKILLS) {
62
+ content = content.replace(
63
+ new RegExp(`^[\\t ]*-\\s*${name}\\s*\\n?`, "gm"),
64
+ "",
65
+ );
66
+ }
67
+ writeFileSync(indexPath, content, "utf-8");
68
+ }
69
+ },
70
+
71
+ down(_workspaceDir: string): void {
72
+ // Deleted skills cannot be restored since they have been removed from the
73
+ // repo. Users would need to reinstall the `vellum-oauth-integrations`
74
+ // skill to regain the consolidated OAuth setup functionality.
75
+ },
76
+ };