@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
@@ -5,9 +5,11 @@ import { seedProviders } from "./oauth-store.js";
5
5
  *
6
6
  * These values are upserted into the `oauth_providers` SQLite table on
7
7
  * every startup. Only Vellum implementation fields (authUrl, tokenUrl,
8
- * tokenEndpointAuthMethod, extraParams, callbackTransport, pingUrl) are
9
- * overwritten on subsequent startups user-customizable
10
- * fields (defaultScopes, scopePolicy, userinfoUrl, baseUrl) are only
8
+ * tokenEndpointAuthMethod, userinfoUrl, extraParams, callbackTransport,
9
+ * pingUrl, managedServiceConfigKey) and display metadata (displayName,
10
+ * description, dashboardUrl, clientIdPlaceholder, requiresClientSecret)
11
+ * are overwritten on subsequent startups — user-customizable
12
+ * fields (defaultScopes, scopePolicy, baseUrl) are only
11
13
  * written on initial insert and preserved across restarts.
12
14
  *
13
15
  * Code-side behavioral fields (identityVerifier, injectionTemplates,
@@ -32,6 +34,12 @@ const PROVIDER_SEED_DATA: Record<
32
34
  };
33
35
  extraParams?: Record<string, string>;
34
36
  callbackTransport?: string;
37
+ managedServiceConfigKey?: string;
38
+ displayName: string;
39
+ description: string;
40
+ dashboardUrl: string | null;
41
+ clientIdPlaceholder: string | null;
42
+ requiresClientSecret?: boolean;
35
43
  }
