@vellumai/assistant 0.5.1 → 0.5.3

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 (405) hide show
  1. package/ARCHITECTURE.md +163 -54
  2. package/docs/architecture/integrations.md +62 -67
  3. package/docs/credential-execution-service.md +3 -3
  4. package/docs/skills.md +100 -0
  5. package/package.json +1 -1
  6. package/src/__tests__/agent-loop.test.ts +111 -0
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
  8. package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
  9. package/src/__tests__/app-dir-path-guard.test.ts +78 -0
  10. package/src/__tests__/app-executors.test.ts +1 -291
  11. package/src/__tests__/app-git-history.test.ts +4 -4
  12. package/src/__tests__/app-routes-csp.test.ts +1 -0
  13. package/src/__tests__/app-store-dir-names.test.ts +426 -0
  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 +156 -5
  26. package/src/__tests__/conversation-agent-loop.test.ts +297 -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-memory-dirty-tail.test.ts +150 -0
  37. package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
  38. package/src/__tests__/conversation-queue.test.ts +36 -1
  39. package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
  40. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
  42. package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
  43. package/src/__tests__/conversation-skill-tools.test.ts +4 -9
  44. package/src/__tests__/conversation-slash-commands.test.ts +149 -0
  45. package/src/__tests__/conversation-store.test.ts +24 -21
  46. package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
  47. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
  48. package/src/__tests__/conversation-title-service.test.ts +137 -0
  49. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
  50. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
  51. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
  52. package/src/__tests__/conversation-wipe.test.ts +226 -0
  53. package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
  54. package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
  55. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  56. package/src/__tests__/credential-vault-unit.test.ts +5 -10
  57. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  58. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
  59. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
  60. package/src/__tests__/db-memory-archive-migration.test.ts +372 -0
  61. package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
  62. package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
  63. package/src/__tests__/diagnostics-export.test.ts +70 -1
  64. package/src/__tests__/first-greeting.test.ts +80 -0
  65. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  66. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
  67. package/src/__tests__/history-repair.test.ts +32 -10
  68. package/src/__tests__/http-conversation-lineage.test.ts +251 -0
  69. package/src/__tests__/image-source-path-reinject.test.ts +136 -0
  70. package/src/__tests__/inline-command-runner.test.ts +311 -0
  71. package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
  72. package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
  73. package/src/__tests__/list-messages-attachments.test.ts +96 -0
  74. package/src/__tests__/llm-context-normalization.test.ts +1116 -0
  75. package/src/__tests__/llm-context-route-provider.test.ts +217 -0
  76. package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
  77. package/src/__tests__/media-generate-image.test.ts +47 -94
  78. package/src/__tests__/memory-brief-open-loops.test.ts +530 -0
  79. package/src/__tests__/memory-brief-time.test.ts +285 -0
  80. package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
  81. package/src/__tests__/memory-chunk-archive.test.ts +400 -0
  82. package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
  83. package/src/__tests__/memory-episode-archive.test.ts +370 -0
  84. package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
  85. package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
  86. package/src/__tests__/memory-observation-archive.test.ts +375 -0
  87. package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
  88. package/src/__tests__/memory-recall-quality.test.ts +7 -7
  89. package/src/__tests__/memory-reducer-store.test.ts +728 -0
  90. package/src/__tests__/memory-reducer-types.test.ts +699 -0
  91. package/src/__tests__/memory-reducer.test.ts +698 -0
  92. package/src/__tests__/memory-regressions.test.ts +6 -4
  93. package/src/__tests__/memory-simplified-config.test.ts +281 -0
  94. package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
  95. package/src/__tests__/migration-export-http.test.ts +3 -1
  96. package/src/__tests__/migration-import-commit-http.test.ts +18 -4
  97. package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
  98. package/src/__tests__/mime-builder.test.ts +3 -2
  99. package/src/__tests__/non-member-access-request.test.ts +12 -1
  100. package/src/__tests__/notification-decision-identity.test.ts +52 -0
  101. package/src/__tests__/oauth-apps-routes.test.ts +103 -0
  102. package/src/__tests__/oauth-store.test.ts +115 -0
  103. package/src/__tests__/parse-identity-fields.test.ts +129 -0
  104. package/src/__tests__/provider-error-scenarios.test.ts +1 -3
  105. package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
  106. package/src/__tests__/recording-handler.test.ts +17 -0
  107. package/src/__tests__/registry.test.ts +3 -8
  108. package/src/__tests__/relay-server.test.ts +1 -1
  109. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
  110. package/src/__tests__/schema-transforms.test.ts +165 -5
  111. package/src/__tests__/server-history-render.test.ts +2 -2
  112. package/src/__tests__/skill-load-inline-command.test.ts +598 -0
  113. package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
  114. package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
  115. package/src/__tests__/skills-transitive-hash.test.ts +333 -0
  116. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  117. package/src/__tests__/slack-inbound-verification.test.ts +2 -2
  118. package/src/__tests__/starter-task-flow.test.ts +1 -0
  119. package/src/__tests__/suggestion-routes.test.ts +443 -0
  120. package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
  121. package/src/__tests__/swarm-recursion.test.ts +1 -0
  122. package/src/__tests__/swarm-tool.test.ts +1 -0
  123. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
  124. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
  125. package/src/__tests__/top-level-renderer.test.ts +22 -0
  126. package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
  127. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +320 -0
  128. package/src/__tests__/web-fetch.test.ts +6 -2
  129. package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
  130. package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
  131. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
  132. package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
  133. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
  134. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
  135. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
  136. package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
  137. package/src/agent/attachments.ts +27 -1
  138. package/src/agent/loop.ts +29 -1
  139. package/src/avatar/traits-png-sync.ts +80 -25
  140. package/src/bundler/app-bundler.ts +4 -4
  141. package/src/calls/call-domain.ts +1 -0
  142. package/src/calls/voice-session-bridge.ts +1 -0
  143. package/src/cli/commands/auth.ts +92 -0
  144. package/src/cli/commands/avatar.ts +7 -6
  145. package/src/cli/commands/config.ts +2 -0
  146. package/src/cli/commands/oauth/providers.ts +29 -0
  147. package/src/cli/program.ts +12 -0
  148. package/src/cli.ts +15 -48
  149. package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
  150. package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
  151. package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
  152. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
  153. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
  154. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
  155. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
  156. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
  157. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
  158. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
  159. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
  160. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
  161. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
  162. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
  163. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
  164. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
  165. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
  166. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
  170. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
  171. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  172. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
  173. package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
  174. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  175. package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
  176. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
  177. package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
  178. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
  179. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
  180. package/src/config/bundled-tool-registry.ts +2 -14
  181. package/src/config/feature-flag-registry.json +24 -0
  182. package/src/config/loader.ts +65 -0
  183. package/src/config/raw-config-utils.ts +58 -0
  184. package/src/config/schema-utils.ts +28 -7
  185. package/src/config/schema.ts +20 -0
  186. package/src/config/schemas/elevenlabs.ts +18 -0
  187. package/src/config/schemas/memory-lifecycle.ts +4 -2
  188. package/src/config/schemas/memory-simplified.ts +101 -0
  189. package/src/config/schemas/memory-storage.ts +1 -1
  190. package/src/config/schemas/memory.ts +4 -0
  191. package/src/config/schemas/services.ts +8 -6
  192. package/src/config/skills.ts +50 -4
  193. package/src/contacts/contact-store.ts +13 -6
  194. package/src/contacts/contacts-write.ts +0 -1
  195. package/src/context/window-manager.ts +13 -2
  196. package/src/daemon/conversation-agent-loop-handlers.ts +54 -8
  197. package/src/daemon/conversation-agent-loop.ts +127 -20
  198. package/src/daemon/conversation-attachments.ts +18 -36
  199. package/src/daemon/conversation-error.ts +2 -1
  200. package/src/daemon/conversation-history.ts +18 -4
  201. package/src/daemon/conversation-lifecycle.ts +50 -16
  202. package/src/daemon/conversation-messaging.ts +70 -26
  203. package/src/daemon/conversation-process.ts +58 -34
  204. package/src/daemon/conversation-runtime-assembly.ts +22 -38
  205. package/src/daemon/conversation-slash.ts +121 -256
  206. package/src/daemon/conversation-surfaces.ts +170 -24
  207. package/src/daemon/conversation-tool-setup.ts +0 -6
  208. package/src/daemon/conversation-workspace.ts +21 -1
  209. package/src/daemon/conversation.ts +69 -30
  210. package/src/daemon/first-greeting.ts +35 -0
  211. package/src/daemon/handlers/config-embeddings.ts +156 -0
  212. package/src/daemon/handlers/config-model.ts +62 -26
  213. package/src/daemon/handlers/conversations.ts +0 -23
  214. package/src/daemon/handlers/identity.ts +12 -1
  215. package/src/daemon/handlers/recording.ts +26 -21
  216. package/src/daemon/host-cu-proxy.ts +2 -2
  217. package/src/daemon/lifecycle.ts +115 -65
  218. package/src/daemon/message-protocol.ts +3 -0
  219. package/src/daemon/message-types/conversations.ts +18 -0
  220. package/src/daemon/message-types/messages.ts +1 -0
  221. package/src/daemon/message-types/shared.ts +2 -0
  222. package/src/daemon/message-types/surfaces.ts +2 -0
  223. package/src/daemon/message-types/upgrades.ts +23 -0
  224. package/src/daemon/server.ts +83 -12
  225. package/src/daemon/shutdown-handlers.ts +8 -5
  226. package/src/daemon/startup-error.ts +9 -0
  227. package/src/daemon/tool-side-effects.ts +11 -28
  228. package/src/events/tool-permission-telemetry-listener.ts +1 -3
  229. package/src/followups/followup-store.ts +47 -1
  230. package/src/instrument.ts +0 -4
  231. package/src/media/app-icon-generator.ts +2 -2
  232. package/src/memory/app-git-service.ts +28 -16
  233. package/src/memory/app-store.ts +230 -41
  234. package/src/memory/archive-store.ts +400 -0
  235. package/src/memory/attachments-store.ts +558 -130
  236. package/src/memory/brief-formatting.ts +33 -0
  237. package/src/memory/brief-open-loops.ts +266 -0
  238. package/src/memory/brief-time.ts +161 -0
  239. package/src/memory/brief.ts +75 -0
  240. package/src/memory/conversation-attention-store.ts +70 -0
  241. package/src/memory/conversation-crud.ts +591 -8
  242. package/src/memory/conversation-directories.ts +125 -0
  243. package/src/memory/conversation-disk-view.ts +390 -0
  244. package/src/memory/conversation-key-store.ts +17 -5
  245. package/src/memory/conversation-queries.ts +5 -1
  246. package/src/memory/conversation-title-service.ts +21 -49
  247. package/src/memory/db-init.ts +40 -0
  248. package/src/memory/embedding-backend.ts +42 -53
  249. package/src/memory/embedding-gemini.test.ts +4 -4
  250. package/src/memory/embedding-local.ts +1 -3
  251. package/src/memory/embedding-ollama.ts +1 -3
  252. package/src/memory/embedding-openai.ts +1 -3
  253. package/src/memory/indexer.ts +114 -21
  254. package/src/memory/items-extractor.ts +42 -13
  255. package/src/memory/job-handlers/conversation-starters.ts +6 -1
  256. package/src/memory/job-handlers/embedding.test.ts +2 -4
  257. package/src/memory/job-handlers/embedding.ts +83 -0
  258. package/src/memory/job-utils.ts +1 -1
  259. package/src/memory/jobs-store.ts +6 -0
  260. package/src/memory/jobs-worker.ts +12 -0
  261. package/src/memory/llm-request-log-store.ts +100 -1
  262. package/src/memory/migrations/102-alter-table-columns.ts +5 -0
  263. package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
  264. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
  265. package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
  266. package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
  267. package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
  268. package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
  269. package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
  270. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
  271. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
  272. package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
  273. package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
  274. package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
  275. package/src/memory/migrations/185-memory-brief-state.ts +52 -0
  276. package/src/memory/migrations/186-memory-archive.ts +109 -0
  277. package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
  278. package/src/memory/migrations/index.ts +10 -0
  279. package/src/memory/migrations/registry.ts +13 -0
  280. package/src/memory/qdrant-client.ts +23 -4
  281. package/src/memory/reducer-store.ts +271 -0
  282. package/src/memory/reducer-types.ts +99 -0
  283. package/src/memory/reducer.ts +453 -0
  284. package/src/memory/retriever.test.ts +601 -2
  285. package/src/memory/retriever.ts +85 -9
  286. package/src/memory/schema/conversations.ts +9 -0
  287. package/src/memory/schema/index.ts +2 -0
  288. package/src/memory/schema/infrastructure.ts +13 -7
  289. package/src/memory/schema/memory-archive.ts +121 -0
  290. package/src/memory/schema/memory-brief.ts +55 -0
  291. package/src/memory/schema/oauth.ts +6 -0
  292. package/src/memory/search/semantic.ts +17 -4
  293. package/src/messaging/providers/gmail/mime-builder.ts +3 -1
  294. package/src/notifications/copy-composer.ts +26 -0
  295. package/src/notifications/decision-engine.ts +14 -1
  296. package/src/notifications/emit-signal.ts +1 -1
  297. package/src/notifications/signal.ts +36 -0
  298. package/src/oauth/byo-connection.test.ts +1 -45
  299. package/src/oauth/byo-connection.ts +2 -8
  300. package/src/oauth/connect-orchestrator.ts +15 -11
  301. package/src/oauth/connection-resolver.test.ts +191 -0
  302. package/src/oauth/connection-resolver.ts +66 -38
  303. package/src/oauth/connection.ts +0 -1
  304. package/src/oauth/oauth-store.ts +99 -47
  305. package/src/oauth/platform-connection.test.ts +0 -1
  306. package/src/oauth/platform-connection.ts +11 -3
  307. package/src/oauth/seed-providers.ts +78 -3
  308. package/src/oauth/token-persistence.ts +16 -10
  309. package/src/permissions/checker.ts +160 -14
  310. package/src/permissions/defaults.ts +14 -0
  311. package/src/prompts/templates/BOOTSTRAP.md +2 -0
  312. package/src/providers/anthropic/client.ts +8 -1
  313. package/src/providers/failover.ts +4 -1
  314. package/src/providers/gemini/client.ts +50 -0
  315. package/src/providers/model-catalog.ts +92 -0
  316. package/src/providers/model-intents.ts +29 -20
  317. package/src/providers/openai/client.ts +49 -0
  318. package/src/providers/types.ts +2 -0
  319. package/src/runtime/access-request-helper.ts +16 -7
  320. package/src/runtime/auth/credential-service.ts +3 -1
  321. package/src/runtime/auth/route-policy.ts +14 -1
  322. package/src/runtime/btw-sidechain.ts +101 -0
  323. package/src/runtime/channel-reply-delivery.ts +17 -1
  324. package/src/runtime/http-router.ts +3 -1
  325. package/src/runtime/http-server.ts +196 -141
  326. package/src/runtime/http-types.ts +1 -0
  327. package/src/runtime/migrations/vbundle-builder.ts +5 -1
  328. package/src/runtime/routes/access-request-decision.ts +41 -0
  329. package/src/runtime/routes/app-management-routes.ts +6 -3
  330. package/src/runtime/routes/app-routes.ts +7 -3
  331. package/src/runtime/routes/approval-routes.ts +1 -0
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
  333. package/src/runtime/routes/attachment-routes.ts +45 -15
  334. package/src/runtime/routes/btw-routes.ts +21 -61
  335. package/src/runtime/routes/conversation-management-routes.ts +74 -0
  336. package/src/runtime/routes/conversation-query-routes.ts +187 -10
  337. package/src/runtime/routes/conversation-routes.ts +269 -28
  338. package/src/runtime/routes/conversation-starter-routes.ts +9 -11
  339. package/src/runtime/routes/diagnostics-routes.ts +1 -0
  340. package/src/runtime/routes/identity-routes.ts +2 -35
  341. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
  342. package/src/runtime/routes/llm-context-normalization.ts +1212 -0
  343. package/src/runtime/routes/log-export-routes.ts +3 -0
  344. package/src/runtime/routes/memory-item-routes.test.ts +34 -0
  345. package/src/runtime/routes/memory-item-routes.ts +94 -5
  346. package/src/runtime/routes/migration-routes.ts +4 -1
  347. package/src/runtime/routes/oauth-apps.ts +291 -0
  348. package/src/runtime/routes/secret-routes.ts +30 -1
  349. package/src/runtime/routes/settings-routes.ts +14 -0
  350. package/src/runtime/routes/surface-action-routes.ts +68 -1
  351. package/src/runtime/routes/trace-event-routes.ts +4 -1
  352. package/src/schedule/schedule-store.ts +30 -21
  353. package/src/security/secure-keys.ts +21 -0
  354. package/src/signals/bash.ts +1 -1
  355. package/src/skills/inline-command-expansions.ts +204 -0
  356. package/src/skills/inline-command-render.ts +127 -0
  357. package/src/skills/inline-command-runner.ts +242 -0
  358. package/src/skills/transitive-version-hash.ts +88 -0
  359. package/src/swarm/backend-claude-code.ts +3 -6
  360. package/src/tasks/task-store.ts +43 -1
  361. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  362. package/src/telemetry/usage-telemetry-reporter.ts +3 -1
  363. package/src/tools/AGENTS.md +6 -10
  364. package/src/tools/apps/executors.ts +17 -232
  365. package/src/tools/claude-code/claude-code.ts +2 -3
  366. package/src/tools/credentials/vault.ts +7 -12
  367. package/src/tools/host-filesystem/read.ts +13 -10
  368. package/src/tools/network/__tests__/web-search.test.ts +4 -2
  369. package/src/tools/permission-checker.ts +8 -1
  370. package/src/tools/schedule/list.ts +2 -7
  371. package/src/tools/schema-transforms.ts +5 -0
  372. package/src/tools/shared/filesystem/format-diff.ts +2 -7
  373. package/src/tools/skills/execute.ts +1 -1
  374. package/src/tools/skills/load.ts +140 -6
  375. package/src/tools/tool-manifest.ts +0 -6
  376. package/src/tools/ui-surface/definitions.ts +2 -2
  377. package/src/util/device-id.ts +28 -5
  378. package/src/util/platform.ts +24 -0
  379. package/src/util/pricing.ts +1 -0
  380. package/src/util/retry.ts +1 -3
  381. package/src/workspace/migrations/003-seed-device-id.ts +3 -4
  382. package/src/workspace/migrations/006-services-config.ts +5 -0
  383. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
  384. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
  385. package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
  386. package/src/workspace/migrations/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
  387. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
  388. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
  389. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
  390. package/src/workspace/migrations/registry.ts +11 -1
  391. package/src/workspace/top-level-renderer.ts +12 -0
  392. package/src/__tests__/asset-materialize-tool.test.ts +0 -523
  393. package/src/__tests__/asset-search-tool.test.ts +0 -536
  394. package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
  395. package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
  396. package/src/__tests__/media-visibility-policy.test.ts +0 -190
  397. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
  398. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
  399. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
  400. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
  401. package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
  402. package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
  403. package/src/daemon/media-visibility-policy.ts +0 -59
  404. package/src/tools/assets/materialize.ts +0 -248
  405. package/src/tools/assets/search.ts +0 -400
