@vellumai/assistant 0.5.15 → 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 (503) hide show
  1. package/ARCHITECTURE.md +3 -3
  2. package/Dockerfile +0 -3
  3. package/docs/architecture/integrations.md +15 -14
  4. package/knip.json +4 -1
  5. package/openapi.yaml +670 -122
  6. package/package.json +1 -1
  7. package/src/__tests__/actor-token-service.test.ts +68 -0
  8. package/src/__tests__/agent-loop.test.ts +0 -32
  9. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  10. package/src/__tests__/anthropic-provider.test.ts +57 -3
  11. package/src/__tests__/app-compiler.test.ts +120 -0
  12. package/src/__tests__/assistant-feature-flags-integration.test.ts +5 -377
  13. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  14. package/src/__tests__/call-domain.test.ts +2 -6
  15. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  16. package/src/__tests__/call-recovery.test.ts +2 -6
  17. package/src/__tests__/call-routes-http.test.ts +2 -6
  18. package/src/__tests__/call-store.test.ts +2 -6
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  20. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  21. package/src/__tests__/ces-rpc-credential-backend.test.ts +4 -1
  22. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  23. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  24. package/src/__tests__/checker.test.ts +84 -3
  25. package/src/__tests__/clawhub.test.ts +54 -24
  26. package/src/__tests__/cli-command-risk-guard.test.ts +108 -6
  27. package/src/__tests__/cli-memory.test.ts +377 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +12 -2
  29. package/src/__tests__/config-schema.test.ts +1 -3
  30. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  31. package/src/__tests__/config-watcher-feature-flags.test.ts +211 -0
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  33. package/src/__tests__/contacts-tools.test.ts +31 -0
  34. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  35. package/src/__tests__/context-token-estimator.test.ts +175 -10
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
  37. package/src/__tests__/conversation-agent-loop.test.ts +9 -0
  38. package/src/__tests__/conversation-attachments.test.ts +2 -6
  39. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  40. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  41. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  42. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  43. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  44. package/src/__tests__/conversation-error.test.ts +33 -2
  45. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  46. package/src/__tests__/conversation-history-web-search.test.ts +5 -0
  47. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  48. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  49. package/src/__tests__/conversation-runtime-assembly.test.ts +7 -4
  50. package/src/__tests__/conversation-slash-commands.test.ts +2 -6
  51. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  52. package/src/__tests__/conversation-store.test.ts +2 -6
  53. package/src/__tests__/conversation-usage.test.ts +3 -6
  54. package/src/__tests__/conversation-wipe.test.ts +11 -408
  55. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  56. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  57. package/src/__tests__/credential-security-e2e.test.ts +6 -1
  58. package/src/__tests__/docker-signing-key-bootstrap.test.ts +7 -73
  59. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -7
  60. package/src/__tests__/followup-tools.test.ts +2 -6
  61. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  62. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  63. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  64. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  65. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  66. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  67. package/src/__tests__/guardian-action-store.test.ts +2 -6
  68. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  69. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  70. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  71. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  72. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  73. package/src/__tests__/guardian-routing-invariants.test.ts +343 -6
  74. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  75. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  76. package/src/__tests__/heartbeat-service.test.ts +1 -3
  77. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  78. package/src/__tests__/injection-block.test.ts +154 -0
  79. package/src/__tests__/install-meta.test.ts +506 -0
  80. package/src/__tests__/install-skill-routing.test.ts +292 -0
  81. package/src/__tests__/intent-routing.test.ts +6 -18
  82. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  83. package/src/__tests__/invite-routes-http.test.ts +2 -6
  84. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
  85. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  86. package/src/__tests__/llm-context-route-provider.test.ts +2 -6
  87. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
  88. package/src/__tests__/llm-usage-store.test.ts +2 -6
  89. package/src/__tests__/log-export-workspace.test.ts +4 -34
  90. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -37
  91. package/src/__tests__/managed-store.test.ts +40 -21
  92. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  93. package/src/__tests__/memory-recall-log-store.test.ts +2 -6
  94. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  95. package/src/__tests__/messaging-send-tool.test.ts +6 -6
  96. package/src/__tests__/migration-cross-version-compatibility.test.ts +1 -29
  97. package/src/__tests__/migration-export-http.test.ts +3 -34
  98. package/src/__tests__/migration-import-commit-http.test.ts +1 -29
  99. package/src/__tests__/migration-import-preflight-http.test.ts +3 -34
  100. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +2 -1
  101. package/src/__tests__/non-member-access-request.test.ts +2 -6
  102. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  103. package/src/__tests__/oauth-apps-routes.test.ts +120 -10
  104. package/src/__tests__/oauth-cli.test.ts +364 -2
  105. package/src/__tests__/oauth-connect-orchestrator.test.ts +709 -0
  106. package/src/__tests__/oauth-provider-serializer.test.ts +2 -1
  107. package/src/__tests__/oauth-provider-visibility.test.ts +149 -0
  108. package/src/__tests__/oauth-providers-routes.test.ts +5 -2
  109. package/src/__tests__/oauth-store.test.ts +0 -5
  110. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  111. package/src/__tests__/outlook-attachments.test.ts +301 -0
  112. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  113. package/src/__tests__/outlook-categories.test.ts +212 -0
  114. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  115. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  116. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  117. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  118. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  119. package/src/__tests__/outlook-messaging-provider.test.ts +1071 -0
  120. package/src/__tests__/outlook-trash.test.ts +77 -0
  121. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  122. package/src/__tests__/path-policy.test.ts +2 -17
  123. package/src/__tests__/permission-types.test.ts +0 -1
  124. package/src/__tests__/platform-callback-registration.test.ts +7 -11
  125. package/src/__tests__/playbook-execution.test.ts +76 -80
  126. package/src/__tests__/playbook-tools.test.ts +5 -7
  127. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  128. package/src/__tests__/provider-error-scenarios.test.ts +21 -2
  129. package/src/__tests__/qdrant-manager.test.ts +68 -21
  130. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  131. package/src/__tests__/registry.test.ts +2 -2
  132. package/src/__tests__/require-fresh-approval.test.ts +64 -3
  133. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  134. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  135. package/src/__tests__/sandbox-diagnostics.test.ts +20 -29
  136. package/src/__tests__/scaffold-managed-skill-tool.test.ts +2 -10
  137. package/src/__tests__/schedule-store.test.ts +2 -6
  138. package/src/__tests__/schedule-tools.test.ts +2 -6
  139. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  140. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  141. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  142. package/src/__tests__/search-skills-unified.test.ts +421 -0
  143. package/src/__tests__/secret-allowlist.test.ts +20 -35
  144. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  145. package/src/__tests__/send-endpoint-busy.test.ts +2 -6
  146. package/src/__tests__/sequence-store.test.ts +2 -6
  147. package/src/__tests__/server-history-render.test.ts +2 -6
  148. package/src/__tests__/shell-credential-ref.test.ts +0 -5
  149. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  150. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  151. package/src/__tests__/skill-load-feature-flag.test.ts +13 -54
  152. package/src/__tests__/skill-load-inline-command.test.ts +3 -65
  153. package/src/__tests__/skill-load-inline-includes.test.ts +3 -65
  154. package/src/__tests__/skill-load-tool.test.ts +3 -67
  155. package/src/__tests__/skill-memory.test.ts +480 -195
  156. package/src/__tests__/skills-uninstall.test.ts +2 -2
  157. package/src/__tests__/skills.test.ts +23 -50
  158. package/src/__tests__/slack-channel-config.test.ts +2 -21
  159. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  160. package/src/__tests__/starter-bundle.test.ts +2 -8
  161. package/src/__tests__/stt-hints.test.ts +7 -2
  162. package/src/__tests__/system-prompt.test.ts +25 -45
  163. package/src/__tests__/task-compiler.test.ts +2 -27
  164. package/src/__tests__/task-management-tools.test.ts +2 -27
  165. package/src/__tests__/task-memory-cleanup.test.ts +173 -250
  166. package/src/__tests__/task-runner.test.ts +2 -27
  167. package/src/__tests__/task-scheduler.test.ts +2 -27
  168. package/src/__tests__/terminal-tools.test.ts +1 -17
  169. package/src/__tests__/test-preload.ts +3 -0
  170. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +0 -79
  171. package/src/__tests__/tool-approval-handler.test.ts +4 -27
  172. package/src/__tests__/tool-execution-abort-cleanup.test.ts +2 -11
  173. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -25
  174. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  175. package/src/__tests__/tool-executor.test.ts +0 -1
  176. package/src/__tests__/tool-grant-request-escalation.test.ts +4 -27
  177. package/src/__tests__/tool-preview-lifecycle.test.ts +0 -20
  178. package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
  179. package/src/__tests__/trust-store.test.ts +10 -42
  180. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -30
  181. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +3 -27
  182. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -28
  183. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -28
  184. package/src/__tests__/trusted-contact-verification.test.ts +2 -28
  185. package/src/__tests__/turn-boundary-resolution.test.ts +2 -34
  186. package/src/__tests__/twilio-provider.test.ts +0 -16
  187. package/src/__tests__/twilio-routes-twiml.test.ts +7 -12
  188. package/src/__tests__/twilio-routes.test.ts +0 -24
  189. package/src/__tests__/update-bulletin.test.ts +17 -89
  190. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -26
  191. package/src/__tests__/usage-routes.test.ts +2 -27
  192. package/src/__tests__/user-reference.test.ts +1 -5
  193. package/src/__tests__/vbundle-pax-and-symlink.test.ts +4 -34
  194. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -53
  195. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  196. package/src/__tests__/voice-invite-redemption.test.ts +2 -27
  197. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -30
  198. package/src/__tests__/voice-session-bridge.test.ts +2 -27
  199. package/src/__tests__/volume-security-guard.test.ts +2 -0
  200. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  201. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -29
  202. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +2 -2
  203. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +4 -29
  204. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  205. package/src/__tests__/workspace-migration-down-functions.test.ts +0 -6
  206. package/src/__tests__/workspace-policy.test.ts +1 -1
  207. package/src/acp/client-handler.ts +1 -2
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +1 -15
  211. package/src/bundler/app-compiler.ts +179 -2
  212. package/src/bundler/package-resolver.ts +3 -5
  213. package/src/cli/__tests__/notifications.test.ts +1 -24
  214. package/src/cli/cli-memory.ts +179 -0
  215. package/src/cli/commands/avatar.ts +3 -3
  216. package/src/cli/commands/config.ts +26 -13
  217. package/src/cli/commands/doctor.ts +2 -2
  218. package/src/cli/commands/memory.ts +41 -55
  219. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  220. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  221. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  222. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  223. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  224. package/src/cli/commands/oauth/connect.ts +26 -6
  225. package/src/cli/commands/oauth/mode.ts +7 -0
  226. package/src/cli/commands/oauth/providers.ts +49 -42
  227. package/src/cli/commands/oauth/shared.ts +39 -3
  228. package/src/cli/commands/platform/__tests__/connect.test.ts +3 -49
  229. package/src/cli/commands/platform/__tests__/disconnect.test.ts +3 -49
  230. package/src/cli/commands/platform/__tests__/status.test.ts +5 -55
  231. package/src/cli/commands/platform/index.ts +16 -16
  232. package/src/cli/commands/skills.ts +88 -16
  233. package/src/cli/commands/trust.ts +2 -2
  234. package/src/cli/lib/daemon-credential-client.ts +2 -3
  235. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  236. package/src/config/bundled-skills/computer-use/TOOLS.json +7 -7
  237. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  238. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  239. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  240. package/src/config/bundled-skills/gmail/SKILL.md +2 -10
  241. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  242. package/src/config/bundled-skills/messaging/SKILL.md +26 -19
  243. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  244. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  245. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  246. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  247. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  248. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  249. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  250. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  251. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  252. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  253. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  254. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  255. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  256. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  257. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  258. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  259. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  260. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  261. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  262. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  263. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  264. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  265. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  266. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  267. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  268. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  269. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  270. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  271. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  272. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  273. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  274. package/src/config/bundled-skills/slack/SKILL.md +1 -7
  275. package/src/config/bundled-tool-registry.ts +56 -4
  276. package/src/config/env-registry.ts +15 -8
  277. package/src/config/feature-flag-registry.json +29 -116
  278. package/src/config/loader.ts +4 -0
  279. package/src/config/schemas/platform.ts +8 -0
  280. package/src/config/schemas/security.ts +0 -6
  281. package/src/config/schemas/services.ts +8 -0
  282. package/src/config/schemas/timeouts.ts +1 -1
  283. package/src/config/skills.ts +18 -7
  284. package/src/context/token-estimator.ts +25 -18
  285. package/src/context/window-manager.ts +32 -9
  286. package/src/credential-execution/approval-bridge.ts +0 -1
  287. package/src/credential-execution/process-manager.ts +3 -1
  288. package/src/daemon/config-watcher.ts +51 -0
  289. package/src/daemon/context-overflow-reducer.ts +46 -2
  290. package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
  291. package/src/daemon/conversation-agent-loop.ts +99 -63
  292. package/src/daemon/conversation-error.ts +31 -8
  293. package/src/daemon/conversation-lifecycle.ts +33 -0
  294. package/src/daemon/conversation-media-retry.ts +85 -7
  295. package/src/daemon/conversation-notifiers.ts +4 -1
  296. package/src/daemon/conversation-process.ts +1 -0
  297. package/src/daemon/conversation-runtime-assembly.ts +5 -0
  298. package/src/daemon/conversation-usage.ts +1 -0
  299. package/src/daemon/conversation.ts +41 -2
  300. package/src/daemon/daemon-control.ts +8 -2
  301. package/src/daemon/handlers/shared.ts +22 -12
  302. package/src/daemon/handlers/skills.ts +423 -201
  303. package/src/daemon/lifecycle.ts +52 -4
  304. package/src/daemon/main.ts +5 -1
  305. package/src/daemon/message-types/conversations.ts +5 -1
  306. package/src/daemon/message-types/messages.ts +3 -1
  307. package/src/daemon/message-types/skills.ts +97 -36
  308. package/src/daemon/providers-setup.ts +7 -0
  309. package/src/daemon/server.ts +35 -22
  310. package/src/daemon/tool-side-effects.ts +27 -5
  311. package/src/events/domain-events.ts +1 -2
  312. package/src/heartbeat/heartbeat-service.ts +1 -0
  313. package/src/hooks/cli.ts +2 -2
  314. package/src/hooks/runner.ts +15 -38
  315. package/src/inbound/platform-callback-registration.ts +14 -14
  316. package/src/memory/admin.ts +11 -45
  317. package/src/memory/conversation-bootstrap.ts +2 -0
  318. package/src/memory/conversation-crud.ts +242 -348
  319. package/src/memory/conversation-group-migration.ts +157 -0
  320. package/src/memory/conversation-queries.ts +4 -2
  321. package/src/memory/db-init.ts +39 -3
  322. package/src/memory/embed.ts +73 -0
  323. package/src/memory/embedding-backend.ts +8 -14
  324. package/src/memory/embedding-runtime-manager.ts +12 -114
  325. package/src/memory/fingerprint.ts +2 -2
  326. package/src/memory/graph/bootstrap.ts +512 -0
  327. package/src/memory/graph/capability-seed.ts +297 -0
  328. package/src/memory/graph/consolidation.ts +691 -0
  329. package/src/memory/graph/conversation-graph-memory.ts +630 -0
  330. package/src/memory/graph/decay.test.ts +208 -0
  331. package/src/memory/graph/decay.ts +195 -0
  332. package/src/memory/graph/extraction-job.ts +69 -0
  333. package/src/memory/graph/extraction.test.ts +936 -0
  334. package/src/memory/graph/extraction.ts +1254 -0
  335. package/src/memory/graph/graph-search.ts +266 -0
  336. package/src/memory/graph/image-ref-utils.ts +29 -0
  337. package/src/memory/graph/injection.test.ts +513 -0
  338. package/src/memory/graph/injection.ts +439 -0
  339. package/src/memory/graph/inspect.ts +534 -0
  340. package/src/memory/graph/narrative.ts +267 -0
  341. package/src/memory/graph/pattern-scan.ts +269 -0
  342. package/src/memory/graph/retriever.ts +1008 -0
  343. package/src/memory/graph/scoring.test.ts +548 -0
  344. package/src/memory/graph/scoring.ts +232 -0
  345. package/src/memory/graph/serendipity.ts +65 -0
  346. package/src/memory/graph/store.test.ts +1050 -0
  347. package/src/memory/graph/store.ts +699 -0
  348. package/src/memory/graph/tool-handlers.ts +426 -0
  349. package/src/memory/graph/tools.ts +141 -0
  350. package/src/memory/graph/triggers.test.ts +487 -0
  351. package/src/memory/graph/triggers.ts +223 -0
  352. package/src/memory/graph/types.ts +271 -0
  353. package/src/memory/group-crud.ts +191 -0
  354. package/src/memory/indexer.ts +37 -19
  355. package/src/memory/job-handlers/cleanup.ts +0 -53
  356. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  357. package/src/memory/job-handlers/embedding.test.ts +3 -27
  358. package/src/memory/job-handlers/embedding.ts +5 -31
  359. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  360. package/src/memory/job-handlers/summarization.ts +32 -17
  361. package/src/memory/job-utils.ts +1 -1
  362. package/src/memory/jobs-store.ts +50 -70
  363. package/src/memory/jobs-worker.ts +147 -112
  364. package/src/memory/llm-usage-store.ts +35 -2
  365. package/src/memory/message-content.ts +1 -0
  366. package/src/memory/migrations/201-oauth-providers-feature-flag.ts +11 -0
  367. package/src/memory/migrations/202-drop-callback-transport-column.ts +13 -0
  368. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  369. package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
  370. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  371. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  372. package/src/memory/migrations/index.ts +6 -0
  373. package/src/memory/migrations/registry.ts +8 -0
  374. package/src/memory/qdrant-client.ts +44 -17
  375. package/src/memory/qdrant-manager.ts +26 -5
  376. package/src/memory/schema/index.ts +1 -0
  377. package/src/memory/schema/memory-graph.ts +139 -0
  378. package/src/memory/schema/oauth.ts +1 -1
  379. package/src/memory/search/semantic.ts +47 -91
  380. package/src/memory/slack-thread-store.ts +17 -0
  381. package/src/memory/task-memory-cleanup.ts +28 -50
  382. package/src/messaging/providers/outlook/adapter.ts +200 -0
  383. package/src/messaging/providers/outlook/client.ts +610 -0
  384. package/src/messaging/providers/outlook/types.ts +201 -0
  385. package/src/notifications/adapters/macos.ts +1 -0
  386. package/src/notifications/adapters/slack.ts +1 -1
  387. package/src/notifications/copy-composer.ts +9 -0
  388. package/src/notifications/signal.ts +16 -0
  389. package/src/oauth/__tests__/identity-verifier.test.ts +1 -1
  390. package/src/oauth/connect-orchestrator.ts +10 -3
  391. package/src/oauth/oauth-store.ts +10 -11
  392. package/src/oauth/provider-serializer.ts +3 -0
  393. package/src/oauth/provider-visibility.ts +16 -0
  394. package/src/oauth/seed-providers.ts +50 -17
  395. package/src/permissions/checker.ts +62 -9
  396. package/src/permissions/defaults.ts +4 -4
  397. package/src/permissions/types.ts +2 -4
  398. package/src/permissions/workspace-policy.ts +1 -1
  399. package/src/playbooks/playbook-compiler.ts +19 -18
  400. package/src/playbooks/types.ts +4 -3
  401. package/src/prompts/system-prompt.ts +6 -93
  402. package/src/prompts/templates/UPDATES.md +6 -0
  403. package/src/providers/anthropic/client.ts +47 -19
  404. package/src/providers/gemini/client.ts +1 -1
  405. package/src/providers/openai/client.ts +1 -1
  406. package/src/providers/registry.ts +1 -1
  407. package/src/providers/retry.ts +19 -3
  408. package/src/runtime/actor-trust-resolver.ts +5 -1
  409. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -27
  410. package/src/runtime/auth/__tests__/token-service.test.ts +1 -25
  411. package/src/runtime/auth/route-policy.ts +7 -4
  412. package/src/runtime/guardian-reply-router.ts +10 -2
  413. package/src/runtime/http-server.ts +23 -3
  414. package/src/runtime/middleware/auth.ts +20 -0
  415. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  416. package/src/runtime/routes/attachment-routes.ts +106 -16
  417. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  418. package/src/runtime/routes/btw-routes.ts +8 -0
  419. package/src/runtime/routes/conversation-management-routes.ts +2 -0
  420. package/src/runtime/routes/conversation-query-routes.ts +2 -58
  421. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  422. package/src/runtime/routes/debug-routes.ts +1 -1
  423. package/src/runtime/routes/global-search-routes.ts +21 -19
  424. package/src/runtime/routes/group-routes.ts +207 -0
  425. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  426. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  427. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  428. package/src/runtime/routes/inbound-stages/background-dispatch.ts +43 -2
  429. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  430. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  431. package/src/runtime/routes/memory-item-routes.test.ts +2 -31
  432. package/src/runtime/routes/memory-item-routes.ts +385 -341
  433. package/src/runtime/routes/oauth-apps.ts +18 -1
  434. package/src/runtime/routes/oauth-providers.ts +13 -1
  435. package/src/runtime/routes/schedule-routes.ts +2 -0
  436. package/src/runtime/routes/settings-routes.ts +1 -0
  437. package/src/runtime/routes/skills-routes.ts +103 -37
  438. package/src/runtime/routes/usage-routes.ts +19 -2
  439. package/src/runtime/routes/work-items-routes.test.ts +2 -27
  440. package/src/runtime/routes/workspace-routes.test.ts +3 -27
  441. package/src/schedule/scheduler.ts +8 -1
  442. package/src/security/oauth2.ts +1 -1
  443. package/src/security/secret-allowlist.ts +4 -4
  444. package/src/security/secure-keys.ts +4 -8
  445. package/src/shared/provider-env-vars.ts +19 -0
  446. package/src/skills/catalog-cache.ts +5 -0
  447. package/src/skills/catalog-install.ts +15 -14
  448. package/src/skills/clawhub.ts +134 -154
  449. package/src/skills/install-meta.ts +208 -0
  450. package/src/skills/managed-store.ts +27 -16
  451. package/src/skills/skill-memory.ts +210 -96
  452. package/src/skills/skillssh-registry.ts +19 -17
  453. package/src/tasks/task-runner.ts +3 -1
  454. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  455. package/src/tools/browser/runtime-check.ts +3 -1
  456. package/src/tools/memory/register.ts +63 -46
  457. package/src/tools/permission-checker.ts +7 -19
  458. package/src/tools/shared/filesystem/image-read.ts +22 -85
  459. package/src/tools/skills/skill-script-runner.ts +1 -1
  460. package/src/tools/terminal/safe-env.ts +1 -0
  461. package/src/tools/tool-manifest.ts +3 -3
  462. package/src/util/browser.ts +25 -10
  463. package/src/util/bun-runtime.ts +172 -0
  464. package/src/util/device-id.ts +3 -65
  465. package/src/watcher/providers/outlook-calendar.ts +343 -0
  466. package/src/watcher/providers/outlook.ts +198 -0
  467. package/src/workspace/git-service.ts +27 -6
  468. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  469. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  470. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  471. package/src/workspace/migrations/registry.ts +6 -0
  472. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  473. package/src/__tests__/journal-context.test.ts +0 -268
  474. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  475. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  476. package/src/__tests__/memory-query-builder.test.ts +0 -59
  477. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  478. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  479. package/src/__tests__/memory-regressions.test.ts +0 -3696
  480. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  481. package/src/daemon/conversation-memory.ts +0 -207
  482. package/src/memory/conversation-starters-cadence.ts +0 -74
  483. package/src/memory/items-extractor.ts +0 -860
  484. package/src/memory/job-handlers/batch-extraction.ts +0 -741
  485. package/src/memory/job-handlers/extraction.ts +0 -40
  486. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -383
  487. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  488. package/src/memory/journal-memory.ts +0 -224
  489. package/src/memory/query-builder.ts +0 -47
  490. package/src/memory/query-expansion.ts +0 -83
  491. package/src/memory/retriever.test.ts +0 -1590
  492. package/src/memory/retriever.ts +0 -1323
  493. package/src/memory/search/formatting.test.ts +0 -140
  494. package/src/memory/search/formatting.ts +0 -262
  495. package/src/memory/search/mmr.ts +0 -136
  496. package/src/memory/search/ranking.ts +0 -15
  497. package/src/memory/search/staleness.ts +0 -40
  498. package/src/memory/search/tier-classifier.ts +0 -18
  499. package/src/memory/search/types.ts +0 -121
  500. package/src/prompts/journal-context.ts +0 -156
  501. package/src/tools/memory/definitions.ts +0 -69
  502. package/src/tools/memory/handlers.test.ts +0 -590
  503. package/src/tools/memory/handlers.ts +0 -434
