@vellumai/assistant 0.5.6 → 0.5.8

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 (442) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +3 -2
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docker-entrypoint.sh +9 -0
  7. package/docs/architecture/keychain-broker.md +45 -240
  8. package/docs/architecture/memory.md +13 -11
  9. package/docs/architecture/security.md +0 -17
  10. package/docs/credential-execution-service.md +2 -2
  11. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-contracts/src/error.ts +1 -1
  13. package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
  14. package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
  15. package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
  16. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +120 -1
  17. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  18. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  19. package/package.json +2 -3
  20. package/src/__tests__/actor-token-service.test.ts +0 -114
  21. package/src/__tests__/approval-cascade.test.ts +0 -1
  22. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  23. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  24. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  25. package/src/__tests__/btw-routes.test.ts +0 -39
  26. package/src/__tests__/call-controller.test.ts +0 -1
  27. package/src/__tests__/call-domain.test.ts +0 -128
  28. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  29. package/src/__tests__/ces-startup-timeout.test.ts +40 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  31. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  32. package/src/__tests__/checker.test.ts +4 -2
  33. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  34. package/src/__tests__/config-schema-cmd.test.ts +0 -2
  35. package/src/__tests__/config-schema.test.ts +3 -1
  36. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
  37. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  38. package/src/__tests__/conversation-agent-loop.test.ts +2 -4
  39. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  40. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
  41. package/src/__tests__/conversation-error.test.ts +15 -1
  42. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  43. package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
  44. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
  45. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
  46. package/src/__tests__/conversation-queue.test.ts +0 -1
  47. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  48. package/src/__tests__/conversation-slash-queue.test.ts +0 -1
  49. package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
  50. package/src/__tests__/conversation-title-service.test.ts +87 -0
  51. package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
  52. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
  53. package/src/__tests__/credential-execution-client.test.ts +5 -2
  54. package/src/__tests__/credential-execution-feature-gates.test.ts +59 -30
  55. package/src/__tests__/credential-execution-managed-contract.test.ts +35 -20
  56. package/src/__tests__/credential-security-e2e.test.ts +1 -67
  57. package/src/__tests__/credential-security-invariants.test.ts +6 -50
  58. package/src/__tests__/credentials-cli.test.ts +82 -3
  59. package/src/__tests__/daemon-credential-client.test.ts +123 -0
  60. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  61. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
  62. package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
  63. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  64. package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
  65. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  66. package/src/__tests__/host-shell-tool.test.ts +6 -7
  67. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  68. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  69. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  70. package/src/__tests__/intent-routing.test.ts +0 -13
  71. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  72. package/src/__tests__/journal-context.test.ts +335 -0
  73. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  74. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
  75. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  76. package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
  77. package/src/__tests__/memory-recall-quality.test.ts +48 -17
  78. package/src/__tests__/memory-regressions.test.ts +408 -363
  79. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
  80. package/src/__tests__/migration-export-http.test.ts +2 -2
  81. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  82. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  83. package/src/__tests__/migration-validate-http.test.ts +2 -2
  84. package/src/__tests__/non-member-access-request.test.ts +2 -7
  85. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  86. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  87. package/src/__tests__/notification-decision-strategy.test.ts +71 -0
  88. package/src/__tests__/oauth-cli.test.ts +5 -1
  89. package/src/__tests__/permission-types.test.ts +1 -0
  90. package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
  91. package/src/__tests__/provider-error-scenarios.test.ts +0 -267
  92. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  93. package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
  94. package/src/__tests__/qdrant-manager.test.ts +28 -2
  95. package/src/__tests__/registry.test.ts +0 -6
  96. package/src/__tests__/relay-server.test.ts +1 -2
  97. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  98. package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
  99. package/src/__tests__/secret-onetime-send.test.ts +1 -1
  100. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  101. package/src/__tests__/secure-keys.test.ts +95 -272
  102. package/src/__tests__/shell-identity.test.ts +96 -6
  103. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  104. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  105. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  106. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  107. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  108. package/src/__tests__/skill-load-tool.test.ts +0 -2
  109. package/src/__tests__/skill-memory.test.ts +17 -3
  110. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  111. package/src/__tests__/skills.test.ts +0 -2
  112. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  113. package/src/__tests__/stale-approval-dedup.test.ts +171 -0
  114. package/src/__tests__/stt-hints.test.ts +437 -0
  115. package/src/__tests__/suggestion-routes.test.ts +1 -32
  116. package/src/__tests__/system-prompt.test.ts +0 -1
  117. package/src/__tests__/task-memory-cleanup.test.ts +14 -0
  118. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  119. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  120. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  121. package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
  122. package/src/__tests__/update-bulletin.test.ts +0 -2
  123. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  124. package/src/__tests__/voice-quality.test.ts +58 -0
  125. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -7
  126. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  127. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +220 -0
  128. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  129. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  130. package/src/acp/agent-process.ts +9 -1
  131. package/src/agent/loop.ts +1 -1
  132. package/src/approvals/guardian-request-resolvers.ts +164 -38
  133. package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
  134. package/src/calls/audio-store.test.ts +97 -0
  135. package/src/calls/audio-store.ts +205 -0
  136. package/src/calls/call-controller.ts +90 -8
  137. package/src/calls/call-domain.ts +3 -0
  138. package/src/calls/call-store.ts +10 -3
  139. package/src/calls/fish-audio-client.ts +129 -0
  140. package/src/calls/relay-server.ts +27 -0
  141. package/src/calls/stt-hints.ts +189 -0
  142. package/src/calls/tts-text-sanitizer.ts +61 -0
  143. package/src/calls/twilio-routes.ts +34 -5
  144. package/src/calls/types.ts +1 -0
  145. package/src/calls/voice-ingress-preflight.ts +0 -42
  146. package/src/calls/voice-quality.ts +38 -5
  147. package/src/calls/voice-session-bridge.ts +7 -12
  148. package/src/cli/commands/avatar.ts +2 -2
  149. package/src/cli/commands/config.ts +1 -4
  150. package/src/cli/commands/credentials.ts +128 -82
  151. package/src/cli/commands/doctor.ts +2 -2
  152. package/src/cli/commands/keys.ts +7 -7
  153. package/src/cli/commands/memory.ts +1 -1
  154. package/src/cli/commands/oauth/connections.ts +11 -29
  155. package/src/cli/commands/oauth/index.ts +7 -0
  156. package/src/cli/commands/oauth/platform.ts +525 -0
  157. package/src/cli/commands/platform.ts +3 -3
  158. package/src/cli/lib/daemon-credential-client.ts +284 -0
  159. package/src/cli.ts +1 -1
  160. package/src/config/assistant-feature-flags.ts +186 -5
  161. package/src/config/bundled-skills/AGENTS.md +34 -0
  162. package/src/config/bundled-skills/acp/SKILL.md +10 -0
  163. package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
  164. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  165. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  166. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  167. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
  168. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
  169. package/src/config/bundled-skills/settings/SKILL.md +15 -2
  170. package/src/config/bundled-skills/settings/TOOLS.json +47 -2
  171. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
  172. package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
  173. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  174. package/src/config/bundled-skills/slack/SKILL.md +1 -1
  175. package/src/config/bundled-tool-registry.ts +5 -11
  176. package/src/config/defaults.ts +0 -2
  177. package/src/config/env-registry.ts +5 -5
  178. package/src/config/env.ts +21 -14
  179. package/src/config/feature-flag-registry.json +49 -9
  180. package/src/config/loader.ts +106 -42
  181. package/src/config/schema.ts +9 -29
  182. package/src/config/schemas/calls.ts +30 -0
  183. package/src/config/schemas/fish-audio.ts +39 -0
  184. package/src/config/schemas/inference.ts +2 -2
  185. package/src/config/schemas/journal.ts +16 -0
  186. package/src/config/schemas/memory-processing.ts +2 -2
  187. package/src/config/schemas/security.ts +0 -4
  188. package/src/config/types.ts +1 -1
  189. package/src/contacts/contact-store.ts +39 -0
  190. package/src/contacts/types.ts +2 -0
  191. package/src/credential-execution/approval-bridge.ts +1 -0
  192. package/src/credential-execution/executable-discovery.ts +28 -4
  193. package/src/credential-execution/feature-gates.ts +16 -0
  194. package/src/credential-execution/process-manager.ts +38 -0
  195. package/src/credential-execution/startup-timeout.ts +36 -0
  196. package/src/daemon/approval-generators.ts +3 -9
  197. package/src/daemon/assistant-attachments.ts +9 -0
  198. package/src/daemon/config-watcher.ts +5 -0
  199. package/src/daemon/conversation-error.ts +13 -1
  200. package/src/daemon/conversation-memory.ts +1 -2
  201. package/src/daemon/conversation-process.ts +18 -1
  202. package/src/daemon/conversation-surfaces.ts +30 -1
  203. package/src/daemon/conversation-tool-setup.ts +0 -105
  204. package/src/daemon/conversation.ts +21 -1
  205. package/src/daemon/guardian-action-generators.ts +3 -9
  206. package/src/daemon/handlers/config-vercel.ts +92 -0
  207. package/src/daemon/handlers/skills.ts +2 -15
  208. package/src/daemon/install-symlink.ts +195 -0
  209. package/src/daemon/lifecycle.ts +234 -51
  210. package/src/daemon/message-types/conversations.ts +4 -4
  211. package/src/daemon/message-types/diagnostics.ts +3 -22
  212. package/src/daemon/message-types/messages.ts +0 -2
  213. package/src/daemon/message-types/upgrades.ts +8 -0
  214. package/src/daemon/server.ts +32 -95
  215. package/src/events/domain-events.ts +2 -1
  216. package/src/inbound/platform-callback-registration.ts +3 -3
  217. package/src/instrument.ts +8 -5
  218. package/src/memory/app-store.ts +31 -0
  219. package/src/memory/conversation-title-service.ts +50 -1
  220. package/src/memory/db-init.ts +16 -0
  221. package/src/memory/indexer.ts +19 -10
  222. package/src/memory/items-extractor.ts +328 -321
  223. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  224. package/src/memory/job-handlers/summarization.ts +26 -16
  225. package/src/memory/jobs-store.ts +63 -6
  226. package/src/memory/jobs-worker.ts +31 -7
  227. package/src/memory/journal-memory.ts +214 -0
  228. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  229. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  230. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  231. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  232. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  233. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  234. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  235. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  236. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  237. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  238. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  239. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  240. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  241. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  242. package/src/memory/migrations/116-messages-fts.ts +106 -1
  243. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  244. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  245. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  246. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  247. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  248. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  249. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  250. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  251. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  252. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  253. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  254. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  255. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  256. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  257. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  258. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  259. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  260. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  261. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  262. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  263. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  264. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  265. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  266. package/src/memory/migrations/193-add-source-type-columns.ts +81 -0
  267. package/src/memory/migrations/index.ts +5 -0
  268. package/src/memory/migrations/registry.ts +98 -0
  269. package/src/memory/migrations/validate-migration-state.ts +137 -11
  270. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  271. package/src/memory/qdrant-manager.ts +64 -7
  272. package/src/memory/retriever.test.ts +37 -25
  273. package/src/memory/retriever.ts +24 -49
  274. package/src/memory/schema/calls.ts +1 -0
  275. package/src/memory/schema/contacts.ts +1 -0
  276. package/src/memory/schema/memory-core.ts +2 -0
  277. package/src/memory/search/formatting.ts +7 -44
  278. package/src/memory/search/staleness.ts +4 -0
  279. package/src/memory/search/tier-classifier.ts +10 -2
  280. package/src/memory/search/types.ts +2 -5
  281. package/src/memory/task-memory-cleanup.ts +4 -3
  282. package/src/notifications/adapters/slack.ts +168 -6
  283. package/src/notifications/broadcaster.ts +1 -0
  284. package/src/notifications/copy-composer.ts +59 -2
  285. package/src/notifications/decision-engine.ts +4 -1
  286. package/src/notifications/signal.ts +2 -0
  287. package/src/notifications/types.ts +2 -0
  288. package/src/oauth/connection-resolver.ts +6 -4
  289. package/src/permissions/checker.ts +0 -38
  290. package/src/permissions/shell-identity.ts +76 -22
  291. package/src/permissions/types.ts +4 -2
  292. package/src/platform/client.ts +35 -7
  293. package/src/prompts/journal-context.ts +133 -0
  294. package/src/prompts/persona-resolver.ts +194 -0
  295. package/src/prompts/system-prompt.ts +44 -4
  296. package/src/prompts/templates/SOUL.md +10 -0
  297. package/src/prompts/templates/users/default.md +1 -0
  298. package/src/providers/provider-send-message.ts +3 -32
  299. package/src/providers/registry.ts +29 -179
  300. package/src/providers/types.ts +1 -1
  301. package/src/runtime/access-request-helper.ts +4 -0
  302. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  303. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  304. package/src/runtime/auth/__tests__/guard-tests.test.ts +9 -50
  305. package/src/runtime/auth/external-assistant-id.ts +13 -59
  306. package/src/runtime/auth/route-policy.ts +17 -1
  307. package/src/runtime/auth/token-service.ts +43 -138
  308. package/src/runtime/channel-readiness-service.ts +1 -16
  309. package/src/runtime/gateway-client.ts +47 -4
  310. package/src/runtime/guardian-decision-types.ts +45 -4
  311. package/src/runtime/http-server.ts +31 -3
  312. package/src/runtime/middleware/error-handler.ts +1 -9
  313. package/src/runtime/routes/access-request-decision.ts +2 -2
  314. package/src/runtime/routes/app-management-routes.ts +2 -1
  315. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
  316. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
  317. package/src/runtime/routes/audio-routes.ts +40 -0
  318. package/src/runtime/routes/btw-routes.ts +0 -17
  319. package/src/runtime/routes/channel-readiness-routes.ts +9 -4
  320. package/src/runtime/routes/conversation-query-routes.ts +63 -1
  321. package/src/runtime/routes/conversation-routes.ts +4 -44
  322. package/src/runtime/routes/debug-routes.ts +12 -9
  323. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  324. package/src/runtime/routes/guardian-approval-interception.ts +168 -11
  325. package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
  326. package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
  327. package/src/runtime/routes/identity-routes.ts +19 -30
  328. package/src/runtime/routes/inbound-message-handler.ts +31 -1
  329. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
  330. package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
  331. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  332. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  333. package/src/runtime/routes/integrations/twilio.ts +52 -10
  334. package/src/runtime/routes/integrations/vercel.ts +89 -0
  335. package/src/runtime/routes/log-export-routes.ts +5 -0
  336. package/src/runtime/routes/memory-item-routes.test.ts +3 -3
  337. package/src/runtime/routes/memory-item-routes.ts +46 -14
  338. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  339. package/src/runtime/routes/migration-routes.ts +17 -1
  340. package/src/runtime/routes/notification-routes.ts +58 -0
  341. package/src/runtime/routes/schedule-routes.ts +65 -0
  342. package/src/runtime/routes/secret-routes.ts +141 -10
  343. package/src/runtime/routes/settings-routes.ts +41 -1
  344. package/src/runtime/routes/tts-routes.ts +96 -0
  345. package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
  346. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  347. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  348. package/src/runtime/routes/workspace-routes.ts +1 -1
  349. package/src/runtime/routes/workspace-utils.ts +86 -2
  350. package/src/security/ces-credential-client.ts +75 -29
  351. package/src/security/ces-rpc-credential-backend.ts +86 -0
  352. package/src/security/credential-backend.ts +22 -92
  353. package/src/security/keychain-broker-client.ts +10 -2
  354. package/src/security/secure-keys.ts +113 -115
  355. package/src/skills/catalog-install.ts +6 -32
  356. package/src/skills/skill-memory.ts +1 -0
  357. package/src/subagent/manager.ts +2 -5
  358. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  359. package/src/tools/acp/spawn.ts +78 -1
  360. package/src/tools/calls/call-start.ts +1 -0
  361. package/src/tools/credentials/vault.ts +5 -3
  362. package/src/tools/executor.ts +0 -4
  363. package/src/tools/memory/definitions.ts +3 -2
  364. package/src/tools/memory/handlers.ts +10 -7
  365. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  366. package/src/tools/network/web-fetch.ts +3 -1
  367. package/src/tools/skills/execute.ts +1 -1
  368. package/src/tools/terminal/safe-env.ts +1 -0
  369. package/src/tools/types.ts +0 -8
  370. package/src/util/browser.ts +15 -0
  371. package/src/util/errors.ts +0 -12
  372. package/src/util/platform.ts +4 -51
  373. package/src/workspace/git-service.ts +5 -2
  374. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  375. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  376. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  377. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  378. package/src/workspace/migrations/006-services-config.ts +49 -0
  379. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  380. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  381. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  382. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  383. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  384. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  385. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  386. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  387. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  388. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  389. package/src/workspace/migrations/017-seed-persona-dirs.ts +96 -0
  390. package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
  391. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
  392. package/src/workspace/migrations/migrate-to-workspace-volume.ts +27 -5
  393. package/src/workspace/migrations/registry.ts +12 -0
  394. package/src/workspace/migrations/runner.ts +106 -2
  395. package/src/workspace/migrations/types.ts +4 -0
  396. package/src/workspace/provider-commit-message-generator.ts +12 -21
  397. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  398. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  399. package/src/__tests__/diagnostics-export.test.ts +0 -288
  400. package/src/__tests__/local-gateway-health.test.ts +0 -209
  401. package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
  402. package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
  403. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  404. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  405. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  406. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  407. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  408. package/src/__tests__/swarm-recursion.test.ts +0 -197
  409. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  410. package/src/__tests__/swarm-tool.test.ts +0 -185
  411. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  412. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  413. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  414. package/src/commands/cc-command-registry.ts +0 -248
  415. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  416. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  417. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  418. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  419. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  420. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  421. package/src/config/schemas/swarm.ts +0 -82
  422. package/src/logfire.ts +0 -135
  423. package/src/memory/search/lexical.ts +0 -48
  424. package/src/providers/failover.ts +0 -186
  425. package/src/runtime/local-gateway-health.ts +0 -275
  426. package/src/security/secret-ingress.ts +0 -68
  427. package/src/swarm/backend-claude-code.ts +0 -225
  428. package/src/swarm/checkpoint.ts +0 -137
  429. package/src/swarm/graph-utils.ts +0 -53
  430. package/src/swarm/index.ts +0 -55
  431. package/src/swarm/limits.ts +0 -66
  432. package/src/swarm/orchestrator.ts +0 -424
  433. package/src/swarm/plan-validator.ts +0 -117
  434. package/src/swarm/router-planner.ts +0 -162
  435. package/src/swarm/router-prompts.ts +0 -39
  436. package/src/swarm/synthesizer.ts +0 -81
  437. package/src/swarm/types.ts +0 -72
  438. package/src/swarm/worker-backend.ts +0 -131
  439. package/src/swarm/worker-prompts.ts +0 -80
  440. package/src/swarm/worker-runner.ts +0 -170
  441. package/src/tools/claude-code/claude-code.ts +0 -610
  442. package/src/tools/swarm/delegate.ts +0 -205
