@vellumai/assistant 0.5.0 → 0.5.2

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 (347) hide show
  1. package/ARCHITECTURE.md +54 -54
  2. package/docs/architecture/integrations.md +62 -67
  3. package/docs/credential-execution-service.md +3 -3
  4. package/package.json +1 -1
  5. package/src/__tests__/agent-loop.test.ts +111 -0
  6. package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
  7. package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
  8. package/src/__tests__/app-dir-path-guard.test.ts +78 -0
  9. package/src/__tests__/app-executors.test.ts +1 -291
  10. package/src/__tests__/app-git-history.test.ts +4 -4
  11. package/src/__tests__/app-routes-csp.test.ts +1 -0
  12. package/src/__tests__/app-store-dir-names.test.ts +426 -0
  13. package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -9
  14. package/src/__tests__/attachments-store.test.ts +169 -21
  15. package/src/__tests__/attachments.test.ts +115 -1
  16. package/src/__tests__/btw-routes.test.ts +1 -0
  17. package/src/__tests__/canonical-guardian-store.test.ts +38 -0
  18. package/src/__tests__/channel-reply-delivery.test.ts +55 -0
  19. package/src/__tests__/checker.test.ts +54 -0
  20. package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
  21. package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
  22. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  23. package/src/__tests__/config-schema-cmd.test.ts +68 -21
  24. package/src/__tests__/config-schema.test.ts +1 -1
  25. package/src/__tests__/conversation-agent-loop-overflow.test.ts +149 -5
  26. package/src/__tests__/conversation-agent-loop.test.ts +290 -2
  27. package/src/__tests__/conversation-attachments.test.ts +17 -19
  28. package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
  29. package/src/__tests__/conversation-disk-view.test.ts +810 -0
  30. package/src/__tests__/conversation-error.test.ts +1 -1
  31. package/src/__tests__/conversation-fork-crud.test.ts +551 -0
  32. package/src/__tests__/conversation-fork-route.test.ts +386 -0
  33. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  34. package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
  35. package/src/__tests__/conversation-media-retry.test.ts +8 -2
  36. package/src/__tests__/conversation-queue.test.ts +36 -1
  37. package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
  38. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  39. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
  41. package/src/__tests__/conversation-skill-tools.test.ts +4 -9
  42. package/src/__tests__/conversation-slash-commands.test.ts +149 -0
  43. package/src/__tests__/conversation-store.test.ts +24 -21
  44. package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
  45. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
  46. package/src/__tests__/conversation-title-service.test.ts +137 -0
  47. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
  48. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
  49. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
  50. package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
  51. package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
  52. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  53. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  54. package/src/__tests__/credential-vault-unit.test.ts +5 -10
  55. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  56. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
  57. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
  58. package/src/__tests__/diagnostics-export.test.ts +70 -1
  59. package/src/__tests__/filesystem-tools.test.ts +4 -2
  60. package/src/__tests__/first-greeting.test.ts +80 -0
  61. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  62. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
  63. package/src/__tests__/history-repair.test.ts +103 -10
  64. package/src/__tests__/http-conversation-lineage.test.ts +251 -0
  65. package/src/__tests__/image-source-path-reinject.test.ts +136 -0
  66. package/src/__tests__/llm-context-normalization.test.ts +1116 -0
  67. package/src/__tests__/llm-context-route-provider.test.ts +217 -0
  68. package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
  69. package/src/__tests__/media-generate-image.test.ts +47 -94
  70. package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
  71. package/src/__tests__/memory-recall-quality.test.ts +5 -5
  72. package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
  73. package/src/__tests__/migration-export-http.test.ts +3 -1
  74. package/src/__tests__/migration-import-commit-http.test.ts +18 -4
  75. package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
  76. package/src/__tests__/mime-builder.test.ts +3 -2
  77. package/src/__tests__/non-member-access-request.test.ts +12 -1
  78. package/src/__tests__/notification-decision-identity.test.ts +52 -0
  79. package/src/__tests__/oauth-apps-routes.test.ts +103 -0
  80. package/src/__tests__/oauth-store.test.ts +115 -0
  81. package/src/__tests__/provider-error-scenarios.test.ts +1 -3
  82. package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
  83. package/src/__tests__/recording-handler.test.ts +17 -0
  84. package/src/__tests__/registry.test.ts +3 -8
  85. package/src/__tests__/relay-server.test.ts +1 -1
  86. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
  87. package/src/__tests__/schema-transforms.test.ts +165 -5
  88. package/src/__tests__/server-history-render.test.ts +2 -2
  89. package/src/__tests__/skill-feature-flags-integration.test.ts +18 -17
  90. package/src/__tests__/skill-feature-flags.test.ts +13 -13
  91. package/src/__tests__/skill-load-feature-flag.test.ts +4 -4
  92. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  93. package/src/__tests__/slack-inbound-verification.test.ts +2 -2
  94. package/src/__tests__/starter-task-flow.test.ts +1 -0
  95. package/src/__tests__/suggestion-routes.test.ts +443 -0
  96. package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
  97. package/src/__tests__/swarm-recursion.test.ts +1 -0
  98. package/src/__tests__/swarm-tool.test.ts +1 -0
  99. package/src/__tests__/system-prompt.test.ts +8 -0
  100. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
  101. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
  102. package/src/__tests__/top-level-renderer.test.ts +22 -0
  103. package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
  104. package/src/__tests__/web-fetch.test.ts +6 -2
  105. package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
  106. package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
  107. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
  108. package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
  109. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
  110. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
  111. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
  112. package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
  113. package/src/agent/attachments.ts +27 -1
  114. package/src/agent/loop.ts +29 -1
  115. package/src/avatar/traits-png-sync.ts +80 -25
  116. package/src/bundler/app-bundler.ts +4 -4
  117. package/src/calls/call-domain.ts +1 -0
  118. package/src/calls/voice-session-bridge.ts +1 -0
  119. package/src/cli/commands/auth.ts +92 -0
  120. package/src/cli/commands/avatar.ts +7 -6
  121. package/src/cli/commands/config.ts +2 -0
  122. package/src/cli/commands/oauth/providers.ts +29 -0
  123. package/src/cli/program.ts +12 -0
  124. package/src/cli.ts +15 -48
  125. package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
  126. package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
  127. package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
  128. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
  129. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
  130. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
  131. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
  132. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
  133. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
  134. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
  135. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
  136. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
  137. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
  138. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
  139. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
  140. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
  141. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
  142. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  143. package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
  144. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
  145. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
  146. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
  147. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  148. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
  149. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  150. package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
  151. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
  152. package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
  153. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
  154. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
  155. package/src/config/bundled-tool-registry.ts +2 -14
  156. package/src/config/feature-flag-registry.json +16 -0
  157. package/src/config/loader.ts +64 -0
  158. package/src/config/raw-config-utils.ts +30 -0
  159. package/src/config/schema-utils.ts +28 -7
  160. package/src/config/schema.ts +8 -0
  161. package/src/config/schemas/elevenlabs.ts +18 -0
  162. package/src/config/schemas/memory-lifecycle.ts +4 -2
  163. package/src/config/schemas/memory-storage.ts +1 -1
  164. package/src/config/schemas/services.ts +8 -6
  165. package/src/contacts/contact-store.ts +13 -6
  166. package/src/contacts/contacts-write.ts +0 -1
  167. package/src/context/window-manager.ts +13 -2
  168. package/src/daemon/conversation-agent-loop-handlers.ts +46 -42
  169. package/src/daemon/conversation-agent-loop.ts +56 -19
  170. package/src/daemon/conversation-attachments.ts +18 -36
  171. package/src/daemon/conversation-error.ts +2 -1
  172. package/src/daemon/conversation-history.ts +18 -4
  173. package/src/daemon/conversation-lifecycle.ts +39 -15
  174. package/src/daemon/conversation-messaging.ts +70 -26
  175. package/src/daemon/conversation-process.ts +58 -34
  176. package/src/daemon/conversation-runtime-assembly.ts +21 -38
  177. package/src/daemon/conversation-slash.ts +121 -256
  178. package/src/daemon/conversation-surfaces.ts +143 -20
  179. package/src/daemon/conversation-tool-setup.ts +0 -6
  180. package/src/daemon/conversation-workspace.ts +21 -1
  181. package/src/daemon/conversation.ts +51 -29
  182. package/src/daemon/first-greeting.ts +35 -0
  183. package/src/daemon/handlers/config-embeddings.ts +148 -0
  184. package/src/daemon/handlers/config-model.ts +71 -26
  185. package/src/daemon/handlers/conversations.ts +0 -23
  186. package/src/daemon/handlers/recording.ts +26 -21
  187. package/src/daemon/history-repair.ts +28 -8
  188. package/src/daemon/host-cu-proxy.ts +2 -2
  189. package/src/daemon/lifecycle.ts +106 -64
  190. package/src/daemon/message-protocol.ts +3 -0
  191. package/src/daemon/message-types/conversations.ts +19 -0
  192. package/src/daemon/message-types/messages.ts +1 -0
  193. package/src/daemon/message-types/shared.ts +2 -0
  194. package/src/daemon/message-types/surfaces.ts +2 -0
  195. package/src/daemon/message-types/upgrades.ts +23 -0
  196. package/src/daemon/server.ts +83 -12
  197. package/src/daemon/shutdown-handlers.ts +8 -5
  198. package/src/daemon/startup-error.ts +9 -0
  199. package/src/daemon/tool-side-effects.ts +11 -28
  200. package/src/events/tool-permission-telemetry-listener.ts +1 -3
  201. package/src/instrument.ts +0 -4
  202. package/src/media/app-icon-generator.ts +2 -2
  203. package/src/memory/app-git-service.ts +28 -16
  204. package/src/memory/app-store.ts +230 -41
  205. package/src/memory/attachments-store.ts +558 -130
  206. package/src/memory/conversation-attention-store.ts +70 -0
  207. package/src/memory/conversation-crud.ts +442 -3
  208. package/src/memory/conversation-directories.ts +125 -0
  209. package/src/memory/conversation-disk-view.ts +390 -0
  210. package/src/memory/conversation-key-store.ts +17 -5
  211. package/src/memory/conversation-queries.ts +5 -1
  212. package/src/memory/conversation-title-service.ts +21 -49
  213. package/src/memory/db-init.ts +28 -0
  214. package/src/memory/embedding-backend.ts +42 -53
  215. package/src/memory/embedding-gemini.test.ts +4 -4
  216. package/src/memory/embedding-local.ts +1 -3
  217. package/src/memory/embedding-ollama.ts +1 -3
  218. package/src/memory/embedding-openai.ts +1 -3
  219. package/src/memory/indexer.ts +9 -7
  220. package/src/memory/items-extractor.ts +42 -13
  221. package/src/memory/job-handlers/conversation-starters.ts +6 -1
  222. package/src/memory/job-handlers/embedding.test.ts +1 -4
  223. package/src/memory/llm-request-log-store.ts +100 -1
  224. package/src/memory/migrations/102-alter-table-columns.ts +5 -0
  225. package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
  226. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
  227. package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
  228. package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
  229. package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
  230. package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
  231. package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
  232. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
  233. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
  234. package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
  235. package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
  236. package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
  237. package/src/memory/migrations/index.ts +7 -0
  238. package/src/memory/migrations/registry.ts +13 -0
  239. package/src/memory/retriever.test.ts +601 -2
  240. package/src/memory/retriever.ts +85 -9
  241. package/src/memory/schema/conversations.ts +6 -0
  242. package/src/memory/schema/infrastructure.ts +13 -7
  243. package/src/memory/schema/oauth.ts +6 -0
  244. package/src/messaging/providers/gmail/mime-builder.ts +3 -1
  245. package/src/notifications/copy-composer.ts +26 -0
  246. package/src/notifications/decision-engine.ts +14 -1
  247. package/src/notifications/emit-signal.ts +1 -1
  248. package/src/notifications/signal.ts +36 -0
  249. package/src/oauth/byo-connection.test.ts +1 -45
  250. package/src/oauth/byo-connection.ts +2 -8
  251. package/src/oauth/connect-orchestrator.ts +15 -11
  252. package/src/oauth/connection-resolver.test.ts +191 -0
  253. package/src/oauth/connection-resolver.ts +66 -38
  254. package/src/oauth/connection.ts +0 -1
  255. package/src/oauth/oauth-store.ts +97 -47
  256. package/src/oauth/platform-connection.test.ts +0 -1
  257. package/src/oauth/platform-connection.ts +11 -3
  258. package/src/oauth/seed-providers.ts +78 -3
  259. package/src/oauth/token-persistence.ts +16 -10
  260. package/src/permissions/checker.ts +62 -19
  261. package/src/prompts/system-prompt.ts +2 -0
  262. package/src/prompts/templates/BOOTSTRAP.md +2 -0
  263. package/src/providers/anthropic/client.ts +8 -1
  264. package/src/providers/failover.ts +4 -1
  265. package/src/providers/gemini/client.ts +50 -0
  266. package/src/providers/model-catalog.ts +92 -0
  267. package/src/providers/model-intents.ts +29 -20
  268. package/src/providers/openai/client.ts +49 -0
  269. package/src/providers/types.ts +2 -0
  270. package/src/runtime/access-request-helper.ts +16 -7
  271. package/src/runtime/auth/credential-service.ts +3 -1
  272. package/src/runtime/auth/route-policy.ts +14 -1
  273. package/src/runtime/btw-sidechain.ts +101 -0
  274. package/src/runtime/channel-reply-delivery.ts +17 -1
  275. package/src/runtime/http-router.ts +3 -1
  276. package/src/runtime/http-server.ts +196 -141
  277. package/src/runtime/http-types.ts +1 -0
  278. package/src/runtime/migrations/vbundle-builder.ts +5 -1
  279. package/src/runtime/routes/access-request-decision.ts +41 -0
  280. package/src/runtime/routes/app-management-routes.ts +6 -3
  281. package/src/runtime/routes/app-routes.ts +7 -3
  282. package/src/runtime/routes/approval-routes.ts +1 -0
  283. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
  284. package/src/runtime/routes/attachment-routes.ts +45 -15
  285. package/src/runtime/routes/btw-routes.ts +21 -61
  286. package/src/runtime/routes/conversation-management-routes.ts +68 -0
  287. package/src/runtime/routes/conversation-query-routes.ts +180 -10
  288. package/src/runtime/routes/conversation-routes.ts +222 -28
  289. package/src/runtime/routes/conversation-starter-routes.ts +9 -11
  290. package/src/runtime/routes/diagnostics-routes.ts +1 -0
  291. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
  292. package/src/runtime/routes/llm-context-normalization.ts +1199 -0
  293. package/src/runtime/routes/log-export-routes.ts +3 -0
  294. package/src/runtime/routes/memory-item-routes.test.ts +34 -0
  295. package/src/runtime/routes/memory-item-routes.ts +4 -0
  296. package/src/runtime/routes/migration-routes.ts +4 -1
  297. package/src/runtime/routes/oauth-apps.ts +291 -0
  298. package/src/runtime/routes/secret-routes.ts +28 -1
  299. package/src/runtime/routes/settings-routes.ts +14 -0
  300. package/src/runtime/routes/trace-event-routes.ts +4 -1
  301. package/src/schedule/schedule-store.ts +9 -21
  302. package/src/security/secure-keys.ts +21 -0
  303. package/src/signals/bash.ts +1 -1
  304. package/src/swarm/backend-claude-code.ts +3 -6
  305. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  306. package/src/telemetry/usage-telemetry-reporter.ts +3 -1
  307. package/src/tools/AGENTS.md +6 -10
  308. package/src/tools/apps/executors.ts +17 -232
  309. package/src/tools/claude-code/claude-code.ts +2 -3
  310. package/src/tools/credentials/vault.ts +7 -12
  311. package/src/tools/host-filesystem/read.ts +13 -10
  312. package/src/tools/network/__tests__/web-search.test.ts +4 -2
  313. package/src/tools/schedule/list.ts +2 -7
  314. package/src/tools/schema-transforms.ts +5 -0
  315. package/src/tools/shared/filesystem/format-diff.ts +4 -21
  316. package/src/tools/skills/execute.ts +1 -1
  317. package/src/tools/tool-manifest.ts +0 -6
  318. package/src/tools/ui-surface/definitions.ts +2 -2
  319. package/src/util/device-id.ts +28 -5
  320. package/src/util/platform.ts +6 -0
  321. package/src/util/pricing.ts +1 -0
  322. package/src/util/retry.ts +1 -3
  323. package/src/workspace/migrations/002-backfill-installation-id.ts +23 -12
  324. package/src/workspace/migrations/003-seed-device-id.ts +3 -4
  325. package/src/workspace/migrations/006-services-config.ts +5 -0
  326. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
  327. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
  328. package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
  329. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
  330. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
  331. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
  332. package/src/workspace/migrations/registry.ts +10 -0
  333. package/src/workspace/top-level-renderer.ts +12 -0
  334. package/src/__tests__/asset-materialize-tool.test.ts +0 -523
  335. package/src/__tests__/asset-search-tool.test.ts +0 -536
  336. package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
  337. package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
  338. package/src/__tests__/media-visibility-policy.test.ts +0 -190
  339. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
  340. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
  341. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
  342. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
  343. package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
  344. package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
  345. package/src/daemon/media-visibility-policy.ts +0 -59
  346. package/src/tools/assets/materialize.ts +0 -248
  347. package/src/tools/assets/search.ts +0 -400
