@vellumai/assistant 0.5.13 → 0.5.14

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 (425) hide show
  1. package/.env.example +1 -6
  2. package/AGENTS.md +4 -0
  3. package/ARCHITECTURE.md +0 -1
  4. package/bunfig.toml +1 -0
  5. package/docs/architecture/memory.md +3 -3
  6. package/openapi.yaml +127 -22
  7. package/package.json +1 -1
  8. package/src/__tests__/access-request-decision.test.ts +2 -32
  9. package/src/__tests__/actor-token-service.test.ts +1 -31
  10. package/src/__tests__/anthropic-provider.test.ts +53 -40
  11. package/src/__tests__/app-git-history.test.ts +9 -17
  12. package/src/__tests__/app-git-service.test.ts +14 -20
  13. package/src/__tests__/app-store-dir-names.test.ts +10 -20
  14. package/src/__tests__/approval-cascade.test.ts +2 -19
  15. package/src/__tests__/approval-primitive.test.ts +2 -27
  16. package/src/__tests__/approval-routes-http.test.ts +2 -30
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
  19. package/src/__tests__/attachments-store.test.ts +5 -32
  20. package/src/__tests__/audit-log-rotation.test.ts +5 -36
  21. package/src/__tests__/avatar-e2e.test.ts +1 -9
  22. package/src/__tests__/avatar-generator.test.ts +1 -7
  23. package/src/__tests__/browser-fill-credential.test.ts +0 -4
  24. package/src/__tests__/browser-manager.test.ts +0 -6
  25. package/src/__tests__/call-controller.test.ts +1 -22
  26. package/src/__tests__/call-conversation-messages.test.ts +0 -21
  27. package/src/__tests__/call-domain.test.ts +0 -25
  28. package/src/__tests__/call-pointer-messages.test.ts +0 -21
  29. package/src/__tests__/call-recovery.test.ts +0 -22
  30. package/src/__tests__/call-routes-http.test.ts +0 -24
  31. package/src/__tests__/call-store.test.ts +0 -21
  32. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
  33. package/src/__tests__/canonical-guardian-store.test.ts +48 -21
  34. package/src/__tests__/channel-approval-routes.test.ts +6 -26
  35. package/src/__tests__/channel-approvals.test.ts +1 -38
  36. package/src/__tests__/channel-delivery-store.test.ts +0 -21
  37. package/src/__tests__/channel-guardian.test.ts +0 -26
  38. package/src/__tests__/channel-reply-delivery.test.ts +5 -0
  39. package/src/__tests__/channel-retry-sweep.test.ts +0 -21
  40. package/src/__tests__/checker.test.ts +26 -61
  41. package/src/__tests__/clawhub.test.ts +9 -25
  42. package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
  43. package/src/__tests__/config-loader-backfill.test.ts +9 -28
  44. package/src/__tests__/config-schema-cmd.test.ts +5 -25
  45. package/src/__tests__/config-schema.test.ts +21 -40
  46. package/src/__tests__/config-watcher.test.ts +4 -91
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
  48. package/src/__tests__/contacts-tools.test.ts +0 -21
  49. package/src/__tests__/context-memory-e2e.test.ts +0 -21
  50. package/src/__tests__/context-window-manager.test.ts +130 -3
  51. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
  52. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
  53. package/src/__tests__/conversation-agent-loop.test.ts +0 -4
  54. package/src/__tests__/conversation-attachments.test.ts +1 -24
  55. package/src/__tests__/conversation-attention-store.test.ts +0 -21
  56. package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
  57. package/src/__tests__/conversation-clear-safety.test.ts +0 -22
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
  59. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
  60. package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
  61. package/src/__tests__/conversation-disk-view.test.ts +5 -27
  62. package/src/__tests__/conversation-error.test.ts +1 -1
  63. package/src/__tests__/conversation-fork-crud.test.ts +1 -33
  64. package/src/__tests__/conversation-fork-route.test.ts +0 -27
  65. package/src/__tests__/conversation-history-web-search.test.ts +23 -16
  66. package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
  67. package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
  68. package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
  69. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
  70. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
  71. package/src/__tests__/conversation-queue.test.ts +8 -8
  72. package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
  74. package/src/__tests__/conversation-slash-commands.test.ts +5 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +0 -4
  76. package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
  77. package/src/__tests__/conversation-speed-override.test.ts +326 -0
  78. package/src/__tests__/conversation-starter-routes.test.ts +0 -23
  79. package/src/__tests__/conversation-store.test.ts +0 -21
  80. package/src/__tests__/conversation-unread-route.test.ts +0 -24
  81. package/src/__tests__/conversation-usage.test.ts +56 -21
  82. package/src/__tests__/conversation-wipe.test.ts +0 -21
  83. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  84. package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
  85. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
  86. package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
  87. package/src/__tests__/credential-vault-unit.test.ts +9 -428
  88. package/src/__tests__/credentials-cli.test.ts +10 -10
  89. package/src/__tests__/daemon-assistant-events.test.ts +0 -19
  90. package/src/__tests__/date-context.test.ts +77 -97
  91. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
  92. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
  93. package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
  94. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
  95. package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
  96. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
  97. package/src/__tests__/email-cli.test.ts +6 -6
  98. package/src/__tests__/ephemeral-permissions.test.ts +5 -17
  99. package/src/__tests__/first-greeting.test.ts +4 -32
  100. package/src/__tests__/followup-tools.test.ts +0 -21
  101. package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
  102. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
  103. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
  104. package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
  105. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
  106. package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
  107. package/src/__tests__/guardian-action-store.test.ts +0 -21
  108. package/src/__tests__/guardian-action-sweep.test.ts +0 -21
  109. package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
  110. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
  111. package/src/__tests__/guardian-dispatch.test.ts +0 -21
  112. package/src/__tests__/guardian-grant-minting.test.ts +0 -22
  113. package/src/__tests__/guardian-outbound-http.test.ts +0 -22
  114. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
  115. package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
  116. package/src/__tests__/guardian-routing-state.test.ts +0 -22
  117. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
  118. package/src/__tests__/headless-browser-interactions.test.ts +0 -4
  119. package/src/__tests__/headless-browser-navigate.test.ts +0 -4
  120. package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
  121. package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
  122. package/src/__tests__/heartbeat-service.test.ts +99 -26
  123. package/src/__tests__/hooks-blocking.test.ts +3 -3
  124. package/src/__tests__/hooks-config.test.ts +7 -7
  125. package/src/__tests__/hooks-discovery.test.ts +3 -3
  126. package/src/__tests__/hooks-integration.test.ts +5 -5
  127. package/src/__tests__/hooks-manager.test.ts +3 -3
  128. package/src/__tests__/hooks-runner.test.ts +5 -23
  129. package/src/__tests__/hooks-settings.test.ts +3 -3
  130. package/src/__tests__/hooks-templates.test.ts +3 -3
  131. package/src/__tests__/http-conversation-lineage.test.ts +0 -27
  132. package/src/__tests__/identity-intro-cache.test.ts +0 -4
  133. package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
  134. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
  135. package/src/__tests__/intent-routing.test.ts +2 -55
  136. package/src/__tests__/invite-redemption-service.test.ts +0 -21
  137. package/src/__tests__/invite-routes-http.test.ts +0 -21
  138. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
  139. package/src/__tests__/journal-context.test.ts +8 -75
  140. package/src/__tests__/list-messages-attachments.test.ts +0 -22
  141. package/src/__tests__/llm-context-route-provider.test.ts +0 -21
  142. package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
  143. package/src/__tests__/llm-usage-store.test.ts +0 -21
  144. package/src/__tests__/log-export-workspace.test.ts +1 -1
  145. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
  146. package/src/__tests__/managed-store.test.ts +1 -1
  147. package/src/__tests__/mcp-cli.test.ts +7 -10
  148. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
  149. package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
  150. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
  151. package/src/__tests__/memory-recall-log-store.test.ts +0 -27
  152. package/src/__tests__/memory-recall-quality.test.ts +0 -21
  153. package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
  154. package/src/__tests__/memory-regressions.test.ts +282 -70
  155. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
  156. package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
  157. package/src/__tests__/messaging-send-tool.test.ts +201 -0
  158. package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
  159. package/src/__tests__/migration-export-http.test.ts +7 -1
  160. package/src/__tests__/migration-import-commit-http.test.ts +16 -14
  161. package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
  162. package/src/__tests__/migration-validate-http.test.ts +1 -28
  163. package/src/__tests__/native-web-search.test.ts +25 -22
  164. package/src/__tests__/non-member-access-request.test.ts +0 -22
  165. package/src/__tests__/notification-guardian-path.test.ts +0 -21
  166. package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
  167. package/src/__tests__/oauth-apps-routes.test.ts +103 -2
  168. package/src/__tests__/oauth-cli.test.ts +52 -0
  169. package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
  170. package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
  171. package/src/__tests__/oauth-providers-routes.test.ts +257 -0
  172. package/src/__tests__/oauth-store.test.ts +0 -21
  173. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  174. package/src/__tests__/openai-provider.test.ts +261 -0
  175. package/src/__tests__/pairing-concurrent.test.ts +6 -6
  176. package/src/__tests__/pairing-routes.test.ts +7 -1
  177. package/src/__tests__/path-policy.test.ts +1 -1
  178. package/src/__tests__/platform.test.ts +64 -88
  179. package/src/__tests__/playbook-execution.test.ts +0 -21
  180. package/src/__tests__/playbook-tools.test.ts +0 -21
  181. package/src/__tests__/pricing.test.ts +100 -0
  182. package/src/__tests__/relay-server.test.ts +1 -25
  183. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
  184. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
  185. package/src/__tests__/runtime-events-sse.test.ts +0 -24
  186. package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
  187. package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
  188. package/src/__tests__/schedule-store.test.ts +0 -21
  189. package/src/__tests__/schedule-tools.test.ts +0 -21
  190. package/src/__tests__/scheduler-recurrence.test.ts +0 -21
  191. package/src/__tests__/scoped-approval-grants.test.ts +0 -21
  192. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
  193. package/src/__tests__/secret-allowlist.test.ts +1 -1
  194. package/src/__tests__/secret-ingress-channel.test.ts +0 -5
  195. package/src/__tests__/secret-ingress-cli.test.ts +0 -6
  196. package/src/__tests__/secret-ingress-http.test.ts +0 -5
  197. package/src/__tests__/secret-ingress.test.ts +0 -5
  198. package/src/__tests__/send-endpoint-busy.test.ts +0 -24
  199. package/src/__tests__/sequence-store.test.ts +0 -21
  200. package/src/__tests__/server-history-render.test.ts +0 -24
  201. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
  202. package/src/__tests__/skill-load-inline-command.test.ts +9 -0
  203. package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
  204. package/src/__tests__/skill-load-tool.test.ts +11 -0
  205. package/src/__tests__/skills-uninstall.test.ts +10 -8
  206. package/src/__tests__/skills.test.ts +1 -1
  207. package/src/__tests__/slack-channel-config.test.ts +1 -1
  208. package/src/__tests__/slack-inbound-verification.test.ts +0 -22
  209. package/src/__tests__/starter-bundle.test.ts +4 -1
  210. package/src/__tests__/suggestion-routes.test.ts +2 -0
  211. package/src/__tests__/system-prompt.test.ts +1 -1
  212. package/src/__tests__/terminal-tools.test.ts +1 -1
  213. package/src/__tests__/test-preload.ts +31 -0
  214. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
  215. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  216. package/src/__tests__/tool-executor.test.ts +0 -20
  217. package/src/__tests__/tool-input-summary.test.ts +124 -0
  218. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
  219. package/src/__tests__/trust-store.test.ts +7 -1
  220. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
  221. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
  222. package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
  223. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  224. package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
  225. package/src/__tests__/twilio-routes.test.ts +1 -1
  226. package/src/__tests__/update-bulletin.test.ts +1 -1
  227. package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
  228. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
  229. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
  230. package/src/__tests__/voice-session-bridge.test.ts +1 -1
  231. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
  232. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
  233. package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
  234. package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
  235. package/src/agent/loop.ts +6 -9
  236. package/src/approvals/guardian-decision-primitive.ts +46 -18
  237. package/src/approvals/guardian-request-resolvers.ts +19 -2
  238. package/src/calls/active-call-lease.ts +2 -2
  239. package/src/cli/AGENTS.md +1 -1
  240. package/src/cli/commands/doctor.ts +9 -9
  241. package/src/cli/commands/memory.ts +142 -0
  242. package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
  243. package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
  244. package/src/cli/commands/oauth/connect.ts +13 -12
  245. package/src/cli/commands/oauth/index.ts +1 -1
  246. package/src/cli/commands/oauth/providers.ts +47 -62
  247. package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
  248. package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
  249. package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
  250. package/src/cli/commands/platform/connect.ts +17 -7
  251. package/src/cli/commands/platform/disconnect.ts +28 -3
  252. package/src/cli/commands/platform/index.ts +3 -3
  253. package/src/cli.ts +1 -299
  254. package/src/config/assistant-feature-flags.ts +23 -15
  255. package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
  256. package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
  257. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
  258. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
  259. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
  260. package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
  261. package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
  262. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
  263. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
  264. package/src/config/bundled-skills/messaging/SKILL.md +7 -7
  265. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
  266. package/src/config/bundled-skills/slack/SKILL.md +18 -0
  267. package/src/config/env-registry.ts +15 -11
  268. package/src/config/env.ts +1 -11
  269. package/src/config/feature-flag-registry.json +16 -0
  270. package/src/config/schema.ts +4 -0
  271. package/src/config/schemas/heartbeat.ts +6 -1
  272. package/src/config/schemas/inference.ts +14 -3
  273. package/src/config/schemas/memory-processing.ts +16 -8
  274. package/src/config/schemas/memory-retrieval.ts +3 -3
  275. package/src/config/skills.ts +1 -1
  276. package/src/context/window-manager.ts +174 -51
  277. package/src/credential-execution/executable-discovery.ts +2 -2
  278. package/src/daemon/approved-devices-store.ts +2 -2
  279. package/src/daemon/assistant-attachments.ts +2 -0
  280. package/src/daemon/config-watcher.ts +4 -50
  281. package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
  282. package/src/daemon/conversation-agent-loop.ts +12 -0
  283. package/src/daemon/conversation-error.ts +3 -5
  284. package/src/daemon/conversation-history.ts +7 -3
  285. package/src/daemon/conversation-lifecycle.ts +16 -0
  286. package/src/daemon/conversation-messaging.ts +1 -0
  287. package/src/daemon/conversation-notifiers.ts +67 -30
  288. package/src/daemon/conversation-process.ts +161 -2
  289. package/src/daemon/conversation-queue-manager.ts +2 -0
  290. package/src/daemon/conversation-runtime-assembly.ts +33 -11
  291. package/src/daemon/conversation-slash.ts +14 -3
  292. package/src/daemon/conversation-tool-setup.ts +2 -0
  293. package/src/daemon/conversation-usage.ts +32 -4
  294. package/src/daemon/conversation.ts +33 -1
  295. package/src/daemon/daemon-control.ts +32 -16
  296. package/src/daemon/date-context.ts +47 -45
  297. package/src/daemon/dictation-profile-store.ts +2 -2
  298. package/src/daemon/handlers/conversations.ts +19 -0
  299. package/src/daemon/handlers/shared.ts +14 -21
  300. package/src/daemon/lifecycle.ts +5 -7
  301. package/src/daemon/message-types/conversations.ts +2 -0
  302. package/src/daemon/message-types/guardian-actions.ts +3 -17
  303. package/src/daemon/message-types/integrations.ts +11 -1
  304. package/src/daemon/message-types/messages.ts +1 -0
  305. package/src/daemon/pairing-store.ts +2 -79
  306. package/src/daemon/server.ts +154 -8
  307. package/src/daemon/watch-handler.ts +65 -21
  308. package/src/email/guardrails.ts +3 -3
  309. package/src/heartbeat/heartbeat-service.ts +14 -7
  310. package/src/hooks/cli.ts +2 -2
  311. package/src/hooks/config.ts +2 -2
  312. package/src/hooks/discovery.ts +2 -2
  313. package/src/hooks/manager.ts +2 -2
  314. package/src/hooks/runner.ts +5 -2
  315. package/src/hooks/templates.ts +2 -2
  316. package/src/memory/admin.ts +181 -2
  317. package/src/memory/app-git-service.ts +61 -4
  318. package/src/memory/attachments-store.ts +2 -0
  319. package/src/memory/canonical-guardian-store.ts +16 -0
  320. package/src/memory/db-init.ts +8 -0
  321. package/src/memory/embedding-local.ts +5 -2
  322. package/src/memory/indexer.ts +44 -26
  323. package/src/memory/items-extractor.ts +34 -82
  324. package/src/memory/job-handlers/batch-extraction.ts +741 -0
  325. package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
  326. package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
  327. package/src/memory/jobs-store.ts +28 -0
  328. package/src/memory/jobs-worker.ts +56 -9
  329. package/src/memory/lifecycle-events-store.ts +4 -2
  330. package/src/memory/llm-request-log-store.ts +40 -2
  331. package/src/memory/llm-usage-store.ts +4 -3
  332. package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
  333. package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
  334. package/src/memory/migrations/index.ts +2 -0
  335. package/src/memory/query-expansion.ts +83 -0
  336. package/src/memory/retriever.test.ts +119 -0
  337. package/src/memory/retriever.ts +513 -105
  338. package/src/memory/schema/guardian.ts +4 -0
  339. package/src/memory/schema/infrastructure.ts +1 -0
  340. package/src/memory/search/formatting.test.ts +140 -0
  341. package/src/memory/search/formatting.ts +143 -198
  342. package/src/memory/search/mmr.ts +136 -0
  343. package/src/memory/search/staleness.ts +0 -15
  344. package/src/memory/search/tier-classifier.ts +10 -21
  345. package/src/memory/search/types.ts +17 -0
  346. package/src/messaging/providers/slack/adapter.ts +51 -5
  347. package/src/notifications/broadcaster.ts +13 -0
  348. package/src/notifications/copy-composer.ts +8 -0
  349. package/src/oauth/connect-orchestrator.ts +1 -1
  350. package/src/oauth/connection-resolver.ts +2 -2
  351. package/src/oauth/provider-serializer.ts +116 -0
  352. package/src/permissions/trust-store.ts +24 -7
  353. package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
  354. package/src/prompts/journal-context.ts +50 -35
  355. package/src/prompts/persona-resolver.ts +1 -1
  356. package/src/prompts/system-prompt.ts +27 -28
  357. package/src/prompts/templates/BOOTSTRAP.md +14 -1
  358. package/src/prompts/templates/HEARTBEAT.md +10 -0
  359. package/src/prompts/templates/NOW.md +19 -25
  360. package/src/prompts/templates/SOUL.md +13 -1
  361. package/src/prompts/templates/UPDATES.md +12 -0
  362. package/src/prompts/update-bulletin.ts +1 -1
  363. package/src/providers/anthropic/client.ts +89 -18
  364. package/src/providers/model-catalog.ts +22 -2
  365. package/src/providers/model-intents.ts +2 -2
  366. package/src/providers/openai/client.ts +40 -1
  367. package/src/providers/retry.ts +23 -4
  368. package/src/providers/types.ts +2 -0
  369. package/src/runtime/assistant-scope.ts +1 -1
  370. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
  371. package/src/runtime/auth/route-policy.ts +1 -0
  372. package/src/runtime/auth/token-service.ts +51 -29
  373. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
  374. package/src/runtime/guardian-decision-types.ts +16 -10
  375. package/src/runtime/http-server.ts +3 -14
  376. package/src/runtime/http-types.ts +1 -0
  377. package/src/runtime/migrations/vbundle-builder.ts +7 -4
  378. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
  379. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  380. package/src/runtime/routes/conversation-query-routes.ts +40 -8
  381. package/src/runtime/routes/conversation-routes.ts +125 -3
  382. package/src/runtime/routes/guardian-action-routes.ts +9 -3
  383. package/src/runtime/routes/identity-routes.ts +25 -4
  384. package/src/runtime/routes/llm-context-normalization.ts +1 -0
  385. package/src/runtime/routes/log-export-routes.ts +34 -12
  386. package/src/runtime/routes/migration-routes.ts +6 -10
  387. package/src/runtime/routes/oauth-apps.ts +2 -9
  388. package/src/runtime/routes/oauth-providers.ts +60 -0
  389. package/src/runtime/routes/pairing-routes.ts +0 -8
  390. package/src/runtime/routes/settings-routes.ts +0 -1
  391. package/src/runtime/routes/telemetry-routes.ts +16 -4
  392. package/src/security/encrypted-store.ts +2 -2
  393. package/src/security/secret-allowlist.ts +3 -3
  394. package/src/signals/emit-event.ts +42 -0
  395. package/src/signals/user-message.ts +37 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
  397. package/src/telemetry/usage-telemetry-reporter.ts +23 -17
  398. package/src/tools/browser/runtime-check.ts +2 -2
  399. package/src/tools/credentials/vault.ts +2 -249
  400. package/src/tools/memory/definitions.ts +1 -1
  401. package/src/tools/memory/handlers.test.ts +50 -8
  402. package/src/tools/memory/handlers.ts +3 -1
  403. package/src/tools/side-effects.ts +1 -6
  404. package/src/tools/terminal/safe-env.ts +3 -2
  405. package/src/tools/terminal/shell.ts +11 -14
  406. package/src/tools/tool-approval-handler.ts +20 -1
  407. package/src/tools/tool-input-summary.ts +66 -0
  408. package/src/tools/types.ts +4 -0
  409. package/src/usage/types.ts +4 -0
  410. package/src/util/device-id.ts +10 -10
  411. package/src/util/platform.ts +71 -33
  412. package/src/util/pricing.ts +19 -6
  413. package/src/util/strip-comment-lines.ts +28 -0
  414. package/src/workspace/git-service.ts +8 -18
  415. package/src/workspace/migrations/003-seed-device-id.ts +6 -4
  416. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
  417. package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
  418. package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
  419. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
  420. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
  421. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
  422. package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
  423. package/src/workspace/migrations/registry.ts +8 -0
  424. package/src/signals/confirm.ts +0 -82
  425. package/src/signals/trust-rule.ts +0 -174
