@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
@@ -0,0 +1,426 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ rmSync,
6
+ writeFileSync,
7
+ } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Mock getDataDir to use a temp directory
14
+ // ---------------------------------------------------------------------------
15
+
16
+ let testDataDir: string;
17
+
18
+ mock.module("../util/platform.js", () => ({
19
+ getDataDir: () => testDataDir,
20
+ getProjectDir: () => testDataDir,
21
+ }));
22
+
23
+ // Re-import app-store after mocking so it uses our temp dir
24
+ const {
25
+ slugify,
26
+ generateAppDirName,
27
+ validateDirName,
28
+ resolveAppDir,
29
+ getAppDirPath,
30
+ getAppsDir,
31
+ createApp,
32
+ getApp,
33
+ updateApp,
34
+ deleteApp,
35
+ } = await import("../memory/app-store.js");
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Helpers
39
+ // ---------------------------------------------------------------------------
40
+
41
+ function freshTempDir(): string {
42
+ return join(
43
+ tmpdir(),
44
+ `vellum-app-dir-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
45
+ );
46
+ }
47
+
48
+ function makeAppParams(name: string) {
49
+ return {
50
+ name,
51
+ schemaJson: "{}",
52
+ htmlDefinition: "<h1>Hello</h1>",
53
+ };
54
+ }
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // Setup / teardown
58
+ // ---------------------------------------------------------------------------
59
+
60
+ beforeEach(() => {
61
+ testDataDir = freshTempDir();
62
+ mkdirSync(join(testDataDir, "apps"), { recursive: true });
63
+ });
64
+
65
+ afterEach(() => {
66
+ if (existsSync(testDataDir)) {
67
+ rmSync(testDataDir, { recursive: true, force: true });
68
+ }
69
+ });
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // slugify()
73
+ // ---------------------------------------------------------------------------
74
+
75
+ describe("slugify()", () => {
76
+ test("normal names", () => {
77
+ expect(slugify("My Cool App")).toBe("my-cool-app");
78
+ });
79
+
80
+ test("special characters are replaced with hyphens", () => {
81
+ expect(slugify("hello@world!#$%")).toBe("hello-world");
82
+ });
83
+
84
+ test("unicode characters are replaced", () => {
85
+ expect(slugify("café résumé")).toBe("caf-r-sum");
86
+ });
87
+
88
+ test("emoji-only names produce fallback slug", () => {
89
+ const result = slugify("🚀🎉");
90
+ expect(result).toMatch(/^app-[a-f0-9]{8}$/);
91
+ });
92
+
93
+ test("empty string produces fallback slug", () => {
94
+ const result = slugify("");
95
+ expect(result).toMatch(/^app-[a-f0-9]{8}$/);
96
+ });
97
+
98
+ test("very long names are truncated to 60 chars", () => {
99
+ const longName = "a".repeat(100);
100
+ const result = slugify(longName);
101
+ expect(result.length).toBeLessThanOrEqual(60);
102
+ expect(result).toBe("a".repeat(60));
103
+ });
104
+
105
+ test("truncation removes trailing hyphens", () => {
106
+ // Create a name where position 60 lands on a hyphen sequence
107
+ const name = "a".repeat(58) + "--bbb";
108
+ const result = slugify(name);
109
+ expect(result.length).toBeLessThanOrEqual(60);
110
+ expect(result).not.toMatch(/-$/);
111
+ });
112
+
113
+ test("names with only hyphens produce fallback slug", () => {
114
+ const result = slugify("---");
115
+ expect(result).toMatch(/^app-[a-f0-9]{8}$/);
116
+ });
117
+
118
+ test("leading and trailing hyphens are stripped", () => {
119
+ expect(slugify("-hello-world-")).toBe("hello-world");
120
+ });
121
+
122
+ test("consecutive hyphens are collapsed", () => {
123
+ expect(slugify("hello---world")).toBe("hello-world");
124
+ });
125
+ });
126
+
127
+ // ---------------------------------------------------------------------------
128
+ // generateAppDirName()
129
+ // ---------------------------------------------------------------------------
130
+
131
+ describe("generateAppDirName()", () => {
132
+ test("returns base slug when no collision", () => {
133
+ const result = generateAppDirName("My App", new Set());
134
+ expect(result).toBe("my-app");
135
+ });
136
+
137
+ test("appends -2 on first collision", () => {
138
+ const result = generateAppDirName("My App", new Set(["my-app"]));
139
+ expect(result).toBe("my-app-2");
140
+ });
141
+
142
+ test("escalates numeric suffix on multiple collisions", () => {
143
+ const existing = new Set(["my-app", "my-app-2", "my-app-3"]);
144
+ const result = generateAppDirName("My App", existing);
145
+ expect(result).toBe("my-app-4");
146
+ });
147
+
148
+ test("collision with truncated names still deduplicates", () => {
149
+ const longName = "a".repeat(100);
150
+ const base = slugify(longName);
151
+ const existing = new Set([base]);
152
+ const result = generateAppDirName(longName, existing);
153
+ expect(result).toBe(`${base}-2`);
154
+ });
155
+ });
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // createApp() — directory named after slug
159
+ // ---------------------------------------------------------------------------
160
+
161
+ describe("createApp()", () => {
162
+ test("directory is named after slug, not UUID", () => {
163
+ const app = createApp(makeAppParams("My Test App"));
164
+ const appsDir = getAppsDir();
165
+
166
+ // dirName should be the slug
167
+ expect(app.dirName).toBe("my-test-app");
168
+
169
+ // JSON file should be named after slug
170
+ expect(existsSync(join(appsDir, "my-test-app.json"))).toBe(true);
171
+
172
+ // Directory should be named after slug
173
+ expect(existsSync(join(appsDir, "my-test-app"))).toBe(true);
174
+
175
+ // UUID-named files should NOT exist
176
+ expect(existsSync(join(appsDir, `${app.id}.json`))).toBe(false);
177
+ });
178
+
179
+ test("dirName is persisted in JSON", () => {
180
+ const app = createApp(makeAppParams("Slug Test"));
181
+ const appsDir = getAppsDir();
182
+ const jsonPath = join(appsDir, "slug-test.json");
183
+ const persisted = JSON.parse(readFileSync(jsonPath, "utf-8"));
184
+ expect(persisted.dirName).toBe("slug-test");
185
+ expect(persisted.id).toBe(app.id);
186
+ });
187
+
188
+ test("index.html is in the slugified directory", () => {
189
+ createApp(makeAppParams("Html App"));
190
+ const appsDir = getAppsDir();
191
+ const indexPath = join(appsDir, "html-app", "index.html");
192
+ expect(existsSync(indexPath)).toBe(true);
193
+ expect(readFileSync(indexPath, "utf-8")).toBe("<h1>Hello</h1>");
194
+ });
195
+
196
+ test("deduplicates dirNames across multiple creates", () => {
197
+ const app1 = createApp(makeAppParams("Duplicate"));
198
+ const app2 = createApp(makeAppParams("Duplicate"));
199
+ expect(app1.dirName).toBe("duplicate");
200
+ expect(app2.dirName).toBe("duplicate-2");
201
+ });
202
+ });
203
+
204
+ // ---------------------------------------------------------------------------
205
+ // createApp() + updateApp() — frozen dirName invariant
206
+ // ---------------------------------------------------------------------------
207
+
208
+ describe("frozen dirName invariant", () => {
209
+ test("renaming an app does NOT change its directory name", () => {
210
+ const app = createApp(makeAppParams("Original Name"));
211
+ const appsDir = getAppsDir();
212
+
213
+ expect(app.dirName).toBe("original-name");
214
+ expect(existsSync(join(appsDir, "original-name.json"))).toBe(true);
215
+
216
+ // Rename the app
217
+ const updated = updateApp(app.id, { name: "New Name" });
218
+ expect(updated.name).toBe("New Name");
219
+
220
+ // Directory and files should still be at original slug
221
+ expect(existsSync(join(appsDir, "original-name.json"))).toBe(true);
222
+ expect(existsSync(join(appsDir, "original-name"))).toBe(true);
223
+
224
+ // New name slug should NOT exist as files
225
+ expect(existsSync(join(appsDir, "new-name.json"))).toBe(false);
226
+ });
227
+ });
228
+
229
+ // ---------------------------------------------------------------------------
230
+ // getApp() — lookup by UUID with slugified dirs
231
+ // ---------------------------------------------------------------------------
232
+
233
+ describe("getApp()", () => {
234
+ test("lookup by UUID works with slugified dirs", () => {
235
+ const created = createApp(makeAppParams("Lookup App"));
236
+ const fetched = getApp(created.id);
237
+ expect(fetched).not.toBeNull();
238
+ expect(fetched!.id).toBe(created.id);
239
+ expect(fetched!.name).toBe("Lookup App");
240
+ expect(fetched!.htmlDefinition).toBe("<h1>Hello</h1>");
241
+ });
242
+
243
+ test("backward compat: works when JSON has no dirName (uses id as fallback)", () => {
244
+ const appsDir = getAppsDir();
245
+ const fakeId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
246
+
247
+ // Write a JSON file with no dirName, using the UUID as the filename
248
+ const appData = {
249
+ id: fakeId,
250
+ name: "Legacy App",
251
+ schemaJson: "{}",
252
+ createdAt: Date.now(),
253
+ updatedAt: Date.now(),
254
+ };
255
+ writeFileSync(
256
+ join(appsDir, `${fakeId}.json`),
257
+ JSON.stringify(appData, null, 2),
258
+ );
259
+
260
+ // Create directory with index.html
261
+ const appDir = join(appsDir, fakeId);
262
+ mkdirSync(appDir, { recursive: true });
263
+ writeFileSync(join(appDir, "index.html"), "<p>legacy</p>", "utf-8");
264
+
265
+ const fetched = getApp(fakeId);
266
+ expect(fetched).not.toBeNull();
267
+ expect(fetched!.id).toBe(fakeId);
268
+ expect(fetched!.name).toBe("Legacy App");
269
+ expect(fetched!.htmlDefinition).toBe("<p>legacy</p>");
270
+ });
271
+ });
272
+
273
+ // ---------------------------------------------------------------------------
274
+ // deleteApp() — cleans up slugified files
275
+ // ---------------------------------------------------------------------------
276
+
277
+ describe("deleteApp()", () => {
278
+ test("cleans up slugified directory, JSON, and preview files", () => {
279
+ const app = createApp({
280
+ ...makeAppParams("Delete Me"),
281
+ preview: "base64-preview-data",
282
+ });
283
+ const appsDir = getAppsDir();
284
+
285
+ // Verify files exist before deletion
286
+ expect(existsSync(join(appsDir, "delete-me.json"))).toBe(true);
287
+ expect(existsSync(join(appsDir, "delete-me"))).toBe(true);
288
+ expect(existsSync(join(appsDir, "delete-me.preview"))).toBe(true);
289
+
290
+ deleteApp(app.id);
291
+
292
+ // All files should be gone
293
+ expect(existsSync(join(appsDir, "delete-me.json"))).toBe(false);
294
+ expect(existsSync(join(appsDir, "delete-me"))).toBe(false);
295
+ expect(existsSync(join(appsDir, "delete-me.preview"))).toBe(false);
296
+ });
297
+ });
298
+
299
+ // ---------------------------------------------------------------------------
300
+ // resolveAppDir() — validation rejects malicious dirName values
301
+ // When a JSON file has an invalid dirName, resolveAppDir defensively falls
302
+ // back to using the app ID instead of the malicious dirName. The
303
+ // validateDirName() call inside the try/catch causes the invalid entry to
304
+ // be skipped, and the function returns the safe fallback.
305
+ // ---------------------------------------------------------------------------
306
+
307
+ describe("resolveAppDir() validation", () => {
308
+ test("falls back to id when dirName contains path traversal (..)", () => {
309
+ const appsDir = getAppsDir();
310
+ const fakeId = "bbbbbbbb-cccc-dddd-eeee-ffffffffffff";
311
+ writeFileSync(
312
+ join(appsDir, `${fakeId}.json`),
313
+ JSON.stringify({ id: fakeId, name: "Evil", dirName: "../etc" }),
314
+ );
315
+
316
+ // Should NOT use the malicious dirName — falls back to id
317
+ const result = resolveAppDir(fakeId);
318
+ expect(result.dirName).toBe(fakeId);
319
+ expect(result.appDir).toBe(join(appsDir, fakeId));
320
+ });
321
+
322
+ test("falls back to id when dirName contains forward slash", () => {
323
+ const appsDir = getAppsDir();
324
+ const fakeId = "cccccccc-dddd-eeee-ffff-aaaaaaaaaaaa";
325
+ writeFileSync(
326
+ join(appsDir, `${fakeId}.json`),
327
+ JSON.stringify({ id: fakeId, name: "Evil", dirName: "foo/bar" }),
328
+ );
329
+
330
+ const result = resolveAppDir(fakeId);
331
+ expect(result.dirName).toBe(fakeId);
332
+ });
333
+
334
+ test("falls back to id when dirName contains backslash", () => {
335
+ const appsDir = getAppsDir();
336
+ const fakeId = "dddddddd-eeee-ffff-aaaa-bbbbbbbbbbbb";
337
+ writeFileSync(
338
+ join(appsDir, `${fakeId}.json`),
339
+ JSON.stringify({ id: fakeId, name: "Evil", dirName: "foo\\bar" }),
340
+ );
341
+
342
+ const result = resolveAppDir(fakeId);
343
+ expect(result.dirName).toBe(fakeId);
344
+ });
345
+
346
+ test("falls back to id when dirName is empty string", () => {
347
+ const appsDir = getAppsDir();
348
+ const fakeId = "eeeeeeee-ffff-aaaa-bbbb-cccccccccccc";
349
+ writeFileSync(
350
+ join(appsDir, `${fakeId}.json`),
351
+ JSON.stringify({ id: fakeId, name: "Evil", dirName: "" }),
352
+ );
353
+
354
+ // Empty string is falsy, so `parsed.dirName || id` falls back to the app ID.
355
+ // This prevents appDir from resolving to the apps root directory.
356
+ const result = resolveAppDir(fakeId);
357
+ expect(result.dirName).toBe(fakeId);
358
+ expect(result.appDir).toBe(join(appsDir, fakeId));
359
+ });
360
+ });
361
+
362
+ // ---------------------------------------------------------------------------
363
+ // validateDirName() — direct unit tests
364
+ // ---------------------------------------------------------------------------
365
+
366
+ describe("validateDirName()", () => {
367
+ test("accepts valid slug names", () => {
368
+ expect(() => validateDirName("my-cool-app")).not.toThrow();
369
+ expect(() => validateDirName("app-123")).not.toThrow();
370
+ });
371
+
372
+ test("rejects git pathspec metacharacters", () => {
373
+ expect(() => validateDirName("app*")).toThrow("git pathspec");
374
+ expect(() => validateDirName("app?")).toThrow("git pathspec");
375
+ expect(() => validateDirName("app[0]")).toThrow("git pathspec");
376
+ expect(() => validateDirName("app:foo")).toThrow("git pathspec");
377
+ expect(() => validateDirName("app(1)")).toThrow("git pathspec");
378
+ });
379
+
380
+ test("rejects path traversal", () => {
381
+ expect(() => validateDirName("..")).toThrow("Invalid dirName");
382
+ expect(() => validateDirName("foo/bar")).toThrow("Invalid dirName");
383
+ });
384
+ });
385
+
386
+ // ---------------------------------------------------------------------------
387
+ // getAppDirPath() — returns correct path
388
+ // ---------------------------------------------------------------------------
389
+
390
+ describe("getAppDirPath()", () => {
391
+ test("returns correct path for slugified apps", () => {
392
+ const app = createApp(makeAppParams("Path Test"));
393
+ const appsDir = getAppsDir();
394
+ const result = getAppDirPath(app.id);
395
+ expect(result).toBe(join(appsDir, "path-test"));
396
+ });
397
+
398
+ test("returns correct path for legacy apps (no dirName)", () => {
399
+ const appsDir = getAppsDir();
400
+ const fakeId = "11111111-2222-3333-4444-555555555555";
401
+ // Write a legacy JSON with no dirName
402
+ writeFileSync(
403
+ join(appsDir, `${fakeId}.json`),
404
+ JSON.stringify({ id: fakeId, name: "Legacy" }),
405
+ );
406
+
407
+ const result = getAppDirPath(fakeId);
408
+ expect(result).toBe(join(appsDir, fakeId));
409
+ });
410
+ });
411
+
412
+ // ---------------------------------------------------------------------------
413
+ // Guard test: no file outside app-store.ts constructs getAppsDir() + appId
414
+ // (Note: the primary guard test is in app-dir-path-guard.test.ts; this is
415
+ // a complementary check that we can import and use the guard-relevant
416
+ // functions without issues.)
417
+ // ---------------------------------------------------------------------------
418
+
419
+ describe("guard: getAppsDir + appId path construction", () => {
420
+ test("app-dir-path-guard.test.ts exists and covers this concern", () => {
421
+ // This is a meta-test to ensure the guard test file is present
422
+ expect(existsSync(join(__dirname, "app-dir-path-guard.test.ts"))).toBe(
423
+ true,
424
+ );
425
+ });
426
+ });