@@ -253,7 +253,10 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
253
253
  );
254
254
  }
255
255
 
256
- let body: { scopes?: string[] } = {};
256
+ let body: {
257
+ scopes?: string[];
258
+ callback_transport?: "loopback" | "gateway";
259
+ } = {};
257
260
  try {
258
261
  const text = await req.text();
259
262
  if (text) {
@@ -263,6 +266,19 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
263
266
  // No body or invalid JSON — use defaults
264
267
  }
265
268
 
269
+ // Validate callback_transport if present
270
+ if (
271
+ body.callback_transport !== undefined &&
272
+ body.callback_transport !== "loopback" &&
273
+ body.callback_transport !== "gateway"
274
+ ) {
275
+ return httpError(
276
+ "BAD_REQUEST",
277
+ 'callback_transport must be "loopback" or "gateway"',
278
+ 400,
279
+ );
280
+ }
281
+
266
282
  const clientSecret = await getAppClientSecret(app);
267
283
 
268
284
  const result = await orchestrateOAuthConnect({
@@ -270,6 +286,7 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
270
286
  clientId: app.clientId,
271
287
  clientSecret,
272
288
  requestedScopes: body.scopes,
289
+ callbackTransport: body.callback_transport ?? "loopback",
273
290
  isInteractive: false,
274
291
  });
275
292
 
@@ -6,8 +6,10 @@
6
6
  * runtime auth middleware.
7
7
  */
8
8
 
9
+ import { loadConfig } from "../../config/loader.js";
9
10
  import { getProvider, listProviders } from "../../oauth/oauth-store.js";
10
11
  import { serializeProviderSummary } from "../../oauth/provider-serializer.js";
12
+ import { isProviderVisible } from "../../oauth/provider-visibility.js";
11
13
  import { httpError } from "../http-errors.js";
12
14
  import type { RouteDefinition } from "../http-router.js";
13
15
 
@@ -22,7 +24,9 @@ export function oauthProvidersRouteDefinitions(): RouteDefinition[] {
22
24
  method: "GET",
23
25
  handler: ({ url }) => {
24
26
  const rows = listProviders();
25
- let serialized = rows
27
+ const config = loadConfig();
28
+ const visibleRows = rows.filter((r) => isProviderVisible(r, config));
29
+ let serialized = visibleRows
26
30
  .map((row) => serializeProviderSummary(row))
27
31
  .filter((s): s is NonNullable<typeof s> => s !== null);
28
32
 
@@ -53,6 +57,14 @@ export function oauthProvidersRouteDefinitions(): RouteDefinition[] {
53
57
  );
54
58
  }
55
59
 
60
+ if (!isProviderVisible(row, loadConfig())) {
61
+ return httpError(
62
+ "NOT_FOUND",
63
+ `No OAuth provider registered for "${params.providerKey}"`,
64
+ 404,
65
+ );
66
+ }
67
+
56
68
  return Response.json({ provider: serializeProviderSummary(row) });
57
69
  },
58
70
  },