@@ -444,6 +444,7 @@ const WORKSPACE_SKIP_DIRS = new Set([
444
444
  "embedding-models",
445
445
  "data/qdrant",
446
446
  "data/attachments",
447
+ "conversations",
447
448
  ]);
448
449
 
449
450
  /** Files at the workspace root to skip (already covered by sanitized fields). */
@@ -513,6 +514,8 @@ function collectWorkspaceFiles(): Record<string, string> {
513
514
 
514
515
  // SQLite DB handling: dump as SQL text, then enforce size cap
515
516
  if (entry.endsWith(".db")) {
517
+ // Skip the dump entirely if the budget is already exhausted
518
+ if (totalBytes >= MAX_WORKSPACE_PAYLOAD_BYTES) continue;
516
519
  try {
517
520
  const proc = spawnSync("sqlite3", [fullPath, ".dump"], {
518
521
  timeout: 10_000,
@@ -347,6 +347,40 @@ describe("Memory Item Routes", () => {
347
347
  expect(body.items[1].id).toBe("i1");
348
348
  });
349
349
 
350
+ test("supports sort by accessCount descending", async () => {
351
+ insertItem({
352
+ id: "i1",
353
+ kind: "preference",
354
+ subject: "s1",
355
+ statement: "st1",
356
+ });
357
+ insertItem({
358
+ id: "i2",
359
+ kind: "preference",
360
+ subject: "s2",
361
+ statement: "st2",
362
+ });
363
+
364
+ getDb()
365
+ .update(memoryItems)
366
+ .set({ accessCount: 2 })
367
+ .where(eq(memoryItems.id, "i1"))
368
+ .run();
369
+ getDb()
370
+ .update(memoryItems)
371
+ .set({ accessCount: 7 })
372
+ .where(eq(memoryItems.id, "i2"))
373
+ .run();
374
+
375
+ const ctx = makeCtx({ sort: "accessCount", order: "desc" });
376
+ const res = await handler(ctx);
377
+ const body = (await res.json()) as {
378
+ items: Array<{ id: string }>;
379
+ };
380
+ expect(body.items[0].id).toBe("i2");
381
+ expect(body.items[1].id).toBe("i1");
382
+ });
383
+
350
384
  test("rejects invalid kind filter", async () => {
351
385
  const ctx = makeCtx({ kind: "bogus" });
352
386
  const res = await handler(ctx);
@@ -37,6 +37,7 @@ type MemoryItemKind = (typeof VALID_KINDS)[number];
37
37
  const VALID_SORT_FIELDS = [
38
38
  "lastSeenAt",
39
39
  "importance",
40
+ "accessCount",
40
41
  "kind",
41
42
  "firstSeenAt",
42
43
  ] as const;
@@ -46,6 +47,7 @@ type SortField = (typeof VALID_SORT_FIELDS)[number];
46
47
  const SORT_COLUMN_MAP = {
47
48
  lastSeenAt: memoryItems.lastSeenAt,
48
49
  importance: memoryItems.importance,
50
+ accessCount: memoryItems.accessCount,
49
51
  kind: memoryItems.kind,
50
52
  firstSeenAt: memoryItems.firstSeenAt,
51
53
  } as const;
@@ -103,6 +105,8 @@ export function handleListMemoryItems(url: URL): Response {
103
105
 
104
106
  // Build WHERE conditions
105
107
  const conditions = [];
108
+ // Hide system-managed capability memories (skill announcements) from the UI
109
+ conditions.push(ne(memoryItems.kind, "capability"));
106
110
  if (statusParam && statusParam !== "all") {
107
111
  conditions.push(eq(memoryItems.status, statusParam));
108
112
  }
@@ -160,7 +160,10 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
160
160
  // Best-effort: if the DB can't be checkpointed (e.g. not a valid
161
161
  // SQLite file, missing WAL, etc.) we still proceed with the export
162
162
  // using whatever is on disk.
163
- log.warn({ err }, "WAL checkpoint failed — exporting without checkpoint");
163
+ log.warn(
164
+ { err },
165
+ "WAL checkpoint failed — exporting without checkpoint",
166
+ );
164
167
  }
165
168
  },
166
169
  });
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Route handlers for OAuth app and connection CRUD.
3
+ *
4
+ * Provides endpoints for managing user-supplied OAuth apps (e.g. "your own"
5
+ * Google client credentials) and their connections. All endpoints are
6
+ * bearer-token authenticated via the standard runtime auth middleware.
7
+ */
8
+
9
+ import { orchestrateOAuthConnect } from "../../oauth/connect-orchestrator.js";
10
+ import {
11
+ deleteApp,
12
+ disconnectOAuthProvider,
13
+ getApp,
14
+ getAppClientSecret,
15
+ getConnection,
16
+ getProvider,
17
+ listApps,
18
+ listConnections,
19
+ upsertApp,
20
+ } from "../../oauth/oauth-store.js";
21
+ import { httpError } from "../http-errors.js";
22
+ import type { RouteDefinition } from "../http-router.js";
23
+
24
+ function parseGrantedScopes(grantedScopes: string | string[] | null | undefined): string[] {
25
+ if (Array.isArray(grantedScopes)) {
26
+ return grantedScopes.filter((scope): scope is string => typeof scope === "string");
27
+ }
28
+
29
+ if (typeof grantedScopes !== "string" || grantedScopes.trim() === "") {
30
+ return [];
31
+ }
32
+
33
+ try {
34
+ const parsed = JSON.parse(grantedScopes) as unknown;
35
+ if (!Array.isArray(parsed)) return [];
36
+ return parsed.filter((scope): scope is string => typeof scope === "string");
37
+ } catch {
38
+ return [];
39
+ }
40
+ }
41
+
42
+ function normalizeHasRefreshToken(
43
+ hasRefreshToken: boolean | number | null | undefined,
44
+ ): boolean {
45
+ return hasRefreshToken === true || hasRefreshToken === 1;
46
+ }
47
+
48
+ /**
49
+ * Build route definitions for OAuth app and connection CRUD endpoints.
50
+ */
51
+ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
52
+ return [
53
+ // GET /v1/oauth/apps — List apps filtered by provider_key query param.
54
+ {
55
+ endpoint: "oauth/apps",
56
+ method: "GET",
57
+ handler: ({ url }) => {
58
+ const providerKey = url.searchParams.get("provider_key");
59
+ if (!providerKey) {
60
+ return httpError(
61
+ "BAD_REQUEST",
62
+ "provider_key query parameter is required",
63
+ 400,
64
+ );
65
+ }
66
+
67
+ const allApps = listApps();
68
+ const filtered = allApps.filter(
69
+ (row) => row.providerKey === providerKey,
70
+ );
71
+
72
+ const providerRow = getProvider(providerKey);
73
+ const provider = providerRow
74
+ ? {
75
+ provider_key: providerRow.providerKey,
76
+ display_name: providerRow.displayName ?? null,
77
+ description: providerRow.description ?? null,
78
+ dashboard_url: providerRow.dashboardUrl ?? null,
79
+ client_id_placeholder: providerRow.clientIdPlaceholder ?? null,
80
+ requires_client_secret: providerRow.requiresClientSecret ?? 1,
81
+ }
82
+ : null;
83
+
84
+ return Response.json({
85
+ provider,
86
+ apps: filtered.map((row) => ({
87
+ id: row.id,
88
+ provider_key: row.providerKey,
89
+ client_id: row.clientId,
90
+ created_at: row.createdAt,
91
+ updated_at: row.updatedAt,
92
+ })),
93
+ });
94
+ },
95
+ },
96
+
97
+ // POST /v1/oauth/apps — Create an OAuth app.
98
+ {
99
+ endpoint: "oauth/apps",
100
+ method: "POST",
101
+ policyKey: "oauth/apps.create",
102
+ handler: async ({ req }) => {
103
+ const body = (await req.json()) as {
104
+ provider_key?: string;
105
+ client_id?: string;
106
+ client_secret?: string;
107
+ };
108
+
109
+ const { provider_key, client_id, client_secret } = body;
110
+
111
+ if (
112
+ !provider_key ||
113
+ typeof provider_key !== "string" ||
114
+ !client_id ||
115
+ typeof client_id !== "string" ||
116
+ !client_secret ||
117
+ typeof client_secret !== "string"
118
+ ) {
119
+ return httpError(
120
+ "BAD_REQUEST",
121
+ "provider_key, client_id, and client_secret are required non-empty strings",
122
+ 400,
123
+ );
124
+ }
125
+
126
+ const provider = getProvider(provider_key);
127
+ if (!provider) {
128
+ return httpError(
129
+ "NOT_FOUND",
130
+ `No OAuth provider registered for "${provider_key}"`,
131
+ 404,
132
+ );
133
+ }
134
+
135
+ const app = await upsertApp(provider_key, client_id, {
136
+ clientSecretValue: client_secret,
137
+ });
138
+
139
+ return Response.json(
140
+ {
141
+ app: {
142
+ id: app.id,
143
+ provider_key: app.providerKey,
144
+ client_id: app.clientId,
145
+ created_at: app.createdAt,
146
+ updated_at: app.updatedAt,
147
+ },
148
+ },
149
+ { status: 201 },
150
+ );
151
+ },
152
+ },
153
+
154
+ // DELETE /v1/oauth/apps/:id — Delete an OAuth app.
155
+ {
156
+ endpoint: "oauth/apps/:id",
157
+ method: "DELETE",
158
+ policyKey: "oauth/apps.delete",
159
+ handler: async ({ params }) => {
160
+ const app = getApp(params.id);
161
+ if (!app) {
162
+ return httpError("NOT_FOUND", `OAuth app not found: ${params.id}`, 404);
163
+ }
164
+
165
+ // Disconnect all connections for this app first to clean up tokens.
166
+ const connections = listConnections(app.providerKey, app.clientId);
167
+ for (const conn of connections) {
168
+ await disconnectOAuthProvider(app.providerKey, app.clientId, conn.id);
169
+ }
170
+
171
+ await deleteApp(params.id);
172
+
173
+ return Response.json({ ok: true });
174
+ },
175
+ },
176
+
177
+ // GET /v1/oauth/apps/:appId/connections — List connections for an app.
178
+ {
179
+ endpoint: "oauth/apps/:appId/connections",
180
+ method: "GET",
181
+ handler: ({ params }) => {
182
+ const app = getApp(params.appId);
183
+ if (!app) {
184
+ return httpError(
185
+ "NOT_FOUND",
186
+ `OAuth app not found: ${params.appId}`,
187
+ 404,
188
+ );
189
+ }
190
+
191
+ const connections = listConnections(app.providerKey, app.clientId);
192
+
193
+ return Response.json({
194
+ connections: connections.map((row) => ({
195
+ id: row.id,
196
+ provider_key: row.providerKey,
197
+ account_info: row.accountInfo,
198
+ granted_scopes: parseGrantedScopes(row.grantedScopes),
199
+ status: row.status,
200
+ has_refresh_token: normalizeHasRefreshToken(row.hasRefreshToken),
201
+ expires_at: row.expiresAt,
202
+ created_at: row.createdAt,
203
+ updated_at: row.updatedAt,
204
+ })),
205
+ });
206
+ },
207
+ },
208
+
209
+ // DELETE /v1/oauth/connections/:id — Disconnect a single connection.
210
+ {
211
+ endpoint: "oauth/connections/:id",
212
+ method: "DELETE",
213
+ handler: async ({ params }) => {
214
+ const conn = getConnection(params.id);
215
+ if (!conn) {
216
+ return httpError(
217
+ "NOT_FOUND",
218
+ `OAuth connection not found: ${params.id}`,
219
+ 404,
220
+ );
221
+ }
222
+
223
+ const result = await disconnectOAuthProvider(
224
+ conn.providerKey,
225
+ undefined,
226
+ conn.id,
227
+ );
228
+ if (result === "error") {
229
+ return httpError(
230
+ "INTERNAL_ERROR",
231
+ "Failed to clean up connection tokens. The connection was not removed.",
232
+ 500,
233
+ );
234
+ }
235
+
236
+ return Response.json({ ok: true });
237
+ },
238
+ },
239
+
240
+ // POST /v1/oauth/apps/:appId/connect — Start OAuth connect flow.
241
+ {
242
+ endpoint: "oauth/apps/:appId/connect",
243
+ method: "POST",
244
+ handler: async ({ req, params }) => {
245
+ const app = getApp(params.appId);
246
+ if (!app) {
247
+ return httpError(
248
+ "NOT_FOUND",
249
+ `OAuth app not found: ${params.appId}`,
250
+ 404,
251
+ );
252
+ }
253
+
254
+ let body: { scopes?: string[] } = {};
255
+ try {
256
+ const text = await req.text();
257
+ if (text) {
258
+ body = JSON.parse(text);
259
+ }
260
+ } catch {
261
+ // No body or invalid JSON — use defaults
262
+ }
263
+
264
+ const clientSecret = await getAppClientSecret(app);
265
+
266
+ const result = await orchestrateOAuthConnect({
267
+ service: app.providerKey,
268
+ clientId: app.clientId,
269
+ clientSecret,
270
+ requestedScopes: body.scopes,
271
+ isInteractive: false,
272
+ });
273
+
274
+ if (result.success && result.deferred) {
275
+ return Response.json({
276
+ auth_url: result.authUrl,
277
+ state: result.state,
278
+ });
279
+ }
280
+
281
+ if (!result.success) {
282
+ return Response.json({ error: result.error }, { status: 500 });
283
+ }
284
+
285
+ // Interactive success (shouldn't happen with isInteractive: false,
286
+ // but handle gracefully)
287
+ return Response.json({ ok: true });
288
+ },
289
+ },
290
+ ];
291
+ }
@@ -13,6 +13,8 @@ import type { CesClient } from "../../credential-execution/client.js";
13
13
  import { setSentryOrganizationId } from "../../instrument.js";
14
14
  import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
15
15
  import { validateAnthropicApiKey } from "../../providers/anthropic/client.js";
16
+ import { validateGeminiApiKey } from "../../providers/gemini/client.js";
17
+ import { validateOpenAIApiKey } from "../../providers/openai/client.js";
16
18
  import { initializeProviders } from "../../providers/registry.js";
17
19
  import { credentialKey } from "../../security/credential-key.js";
18
20
  import {
@@ -131,7 +133,7 @@ export async function handleAddSecret(
131
133
  400,
132
134
  );
133
135
  }
134
- // Validate Anthropic API keys before storing
136
+ // Validate API keys before storing (Anthropic, OpenAI, Gemini)
135
137
  if (name === "anthropic") {
136
138
  const validation = await validateAnthropicApiKey(value);
137
139
  if (!validation.valid) {
@@ -144,7 +146,32 @@ export async function handleAddSecret(
144
146
  { status: 422 },
145
147
  );
146
148
  }
149
+ } else if (name === "openai") {
150
+ const validation = await validateOpenAIApiKey(value);
151
+ if (!validation.valid) {
152
+ log.warn(
153
+ { provider: name, reason: validation.reason },
154
+ "API key validation failed",
155
+ );
156
+ return Response.json(
157
+ { success: false, error: validation.reason },
158
+ { status: 422 },
159
+ );
160
+ }
161
+ } else if (name === "gemini") {
162
+ const validation = await validateGeminiApiKey(value);
163
+ if (!validation.valid) {
164
+ log.warn(
165
+ { provider: name, reason: validation.reason },
166
+ "API key validation failed",
167
+ );
168
+ return Response.json(
169
+ { success: false, error: validation.reason },
170
+ { status: 422 },
171
+ );
172
+ }
147
173
  }
174
+ // fireworks, openrouter, ollama — no validation (allow storage)
148
175
 
149
176
  const stored = await setSecureKeyAsync(name, value);
150
177
  if (!stored) {
@@ -644,6 +644,20 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
644
644
  },
645
645
 
646
646
  // OAuth connect
647
+ {
648
+ endpoint: "oauth/start",
649
+ method: "POST",
650
+ policyKey: "oauth/start",
651
+ handler: async ({ req }) => {
652
+ const body = (await req.json()) as {
653
+ service?: string;
654
+ requestedScopes?: string[];
655
+ };
656
+ return handleOAuthConnectStart(body);
657
+ },
658
+ },
659
+ // Legacy alias for oauth/start (kept for backwards compatibility with
660
+ // older clients and platform proxy routes)
647
661
  {
648
662
  endpoint: "integrations/oauth/start",
649
663
  method: "POST",
@@ -42,7 +42,10 @@ export function traceEventRouteDefinitions(): RouteDefinition[] {
42
42
  const afterSequence = afterSequenceParam
43
43
  ? parseInt(afterSequenceParam, 10)
44
44
  : undefined;
45
- if (afterSequenceParam && (isNaN(afterSequence!) || afterSequence! < 0)) {
45
+ if (
46
+ afterSequenceParam &&
47
+ (isNaN(afterSequence!) || afterSequence! < 0)
48
+ ) {
46
49
  return httpError(
47
50
  "BAD_REQUEST",
48
51
  "afterSequence must be a non-negative integer",
@@ -15,10 +15,7 @@ import type { ScheduleSyntax } from "./recurrence-types.js";
15
15
  const logger = getLogger("schedule-store");
16
16
 
17
17
  export type ScheduleMode = "notify" | "execute";
18
- export type RoutingIntent =
19
- | "single_channel"
20
- | "multi_channel"
21
- | "all_channels";
18
+ export type RoutingIntent = "single_channel" | "multi_channel" | "all_channels";
22
19
  export type ScheduleStatus = "active" | "firing" | "fired" | "cancelled";
23
20
 
24
21
  export interface ScheduleJob {
@@ -193,9 +190,7 @@ export function listSchedules(options?: {
193
190
  conditions.push(isNull(scheduleJobs.cronExpression));
194
191
  }
195
192
  if (options?.recurringOnly) {
196
- conditions.push(
197
- sql`${scheduleJobs.cronExpression} IS NOT NULL`,
198
- );
193
+ conditions.push(sql`${scheduleJobs.cronExpression} IS NOT NULL`);
199
194
  }
200
195
  const where = conditions.length > 0 ? and(...conditions) : undefined;
201
196
  const rows = db
@@ -415,10 +410,7 @@ export function claimDueSchedules(now: number): ScheduleJob[] {
415
410
  updatedAt: now,
416
411
  })
417
412
  .where(
418
- and(
419
- eq(scheduleJobs.id, row.id),
420
- eq(scheduleJobs.status, "active"),
421
- ),
413
+ and(eq(scheduleJobs.id, row.id), eq(scheduleJobs.status, "active")),
422
414
  )
423
415
  .run();
424
416
 
@@ -450,9 +442,7 @@ export function completeOneShot(id: string): void {
450
442
  enabled: false,
451
443
  updatedAt: now,
452
444
  })
453
- .where(
454
- and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")),
455
- )
445
+ .where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")))
456
446
  .run();
457
447
  }
458
448
 
@@ -468,9 +458,7 @@ export function failOneShot(id: string): void {
468
458
  status: "active",
469
459
  updatedAt: now,
470
460
  })
471
- .where(
472
- and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")),
473
- )
461
+ .where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")))
474
462
  .run();
475
463
  }
476
464
 
@@ -487,9 +475,7 @@ export function cancelSchedule(id: string): boolean {
487
475
  enabled: false,
488
476
  updatedAt: now,
489
477
  })
490
- .where(
491
- and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "active")),
492
- )
478
+ .where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "active")))
493
479
  .run();
