@vellumai/assistant 0.5.5 → 0.5.7

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 (382) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +4 -5
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docs/architecture/keychain-broker.md +45 -240
  7. package/docs/architecture/security.md +0 -17
  8. package/docs/credential-execution-service.md +2 -2
  9. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  10. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
  11. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  12. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  13. package/package.json +2 -3
  14. package/src/__tests__/actor-token-service.test.ts +1 -2
  15. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  16. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  17. package/src/__tests__/btw-routes.test.ts +0 -39
  18. package/src/__tests__/call-domain.test.ts +0 -128
  19. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  20. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  21. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  22. package/src/__tests__/checker.test.ts +4 -2
  23. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  24. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  25. package/src/__tests__/config-schema.test.ts +3 -3
  26. package/src/__tests__/context-window-manager.test.ts +78 -0
  27. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  28. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  29. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  30. package/src/__tests__/conversation-title-service.test.ts +117 -1
  31. package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
  32. package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
  33. package/src/__tests__/credential-security-e2e.test.ts +0 -66
  34. package/src/__tests__/credential-security-invariants.test.ts +4 -45
  35. package/src/__tests__/credentials-cli.test.ts +78 -0
  36. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  37. package/src/__tests__/docker-signing-key-bootstrap.test.ts +98 -0
  38. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  39. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  40. package/src/__tests__/host-shell-tool.test.ts +6 -7
  41. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  42. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  43. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  44. package/src/__tests__/intent-routing.test.ts +0 -13
  45. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  46. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  47. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  48. package/src/__tests__/memory-regressions.test.ts +8 -30
  49. package/src/__tests__/migration-export-http.test.ts +2 -2
  50. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  51. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  52. package/src/__tests__/migration-validate-http.test.ts +2 -2
  53. package/src/__tests__/non-member-access-request.test.ts +0 -5
  54. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  55. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  56. package/src/__tests__/permission-types.test.ts +1 -0
  57. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  58. package/src/__tests__/qdrant-manager.test.ts +28 -2
  59. package/src/__tests__/registry.test.ts +0 -6
  60. package/src/__tests__/require-fresh-approval.test.ts +4 -0
  61. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  62. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  63. package/src/__tests__/secure-keys.test.ts +83 -263
  64. package/src/__tests__/shell-identity.test.ts +96 -6
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  66. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  67. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  68. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  69. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  70. package/src/__tests__/skill-load-tool.test.ts +0 -2
  71. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  72. package/src/__tests__/skills.test.ts +0 -2
  73. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  74. package/src/__tests__/suggestion-routes.test.ts +1 -32
  75. package/src/__tests__/system-prompt.test.ts +0 -1
  76. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
  77. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  78. package/src/__tests__/tool-executor.test.ts +4 -0
  79. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  80. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  81. package/src/__tests__/update-bulletin.test.ts +0 -2
  82. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  83. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
  84. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  85. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
  86. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  87. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  88. package/src/calls/audio-store.test.ts +97 -0
  89. package/src/calls/audio-store.ts +205 -0
  90. package/src/calls/call-controller.ts +85 -7
  91. package/src/calls/call-domain.ts +3 -0
  92. package/src/calls/call-store.ts +10 -3
  93. package/src/calls/fish-audio-client.ts +117 -0
  94. package/src/calls/relay-server.ts +27 -0
  95. package/src/calls/twilio-routes.ts +2 -1
  96. package/src/calls/types.ts +1 -0
  97. package/src/calls/voice-ingress-preflight.ts +0 -42
  98. package/src/calls/voice-quality.ts +26 -5
  99. package/src/calls/voice-session-bridge.ts +6 -12
  100. package/src/cli/commands/config.ts +1 -4
  101. package/src/cli/commands/conversations.ts +0 -18
  102. package/src/cli/commands/credentials.ts +34 -4
  103. package/src/cli/commands/oauth/index.ts +7 -0
  104. package/src/cli/commands/oauth/platform.ts +179 -0
  105. package/src/cli/commands/platform.ts +3 -3
  106. package/src/config/assistant-feature-flags.ts +186 -5
  107. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  108. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  109. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  110. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  111. package/src/config/bundled-tool-registry.ts +1 -11
  112. package/src/config/env-registry.ts +1 -1
  113. package/src/config/env.ts +16 -16
  114. package/src/config/feature-flag-registry.json +48 -16
  115. package/src/config/loader.ts +98 -31
  116. package/src/config/schema.ts +4 -25
  117. package/src/config/schemas/calls.ts +13 -0
  118. package/src/config/schemas/fish-audio.ts +39 -0
  119. package/src/config/schemas/memory.ts +0 -4
  120. package/src/config/schemas/platform.ts +1 -1
  121. package/src/config/schemas/security.ts +4 -4
  122. package/src/config/types.ts +0 -1
  123. package/src/contacts/contact-store.ts +39 -0
  124. package/src/contacts/types.ts +2 -0
  125. package/src/context/window-manager.ts +53 -2
  126. package/src/credential-execution/approval-bridge.ts +1 -0
  127. package/src/credential-execution/executable-discovery.ts +28 -4
  128. package/src/credential-execution/feature-gates.ts +16 -0
  129. package/src/credential-execution/process-manager.ts +38 -0
  130. package/src/daemon/assistant-attachments.ts +9 -0
  131. package/src/daemon/config-watcher.ts +6 -4
  132. package/src/daemon/conversation-agent-loop.ts +0 -60
  133. package/src/daemon/conversation-memory.ts +0 -117
  134. package/src/daemon/conversation-runtime-assembly.ts +0 -2
  135. package/src/daemon/conversation-tool-setup.ts +0 -105
  136. package/src/daemon/conversation.ts +10 -1
  137. package/src/daemon/handlers/config-vercel.ts +92 -0
  138. package/src/daemon/handlers/conversations.ts +0 -11
  139. package/src/daemon/handlers/skills.ts +2 -15
  140. package/src/daemon/install-symlink.ts +195 -0
  141. package/src/daemon/lifecycle.ts +229 -96
  142. package/src/daemon/message-types/conversations.ts +3 -4
  143. package/src/daemon/message-types/diagnostics.ts +3 -22
  144. package/src/daemon/message-types/messages.ts +0 -2
  145. package/src/daemon/message-types/upgrades.ts +8 -0
  146. package/src/daemon/server.ts +30 -92
  147. package/src/events/domain-events.ts +2 -1
  148. package/src/followups/followup-store.ts +5 -2
  149. package/src/inbound/platform-callback-registration.ts +3 -3
  150. package/src/instrument.ts +8 -5
  151. package/src/memory/conversation-crud.ts +0 -236
  152. package/src/memory/conversation-title-service.ts +76 -11
  153. package/src/memory/db-init.ts +15 -11
  154. package/src/memory/indexer.ts +15 -106
  155. package/src/memory/items-extractor.ts +15 -1
  156. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  157. package/src/memory/job-handlers/embedding.ts +0 -79
  158. package/src/memory/job-utils.ts +1 -1
  159. package/src/memory/jobs-store.ts +30 -13
  160. package/src/memory/jobs-worker.ts +31 -27
  161. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  162. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  163. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  164. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  165. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  166. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  167. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  168. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  169. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  170. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  171. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  172. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  173. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  174. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  175. package/src/memory/migrations/116-messages-fts.ts +106 -1
  176. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  177. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  178. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  179. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  180. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  181. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  182. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  183. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  184. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  185. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  186. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  187. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  188. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  189. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  190. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  191. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  192. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  193. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  194. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  195. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  196. package/src/memory/migrations/189-drop-simplified-memory.ts +42 -0
  197. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  198. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  199. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  200. package/src/memory/migrations/index.ts +5 -3
  201. package/src/memory/migrations/registry.ts +90 -0
  202. package/src/memory/migrations/validate-migration-state.ts +137 -11
  203. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  204. package/src/memory/qdrant-client.ts +4 -6
  205. package/src/memory/qdrant-manager.ts +64 -7
  206. package/src/memory/schema/calls.ts +1 -0
  207. package/src/memory/schema/contacts.ts +1 -0
  208. package/src/memory/schema/conversations.ts +0 -3
  209. package/src/memory/schema/index.ts +0 -2
  210. package/src/messaging/draft-store.ts +2 -2
  211. package/src/notifications/decision-engine.ts +4 -1
  212. package/src/oauth/connection-resolver.ts +6 -4
  213. package/src/permissions/checker.ts +0 -38
  214. package/src/permissions/defaults.ts +3 -3
  215. package/src/permissions/shell-identity.ts +76 -22
  216. package/src/permissions/trust-client.ts +2 -13
  217. package/src/permissions/trust-store.ts +8 -3
  218. package/src/permissions/types.ts +4 -2
  219. package/src/platform/client.ts +35 -7
  220. package/src/prompts/persona-resolver.ts +138 -0
  221. package/src/prompts/system-prompt.ts +36 -4
  222. package/src/prompts/templates/users/default.md +1 -0
  223. package/src/providers/registry.ts +27 -40
  224. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  225. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  226. package/src/runtime/auth/external-assistant-id.ts +13 -59
  227. package/src/runtime/auth/route-policy.ts +29 -1
  228. package/src/runtime/auth/token-service.ts +53 -15
  229. package/src/runtime/channel-readiness-service.ts +1 -16
  230. package/src/runtime/http-server.ts +29 -2
  231. package/src/runtime/middleware/error-handler.ts +1 -9
  232. package/src/runtime/routes/audio-routes.ts +40 -0
  233. package/src/runtime/routes/btw-routes.ts +0 -17
  234. package/src/runtime/routes/conversation-management-routes.ts +0 -36
  235. package/src/runtime/routes/conversation-query-routes.ts +106 -2
  236. package/src/runtime/routes/conversation-routes.ts +4 -43
  237. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  238. package/src/runtime/routes/identity-routes.ts +18 -29
  239. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  240. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  241. package/src/runtime/routes/integrations/vercel.ts +89 -0
  242. package/src/runtime/routes/log-export-routes.ts +5 -0
  243. package/src/runtime/routes/memory-item-routes.test.ts +221 -3
  244. package/src/runtime/routes/memory-item-routes.ts +144 -4
  245. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  246. package/src/runtime/routes/migration-routes.ts +17 -1
  247. package/src/runtime/routes/notification-routes.ts +58 -0
  248. package/src/runtime/routes/schedule-routes.ts +65 -0
  249. package/src/runtime/routes/settings-routes.ts +41 -1
  250. package/src/runtime/routes/tts-routes.ts +86 -0
  251. package/src/runtime/routes/upgrade-broadcast-routes.ts +175 -0
  252. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  253. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  254. package/src/runtime/routes/workspace-routes.ts +1 -1
  255. package/src/runtime/routes/workspace-utils.ts +86 -2
  256. package/src/schedule/schedule-store.ts +0 -21
  257. package/src/security/ces-credential-client.ts +59 -22
  258. package/src/security/ces-rpc-credential-backend.ts +85 -0
  259. package/src/security/credential-backend.ts +12 -88
  260. package/src/security/keychain-broker-client.ts +10 -2
  261. package/src/security/secure-keys.ts +94 -113
  262. package/src/skills/catalog-install.ts +13 -7
  263. package/src/skills/inline-command-render.ts +5 -1
  264. package/src/skills/inline-command-runner.ts +30 -2
  265. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  266. package/src/tools/calls/call-start.ts +1 -0
  267. package/src/tools/executor.ts +0 -4
  268. package/src/tools/memory/handlers.ts +1 -129
  269. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  270. package/src/tools/network/web-fetch.ts +3 -1
  271. package/src/tools/permission-checker.ts +18 -0
  272. package/src/tools/skills/execute.ts +1 -1
  273. package/src/tools/skills/load.ts +9 -2
  274. package/src/tools/types.ts +0 -8
  275. package/src/util/errors.ts +0 -12
  276. package/src/util/platform.ts +8 -55
  277. package/src/util/xml.ts +8 -0
  278. package/src/workspace/git-service.ts +5 -2
  279. package/src/workspace/heartbeat-service.ts +5 -24
  280. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  281. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  282. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  283. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  284. package/src/workspace/migrations/006-services-config.ts +49 -0
  285. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  286. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  287. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  288. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  289. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  290. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  291. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  292. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  293. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  294. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  295. package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
  296. package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
  297. package/src/workspace/migrations/registry.ts +8 -0
  298. package/src/workspace/migrations/runner.ts +106 -2
  299. package/src/workspace/migrations/types.ts +4 -0
  300. package/src/__tests__/archive-recall.test.ts +0 -560
  301. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  302. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  303. package/src/__tests__/conversation-memory-dirty-tail.test.ts +0 -150
  304. package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
  305. package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
  306. package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
  307. package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
  308. package/src/__tests__/diagnostics-export.test.ts +0 -288
  309. package/src/__tests__/local-gateway-health.test.ts +0 -209
  310. package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
  311. package/src/__tests__/memory-brief-time.test.ts +0 -285
  312. package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
  313. package/src/__tests__/memory-chunk-archive.test.ts +0 -400
  314. package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
  315. package/src/__tests__/memory-episode-archive.test.ts +0 -370
  316. package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
  317. package/src/__tests__/memory-observation-archive.test.ts +0 -375
  318. package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
  319. package/src/__tests__/memory-reducer-job.test.ts +0 -538
  320. package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
  321. package/src/__tests__/memory-reducer-store.test.ts +0 -728
  322. package/src/__tests__/memory-reducer-types.test.ts +0 -707
  323. package/src/__tests__/memory-reducer.test.ts +0 -704
  324. package/src/__tests__/memory-simplified-config.test.ts +0 -281
  325. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  326. package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
  327. package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
  328. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  329. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  330. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  331. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  332. package/src/__tests__/swarm-recursion.test.ts +0 -197
  333. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  334. package/src/__tests__/swarm-tool.test.ts +0 -185
  335. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  336. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  337. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  338. package/src/commands/cc-command-registry.ts +0 -248
  339. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  340. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  341. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  342. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  343. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  344. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  345. package/src/config/schemas/memory-simplified.ts +0 -101
  346. package/src/config/schemas/swarm.ts +0 -82
  347. package/src/logfire.ts +0 -135
  348. package/src/memory/archive-recall.ts +0 -516
  349. package/src/memory/archive-store.ts +0 -400
  350. package/src/memory/brief-formatting.ts +0 -33
  351. package/src/memory/brief-open-loops.ts +0 -266
  352. package/src/memory/brief-time.ts +0 -162
  353. package/src/memory/brief.ts +0 -75
  354. package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
  355. package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
  356. package/src/memory/migrations/185-memory-brief-state.ts +0 -52
  357. package/src/memory/migrations/186-memory-archive.ts +0 -109
  358. package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
  359. package/src/memory/reducer-scheduler.ts +0 -242
  360. package/src/memory/reducer-store.ts +0 -271
  361. package/src/memory/reducer-types.ts +0 -106
  362. package/src/memory/reducer.ts +0 -467
  363. package/src/memory/schema/memory-archive.ts +0 -121
  364. package/src/memory/schema/memory-brief.ts +0 -55
  365. package/src/runtime/local-gateway-health.ts +0 -275
  366. package/src/security/secret-ingress.ts +0 -68
  367. package/src/swarm/backend-claude-code.ts +0 -225
  368. package/src/swarm/checkpoint.ts +0 -137
  369. package/src/swarm/graph-utils.ts +0 -53
  370. package/src/swarm/index.ts +0 -55
  371. package/src/swarm/limits.ts +0 -66
  372. package/src/swarm/orchestrator.ts +0 -424
  373. package/src/swarm/plan-validator.ts +0 -117
  374. package/src/swarm/router-planner.ts +0 -162
  375. package/src/swarm/router-prompts.ts +0 -39
  376. package/src/swarm/synthesizer.ts +0 -81
  377. package/src/swarm/types.ts +0 -72
  378. package/src/swarm/worker-backend.ts +0 -131
  379. package/src/swarm/worker-prompts.ts +0 -80
  380. package/src/swarm/worker-runner.ts +0 -170
  381. package/src/tools/claude-code/claude-code.ts +0 -610
  382. package/src/tools/swarm/delegate.ts +0 -205
