@vellumai/assistant 0.5.1 → 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 (338) 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__/attachments-store.test.ts +169 -21
  14. package/src/__tests__/attachments.test.ts +115 -1
  15. package/src/__tests__/btw-routes.test.ts +1 -0
  16. package/src/__tests__/canonical-guardian-store.test.ts +38 -0
  17. package/src/__tests__/channel-reply-delivery.test.ts +55 -0
  18. package/src/__tests__/checker.test.ts +54 -0
  19. package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
  20. package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
  21. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  22. package/src/__tests__/config-schema-cmd.test.ts +68 -21
  23. package/src/__tests__/config-schema.test.ts +1 -1
  24. package/src/__tests__/conversation-agent-loop-overflow.test.ts +149 -5
  25. package/src/__tests__/conversation-agent-loop.test.ts +290 -2
  26. package/src/__tests__/conversation-attachments.test.ts +17 -19
  27. package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
  28. package/src/__tests__/conversation-disk-view.test.ts +810 -0
  29. package/src/__tests__/conversation-error.test.ts +1 -1
  30. package/src/__tests__/conversation-fork-crud.test.ts +551 -0
  31. package/src/__tests__/conversation-fork-route.test.ts +386 -0
  32. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  33. package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
  34. package/src/__tests__/conversation-media-retry.test.ts +8 -2
  35. package/src/__tests__/conversation-queue.test.ts +36 -1
  36. package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
  37. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  38. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
  39. package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
  40. package/src/__tests__/conversation-skill-tools.test.ts +4 -9
  41. package/src/__tests__/conversation-slash-commands.test.ts +149 -0
  42. package/src/__tests__/conversation-store.test.ts +24 -21
  43. package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
  44. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
  45. package/src/__tests__/conversation-title-service.test.ts +137 -0
  46. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
  47. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
  48. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
  49. package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
  50. package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
  51. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  52. package/src/__tests__/credential-vault-unit.test.ts +5 -10
  53. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  54. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
  55. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
  56. package/src/__tests__/diagnostics-export.test.ts +70 -1
  57. package/src/__tests__/first-greeting.test.ts +80 -0
  58. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  59. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
  60. package/src/__tests__/history-repair.test.ts +32 -10
  61. package/src/__tests__/http-conversation-lineage.test.ts +251 -0
  62. package/src/__tests__/image-source-path-reinject.test.ts +136 -0
  63. package/src/__tests__/llm-context-normalization.test.ts +1116 -0
  64. package/src/__tests__/llm-context-route-provider.test.ts +217 -0
  65. package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
  66. package/src/__tests__/media-generate-image.test.ts +47 -94
  67. package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
  68. package/src/__tests__/memory-recall-quality.test.ts +5 -5
  69. package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
  70. package/src/__tests__/migration-export-http.test.ts +3 -1
  71. package/src/__tests__/migration-import-commit-http.test.ts +18 -4
  72. package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
  73. package/src/__tests__/mime-builder.test.ts +3 -2
  74. package/src/__tests__/non-member-access-request.test.ts +12 -1
  75. package/src/__tests__/notification-decision-identity.test.ts +52 -0
  76. package/src/__tests__/oauth-apps-routes.test.ts +103 -0
  77. package/src/__tests__/oauth-store.test.ts +115 -0
  78. package/src/__tests__/provider-error-scenarios.test.ts +1 -3
  79. package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
  80. package/src/__tests__/recording-handler.test.ts +17 -0
  81. package/src/__tests__/registry.test.ts +3 -8
  82. package/src/__tests__/relay-server.test.ts +1 -1
  83. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
  84. package/src/__tests__/schema-transforms.test.ts +165 -5
  85. package/src/__tests__/server-history-render.test.ts +2 -2
  86. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  87. package/src/__tests__/slack-inbound-verification.test.ts +2 -2
  88. package/src/__tests__/starter-task-flow.test.ts +1 -0
  89. package/src/__tests__/suggestion-routes.test.ts +443 -0
  90. package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
  91. package/src/__tests__/swarm-recursion.test.ts +1 -0
  92. package/src/__tests__/swarm-tool.test.ts +1 -0
  93. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
  94. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
  95. package/src/__tests__/top-level-renderer.test.ts +22 -0
  96. package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
  97. package/src/__tests__/web-fetch.test.ts +6 -2
  98. package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
  99. package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
  100. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
  101. package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
  102. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
  103. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
  104. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
  105. package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
  106. package/src/agent/attachments.ts +27 -1
  107. package/src/agent/loop.ts +29 -1
  108. package/src/avatar/traits-png-sync.ts +80 -25
  109. package/src/bundler/app-bundler.ts +4 -4
  110. package/src/calls/call-domain.ts +1 -0
  111. package/src/calls/voice-session-bridge.ts +1 -0
  112. package/src/cli/commands/auth.ts +92 -0
  113. package/src/cli/commands/avatar.ts +7 -6
  114. package/src/cli/commands/config.ts +2 -0
  115. package/src/cli/commands/oauth/providers.ts +29 -0
  116. package/src/cli/program.ts +12 -0
  117. package/src/cli.ts +15 -48
  118. package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
  119. package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
  120. package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
  121. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
  122. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
  123. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
  124. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
  125. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
  126. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
  127. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
  128. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
  129. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
  130. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
  131. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
  132. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
  133. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
  134. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
  135. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  136. package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
  137. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
  138. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
  139. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
  140. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  141. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
  142. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  143. package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
  144. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
  145. package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
  146. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
  147. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
  148. package/src/config/bundled-tool-registry.ts +2 -14
  149. package/src/config/feature-flag-registry.json +8 -0
  150. package/src/config/loader.ts +64 -0
  151. package/src/config/raw-config-utils.ts +30 -0
  152. package/src/config/schema-utils.ts +28 -7
  153. package/src/config/schema.ts +8 -0
  154. package/src/config/schemas/elevenlabs.ts +18 -0
  155. package/src/config/schemas/memory-lifecycle.ts +4 -2
  156. package/src/config/schemas/memory-storage.ts +1 -1
  157. package/src/config/schemas/services.ts +8 -6
  158. package/src/contacts/contact-store.ts +13 -6
  159. package/src/contacts/contacts-write.ts +0 -1
  160. package/src/context/window-manager.ts +13 -2
  161. package/src/daemon/conversation-agent-loop-handlers.ts +48 -7
  162. package/src/daemon/conversation-agent-loop.ts +56 -19
  163. package/src/daemon/conversation-attachments.ts +18 -36
  164. package/src/daemon/conversation-error.ts +2 -1
  165. package/src/daemon/conversation-history.ts +18 -4
  166. package/src/daemon/conversation-lifecycle.ts +39 -15
  167. package/src/daemon/conversation-messaging.ts +70 -26
  168. package/src/daemon/conversation-process.ts +58 -34
  169. package/src/daemon/conversation-runtime-assembly.ts +21 -38
  170. package/src/daemon/conversation-slash.ts +121 -256
  171. package/src/daemon/conversation-surfaces.ts +143 -20
  172. package/src/daemon/conversation-tool-setup.ts +0 -6
  173. package/src/daemon/conversation-workspace.ts +21 -1
  174. package/src/daemon/conversation.ts +51 -29
  175. package/src/daemon/first-greeting.ts +35 -0
  176. package/src/daemon/handlers/config-embeddings.ts +148 -0
  177. package/src/daemon/handlers/config-model.ts +71 -26
  178. package/src/daemon/handlers/conversations.ts +0 -23
  179. package/src/daemon/handlers/recording.ts +26 -21
  180. package/src/daemon/host-cu-proxy.ts +2 -2
  181. package/src/daemon/lifecycle.ts +106 -64
  182. package/src/daemon/message-protocol.ts +3 -0
  183. package/src/daemon/message-types/conversations.ts +19 -0
  184. package/src/daemon/message-types/messages.ts +1 -0
  185. package/src/daemon/message-types/shared.ts +2 -0
  186. package/src/daemon/message-types/surfaces.ts +2 -0
  187. package/src/daemon/message-types/upgrades.ts +23 -0
  188. package/src/daemon/server.ts +83 -12
  189. package/src/daemon/shutdown-handlers.ts +8 -5
  190. package/src/daemon/startup-error.ts +9 -0
  191. package/src/daemon/tool-side-effects.ts +11 -28
  192. package/src/events/tool-permission-telemetry-listener.ts +1 -3
  193. package/src/instrument.ts +0 -4
  194. package/src/media/app-icon-generator.ts +2 -2
  195. package/src/memory/app-git-service.ts +28 -16
  196. package/src/memory/app-store.ts +230 -41
  197. package/src/memory/attachments-store.ts +558 -130
  198. package/src/memory/conversation-attention-store.ts +70 -0
  199. package/src/memory/conversation-crud.ts +442 -3
  200. package/src/memory/conversation-directories.ts +125 -0
  201. package/src/memory/conversation-disk-view.ts +390 -0
  202. package/src/memory/conversation-key-store.ts +17 -5
  203. package/src/memory/conversation-queries.ts +5 -1
  204. package/src/memory/conversation-title-service.ts +21 -49
  205. package/src/memory/db-init.ts +28 -0
  206. package/src/memory/embedding-backend.ts +42 -53
  207. package/src/memory/embedding-gemini.test.ts +4 -4
  208. package/src/memory/embedding-local.ts +1 -3
  209. package/src/memory/embedding-ollama.ts +1 -3
  210. package/src/memory/embedding-openai.ts +1 -3
  211. package/src/memory/indexer.ts +9 -7
  212. package/src/memory/items-extractor.ts +42 -13
  213. package/src/memory/job-handlers/conversation-starters.ts +6 -1
  214. package/src/memory/job-handlers/embedding.test.ts +1 -4
  215. package/src/memory/llm-request-log-store.ts +100 -1
  216. package/src/memory/migrations/102-alter-table-columns.ts +5 -0
  217. package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
  218. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
  219. package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
  220. package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
  221. package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
  222. package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
  223. package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
  224. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
  225. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
  226. package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
  227. package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
  228. package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
  229. package/src/memory/migrations/index.ts +7 -0
  230. package/src/memory/migrations/registry.ts +13 -0
  231. package/src/memory/retriever.test.ts +601 -2
  232. package/src/memory/retriever.ts +85 -9
  233. package/src/memory/schema/conversations.ts +6 -0
  234. package/src/memory/schema/infrastructure.ts +13 -7
  235. package/src/memory/schema/oauth.ts +6 -0
  236. package/src/messaging/providers/gmail/mime-builder.ts +3 -1
  237. package/src/notifications/copy-composer.ts +26 -0
  238. package/src/notifications/decision-engine.ts +14 -1
  239. package/src/notifications/emit-signal.ts +1 -1
  240. package/src/notifications/signal.ts +36 -0
  241. package/src/oauth/byo-connection.test.ts +1 -45
  242. package/src/oauth/byo-connection.ts +2 -8
  243. package/src/oauth/connect-orchestrator.ts +15 -11
  244. package/src/oauth/connection-resolver.test.ts +191 -0
  245. package/src/oauth/connection-resolver.ts +66 -38
  246. package/src/oauth/connection.ts +0 -1
  247. package/src/oauth/oauth-store.ts +97 -47
  248. package/src/oauth/platform-connection.test.ts +0 -1
  249. package/src/oauth/platform-connection.ts +11 -3
  250. package/src/oauth/seed-providers.ts +78 -3
  251. package/src/oauth/token-persistence.ts +16 -10
  252. package/src/permissions/checker.ts +71 -8
  253. package/src/prompts/templates/BOOTSTRAP.md +2 -0
  254. package/src/providers/anthropic/client.ts +8 -1
  255. package/src/providers/failover.ts +4 -1
  256. package/src/providers/gemini/client.ts +50 -0
  257. package/src/providers/model-catalog.ts +92 -0
  258. package/src/providers/model-intents.ts +29 -20
  259. package/src/providers/openai/client.ts +49 -0
  260. package/src/providers/types.ts +2 -0
  261. package/src/runtime/access-request-helper.ts +16 -7
  262. package/src/runtime/auth/credential-service.ts +3 -1
  263. package/src/runtime/auth/route-policy.ts +14 -1
  264. package/src/runtime/btw-sidechain.ts +101 -0
  265. package/src/runtime/channel-reply-delivery.ts +17 -1
  266. package/src/runtime/http-router.ts +3 -1
  267. package/src/runtime/http-server.ts +196 -141
  268. package/src/runtime/http-types.ts +1 -0
  269. package/src/runtime/migrations/vbundle-builder.ts +5 -1
  270. package/src/runtime/routes/access-request-decision.ts +41 -0
  271. package/src/runtime/routes/app-management-routes.ts +6 -3
  272. package/src/runtime/routes/app-routes.ts +7 -3
  273. package/src/runtime/routes/approval-routes.ts +1 -0
  274. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
  275. package/src/runtime/routes/attachment-routes.ts +45 -15
  276. package/src/runtime/routes/btw-routes.ts +21 -61
  277. package/src/runtime/routes/conversation-management-routes.ts +68 -0
  278. package/src/runtime/routes/conversation-query-routes.ts +180 -10
  279. package/src/runtime/routes/conversation-routes.ts +222 -28
  280. package/src/runtime/routes/conversation-starter-routes.ts +9 -11
  281. package/src/runtime/routes/diagnostics-routes.ts +1 -0
  282. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
  283. package/src/runtime/routes/llm-context-normalization.ts +1199 -0
  284. package/src/runtime/routes/log-export-routes.ts +3 -0
  285. package/src/runtime/routes/memory-item-routes.test.ts +34 -0
  286. package/src/runtime/routes/memory-item-routes.ts +4 -0
  287. package/src/runtime/routes/migration-routes.ts +4 -1
  288. package/src/runtime/routes/oauth-apps.ts +291 -0
  289. package/src/runtime/routes/secret-routes.ts +28 -1
  290. package/src/runtime/routes/settings-routes.ts +14 -0
  291. package/src/runtime/routes/trace-event-routes.ts +4 -1
  292. package/src/schedule/schedule-store.ts +9 -21
  293. package/src/security/secure-keys.ts +21 -0
  294. package/src/signals/bash.ts +1 -1
  295. package/src/swarm/backend-claude-code.ts +3 -6
  296. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  297. package/src/telemetry/usage-telemetry-reporter.ts +3 -1
  298. package/src/tools/AGENTS.md +6 -10
  299. package/src/tools/apps/executors.ts +17 -232
  300. package/src/tools/claude-code/claude-code.ts +2 -3
  301. package/src/tools/credentials/vault.ts +7 -12
  302. package/src/tools/host-filesystem/read.ts +13 -10
  303. package/src/tools/network/__tests__/web-search.test.ts +4 -2
  304. package/src/tools/schedule/list.ts +2 -7
  305. package/src/tools/schema-transforms.ts +5 -0
  306. package/src/tools/shared/filesystem/format-diff.ts +2 -7
  307. package/src/tools/skills/execute.ts +1 -1
  308. package/src/tools/tool-manifest.ts +0 -6
  309. package/src/tools/ui-surface/definitions.ts +2 -2
  310. package/src/util/device-id.ts +28 -5
  311. package/src/util/platform.ts +6 -0
  312. package/src/util/pricing.ts +1 -0
  313. package/src/util/retry.ts +1 -3
  314. package/src/workspace/migrations/002-backfill-installation-id.ts +23 -12
  315. package/src/workspace/migrations/003-seed-device-id.ts +3 -4
  316. package/src/workspace/migrations/006-services-config.ts +5 -0
  317. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
  318. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
  319. package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
  320. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
  321. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
  322. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
  323. package/src/workspace/migrations/registry.ts +10 -0
  324. package/src/workspace/top-level-renderer.ts +12 -0
  325. package/src/__tests__/asset-materialize-tool.test.ts +0 -523
  326. package/src/__tests__/asset-search-tool.test.ts +0 -536
  327. package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
  328. package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
  329. package/src/__tests__/media-visibility-policy.test.ts +0 -190
  330. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
  331. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
  332. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
  333. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
  334. package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
  335. package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
  336. package/src/daemon/media-visibility-policy.ts +0 -59
  337. package/src/tools/assets/materialize.ts +0 -248
  338. package/src/tools/assets/search.ts +0 -400