package/src/cli.ts CHANGED
@@ -153,7 +153,6 @@ export async function startCli(): Promise<void> {
153
153
  model: string;
154
154
  } | null = null;
155
155
  let pendingSessionPick = false;
156
- let pendingConfirmation = false;
157
156
  let pendingCopySession = false;
158
157
  let toolStreaming = false;
159
158
  let lastDisplayedError: string | null = null;
@@ -232,91 +231,6 @@ export async function startCli(): Promise<void> {
232
231
  rl.prompt();
233
232
  }
234
233
 
235
- /** Send a confirmation decision via signal file (read by the daemon). */
236
- function sendConfirmation(requestId: string, decision: string): void {
237
- try {
238
- const signalsDir = getSignalsDir();
239
- mkdirSync(signalsDir, { recursive: true });
240
- writeFileSync(
241
- join(signalsDir, "confirm"),
242
- JSON.stringify({ requestId, decision }),
243
- );
244
- } catch {
245
- process.stdout.write("[Failed to send confirmation]\n");
246
- }
247
- }
248
-
249
- /** Add a trust rule via signal file, then confirm once the daemon acknowledges. */
250
- function sendTrustRuleAndConfirm(
251
- requestId: string,
252
- pattern: string,
253
- scope: string,
254
- decision: "allow" | "deny",
255
- confirmDecision: string,
256
- options?: { allowHighRisk?: boolean },
257
- ): void {
258
- try {
259
- const signalsDir = getSignalsDir();
260
- mkdirSync(signalsDir, { recursive: true });
261
- const resultPath = join(signalsDir, "trust-rule.result");
262
- writeFileSync(
263
- join(signalsDir, "trust-rule"),
264
- JSON.stringify({
265
- requestId,
266
- pattern,
267
- scope,
268
- decision,
269
- ...(options?.allowHighRisk ? { allowHighRisk: true } : {}),
270
- }),
271
- );
272
-
273
- let settled = false;
274
-
275
- const onResult = (): void => {
276
- try {
277
- const raw = readFileSync(resultPath, "utf-8");
278
- const result = JSON.parse(raw) as {
279
- ok?: boolean;
280
- requestId?: string;
281
- error?: string;
282
- };
283
- if (result.requestId !== requestId) return;
284
- settled = true;
285
- watcher.close();
286
- clearTimeout(timeoutId);
287
- if (result.ok) {
288
- sendConfirmation(requestId, confirmDecision);
289
- } else {
290
- process.stdout.write(
291
- `[Failed to add trust rule: ${result.error ?? "unknown error"}]\n`,
292
- );
293
- }
294
- } catch {
295
- // Result file not yet readable; ignore.
296
- }
297
- };
298
-
299
- const watcher = watch(signalsDir, (_event, filename) => {
300
- if (filename === "trust-rule.result") {
301
- onResult();
302
- }
303
- });
304
-
305
- const timeoutId = setTimeout(() => {
306
- if (!settled) {
307
- watcher.close();
308
- process.stdout.write("[Trust rule timed out]\n");
309
- }
310
- }, 5_000);
311
-
312
- if (existsSync(resultPath)) {
313
- onResult();
314
- }
315
- } catch {
316
- process.stdout.write("[Failed to send trust rule]\n");
317
- }
318
- }
319
-
320
234
  /** Send a user message via signal file to the daemon. */