@@ -105,8 +105,10 @@ export async function analyzeShellCommand(
105
105
  * - action:gh pr
106
106
  * - action:gh
107
107
  *
108
- * Only "simple action" commands (optional setup prefix + one action) get
109
- * action keys. Pipelines and complex chains are marked non-simple.
108
+ * Simple actions (optional setup prefix + one action) and pipelines get
109
+ * action keys. Both are marked non-simple when they involve pipes, but
110
+ * pipelines extract keys from the first segment before the first pipe.
111
+ * Complex chains (semicolons, ||, &, newlines) get no action keys.
110
112
  */
111
113
  export function deriveShellActionKeys(
112
114
  analysis: ShellIdentityAnalysis,
@@ -117,18 +119,22 @@ export function deriveShellActionKeys(
117
119
  return { keys: [], isSimpleAction: false };
118
120
  }
119
121
 
120
- // For multi-segment commands, only allow simple-action classification if
121
- // ALL inter-segment operators are explicitly &&. Any other operator (|, ||,
122
- // ;, &, empty/missing) means the separator is unknown or unsafe.
123
- // This safely handles cases where the parser doesn't capture certain
124
- // separators (;, newline, &) and leaves them as empty operators.
122
+ // For multi-segment commands, check operators to determine the command shape.
123
+ // Pipes (|) get special handling we can extract action keys from the first
124
+ // segment before the pipe. Other complex operators (||, ;, &, empty/missing)
125
+ // are truly opaque and get no action keys.
125
126
  if (segments.length > 1) {
127
+ let hasPipe = false;
128
+
126
129
  for (const seg of segments) {
127
130
  const op = seg.operator;
128
- // Non-empty operator that isn't && → definitely complex
129
- if (op && op !== "&&") {
131
+ // Non-empty operator that isn't && or | → definitely complex, no keys
132
+ if (op && op !== "&&" && op !== "|") {
130
133
  return { keys: [], isSimpleAction: false };
131
134
  }
135
+ if (op === "|") {
136
+ hasPipe = true;
137
+ }
132
138
  }
133
139
  // Also check: if there are multiple segments but no operators at all
134
140
  // between them (e.g. newline-separated), that's suspicious.
@@ -140,6 +146,42 @@ export function deriveShellActionKeys(
140
146
  return { keys: [], isSimpleAction: false };
141
147
  }
142
148
  }
149
+
150
+ // For pipelines, extract action keys from the first non-setup-prefix segment
151
+ // before the first pipe. This enables broader "Any pdftotext command" rules
152
+ // that match pipelines like "pdftotext file | head -100".
153
+ if (hasPipe) {
154
+ const firstPipeIndex = segments.findIndex((s) => s.operator === "|");
155
+ if (firstPipeIndex > 0) {
156
+ const preSegments = segments.slice(0, firstPipeIndex);
157
+ const actionSegs = preSegments.filter(
158
+ (s) => !SETUP_PREFIX_PROGRAMS.has(s.program),
159
+ );
160
+ if (actionSegs.length === 1) {
161
+ const seg = actionSegs[0];
162
+ const tokens: string[] = [seg.program];
163
+ for (const arg of seg.args) {
164
+ if (tokens.length >= MAX_ACTION_KEY_DEPTH) break;
165
+ if (arg.startsWith("-")) continue;
166
+ if (arg.includes("/") || arg.startsWith(".")) continue;
167
+ if (/^\d+$/.test(arg)) continue;
168
+ if (arg.includes("$") || arg.includes('"') || arg.includes("'"))
169
+ continue;
170
+ tokens.push(arg);
171
+ }
172
+ const keys: ShellActionKey[] = [];
173
+ for (let depth = tokens.length; depth >= 1; depth--) {
174
+ keys.push({
175
+ key: `action:${tokens.slice(0, depth).join(" ")}`,
176
+ depth,
177
+ });
178
+ }
179
+ return { keys, isSimpleAction: false, primarySegment: seg };
180
+ }
181
+ }
182
+ // Pipeline but couldn't extract a single primary action — no keys
183
+ return { keys: [], isSimpleAction: false };
184
+ }
143
185
  }
144
186
 
145
187
  // Separate setup-prefix segments from action segments
@@ -190,9 +232,10 @@ export function deriveShellActionKeys(
190
232
  * Candidate ordering:
191
233
  * 1. Raw command (most specific match — the full command as written)
192
234
  * 2. Canonical primary command (if simple action) — the full primary segment text
193
- * 3. Action keys from narrowest to broadest (if simple action)
235
+ * 3. Action keys from narrowest to broadest (if simple action or pipeline)
194
236
  *
195
- * Complex commands (pipelines, multi-action chains) only return the raw candidate.
237
+ * Complex non-pipeline commands (multi-action chains, semicolons, etc.) only
238
+ * return the raw candidate.
196
239
  */
197
240
  export async function buildShellCommandCandidates(
198
241
  command: string,
@@ -206,14 +249,15 @@ export async function buildShellCommandCandidates(
206
249
 
207
250
  const candidates: string[] = [trimmed];
208
251
 
209
- if (actionResult.isSimpleAction && actionResult.primarySegment) {
210
- // Add canonical primary command text (the actual segment, not the full command with setup prefixes)
211
- const canonical = actionResult.primarySegment.command;
212
- if (canonical !== trimmed) {
213
- candidates.push(canonical);
252
+ // Add action keys as candidates if available (simple actions AND pipelines)
253
+ if (actionResult.keys.length > 0) {
254
+ // For simple actions, also add the canonical primary command text
255
+ if (actionResult.isSimpleAction && actionResult.primarySegment) {
256
+ const canonical = actionResult.primarySegment.command;
257
+ if (canonical !== trimmed) {
258
+ candidates.push(canonical);
259
+ }
214
260
  }
215
-
216
- // Add action keys
217
261
  for (const actionKey of actionResult.keys) {
218
262
  candidates.push(actionKey.key);
219
263
  }
@@ -231,8 +275,9 @@ export async function buildShellCommandCandidates(
231
275
  * 2. Deepest action key (e.g. "action:gh pr view")
232
276
  * 3. Broader action keys (e.g. "action:gh pr", "action:gh")
233
277
  *
234
- * For complex commands (pipelines, multi-action chains), only the exact
235
- * command is offered (no broad options).
278
+ * For pipelines, the exact command plus action-key-based broader options
279
+ * are offered. For other complex commands (multi-action chains, semicolons,
280
+ * etc.), only the exact command is offered.
236
281
  */
237
282
  export async function buildShellAllowlistOptions(
238
283
  command: string,
@@ -244,14 +289,23 @@ export async function buildShellAllowlistOptions(
244
289
  const actionResult = deriveShellActionKeys(analysis);
245
290
 
246
291
  if (!actionResult.isSimpleAction || !actionResult.primarySegment) {
247
- // Complex command exact only
248
- return [
292
+ const options: AllowlistOption[] = [
249
293
  {
250
294
  label: trimmed,
251
295
  description: "This exact compound command",
252
296
  pattern: trimmed,
253
297
  },
254
298
  ];
299
+ // If pipeline action keys were extracted, offer them as broader options
300
+ for (const actionKey of actionResult.keys) {
301
+ const keyTokens = actionKey.key.replace(/^action:/, "");
302
+ options.push({
303
+ label: `${keyTokens} *`,
304
+ description: `Any "${keyTokens}" command`,
305
+ pattern: actionKey.key,
306
+ });
307
+ }
308
+ return options;
255
309
  }
256
310
 
257
311
  const options: AllowlistOption[] = [];
@@ -33,17 +33,6 @@ export interface AcceptStarterBundleResult {
33
33
  // Helpers
34
34
  // ---------------------------------------------------------------------------
35
35
 
36
- /**
37
- * Resolve the gateway base URL for trust rule requests.
38
- *
39
- * Prefers the `GATEWAY_INTERNAL_URL` env var (set in Docker environments
40
- * where the gateway runs in a separate container), falling back to the
41
- * existing `getGatewayInternalBaseUrl()` helper for local deployments.
42
- */
43
- function getBaseUrl(): string {
44
- return process.env.GATEWAY_INTERNAL_URL ?? getGatewayInternalBaseUrl();
45
- }
46
-
47
36
  function authHeaders(): Record<string, string> {
48
37
  return {
49
38
  Authorization: `Bearer ${mintDaemonDeliveryToken()}`,
@@ -60,7 +49,7 @@ async function request<T>(
60
49
  path: string,
61
50
  body?: unknown,
62
51
  ): Promise<T> {
63
- const url = `${getBaseUrl()}${path}`;
52
+ const url = `${getGatewayInternalBaseUrl()}${path}`;
64
53
  const options: RequestInit = {
65
54
  method,
66
55
  headers: authHeaders(),
@@ -102,7 +91,7 @@ async function request<T>(
102
91
  * Write operations are user-initiated and infrequent, so blocking is acceptable.
103
92
  */
104
93
  function requestSync<T>(method: string, path: string, body?: unknown): T {
105
- const url = `${getBaseUrl()}${path}`;
94
+ const url = `${getGatewayInternalBaseUrl()}${path}`;
106
95
  const headers = authHeaders();
107
96
  const args: string[] = [
108
97
  "curl",
@@ -185,9 +185,10 @@ function backfillDefaults(rules: TrustRule[]): boolean {
185
185
  }
186
186
  }
187
187
 
188
- // Migrate existing default rules whose priority, pattern, decision, or
189
- // allowHighRisk has changed in the template (e.g. host_bash pattern changed
190
- // from '*' to '**', host tool priorities changed from 1000 to 50).
188
+ // Migrate existing default rules whose priority, pattern, scope, decision,
189
+ // or allowHighRisk has changed in the template (e.g. host_bash pattern
190
+ // changed from '*' to '**', host tool priorities changed from 1000 to 50,
191
+ // workspace scope changed from getRootDir()+workspace to getWorkspaceDir()).
191
192
  for (const template of getDefaultRuleTemplates()) {
192
193
  if (existingIds.has(template.id)) {
193
194
  const rule = rules.find((r) => r.id === template.id);
@@ -195,6 +196,7 @@ function backfillDefaults(rules: TrustRule[]): boolean {
195
196
  rule &&
196
197
  (rule.priority !== template.priority ||
197
198
  rule.pattern !== template.pattern ||
199
+ rule.scope !== template.scope ||
198
200
  rule.decision !== template.decision ||
199
201
  rule.allowHighRisk !== template.allowHighRisk)
200
202
  ) {
@@ -205,11 +207,14 @@ function backfillDefaults(rules: TrustRule[]): boolean {
205
207
  newPriority: template.priority,
206
208
  oldPattern: rule.pattern,
207
209
  newPattern: template.pattern,
210
+ oldScope: rule.scope,
211
+ newScope: template.scope,
208
212
  },
209
213
  "Migrated default rule to updated template values",
210
214
  );
211
215
  rule.priority = template.priority;
212
216
  rule.pattern = template.pattern;
217
+ rule.scope = template.scope;
213
218
  rule.decision = template.decision;
214
219
  if (template.allowHighRisk != null) {
215
220
  rule.allowHighRisk = template.allowHighRisk;
@@ -24,7 +24,8 @@ export type UserDecision =
24
24
  | "always_allow_high_risk"
25
25
  | "deny"
26
26
  | "always_deny"
27
- | "temporary_override";
27
+ | "temporary_override"
28
+ | "dangerously_skip_permissions";
28
29
 
29
30
  /** Returns true for any allow-variant decision. Centralizes the check to prevent omissions when new allow variants are added. */
30
31
  export function isAllowDecision(decision: UserDecision): boolean {
@@ -34,7 +35,8 @@ export function isAllowDecision(decision: UserDecision): boolean {
34
35
  decision === "allow_conversation" ||
35
36
  decision === "always_allow" ||
36
37
  decision === "always_allow_high_risk" ||
37
- decision === "temporary_override"
38
+ decision === "temporary_override" ||
39
+ decision === "dangerously_skip_permissions"
38
40
  );
39
41
  }
40
42
 
@@ -7,6 +7,8 @@
7
7
 
8
8
  import { getPlatformAssistantId } from "../config/env.js";
9
9
  import { resolveManagedProxyContext } from "../providers/managed-proxy/context.js";
10
+ import { credentialKey } from "../security/credential-key.js";
11
+ import { getSecureKeyAsync } from "../security/secure-keys.js";
10
12
 
11
13
  export class VellumPlatformClient {
12
14
  private readonly platformBaseUrl: string;
@@ -26,21 +28,47 @@ export class VellumPlatformClient {
26
28
  /**
27
29
  * Create a platform client by resolving managed proxy context.
28
30
  *
31
+ * First tries the in-memory managed proxy context (available when the daemon
32
+ * has rehydrated env overrides). Falls back to reading platform credentials
33
+ * directly from the secure keychain so that standalone CLI invocations work
34
+ * without the daemon having run its rehydration step.
35
+ *
29
36
  * Returns `null` when auth prerequisites are missing (not logged in, no API
30
37
  * key). The assistant ID is resolved but not required — callers that need it
31
38
  * should check `platformAssistantId` themselves.
32
39
  */
33
40
  static async create(): Promise<VellumPlatformClient | null> {
34
41
  const ctx = await resolveManagedProxyContext();
35
- if (!ctx.enabled) return null;
36
42
 
37
- const assistantId = getPlatformAssistantId();
43
+ let baseUrl = ctx.enabled ? ctx.platformBaseUrl : "";
44
+ let apiKey = ctx.enabled ? ctx.assistantApiKey : "";
45
+ let assistantId = getPlatformAssistantId();
46
+
47
+ // Fall back to keychain for values not yet rehydrated (standalone CLI).
48
+ if (!baseUrl) {
49
+ baseUrl =
50
+ (await getSecureKeyAsync(
51
+ credentialKey("vellum", "platform_base_url"),
52
+ )) ?? "";
53
+ }
54
+ if (!apiKey) {
55
+ apiKey =
56
+ (await getSecureKeyAsync(
57
+ credentialKey("vellum", "assistant_api_key"),
58
+ )) ?? "";
59
+ }
60
+ if (!assistantId) {
61
+ assistantId =
62
+ (
63
+ await getSecureKeyAsync(
64
+ credentialKey("vellum", "platform_assistant_id"),
65
+ )
66
+ )?.trim() ?? "";
67
+ }
68
+
69
+ if (!baseUrl || !apiKey) return null;
38
70
 
39
- return new VellumPlatformClient(
40
- ctx.platformBaseUrl,
41
- ctx.assistantApiKey,
42
- assistantId,
43
- );
71
+ return new VellumPlatformClient(baseUrl, apiKey, assistantId);
44
72
  }
45
73
 
46
74
  /**
@@ -0,0 +1,138 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import {
5
+ findContactByChannelExternalId,
6
+ listGuardianChannels,
7
+ } from "../contacts/contact-store.js";
8
+ import type {
9
+ ChannelCapabilities,
10
+ TrustContext,
11
+ } from "../daemon/conversation-runtime-assembly.js";
12
+ import { getLogger } from "../util/logger.js";
13
+ import { getWorkspaceDir } from "../util/platform.js";
14
+ import { stripCommentLines } from "./system-prompt.js";
15
+
16
+ const log = getLogger("persona-resolver");
17
+
18
+ // ── Types ──────────────────────────────────────────────────────────
19
+
20
+ export interface PersonaContext {
21
+ userPersona: string | null;
22
+ channelPersona: string | null;
23
+ }
24
+
25
+ // ── Helpers ────────────────────────────────────────────────────────
26
+
27
+ /**
28
+ * Read a persona file from disk, apply comment stripping, and return
29
+ * the content. Returns null if the file does not exist or is empty
30
+ * after stripping.
31
+ */
32
+ function readPersonaFile(filePath: string): string | null {
33
+ if (!existsSync(filePath)) return null;
34
+ const raw = readFileSync(filePath, "utf-8");
35
+ const content = stripCommentLines(raw).trim();
36
+ return content.length > 0 ? content : null;
37
+ }
38
+
39
+ // ── User persona ───────────────────────────────────────────────────
40
+
41
+ /**
42
+ * Resolve the per-user persona file for the current actor.
43
+ *
44
+ * - If `trustContext` is undefined (desktop/native), looks up the guardian
45
+ * contact and reads their user file.
46
+ * - If `trustContext` is defined and carries a `requesterExternalUserId`,
47
+ * looks up the contact by channel + external user ID.
48
+ * - Falls back to `users/default.md` when no contact is found or the
49
+ * contact has no `userFile` set.
50
+ * - Logs a debug warning when a contact's `userFile` is set but the
51
+ * corresponding file is missing on disk.
52
+ */
53
+ export function resolveUserPersona(
54
+ trustContext: TrustContext | undefined,
55
+ ): string | null {
56
+ const usersDir = join(getWorkspaceDir(), "users");
57
+ const defaultPath = join(usersDir, "default.md");
58
+
59
+ let filename: string | null = null;
60
+
61
+ if (trustContext === undefined) {
62
+ // Desktop / native — resolve via guardian contact
63
+ const guardian = listGuardianChannels();
64
+ if (guardian) {
65
+ filename = guardian.contact.userFile ?? null;
66
+ }
67
+ } else if (trustContext.requesterExternalUserId) {
68
+ // Channel-routed request — look up contact by channel identity
69
+ const contactWithChannels = findContactByChannelExternalId(
70
+ trustContext.sourceChannel,
71
+ trustContext.requesterExternalUserId,
72
+ );
73
+ if (contactWithChannels) {
74
+ filename = contactWithChannels.userFile ?? null;
75
+ }
76
+ }
77
+
78
+ // Resolve file path
79
+ if (filename) {
80
+ const filePath = join(usersDir, filename);
81
+ if (existsSync(filePath)) {
82
+ return readPersonaFile(filePath);
83
+ }
84
+ // userFile is set but the file doesn't exist on disk
85
+ log.debug(
86
+ { userFile: filename },
87
+ "Contact has userFile set but file is missing on disk; falling back to default.md",
88
+ );
89
+ }
90
+
91
+ // Fall back to default.md
92
+ return readPersonaFile(defaultPath);
93
+ }
94
+
95
+ // ── Channel persona ────────────────────────────────────────────────
96
+
97
+ /**
98
+ * Resolve the per-channel persona file based on channel capabilities.
99
+ *
100
+ * Reads from `channels/<channel>.md` in the workspace directory.
101
+ * Defaults to `"vellum"` when no channel capabilities are provided.
102
+ * Returns null if the channel file does not exist.
103
+ */
104
+ export function resolveChannelPersona(
105
+ channelCapabilities: ChannelCapabilities | undefined,
106
+ ): string | null {
107
+ const channel = channelCapabilities?.channel ?? "vellum";
108
+ const filePath = join(getWorkspaceDir(), "channels", channel + ".md");
109
+ return readPersonaFile(filePath);
110
+ }
111
+
112
+ // ── Combined resolver ──────────────────────────────────────────────
113
+
114
+ /**
115
+ * Resolve both user and channel persona context in a single call.
116
+ */
117
+ export function resolvePersonaContext(
118
+ trustContext: TrustContext | undefined,
119
+ channelCapabilities: ChannelCapabilities | undefined,
120
+ ): PersonaContext {
121
+ return {
122
+ userPersona: resolveUserPersona(trustContext),
123
+ channelPersona: resolveChannelPersona(channelCapabilities),
124
+ };
125
+ }
126
+
127
+ // ── Guardian convenience ───────────────────────────────────────────
128
+
129
+ /**
130
+ * Resolve the guardian's user persona.
131
+ *
132
+ * This is a convenience wrapper for background subsystems that need
133
+ * the guardian's persona without a full trust context. Passing
134
+ * `undefined` triggers the guardian lookup path in `resolveUserPersona`.
135
+ */
136
+ export function resolveGuardianPersona(): string | null {
137
+ return resolveUserPersona(undefined);
138
+ }
@@ -1,4 +1,4 @@
1
- import { copyFileSync, existsSync, readFileSync } from "node:fs";
1
+ import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
4
  import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
@@ -9,7 +9,11 @@ import { loadSkillCatalog, type SkillSummary } from "../config/skills.js";
9
9
  import { listConnections } from "../oauth/oauth-store.js";
10
10
  import { resolveBundledDir } from "../util/bundled-asset.js";
11
11
  import { getLogger } from "../util/logger.js";
12
- import { getWorkspacePromptPath, isMacOS } from "../util/platform.js";
12
+ import {
13
+ getWorkspaceDir,
14
+ getWorkspacePromptPath,
15
+ isMacOS,
16
+ } from "../util/platform.js";
13
17
  import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "./cache-boundary.js";
14
18
 
15
19
  export { SYSTEM_PROMPT_CACHE_BOUNDARY };
@@ -78,6 +82,26 @@ export function ensurePromptFiles(): void {
78
82
  }
79
83
  }
80
84
  }
85
+
86
+ // Seed users/default.md persona template
87
+ try {
88
+ const usersDir = join(getWorkspaceDir(), "users");
89
+ mkdirSync(usersDir, { recursive: true });
90
+ const defaultPersonaSrc = join(templatesDir, "users", "default.md");
91
+ const defaultPersonaDest = join(usersDir, "default.md");
92
+ if (!existsSync(defaultPersonaDest) && existsSync(defaultPersonaSrc)) {
93
+ copyFileSync(defaultPersonaSrc, defaultPersonaDest);
94
+ log.info(
95
+ { file: "users/default.md", dest: defaultPersonaDest },
96
+ "Created default persona file from template",
97
+ );
98
+ }
99
+ } catch (err) {
100
+ log.warn(
101
+ { err, file: "users/default.md" },
102
+ "Failed to create default persona file from template",
103
+ );
104
+ }
81
105
  }
82
106
 
83
107
  /**
@@ -93,6 +117,8 @@ export function ensurePromptFiles(): void {
93
117
  export interface BuildSystemPromptOptions {
94
118
  hasNoClient?: boolean;
95
119
  excludeBootstrap?: boolean;
120
+ userPersona?: string | null;
121
+ channelPersona?: string | null;
96
122
  }
97
123
 
98
124
  /**
@@ -159,7 +185,9 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
159
185
  dynamicParts.push(identity);
160
186
  }
161
187
  if (soul) dynamicParts.push(soul);
162
- if (user && !userIsTemplate) dynamicParts.push(user);
188
+ if (options?.userPersona) dynamicParts.push(options.userPersona);
189
+ if (options?.channelPersona) dynamicParts.push(options.channelPersona);
190
+ if (user && !userIsTemplate && !options?.userPersona) dynamicParts.push(user);
163
191
  if (includeBootstrap) {
164
192
  dynamicParts.push(
165
193
  "# First-Run Ritual\n\n" +
@@ -377,7 +405,9 @@ function readPromptFile(path: string): string | null {
377
405
  * This is useful for injecting identity context into subsystems (e.g. memory
378
406
  * extraction) that run outside the main system prompt pipeline.
379
407
  */
380
- export function buildCoreIdentityContext(): string | null {
408
+ export function buildCoreIdentityContext(
409
+ opts?: { userPersona?: string | null },
410
+ ): string | null {
381
411
  const parts: string[] = [];
382
412
  for (const file of PROMPT_FILES) {
383
413
  const content = readPromptFile(getWorkspacePromptPath(file));
@@ -386,8 +416,10 @@ export function buildCoreIdentityContext(): string | null {
386
416
  // before onboarding completes. Only skip IDENTITY.md and USER.md when
387
417
  // they are still unmodified templates (matching buildSystemPrompt).
388
418
  if (file !== "SOUL.md" && isTemplateContent(content, file)) continue;
419
+ if (file === "USER.md" && opts?.userPersona) continue;
389
420
  parts.push(content);
390
421
  }
422
+ if (opts?.userPersona) parts.push(opts.userPersona);
391
423
  return parts.length > 0 ? parts.join("\n\n") : null;
392
424
  }
393
425
 
@@ -1,4 +1,3 @@
1
- import { wrapWithLogfire } from "../logfire.js";
2
1
  import { getProviderKeyAsync } from "../security/secure-keys.js";
3
2
  import { ConfigError, ProviderNotConfiguredError } from "../util/errors.js";
4
3
  import { AnthropicProvider } from "./anthropic/client.js";
@@ -293,15 +292,13 @@ export async function initializeProviders(
293
292
  registerProvider(
294
293
  "anthropic",
295
294
  new RetryProvider(
296
- wrapWithLogfire(
297
- new AnthropicProvider(anthropicCreds.apiKey, model, {
298
- useNativeWebSearch,
299
- streamTimeoutMs,
300
- ...(anthropicCreds.baseURL
301
- ? { baseURL: anthropicCreds.baseURL }
302
- : {}),
303
- }),
304
- ),
295
+ new AnthropicProvider(anthropicCreds.apiKey, model, {
296
+ useNativeWebSearch,
297
+ streamTimeoutMs,
298
+ ...(anthropicCreds.baseURL
299
+ ? { baseURL: anthropicCreds.baseURL }
300
+ : {}),
301
+ }),
305
302
  ),
306
303
  );
307
304
  routingSources.set("anthropic", anthropicCreds.source);
@@ -314,12 +311,10 @@ export async function initializeProviders(
314
311
  registerProvider(
315
312
  "openai",
316
313
  new RetryProvider(
317
- wrapWithLogfire(
318
- new OpenAIProvider(openaiCreds.apiKey, model, {
319
- streamTimeoutMs,
320
- ...(openaiCreds.baseURL ? { baseURL: openaiCreds.baseURL } : {}),
321
- }),
322
- ),
314
+ new OpenAIProvider(openaiCreds.apiKey, model, {
315
+ streamTimeoutMs,
316
+ ...(openaiCreds.baseURL ? { baseURL: openaiCreds.baseURL } : {}),
317
+ }),
323
318
  ),
324
319
  );
325
320
  routingSources.set("openai", openaiCreds.source);
@@ -332,14 +327,12 @@ export async function initializeProviders(
332
327
  registerProvider(
333
328
  "gemini",
334
329
  new RetryProvider(
335
- wrapWithLogfire(
336
- new GeminiProvider(geminiCreds.apiKey, model, {
337
- streamTimeoutMs,
338
- ...(geminiCreds.baseURL
339
- ? { managedBaseUrl: geminiCreds.baseURL }
340
- : {}),
341
- }),
342
- ),
330
+ new GeminiProvider(geminiCreds.apiKey, model, {
331
+ streamTimeoutMs,
332
+ ...(geminiCreds.baseURL
333
+ ? { managedBaseUrl: geminiCreds.baseURL }
334
+ : {}),
335
+ }),
343
336
  ),
344
337
  );
345
338
  routingSources.set("gemini", geminiCreds.source);
@@ -352,12 +345,10 @@ export async function initializeProviders(
352
345
  registerProvider(
353
346
  "ollama",
354
347
  new RetryProvider(
355
- wrapWithLogfire(
356
- new OllamaProvider(model, {
357
- apiKey: ollamaKey ?? undefined,
358
- streamTimeoutMs,
359
- }),
360
- ),
348
+ new OllamaProvider(model, {
349
+ apiKey: ollamaKey ?? undefined,
350
+ streamTimeoutMs,
351
+ }),
361
352
  ),
362
353
  );
363
354
  routingSources.set("ollama", "user-key");
@@ -370,11 +361,9 @@ export async function initializeProviders(
370
361
  registerProvider(
371
362
  "fireworks",
372
363
  new RetryProvider(
373
- wrapWithLogfire(
374
- new FireworksProvider(fireworksKey, model, {
375
- streamTimeoutMs,
376
- }),
377
- ),
364
+ new FireworksProvider(fireworksKey, model, {
365
+ streamTimeoutMs,
366
+ }),
378
367
  ),
379
368
  );
380
369
  routingSources.set("fireworks", "user-key");
@@ -387,11 +376,9 @@ export async function initializeProviders(
387
376
  registerProvider(
388
377
  "openrouter",
389
378
  new RetryProvider(
390
- wrapWithLogfire(
391
- new OpenRouterProvider(openrouterKey, model, {
392
- streamTimeoutMs,
393
- }),
394
- ),
379
+ new OpenRouterProvider(openrouterKey, model, {
380
+ streamTimeoutMs,
381
+ }),
395
382
  ),
396
383
  );
397
384
  routingSources.set("openrouter", "user-key");
@@ -17,7 +17,6 @@ mock.module("../../../util/platform.js", () => ({
17
17
  getDataDir: () => testDir,
18
18
  getDbPath: () => join(testDir, "test.db"),
19
19
  normalizeAssistantId: (id: string) => (id === "self" ? "self" : id),
20
- readLockfile: () => ({ assistants: [{ assistantId: "vellum-test-eel" }] }),
21
20
  isMacOS: () => process.platform === "darwin",
22
21
  isLinux: () => process.platform === "linux",
23
22
  isWindows: () => process.platform === "win32",