@@ -229,6 +229,7 @@ async function handleRunScheduleNow(
229
229
  );
230
230
  const fallbackConversation = bootstrapConversation({
231
231
  source: "schedule",
232
+ groupId: "system:scheduled",
232
233
  origin: "schedule",
233
234
  systemHint: `Schedule (manual): ${schedule.name}`,
234
235
  });
@@ -241,6 +242,7 @@ async function handleRunScheduleNow(
241
242
  // Regular message-based schedule
242
243
  const conversation = bootstrapConversation({
243
244
  source: "schedule",
245
+ groupId: "system:scheduled",
244
246
  origin: "schedule",
245
247
  systemHint: `Schedule (manual): ${schedule.name}`,
246
248
  });
@@ -223,6 +223,7 @@ async function handleOAuthConnectStart(body: {
223
223
  requestedScopes: body.requestedScopes,
224
224
  clientId,
225
225
  clientSecret,
226
+ callbackTransport: "loopback",
226
227
  isInteractive: true,
227
228
  openUrl: (url: string) => {
228
229
  authUrl = url;
@@ -39,6 +39,85 @@ export interface SkillRouteDeps {
39
39
  getSkillContext: () => SkillOperationContext;
40
40
  }
41
41
 
42
+ const slimSkillBase = {
43
+ id: z.string(),
44
+ name: z.string(),
45
+ description: z.string(),
46
+ emoji: z.string().optional(),
47
+ kind: z.enum(["bundled", "installed", "catalog"]),
48
+ status: z.enum(["enabled", "disabled", "available"]),
49
+ };
50
+
51
+ const slimSkillSchema = z.discriminatedUnion("origin", [
52
+ z.object({ ...slimSkillBase, origin: z.literal("vellum") }),
53
+ z.object({
54
+ ...slimSkillBase,
55
+ origin: z.literal("clawhub"),
56
+ slug: z.string(),
57
+ author: z.string(),
58
+ stars: z.number(),
59
+ installs: z.number(),
60
+ reports: z.number(),
61
+ publishedAt: z.string().optional(),
62
+ }),
63
+ z.object({
64
+ ...slimSkillBase,
65
+ origin: z.literal("skillssh"),
66
+ slug: z.string(),
67
+ sourceRepo: z.string(),
68
+ installs: z.number(),
69
+ }),
70
+ z.object({ ...slimSkillBase, origin: z.literal("custom") }),
71
+ ]);
72
+
73
+ const skillDetailSchema = z.discriminatedUnion("origin", [
74
+ z.object({ ...slimSkillBase, origin: z.literal("vellum") }),
75
+ z.object({
76
+ ...slimSkillBase,
77
+ origin: z.literal("clawhub"),
78
+ slug: z.string(),
79
+ author: z.string(),
80
+ stars: z.number(),
81
+ installs: z.number(),
82
+ reports: z.number(),
83
+ publishedAt: z.string().optional(),
84
+ owner: z
85
+ .object({
86
+ handle: z.string(),
87
+ displayName: z.string(),
88
+ image: z.string().optional(),
89
+ })
90
+ .nullable()
91
+ .optional(),
92
+ stats: z
93
+ .object({
94
+ stars: z.number(),
95
+ installs: z.number(),
96
+ downloads: z.number(),
97
+ versions: z.number(),
98
+ })
99
+ .nullable()
100
+ .optional(),
101
+ latestVersion: z
102
+ .object({
103
+ version: z.string(),
104
+ changelog: z.string().optional(),
105
+ })
106
+ .nullable()
107
+ .optional(),
108
+ createdAt: z.number().nullable().optional(),
109
+ updatedAt: z.number().nullable().optional(),
110
+ }),
111
+ z.object({
112
+ ...slimSkillBase,
113
+ origin: z.literal("skillssh"),
114
+ slug: z.string(),
115
+ sourceRepo: z.string(),
116
+ installs: z.number(),
117
+ }),
118
+ z.object({ ...slimSkillBase, origin: z.literal("custom") }),
119
+ ]);
120
+
42
121
  export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
43
122
  const ctx = () => deps.getSkillContext();
44
123
 
@@ -60,34 +139,7 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
60
139
  },
61
140
  ],
62
141
  responseBody: z.object({
63
- skills: z
64
- .array(
65
- z.object({
66
- id: z.string(),
67
- name: z.string(),
68
- description: z.string(),
69
- emoji: z.string().optional(),
70
- homepage: z.string().optional(),
71
- source: z.enum([
72
- "bundled",
73
- "managed",
74
- "workspace",
75
- "clawhub",
76
- "extra",
77
- "catalog",
78
- ]),
79
- state: z.enum(["enabled", "disabled"]),
80
- installStatus: z.enum(["bundled", "installed", "available"]),
81
- updateAvailable: z.boolean(),
82
- provenance: z.object({
83
- kind: z.enum(["first-party", "third-party", "local"]),
84
- provider: z.string().optional(),
85
- originId: z.string().optional(),
86
- sourceUrl: z.string().optional(),
87
- }),
88
- }),
89
- )
90
- .describe("Skill objects"),
142
+ skills: z.array(slimSkillSchema).describe("Skill objects"),
91
143
  }),
92
144
  handler: async ({ url }) => {
93
145
  const include = url.searchParams.get("include");
@@ -201,16 +253,23 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
201
253
  url: z.string().describe("Skill URL"),
202
254
  spec: z.string().describe("Skill spec"),
203
255
  version: z.string(),
256
+ origin: z
257
+ .enum(["clawhub", "skillssh"])
258
+ .optional()
259
+ .describe(
260
+ "Which registry to install from. When omitted, the install flow auto-detects based on slug format.",
261
+ ),
204
262
  }),
205
263
  responseBody: z.object({
206
264
  ok: z.boolean(),
207
265
  }),
208
- handler: async ({ req }) => {
266
+ handler: async ({ req, authContext }) => {
209
267
  const body = (await req.json()) as {
210
268
  slug?: string;
211
269
  url?: string;
212
270
  spec?: string;
213
271
  version?: string;
272
+ origin?: "clawhub" | "skillssh";
214
273
  };
215
274
  const slug = body.slug ?? body.url ?? body.spec;
216
275
  if (!slug || typeof slug !== "string") {
@@ -220,8 +279,9 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
220
279
  400,
221
280
  );
222
281
  }
282
+ const contactId = authContext.actorPrincipalId ?? undefined;
223
283
  const result = await installSkill(
224
- { slug, version: body.version },
284
+ { slug, version: body.version, origin: body.origin, contactId },
225
285
  ctx(),
226
286
  );
227
287
  if (!result.success) {
@@ -265,7 +325,9 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
265
325
  },
266
326
  ],
267
327
  responseBody: z.object({
268
- data: z.object({}).passthrough().describe("Search results"),
328
+ skills: z
329
+ .array(slimSkillSchema)
330
+ .describe("Skill objects matching the search query"),
269
331
  }),
270
332
  handler: async ({ url }) => {
271
333
  const query = url.searchParams.get("q") ?? "";
@@ -276,7 +338,7 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
276
338
  if (!result.success) {
277
339
  return httpError("INTERNAL_ERROR", result.error, 500);
278
340
  }
279
- return Response.json({ data: result.data });
341
+ return Response.json({ skills: result.skills });
280
342
  },
281
343
  },
