@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
@@ -36,6 +36,7 @@ import {
36
36
  import type { ServerMessage } from "../daemon/message-protocol.js";
37
37
  import { PairingStore } from "../daemon/pairing-store.js";
38
38
  import {
39
+ type AttentionState,
39
40
  type Confidence,
40
41
  getAttentionStateByConversationIds,
41
42
  markConversationUnread,
@@ -43,6 +44,8 @@ import {
43
44
  type SignalType,
44
45
  } from "../memory/conversation-attention-store.js";
45
46
  import {
47
+ type ConversationRow,
48
+ forkConversation as forkConversationInStore,
46
49
  getConversation,
47
50
  getDisplayMetaForConversations,
48
51
  } from "../memory/conversation-crud.js";
@@ -51,6 +54,7 @@ import {
51
54
  countConversations,
52
55
  listConversations,
53
56
  } from "../memory/conversation-queries.js";
57
+ import type { ExternalConversationBinding } from "../memory/external-conversation-store.js";
54
58
  import * as externalConversationStore from "../memory/external-conversation-store.js";
55
59
  import {
56
60
  consumeCallback,
@@ -122,7 +126,10 @@ import {
122
126
  contactRouteDefinitions,
123
127
  } from "./routes/contact-routes.js";
124
128
  import { conversationAttentionRouteDefinitions } from "./routes/conversation-attention-routes.js";
125
- import { conversationManagementRouteDefinitions } from "./routes/conversation-management-routes.js";
129
+ import {
130
+ type ConversationManagementDeps,
131
+ conversationManagementRouteDefinitions,
132
+ } from "./routes/conversation-management-routes.js";
126
133
  import { conversationQueryRouteDefinitions } from "./routes/conversation-query-routes.js";
127
134
  import { conversationRouteDefinitions } from "./routes/conversation-routes.js";
128
135
  import { conversationStarterRouteDefinitions } from "./routes/conversation-starter-routes.js";
@@ -147,6 +154,7 @@ import { inviteRouteDefinitions } from "./routes/invite-routes.js";
147
154
  import { logExportRouteDefinitions } from "./routes/log-export-routes.js";
148
155
  import { memoryItemRouteDefinitions } from "./routes/memory-item-routes.js";
149
156
  import { migrationRouteDefinitions } from "./routes/migration-routes.js";
157
+ import { oauthAppsRouteDefinitions } from "./routes/oauth-apps.js";
150
158
  import type { PairingHandlerContext } from "./routes/pairing-routes.js";
151
159
  import {
152
160
  handlePairingRequest,
@@ -713,6 +721,175 @@ export class RuntimeHttpServer {
713
721
  });
714
722
  }
715
723
 
724
+ private buildAssistantAttention(attentionState: AttentionState | undefined):
725
+ | {
726
+ hasUnseenLatestAssistantMessage: boolean;
727
+ latestAssistantMessageAt?: number;
728
+ lastSeenAssistantMessageAt?: number;
729
+ lastSeenConfidence?: Confidence;
730
+ lastSeenSignalType?: SignalType;
731
+ }
732
+ | undefined {
733
+ if (!attentionState) return undefined;
734
+
735
+ return {
736
+ hasUnseenLatestAssistantMessage:
737
+ attentionState.latestAssistantMessageAt != null &&
738
+ (attentionState.lastSeenAssistantMessageAt == null ||
739
+ attentionState.lastSeenAssistantMessageAt <
740
+ attentionState.latestAssistantMessageAt),
741
+ ...(attentionState.latestAssistantMessageAt != null
742
+ ? {
743
+ latestAssistantMessageAt: attentionState.latestAssistantMessageAt,
744
+ }
745
+ : {}),
746
+ ...(attentionState.lastSeenAssistantMessageAt != null
747
+ ? {
748
+ lastSeenAssistantMessageAt:
749
+ attentionState.lastSeenAssistantMessageAt,
750
+ }
751
+ : {}),
752
+ ...(attentionState.lastSeenConfidence != null
753
+ ? { lastSeenConfidence: attentionState.lastSeenConfidence }
754
+ : {}),
755
+ ...(attentionState.lastSeenSignalType != null
756
+ ? { lastSeenSignalType: attentionState.lastSeenSignalType }
757
+ : {}),
758
+ };
759
+ }
760
+
761
+ private buildForkParent(
762
+ conversation: ConversationRow,
763
+ parentCache: Map<string, ConversationRow | null>,
764
+ ): { conversationId: string; messageId: string; title: string } | undefined {
765
+ const parentConversationId = conversation.forkParentConversationId;
766
+ const parentMessageId = conversation.forkParentMessageId;
767
+ if (!parentConversationId || !parentMessageId) return undefined;
768
+
769
+ let parentConversation: ConversationRow | null | undefined =
770
+ parentCache.get(parentConversationId);
771
+ if (parentConversation === undefined) {
772
+ parentConversation = getConversation(parentConversationId);
773
+ parentCache.set(parentConversationId, parentConversation);
774
+ }
775
+ if (
776
+ !parentConversation ||
777
+ parentConversation.conversationType === "private"
778
+ ) {
779
+ return undefined;
780
+ }
781
+
782
+ return {
783
+ conversationId: parentConversationId,
784
+ messageId: parentMessageId,
785
+ title: parentConversation.title ?? "Untitled",
786
+ };
787
+ }
788
+
789
+ private serializeConversationSummary(params: {
790
+ conversation: ConversationRow;
791
+ binding?: ExternalConversationBinding | null;
792
+ attentionState?: AttentionState;
793
+ displayMeta?: { displayOrder: number | null; isPinned: boolean };
794
+ parentCache: Map<string, ConversationRow | null>;
795
+ }) {
796
+ const { conversation, binding, attentionState, displayMeta, parentCache } =
797
+ params;
798
+ const originChannel = parseChannelId(conversation.originChannel);
799
+ const assistantAttention = this.buildAssistantAttention(attentionState);
800
+ const forkParent = this.buildForkParent(conversation, parentCache);
801
+
802
+ return {
803
+ id: conversation.id,
804
+ title: conversation.title ?? "Untitled",
805
+ createdAt: conversation.createdAt,
806
+ updatedAt: conversation.updatedAt,
807
+ conversationType:
808
+ conversation.conversationType === "private" ? "private" : "standard",
809
+ source: conversation.source ?? "user",
810
+ ...(conversation.scheduleJobId
811
+ ? { scheduleJobId: conversation.scheduleJobId }
812
+ : {}),
813
+ ...(binding
814
+ ? {
815
+ channelBinding: {
816
+ sourceChannel: binding.sourceChannel,
817
+ externalChatId: binding.externalChatId,
818
+ externalUserId: binding.externalUserId,
819
+ displayName: binding.displayName,
820
+ username: binding.username,
821
+ },
822
+ }
823
+ : {}),
824
+ ...(originChannel ? { conversationOriginChannel: originChannel } : {}),
825
+ ...(assistantAttention ? { assistantAttention } : {}),
826
+ ...(displayMeta?.isPinned
827
+ ? {
828
+ isPinned: true as const,
829
+ displayOrder: displayMeta.displayOrder,
830
+ }
831
+ : displayMeta?.displayOrder != null
832
+ ? {
833
+ displayOrder: displayMeta.displayOrder,
834
+ }
835
+ : {}),
836
+ ...(forkParent ? { forkParent } : {}),
837
+ };
838
+ }
839
+
840
+ private buildConversationDetailResponse(conversationId: string) {
841
+ const conversation = getConversation(conversationId);
842
+ if (!conversation) {
843
+ return null;
844
+ }
845
+
846
+ const bindings = externalConversationStore.getBindingsForConversations([
847
+ conversation.id,
848
+ ]);
849
+ const attentionStates = getAttentionStateByConversationIds([
850
+ conversation.id,
851
+ ]);
852
+ const displayMeta = getDisplayMetaForConversations([conversation.id]);
853
+ const parentCache = new Map<string, ConversationRow | null>();
854
+
855
+ return {
856
+ conversation: this.serializeConversationSummary({
857
+ conversation,
858
+ binding: bindings.get(conversation.id),
859
+ attentionState: attentionStates.get(conversation.id),
860
+ displayMeta: displayMeta.get(conversation.id),
861
+ parentCache,
862
+ }),
863
+ };
864
+ }
865
+
866
+ private getConversationManagementRouteDeps(): ConversationManagementDeps | null {
867
+ if (!this.conversationManagementDeps) {
868
+ return null;
869
+ }
870
+
871
+ return {
872
+ ...this.conversationManagementDeps,
873
+ forkConversation:
874
+ this.conversationManagementDeps.forkConversation ??
875
+ (async ({ conversationId, throughMessageId }) => {
876
+ const forkedConversation = forkConversationInStore({
877
+ conversationId,
878
+ throughMessageId,
879
+ });
880
+ const detail = this.buildConversationDetailResponse(
881
+ forkedConversation.id,
882
+ );
883
+ if (!detail) {
884
+ throw new Error(
885
+ `Forked conversation ${forkedConversation.id} could not be loaded`,
886
+ );
887
+ }
888
+ return detail.conversation;
889
+ }),
890
+ };
891
+ }
892
+
716
893
  // ---------------------------------------------------------------------------
717
894
  // Declarative route table
718
895
  // ---------------------------------------------------------------------------
@@ -728,6 +905,8 @@ export class RuntimeHttpServer {
728
905
  */
729
906
  private buildRouteTable(): RouteDefinition[] {
730
907
  const assistantId = DAEMON_INTERNAL_ASSISTANT_ID;
908
+ const conversationManagementDeps =
909
+ this.getConversationManagementRouteDeps();
731
910
 
732
911
  return [
733
912
  ...pairingRouteDefinitions({
@@ -823,84 +1002,25 @@ export class RuntimeHttpServer {
823
1002
  );
824
1003
  const attentionStates =
825
1004
  getAttentionStateByConversationIds(conversationIds);
1005
+ const parentCache = new Map<string, ConversationRow | null>();
826
1006
  return Response.json({
827
- conversations: conversations.map((c) => {
828
- const binding = bindings.get(c.id);
829
- const originChannel = parseChannelId(c.originChannel);
830
- const attn = attentionStates.get(c.id);
831
- const assistantAttention = attn
832
- ? {
833
- hasUnseenLatestAssistantMessage:
834
- attn.latestAssistantMessageAt != null &&
835
- (attn.lastSeenAssistantMessageAt == null ||
836
- attn.lastSeenAssistantMessageAt <
837
- attn.latestAssistantMessageAt),
838
- ...(attn.latestAssistantMessageAt != null
839
- ? {
840
- latestAssistantMessageAt:
841
- attn.latestAssistantMessageAt,
842
- }
843
- : {}),
844
- ...(attn.lastSeenAssistantMessageAt != null
845
- ? {
846
- lastSeenAssistantMessageAt:
847
- attn.lastSeenAssistantMessageAt,
848
- }
849
- : {}),
850
- ...(attn.lastSeenConfidence != null
851
- ? { lastSeenConfidence: attn.lastSeenConfidence }
852
- : {}),
853
- ...(attn.lastSeenSignalType != null
854
- ? { lastSeenSignalType: attn.lastSeenSignalType }
855
- : {}),
856
- }
857
- : undefined;
858
- return {
859
- id: c.id,
860
- title: c.title ?? "Untitled",
861
- createdAt: c.createdAt,
862
- updatedAt: c.updatedAt,
863
- conversationType:
864
- c.conversationType === "private" ? "private" : "standard",
865
- source: c.source ?? "user",
866
- ...(c.scheduleJobId ? { scheduleJobId: c.scheduleJobId } : {}),
867
- ...(binding
868
- ? {
869
- channelBinding: {
870
- sourceChannel: binding.sourceChannel,
871
- externalChatId: binding.externalChatId,
872
- externalUserId: binding.externalUserId,
873
- displayName: binding.displayName,
874
- username: binding.username,
875
- },
876
- }
877
- : {}),
878
- ...(originChannel
879
- ? { conversationOriginChannel: originChannel }
880
- : {}),
881
- ...(assistantAttention ? { assistantAttention } : {}),
882
- ...(displayMeta.get(c.id)?.isPinned
883
- ? {
884
- isPinned: true,
885
- displayOrder: displayMeta.get(c.id)!.displayOrder,
886
- }
887
- : displayMeta.get(c.id)?.displayOrder != null
888
- ? {
889
- displayOrder: displayMeta.get(c.id)!.displayOrder,
890
- }
891
- : {}),
892
- };
893
- }),
1007
+ conversations: conversations.map((conversation) =>
1008
+ this.serializeConversationSummary({
1009
+ conversation,
1010
+ binding: bindings.get(conversation.id),
1011
+ attentionState: attentionStates.get(conversation.id),
1012
+ displayMeta: displayMeta.get(conversation.id),
1013
+ parentCache,
1014
+ }),
1015
+ ),
894
1016
  hasMore: offset + conversations.length < totalCount,
895
1017
  });
896
1018
  },
897
1019
  },
898
1020
  ...conversationAttentionRouteDefinitions(),
899
1021
 
900
- ...(this.conversationManagementDeps
901
- ? conversationManagementRouteDefinitions(
902
- this.conversationManagementDeps,
903
- )
1022
+ ...(conversationManagementDeps
1023
+ ? conversationManagementRouteDefinitions(conversationManagementDeps)
904
1024
  : []),
905
1025
 
906
1026
  {
@@ -990,81 +1110,15 @@ export class RuntimeHttpServer {
990
1110
  endpoint: "conversations/:id",
991
1111
  method: "GET",
992
1112
  handler: ({ params }) => {
993
- const conversation = getConversation(params.id);
994
- if (!conversation) {
1113
+ const detail = this.buildConversationDetailResponse(params.id);
1114
+ if (!detail) {
995
1115
  return httpError(
996
1116
  "NOT_FOUND",
997
1117
  `Conversation ${params.id} not found`,
998
1118
  404,
999
1119
  );
1000
1120
  }
1001
- const bindings =
1002
- externalConversationStore.getBindingsForConversations([
1003
- conversation.id,
1004
- ]);
1005
- const attentionStates = getAttentionStateByConversationIds([
1006
- conversation.id,
1007
- ]);
1008
- const binding = bindings.get(conversation.id);
1009
- const originChannel = parseChannelId(conversation.originChannel);
1010
- const attn = attentionStates.get(conversation.id);
1011
- const assistantAttention = attn
1012
- ? {
1013
- hasUnseenLatestAssistantMessage:
1014
- attn.latestAssistantMessageAt != null &&
1015
- (attn.lastSeenAssistantMessageAt == null ||
1016
- attn.lastSeenAssistantMessageAt <
1017
- attn.latestAssistantMessageAt),
1018
- ...(attn.latestAssistantMessageAt != null
1019
- ? {
1020
- latestAssistantMessageAt: attn.latestAssistantMessageAt,
1021
- }
1022
- : {}),
1023
- ...(attn.lastSeenAssistantMessageAt != null
1024
- ? {
1025
- lastSeenAssistantMessageAt:
1026
- attn.lastSeenAssistantMessageAt,
1027
- }
1028
- : {}),
1029
- ...(attn.lastSeenConfidence != null
1030
- ? { lastSeenConfidence: attn.lastSeenConfidence }
1031
- : {}),
1032
- ...(attn.lastSeenSignalType != null
1033
- ? { lastSeenSignalType: attn.lastSeenSignalType }
1034
- : {}),
1035
- }
1036
- : undefined;
1037
- return Response.json({
1038
- conversation: {
1039
- id: conversation.id,
1040
- title: conversation.title ?? "Untitled",
1041
- createdAt: conversation.createdAt,
1042
- updatedAt: conversation.updatedAt,
1043
- conversationType:
1044
- conversation.conversationType === "private"
1045
- ? "private"
1046
- : "standard",
1047
- source: conversation.source ?? "user",
1048
- ...(conversation.scheduleJobId
1049
- ? { scheduleJobId: conversation.scheduleJobId }
1050
- : {}),
1051
- ...(binding
1052
- ? {
1053
- channelBinding: {
1054
- sourceChannel: binding.sourceChannel,
1055
- externalChatId: binding.externalChatId,
1056
- externalUserId: binding.externalUserId,
1057
- displayName: binding.displayName,
1058
- username: binding.username,
1059
- },
1060
- }
1061
- : {}),
1062
- ...(originChannel
1063
- ? { conversationOriginChannel: originChannel }
1064
- : {}),
1065
- ...(assistantAttention ? { assistantAttention } : {}),
1066
- },
1067
- });
1121
+ return Response.json(detail);
1068
1122
  },
1069
1123
  },
1070
1124
 
@@ -1110,6 +1164,7 @@ export class RuntimeHttpServer {
1110
1164
  ...slackShareRouteDefinitions(),
1111
1165
  ...twilioRouteDefinitions(),
1112
1166
  ...channelReadinessRouteDefinitions(),
1167
+ ...oauthAppsRouteDefinitions(),
1113
1168
  ...attachmentRouteDefinitions(),
1114
1169
 
1115
1170
  ...(this.getWatchDeps
@@ -157,6 +157,7 @@ export interface SendMessageDeps {
157
157
  filename: string;
158
158
  mimeType: string;
159
159
  data: string;
160
+ filePath?: string;
160
161
  }>;
161
162
  }
162
163
 
@@ -447,7 +447,11 @@ export function buildExportVBundle(
447
447
 
448
448
  // Walk the entire workspace directory, including binary files (DB,
449
449
  // attachments) but skipping large/regenerable subdirectories.
450
- if (workspaceDir && existsSync(workspaceDir) && lstatSync(workspaceDir).isDirectory()) {
450
+ if (
451
+ workspaceDir &&
452
+ existsSync(workspaceDir) &&
453
+ lstatSync(workspaceDir).isDirectory()
454
+ ) {
451
455
  files.push(
452
456
  ...walkDirectory(workspaceDir, "workspace", {
453
457
  includeBinary: true,
@@ -174,6 +174,47 @@ function resolveRequesterTarget(params: {
174
174
  };
175
175
  }
176
176
 
177
+ /**
178
+ * Deliver the verification code directly to the requester's DM on Slack,
179
+ * removing the need for the guardian to manually share it.
180
+ */
181
+ export async function deliverVerificationCodeToRequester(params: {
182
+ replyCallbackUrl: string;
183
+ requesterChatId: string;
184
+ verificationCode: string;
185
+ assistantId: string;
186
+ bearerToken?: string;
187
+ channel?: string;
188
+ requesterExternalUserId?: string;
189
+ }): Promise<DeliveryResult> {
190
+ const text =
191
+ `Great news — your access request was approved! ` +
192
+ `Your verification code is: ${params.verificationCode}. ` +
193
+ `Reply with it here to complete verification. The code expires in 10 minutes.`;
194
+
195
+ const target = resolveRequesterTarget(params);
196
+
197
+ try {
198
+ await deliverChannelReply(
199
+ target.callbackUrl,
200
+ {
201
+ chatId: target.chatId,
202
+ text,
203
+ assistantId: params.assistantId,
204
+ },
205
+ params.bearerToken,
206
+ );
207
+ return { ok: true };
208
+ } catch (err) {
209
+ log.error(
210
+ { err, requesterChatId: params.requesterChatId },
211
+ "Failed to deliver verification code to requester",
212
+ );
213
+ const reason = err instanceof Error ? err.message : String(err);
214
+ return { ok: false, reason };
215
+ }
216
+ }
217
+
177
218
  /**
178
219
  * Notify the requester that the guardian has approved their access request
179
220
  * and they should enter the verification code they receive from the guardian.
@@ -34,11 +34,12 @@ import {
34
34
  deleteApp,
35
35
  deleteAppRecord,
36
36
  getApp,
37
+ getAppDirPath,
37
38
  getAppPreview,
38
- getAppsDir,
39
39
  isMultifileApp,
40
40
  listApps,
41
41
  queryAppRecords,
42
+ resolveAppDir,
42
43
  updateApp,
43
44
  updateAppRecord,
44
45
  } from "../../memory/app-store.js";
@@ -289,7 +290,7 @@ async function installGalleryApp(
289
290
  });
290
291
 
291
292
  if (galleryApp.formatVersion === 2 && galleryApp.sourceFiles) {
292
- const appDir = join(getAppsDir(), app.id);
293
+ const appDir = getAppDirPath(app.id);
293
294
  for (const [relPath, content] of Object.entries(galleryApp.sourceFiles)) {
294
295
  const fullPath = join(appDir, relPath);
295
296
  mkdirSync(dirname(fullPath), { recursive: true });
@@ -671,7 +672,7 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
671
672
  let html = app.htmlDefinition;
672
673
 
673
674
  if (isMultifileApp(app)) {
674
- const appDir = join(getAppsDir(), appId);
675
+ const appDir = getAppDirPath(appId);
675
676
  const distIndex = join(appDir, "dist", "index.html");
676
677
  if (!existsSync(distIndex)) {
677
678
  const result = await compileApp(appDir);
@@ -689,8 +690,10 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
689
690
  }
690
691
  }
691
692
 
693
+ const { dirName } = resolveAppDir(app.id);
692
694
  return Response.json({
693
695
  appId: app.id,
696
+ dirName,
694
697
  name: app.name,
695
698
  html,
696
699
  });
@@ -8,7 +8,11 @@ import { extname, join } from "node:path";
8
8
  import JSZip from "jszip";
9
9
 
10
10
  import type { AppManifest } from "../../bundler/manifest.js";
11
- import { getApp, getAppsDir, isMultifileApp } from "../../memory/app-store.js";
11
+ import {
12
+ getApp,
13
+ getAppDirPath,
14
+ isMultifileApp,
15
+ } from "../../memory/app-store.js";
12
16
  import * as sharedAppLinksStore from "../../memory/shared-app-links-store.js";
13
17
  import { getLogger } from "../../util/logger.js";
14
18
  import { httpError } from "../http-errors.js";
@@ -109,7 +113,7 @@ ${noncedHtml}
109
113
  * Falls back to a "not compiled yet" message if dist/index.html is missing.
110
114
  */
111
115
  function serveMultifileApp(appId: string, appName: string): Response {
112
- const distDir = join(getAppsDir(), appId, "dist");
116
+ const distDir = join(getAppDirPath(appId), "dist");
113
117
  const indexPath = join(distDir, "index.html");
114
118
 
115
119
  if (!existsSync(indexPath)) {
@@ -203,7 +207,7 @@ export function handleServeDistFile(appId: string, filename: string): Response {
203
207
  return httpError("BAD_REQUEST", "Invalid filename", 400);
204
208
  }
205
209
 
206
- const filePath = join(getAppsDir(), appId, "dist", filename);
210
+ const filePath = join(getAppDirPath(appId), "dist", filename);
207
211
  if (!existsSync(filePath)) {
208
212
  return httpError("NOT_FOUND", "File not found", 404);
209
213
  }
@@ -139,6 +139,7 @@ export async function handleConfirm(
139
139
  source: "button",
140
140
  },
141
141
  );
142
+
142
143
  return Response.json({ accepted: true });
143
144
  }
144
145
 
@@ -27,6 +27,7 @@ import type {
27
27
  } from "../../http-types.js";
28
28
  import {
29
29
  deliverVerificationCodeToGuardian,
30
+ deliverVerificationCodeToRequester,
30
31
  type DeliveryResult,
31
32
  handleAccessRequestDecision,
32
33
  notifyRequesterOfApproval,
@@ -688,7 +689,38 @@ async function handleAccessRequestApproval(
688
689
  }
689
690
  }
690
691
 
691
- if (codeDelivered) {
692
+ // On Slack, auto-deliver the verification code directly to the requester's
693
+ // DM so the guardian doesn't have to manually share it. The identity binding
694
+ // still protects against abuse — only the bound user can consume the code.
695
+ let requesterCodeDelivered = false;
696
+ if (
697
+ codeDelivered &&
698
+ approval.channel === "slack" &&
699
+ decisionResult.verificationCode
700
+ ) {
701
+ const requesterCodeResult = await deliverVerificationCodeToRequester({
702
+ replyCallbackUrl,
703
+ requesterChatId: approval.requesterChatId,
704
+ verificationCode: decisionResult.verificationCode,
705
+ assistantId,
706
+ bearerToken,
707
+ channel: approval.channel,
708
+ requesterExternalUserId: approval.requesterExternalUserId,
709
+ });
710
+ if (requesterCodeResult.ok) {
711
+ requesterCodeDelivered = true;
712
+ } else {
713
+ log.error(
714
+ { reason: requesterCodeResult.reason, approvalId: approval.id },
715
+ "Failed to auto-deliver verification code to requester on Slack",
716
+ );
717
+ }
718
+ }
719
+
720
+ // Skip the separate approval notification when the requester already
721
+ // received the verification code directly (on Slack both messages go
722
+ // to the same DM, so sending both is redundant).
723
+ if (codeDelivered && !requesterCodeDelivered) {
692
724
  await notifyRequesterOfApproval({
693
725
  replyCallbackUrl,
694
726
  requesterChatId: approval.requesterChatId,
@@ -697,7 +729,7 @@ async function handleAccessRequestApproval(
697
729
  channel: approval.channel,
698
730
  requesterExternalUserId: approval.requesterExternalUserId,
699
731
  });
700
- } else {
732
+ } else if (!codeDelivered) {
701
733
  // Let the requester know something went wrong without revealing details
702
734
  await notifyRequesterOfDeliveryFailure({
703
735
  replyCallbackUrl,