@@ -100,6 +100,8 @@ export interface DynamicPageSurfaceData {
100
100
  width?: number;
101
101
  height?: number;
102
102
  appId?: string;
103
+ /** Filesystem directory name for this app (may differ from `appId`). */
104
+ dirName?: string;
103
105
  reloadGeneration?: number;
104
106
  status?: string;
105
107
  preview?: DynamicPagePreview;
@@ -0,0 +1,23 @@
1
+ /** Broadcast to connected clients when a service group update is about to begin. */
2
+ export interface ServiceGroupUpdateStarting {
3
+ type: "service_group_update_starting";
4
+ /** The version being upgraded to. */
5
+ targetVersion: string;
6
+ /** Estimated seconds of downtime. */
7
+ expectedDowntimeSeconds: number;
8
+ }
9
+
10
+ /** Broadcast to connected clients when a service group update has completed. */
11
+ export interface ServiceGroupUpdateComplete {
12
+ type: "service_group_update_complete";
13
+ /** The version that was installed (may differ from target if rolled back). */
14
+ installedVersion: string;
15
+ /** Whether the update succeeded or rolled back. */
16
+ success: boolean;
17
+ /** If rolled back, the version reverted to. */
18
+ rolledBackToVersion?: string;
19
+ }
20
+
21
+ export type _UpgradesServerMessages =
22
+ | ServiceGroupUpdateStarting
23
+ | ServiceGroupUpdateComplete;
@@ -6,6 +6,7 @@ import {
6
6
  getAcpSessionManager,
7
7
  setBroadcastToAllClients,
8
8
  } from "../acp/index.js";
9
+ import { enrichMessageWithSourcePaths } from "../agent/attachments.js";
9
10
  import {
10
11
  createAssistantMessage,
11
12
  createUserMessage,
@@ -34,12 +35,14 @@ import {
34
35
  } from "../memory/canonical-guardian-store.js";
35
36
  import {
36
37
  addMessage,
38
+ getConversation,
37
39
  getConversationMemoryScopeId,
38
40
  getConversationType,
39
41
  provenanceFromTrustContext,
40
42
  setConversationOriginChannelIfUnset,
41
43
  setConversationOriginInterfaceIfUnset,
42
44
  } from "../memory/conversation-crud.js";
45
+ import { updateMetaFile } from "../memory/conversation-disk-view.js";
43
46
  import { getOrCreateConversation } from "../memory/conversation-key-store.js";
44
47
  import { buildSystemPrompt } from "../prompts/system-prompt.js";
45
48
  import { resolveManagedProxyContext } from "../providers/managed-proxy/context.js";
@@ -75,7 +78,7 @@ import {
75
78
  } from "./conversation.js";
76
79
  import { ConversationEvictor } from "./conversation-evictor.js";
77
80
  import { resolveChannelCapabilities } from "./conversation-runtime-assembly.js";
78
- import { resolveSlash } from "./conversation-slash.js";
81
+ import { resolveSlash, type SlashContext } from "./conversation-slash.js";
79
82
  import { undoLastMessage } from "./handlers/conversations.js";
80
83
  import { parseIdentityFields } from "./handlers/identity.js";
81
84
  import type {
@@ -878,6 +881,7 @@ export class DaemonServer {
878
881
  filename: string;
879
882
  mimeType: string;
880
883
  data: string;
884
+ filePath?: string;
881
885
  }[];
882
886
  }> {
883
887
  const ingressCheck = checkIngressForSecrets(content);
@@ -960,12 +964,22 @@ export class DaemonServer {
960
964
  });
961
965
 
962
966
  const attachments = attachmentIds
963
- ? attachmentsStore.getAttachmentsByIds(attachmentIds).map((a) => ({
964
- id: a.id,
965
- filename: a.originalFilename,
966
- mimeType: a.mimeType,
967
- data: a.dataBase64,
968
- }))
967
+ ? (() => {
968
+ const resolved = attachmentsStore.getAttachmentsByIds(attachmentIds, {
969
+ hydrateFileData: true,
970
+ });
971
+ const sourcePaths =
972
+ attachmentsStore.getSourcePathsForAttachments(attachmentIds);
973
+ return resolved.map((a) => ({
974
+ id: a.id,
975
+ filename: a.originalFilename,
976
+ mimeType: a.mimeType,
977
+ data: a.dataBase64,
978
+ ...(sourcePaths.has(a.id)
979
+ ? { filePath: sourcePaths.get(a.id) }
980
+ : {}),
981
+ }));
982
+ })()
969
983
  : [];
970
984
 
971
985
  return { conversation, attachments };
@@ -1057,14 +1071,32 @@ export class DaemonServer {
1057
1071
  sourceInterface,
1058
1072
  );
1059
1073
 
1060
- const slashResult = await resolveSlash(content);
1074
+ const config = getConfig();
1075
+ const serverInterfaceCtx = conversation.getTurnInterfaceContext();
1076
+ const slashContext: SlashContext = {
1077
+ messageCount: conversation.getMessages().length,
1078
+ inputTokens: conversation.usageStats.inputTokens,
1079
+ outputTokens: conversation.usageStats.outputTokens,
1080
+ maxInputTokens: config.contextWindow.maxInputTokens,
1081
+ model: config.services.inference.model,
1082
+ provider: config.services.inference.provider,
1083
+ estimatedCost: conversation.usageStats.estimatedCost,
1084
+ userMessageInterface: serverInterfaceCtx?.userMessageInterface,
1085
+ };
1086
+ const slashResult = await resolveSlash(content, slashContext);
1061
1087
 
1062
1088
  if (slashResult.kind === "unknown") {
1063
1089
  const serverTurnCtx = conversation.getTurnChannelContext();
1064
- const serverInterfaceCtx = conversation.getTurnInterfaceContext();
1065
1090
  const serverProvenance = provenanceFromTrustContext(
1066
1091
  conversation.trustContext,
1067
1092
  );
1093
+ const imageSourcePaths: Record<string, string> = {};
1094
+ for (let i = 0; i < attachments.length; i++) {
1095
+ const a = attachments[i];
1096
+ if (a.filePath && a.mimeType.toLowerCase().startsWith("image/")) {
1097
+ imageSourcePaths[`${i}:${a.filename}`] = a.filePath;
1098
+ }
1099
+ }
1068
1100
  const serverChannelMeta = {
1069
1101
  ...serverProvenance,
1070
1102
  ...(serverTurnCtx
@@ -1080,15 +1112,19 @@ export class DaemonServer {
1080
1112
  serverInterfaceCtx.assistantMessageInterface,
1081
1113
  }
1082
1114
  : {}),
1115
+ ...(Object.keys(imageSourcePaths).length > 0
1116
+ ? { imageSourcePaths }
1117
+ : {}),
1083
1118
  };
1084
- const userMsg = createUserMessage(content, attachments);
1119
+ const cleanMsg = createUserMessage(content, attachments);
1120
+ const llmMsg = enrichMessageWithSourcePaths(cleanMsg, attachments);
1085
1121
  const persisted = await addMessage(
1086
1122
  conversationId,
1087
1123
  "user",
1088
- JSON.stringify(userMsg.content),
1124
+ JSON.stringify(cleanMsg.content),
1089
1125
  serverChannelMeta,
1090
1126
  );
1091
- conversation.getMessages().push(userMsg);
1127
+ conversation.getMessages().push(llmMsg);
1092
1128
 
1093
1129
  if (serverTurnCtx) {
1094
1130
  try {
@@ -1117,6 +1153,21 @@ export class DaemonServer {
1117
1153
  }
1118
1154
  }
1119
1155
 
1156
+ // Rewrite meta.json so the on-disk metadata reflects the origin channel
1157
+ if (serverTurnCtx || serverInterfaceCtx) {
1158
+ try {
1159
+ const convForMeta = getConversation(conversationId);
1160
+ if (convForMeta) {
1161
+ updateMetaFile(convForMeta);
1162
+ }
1163
+ } catch (err) {
1164
+ log.warn(
1165
+ { err, conversationId },
1166
+ "Failed to update disk meta (best-effort)",
1167
+ );
1168
+ }
1169
+ }
1170
+
1120
1171
  const assistantMsg = createAssistantMessage(slashResult.message);
1121
1172
  await addMessage(
1122
1173
  conversationId,
@@ -1198,9 +1249,29 @@ export class DaemonServer {
1198
1249
  * Look up an active conversation that owns a given surfaceId.
1199
1250
  */
1200
1251
  findConversationBySurfaceId(surfaceId: string): Conversation | undefined {
1252
+ // Fast path: exact surfaceId match in surfaceState
1201
1253
  for (const c of this.conversations.values()) {
1202
1254
  if (c.surfaceState.has(surfaceId)) return c;
1203
1255
  }
1256
+
1257
+ // Fallback: standalone app surfaces use "app-open-{appId}" IDs that
1258
+ // were never part of any conversation. Extract the appId and find
1259
+ // a conversation whose surfaceState has a surface for that app.
1260
+ const appOpenPrefix = "app-open-";
1261
+ if (surfaceId.startsWith(appOpenPrefix)) {
1262
+ const appId = surfaceId.slice(appOpenPrefix.length);
1263
+ for (const c of this.conversations.values()) {
1264
+ for (const [, state] of c.surfaceState.entries()) {
1265
+ const data = state.data as Record<string, unknown>;
1266
+ if (data?.appId === appId) {
1267
+ // Register this surfaceId so subsequent lookups are O(1)
1268
+ c.surfaceState.set(surfaceId, state);
1269
+ return c;
1270
+ }
1271
+ }
1272
+ }
1273
+ }
1274
+
1204
1275
  return undefined;
1205
1276
  }
1206
1277
 
@@ -21,8 +21,8 @@ export interface ShutdownDeps {
21
21
  hookManager: HookManager;
22
22
  runtimeHttp: RuntimeHttpServer | null;
23
23
  scheduler: { stop(): void };
24
- memoryWorker: { stop(): void };
25
- qdrantManager: QdrantManager;
24
+ getMemoryWorker: () => { stop(): void } | null;
25
+ getQdrantManager: () => QdrantManager | null;
26
26
  mcpManager: McpServerManager | null;
27
27
  telemetryReporter: { stop(): Promise<void> } | null;
28
28
  cleanupPidFile: () => void;
@@ -106,7 +106,7 @@ export function installShutdownHandlers(deps: ShutdownDeps): void {
106
106
  if (deps.runtimeHttp) await deps.runtimeHttp.stop();
107
107
  await browserManager.closeAllPages();
108
108
  deps.scheduler.stop();
109
- deps.memoryWorker.stop();
109
+ deps.getMemoryWorker()?.stop();
110
110
 
111
111
  if (deps.mcpManager) {
112
112
  try {
@@ -116,7 +116,7 @@ export function installShutdownHandlers(deps: ShutdownDeps): void {
116
116
  }
117
117
  }
118
118
 
119
- await deps.qdrantManager.stop();
119
+ await deps.getQdrantManager()?.stop();
120
120
 
121
121
  // Checkpoint WAL and close SQLite so no writes are lost on exit.
122
122
  // Checkpoint and close are in separate try blocks so that close()
@@ -143,7 +143,10 @@ export function installShutdownHandlers(deps: ShutdownDeps): void {
143
143
  process.on("SIGHUP", shutdown);
144
144
 
145
145
  process.on("unhandledRejection", (reason) => {
146
- log.error({ err: reason }, "Unhandled promise rejection — initiating shutdown");
146
+ log.error(
147
+ { err: reason },
148
+ "Unhandled promise rejection — initiating shutdown",
149
+ );
147
150
  Sentry.captureException(reason);
148
151
  exitCode = 1;
149
152
  void shutdown();
@@ -27,6 +27,14 @@ const DAEMON_ERROR_PREFIX = "DAEMON_ERROR:";
27
27
  * Inspect an error and return a categorized {@link DaemonStartupError}.
28
28
  */
29
29
  export function categorizeDaemonError(err: unknown): DaemonStartupError {
30
+ if (err == null) {
31
+ return {
32
+ error: "UNKNOWN",
33
+ message: String(err),
34
+ detail: "An unexpected error occurred during startup.",
35
+ };
36
+ }
37
+
30
38
  const code = (err as { code?: string }).code ?? "";
31
39
  const name = (err as { name?: string }).name ?? "";
32
40
  const message =
@@ -96,6 +104,7 @@ export function categorizeDaemonError(err: unknown): DaemonStartupError {
96
104
  message.startsWith("Invalid RUNTIME_HTTP_PORT") ||
97
105
  message.startsWith("Invalid integer for GATEWAY_PORT") ||
98
106
  message.startsWith("Invalid integer for RUNTIME_HTTP_PORT") ||
107
+ message.startsWith("Invalid integer for QDRANT_HTTP_PORT") ||
99
108
  /\benv\b.*\bvalidat/i.test(message) ||
100
109
  /\bvalidat.*\benv\b/i.test(message)
101
110
  ) {
@@ -7,11 +7,9 @@
7
7
  * registry entry instead of another if/else branch.
8
8
  */
9
9
 
10
- import { join } from "node:path";
11
-
12
10
  import { compileApp } from "../bundler/app-compiler.js";
13
11
  import { generateAppIcon } from "../media/app-icon-generator.js";
14
- import { getApp, getAppsDir, isMultifileApp } from "../memory/app-store.js";
12
+ import { getApp, getAppDirPath, isMultifileApp } from "../memory/app-store.js";
15
13
  import { deliverVerificationSlack } from "../runtime/verification-outbound-actions.js";
16
14
  import { updatePublishedAppDeployment } from "../services/published-app-updater.js";
17
15
  import type { ToolExecutionResult } from "../tools/types.js";
@@ -51,7 +49,7 @@ function handleAppChange(
51
49
  // Multifile apps need a recompile before refreshing surfaces so the
52
50
  // WebView picks up the latest compiled output.
53
51
  if (app && isMultifileApp(app)) {
54
- const appDir = join(getAppsDir(), appId);
52
+ const appDir = getAppDirPath(appId);
55
53
  void compileApp(appDir)
56
54
  .then((result) => {
57
55
  if (!result.ok) {
@@ -147,25 +145,25 @@ registerHook(
147
145
  },
148
146
  );
149
147
 
150
- // Auto-refresh workspace surfaces when a persisted app is updated.
148
+ // Broadcast app_files_changed when an app is deleted so clients remove it
149
+ // from their cached app lists.
151
150
  registerHook(
152
- "app_update",
153
- (_name, input, _result, { ctx, broadcastToAllClients }) => {
151
+ "app_delete",
152
+ (_name, input, _result, { broadcastToAllClients }) => {
154
153
  const appId = input.app_id as string | undefined;
155
154
  if (appId) {
156
- handleAppChange(ctx, appId, broadcastToAllClients);
155
+ broadcastToAllClients?.({ type: "app_files_changed", appId });
157
156
  }
158
157
  },
159
158
  );
160
159
 
161
- // Broadcast app_files_changed when an app is deleted so clients remove it
162
- // from their cached app lists.
160
+ // Trigger compilation + surface refresh + broadcast when an app is refreshed.
163
161
  registerHook(
164
- "app_delete",
165
- (_name, input, _result, { broadcastToAllClients }) => {
162
+ "app_refresh",
163
+ (_name, input, _result, { ctx, broadcastToAllClients }) => {
166
164
  const appId = input.app_id as string | undefined;
167
165
  if (appId) {
168
- broadcastToAllClients?.({ type: "app_files_changed", appId });
166
+ handleAppChange(ctx, appId, broadcastToAllClients, { fileChange: true });
169
167
  }
170
168
  },
171
169
  );
@@ -179,21 +177,6 @@ registerHook(
179
177
  },
180
178
  );
181
179
 
182
- // Auto-refresh workspace surfaces when app files are edited.
183
- registerHook(
184
- ["app_file_edit", "app_file_write"],
185
- (_name, input, _result, { ctx, broadcastToAllClients }) => {
186
- const appId = input.app_id as string | undefined;
187
- const status = input.status as string | undefined;
188
- if (appId) {
189
- handleAppChange(ctx, appId, broadcastToAllClients, {
190
- fileChange: true,
191
- status,
192
- });
193
- }
194
- },
195
- );
196
-
197
180
  // Broadcast voice config changes to all connected clients so every window
198
181
  // picks up the updated UserDefaults value immediately.
199
182
  registerHook(
@@ -30,9 +30,7 @@ export function registerToolPermissionTelemetryListener(
30
30
  const key = requestId ? `${requestId}:${toolName}` : undefined;
31
31
  if (key && promptedToolCalls.has(key)) {
32
32
  promptedToolCalls.delete(key);
33
- recordLifecycleEvent(
34
- `permission_decided:${toolName}:${decision}`,
35
- );
33
+ recordLifecycleEvent(`permission_decided:${toolName}:${decision}`);
36
34
  }
37
35
  return;
38
36
  }
package/src/instrument.ts CHANGED
@@ -121,7 +121,6 @@ export function setSentryOrganizationId(
121
121
  const CONVERSATION_TAG_KEYS = [
122
122
  "assistant_id",
123
123
  "conversation_id",
124
- "session_id",
125
124
  "message_count",
126
125
  "user_identifier",
127
126
  ] as const;
@@ -148,9 +147,6 @@ export function setSentryConversationContext(
148
147
  ): void {
149
148
  Sentry.setTag("assistant_id", ctx.assistantId);
150
149
  Sentry.setTag("conversation_id", ctx.conversationId);
151
- // session_id mirrors conversation_id — downstream Sentry dashboards and
152
- // alerts may still filter by the legacy tag name.
153
- Sentry.setTag("session_id", ctx.conversationId);
154
150
  Sentry.setTag("message_count", String(ctx.messageCount));
155
151
  if (ctx.userIdentifier) {
156
152
  Sentry.setTag("user_identifier", ctx.userIdentifier);
@@ -10,7 +10,7 @@ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
10
10
  import { join } from "node:path";
11
11
 
12
12
  import { getConfig } from "../config/loader.js";
13
- import { getAppsDir } from "../memory/app-store.js";
13
+ import { getAppDirPath } from "../memory/app-store.js";
14
14
  import {
15
15
  buildManagedBaseUrl,
16
16
  resolveManagedProxyContext,
@@ -68,7 +68,7 @@ export async function generateAppIcon(
68
68
  return;
69
69
  }
70
70
 
71
- const appDir = join(getAppsDir(), appId);
71
+ const appDir = getAppDirPath(appId);
72
72
  const iconPath = join(appDir, "icon.png");
73
73
 
74
74
  // Don't regenerate if icon already exists
@@ -3,13 +3,18 @@
3
3
  *
4
4
  * Initializes a git repository in the apps directory (~/.vellum/apps/) and
5
5
  * commits after every app mutation (create, update, delete, file write/edit).
6
- * Commits are fire-and-forget they never block the caller.
6
+ * Commits are fire-and-forget -- they never block the caller.
7
7
  *
8
8
  * Also exposes query methods (history, diff, file-at-version, restore) for
9
9
  * browsing and reverting app version history.
10
10
  *
11
11
  * Reuses WorkspaceGitService for all git operations (mutex, circuit breaker,
12
12
  * lazy init, etc.).
13
+ *
14
+ * NOTE: After the 010-app-dir-rename migration, git pathspecs use dirName
15
+ * (slug) instead of appId (UUID). History queries will only return commits
16
+ * made after the migration rename. Commits before the migration used UUID-
17
+ * based paths and are not visible through the current slug-based pathspecs.
13
18
  */
14
19
 
15
20
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
@@ -17,7 +22,7 @@ import { join } from "node:path";
17
22
 
18
23
  import { getLogger } from "../util/logger.js";
19
24
  import { getWorkspaceGitService } from "../workspace/git-service.js";
20
- import { getAppsDir } from "./app-store.js";
25
+ import { getAppsDir, resolveAppDir } from "./app-store.js";
21
26
 
22
27
  const log = getLogger("app-git");
23
28
 
@@ -38,8 +43,8 @@ export interface AppVersion {
38
43
 
39
44
  /**
40
45
  * Patterns excluded from app version tracking.
41
- * - *.preview large base64 preview images
42
- * - records directories user data (form submissions), not app code
46
+ * - *.preview -- large base64 preview images
47
+ * - records directories -- user data (form submissions), not app code
43
48
  */
44
49
  const APP_GITIGNORE_RULES = ["*.preview", "*/records/"];
45
50
 
@@ -116,7 +121,7 @@ function validateRelativePath(path: string): void {
116
121
  * mutation's files get absorbed into WorkspaceGitService's bootstrap
117
122
  * commit and the "Create app: ..." commit ends up empty.
118
123
  *
119
- * Safe to call multiple times ensureInitialized() is idempotent.
124
+ * Safe to call multiple times -- ensureInitialized() is idempotent.
120
125
  * Fire-and-forget: errors are logged but never thrown.
121
126
  */
122
127
  export async function initAppGit(): Promise<void> {
@@ -189,14 +194,18 @@ export async function commitAppTurnChanges(
189
194
  /**
190
195
  * Get the commit history for a specific app.
191
196
  *
192
- * Scopes `git log` to files belonging to this app:
193
- * {appId}.json, {appId}/index.html, {appId}/pages/*, etc.
197
+ * Scopes `git log` to files belonging to this app using dirName-based
198
+ * pathspecs: {dirName}.json, {dirName}/.
199
+ *
200
+ * Note: After the 010 migration, only commits made after the rename are
201
+ * visible. Pre-migration commits used UUID-based paths.
194
202
  */
195
203
  export async function getAppHistory(
196
204
  appId: string,
197
205
  limit = 50,
198
206
  ): Promise<AppVersion[]> {
199
207
  validateAppId(appId);
208
+ const { dirName } = resolveAppDir(appId);
200
209
  const safeLimit = Math.max(1, Math.min(Math.floor(limit) || 50, 500));
201
210
  const appsDir = getAppsDir();
202
211
  const gitService = getWorkspaceGitService(appsDir);
@@ -207,8 +216,8 @@ export async function getAppHistory(
207
216
  `--max-count=${safeLimit}`,
208
217
  "--format=%H\t%at\t%s",
209
218
  "--",
210
- `${appId}.json`,
211
- `${appId}/`,
219
+ `${dirName}.json`,
220
+ `${dirName}/`,
212
221
  ]);
213
222
 
214
223
  if (!stdout.trim()) return [];
@@ -239,6 +248,7 @@ export async function getAppDiff(
239
248
  validateCommitHash(fromCommit);
240
249
  if (toCommit) validateCommitHash(toCommit);
241
250
 
251
+ const { dirName } = resolveAppDir(appId);
242
252
  const appsDir = getAppsDir();
243
253
  const gitService = getWorkspaceGitService(appsDir);
244
254
 
@@ -247,8 +257,8 @@ export async function getAppDiff(
247
257
  "diff",
248
258
  range,
249
259
  "--",
250
- `${appId}.json`,
251
- `${appId}/`,
260
+ `${dirName}.json`,
261
+ `${dirName}/`,
252
262
  ]);
253
263
 
254
264
  return stdout;
@@ -266,12 +276,13 @@ export async function getAppFileAtVersion(
266
276
  validateRelativePath(path);
267
277
  validateCommitHash(commitHash);
268
278
 
279
+ const { dirName } = resolveAppDir(appId);
269
280
  const appsDir = getAppsDir();
270
281
  const gitService = getWorkspaceGitService(appsDir);
271
282
 
272
283
  const { stdout } = await gitService.runReadOnlyGit([
273
284
  "show",
274
- `${commitHash}:${appId}/${path}`,
285
+ `${commitHash}:${dirName}/${path}`,
275
286
  ]);
276
287
 
277
288
  return stdout;
@@ -294,6 +305,7 @@ export async function restoreAppVersion(
294
305
  validateAppId(appId);
295
306
  validateCommitHash(commitHash);
296
307
 
308
+ const { dirName } = resolveAppDir(appId);
297
309
  const appsDir = getAppsDir();
298
310
  const gitService = getWorkspaceGitService(appsDir);
299
311
 
@@ -305,14 +317,14 @@ export async function restoreAppVersion(
305
317
  commitHash,
306
318
  "--no-overlay",
307
319
  "--",
308
- `${appId}.json`,
309
- `${appId}/`,
320
+ `${dirName}.json`,
321
+ `${dirName}/`,
310
322
  ]);
311
323
 
312
324
  // Read the app name and refresh updatedAt so the restored app
313
325
  // doesn't appear stale in recency ordering.
314
326
  let appName = appId;
315
- const jsonPath = join(appsDir, `${appId}.json`);
327
+ const jsonPath = join(appsDir, `${dirName}.json`);
316
328
  if (existsSync(jsonPath)) {
317
329
  try {
318
330
  const raw = readFileSync(jsonPath, "utf-8");
@@ -328,7 +340,7 @@ export async function restoreAppVersion(
328
340
  const shortHash = commitHash.substring(0, 7);
329
341
 
330
342
  // Stage only this app's files and commit atomically within the same mutex lock
331
- await exec(["add", "--", `${appId}.json`, `${appId}/`]);
343
+ await exec(["add", "--", `${dirName}.json`, `${dirName}/`]);
332
344
  await exec([
333
345
  "commit",
334
346
  "-m",