36
44
  > = {
37
45
  "integration:google": {
@@ -41,6 +49,10 @@ const PROVIDER_SEED_DATA: Record<
41
49
  userinfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
42
50
  pingUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
43
51
  baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me",
52
+ displayName: "Google",
53
+ description: "Gmail, Calendar, and Contacts",
54
+ dashboardUrl: "https://console.cloud.google.com/apis/credentials",
55
+ clientIdPlaceholder: "123456789.apps.googleusercontent.com",
44
56
  defaultScopes: [
45
57
  "https://www.googleapis.com/auth/gmail.readonly",
46
58
  "https://www.googleapis.com/auth/gmail.modify",
@@ -60,6 +72,7 @@ const PROVIDER_SEED_DATA: Record<
60
72
  },
61
73
  extraParams: { access_type: "offline", prompt: "consent" },
62
74
  callbackTransport: "loopback",
75
+ managedServiceConfigKey: "google-oauth",
63
76
  },
64
77
 
65
78
  "integration:slack": {
@@ -68,6 +81,10 @@ const PROVIDER_SEED_DATA: Record<
68
81
  tokenUrl: "https://slack.com/api/oauth.v2.access",
69
82
  pingUrl: "https://slack.com/api/auth.test",
70
83
  baseUrl: "https://slack.com/api",
84
+ displayName: "Slack",
85
+ description: "Workspace messaging",
86
+ dashboardUrl: "https://api.slack.com/apps",
87
+ clientIdPlaceholder: null,
71
88
  defaultScopes: [
72
89
  "channels:read",
73
90
  "channels:history",
@@ -101,6 +118,10 @@ const PROVIDER_SEED_DATA: Record<
101
118
  tokenUrl: "https://api.notion.com/v1/oauth/token",
102
119
  pingUrl: "https://api.notion.com/v1/users/me",
103
120
  baseUrl: "https://api.notion.com",
121
+ displayName: "Notion",
122
+ description: "Pages and databases",
123
+ dashboardUrl: "https://www.notion.so/my-integrations",
124
+ clientIdPlaceholder: null,
104
125
  defaultScopes: [],
105
126
  scopePolicy: {
106
127
  allowAdditionalScopes: false,
@@ -118,6 +139,10 @@ const PROVIDER_SEED_DATA: Record<
118
139
  tokenUrl: "https://api.x.com/2/oauth2/token",
119
140
  pingUrl: "https://api.x.com/2/users/me",
120
141
  baseUrl: "https://api.x.com",
142
+ displayName: "Twitter",
143
+ description: "Posts and direct messages",
144
+ dashboardUrl: "https://developer.twitter.com/en/portal/dashboard",
145
+ clientIdPlaceholder: null,
121
146
  defaultScopes: [
122
147
  "tweet.read",
123
148
  "tweet.write",
@@ -139,6 +164,10 @@ const PROVIDER_SEED_DATA: Record<
139
164
  tokenUrl: "https://github.com/login/oauth/access_token",
140
165
  pingUrl: "https://api.github.com/user",
141
166
  baseUrl: "https://api.github.com",
167
+ displayName: "GitHub",
168
+ description: "Repositories and issues",
169
+ dashboardUrl: "https://github.com/settings/developers",
170
+ clientIdPlaceholder: null,
142
171
  defaultScopes: ["repo", "read:user", "notifications"],
143
172
  scopePolicy: {
144
173
  allowAdditionalScopes: true,
@@ -159,6 +188,10 @@ const PROVIDER_SEED_DATA: Record<
159
188
  tokenUrl: "https://api.linear.app/oauth/token",
160
189
  pingUrl: "https://api.linear.app/graphql",
161
190
  baseUrl: "https://api.linear.app",
191
+ displayName: "Linear",
192
+ description: "Issues and projects",
193
+ dashboardUrl: "https://linear.app/settings/api",
194
+ clientIdPlaceholder: null,
162
195
  defaultScopes: ["read", "write", "issues:create"],
163
196
  scopePolicy: {
164
197
  allowAdditionalScopes: false,
@@ -175,6 +208,10 @@ const PROVIDER_SEED_DATA: Record<
175
208
  tokenUrl: "https://accounts.spotify.com/api/token",
176
209
  pingUrl: "https://api.spotify.com/v1/me",
177
210
  baseUrl: "https://api.spotify.com/v1",
211
+ displayName: "Spotify",
212
+ description: "Music and playlists",
213
+ dashboardUrl: "https://developer.spotify.com/dashboard",
214
+ clientIdPlaceholder: null,
178
215
  defaultScopes: [
179
216
  "user-read-playback-state",
180
217
  "user-modify-playback-state",
@@ -201,6 +238,10 @@ const PROVIDER_SEED_DATA: Record<
201
238
  tokenUrl: "https://todoist.com/oauth/access_token",
202
239
  pingUrl: "https://api.todoist.com/rest/v2/projects",
203
240
  baseUrl: "https://api.todoist.com/rest/v2",
241
+ displayName: "Todoist",
242
+ description: "Tasks and projects",
243
+ dashboardUrl: "https://developer.todoist.com/appconsole.html",
244
+ clientIdPlaceholder: null,
204
245
  defaultScopes: ["data:read_write"],
205
246
  scopePolicy: {
206
247
  allowAdditionalScopes: false,
@@ -216,6 +257,10 @@ const PROVIDER_SEED_DATA: Record<
216
257
  tokenUrl: "https://discord.com/api/v10/oauth2/token",
217
258
  pingUrl: "https://discord.com/api/v10/users/@me",
218
259
  baseUrl: "https://discord.com/api/v10",
260
+ displayName: "Discord",
261
+ description: "Servers and messages",
262
+ dashboardUrl: "https://discord.com/developers/applications",
263
+ clientIdPlaceholder: null,
219
264
  defaultScopes: [
220
265
  "identify",
221
266
  "guilds",
@@ -236,6 +281,10 @@ const PROVIDER_SEED_DATA: Record<
236
281
  tokenUrl: "https://api.dropboxapi.com/oauth2/token",
237
282
  pingUrl: "https://api.dropboxapi.com/2/users/get_current_account",
238
283
  baseUrl: "https://api.dropboxapi.com/2",
284
+ displayName: "Dropbox",
285
+ description: "Files and folders",
286
+ dashboardUrl: "https://www.dropbox.com/developers/apps",
287
+ clientIdPlaceholder: null,
239
288
  defaultScopes: [
240
289
  "files.metadata.read",
241
290
  "files.content.read",
@@ -257,6 +306,10 @@ const PROVIDER_SEED_DATA: Record<
257
306
  tokenUrl: "https://app.asana.com/-/oauth_token",
258
307
  pingUrl: "https://app.asana.com/api/1.0/users/me",
259
308
  baseUrl: "https://app.asana.com/api/1.0",
309
+ displayName: "Asana",
310
+ description: "Tasks and projects",
311
+ dashboardUrl: "https://app.asana.com/0/my-apps",
312
+ clientIdPlaceholder: null,
260
313
  defaultScopes: ["default"],
261
314
  scopePolicy: {
262
315
  allowAdditionalScopes: false,
@@ -272,6 +325,10 @@ const PROVIDER_SEED_DATA: Record<
272
325
  tokenUrl: "https://airtable.com/oauth2/v1/token",
273
326
  pingUrl: "https://api.airtable.com/v0/meta/whoami",
274
327
  baseUrl: "https://api.airtable.com/v0",
328
+ displayName: "Airtable",
329
+ description: "Bases and records",
330
+ dashboardUrl: "https://airtable.com/create/tokens",
331
+ clientIdPlaceholder: null,
275
332
  defaultScopes: [
276
333
  "data.records:read",
277
334
  "data.records:write",
@@ -292,6 +349,10 @@ const PROVIDER_SEED_DATA: Record<
292
349
  tokenUrl: "https://api.hubapi.com/oauth/v1/token",
293
350
  pingUrl: "https://api.hubapi.com/crm/v3/objects/contacts?limit=1",
294
351
  baseUrl: "https://api.hubapi.com",
352
+ displayName: "HubSpot",
353
+ description: "CRM contacts and deals",
354
+ dashboardUrl: "https://developers.hubspot.com/",
355
+ clientIdPlaceholder: null,
295
356
  defaultScopes: [
296
357
  "crm.objects.contacts.read",
297
358
  "crm.objects.contacts.write",
@@ -316,6 +377,10 @@ const PROVIDER_SEED_DATA: Record<
316
377
  tokenUrl: "https://api.figma.com/v1/oauth/token",
317
378
  pingUrl: "https://api.figma.com/v1/me",
318
379
  baseUrl: "https://api.figma.com/v1",
380
+ displayName: "Figma",
381
+ description: "Design files and comments",
382
+ dashboardUrl: "https://www.figma.com/developers/apps",
383
+ clientIdPlaceholder: null,
319
384
  defaultScopes: ["files:read", "file_comments:write"],
320
385
  scopePolicy: {
321
386
  allowAdditionalScopes: false,
@@ -335,6 +400,11 @@ const PROVIDER_SEED_DATA: Record<
335
400
  tokenUrl: "urn:manual-token",
336
401
  pingUrl: "https://slack.com/api/auth.test",
337
402
  baseUrl: "https://slack.com/api",
403
+ displayName: "Slack Channel",
404
+ description: "Channel bot token",
405
+ dashboardUrl: null,
406
+ clientIdPlaceholder: null,
407
+ requiresClientSecret: false,
338
408
  defaultScopes: [],
339
409
  scopePolicy: {
340
410
  allowAdditionalScopes: false,
@@ -348,6 +418,11 @@ const PROVIDER_SEED_DATA: Record<
348
418
  authUrl: "urn:manual-token",
349
419
  tokenUrl: "urn:manual-token",
350
420
  baseUrl: "https://api.telegram.org",
421
+ displayName: "Telegram",
422
+ description: "Bot messaging",
423
+ dashboardUrl: null,
424
+ clientIdPlaceholder: null,
425
+ requiresClientSecret: false,
351
426
  defaultScopes: [],
352
427
  scopePolicy: {
353
428
  allowAdditionalScopes: false,
@@ -28,9 +28,8 @@ import {
28
28
  import { runPostConnectHook } from "../tools/credentials/post-connect-hooks.js";
29
29
  import {
30
30
  createConnection,
31
+ getActiveConnection,
31
32
  getApp,
32
- getConnectionByProvider,
33
- getConnectionByProviderAndAccount,
34
33
  listActiveConnectionsByProvider,
35
34
  updateConnection,
36
35
  upsertApp,
@@ -59,8 +58,12 @@ export interface StoreOAuth2TokensParams {
59
58
  clientId: string;
60
59
  clientSecret?: string;
61
60
  userinfoUrl?: string;
62
- /** Fallback account info from an identity verifier (e.g. @username, email). */
63
- identityAccountInfo?: string;
61
+ /**
62
+ * Best-effort account identifier parsed from the provider's identity
63
+ * endpoint (e.g. email, @username, display name). The format varies by
64
+ * provider and may be undefined if the API call fails.
65
+ */
66
+ parsedAccountIdentifier?: string;
64
67
  /** Pre-resolved oauth_app ID — skips the upsertApp() call if provided. */
65
68
  oauthAppId?: string;
66
69
  }
@@ -94,6 +97,10 @@ export async function storeOAuth2Tokens(
94
97
  ? Date.now() + tokens.expiresIn * 1000
95
98
  : null;
96
99
 
100
+ // Account identifier parsing is best-effort. The format varies by provider
101
+ // (email for Google, username for GitHub, display name for Spotify, etc.)
102
+ // and may be undefined if the userinfo/identity API call fails or the
103
+ // required scope wasn't granted.
97
104
  let accountInfo: string | undefined;
98
105
  if (userinfoUrl && grantedScopes.some((s) => s.includes("userinfo"))) {
99
106
  try {
@@ -109,7 +116,7 @@ export async function storeOAuth2Tokens(
109
116
  }
110
117
  }
111
118
 
112
- const resolvedAccountInfo = accountInfo ?? params.identityAccountInfo;
119
+ const resolvedAccountInfo = accountInfo ?? params.parsedAccountIdentifier;
113
120
 
114
121
  // -------------------------------------------------------------------
115
122
  // SQLite oauth_app + oauth_connection + new-format secure keys
@@ -144,12 +151,11 @@ export async function storeOAuth2Tokens(
144
151
  // lookup so that re-auth without userinfo still updates the right row.
145
152
  // However, treat provider-only matches as ambiguous when multiple active
146
153
  // connections exist to avoid overwriting the wrong account's tokens.
147
- let existingConn: ReturnType<typeof getConnectionByProvider>;
154
+ let existingConn: ReturnType<typeof getActiveConnection>;
148
155
  if (resolvedAccountInfo) {
149
- existingConn = getConnectionByProviderAndAccount(
150
- service,
151
- resolvedAccountInfo,
152
- );
156
+ existingConn = getActiveConnection(service, {
157
+ account: resolvedAccountInfo,
158
+ });
153
159
  } else {
154
160
  const activeConns = listActiveConnectionsByProvider(service);
155
161
  // Only reuse the provider-only match when it's unambiguous (single connection).
@@ -144,6 +144,7 @@ const LOW_RISK_PROGRAMS = new Set([
144
144
  "du",
145
145
  "df",
146
146
  "assistant",
147
+ "vellum",
147
148
  ]);
148
149
 
149
150
  // High-risk shell programs / patterns
@@ -197,13 +198,30 @@ const LOW_RISK_GIT_SUBCOMMANDS = new Set([
197
198
  "reflog",
198
199
  ]);
199
200
 
200
- // Vellum/assistant CLI subcommands that are low-risk (read-only)
201
- const LOW_RISK_CLI_SUBCOMMANDS = new Set([
202
- "ps",
203
- "doctor",
204
- "audit",
205
- "completions",
206
- "map",
201
+ // Mutating assistant/vellum CLI subcommands that should be escalated to Medium
202
+ // risk. Most assistant/vellum subcommands are read-only and stay Low risk.
203
+ // This mirrors the git subcommand pattern — only known mutating operations
204
+ // get escalated.
205
+ const MEDIUM_RISK_CLI_SUBCOMMANDS = new Set([
206
+ "credentials",
207
+ "config",
208
+ "bash",
209
+ "trust",
210
+ "autonomy",
211
+ "contacts",
212
+ "mcp",
213
+ "keys",
214
+ "wake",
215
+ "sleep",
216
+ "hatch",
217
+ "retire",
218
+ "clean",
219
+ "setup",
220
+ "upgrade",
221
+ "recover",
222
+ "login",
223
+ "use",
224
+ "pair",
207
225
  ]);
208
226
 
209
227
  // Commands that wrap another program — the real program appears as the first
@@ -228,15 +246,40 @@ const WRAPPER_PROGRAMS = new Set([
228
246
  // value of -u) as the wrapped program instead of `echo`.
229
247
  const ENV_VALUE_FLAGS = new Set(["-u", "--unset", "-C", "--chdir"]);
230
248
 
249
+ // `git` global flags that consume the next positional argument as their value.
250
+ // Without this, `git -C status commit` would incorrectly identify `status`
251
+ // (the directory path) as the subcommand instead of `commit`.
252
+ const GIT_VALUE_FLAGS = new Set([
253
+ "-C",
254
+ "-c",
255
+ "--git-dir",
256
+ "--work-tree",
257
+ "--namespace",
258
+ "--super-prefix",
259
+ "--config-env",
260
+ ]);
261
+
231
262
  /**
232
- * Return the first non-flag argument from an argument list.
233
- * Flags are arguments that start with `-`. This is used to skip global
234
- * options (e.g. `--verbose`, `-h`) when extracting the subcommand from
235
- * CLIs like `git`, `vellum`, and `assistant`.
263
+ * Return the first non-flag argument from an argument list, optionally
264
+ * skipping value-taking flags. Flags are arguments that start with `-`.
265
+ * This is used to skip global options (e.g. `--verbose`, `-h`, `-C <path>`)
266
+ * when extracting the subcommand from CLIs like `git`, `vellum`, and
267
+ * `assistant`.
268
+ *
269
+ * When `valueFlags` is provided, any flag in that set causes the next
270
+ * argument to be skipped as well (it is the flag's value, not a positional).
236
271
  */
237
- function firstPositionalArg(args: string[]): string | undefined {
238
- for (const arg of args) {
239
- if (!arg.startsWith("-")) return arg;
272
+ function firstPositionalArg(
273
+ args: string[],
274
+ valueFlags?: Set<string>,
275
+ ): string | undefined {
276
+ for (let i = 0; i < args.length; i++) {
277
+ const arg = args[i];
278
+ if (arg.startsWith("-")) {
279
+ if (valueFlags?.has(arg)) i++; // skip the next arg (the flag's value)
280
+ continue;
281
+ }
282
+ return arg;
240
283
  }
241
284
  return undefined;
242
285
  }
@@ -661,7 +704,7 @@ async function classifyRiskUncached(
661
704
  }
662
705
 
663
706
  if (prog === "git") {
664
- const subcommand = firstPositionalArg(seg.args);
707
+ const subcommand = firstPositionalArg(seg.args, GIT_VALUE_FLAGS);
665
708
  if (subcommand && LOW_RISK_GIT_SUBCOMMANDS.has(subcommand)) {
666
709
  // Stay at current risk
667
710
  continue;
@@ -673,12 +716,12 @@ async function classifyRiskUncached(
673
716
 
674
717
  if (prog === "vellum" || prog === "assistant") {
675
718
  const subcommand = firstPositionalArg(seg.args);
676
- if (subcommand && LOW_RISK_CLI_SUBCOMMANDS.has(subcommand)) {
677
- // Read-only subcommands stay at current risk
719
+ if (subcommand && MEDIUM_RISK_CLI_SUBCOMMANDS.has(subcommand)) {
720
+ // Known mutating subcommands are medium
721
+ maxRisk = RiskLevel.Medium;
678
722
  continue;
679
723
  }
680
- // Mutating subcommands are medium
681
- maxRisk = RiskLevel.Medium;
724
+ // Read-only / unknown subcommands stay at current risk
682
725
  continue;
683
726
  }
684
727
 
@@ -206,6 +206,8 @@ function buildAttachmentSection(): string {
206
206
  "",
207
207
  'Use `source="host"` with an absolute path for host filesystem files. Optional attributes: `filename` (display name override), `mime_type` (override auto-detection).',
208
208
  "",
209
+ "Image and video attachments can render inline in chat. If the user asks to preview a media file here, attach it instead of only printing its path.",
210
+ "",
209
211
  "Embed images/GIFs inline using markdown: `![description](URL)`.",
210
212
  ].join("\n");
211
213
  }
@@ -208,6 +208,8 @@ Once you've completed Phase 1 and made reasonable progress through Phase 2, you'
208
208
 
209
209
  If you still haven't shown the two suggestions (Phase 2 step 4), do that before wrapping.
210
210
 
211
+ When you're confident onboarding is complete, delete `BOOTSTRAP.md` so it doesn't re-trigger on the next conversation.
212
+
211
213
  ---
212
214
 
213
215
  _Good luck out there. Make it count._
@@ -16,6 +16,9 @@ import type {
16
16
 
17
17
  const log = getLogger("anthropic-client");
18
18
 
19
+ /** Validation-specific timeout (10s) so a stalled network doesn't block key submission. */
20
+ const VALIDATION_TIMEOUT_MS = 10_000;
21
+
19
22
  /**
20
23
  * Validate an Anthropic API key by making a lightweight GET /v1/models call.
21
24
  * Returns `{ valid: true }` on success or `{ valid: false, reason: string }` on failure.
@@ -24,7 +27,11 @@ export async function validateAnthropicApiKey(
24
27
  apiKey: string,
25
28
  ): Promise<{ valid: true } | { valid: false; reason: string }> {
26
29
  try {
27
- const client = new Anthropic({ apiKey });
30
+ const client = new Anthropic({
31
+ apiKey,
32
+ timeout: VALIDATION_TIMEOUT_MS,
33
+ maxRetries: 0,
34
+ });
28
35
  await client.models.list({ limit: 1 });
29
36
  return { valid: true };
30
37
  } catch (error) {
@@ -133,7 +133,10 @@ export class FailoverProvider implements Provider {
133
133
  );
134
134
  health.unhealthySince = null;
135
135
  }
136
- return response;
136
+ return {
137
+ ...response,
138
+ actualProvider: response.actualProvider ?? provider.name,
139
+ };
137
140
  } catch (error) {
138
141
  lastError = error;
139
142
 
@@ -3,6 +3,7 @@ import { ApiError, GoogleGenAI } from "@google/genai";
3
3
 
4
4
  import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "../../prompts/cache-boundary.js";
5
5
  import { ProviderError } from "../../util/errors.js";
6
+ import { getLogger } from "../../util/logger.js";
6
7
  import { createStreamTimeout } from "../stream-timeout.js";
7
8
  import type {
8
9
  ContentBlock,
@@ -13,6 +14,55 @@ import type {
13
14
  ToolDefinition,
14
15
  } from "../types.js";
15
16
 
17
+ const log = getLogger("gemini-client");
18
+
19
+ /** Validation-specific timeout (10s) so a stalled network doesn't block key submission. */
20
+ const VALIDATION_TIMEOUT_MS = 10_000;
21
+
22
+ /**
23
+ * Validate a Gemini API key by making a lightweight models.list() call.
24
+ * Returns `{ valid: true }` on success or `{ valid: false, reason: string }` on failure.
25
+ */
26
+ export async function validateGeminiApiKey(
27
+ apiKey: string,
28
+ ): Promise<{ valid: true } | { valid: false; reason: string }> {
29
+ try {
30
+ const client = new GoogleGenAI({ apiKey });
31
+ await client.models.list({
32
+ config: {
33
+ pageSize: 1,
34
+ httpOptions: { timeout: VALIDATION_TIMEOUT_MS },
35
+ },
36
+ });
37
+ return { valid: true };
38
+ } catch (error) {
39
+ if (error instanceof ApiError) {
40
+ if (error.status === 401) {
41
+ return { valid: false, reason: "API key is invalid or expired." };
42
+ }
43
+ if (error.status === 403) {
44
+ return {
45
+ valid: false,
46
+ reason: `Gemini API error (${error.status}): ${error.message}`,
47
+ };
48
+ }
49
+ // Transient errors (429, 5xx, etc.) — validation is inconclusive,
50
+ // allow the key to be stored rather than blocking the user.
51
+ log.warn(
52
+ { status: error.status },
53
+ "Gemini API returned a transient error during key validation — allowing key storage",
54
+ );
55
+ return { valid: true };
56
+ }
57
+ // Network errors — validation is inconclusive, allow key storage.
58
+ log.warn(
59
+ { error: error instanceof Error ? error.message : String(error) },
60
+ "Network error during Gemini key validation — allowing key storage",
61
+ );
62
+ return { valid: true };
63
+ }
64
+ }
65
+
16
66
  export interface GeminiProviderOptions {
17
67
  streamTimeoutMs?: number;
18
68
  /** When set, routes requests through the managed proxy at this base URL. */
@@ -0,0 +1,92 @@
1
+ export interface CatalogModel {
2
+ id: string;
3
+ displayName: string;
4
+ }
5
+
6
+ export interface ProviderCatalogEntry {
7
+ id: string;
8
+ displayName: string;
9
+ models: CatalogModel[];
10
+ defaultModel: string;
11
+ apiKeyUrl?: string;
12
+ apiKeyPlaceholder?: string;
13
+ }
14
+
15
+ /** Single source of truth for all inference provider metadata and models. */
16
+ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
17
+ {
18
+ id: "anthropic",
19
+ displayName: "Anthropic",
20
+ models: [
21
+ { id: "claude-opus-4-6", displayName: "Claude Opus 4.6" },
22
+ { id: "claude-sonnet-4-6", displayName: "Claude Sonnet 4.6" },
23
+ { id: "claude-haiku-4-5-20251001", displayName: "Claude Haiku 4.5" },
24
+ ],
25
+ defaultModel: "claude-opus-4-6",
26
+ apiKeyUrl: "https://console.anthropic.com/settings/keys",
27
+ apiKeyPlaceholder: "sk-ant-api03-...",
28
+ },
29
+ {
30
+ id: "openai",
31
+ displayName: "OpenAI",
32
+ models: [
33
+ { id: "gpt-5.4", displayName: "GPT-5.4" },
34
+ { id: "gpt-5.2", displayName: "GPT-5.2" },
35
+ { id: "gpt-5.4-mini", displayName: "GPT-5.4 Mini" },
36
+ { id: "gpt-5.4-nano", displayName: "GPT-5.4 Nano" },
37
+ ],
38
+ defaultModel: "gpt-5.4",
39
+ apiKeyUrl: "https://platform.openai.com/api-keys",
40
+ apiKeyPlaceholder: "sk-proj-...",
41
+ },
42
+ {
43
+ id: "gemini",
44
+ displayName: "Google Gemini",
45
+ models: [
46
+ { id: "gemini-3-flash", displayName: "Gemini 3 Flash" },
47
+ { id: "gemini-3-pro", displayName: "Gemini 3 Pro" },
48
+ ],
49
+ defaultModel: "gemini-3-flash",
50
+ apiKeyUrl: "https://aistudio.google.com/apikey",
51
+ apiKeyPlaceholder: "AIza...",
52
+ },
53
+ {
54
+ id: "ollama",
55
+ displayName: "Ollama",
56
+ models: [
57
+ { id: "llama3.2", displayName: "Llama 3.2" },
58
+ { id: "mistral", displayName: "Mistral" },
59
+ ],
60
+ defaultModel: "llama3.2",
61
+ },
62
+ {
63
+ id: "fireworks",
64
+ displayName: "Fireworks",
65
+ models: [
66
+ {
67
+ id: "accounts/fireworks/models/kimi-k2p5",
68
+ displayName: "Kimi K2.5",
69
+ },
70
+ ],
71
+ defaultModel: "accounts/fireworks/models/kimi-k2p5",
72
+ apiKeyUrl: "https://fireworks.ai/account/api-keys",
73
+ apiKeyPlaceholder: "fw_...",
74
+ },
75
+ {
76
+ id: "openrouter",
77
+ displayName: "OpenRouter",
78
+ models: [
79
+ { id: "x-ai/grok-4", displayName: "Grok 4" },
80
+ { id: "x-ai/grok-4.20-beta", displayName: "Grok 4.20 Beta" },
81
+ ],
82
+ defaultModel: "x-ai/grok-4",
83
+ apiKeyUrl: "https://openrouter.ai/keys",
84
+ apiKeyPlaceholder: "sk-or-v1-...",
85
+ },
86
+ ];
87
+
88
+ /** Check if a model ID is in the catalog for a given provider. */
89
+ export function isModelInCatalog(provider: string, modelId: string): boolean {
90
+ const entry = PROVIDER_CATALOG.find((p) => p.id === provider);
91
+ return entry?.models.some((m) => m.id === modelId) ?? false;
92
+ }