@@ -1,55 +1,22 @@
1
1
  import { getConfig } from "../../../../config/loader.js";
2
- import {
3
- type AttachmentContext,
4
- isAttachmentVisible,
5
- } from "../../../../daemon/media-visibility-policy.js";
6
2
  import {
7
3
  generateImage,
8
4
  type ImageGenCredentials,
9
5
  mapGeminiError,
10
6
  } from "../../../../media/gemini-image-service.js";
11
- import {
12
- getAttachmentContent,
13
- getAttachmentsByIds,
14
- } from "../../../../memory/attachments-store.js";
15
- import { getConversationType } from "../../../../memory/conversation-crud.js";
7
+ import { getFilePathBySourcePath } from "../../../../memory/attachments-store.js";
16
8
  import {
17
9
  buildManagedBaseUrl,
18
10
  resolveManagedProxyContext,
19
11
  } from "../../../../providers/managed-proxy/context.js";
20
12
  import type { ImageContent } from "../../../../providers/types.js";
21
13
  import { getProviderKeyAsync } from "../../../../security/secure-keys.js";
22
- import { getAttachmentSourceConversations } from "../../../../tools/assets/search.js";
14
+ import { sandboxPolicy } from "../../../../tools/shared/filesystem/path-policy.js";
23
15
  import type {
24
16
  ToolContext,
25
17
  ToolExecutionResult,
26
18
  } from "../../../../tools/types.js";
