@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
@@ -1,48 +0,0 @@
1
- import { and, desc, eq, inArray, notInArray } from "drizzle-orm";
2
-
3
- import { getDb } from "../db.js";
4
- import { memorySegments } from "../schema.js";
5
- import { computeRecencyScore } from "./ranking.js";
6
- import type { Candidate, CandidateType } from "./types.js";
7
-
8
- export function recencySearch(
9
- conversationId: string,
10
- limit: number,
11
- excludedMessageIds: string[] = [],
12
- scopeIds?: string[],
13
- ): Candidate[] {
14
- if (!conversationId || limit <= 0) return [];
15
- const db = getDb();
16
- const conditions = [eq(memorySegments.conversationId, conversationId)];
17
- if (excludedMessageIds.length > 0) {
18
- conditions.push(notInArray(memorySegments.messageId, excludedMessageIds));
19
- }
20
- if (scopeIds && scopeIds.length > 0) {
21
- conditions.push(inArray(memorySegments.scopeId, scopeIds));
22
- }
23
- const whereClause =
24
- conditions.length > 1 ? and(...conditions) : conditions[0];
25
- const rows = db
26
- .select()
27
- .from(memorySegments)
28
- .where(whereClause)
29
- .orderBy(desc(memorySegments.createdAt))
30
- .limit(limit)
31
- .all();
32
- return rows.map((row) => ({
33
- key: `segment:${row.id}`,
34
- type: "segment" as CandidateType,
35
- id: row.id,
36
- source: "recency",
37
- text: row.text,
38
- kind: "segment",
39
- conversationId: row.conversationId,
40
- messageId: row.messageId,
41
- confidence: 0.55,
42
- importance: 0.5,
43
- createdAt: row.createdAt,
44
- semantic: 0,
45
- recency: computeRecencyScore(row.createdAt),
46
- finalScore: 0,
47
- }));
48
- }
@@ -1,186 +0,0 @@
1
- import { ProviderError } from "../util/errors.js";
2
- import { getLogger } from "../util/logger.js";
3
- import type {
4
- Message,
5
- Provider,
6
- ProviderResponse,
7
- SendMessageOptions,
8
- ToolDefinition,
9
- } from "./types.js";
10
-
11
- const log = getLogger("failover");
12
-
13
- const DEFAULT_COOLDOWN_MS = 60_000;
14
-
15
- interface ProviderHealth {
16
- unhealthySince: number | null;
17
- }
18
-
19
- /**
20
- * Determine whether an error should trigger failover to the next provider.
21
- * Connection errors, auth errors, and 5xx server errors trigger failover.
22
- * 4xx client errors do NOT trigger failover (except 429 rate limit).
23
- */
24
- function isFailoverError(error: unknown): boolean {
25
- if (error instanceof ProviderError && error.statusCode !== undefined) {
26
- // 429 rate limit — try next provider
27
- if (error.statusCode === 429) return true;
28
- // 5xx server errors — try next provider
29
- if (error.statusCode >= 500) return true;
30
- // Other 4xx — don't failover (bad request, auth with wrong format, etc.)
31
- return false;
32
- }
33
-
34
- // Network errors — try next provider
35
- if (error instanceof Error) {
36
- const code = (error as NodeJS.ErrnoException).code;
37
- if (
38
- code === "ECONNRESET" ||
39
- code === "ECONNREFUSED" ||
40
- code === "ETIMEDOUT" ||
41
- code === "EPIPE"
42
- ) {
43
- return true;
44
- }
45
- if (error.cause instanceof Error) {
46
- const causeCode = (error.cause as NodeJS.ErrnoException).code;
47
- if (
48
- causeCode === "ECONNRESET" ||
49
- causeCode === "ECONNREFUSED" ||
50
- causeCode === "ETIMEDOUT" ||
51
- causeCode === "EPIPE"
52
- ) {
53
- return true;
54
- }
55
- }
56
- }
57
-
58
- // ProviderError without a status code = connection/unknown failure
59
- if (error instanceof ProviderError && error.statusCode === undefined) {
60
- return true;
61
- }
62
-
63
- return false;
64
- }
65
-
66
- export interface ProviderHealthStatus {
67
- name: string;
68
- healthy: boolean;
69
- unhealthySince: string | null;
70
- }
71
-
72
- export class FailoverProvider implements Provider {
73
- public readonly name: string;
74
- private readonly healthMap = new Map<string, ProviderHealth>();
75
-
76
- constructor(
77
- private readonly providers: Provider[],
78
- private readonly cooldownMs: number = DEFAULT_COOLDOWN_MS,
79
- ) {
80
- if (providers.length === 0) {
81
- throw new Error("FailoverProvider requires at least one provider");
82
- }
83
- this.name = providers[0].name;
84
- for (const p of providers) {
85
- this.healthMap.set(p.name, { unhealthySince: null });
86
- }
87
- }
88
-
89
- async sendMessage(
90
- messages: Message[],
91
- tools?: ToolDefinition[],
92
- systemPrompt?: string,
93
- options?: SendMessageOptions,
94
- ): Promise<ProviderResponse> {
95
- let lastError: unknown;
96
-
97
- for (const provider of this.providers) {
98
- const health = this.healthMap.get(provider.name)!;
99
- const now = Date.now();
100
-
101
- // Skip providers that are still in cooldown
102
- if (health.unhealthySince != null) {
103
- const elapsed = now - health.unhealthySince;
104
- if (elapsed < this.cooldownMs) {
105
- log.debug(
106
- {
107
- provider: provider.name,
108
- cooldownRemainingMs: this.cooldownMs - elapsed,
109
- },
110
- "Skipping unhealthy provider (in cooldown)",
111
- );
112
- continue;
113
- }
114
- // Cooldown expired — give it another chance
115
- log.info(
116
- { provider: provider.name },
117
- "Provider cooldown expired, retrying",
118
- );
119
- }
120
-
121
- try {
122
- const response = await provider.sendMessage(
123
- messages,
124
- tools,
125
- systemPrompt,
126
- options,
127
- );
128
- // Success — mark healthy
129
- if (health.unhealthySince != null) {
130
- log.info(
131
- { provider: provider.name },
132
- "Provider recovered, marking healthy",
133
- );
134
- health.unhealthySince = null;
135
- }
136
- return {
137
- ...response,
138
- actualProvider: response.actualProvider ?? provider.name,
139
- };
140
- } catch (error) {
141
- lastError = error;
142
-
143
- if (isFailoverError(error)) {
144
- health.unhealthySince = Date.now();
145
- log.warn(
146
- {
147
- provider: provider.name,
148
- error: error instanceof Error ? error.message : String(error),
149
- statusCode:
150
- error instanceof ProviderError ? error.statusCode : undefined,
151
- },
152
- "Provider failed, marked unhealthy",
153
- );
154
- continue;
155
- }
156
-
157
- // Non-failover error (e.g. 400 bad request) — don't try other providers
158
- throw error;
159
- }
160
- }
161
-
162
- // All providers exhausted
163
- throw (
164
- lastError ??
165
- new ProviderError(
166
- "All configured providers are unavailable",
167
- this.name,
168
- undefined,
169
- )
170
- );
171
- }
172
-
173
- getHealthStatus(): ProviderHealthStatus[] {
174
- return this.providers.map((p) => {
175
- const health = this.healthMap.get(p.name)!;
176
- return {
177
- name: p.name,
178
- healthy: health.unhealthySince == null,
179
- unhealthySince:
180
- health.unhealthySince != null
181
- ? new Date(health.unhealthySince).toISOString()
182
- : null,
183
- };
184
- });
185
- }
186
- }
@@ -1,275 +0,0 @@
1
- import { getGatewayInternalBaseUrl } from "../config/env.js";
2
- import { getBaseDataDir, getIsContainerized } from "../config/env-registry.js";
3
- import { readLockfile } from "../util/platform.js";
4
- import { sleep } from "../util/retry.js";
5
-
6
- const DEFAULT_PROBE_TIMEOUT_MS = 2_000;
7
- const DEFAULT_RECOVERY_POLL_TIMEOUT_MS = 30_000;
8
- const DEFAULT_RECOVERY_POLL_INTERVAL_MS = 250;
9
- const DEFAULT_WAKE_TIMEOUT_MS = 90_000;
10
-
11
- interface LockfileAssistantEntry {
12
- assistantId?: string;
13
- cloud?: string;
14
- hatchedAt?: string | number | Date;
15
- }
16
-
17
- export interface WakeCommandResult {
18
- exitCode: number;
19
- stdout: string;
20
- stderr: string;
21
- }
22
-
23
- export interface LocalGatewayHealthResult {
24
- target: string;
25
- healthy: boolean;
26
- localDeployment: boolean;
27
- error?: string;
28
- }
29
-
30
- export interface EnsureLocalGatewayReadyResult extends LocalGatewayHealthResult {
31
- recovered: boolean;
32
- recoveryAttempted: boolean;
33
- recoverySkipped: boolean;
34
- }
35
-
36
- export interface ProbeLocalGatewayHealthOptions {
37
- timeoutMs?: number;
38
- fetchImpl?: typeof fetch;
39
- }
40
-
41
- export interface EnsureLocalGatewayReadyOptions extends ProbeLocalGatewayHealthOptions {
42
- pollTimeoutMs?: number;
43
- pollIntervalMs?: number;
44
- wakeTimeoutMs?: number;
45
- runWakeCommand?: () => Promise<WakeCommandResult>;
46
- sleepImpl?: (ms: number) => Promise<void>;
47
- }
48
-
49
- function getLatestAssistantEntry(): LockfileAssistantEntry | null {
50
- try {
51
- const lockData = readLockfile();
52
- const assistants = lockData?.assistants;
53
- if (!Array.isArray(assistants) || assistants.length === 0) {
54
- return null;
55
- }
56
-
57
- const sorted = [...assistants].sort((a, b) => {
58
- const dateA = new Date(
59
- (a as LockfileAssistantEntry).hatchedAt || 0,
60
- ).getTime();
61
- const dateB = new Date(
62
- (b as LockfileAssistantEntry).hatchedAt || 0,
63
- ).getTime();
64
- return dateB - dateA;
65
- });
66
-
67
- return (sorted[0] as LockfileAssistantEntry) ?? null;
68
- } catch {
69
- return null;
70
- }
71
- }
72
-
73
- function resolveLocalDeployment(): boolean {
74
- if (getIsContainerized()) {
75
- return false;
76
- }
77
-
78
- const latestAssistant = getLatestAssistantEntry();
79
- if (typeof latestAssistant?.cloud === "string") {
80
- return latestAssistant.cloud === "local";
81
- }
82
-
83
- return true;
84
- }
85
-
86
- /**
87
- * Derive instance name from BASE_DATA_DIR which follows the multi-instance
88
- * path pattern (~/.local/share/vellum/assistants/<name>/).
89
- */
90
- function resolveInstanceNameFromBaseDataDir(): string | undefined {
91
- const base = getBaseDataDir();
92
- if (!base || typeof base !== "string") return undefined;
93
-
94
- const normalized = base.replace(/\\/g, "/").replace(/\/+$/, "");
95
- const match = normalized.match(/\/assistants\/([^/]+)$/);
96
- if (match) return match[1];
97
- return undefined;
98
- }
99
-
100
- function resolveLocalAssistantName(): string | undefined {
101
- const fromPath = resolveInstanceNameFromBaseDataDir();
102
- if (fromPath) return fromPath;
103
-
104
- const latestAssistant = getLatestAssistantEntry();
105
- if (
106
- latestAssistant &&
107
- typeof latestAssistant.assistantId === "string" &&
108
- latestAssistant.assistantId.trim().length > 0
109
- ) {
110
- return latestAssistant.assistantId.trim();
111
- }
112
-
113
- return undefined;
114
- }
115
-
116
- function formatError(err: unknown): string {
117
- return err instanceof Error ? err.message : String(err);
118
- }
119
-
120
- async function runDefaultWakeCommand(
121
- timeoutMs: number,
122
- ): Promise<WakeCommandResult> {
123
- const assistantName = resolveLocalAssistantName();
124
- const command = assistantName
125
- ? ["vellum", "wake", assistantName]
126
- : ["vellum", "wake"];
127
-
128
- // Only when the assistant name came from the instance path (e.g.
129
- // ~/.local/share/vellum/assistants/<name>/), unset BASE_DATA_DIR so the
130
- // spawned CLI reads the global lockfile. When the name came from the
131
- // lockfile, keep BASE_DATA_DIR — vellum wake resolves names through the
132
- // lockfile rooted at BASE_DATA_DIR, so clearing it would read the wrong
133
- // lockfile (e.g. $HOME) and fail or wake the wrong assistant.
134
- const fromInstancePath = resolveInstanceNameFromBaseDataDir();
135
- const env =
136
- fromInstancePath && getBaseDataDir()
137
- ? { ...process.env, BASE_DATA_DIR: undefined }
138
- : process.env;
139
-
140
- return new Promise((resolve, reject) => {
141
- const proc = Bun.spawn(command, {
142
- stdout: "pipe",
143
- stderr: "pipe",
144
- env: {
145
- ...env,
146
- PATH: [env.PATH, "/opt/homebrew/bin", "/usr/local/bin"]
147
- .filter(Boolean)
148
- .join(":"),
149
- },
150
- });
151
- const timer = setTimeout(() => {
152
- proc.kill();
153
- reject(
154
- new Error(`Process timed out after ${timeoutMs}ms: ${command[0]}`),
155
- );
156
- }, timeoutMs);
157
- proc.exited.then(async (exitCode) => {
158
- clearTimeout(timer);
159
- const stdout = await new Response(proc.stdout).text();
160
- const stderr = await new Response(proc.stderr).text();
161
- resolve({ exitCode, stdout, stderr });
162
- });
163
- });
164
- }
165
-
166
- export async function probeLocalGatewayHealth(
167
- options: ProbeLocalGatewayHealthOptions = {},
168
- ): Promise<LocalGatewayHealthResult> {
169
- const target = getGatewayInternalBaseUrl();
170
- const localDeployment = resolveLocalDeployment();
171
- const fetchImpl = options.fetchImpl ?? fetch;
172
- const timeoutMs = options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
173
-
174
- try {
175
- const response = await fetchImpl(`${target}/healthz`, {
176
- method: "GET",
177
- signal: AbortSignal.timeout(timeoutMs),
178
- });
179
- if (!response.ok) {
180
- return {
181
- target,
182
- healthy: false,
183
- localDeployment,
184
- error: `Gateway health check returned HTTP ${response.status}`,
185
- };
186
- }
187
-
188
- return {
189
- target,
190
- healthy: true,
191
- localDeployment,
192
- };
193
- } catch (err) {
194
- return {
195
- target,
196
- healthy: false,
197
- localDeployment,
198
- error: formatError(err),
199
- };
200
- }
201
- }
202
-
203
- export async function ensureLocalGatewayReady(
204
- options: EnsureLocalGatewayReadyOptions = {},
205
- ): Promise<EnsureLocalGatewayReadyResult> {
206
- const initialProbe = await probeLocalGatewayHealth(options);
207
- if (initialProbe.healthy) {
208
- return {
209
- ...initialProbe,
210
- recovered: false,
211
- recoveryAttempted: false,
212
- recoverySkipped: false,
213
- };
214
- }
215
-
216
- if (!initialProbe.localDeployment) {
217
- return {
218
- ...initialProbe,
219
- recovered: false,
220
- recoveryAttempted: false,
221
- recoverySkipped: true,
222
- error:
223
- initialProbe.error ??
224
- "Skipped gateway recovery because this assistant is not locally managed",
225
- };
226
- }
227
-
228
- const runWakeCommand =
229
- options.runWakeCommand ??
230
- (() =>
231
- runDefaultWakeCommand(options.wakeTimeoutMs ?? DEFAULT_WAKE_TIMEOUT_MS));
232
- const sleepImpl = options.sleepImpl ?? sleep;
233
- const pollTimeoutMs =
234
- options.pollTimeoutMs ?? DEFAULT_RECOVERY_POLL_TIMEOUT_MS;
235
- const pollIntervalMs =
236
- options.pollIntervalMs ?? DEFAULT_RECOVERY_POLL_INTERVAL_MS;
237
-
238
- let wakeError: string | undefined;
239
- try {
240
- const wakeResult = await runWakeCommand();
241
- if (wakeResult.exitCode !== 0) {
242
- const detail = wakeResult.stderr.trim() || wakeResult.stdout.trim();
243
- wakeError = detail
244
- ? `vellum wake exited with code ${wakeResult.exitCode}: ${detail}`
245
- : `vellum wake exited with code ${wakeResult.exitCode}`;
246
- }
247
- } catch (err) {
248
- wakeError = `Failed to run vellum wake: ${formatError(err)}`;
249
- }
250
-
251
- const deadline = Date.now() + pollTimeoutMs;
252
- let probe = await probeLocalGatewayHealth(options);
253
- while (!probe.healthy && Date.now() < deadline) {
254
- await sleepImpl(pollIntervalMs);
255
- probe = await probeLocalGatewayHealth(options);
256
- }
257
-
258
- if (probe.healthy) {
259
- return {
260
- ...probe,
261
- recovered: true,
262
- recoveryAttempted: true,
263
- recoverySkipped: false,
264
- };
265
- }
266
-
267
- const combinedError = [wakeError, probe.error].filter(Boolean).join("; ");
268
- return {
269
- ...probe,
270
- recovered: false,
271
- recoveryAttempted: true,
272
- recoverySkipped: false,
273
- error: combinedError || undefined,
274
- };
275
- }
@@ -1,68 +0,0 @@
1
- import { getConfig } from "../config/loader.js";
2
- import { getLogger } from "../util/logger.js";
3
- import { compileCustomPatterns, scanText } from "./secret-scanner.js";
4
-
5
- const log = getLogger("secret-ingress");
6
-
7
- export interface IngressCheckResult {
8
- /** Whether the message should be blocked from entering the model context. */
9
- blocked: boolean;
10
- /** Secret types detected (empty if none). */
11
- detectedTypes: string[];
12
- /**
13
- * User-facing notice explaining why the message was blocked.
14
- * Does NOT echo the secret value — only describes what was found.
15
- */
16
- userNotice?: string;
17
- }
18
-
19
- /**
20
- * Scan inbound user text for secrets before it enters model context.
21
- *
22
- * When `secretDetection.blockIngress` is `true` (default), any message
23
- * containing a detected secret is rejected with a safe notice. This is
24
- * independent of `secretDetection.action`, which only controls how
25
- * secrets in tool *output* are handled.
26
- *
27
- * SECURITY: This function intentionally never logs the message content.
28
- */
29
- export function checkIngressForSecrets(content: string): IngressCheckResult {
30
- const config = getConfig();
31
- if (!config.secretDetection.enabled) {
32
- return { blocked: false, detectedTypes: [] };
33
- }
34
-
35
- if (!config.secretDetection.blockIngress) {
36
- return { blocked: false, detectedTypes: [] };
37
- }
38
-
39
- const entropyConfig = {
40
- enabled: true,
41
- base64Threshold: config.secretDetection.entropyThreshold,
42
- };
43
- const compiledCustom = config.secretDetection.customPatterns?.length
44
- ? compileCustomPatterns(config.secretDetection.customPatterns)
45
- : undefined;
46
- const matches = scanText(content, entropyConfig, compiledCustom);
47
-
48
- if (matches.length === 0) {
49
- return { blocked: false, detectedTypes: [] };
50
- }
51
-
52
- const detectedTypes = [...new Set(matches.map((m) => m.type))];
53
- log.warn(
54
- { detectedTypes, matchCount: matches.length },
55
- "Blocked inbound message containing secrets",
56
- );
57
-
58
- return {
59
- blocked: true,
60
- detectedTypes,
61
- userNotice:
62
- `Your message appears to contain sensitive information (${detectedTypes.join(
63
- ", ",
64
- )}). ` +
65
- `For security, it was not sent to the AI. ` +
66
- `Please use the secure credential prompt instead — the assistant will ask for secrets when it needs them.`,
67
- };
68
- }