282
344
 
@@ -323,7 +385,7 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
323
385
  responseBody: z.object({
324
386
  ok: z.boolean(),
325
387
  }),
326
- handler: async ({ req }) => {
388
+ handler: async ({ req, authContext }) => {
327
389
  const body = (await req.json()) as CreateSkillParams;
328
390
  if (
329
391
  !body.skillId ||
@@ -337,7 +399,8 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
337
399
  400,
338
400
  );
339
401
  }
340
- const result = await createSkill(body, ctx());
402
+ const contactId = authContext.actorPrincipalId ?? undefined;
403
+ const result = await createSkill({ ...body, contactId }, ctx());
341
404
  if (!result.success) {
342
405
  return httpError("INTERNAL_ERROR", result.error, 500);
343
406
  }
@@ -395,10 +458,13 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
395
458
  method: "GET",
396
459
  policyKey: "skills",
397
460
  summary: "Get skill",
398
- description: "Return a single skill by ID.",
461
+ description: "Return a single skill by ID with enriched detail fields.",
399
462
  tags: ["skills"],
400
- handler: ({ params }) => {
401
- const result = getSkill(params.id, ctx());
463
+ responseBody: z.object({
464
+ skill: skillDetailSchema.describe("Skill detail object"),
465
+ }),
466
+ handler: async ({ params }) => {
467
+ const result = await getSkill(params.id, ctx());
402
468
  if ("error" in result) {
403
469
  if (result.status === 404) {
404
470
  return httpError("NOT_FOUND", result.error, 404);
@@ -11,6 +11,7 @@ import { z } from "zod";
11
11
  import {
12
12
  getUsageDayBuckets,
13
13
  getUsageGroupBreakdown,
14
+ getUsageHourBuckets,
14
15
  getUsageTotals,
15
16
  } from "../../memory/llm-usage-store.js";
16
17
  import { httpError } from "../http-errors.js";
@@ -104,14 +105,30 @@ export function usageRouteDefinitions(): RouteDefinition[] {
104
105
  schema: { type: "integer" },
105
106
  description: "End epoch millis (required)",
106
107
  },
108
+ {
109
+ name: "granularity",
110
+ schema: { type: "string", enum: ["daily", "hourly"] },
111
+ description: 'Bucket granularity: "daily" (default) or "hourly"',
112
+ },
107
113
  ],
108
114
  responseBody: z.object({
109
- buckets: z.array(z.unknown()).describe("Daily usage bucket objects"),
115
+ buckets: z.array(z.unknown()).describe("Usage bucket objects"),
110
116
  }),
111
117
  handler: ({ url }) => {
112
118
  const range = parseTimeRange(url);
113
119
  if (range instanceof Response) return range;
114
- const buckets = getUsageDayBuckets(range);
120
+ const granularity = url.searchParams.get("granularity") ?? "daily";
121
+ if (granularity !== "daily" && granularity !== "hourly") {
122
+ return httpError(
123
+ "BAD_REQUEST",
124
+ `Invalid "granularity" value: "${granularity}". Must be one of: daily, hourly`,
125
+ 400,
126
+ );
127
+ }
128
+ const buckets =
129
+ granularity === "hourly"
130
+ ? getUsageHourBuckets(range)
131
+ : getUsageDayBuckets(range);
115
132
  return Response.json({ buckets });
116
133
  },
117
134
  },
@@ -5,23 +5,7 @@
5
5
  * item falls back to the task-level required tools instead of silently
6
6
  * skipping permission checks.
7
7
  */
8
- import { mkdtempSync, rmSync } from "node:fs";
9
- import { tmpdir } from "node:os";
10
- import { join } from "node:path";
11
- import { afterAll, describe, expect, mock, test } from "bun:test";
12
-
13
- const testDir = mkdtempSync(join(tmpdir(), "work-items-routes-test-"));
14
-
15
- mock.module("../../util/platform.js", () => ({
16
- getDataDir: () => testDir,
17
- isMacOS: () => process.platform === "darwin",
18
- isLinux: () => process.platform === "linux",
19
- isWindows: () => process.platform === "win32",
20
- getPidPath: () => join(testDir, "test.pid"),
21
- getDbPath: () => join(testDir, "test.db"),
22
- getLogPath: () => join(testDir, "test.log"),
23
- ensureDataDir: () => {},
24
- }));
8
+ import { describe, expect, mock, test } from "bun:test";
25
9
 
26
10
  mock.module("../../util/logger.js", () => ({
27
11
  getLogger: () =>
@@ -35,7 +19,7 @@ mock.module("../../permissions/checker.js", () => ({
35
19
  classifyRisk: async () => "high",
36
20
  }));
37
21
 
38
- import { initializeDb, resetDb } from "../../memory/db.js";
22
+ import { initializeDb } from "../../memory/db.js";
39
23
  import { createTask } from "../../tasks/task-store.js";
40
24
  import { createWorkItem } from "../../work-items/work-item-store.js";
41
25
  import type { RouteContext } from "../http-router.js";
@@ -46,15 +30,6 @@ import {
46
30
 
47
31
  initializeDb();
48
32
 
49
- afterAll(() => {
50
- resetDb();
51
- try {
52
- rmSync(testDir, { recursive: true });
53
- } catch {
54
- /* best effort */
55
- }
56
- });
57
-
58
33
  describe("empty required_tools snapshot bypass", () => {
59
34
  test("falls back to task required tools when snapshot requiredTools is empty", async () => {
60
35
  const task = createTask({
@@ -4,37 +4,17 @@
4
4
  * Covers path resolution (traversal prevention), MIME type detection,
5
5
  * directory listing, file metadata, and raw content serving with range support.
6
6
  */
7
- import {
8
- existsSync,
9
- mkdirSync,
10
- mkdtempSync,
11
- readFileSync,
12
- realpathSync,
13
- rmSync,
14
- writeFileSync,
15
- } from "node:fs";
16
- import { tmpdir } from "node:os";
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17
8
  import { join } from "node:path";
18
- import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test";
9
+ import { beforeAll, describe, expect, test } from "bun:test";
19
10
 
20
11
  // ---------------------------------------------------------------------------
21
12
  // Create a temp workspace directory for isolation
22
13
  // ---------------------------------------------------------------------------
23
14
 
24
- const testWorkspaceDir = realpathSync(
25
- mkdtempSync(join(tmpdir(), "workspace-routes-test-")),
26
- );
15
+ const testWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR!;
27
16
 
28
17
  // Mock platform module so getWorkspaceDir returns our temp dir
29
- mock.module("../../util/platform.js", () => ({
30
- getWorkspaceDir: () => testWorkspaceDir,
31
- getRootDir: () => testWorkspaceDir,
32
- getDataDir: () => testWorkspaceDir,
33
- isMacOS: () => process.platform === "darwin",
34
- isLinux: () => process.platform === "linux",
35
- isWindows: () => process.platform === "win32",
36
- }));
37
-
38
18
  import { workspaceRouteDefinitions } from "./workspace-routes.js";
39
19
  import { isTextMimeType, resolveWorkspacePath } from "./workspace-utils.js";
40
20
 
@@ -65,10 +45,6 @@ beforeAll(() => {
65
45
  writeFileSync(binaryFile, pngSignature);
66
46
  });
67
47
 
68
- afterAll(() => {
69
- rmSync(testWorkspaceDir, { recursive: true, force: true });
70
- });
71
-
72
48
  // ---------------------------------------------------------------------------
73
49
  // Helpers
74
50
  // ---------------------------------------------------------------------------
@@ -188,7 +188,12 @@ async function runScheduleOnce(
188
188
  );
189
189
  const { runTask } = await import("../tasks/task-runner.js");
190
190
  const result = await runTask(
191
- { taskId, workingDir: process.cwd(), source: "schedule", scheduleJobId: job.id },
191
+ {
192
+ taskId,
193
+ workingDir: process.cwd(),
194
+ source: "schedule",
195
+ scheduleJobId: job.id,
196
+ },
192
197
  processMessage as (
193
198
  conversationId: string,
194
199
  message: string,
@@ -237,6 +242,7 @@ async function runScheduleOnce(
237
242
  const fallbackConversation = bootstrapConversation({
238
243
  source: "schedule",
239
244
  scheduleJobId: job.id,
245
+ groupId: "system:scheduled",
240
246
  origin: "schedule",
241
247
  systemHint: `Schedule: ${job.name}`,
242
248
  });
@@ -255,6 +261,7 @@ async function runScheduleOnce(
255
261
  const conversation = bootstrapConversation({
256
262
  source: "schedule",
257
263
  scheduleJobId: job.id,
264
+ groupId: "system:scheduled",
258
265
  origin: "schedule",
259
266
  systemHint: isOneShot ? `Reminder: ${job.name}` : `Schedule: ${job.name}`,
260
267
  });
@@ -61,7 +61,7 @@ export interface OAuth2TokenResult {
61
61
 
62
62
  export interface OAuth2FlowCallbacks {
63
63
  /** Open a URL in the user's browser (e.g. via `open_url` message). */
64
- openUrl: (url: string) => void;
64
+ openUrl: (url: string) => void | Promise<void>;
65
65
  }
66
66
 
67
67
  export interface OAuth2FlowOptions {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * User-defined allowlist for suppressing secret scanner false positives.
3
3
  *
4
- * Reads `~/.vellum/secret-allowlist.json` (if present) and provides
4
+ * Reads `~/.vellum/workspace/data/secret-allowlist.json` (if present) and provides
5
5
  * `isAllowlisted(value)` to check whether a matched value should be
6
6
  * suppressed.
7
7
  *
@@ -18,7 +18,7 @@ import { join } from "node:path";
18
18
 
19
19
  import { pathExists } from "../util/fs.js";
20
20
  import { getLogger } from "../util/logger.js";
21
- import { getProtectedDir } from "../util/platform.js";
21
+ import { getDataDir } from "../util/platform.js";
22
22
 
23
23
  const log = getLogger("secret-allowlist");
24
24
 
@@ -45,7 +45,7 @@ let allowedRegexes: RegExp[] = [];
45
45
  export function loadAllowlist(): void {
46
46
  if (loaded || fileChecked) return;
47
47
 
48
- const filePath = join(getProtectedDir(), "secret-allowlist.json");
48
+ const filePath = join(getDataDir(), "secret-allowlist.json");
49
49
  if (!pathExists(filePath)) {
50
50
  fileChecked = true;
51
51
  return;
@@ -169,7 +169,7 @@ function validateAllowlist(
169
169
  * Returns validation errors, or null if the file doesn't exist.
170
170
  */
171
171
  export function validateAllowlistFile(): AllowlistValidationError[] | null {
172
- const filePath = join(getProtectedDir(), "secret-allowlist.json");
172
+ const filePath = join(getDataDir(), "secret-allowlist.json");
173
173
  if (!pathExists(filePath)) return null;
174
174
 
175
175
  const raw = readFileSync(filePath, "utf-8");
@@ -22,9 +22,9 @@ import type {
22
22
  SecureKeyDeleteResult,
23
23
  } from "@vellumai/credential-storage";
24
24
 
25
- import providerEnvVarsRegistry from "../../../meta/provider-env-vars.json" with { type: "json" };
26
25
  import { getIsContainerized } from "../config/env-registry.js";
27
26
  import type { CesClient } from "../credential-execution/client.js";
27
+ import { PROVIDER_ENV_VAR_NAMES } from "../shared/provider-env-vars.js";
28
28
  import { getLogger } from "../util/logger.js";
29
29
  import { createCesCredentialBackend } from "./ces-credential-client.js";
30
30
  import { CesRpcCredentialBackend } from "./ces-rpc-credential-backend.js";
@@ -395,14 +395,10 @@ export async function deleteSecureKeyAsync(
395
395
  // ---------------------------------------------------------------------------
396
396
 
397
397
  /**
398
- * Env var names keyed by provider. Loaded from the shared registry at
399
- * `meta/provider-env-vars.json`the single source of truth also consumed
400
- * by the CLI and the macOS client.
401
- * Ollama is intentionally omitted from the registry — it doesn't require
402
- * an API key.
398
+ * Env var names keyed by provider.
399
+ * Ollama is intentionally omitted it doesn't require an API key.
403
400
  */
404
- const PROVIDER_ENV_VARS: Record<string, string> =
405
- providerEnvVarsRegistry.providers;
401
+ const PROVIDER_ENV_VARS: Record<string, string> = PROVIDER_ENV_VAR_NAMES;
406
402
 
407
403
  /**
408
404
  * Retrieve a provider API key, checking secure storage first and falling
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Provider API key environment variable names, keyed by provider ID.
3
+ *
4
+ * Keep in sync with:
5
+ * - cli/src/shared/provider-env-vars.ts
6
+ * - meta/provider-env-vars.json (consumed by the macOS client build)
7
+ *
8
+ * Once a consolidated shared package exists in packages/, all three
9
+ * copies can be replaced by a single import.
10
+ */
11
+ export const PROVIDER_ENV_VAR_NAMES: Record<string, string> = {
12
+ anthropic: "ANTHROPIC_API_KEY",
13
+ openai: "OPENAI_API_KEY",
14
+ gemini: "GEMINI_API_KEY",
15
+ fireworks: "FIREWORKS_API_KEY",
16
+ openrouter: "OPENROUTER_API_KEY",
17
+ brave: "BRAVE_API_KEY",
18
+ perplexity: "PERPLEXITY_API_KEY",
19
+ };
@@ -37,6 +37,11 @@ export async function getCatalog(): Promise<CatalogSkill[]> {
37
37
  return catalog;
38
38
  }
39
39
 
40
+ /** Return the cached catalog synchronously, or [] if no cache exists yet. */
41
+ export function getCachedCatalogSync(): CatalogSkill[] {
42
+ return cachedCatalog ?? [];
43
+ }
44
+
40
45
  /** Invalidate the cache (for testing or forced refresh). */
41
46
  export function invalidateCatalogCache(): void {
42
47
  cachedCatalog = null;
@@ -16,6 +16,7 @@ import { gunzipSync } from "node:zlib";
16
16
  import { getPlatformBaseUrl } from "../config/env.js";
17
17
  import { getLogger } from "../util/logger.js";
18
18
  import { getWorkspaceSkillsDir, readPlatformToken } from "../util/platform.js";
19
+ import { computeSkillHash, writeInstallMeta } from "./install-meta.js";
19
20
  import { deleteSkillCapabilityMemory } from "./skill-memory.js";
20
21
 
21
22
  const log = getLogger("catalog-install");
@@ -270,6 +271,7 @@ export async function installSkillLocally(
270
271
  skillId: string,
271
272
  catalogEntry: CatalogSkill,
272
273
  overwrite: boolean,
274
+ contactId?: string,
273
275
  ): Promise<void> {
274
276
  const skillDir = join(getWorkspaceSkillsDir(), skillId);
275
277
  const skillFilePath = join(skillDir, "SKILL.md");
@@ -294,19 +296,18 @@ export async function installSkillLocally(
294
296
  await fetchAndExtractSkill(skillId, skillDir);
295
297
  }
296
298
 
297
- // Write version metadata
298
- if (catalogEntry.version) {
299
- const meta = {
300
- version: catalogEntry.version,
301
- installedAt: new Date().toISOString(),
302
- };
303
- atomicWriteFile(
304
- join(skillDir, "version.json"),
305
- JSON.stringify(meta, null, 2) + "\n",
306
- );
307
- }
299
+ // Write install metadata
300
+ writeInstallMeta(skillDir, {
301
+ origin: "vellum",
302
+ installedAt: new Date().toISOString(),
303
+ ...(catalogEntry.version ? { version: catalogEntry.version } : {}),
304
+ ...(contactId ? { installedBy: contactId } : {}),
305
+ contentHash: computeSkillHash(skillDir) ?? undefined,
306
+ });
308
307
 
309
- // Install npm dependencies if the skill has a package.json
308
+ // Post-install: install dependencies first, then index the skill.
309
+ // Running bun install before upsertSkillsIndex ensures we don't index a
310
+ // skill whose dependencies failed to install.
310
311
  if (existsSync(join(skillDir, "package.json"))) {
311
312
  const bunPath = `${homedir()}/.bun/bin`;
312
313
  execSync("bun install", {
@@ -315,8 +316,6 @@ export async function installSkillLocally(
315
316
  env: { ...process.env, PATH: `${bunPath}:${process.env.PATH}` },
316
317
  });
317
318
  }
318
-
319
- // Register in SKILLS.md only after all steps succeed
320
319
  upsertSkillsIndex(skillId);
321
320
  }
322
321
 
@@ -393,6 +392,8 @@ export async function autoInstallFromCatalog(
393
392
  return false;
394
393
  }
395
394
 
395
+ // installSkillLocally handles dependency installation and SKILLS.md indexing.
396
396
  await installSkillLocally(skillId, entry, false);
397
+
397
398
  return true;
398
399
  }