@@ -8,7 +8,10 @@
8
8
  */
9
9
  import { isInviteCodeRedemptionEnabled } from "../../../channels/config.js";
10
10
  import type { ChannelId } from "../../../channels/types.js";
11
- import { findContactChannel } from "../../../contacts/contact-store.js";
11
+ import {
12
+ findContactChannel,
13
+ findGuardianForChannel,
14
+ } from "../../../contacts/contact-store.js";
12
15
  import { touchChannelLastSeen } from "../../../contacts/contacts-write.js";
13
16
  import type {
14
17
  ChannelStatus,
@@ -21,7 +24,10 @@ import {
21
24
  findByInviteCodeHash,
22
25
  findByInviteCodeHashAnyChannel,
23
26
  } from "../../../memory/invite-store.js";
27
+ import { MESSAGE_PREVIEW_MAX_LENGTH } from "../../../notifications/copy-composer.js";
28
+ import { resolveGuardianName } from "../../../prompts/user-reference.js";
24
29
  import { getLogger } from "../../../util/logger.js";
30
+ import { truncate } from "../../../util/truncate.js";
25
31
  import { hashVoiceCode } from "../../../util/voice-code.js";
26
32
  import { notifyGuardianOfAccessRequest } from "../../access-request-helper.js";
27
33
  import { getInviteAdapterRegistry } from "../../channel-invite-transport.js";
@@ -32,6 +38,7 @@ import {
32
38
  resolveBootstrapToken,
33
39
  } from "../../channel-verification-service.js";
34
40
  import { deliverChannelReply } from "../../gateway-client.js";
41
+ import { ensureVellumGuardianBinding } from "../../guardian-vellum-migration.js";
35
42
  import {
36
43
  redeemInvite,
37
44
  redeemInviteByCode,
@@ -40,6 +47,42 @@ import { getInviteRedemptionReply } from "../../invite-redemption-templates.js";
40
47
 
41
48
  const log = getLogger("runtime-http");
42
49
 
50
+ /**
51
+ * Resolve the guardian's display name for use in requester-facing messages.
52
+ *
53
+ * Uses the assistant's anchored vellum principal to validate the guardian
54
+ * contact, matching the same strategy used by `notifyGuardianOfAccessRequest`.
55
+ * This prevents stale or cross-assistant contacts from leaking a wrong name.
56
+ */
57
+ function resolveGuardianLabel(
58
+ sourceChannel: ChannelId,
59
+ canonicalAssistantId: string,
60
+ ): string {
61
+ const anchoredPrincipalId = ensureVellumGuardianBinding(canonicalAssistantId);
62
+
63
+ // Try source-channel guardian, but only accept it when the principal
64
+ // matches the assistant's anchor.
65
+ const sourceGuardian = findGuardianForChannel(sourceChannel);
66
+ if (
67
+ sourceGuardian &&
68
+ sourceGuardian.contact.principalId === anchoredPrincipalId
69
+ ) {
70
+ return resolveGuardianName(sourceGuardian.contact.displayName);
71
+ }
72
+
73
+ // Fall back to the vellum-channel guardian with the same anchor check.
74
+ const vellumGuardian = findGuardianForChannel("vellum");
75
+ if (
76
+ vellumGuardian &&
77
+ vellumGuardian.contact.principalId === anchoredPrincipalId
78
+ ) {
79
+ return resolveGuardianName(vellumGuardian.contact.displayName);
80
+ }
81
+
82
+ // No anchored guardian found — use generic fallback.
83
+ return resolveGuardianName(undefined);
84
+ }
85
+
43
86
  // ---------------------------------------------------------------------------
44
87
  // Public API
45
88
  // ---------------------------------------------------------------------------
@@ -321,6 +364,10 @@ export async function enforceIngressAcl(
321
364
  actorExternalId: canonicalSenderId ?? rawSenderId,
322
365
  actorDisplayName,
323
366
  actorUsername,
367
+ messagePreview: truncate(
368
+ trimmedContent,
369
+ MESSAGE_PREVIEW_MAX_LENGTH,
370
+ ),
324
371
  });
325
372
  } catch (err) {
326
373
  log.error(
@@ -349,7 +396,7 @@ export async function enforceIngressAcl(
349
396
  dmCallbackUrl,
350
397
  {
351
398
  chatId: senderUserId,
352
- text: "I don't recognize you yet! I've let my owner know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.",
399
+ text: `I don't recognize you yet! I've let ${resolveGuardianLabel(sourceChannel, canonicalAssistantId)} know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.`,
353
400
  assistantId,
354
401
  },
355
402
  mintBearerToken(),
@@ -387,6 +434,10 @@ export async function enforceIngressAcl(
387
434
  actorExternalId: canonicalSenderId ?? rawSenderId,
388
435
  actorDisplayName,
389
436
  actorUsername,
437
+ messagePreview: truncate(
438
+ trimmedContent,
439
+ MESSAGE_PREVIEW_MAX_LENGTH,
440
+ ),
390
441
  });
391
442
  guardianNotified = accessResult.notified;
392
443
  } catch (err) {
@@ -398,7 +449,7 @@ export async function enforceIngressAcl(
398
449
 
399
450
  if (replyCallbackUrl) {
400
451
  const replyText = guardianNotified
401
- ? "Hmm looks like you don't have access to talk to me. I'll let them know you tried talking to me and get back to you."
452
+ ? `Hmm looks like you don't have access to talk to me. I'll let ${resolveGuardianLabel(sourceChannel, canonicalAssistantId)} know you tried talking to me and get back to you.`
402
453
  : "Sorry, you haven't been approved to message this assistant.";
403
454
  const replyPayload: Parameters<typeof deliverChannelReply>[1] = {
404
455
  chatId: conversationExternalId,
@@ -579,6 +630,10 @@ export async function enforceIngressAcl(
579
630
  previousMemberStatus: channelStatusToMemberStatus(
580
631
  resolvedMember.channel.status,
581
632
  ),
633
+ messagePreview: truncate(
634
+ trimmedContent,
635
+ MESSAGE_PREVIEW_MAX_LENGTH,
636
+ ),
582
637
  });
583
638
  } catch (err) {
584
639
  log.error(
@@ -603,7 +658,7 @@ export async function enforceIngressAcl(
603
658
  dmCallbackUrl,
604
659
  {
605
660
  chatId: senderUserId,
606
- text: "I don't recognize you yet! I've let my owner know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.",
661
+ text: `I don't recognize you yet! I've let ${resolveGuardianLabel(sourceChannel, canonicalAssistantId)} know you're trying to reach me. They'll need to share a 6-digit verification code with you — ask them directly if you know them. Once you have the code, reply here with it.`,
607
662
  assistantId,
608
663
  },
609
664
  mintBearerToken(),
@@ -645,6 +700,10 @@ export async function enforceIngressAcl(
645
700
  previousMemberStatus: channelStatusToMemberStatus(
646
701
  resolvedMember.channel.status,
647
702
  ),
703
+ messagePreview: truncate(
704
+ trimmedContent,
705
+ MESSAGE_PREVIEW_MAX_LENGTH,
706
+ ),
648
707
  });
649
708
  guardianNotified = accessResult.notified;
650
709
  } catch (err) {
@@ -657,7 +716,7 @@ export async function enforceIngressAcl(
657
716
 
658
717
  if (replyCallbackUrl) {
659
718
  const replyText = guardianNotified
660
- ? "Hmm looks like you don't have access to talk to me. I'll let them know you tried talking to me and get back to you."
719
+ ? `Hmm looks like you don't have access to talk to me. I'll let ${resolveGuardianLabel(sourceChannel, canonicalAssistantId)} know you tried talking to me and get back to you.`
661
720
  : "Sorry, you haven't been approved to message this assistant.";
662
721
  const inactiveReplyPayload: Parameters<
663
722
  typeof deliverChannelReply
@@ -75,8 +75,6 @@ export interface BackgroundProcessingParams {
75
75
  approvalCopyGenerator?: ApprovalCopyGenerator;
76
76
  commandIntent?: Record<string, unknown>;
77
77
  sourceLanguageCode?: string;
78
- /** External message ID (e.g. Slack message ts) used for reaction indicators. */
79
- externalMessageId?: string;
80
78
  /** Chat type from the gateway (e.g. "private", "group", "supergroup"). */
81
79
  chatType?: string;
82
80
  }
@@ -106,7 +104,6 @@ export function processChannelMessageInBackground(
106
104
  approvalCopyGenerator,
107
105
  commandIntent,
108
106
  sourceLanguageCode,
109
- externalMessageId,
110
107
  chatType,
111
108
  } = params;
112
109
 
@@ -126,18 +123,18 @@ export function processChannelMessageInBackground(
126
123
  )
127
124
  : undefined;
128
125
 
129
- // Add 👀 reaction to the inbound Slack message as a processing indicator
130
- const removeSlackReaction =
131
- shouldEmitSlackReaction(sourceChannel, replyCallbackUrl) &&
132
- externalMessageId
133
- ? addSlackEyesReaction(
134
- replyCallbackUrl!,
135
- externalChatId,
136
- externalMessageId,
137
- mintBearerToken,
138
- assistantId,
139
- )
140
- : undefined;
126
+ // Set Slack Assistants API "is thinking..." status indicator
127
+ const clearSlackThinkingStatus = shouldEmitSlackReaction(
128
+ sourceChannel,
129
+ replyCallbackUrl,
130
+ )
131
+ ? setSlackThinkingStatus(
132
+ replyCallbackUrl!,
133
+ externalChatId,
134
+ mintBearerToken,
135
+ assistantId,
136
+ )
137
+ : undefined;
141
138
  const stopApprovalWatcher = replyCallbackUrl
142
139
  ? startPendingApprovalPromptWatcher({
143
140
  conversationId,
@@ -230,7 +227,7 @@ export function processChannelMessageInBackground(
230
227
  deliveryStatus.recordProcessingFailure(eventId, err);
231
228
  } finally {
232
229
  stopTypingHeartbeat?.();
233
- removeSlackReaction?.();
230
+ clearSlackThinkingStatus?.();
234
231
  stopApprovalWatcher?.();
235
232
  stopTcApprovalNotifier?.();
236
233
  }
@@ -295,7 +292,7 @@ export function startTelegramTypingHeartbeat(
295
292
  }
296
293
 
297
294
  // ---------------------------------------------------------------------------
298
- // Slack eyes reaction indicator
295
+ // Slack Assistants API thinking status indicator
299
296
  // ---------------------------------------------------------------------------
300
297
 
301
298
  export function shouldEmitSlackReaction(
@@ -310,65 +307,80 @@ export function shouldEmitSlackReaction(
310
307
  }
311
308
  }
312
309
 
313
- const SLACK_EYES_MAX_DURATION_MS = 120_000;
310
+ const SLACK_THINKING_MAX_DURATION_MS = 120_000;
314
311
 
315
312
  /**
316
- * Add a 👀 reaction to the inbound Slack message and return a cleanup
317
- * function that removes it. Both operations are fire-and-forget.
313
+ * Set the Slack Assistants API "is thinking..." status on the thread and
314
+ * return a cleanup function that clears it. Both operations are fire-and-forget.
318
315
  *
319
- * A safety timer auto-removes the reaction after {@link SLACK_EYES_MAX_DURATION_MS}
320
- * to prevent stuck eyes when `processMessage` hangs (e.g. queued behind
321
- * an active session turn that never completes for this message).
316
+ * A safety timer auto-clears the status after {@link SLACK_THINKING_MAX_DURATION_MS}
317
+ * to prevent a stuck indicator when `processMessage` hangs.
322
318
  */
323
- export function addSlackEyesReaction(
319
+ export function setSlackThinkingStatus(
324
320
  callbackUrl: string,
325
321
  chatId: string,
326
- messageTs: string,
327
322
  mintBearerToken: () => string,
328
323
  assistantId?: string,
329
324
  ): () => void {
330
- let removed = false;
325
+ let cleared = false;
326
+
327
+ // Extract the thread timestamp from the callback URL so we can target
328
+ // the correct thread for the Assistants API status.
329
+ const threadTs = extractThreadTsFromCallbackUrl(callbackUrl);
330
+
331
+ // If there's no thread context, we can't set a thread status — bail.
332
+ if (!threadTs) {
333
+ return () => {};
334
+ }
331
335
 
332
- // Track the add promise so remove waits for it to settle first,
333
- // preventing a race where remove arrives at Slack before add.
334
- const addPromise = deliverChannelReply(
336
+ // Track the set promise so clear waits for it to settle first,
337
+ // preventing a race where clear arrives at Slack before set.
338
+ const setPromise = deliverChannelReply(
335
339
  callbackUrl,
336
340
  {
337
341
  chatId,
338
342
  assistantId,
339
- reaction: { action: "add", name: "eyes", messageTs },
343
+ assistantThreadStatus: {
344
+ channel: chatId,
345
+ threadTs,
346
+ status: "is thinking...",
347
+ },
340
348
  },
341
349
  mintBearerToken(),
342
350
  ).catch((err) => {
343
- log.debug({ err, chatId, messageTs }, "Failed to add Slack eyes reaction");
351
+ log.debug({ err, chatId, threadTs }, "Failed to set Slack thinking status");
344
352
  });
345
353
 
346
- const removeReaction = () => {
347
- if (removed) return;
348
- removed = true;
354
+ const clearStatus = () => {
355
+ if (cleared) return;
356
+ cleared = true;
349
357
  clearTimeout(safetyTimer);
350
- void addPromise.then(() =>
358
+ void setPromise.then(() =>
351
359
  deliverChannelReply(
352
360
  callbackUrl,
353
361
  {
354
362
  chatId,
355
363
  assistantId,
356
- reaction: { action: "remove", name: "eyes", messageTs },
364
+ assistantThreadStatus: {
365
+ channel: chatId,
366
+ threadTs,
367
+ status: "",
368
+ },
357
369
  },
358
370
  mintBearerToken(),
359
371
  ).catch((err) => {
360
372
  log.debug(
361
- { err, chatId, messageTs },
362
- "Failed to remove Slack eyes reaction",
373
+ { err, chatId, threadTs },
374
+ "Failed to clear Slack thinking status",
363
375
  );
364
376
  }),
365
377
  );
366
378
  };
367
379
 
368
- const safetyTimer = setTimeout(removeReaction, SLACK_EYES_MAX_DURATION_MS);
380
+ const safetyTimer = setTimeout(clearStatus, SLACK_THINKING_MAX_DURATION_MS);
369
381
  (safetyTimer as { unref?: () => void }).unref?.();
370
382
 
371
- return removeReaction;
383
+ return clearStatus;
372
384
  }
373
385
 
374
386
  // ---------------------------------------------------------------------------
@@ -1,12 +1,6 @@
1
1
  /**
2
- * Secret ingress check stage: persists the raw inbound payload, runs the
3
- * secret detection scan, and records a conversation-seen signal for
4
- * Telegram messages.
5
- *
6
- * The payload is stored before the scan so dead-lettered events can be
7
- * replayed. If the scan detects embedded secrets the stored payload is
8
- * cleared before the IngressBlockedError propagates, ensuring
9
- * secret-bearing content is never left on disk.
2
+ * Inbound payload persistence stage: persists the raw inbound payload and
3
+ * records a conversation-seen signal for Telegram messages.
10
4
  *
11
5
  * Extracted from inbound-message-handler.ts to keep the top-level handler
12
6
  * focused on orchestration.
@@ -15,8 +9,6 @@ import type { ChannelId } from "../../../channels/types.js";
15
9
  import type { TrustContext } from "../../../daemon/conversation-runtime-assembly.js";
16
10
  import { recordConversationSeenSignal } from "../../../memory/conversation-attention-store.js";
17
11
  import * as deliveryCrud from "../../../memory/delivery-crud.js";
18
- import { checkIngressForSecrets } from "../../../security/secret-ingress.js";
19
- import { IngressBlockedError } from "../../../util/errors.js";
20
12
  import { getLogger } from "../../../util/logger.js";
21
13
 
22
14
  const log = getLogger("runtime-http");
@@ -44,10 +36,7 @@ export interface SecretIngressCheckParams {
44
36
  }
45
37
 
46
38
  /**
47
- * Persist the raw payload, run the secret ingress scan, and record a
48
- * Telegram seen signal.
49
- *
50
- * Throws IngressBlockedError if the content contains secrets.
39
+ * Persist the raw payload and record a Telegram seen signal.
51
40
  */
52
41
  export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
53
42
  const {
@@ -68,9 +57,7 @@ export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
68
57
  canonicalAssistantId,
69
58
  } = params;
70
59
 
71
- // Persist the raw payload first so dead-lettered events can always be
72
- // replayed. If the ingress check later detects secrets we clear it
73
- // before throwing, so secret-bearing content is never left on disk.
60
+ // Persist the raw payload so dead-lettered events can always be replayed.
74
61
  deliveryCrud.storePayload(eventId, {
75
62
  sourceChannel,
76
63
  externalChatId: conversationExternalId,
@@ -86,22 +73,6 @@ export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
86
73
  assistantId: canonicalAssistantId,
87
74
  });
88
75
 
89
- const contentToCheck = content ?? "";
90
- let ingressCheck: ReturnType<typeof checkIngressForSecrets>;
91
- try {
92
- ingressCheck = checkIngressForSecrets(contentToCheck);
93
- } catch (checkErr) {
94
- deliveryCrud.clearPayload(eventId);
95
- throw checkErr;
96
- }
97
- if (ingressCheck.blocked) {
98
- deliveryCrud.clearPayload(eventId);
99
- throw new IngressBlockedError(
100
- ingressCheck.userNotice!,
101
- ingressCheck.detectedTypes,
102
- );
103
- }
104
-
105
76
  // Record inferred seen signal for non-duplicate Telegram inbound messages
106
77
  if (sourceChannel === "telegram") {
107
78
  try {
@@ -24,7 +24,7 @@ mock.module("../../../config/assistant-feature-flags.js", () => ({
24
24
  }));
25
25
 
26
26
  mock.module("../../../config/loader.js", () => ({
27
- getConfig: () => ({ assistantFeatureFlagValues: {} }),
27
+ getConfig: () => ({}),
28
28
  }));
29
29
 
30
30
  mock.module("../../../memory/attachments-store.js", () => ({
@@ -91,10 +91,19 @@ export async function handleGetTwilioConfig(): Promise<Response> {
91
91
  export async function handleSetTwilioCredentials(
92
92
  req: Request,
93
93
  ): Promise<Response> {
94
- const body = (await req.json().catch(() => ({}))) as {
95
- accountSid?: string;
96
- authToken?: string;
97
- };
94
+ let body: { accountSid?: string; authToken?: string };
95
+ try {
96
+ body = (await req.json()) as typeof body;
97
+ } catch {
98
+ return Response.json(
99
+ {
100
+ success: false,
101
+ hasCredentials: await hasTwilioCredentials(),
102
+ error: "Invalid JSON in request body",
103
+ },
104
+ { status: 400 },
105
+ );
106
+ }
98
107
 
99
108
  if (!body.accountSid || !body.authToken) {
100
109
  return Response.json(
@@ -261,10 +270,19 @@ export async function handleProvisionTwilioNumber(
261
270
  });
262
271
  }
263
272
 
264
- const body = (await req.json().catch(() => ({}))) as {
265
- country?: string;
266
- areaCode?: string;
267
- };
273
+ let body: { country?: string; areaCode?: string };
274
+ try {
275
+ body = (await req.json()) as typeof body;
276
+ } catch {
277
+ return Response.json(
278
+ {
279
+ success: false,
280
+ hasCredentials: await hasTwilioCredentials(),
281
+ error: "Invalid JSON in request body",
282
+ },
283
+ { status: 400 },
284
+ );
285
+ }
268
286
  const { accountSid, authToken } = await getTwilioCredentials();
269
287
  const country = body.country ?? "US";
270
288
 
@@ -318,7 +336,19 @@ export async function handleProvisionTwilioNumber(
318
336
  export async function handleAssignTwilioNumber(
319
337
  req: Request,
320
338
  ): Promise<Response> {
321
- const body = (await req.json().catch(() => ({}))) as { phoneNumber?: string };
339
+ let body: { phoneNumber?: string };
340
+ try {
341
+ body = (await req.json()) as typeof body;
342
+ } catch {
343
+ return Response.json(
344
+ {
345
+ success: false,
346
+ hasCredentials: await hasTwilioCredentials(),
347
+ error: "Invalid JSON in request body",
348
+ },
349
+ { status: 400 },
350
+ );
351
+ }
322
352
 
323
353
  if (!body.phoneNumber) {
324
354
  return Response.json(
@@ -375,7 +405,19 @@ export async function handleReleaseTwilioNumber(
375
405
  });
376
406
  }
377
407
 
378
- const body = (await req.json().catch(() => ({}))) as { phoneNumber?: string };
408
+ let body: { phoneNumber?: string };
409
+ try {
410
+ body = (await req.json()) as typeof body;
411
+ } catch {
412
+ return Response.json(
413
+ {
414
+ success: false,
415
+ hasCredentials: await hasTwilioCredentials(),
416
+ error: "Invalid JSON in request body",
417
+ },
418
+ { status: 400 },
419
+ );
420
+ }
379
421
  const raw = loadRawConfig();
380
422
  const twilio = (raw?.twilio ?? {}) as Record<string, unknown>;
381
423
  const phoneNumber = body.phoneNumber || (twilio.phoneNumber as string) || "";
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Route handlers for Vercel integration config endpoints.
3
+ *
4
+ * GET /v1/integrations/vercel/config — check if a Vercel API token is stored
5
+ * POST /v1/integrations/vercel/config — set or delete token (dispatched via action field)
6
+ * DELETE /v1/integrations/vercel/config — delete the stored Vercel API token
7
+ *
8
+ * The Swift client sends all mutations as POST with an `action` field
9
+ * ("set" or "delete") rather than using HTTP verbs directly.
10
+ */
11
+
12
+ import {
13
+ deleteVercelConfig,
14
+ getVercelConfig,
15
+ setVercelConfig,
16
+ } from "../../../daemon/handlers/config-vercel.js";
17
+ import type { RouteDefinition } from "../../http-router.js";
18
+
19
+ /**
20
+ * GET /v1/integrations/vercel/config
21
+ */
22
+ export async function handleGetVercelConfig(): Promise<Response> {
23
+ const result = await getVercelConfig();
24
+ return Response.json(result);
25
+ }
26
+
27
+ /**
28
+ * POST /v1/integrations/vercel/config
29
+ *
30
+ * Body: { action: "set" | "delete"; apiToken?: string }
31
+ *
32
+ * The Swift client uses POST for both set and delete operations,
33
+ * distinguished by the `action` field.
34
+ */
35
+ export async function handlePostVercelConfig(req: Request): Promise<Response> {
36
+ const body = (await req.json()) as {
37
+ action?: "get" | "set" | "delete";
38
+ apiToken?: string;
39
+ };
40
+
41
+ switch (body.action) {
42
+ case "delete": {
43
+ const result = await deleteVercelConfig();
44
+ return Response.json(result);
45
+ }
46
+ case "get": {
47
+ const result = await getVercelConfig();
48
+ return Response.json(result);
49
+ }
50
+ case "set":
51
+ default: {
52
+ const result = await setVercelConfig(body.apiToken);
53
+ const status = result.success ? 200 : 400;
54
+ return Response.json(result, { status });
55
+ }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * DELETE /v1/integrations/vercel/config
61
+ */
62
+ export async function handleDeleteVercelConfig(): Promise<Response> {
63
+ const result = await deleteVercelConfig();
64
+ return Response.json(result);
65
+ }
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Route definitions
69
+ // ---------------------------------------------------------------------------
70
+
71
+ export function vercelRouteDefinitions(): RouteDefinition[] {
72
+ return [
73
+ {
74
+ endpoint: "integrations/vercel/config",
75
+ method: "GET",
76
+ handler: () => handleGetVercelConfig(),
77
+ },
78
+ {
79
+ endpoint: "integrations/vercel/config",
80
+ method: "POST",
81
+ handler: async ({ req }) => handlePostVercelConfig(req),
82
+ },
83
+ {
84
+ endpoint: "integrations/vercel/config",
85
+ method: "DELETE",
86
+ handler: async () => handleDeleteVercelConfig(),
87
+ },
88
+ ];
89
+ }
@@ -37,6 +37,7 @@ import {
37
37
  getWorkspaceConfigPath,
38
38
  getWorkspaceDir,
39
39
  } from "../../util/platform.js";
40
+ import { APP_VERSION, COMMIT_SHA } from "../../version.js";
40
41
  import { httpError } from "../http-errors.js";
41
42
  import type { RouteDefinition } from "../http-router.js";
42
43
 
@@ -264,12 +265,16 @@ async function handleExport(body: ExportRequestBody): Promise<Response> {
264
265
  ? {
265
266
  type: "conversation-export" as const,
266
267
  conversationId,
268
+ assistantVersion: APP_VERSION,
269
+ commitSha: COMMIT_SHA,
267
270
  ...(startTime !== undefined ? { startTime } : {}),
268
271
  ...(endTime !== undefined ? { endTime } : {}),
269
272
  exportedAt: new Date().toISOString(),
270
273
  }
271
274
  : {
272
275
  type: "global-export" as const,
276
+ assistantVersion: APP_VERSION,
277
+ commitSha: COMMIT_SHA,
273
278
  exportedAt: new Date().toISOString(),
274
279
  };
275
280
  writeFileSync(
@@ -754,7 +754,7 @@ describe("Memory Item Routes", () => {
754
754
  expect(res.status).toBe(400);
755
755
  });
756
756
 
757
- test("truncates long subject and statement", async () => {
757
+ test("preserves long subject and statement without truncation", async () => {
758
758
  const longSubject = "a".repeat(200);
759
759
  const longStatement = "b".repeat(1000);
760
760
  const ctx = makeJsonCtx("memory-items", "POST", {
@@ -767,8 +767,8 @@ describe("Memory Item Routes", () => {
767
767
  const body = (await res.json()) as {
768
768
  item: { subject: string; statement: string };
769
769
  };
770
- expect(body.item.subject.length).toBeLessThanOrEqual(80);
771
- expect(body.item.statement.length).toBeLessThanOrEqual(500);
770
+ expect(body.item.subject).toBe(longSubject);
771
+ expect(body.item.statement).toBe(longStatement);
772
772
  });
773
773
 
774
774
  test("enqueues embed job on create", async () => {