321
235
  async function sendUserMessage(
322
236
  content: string,
@@ -399,211 +313,6 @@ export async function startCli(): Promise<void> {
399
313
  }
400
314
  }
401
315
 
402
- function renderConfirmationPrompt(req: ConfirmationRequest): void {
403
- const preview = formatConfirmationCommandPreview(req);
404
- const inputLines = formatConfirmationInputLines(req.input);
405
- process.stdout.write("\n");
406
- process.stdout.write(`\u250C ${req.toolName}: ${preview}\n`);
407
- process.stdout.write(
408
- `\u2502 Risk: ${req.riskLevel}${req.sandboxed ? " [sandboxed]" : ""}\n`,
409
- );
410
- if (req.executionTarget) {
411
- process.stdout.write(`\u2502 Target: ${req.executionTarget}\n`);
412
- }
413
- if (inputLines.length > 0) {
414
- process.stdout.write(`\u2502\n`);
415
- for (const line of inputLines) {
416
- process.stdout.write(`\u2502 ${line}\n`);
417
- }
418
- }
419
- if (req.diff) {
420
- const diffOutput = req.diff.isNewFile
421
- ? formatNewFileDiff(req.diff.newContent, req.diff.filePath, null)
422
- : formatDiff(
423
- req.diff.oldContent,
424
- req.diff.newContent,
425
- req.diff.filePath,
426
- );
427
- if (diffOutput) {
428
- process.stdout.write(`\u2502\n`);
429
- for (const line of diffOutput.split("\n")) {
430
- if (line) process.stdout.write(`\u2502 ${line}\n`);
431
- }
432
- }
433
- }
434
- process.stdout.write(`\u2502\n`);
435
- process.stdout.write(`\u2502 [a] Allow once\n`);
436
- if (req.temporaryOptionsAvailable?.includes("allow_10m")) {
437
- process.stdout.write(`\u2502 [t] Allow 10m\n`);
438
- }
439
- if (req.temporaryOptionsAvailable?.includes("allow_conversation")) {
440
- process.stdout.write(`\u2502 [T] Allow Conversation\n`);
441
- }
442
- process.stdout.write(`\u2502 [d] Deny once\n`);
443
- if (req.allowlistOptions.length > 0 && req.scopeOptions.length > 0) {
444
- process.stdout.write(`\u2502 [A] Allowlist...\n`);
445
- process.stdout.write(`\u2502 [H] Allowlist (high-risk)...\n`);
446
- process.stdout.write(`\u2502 [D] Denylist...\n`);
447
- }
448
- process.stdout.write(`\u2514 > `);
449
-
450
- pendingConfirmation = true;
451
- rl.once("line", (answer) => {
452
- const trimmed = answer.trim();
453
- const choice = trimmed.toLowerCase();
454
-
455
- // Uppercase 'A' → allowlist pattern selection (check before lowercase 'a')
456
- // Only process when scope options exist, matching the display guard above
457
- if (
458
- (trimmed === "A" || choice === "allowlist") &&
459
- req.allowlistOptions.length > 0 &&
460
- req.scopeOptions.length > 0
461
- ) {
462
- // pendingConfirmation stays true through sub-prompts
463
- renderPatternSelection(req, "always_allow");
464
- return;
465
- }
466
-
467
- // Uppercase 'H' → high-risk allowlist pattern selection
468
- if (
469
- trimmed === "H" &&
470
- req.allowlistOptions.length > 0 &&
471
- req.scopeOptions.length > 0
472
- ) {
473
- // pendingConfirmation stays true through sub-prompts
474
- renderPatternSelection(req, "always_allow_high_risk");
475
- return;
476
- }
477
-
478
- // Uppercase 'D' → denylist pattern selection (check before lowercase 'd')
479
- if (
480
- (trimmed === "D" || choice === "denylist") &&
481
- req.allowlistOptions.length > 0 &&
482
- req.scopeOptions.length > 0
483
- ) {
484
- // pendingConfirmation stays true through sub-prompts
485
- renderPatternSelection(req, "always_deny");
486
- return;
487
- }
488
-
489
- pendingConfirmation = false;
490
- if (choice === "a") {
491
- sendConfirmation(req.requestId, "allow");
492
- return;
493
- }
494
-
495
- if (
496
- choice === "t" &&
497
- trimmed === "t" &&
498
- req.temporaryOptionsAvailable?.includes("allow_10m")
499
- ) {
500
- sendConfirmation(req.requestId, "allow_10m");
501
- return;
502
- }
503
-
504
- if (
505
- trimmed === "T" &&
506
- req.temporaryOptionsAvailable?.includes("allow_conversation")
507
- ) {
508
- sendConfirmation(req.requestId, "allow_conversation");
509
- return;
510
- }
511
-
512
- if (choice === "d") {
513
- sendConfirmation(req.requestId, "deny");
514
- return;
515
- }
516
-
517
- // Default to deny for unrecognized input
518
- sendConfirmation(req.requestId, "deny");
519
- });
520
- }
521
-
522
- function renderPatternSelection(
523
- req: ConfirmationRequest,
524
- decision: "always_allow" | "always_allow_high_risk" | "always_deny",
525
- ): void {
526
- const label =
527
- decision === "always_deny"
528
- ? "Denylist"
529
- : decision === "always_allow_high_risk"
530
- ? "Allowlist (high-risk)"
531
- : "Allowlist";
532
- process.stdout.write("\n");
533
- process.stdout.write(`\u250C ${label}: choose command pattern\n`);
534
- for (let i = 0; i < req.allowlistOptions.length; i++) {
535
- process.stdout.write(
536
- `\u2502 [${i + 1}] ${req.allowlistOptions[i].label}\n`,
537
- );
538
- }
539
- process.stdout.write(`\u2514 > `);
540
-
541
- rl.once("line", (answer) => {
542
- const parsed = parseInt(answer.trim(), 10);
543
- if (Number.isNaN(parsed)) {
544
- process.stdout.write(" Invalid input — enter a number.\n");
545
- renderPatternSelection(req, decision);
546
- return;
547
- }
548
- const idx = parsed - 1;
549
- if (idx >= 0 && idx < req.allowlistOptions.length) {
550
- const selectedPattern = req.allowlistOptions[idx].pattern;
551
- // pendingConfirmation stays true through scope selection
552
- renderScopeSelection(req, selectedPattern, decision);
553
- } else {
554
- // Invalid selection → deny
555
- pendingConfirmation = false;
556
- sendConfirmation(req.requestId, "deny");
557
- }
558
- });
559
- }
560
-
561
- function renderScopeSelection(
562
- req: ConfirmationRequest,
563
- selectedPattern: string,
564
- decision: "always_allow" | "always_allow_high_risk" | "always_deny",
565
- ): void {
566
- const label =
567
- decision === "always_deny"
568
- ? "Denylist"
569
- : decision === "always_allow_high_risk"
570
- ? "Allowlist (high-risk)"
571
- : "Allowlist";
572
- process.stdout.write("\n");
573
- process.stdout.write(`\u250C ${label}: choose scope\n`);
574
- for (let i = 0; i < req.scopeOptions.length; i++) {
575
- process.stdout.write(`\u2502 [${i + 1}] ${req.scopeOptions[i].label}\n`);
576
- }
577
- process.stdout.write(`\u2514 > `);
578
-
579
- rl.once("line", (answer) => {
580
- const parsed = parseInt(answer.trim(), 10);
581
- if (Number.isNaN(parsed)) {
582
- process.stdout.write(" Invalid input — enter a number.\n");
583
- renderScopeSelection(req, selectedPattern, decision);
584
- return;
585
- }
586
- pendingConfirmation = false;
587
- const idx = parsed - 1;
588
- if (idx >= 0 && idx < req.scopeOptions.length) {
589
- const trustDecision = decision === "always_deny" ? "deny" : "allow";
590
- sendTrustRuleAndConfirm(
591
- req.requestId,
592
- selectedPattern,
593
- req.scopeOptions[idx].scope,
594
- trustDecision,
595
- trustDecision,
596
- decision === "always_allow_high_risk"
597
- ? { allowHighRisk: true }
598
- : undefined,
599
- );
600
- } else {
601
- // Invalid selection → deny
602
- sendConfirmation(req.requestId, "deny");
603
- }
604
- });
605
- }
606
-
607
316
  function renderConversationPicker(
608
317
  conversations: Array<{ id: string; title: string; updatedAt: number }>,
609
318
  ): void {
@@ -875,11 +584,6 @@ export async function startCli(): Promise<void> {
875
584
  spinner.start("Thinking...");
876
585
  break;
877
586
 
878
- case "confirmation_request":
879
- spinner.stop();
880
- renderConfirmationPrompt(msg);
881
- break;
882
-
883
587
  case "conversation_error":
884
588
  spinner.stop();
885
589
  if (lastDisplayedError !== msg.userMessage) {
@@ -891,8 +595,7 @@ export async function startCli(): Promise<void> {
891
595
  case "error":
892
596
  spinner.stop();
893
597
  generating = false;
894
- if (pendingConfirmation || pendingSessionPick || pendingCopySession) {
895
- pendingConfirmation = false;
598
+ if (pendingSessionPick || pendingCopySession) {
896
599
  pendingSessionPick = false;
897
600
  pendingCopySession = false;
898
601
  rl.removeAllListeners("line");
@@ -1042,7 +745,6 @@ export async function startCli(): Promise<void> {
1042
745
  const content = line.trim();
1043
746
  if (!content) return;
1044
747
  if (pendingSessionPick) return;
1045
- if (pendingConfirmation) return;
1046
748
 
1047
749
  // Persist to history file (ensure parent directory exists)
1048
750
  try {
@@ -5,8 +5,7 @@
5
5
  * `meta/feature-flags/feature-flag-registry.json` and resolves the effective
6
6
  * enabled/disabled state for each declared assistant-scope flag by consulting
7
7
  * (in priority order):
8
- * 1. Override values from `~/.vellum/protected/feature-flags.json` (local)
9
- * or via the gateway HTTP API (Docker/containerized)
8
+ * 1. Override values from the gateway HTTP API (or local file fallback)
10
9
  * 2. Remote values from `feature-flags-remote.json` (platform-pushed,
11
10
  * cached locally; only used in local mode — containerized mode gets
12
11
  * remote values via the gateway)
@@ -18,9 +17,9 @@
18
17
  */
19
18
 
20
19
  import { existsSync, readFileSync } from "node:fs";
20
+ import { homedir } from "node:os";
21
21
  import { dirname, join } from "node:path";
22
22
 
23
- import { getRootDir } from "../util/platform.js";
24
23
  import { getIsContainerized } from "./env-registry.js";
25
24
  import type { AssistantConfig } from "./schema.js";
26
25
 
@@ -112,7 +111,7 @@ function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
112
111
  }
113
112
 
114
113
  // ---------------------------------------------------------------------------
115
- // Override loading — reads from protected directory or gateway HTTP
114
+ // Override loading — reads from gateway HTTP API or local file
116
115
  // ---------------------------------------------------------------------------
117
116
 
118
117
  /**
@@ -122,7 +121,7 @@ function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
122
121
  let cachedOverrides: Record<string, boolean> | null = null;
123
122
 
124
123
  /**
125
- * File format for `~/.vellum/protected/feature-flags.json`, matching the
124
+ * File format for the local feature-flags.json override file, matching the
126
125
  * gateway's feature-flag-store.ts schema.
127
126
  */
128
127
  interface FeatureFlagFileData {
@@ -134,18 +133,18 @@ interface FeatureFlagFileData {
134
133
  * Resolve the path to the feature flag overrides file.
135
134
  *
136
135
  * Docker: `GATEWAY_SECURITY_DIR/feature-flags.json`
137
- * Local: `~/.vellum/protected/feature-flags.json`
136
+ * Local: `~/.vellum/` + gateway security subdir + `feature-flags.json`
138
137
  */
139
138
  function getFeatureFlagOverridesPath(): string {
140
139
  const securityDir = process.env.GATEWAY_SECURITY_DIR;
141
140
  if (securityDir) {
142
141
  return join(securityDir, "feature-flags.json");
143
142
  }
144
- return join(getRootDir(), "protected", "feature-flags.json");
143
+ return join(homedir(), ".vellum", "protected", "feature-flags.json");
145
144
  }
146
145
 
147
146
  /**
148
- * Load override values from the protected feature-flags.json file.
147
+ * Load override values from the local feature-flags.json file.
149
148
  * Returns an empty record if the file doesn't exist or is malformed.
150
149
  */
151
150
  function loadOverridesFromFile(): Record<string, boolean> {
@@ -246,16 +245,26 @@ function loadOverridesFromGateway(): Record<string, boolean> {
246
245
  }
247
246
 
248
247
  /**
249
- * Load overrides from the appropriate source based on runtime mode.
248
+ * Load overrides, preferring the gateway HTTP API.
249
+ *
250
+ * In containerized mode, always uses the gateway. In local mode, tries
251
+ * the gateway first and falls back to `loadOverridesFromFile()` when
252
+ * the gateway is not yet available (startup race).
253
+ *
250
254
  * Results are cached at module level.
251
255
  */
252
256
  function loadOverrides(): Record<string, boolean> {
253
257
  if (cachedOverrides != null) return cachedOverrides;
254
258
 
255
- cachedOverrides = getIsContainerized()
256
- ? loadOverridesFromGateway()
257
- : loadOverridesFromFile();
259
+ const gatewayOverrides = loadOverridesFromGateway();
260
+ if (Object.keys(gatewayOverrides).length > 0 || getIsContainerized()) {
261
+ cachedOverrides = gatewayOverrides;
262
+ return cachedOverrides;
263
+ }
258
264
 
265
+ // Graceful fallback: in local mode, if the gateway hasn't started yet
266
+ // (empty response), read overrides from file as a temporary measure.
267
+ cachedOverrides = loadOverridesFromFile();
259
268
  return cachedOverrides;
260
269
  }
261
270
 
@@ -346,8 +355,7 @@ export function _setOverridesForTesting(
346
355
  * Resolve whether an assistant feature flag is enabled.
347
356
  *
348
357
  * Resolution order:
349
- * 1. Override from `~/.vellum/protected/feature-flags.json` (local) or
350
- * gateway HTTP (Docker/containerized)
358
+ * 1. Override from gateway HTTP API (or local file fallback)
351
359
  * 2. Remote value from `feature-flags-remote.json` (platform-pushed,
352
360
  * cached locally)
353
361
  * 3. defaults registry `defaultEnabled` (for declared assistant-scope keys)
@@ -361,7 +369,7 @@ export function isAssistantFeatureFlagEnabled(
361
369
  const declared = defaults[key];
362
370
  const overrides = loadOverrides();
363
371
 
364
- // 1. Check overrides from protected feature-flags file / gateway
372
+ // 1. Check overrides from gateway / local file
365
373
  const explicit = overrides[key];
366
374
  if (typeof explicit === "boolean") return explicit;
367
375
 
@@ -75,6 +75,10 @@
75
75
  },
76
76
  "required": ["title"]
77
77
  },
78
+ "change_summary": {
79
+ "type": "string",
80
+ "description": "Short summary of what changed, using git conventional commit format (e.g. 'feat: add coin flip app', 'fix: correct button alignment'). Used as the version history entry."
81
+ },
78
82
  "activity": {
79
83
  "type": "string",
80
84
  "description": "Brief non-technical explanation of why this tool is being called"
@@ -97,6 +101,10 @@
97
101
  "type": "string",
98
102
  "description": "The ID of the app to delete"
99
103
  },
104
+ "change_summary": {
105
+ "type": "string",
106
+ "description": "Short summary of what changed, using git conventional commit format (e.g. 'chore: delete unused app'). Used as the version history entry."
107
+ },
100
108
  "activity": {
101
109
  "type": "string",
102
110
  "description": "Brief non-technical explanation of why this tool is being called"
@@ -119,6 +127,10 @@
119
127
  "type": "string",
120
128
  "description": "The ID of the app to refresh"
121
129
  },
130
+ "change_summary": {
131
+ "type": "string",
132
+ "description": "Short summary of what changed, using git conventional commit format (e.g. 'feat: add dark mode toggle', 'fix: form validation'). Used as the version history entry."
133
+ },
122
134
  "activity": {
123
135
  "type": "string",
124
136
  "description": "Brief non-technical explanation of why this tool is being called"
@@ -145,6 +157,10 @@
145
157
  "type": "string",
146
158
  "description": "Optional description to guide icon generation (e.g. 'a blue calendar with a checkmark'). If omitted, the app name and description are used."
147
159
  },
160
+ "change_summary": {
161
+ "type": "string",
162
+ "description": "Short summary of what changed, using git conventional commit format (e.g. 'feat: generate app icon'). Used as the version history entry."
163
+ },
148
164
  "activity": {
149
165
  "type": "string",
150
166
  "description": "Brief non-technical explanation of why this tool is being called"
@@ -1,5 +1,6 @@
1
1
  import { isAssistantFeatureFlagEnabled } from "../../../../config/assistant-feature-flags.js";
2
2
  import { getConfig } from "../../../../config/loader.js";
3
+ import { setAppCommitMessage } from "../../../../memory/app-git-service.js";
3
4
  import * as appStore from "../../../../memory/app-store.js";
4
5
  import type { AppCreateInput } from "../../../../tools/apps/executors.js";
5
6
  import { executeAppCreate } from "../../../../tools/apps/executors.js";
@@ -12,6 +13,9 @@ export async function run(
12
13
  input: Record<string, unknown>,
13
14
  context: ToolContext,
14
15
  ): Promise<ToolExecutionResult> {
16
+ if (typeof input.change_summary === "string" && input.change_summary.trim()) {
17
+ setAppCommitMessage(context.conversationId, input.change_summary.trim());
18
+ }
15
19
  const multifileEnabled = isAssistantFeatureFlagEnabled(
16
20
  "app-builder-multifile",
17
21
  getConfig(),
@@ -1,3 +1,4 @@
1
+ import { setAppCommitMessage } from "../../../../memory/app-git-service.js";
1
2
  import * as appStore from "../../../../memory/app-store.js";
2
3
  import { executeAppDelete } from "../../../../tools/apps/executors.js";
3
4
  import type {
@@ -7,7 +8,10 @@ import type {
7
8
 
8
9
  export async function run(
9
10
  input: Record<string, unknown>,
10
- _context: ToolContext,
11
+ context: ToolContext,
11
12
  ): Promise<ToolExecutionResult> {
13
+ if (typeof input.change_summary === "string" && input.change_summary.trim()) {
14
+ setAppCommitMessage(context.conversationId, input.change_summary.trim());
15
+ }
12
16
  return executeAppDelete({ app_id: input.app_id as string }, appStore);
13
17
  }
@@ -1,11 +1,19 @@
1
+ import { setAppCommitMessage } from "../../../../memory/app-git-service.js";
1
2
  import * as appStore from "../../../../memory/app-store.js";
2
3
  import type { AppGenerateIconInput } from "../../../../tools/apps/executors.js";
3
4
  import { executeAppGenerateIcon } from "../../../../tools/apps/executors.js";
4
- import type { ToolExecutionResult } from "../../../../tools/types.js";
5
+ import type {
6
+ ToolContext,
7
+ ToolExecutionResult,
8
+ } from "../../../../tools/types.js";
5
9
 
6
10
  export async function run(
7
11
  input: Record<string, unknown>,
12
+ context: ToolContext,
8
13
  ): Promise<ToolExecutionResult> {
14
+ if (typeof input.change_summary === "string" && input.change_summary.trim()) {
15
+ setAppCommitMessage(context.conversationId, input.change_summary.trim());
16
+ }
9
17
  return executeAppGenerateIcon(
10
18
  input as unknown as AppGenerateIconInput,
11
19
  appStore,
@@ -1,3 +1,4 @@
1
+ import { setAppCommitMessage } from "../../../../memory/app-git-service.js";
1
2
  import * as appStore from "../../../../memory/app-store.js";
2
3
  import { executeAppRefresh } from "../../../../tools/apps/executors.js";
3
4
  import type {
@@ -7,7 +8,10 @@ import type {
7
8
 
8
9
  export async function run(
9
10
  input: Record<string, unknown>,
10
- _context: ToolContext,
11
+ context: ToolContext,
11
12
  ): Promise<ToolExecutionResult> {
13
+ if (typeof input.change_summary === "string" && input.change_summary.trim()) {
14
+ setAppCommitMessage(context.conversationId, input.change_summary.trim());
15
+ }
12
16
  return executeAppRefresh({ app_id: input.app_id as string }, appStore);
13
17
  }
@@ -47,6 +47,14 @@
47
47
  "is_primary": {
48
48
  "type": "boolean",
49
49
  "description": "Whether this is the primary channel for this type"
50
+ },
51
+ "external_user_id": {
52
+ "type": "string",
53
+ "description": "Platform-native user ID (e.g. Slack user ID like U12345). Used to cache user lookups."
54
+ },
55
+ "external_chat_id": {
56
+ "type": "string",
57
+ "description": "Platform-native chat/DM channel ID (e.g. Slack DM channel like D12345). Used to cache DM channel lookups."
50
58
  }
51
59
  },
52
60
  "required": ["type", "address"]
@@ -11,6 +11,8 @@ interface ContactChannel {
11
11
  type: string;
12
12
  address: string;
13
13
  isPrimary: boolean;
14
+ externalUserId?: string | null;
15
+ externalChatId?: string | null;
14
16
  }
15
17
 
16
18
  interface ContactResponse {
@@ -28,7 +30,14 @@ function formatContactSummary(c: ContactResponse): string {
28
30
  parts.push(` Interactions: ${c.interactionCount}`);
29
31
  if (c.channels.length > 0) {
30
32
  const channelList = c.channels
31
- .map((ch) => `${ch.type}:${ch.address}${ch.isPrimary ? "*" : ""}`)
33
+ .map((ch) => {
34
+ let s = `${ch.type}:${ch.address}${ch.isPrimary ? "*" : ""}`;
35
+ const extras: string[] = [];
36
+ if (ch.externalUserId) extras.push(`userId: ${ch.externalUserId}`);
37
+ if (ch.externalChatId) extras.push(`chatId: ${ch.externalChatId}`);
38
+ if (extras.length > 0) s += ` (${extras.join(", ")})`;
39
+ return s;
40
+ })
32
41
  .join(", ");
33
42
  parts.push(` Channels: ${channelList}`);
34
43
  }
@@ -11,6 +11,8 @@ interface ContactChannel {
11
11
  type: string;
12
12
  address: string;
13
13
  isPrimary: boolean;
14
+ externalUserId?: string | null;
15
+ externalChatId?: string | null;
14
16
  }
15
17
 
16
18
  interface ContactResponse {
@@ -30,7 +32,11 @@ function formatContact(c: ContactResponse): string {
30
32
  lines.push(" Channels:");
31
33
  for (const ch of c.channels) {
32
34
  const primary = ch.isPrimary ? " (primary)" : "";
33
- lines.push(` - ${ch.type}: ${ch.address}${primary}`);
35
+ const extras: string[] = [];
36
+ if (ch.externalUserId) extras.push(`userId: ${ch.externalUserId}`);
37
+ if (ch.externalChatId) extras.push(`chatId: ${ch.externalChatId}`);
38
+ const extrasStr = extras.length > 0 ? ` (${extras.join(", ")})` : "";
39
+ lines.push(` - ${ch.type}: ${ch.address}${primary}${extrasStr}`);
34
40
  }
35
41
  }
36
42
  return lines.join("\n");
@@ -53,12 +59,20 @@ export async function executeContactUpsert(
53
59
  }
54
60
 
55
61
  const rawChannels = input.channels as
56
- | Array<{ type: string; address: string; is_primary?: boolean }>
62
+ | Array<{
63
+ type: string;
64
+ address: string;
65
+ is_primary?: boolean;
66
+ external_user_id?: string;
67
+ external_chat_id?: string;
68
+ }>
57
69
  | undefined;
58
70
  const channels = rawChannels?.map((ch) => ({
59
71
  type: ch.type,
60
72
  address: ch.address,
61
73
  isPrimary: ch.is_primary,
74
+ externalUserId: ch.external_user_id,
75
+ externalChatId: ch.external_chat_id,
62
76
  }));
63
77
 
64
78
  try {
@@ -55,6 +55,7 @@ const MIME_MAP: Record<string, string> = {
55
55
  ".tiff": "image/tiff",
56
56
  ".svg": "image/svg+xml",
57
57
  ".heic": "image/heic",
58
+ ".heif": "image/heif",
58
59
  };
59
60
 
60
61
  function detectMimeType(filePath: string): string | null {