494
480
  return rawChanges() > 0;
495
481
  }
@@ -770,7 +756,9 @@ function parseJobRow(row: typeof scheduleJobs.$inferSelect): ScheduleJob {
770
756
  };
771
757
  }
772
758
 
773
- function safeParseJson(json: string | null | undefined): Record<string, unknown> {
759
+ function safeParseJson(
760
+ json: string | null | undefined,
761
+ ): Record<string, unknown> {
774
762
  if (!json) return {};
775
763
  try {
776
764
  return JSON.parse(json) as Record<string, unknown>;
@@ -193,6 +193,27 @@ export async function getProviderKeyAsync(
193
193
  return envVar ? process.env[envVar] : undefined;
194
194
  }
195
195
 
196
+ // ---------------------------------------------------------------------------
197
+ // Masked provider key — for safe display in client UIs
198
+ // ---------------------------------------------------------------------------
199
+
200
+ /**
201
+ * Retrieve a provider API key and return a masked version suitable for
202
+ * display. Shows the first 10 characters and last 4, with `...` in between,
203
+ * always hiding at least 3 characters. Returns `null` if no key is stored.
204
+ */
205
+ export async function getMaskedProviderKey(
206
+ provider: string,
207
+ ): Promise<string | null> {
208
+ const key = await getProviderKeyAsync(provider);
209
+ if (!key || key.length === 0) return null;
210
+ const minHidden = 3;
211
+ const maxVisible = Math.max(1, key.length - minHidden);
212
+ const prefixLen = Math.min(10, maxVisible);
213
+ const suffixLen = Math.min(4, Math.max(0, maxVisible - prefixLen));
214
+ return `${key.slice(0, prefixLen)}...${suffixLen > 0 ? key.slice(-suffixLen) : ""}`;
215
+ }
216
+
196
217
  // ---------------------------------------------------------------------------
197
218
  // Test helpers
198
219
  // ---------------------------------------------------------------------------
@@ -80,7 +80,7 @@ export function handleBashSignal(filename: string): void {
80
80
  exitCode: null,
81
81
  timedOut: false,
82
82
  error:
83
- "Bash signals are disabled. The running assistant process must have been started with VELLUM_DEBUG=1 (setting it on the CLI command alone is not enough). Restart the assistant with: VELLUM_DEBUG=1 assistant start",
83
+ "Bash signals are disabled. The running assistant process must have been started with VELLUM_DEBUG=1 (setting it on the CLI command alone is not enough). Restart the assistant with: vellum sleep && VELLUM_DEBUG=1 vellum wake",
84
84
  });
85
85
  }
86
86
  return;
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { resolveModelIntent } from "../providers/model-intents.js";
9
9
  import type { ModelIntent } from "../providers/types.js";
10
- import { getSecureKeyAsync } from "../security/secure-keys.js";
10
+ import { getProviderKeyAsync } from "../security/secure-keys.js";
11
11
  import { getLogger } from "../util/logger.js";
12
12
  import type {
13
13
  SwarmWorkerBackend,
@@ -29,8 +29,7 @@ export function createClaudeCodeBackend(): SwarmWorkerBackend {
29
29
  name: "claude_code",
30
30
 
31
31
  async isAvailable(): Promise<boolean> {
32
- const apiKey =
33
- (await getSecureKeyAsync("anthropic")) ?? process.env.ANTHROPIC_API_KEY;
32
+ const apiKey = await getProviderKeyAsync("anthropic");
34
33
  return !!apiKey;
35
34
  },
36
35
 
@@ -39,9 +38,7 @@ export function createClaudeCodeBackend(): SwarmWorkerBackend {
39
38
  const stderrLines: string[] = [];
40
39
  try {
41
40
  const { query } = await import("@anthropic-ai/claude-agent-sdk");
42
- const apiKey =
43
- (await getSecureKeyAsync("anthropic")) ??
44
- process.env.ANTHROPIC_API_KEY;
41
+ const apiKey = await getProviderKeyAsync("anthropic");
45
42
  if (!apiKey) {
46
43
  return {
47
44
  success: false,
@@ -97,6 +97,7 @@ mock.module("../version.js", () => ({
97
97
  // Production import (after mocks)
98
98
  // ---------------------------------------------------------------------------
99
99
 
100
+ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
100
101
  import type { UsageEvent } from "../usage/types.js";
101
102
  import { UsageTelemetryReporter } from "./usage-telemetry-reporter.js";
102
103
 
@@ -434,7 +435,7 @@ describe("UsageTelemetryReporter", () => {
434
435
  expect(body.user_id).toBeUndefined();
435
436
  });
436
437
 
437
- test("assistant_id falls back to 'self' when getExternalAssistantId returns undefined", async () => {
438
+ test("assistant_id falls back to DAEMON_INTERNAL_ASSISTANT_ID when getExternalAssistantId returns undefined", async () => {
438
439
  mockGetExternalAssistantId.mockReturnValue(undefined);
439
440
  const events = [makeUsageEvent()];
440
441
  mockQueryUnreportedUsageEvents.mockReturnValue(events);
@@ -450,7 +451,7 @@ describe("UsageTelemetryReporter", () => {
450
451
  (mockFetch.mock.calls[0] as [string, RequestInit])[1].body as string,
451
452
  );
452
453
  expect(body.installation_id).toBe("test-device-id");
453
- expect(body.assistant_id).toBe("self");
454
+ expect(body.assistant_id).toBe(DAEMON_INTERNAL_ASSISTANT_ID);
454
455
  });
455
456
 
456
457
  test("turn events are included in the events array with type discriminator", async () => {
@@ -23,6 +23,7 @@ import { queryUnreportedLifecycleEvents } from "../memory/lifecycle-events-store
23
23
  import { queryUnreportedUsageEvents } from "../memory/llm-usage-store.js";
24
24
  import { queryUnreportedTurnEvents } from "../memory/turn-events-store.js";
25
25
  import { resolveManagedProxyContext } from "../providers/managed-proxy/context.js";
26
+ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
26
27
  import { getExternalAssistantId } from "../runtime/auth/external-assistant-id.js";
27
28
  import { getDeviceId } from "../util/device-id.js";
28
29
  import { getLogger } from "../util/logger.js";
@@ -187,7 +188,8 @@ export class UsageTelemetryReporter {
187
188
  ),
188
189
  ];
189
190
 
190
- const assistantId = getExternalAssistantId() ?? "self";
191
+ const assistantId =
192
+ getExternalAssistantId() ?? DAEMON_INTERNAL_ASSISTANT_ID;
191
193
  const organizationId = getPlatformOrganizationId() || undefined;
192
194
  const userId = getPlatformUserId() || undefined;
193
195
  const payload = {