27
19
 
28
- /**
29
- * Check whether an attachment is visible from the given context.
30
- * Mirrors the logic in tools/assets/search.ts:isAttachmentVisibleFromContext.
31
- */
32
- function isAttachmentAccessible(
33
- attachmentId: string,
34
- currentContext: AttachmentContext,
35
- ): boolean {
36
- const sources = getAttachmentSourceConversations(attachmentId);
37
- if (sources.length === 0) {
38
- return true; // orphan attachments are universally visible
39
- }
40
- const hasStandard = sources.some((s) => s.conversationType !== "private");
41
- if (hasStandard) {
42
- return true;
43
- }
44
- // All sources are private - visible only if the caller is in one of those conversations
45
- return sources.some((s) =>
46
- isAttachmentVisible(
47
- { conversationId: s.conversationId, isPrivate: true },
48
- currentContext,
49
- ),
50
- );
51
- }
52
-
53
20
  export async function run(
54
21
  input: Record<string, unknown>,
55
22
  context: ToolContext,
@@ -87,54 +54,60 @@ export async function run(
87
54
 
88
55
  const prompt = input.prompt as string;
89
56
  const mode = (input.mode as "generate" | "edit") ?? "generate";
90
- const attachmentIds = input.attachment_ids as string[] | undefined;
57
+ const sourcePaths = input.source_paths as string[] | undefined;
91
58
  const model =
92
59
  (input.model as string | undefined) ??
93
60
  config.services["image-generation"].model;
94
61
  const variants = input.variants as number | undefined;
95
62
 
96
- // Resolve source images from attachments for edit mode
63
+ // Resolve source images from file paths (sandboxed to workingDir, edit mode only)
97
64
  let sourceImages: Array<{ mimeType: string; dataBase64: string }> | undefined;
98
65
 
99
- if (attachmentIds && attachmentIds.length > 0) {
100
- const attachments = getAttachmentsByIds(attachmentIds);
101
-
102
- // Build visibility context for the current conversation
103
- const conversationType = getConversationType(context.conversationId);
104
- const currentContext: AttachmentContext = {
105
- conversationId: context.conversationId,
106
- isPrivate: conversationType === "private",
107
- };
108
-
109
- // Filter to only visible attachments using their originating context
110
- const visibleAttachments = attachments.filter((att) =>
111
- isAttachmentAccessible(att.id, currentContext),
112
- );
113
-
114
- if (visibleAttachments.length === 0 && attachmentIds.length > 0) {
66
+ if (mode === "edit" && sourcePaths && sourcePaths.length > 0) {
67
+ const errors: string[] = [];
68
+ const validPathImages: Array<{ mimeType: string; dataBase64: string }> = [];
69
+ for (const filePath of sourcePaths) {
70
+ let resolvedPath: string;
71
+ const pathCheck = sandboxPolicy(filePath, context.workingDir);
72
+ if (!pathCheck.ok) {
73
+ // Fallback: if the source path is outside the sandbox (e.g. an image
74
+ // attached from ~/Desktop), check if the attachment store has a
75
+ // workspace-internal copy stored under its original source_path.
76
+ const storedPath = getFilePathBySourcePath(
77
+ filePath,
78
+ context.conversationId,
79
+ );
80
+ if (!storedPath) {
81
+ errors.push(pathCheck.error);
82
+ continue;
83
+ }
84
+ const fallbackCheck = sandboxPolicy(storedPath, context.workingDir);
85
+ if (!fallbackCheck.ok) {
86
+ errors.push(pathCheck.error);
87
+ continue;
88
+ }
89
+ resolvedPath = fallbackCheck.resolved;
90
+ } else {
91
+ resolvedPath = pathCheck.resolved;
92
+ }
93
+ const file = Bun.file(resolvedPath);
94
+ if (!(await file.exists())) {
95
+ errors.push(`File not found: ${filePath}`);
96
+ continue;
97
+ }
98
+ const buffer = Buffer.from(await file.arrayBuffer());
99
+ validPathImages.push({
100
+ mimeType: file.type,
101
+ dataBase64: buffer.toString("base64"),
102
+ });
103
+ }
104
+ if (validPathImages.length === 0) {
115
105
  return {
116
- content:
117
- "None of the specified attachments could be found or are accessible.",
106
+ content: `None of the specified file paths could be read.\n${errors.join("\n")}`,
118
107
  isError: true,
119
108
  };
120
109
  }
121
-
122
- sourceImages = visibleAttachments
123
- .map((att) => {
124
- let buffer: Buffer | null | undefined;
125
- try {
126
- buffer = getAttachmentContent(att.id);
127
- } catch {
128
- // File-backed attachment may point to a missing or unreadable file
129
- return undefined;
130
- }
131
- if (!buffer) return undefined;
132
- return {
133
- mimeType: att.mimeType,
134
- dataBase64: buffer.toString("base64"),
135
- };
136
- })
137
- .filter((img): img is NonNullable<typeof img> => img !== undefined);
110
+ sourceImages = validPathImages;
138
111
  }
139
112
 
140
113
  try {
@@ -4,7 +4,7 @@ import {
4
4
  getKeyframesForAsset,
5
5
  getMediaAssetById,
6
6
  } from "../../../../memory/media-store.js";
7
- import { getSecureKeyAsync } from "../../../../security/secure-keys.js";
7
+ import { getProviderKeyAsync } from "../../../../security/secure-keys.js";
8
8
  import type {
9
9
  ToolContext,
10
10
  ToolExecutionResult,
@@ -31,7 +31,7 @@ export async function run(
31
31
 
32
32
  let openaiApiKey: string | undefined;
33
33
  if (includeAudio && (!transcriptionMode || transcriptionMode === "api")) {
34
- openaiApiKey = (await getSecureKeyAsync("openai")) ?? undefined;
34
+ openaiApiKey = await getProviderKeyAsync("openai");
35
35
  }
36
36
 
37
37
  const options: PreprocessOptions = {
@@ -137,5 +137,5 @@ export async function getProviderConnection(
137
137
  account?: string,
138
138
  ): Promise<OAuthConnection | string> {
139
139
  if (await provider.isConnected?.()) return "";
140
- return resolveOAuthConnection(provider.credentialService, account);
140
+ return resolveOAuthConnection(provider.credentialService, { account });
141
141
  }
@@ -4,6 +4,7 @@ import {
4
4
  saveRawConfig,
5
5
  setNestedValue,
6
6
  } from "../../../../config/loader.js";
7
+ import { VALID_CONVERSATION_TIMEOUTS } from "../../../../config/schemas/elevenlabs.js";
7
8
  import { normalizeActivationKey } from "../../../../daemon/handlers/config-voice.js";
8
9
  import type {
9
10
  ToolContext,
@@ -29,7 +30,7 @@ type VoiceSettingName = keyof typeof VOICE_SETTINGS;
29
30
 
30
31
  const VALID_SETTINGS = Object.keys(VOICE_SETTINGS) as VoiceSettingName[];
31
32
 
32
- const VALID_TIMEOUTS = [5, 10, 15, 30, 60];
33
+ const VALID_TIMEOUTS: readonly number[] = VALID_CONVERSATION_TIMEOUTS;
33
34
 
34
35
  const FRIENDLY_NAMES: Record<VoiceSettingName, string> = {
35
36
  activation_key: "PTT activation key",
@@ -132,12 +133,14 @@ export async function run(
132
133
  const meta = VOICE_SETTINGS[setting as VoiceSettingName];
133
134
  const friendlyName = FRIENDLY_NAMES[setting as VoiceSettingName];
134
135
 
135
- // Send client_settings_update message to write to UserDefaults
136
+ // Send client_settings_update message to write to UserDefaults.
137
+ // Always stringify the value — Swift's ClientSettingsUpdate.value is typed
138
+ // as String, so a bare JSON number would fail to decode.
136
139
  if (context.sendToClient) {
137
140
  context.sendToClient({
138
141
  type: "client_settings_update",
139
142
  key: meta.userDefaultsKey,
140
- value: validation.coerced,
143
+ value: String(validation.coerced),
141
144
  });
142
145
  }
143
146
 
@@ -150,6 +153,19 @@ export async function run(
150
153
  invalidateConfigCache();
151
154
  }
152
155
 
156
+ // For conversation_timeout, persist to the config file
157
+ // (elevenlabs.conversationTimeoutSeconds).
158
+ if (setting === "conversation_timeout") {
159
+ const raw = loadRawConfig();
160
+ setNestedValue(
161
+ raw,
162
+ "elevenlabs.conversationTimeoutSeconds",
163
+ validation.coerced,
164
+ );
165
+ saveRawConfig(raw);
166
+ invalidateConfigCache();
167
+ }
168
+
153
169
  return {
154
170
  content: `${friendlyName} updated to ${JSON.stringify(
155
171
  validation.coerced,
@@ -7,7 +7,7 @@ metadata:
7
7
  display-name: "Skill Management"
8
8
  ---
9
9
 
10
- Manage the lifecycle of custom managed skills in `~/.vellum/workspace/skills`.
10
+ Manage the lifecycle of custom managed skills in `{workspaceDir}/skills`.
11
11
 
12
12
  ## Capabilities
13
13
 
@@ -3,7 +3,7 @@
3
3
  "tools": [
4
4
  {
5
5
  "name": "scaffold_managed_skill",
6
- "description": "Create or update a managed skill in ~/.vellum/workspace/skills. The skill becomes available for skill_load immediately. Never persist a skill without explicit user consent. Before persisting, test the snippet: write to a temp file with bash and run with `bun run /tmp/vellum-eval/snippet.ts`. Iterate up to 3 attempts, then ask the user. Clean up temp files after. Do not use file_write for temp files outside the working directory. After a skill is written, the next turn may run in a recreated session due to file-watcher eviction - continue normally.",
6
+ "description": "Create or update a managed skill in {workspaceDir}/skills. The skill becomes available for skill_load immediately. Never persist a skill without explicit user consent. Before persisting, test the snippet: write to a temp file with bash and run with `bun run /tmp/vellum-eval/snippet.ts`. Iterate up to 3 attempts, then ask the user. Clean up temp files after. Do not use file_write for temp files outside the working directory. After a skill is written, the next turn may run in a recreated conversation due to file-watcher eviction - continue normally.",
7
7
  "category": "skills",
8
8
  "risk": "high",
9
9
  "input_schema": {
@@ -54,7 +54,7 @@
54
54
  },
55
55
  {
56
56
  "name": "delete_managed_skill",
57
- "description": "Delete a managed skill from ~/.vellum/workspace/skills and remove it from the SKILLS.md index. Never delete a skill without explicit user confirmation. After deletion, the next turn may run in a recreated session due to file-watcher eviction - continue normally.",
57
+ "description": "Delete a managed skill from {workspaceDir}/skills and remove it from the SKILLS.md index. Never delete a skill without explicit user confirmation. After deletion, the next turn may run in a recreated conversation due to file-watcher eviction - continue normally.",
58
58
  "category": "skills",
59
59
  "risk": "high",
60
60
  "input_schema": {
@@ -2,8 +2,8 @@
2
2
  * Shared utilities for slack skill tools.
3
3
  */
4
4
 
5
- import type { OAuthConnection } from "../../../../oauth/connection.js";
6
- import { resolveOAuthConnection } from "../../../../oauth/connection-resolver.js";
5
+ import { credentialKey } from "../../../../security/credential-key.js";
6
+ import { getSecureKeyAsync } from "../../../../security/secure-keys.js";
7
7
  import type { ToolExecutionResult } from "../../../../tools/types.js";
8
8
 
9
9
  export function ok(content: string): ToolExecutionResult {
@@ -14,6 +14,21 @@ export function err(message: string): ToolExecutionResult {
14
14
  return { content: message, isError: true };
15
15
  }
16
16
 
17
- export async function getSlackConnection(): Promise<OAuthConnection> {
18
- return resolveOAuthConnection("integration:slack");
17
+ /**
18
+ * Resolve the Slack bot token from credential storage.
19
+ *
20
+ * Slack uses direct bot/app tokens (Socket Mode), not OAuth connections.
21
+ * The client functions accept `OAuthConnection | string`, so we return the
22
+ * raw token string.
23
+ */
24
+ export async function getSlackConnection(): Promise<string> {
25
+ const token = await getSecureKeyAsync(
26
+ credentialKey("slack_channel", "bot_token"),
27
+ );
28
+ if (!token) {
29
+ throw new Error(
30
+ "No Slack bot token found. Configure Slack with: assistant credentials set --service slack_channel --field bot_token",
31
+ );
32
+ }
33
+ return token;
19
34
  }
@@ -1,7 +1,6 @@
1
1
  import { getConfig } from "../../../../config/loader.js";
2
2
  import * as slack from "../../../../messaging/providers/slack/client.js";
3
3
  import type { SlackConversation } from "../../../../messaging/providers/slack/types.js";
4
- import type { OAuthConnection } from "../../../../oauth/connection.js";
5
4
  import type {
6
5
  ToolContext,
7
6
  ToolExecutionResult,
@@ -28,7 +27,7 @@ interface ChannelDigest {
28
27
  const userNameCache = new Map<string, string>();
29
28
 
30
29
  async function resolveUserName(
31
- connection: OAuthConnection,
30
+ connection: string,
32
31
  userId: string,
33
32
  ): Promise<string> {
34
33
  if (!userId) return "unknown";
@@ -50,7 +49,7 @@ async function resolveUserName(
50
49
  }
51
50
 
52
51
  async function scanChannel(
53
- connection: OAuthConnection,
52
+ connection: string,
54
53
  conv: SlackConversation,
55
54
  oldestTs: string,
56
55
  includeThreads: boolean,
@@ -22,7 +22,7 @@ If the user says "local", "offline", "private", or "on-device" → use `local`.
22
22
 
23
23
  ## Usage Notes
24
24
 
25
- - The tool accepts either a `file_path` (absolute path to a local file) or an `attachment_id` (for uploaded attachments). Prefer `file_path` when the user references a file on disk.
25
+ - The tool accepts a `file_path` (absolute path to a local audio or video file) to transcribe.
26
26
  - Supported formats: any video (mp4, mov, etc.) or audio (mp3, wav, m4a, etc.) file.
27
27
  - For video files, audio is automatically extracted via ffmpeg before transcription.
28
28
  - The API mode has a 25MB per-request limit - large files are automatically split into chunks.
@@ -3,7 +3,7 @@
3
3
  "tools": [
4
4
  {
5
5
  "name": "transcribe_media",
6
- "description": "Transcribe an audio or video file using Whisper. Provide either a file_path to a local file or an attachment_id for an uploaded attachment. Set mode to 'api' (OpenAI cloud) or 'local' (whisper.cpp on-device). Ask the user which mode they prefer before calling.",
6
+ "description": "Transcribe an audio or video file using Whisper. Provide a file_path to the media file. Set mode to 'api' (OpenAI cloud) or 'local' (whisper.cpp on-device). Ask the user which mode they prefer before calling.",
7
7
  "category": "transcribe",
8
8
  "risk": "low",
9
9
  "input_schema": {
@@ -13,10 +13,6 @@
13
13
  "type": "string",
14
14
  "description": "Absolute path to a local audio or video file to transcribe"
15
15
  },
16
- "attachment_id": {
17
- "type": "string",
18
- "description": "The ID of an attached audio or video file to transcribe"
19
- },
20
16
  "mode": {
21
17
  "type": "string",
22
18
  "enum": ["api", "local"],
@@ -27,7 +23,7 @@
27
23
  "description": "Brief non-technical explanation of why this tool is being called"
28
24
  }
29
25
  },
30
- "required": ["mode"]
26
+ "required": ["mode", "file_path"]
31
27
  },
32
28
  "executor": "tools/transcribe-media.ts",
33
29
  "execution_target": "host"
@@ -10,10 +10,6 @@ import {
10
10
  import { tmpdir } from "node:os";
11
11
  import { extname, join } from "node:path";
12
12
 
13
- import {
14
- getAttachmentsByIds,
15
- getFilePathForAttachment,
16
- } from "../../../../memory/attachments-store.js";
17
13
  import { getProviderKeyAsync } from "../../../../security/secure-keys.js";
18
14
  import type {
19
15
  ToolContext,
@@ -123,84 +119,31 @@ async function splitAudio(
123
119
 
124
120
  async function resolveSource(
125
121
  input: Record<string, unknown>,
126
- ): Promise<
127
- | { inputPath: string; isVideo: boolean; tempFile: string | null }
128
- | ToolExecutionResult
129
- > {
122
+ ): Promise<{ inputPath: string; isVideo: boolean } | ToolExecutionResult> {
130
123
  const filePath = input.file_path as string | undefined;
131
- const attachmentId = input.attachment_id as string | undefined;
132
124
 
133
- if (filePath) {
134
- try {
135
- await access(filePath);
136
- } catch {
137
- return { content: `File not found: ${filePath}`, isError: true };
138
- }
139
- const ext = extname(filePath).toLowerCase();
140
- const isVideo = VIDEO_EXTENSIONS.has(ext);
141
- const isAudio = AUDIO_EXTENSIONS.has(ext);
142
- if (!isVideo && !isAudio) {
143
- return {
144
- content: `Unsupported file type: ${ext}. Only video and audio files can be transcribed.`,
145
- isError: true,
146
- };
147
- }
148
- return { inputPath: filePath, isVideo, tempFile: null };
125
+ if (!filePath) {
126
+ return {
127
+ content: "Provide a file_path to the audio or video file to transcribe.",
128
+ isError: true,
129
+ };
149
130
  }
150
131
 
151
- if (attachmentId) {
152
- const attachments = getAttachmentsByIds([attachmentId]);
153
- if (attachments.length === 0) {
154
- return {
155
- content: `Attachment not found: ${attachmentId}`,
156
- isError: true,
157
- };
158
- }
159
- const attachment = attachments[0];
160
- const mime = attachment.mimeType;
161
- if (!mime.startsWith("video/") && !mime.startsWith("audio/")) {
162
- return {
163
- content: `Unsupported file type: ${mime}. Only video and audio files can be transcribed.`,
164
- isError: true,
165
- };
166
- }
167
- // Check if this is a file-backed attachment (large files stored on disk)
168
- const onDiskPath = getFilePathForAttachment(attachment.id);
169
- if (onDiskPath) {
170
- // File-backed attachment - use the on-disk file directly
171
- try {
172
- await access(onDiskPath);
173
- } catch {
174
- return {
175
- content: `Attachment file not found on disk: ${onDiskPath}`,
176
- isError: true,
177
- };
178
- }
179
- return {
180
- inputPath: onDiskPath,
181
- isVideo: mime.startsWith("video/"),
182
- tempFile: null,
183
- };
184
- }
185
-
186
- // Inline attachment - decode base64 to a temp file
187
- const ext = mime.startsWith("video/") ? ".mp4" : ".m4a";
188
- const tempPath = join(
189
- tmpdir(),
190
- `vellum-transcribe-in-${randomUUID()}${ext}`,
191
- );
192
- await writeFile(tempPath, Buffer.from(attachment.dataBase64, "base64"));
132
+ try {
133
+ await access(filePath);
134
+ } catch {
135
+ return { content: `File not found: ${filePath}`, isError: true };
136
+ }
137
+ const ext = extname(filePath).toLowerCase();
138
+ const isVideo = VIDEO_EXTENSIONS.has(ext);
139
+ const isAudio = AUDIO_EXTENSIONS.has(ext);
140
+ if (!isVideo && !isAudio) {
193
141
  return {
194
- inputPath: tempPath,
195
- isVideo: mime.startsWith("video/"),
196
- tempFile: tempPath,
142
+ content: `Unsupported file type: ${ext}. Only video and audio files can be transcribed.`,
143
+ isError: true,
197
144
  };
198
145
  }
199
-
200
- return {
201
- content: "Provide either file_path or attachment_id.",
202
- isError: true,
203
- };
146
+ return { inputPath: filePath, isVideo };
204
147
  }
205
148
 
206
149
  /** Convert source to 16kHz mono WAV for consistent processing. */
@@ -462,7 +405,7 @@ export async function run(
462
405
  const source = await resolveSource(input);
463
406
  if ("isError" in source) return source;
464
407
 
465
- const { inputPath, isVideo, tempFile } = source;
408
+ const { inputPath, isVideo } = source;
466
409
  let wavPath: string | null = null;
467
410
 
468
411
  try {
@@ -487,13 +430,6 @@ export async function run(
487
430
  isError: true,
488
431
  };
489
432
  } finally {
490
- if (tempFile) {
491
- try {
492
- await unlink(tempFile);
493
- } catch {
494
- /* ignore */
495
- }
496
- }
497
433
  if (wavPath) {
498
434
  try {
499
435
  await unlink(wavPath);
@@ -20,14 +20,8 @@ import * as acpStatus from "./bundled-skills/acp/tools/acp-status.js";
20
20
  // ── app-builder ────────────────────────────────────────────────────────────────
21
21
  import * as appCreate from "./bundled-skills/app-builder/tools/app-create.js";
22
22
  import * as appDelete from "./bundled-skills/app-builder/tools/app-delete.js";
23
- import * as appFileEdit from "./bundled-skills/app-builder/tools/app-file-edit.js";
24
- import * as appFileList from "./bundled-skills/app-builder/tools/app-file-list.js";
25
- import * as appFileRead from "./bundled-skills/app-builder/tools/app-file-read.js";
26
- import * as appFileWrite from "./bundled-skills/app-builder/tools/app-file-write.js";
27
23
  import * as appGenerateIcon from "./bundled-skills/app-builder/tools/app-generate-icon.js";
28
- import * as appList from "./bundled-skills/app-builder/tools/app-list.js";
29
- import * as appQuery from "./bundled-skills/app-builder/tools/app-query.js";
30
- import * as appUpdate from "./bundled-skills/app-builder/tools/app-update.js";
24
+ import * as appRefresh from "./bundled-skills/app-builder/tools/app-refresh.js";
31
25
  // ── browser ────────────────────────────────────────────────────────────────────
32
26
  import * as browserClick from "./bundled-skills/browser/tools/browser-click.js";
33
27
  import * as browserClose from "./bundled-skills/browser/tools/browser-close.js";
@@ -193,14 +187,8 @@ export const bundledToolRegistry = new Map<string, SkillToolScript>([
193
187
 
194
188
  // app-builder
195
189
  ["app-builder:tools/app-create.ts", appCreate],
196
- ["app-builder:tools/app-list.ts", appList],
197
- ["app-builder:tools/app-query.ts", appQuery],
198
- ["app-builder:tools/app-update.ts", appUpdate],
199
190
  ["app-builder:tools/app-delete.ts", appDelete],
200
- ["app-builder:tools/app-file-list.ts", appFileList],
201
- ["app-builder:tools/app-file-read.ts", appFileRead],
202
- ["app-builder:tools/app-file-edit.ts", appFileEdit],
203
- ["app-builder:tools/app-file-write.ts", appFileWrite],
191
+ ["app-builder:tools/app-refresh.ts", appRefresh],
204
192
  ["app-builder:tools/app-generate-icon.ts", appGenerateIcon],
205
193
 
206
194
  // browser
@@ -25,6 +25,14 @@
25
25
  "description": "Show the Contacts tab in Settings for viewing and managing contacts",
26
26
  "defaultEnabled": true
27
27
  },
28
+ {
29
+ "id": "custom-inference-provider",
30
+ "scope": "macos",
31
+ "key": "custom_inference_provider_enabled",
32
+ "label": "Custom Inference Provider",
33
+ "description": "Allow selecting a specific LLM provider and model for inference in Your Own mode",
34
+ "defaultEnabled": false
35
+ },
28
36
  {
29
37
  "id": "email-channel",
30
38
  "scope": "assistant",
@@ -249,6 +257,14 @@
249
257
  "description": "Show the Google OAuth service card in Models & Services settings",
250
258
  "defaultEnabled": false
251
259
  },
260
+ {
261
+ "id": "settings-embedding-provider",
262
+ "scope": "assistant",
263
+ "key": "feature_flags.settings-embedding-provider.enabled",
264
+ "label": "Embedding Provider Settings",
265
+ "description": "Show the Embedding service card in Models & Services settings",
266
+ "defaultEnabled": false
267
+ },
252
268
  {
253
269
  "id": "quick-input",
254
270
  "scope": "macos",
@@ -264,6 +280,14 @@
264
280
  "label": "Expand Completed Steps",
265
281
  "description": "Auto-expand completed tool call step groups instead of showing them collapsed",
266
282
  "defaultEnabled": false
283
+ },
284
+ {
285
+ "id": "inline-skill-commands",
286
+ "scope": "assistant",
287
+ "key": "feature_flags.inline-skill-commands.enabled",
288
+ "label": "Inline Skill Command Expansion",
289
+ "description": "Enable secure inline skill command expansion via !`command` syntax, with version-pinned approval and sandboxed execution at skill load time",
290
+ "defaultEnabled": true
267
291
  }
268
292
  ]
269
293
  }
@@ -29,6 +29,7 @@ export const API_KEY_PROVIDERS = [
29
29
  "fireworks",
30
30
  "openrouter",
31
31
  "brave",
32
+ "elevenlabs",
32
33
  "perplexity",
33
34
  ] as const;
34
35
 
@@ -118,6 +119,66 @@ function deleteNestedKey(
118
119
  }
119
120
  }
120
121
 
122
+ /**
123
+ * Deprecated config fields that have been removed. Each entry maps a
124
+ * dot-separated path to the deprecation message shown to the user.
125
+ */
126
+ const DEPRECATED_FIELDS: Record<string, string> = {
127
+ "rateLimit.maxTokensPerSession":
128
+ "rateLimit.maxTokensPerSession has been removed and is no longer enforced. " +
129
+ "Per-session token budget tracking is no longer supported. " +
130
+ "The field will be removed from your config file.",
131
+ };
132
+
133
+ /**
134
+ * Check for deprecated config fields, log a warning for each one found,
135
+ * and strip them from both the in-memory object and the on-disk config file
136
+ * so the warning is only emitted once.
137
+ */
138
+ function warnAndStripDeprecatedFields(
139
+ fileConfig: Record<string, unknown>,
140
+ configPath: string,
141
+ ): void {
142
+ const found: string[] = [];
143
+ for (const dotPath of Object.keys(DEPRECATED_FIELDS)) {
144
+ if (getNestedValue(fileConfig, dotPath) !== undefined) {
145
+ log.warn(DEPRECATED_FIELDS[dotPath]);
146
+ found.push(dotPath);
147
+ }
148
+ }
149
+
150
+ if (found.length === 0) return;
151
+
152
+ // Strip from the in-memory object so Zod never sees them
153
+ for (const dotPath of found) {
154
+ deleteNestedKeyByDotPath(fileConfig, dotPath);
155
+ }
156
+
157
+ // Persist the cleaned config to disk so the warning doesn't repeat
158
+ try {
159
+ if (existsSync(configPath)) {
160
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
161
+ if (raw != null && typeof raw === "object" && !Array.isArray(raw)) {
162
+ for (const dotPath of found) {
163
+ deleteNestedKeyByDotPath(raw as Record<string, unknown>, dotPath);
164
+ }
165
+ writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
166
+ }
167
+ }
168
+ } catch {
169
+ // Best-effort — if the file can't be rewritten, the warning will repeat
170
+ // on next load, which is acceptable.
171
+ }
172
+ }
173
+
174
+ function deleteNestedKeyByDotPath(
175
+ obj: Record<string, unknown>,
176
+ dotPath: string,
177
+ ): void {
178
+ const keys = dotPath.split(".");
179
+ deleteNestedKey(obj, keys);
180
+ }
181
+
121
182
  /**
122
183
  * Deep-merge missing keys from `defaults` into `target`.
123
184
  * Only adds keys that do not already exist in `target`; never overwrites.
@@ -224,6 +285,10 @@ export function loadConfig(): AssistantConfig {
224
285
  configFileExisted = false;
225
286
  }
226
287
 
288
+ // Warn about and strip deprecated config fields so users know their
289
+ // settings are no longer honored rather than silently dropping them.
290
+ warnAndStripDeprecatedFields(fileConfig, configPath);
291
+
227
292
  // Validate and apply defaults via Zod schema
228
293
  const config = validateWithSchema(fileConfig);
229
294