@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,206 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { describe, expect, mock, spyOn, test } from "bun:test";
4
-
5
- // ---------------------------------------------------------------------------
6
- // Mocks — must be set up before importing modules that use them
7
- // ---------------------------------------------------------------------------
8
-
9
- mock.module("@anthropic-ai/claude-agent-sdk", () => ({
10
- query: () => ({
11
- async *[Symbol.asyncIterator]() {
12
- yield {
13
- type: "result" as const,
14
- session_id: "s",
15
- subtype: "success" as const,
16
- result: "ok",
17
- };
18
- },
19
- }),
20
- }));
21
-
22
- mock.module("../util/logger.js", () => ({
23
- getLogger: () =>
24
- new Proxy({} as Record<string, unknown>, {
25
- get: () => () => {},
26
- }),
27
- }));
28
-
29
- mock.module("../config/loader.js", () => ({
30
- getConfig: () => ({
31
- ui: {},
32
- services: {
33
- inference: {
34
- mode: "your-own",
35
- provider: "anthropic",
36
- model: "claude-opus-4-6",
37
- },
38
- "image-generation": {
39
- mode: "your-own",
40
- provider: "gemini",
41
- model: "gemini-3.1-flash-image-preview",
42
- },
43
- "web-search": { mode: "your-own", provider: "inference-provider-native" },
44
- },
45
- }),
46
- loadConfig: () => ({}),
47
- loadRawConfig: () => ({}),
48
- saveConfig: () => {},
49
- saveRawConfig: () => {},
50
- invalidateConfigCache: () => {},
51
- getNestedValue: () => undefined,
52
- setNestedValue: () => {},
53
- syncConfigToLockfile: () => {},
54
- }));
55
-
56
- mock.module("../security/secure-keys.js", () => ({
57
- getSecureKeyAsync: async (name: string) =>
58
- name === "anthropic" ? "fake-anthropic-key" : null,
59
- getProviderKeyAsync: async (provider: string) =>
60
- provider === "anthropic" ? "fake-anthropic-key" : undefined,
61
- }));
62
-
63
- // ---------------------------------------------------------------------------
64
- // Imports (after mocks)
65
- // ---------------------------------------------------------------------------
66
-
67
- import { claudeCodeTool } from "../tools/claude-code/claude-code.js";
68
-
69
- // ---------------------------------------------------------------------------
70
- // Locate the bundled skill directory relative to the test file
71
- // ---------------------------------------------------------------------------
72
-
73
- const SKILL_DIR = path.resolve(
74
- import.meta.dirname ?? __dirname,
75
- "../config/bundled-skills/claude-code",
76
- );
77
-
78
- const SHARED_DIR = path.resolve(
79
- import.meta.dirname ?? __dirname,
80
- "../config/bundled-skills/_shared",
81
- );
82
-
83
- // ---------------------------------------------------------------------------
84
- // Tests
85
- // ---------------------------------------------------------------------------
86
-
87
- describe("Claude Code skill migration regression", () => {
88
- test("skill script wrapper exports a `run` function", async () => {
89
- const wrapperPath = path.join(SKILL_DIR, "tools/claude-code.ts");
90
- // The wrapper module must exist and export `run`
91
- const mod = await import(wrapperPath);
92
- expect(typeof mod.run).toBe("function");
93
- });
94
-
95
- test("TOOLS.json manifest lists claude_code as the tool name", () => {
96
- const manifestPath = path.join(SKILL_DIR, "TOOLS.json");
97
- const raw = fs.readFileSync(manifestPath, "utf-8");
98
- const manifest = JSON.parse(raw);
99
-
100
- expect(manifest.version).toBe(1);
101
- expect(Array.isArray(manifest.tools)).toBe(true);
102
-
103
- const toolNames = manifest.tools.map((t: { name: string }) => t.name);
104
- expect(toolNames).toContain("claude_code");
105
- });
106
-
107
- test("TOOLS.json input_schema matches claudeCodeTool.getDefinition()", () => {
108
- const manifestPath = path.join(SKILL_DIR, "TOOLS.json");
109
- const raw = fs.readFileSync(manifestPath, "utf-8");
110
- const manifest = JSON.parse(raw);
111
-
112
- const manifestTool = manifest.tools.find(
113
- (t: { name: string }) => t.name === "claude_code",
114
- );
115
- expect(manifestTool).toBeDefined();
116
-
117
- const runtimeDef = claudeCodeTool.getDefinition();
118
-
119
- // The input_schema declared in the static manifest must match the
120
- // runtime definition. Drift here would mean the model sees a different
121
- // schema than the executor actually supports.
122
- expect(manifestTool.input_schema).toEqual(runtimeDef.input_schema);
123
- });
124
-
125
- test("TOOLS.json description matches claudeCodeTool.getDefinition()", () => {
126
- const manifestPath = path.join(SKILL_DIR, "TOOLS.json");
127
- const raw = fs.readFileSync(manifestPath, "utf-8");
128
- const manifest = JSON.parse(raw);
129
-
130
- const manifestTool = manifest.tools.find(
131
- (t: { name: string }) => t.name === "claude_code",
132
- );
133
- expect(manifestTool).toBeDefined();
134
-
135
- const runtimeDef = claudeCodeTool.getDefinition();
136
-
137
- // Description parity guards against a manifest edit that diverges from
138
- // the canonical tool description.
139
- expect(manifestTool.description).toBe(runtimeDef.description);
140
- });
141
-
142
- test("wrapper run() delegates to claudeCodeTool.execute()", async () => {
143
- // Verifies the wrapper is not a stale stub but actually calls through
144
- // to the canonical execute method with the exact input and context.
145
- const spy = spyOn(claudeCodeTool, "execute");
146
-
147
- const wrapperPath = path.join(SKILL_DIR, "tools/claude-code.ts");
148
- const mod = await import(wrapperPath);
149
-
150
- const input = { prompt: "hello" };
151
- const ctx = {
152
- conversationId: "test",
153
- workingDir: "/tmp",
154
- onOutput: () => {},
155
- };
156
-
157
- const result = await mod.run(input, ctx);
158
- expect(result.isError).toBeFalsy();
159
-
160
- // The wrapper must delegate to the canonical execute method
161
- expect(spy).toHaveBeenCalledTimes(1);
162
- expect(spy).toHaveBeenCalledWith(input, ctx);
163
-
164
- spy.mockRestore();
165
- });
166
- });
167
-
168
- // ---------------------------------------------------------------------------
169
- // Bundled skill shared guidance — CES tools instead of token-reveal
170
- // ---------------------------------------------------------------------------
171
-
172
- describe("CLI_RETRIEVAL_PATTERN.md CES guidance", () => {
173
- const patternPath = path.join(SHARED_DIR, "CLI_RETRIEVAL_PATTERN.md");
174
- const content = fs.readFileSync(patternPath, "utf-8");
175
-
176
- test("teaches handle discovery via assistant credentials list", () => {
177
- expect(content).toContain("assistant credentials list");
178
- });
179
-
180
- test("teaches handle discovery via assistant oauth connections list", () => {
181
- expect(content).toContain("assistant oauth connections list");
182
- });
183
-
184
- test("teaches make_authenticated_request CES tool", () => {
185
- expect(content).toContain("make_authenticated_request");
186
- });
187
-
188
- test("teaches run_authenticated_command CES tool", () => {
189
- expect(content).toContain("run_authenticated_command");
190
- });
191
-
192
- test("warns that host_bash is outside CES secrecy boundary", () => {
193
- expect(content).toContain("outside the CES secrecy boundary");
194
- });
195
-
196
- // -- Deprecated patterns must NOT appear --
197
-
198
- test("does not teach proxied bash with credential_ids", () => {
199
- expect(content).not.toContain("credential_ids");
200
- expect(content).not.toContain("network_mode: proxied");
201
- });
202
-
203
- test("does not teach oauth connections token for raw token extraction", () => {
204
- expect(content).not.toContain("oauth connections token");
205
- });
206
- });
@@ -1,99 +0,0 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
2
-
3
- // ---------------------------------------------------------------------------
4
- // Mock the Agent SDK — prevents real subprocess spawning
5
- // ---------------------------------------------------------------------------
6
- const queryMock = mock(() => {
7
- // Returns an async iterable that yields a success result
8
- return {
9
- async *[Symbol.asyncIterator]() {
10
- yield {
11
- type: "result" as const,
12
- session_id: "test-session",
13
- subtype: "success" as const,
14
- result: "Done.",
15
- };
16
- },
17
- };
18
- });
19
-
20
- mock.module("@anthropic-ai/claude-agent-sdk", () => ({
21
- query: queryMock,
22
- }));
23
-
24
- // Mock logger
25
- mock.module("../util/logger.js", () => ({
26
- getLogger: () =>
27
- new Proxy({} as Record<string, unknown>, {
28
- get: () => () => {},
29
- }),
30
- }));
31
-
32
- // Mock config
33
- mock.module("../config/loader.js", () => ({
34
- getConfig: () => ({
35
- ui: {},
36
- }),
37
- }));
38
-
39
- // Mock secure-keys — provide a fake Anthropic API key
40
- mock.module("../security/secure-keys.js", () => ({
41
- getSecureKeyAsync: async (name: string) =>
42
- name === "anthropic" ? "fake-anthropic-key" : null,
43
- getProviderKeyAsync: async (provider: string) =>
44
- provider === "anthropic" ? "fake-anthropic-key" : undefined,
45
- }));
46
-
47
- import { claudeCodeTool } from "../tools/claude-code/claude-code.js";
48
- import type { ToolContext } from "../tools/types.js";
49
-
50
- function makeContext(overrides?: Partial<ToolContext>): ToolContext {
51
- return {
52
- conversationId: "test-session",
53
- workingDir: "/tmp/test",
54
- trustClass: "guardian",
55
- onOutput: () => {},
56
- ...overrides,
57
- } as ToolContext;
58
- }
59
-
60
- describe("claude_code tool profile support", () => {
61
- beforeEach(() => {
62
- queryMock.mockClear();
63
- });
64
-
65
- test("getDefinition includes profile parameter", () => {
66
- const def = claudeCodeTool.getDefinition();
67
- const props = (def.input_schema as Record<string, unknown>)
68
- .properties as Record<string, unknown>;
69
- expect(props.profile).toBeDefined();
70
- });
71
-
72
- test("rejects invalid profile", async () => {
73
- const result = await claudeCodeTool.execute(
74
- { prompt: "test", profile: "hacker" },
75
- makeContext(),
76
- );
77
- expect(result.isError).toBe(true);
78
- expect(result.content).toContain("Invalid profile");
79
- });
80
-
81
- test("accepts valid profiles without error", async () => {
82
- for (const profile of ["general", "researcher", "coder", "reviewer"]) {
83
- queryMock.mockClear();
84
- const result = await claudeCodeTool.execute(
85
- { prompt: "test", profile },
86
- makeContext(),
87
- );
88
- expect(result.isError).toBeFalsy();
89
- }
90
- });
91
-
92
- test("omitted profile defaults to general", async () => {
93
- const result = await claudeCodeTool.execute(
94
- { prompt: "test" },
95
- makeContext(),
96
- );
97
- expect(result.isError).toBeFalsy();
98
- });
99
- });
@@ -1,288 +0,0 @@
1
- /**
2
- * Tests for the diagnostics export route handler.
3
- *
4
- * Validates anchor message resolution, including the fallback chain
5
- * (specific ID → most recent assistant → any message → empty conversation).
6
- */
7
-
8
- import { mkdtempSync, readFileSync, rmSync } from "node:fs";
9
- import { tmpdir } from "node:os";
10
- import { join } from "node:path";
11
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
12
-
13
- import JSZip from "jszip";
14
-
15
- const testDir = mkdtempSync(join(tmpdir(), "diagnostics-export-test-"));
16
-
17
- mock.module("../util/platform.js", () => ({
18
- getDataDir: () => testDir,
19
- isMacOS: () => process.platform === "darwin",
20
- isLinux: () => process.platform === "linux",
21
- isWindows: () => process.platform === "win32",
22
- getPidPath: () => join(testDir, "test.pid"),
23
- getDbPath: () => join(testDir, "test.db"),
24
- getLogPath: () => join(testDir, "test.log"),
25
- ensureDataDir: () => {},
26
- }));
27
-
28
- mock.module("../util/logger.js", () => ({
29
- getLogger: () =>
30
- new Proxy({} as Record<string, unknown>, {
31
- get: () => () => {},
32
- }),
33
- }));
34
-
35
- import { getSqlite, initializeDb, resetDb } from "../memory/db.js";
36
- import { diagnosticsRouteDefinitions } from "../runtime/routes/diagnostics-routes.js";
37
-
38
- initializeDb();
39
-
40
- afterAll(() => {
41
- resetDb();
42
- try {
43
- rmSync(testDir, { recursive: true });
44
- } catch {
45
- /* best effort */
46
- }
47
- });
48
-
49
- // ---------------------------------------------------------------------------
50
- // Helpers
51
- // ---------------------------------------------------------------------------
52
-
53
- const routes = diagnosticsRouteDefinitions();
54
- const exportRoute = routes.find((r) => r.endpoint === "diagnostics/export")!;
55
-
56
- async function callExport(body: Record<string, unknown>): Promise<Response> {
57
- const req = new Request("http://localhost/v1/diagnostics/export", {
58
- method: "POST",
59
- headers: { "Content-Type": "application/json" },
60
- body: JSON.stringify(body),
61
- });
62
- const url = new URL(req.url);
63
- return exportRoute.handler({
64
- req,
65
- url,
66
- server: null as never,
67
- authContext: {} as never,
68
- params: {},
69
- });
70
- }
71
-
72
- const db = () => getSqlite();
73
-
74
- function seedConversation(id: string): void {
75
- const now = Date.now();
76
- db().run(
77
- "INSERT INTO conversations (id, title, created_at, updated_at) VALUES (?, ?, ?, ?)",
78
- [id, "Test", now, now],
79
- );
80
- }
81
-
82
- function seedMessage(
83
- id: string,
84
- conversationId: string,
85
- role: string,
86
- content: string,
87
- createdAt: number,
88
- ): void {
89
- db().run(
90
- "INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES (?, ?, ?, ?, ?)",
91
- [id, conversationId, role, content, createdAt],
92
- );
93
- }
94
-
95
- function seedLlmRequestLog(
96
- id: string,
97
- conversationId: string,
98
- provider: string | null,
99
- requestPayload: unknown,
100
- responsePayload: unknown,
101
- createdAt: number,
102
- ): void {
103
- db().run(
104
- "INSERT INTO llm_request_logs (id, conversation_id, provider, request_payload, response_payload, created_at) VALUES (?, ?, ?, ?, ?, ?)",
105
- [
106
- id,
107
- conversationId,
108
- provider,
109
- JSON.stringify(requestPayload),
110
- JSON.stringify(responsePayload),
111
- createdAt,
112
- ],
113
- );
114
- }
115
-
116
- function seedConversationKey(
117
- conversationKey: string,
118
- conversationId: string,
119
- ): void {
120
- db().run(
121
- "INSERT INTO conversation_keys (id, conversation_key, conversation_id, created_at) VALUES (?, ?, ?, ?)",
122
- [crypto.randomUUID(), conversationKey, conversationId, Date.now()],
123
- );
124
- }
125
-
126
- function cleanDb(): void {
127
- db().run("DELETE FROM messages");
128
- db().run("DELETE FROM llm_request_logs");
129
- db().run("DELETE FROM conversation_keys");
130
- db().run("DELETE FROM conversations");
131
- }
132
-
133
- beforeEach(() => {
134
- cleanDb();
135
- });
136
-
137
- // ---------------------------------------------------------------------------
138
- // Tests
139
- // ---------------------------------------------------------------------------
140
-
141
- describe("diagnostics export", () => {
142
- test("returns 400 when conversationId is missing", async () => {
143
- const res = await callExport({});
144
- expect(res.status).toBe(400);
145
- });
146
-
147
- test("succeeds with specific anchorMessageId", async () => {
148
- const convId = "conv-1";
149
- const msgId = "msg-assistant-1";
150
- const now = Date.now();
151
-
152
- seedConversation(convId);
153
- seedMessage("msg-user-1", convId, "user", "hello", now - 1000);
154
- seedMessage(msgId, convId, "assistant", "world", now);
155
-
156
- const res = await callExport({
157
- conversationId: convId,
158
- anchorMessageId: msgId,
159
- });
160
- expect(res.status).toBe(200);
161
- const json = (await res.json()) as { success: boolean; filePath: string };
162
- expect(json.success).toBe(true);
163
- expect(json.filePath).toContain("diagnostics-");
164
- });
165
-
166
- test("falls back to most recent assistant message when anchorMessageId is omitted", async () => {
167
- const convId = "conv-2";
168
- const now = Date.now();
169
-
170
- seedConversation(convId);
171
- seedMessage("msg-user-1", convId, "user", "hello", now - 1000);
172
- seedMessage("msg-assistant-1", convId, "assistant", "world", now);
173
-
174
- const res = await callExport({ conversationId: convId });
175
- expect(res.status).toBe(200);
176
- const json = (await res.json()) as { success: boolean };
177
- expect(json.success).toBe(true);
178
- });
179
-
180
- test("falls back to any message when no assistant messages exist (race condition fix)", async () => {
181
- const convId = "conv-3";
182
- const now = Date.now();
183
-
184
- seedConversation(convId);
185
- // Only a user message — assistant response hasn't been persisted yet
186
- seedMessage("msg-user-1", convId, "user", "hello", now);
187
-
188
- const res = await callExport({ conversationId: convId });
189
- expect(res.status).toBe(200);
190
- const json = (await res.json()) as { success: boolean; filePath: string };
191
- expect(json.success).toBe(true);
192
- expect(json.filePath).toContain("diagnostics-");
193
- });
194
-
195
- test("succeeds with empty conversation (no messages at all)", async () => {
196
- const convId = "conv-4";
197
-
198
- seedConversation(convId);
199
- // No messages at all — conversation was just created
200
-
201
- const res = await callExport({ conversationId: convId });
202
- expect(res.status).toBe(200);
203
- const json = (await res.json()) as { success: boolean; filePath: string };
204
- expect(json.success).toBe(true);
205
- expect(json.filePath).toContain("diagnostics-");
206
- });
207
-
208
- test("resolves conversation key to daemon conversation ID", async () => {
209
- const daemonConvId = "conv-6-daemon";
210
- const clientKey = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE";
211
- const now = Date.now();
212
-
213
- seedConversation(daemonConvId);
214
- seedConversationKey(clientKey, daemonConvId);
215
- seedMessage("msg-user-6", daemonConvId, "user", "hello", now - 1000);
216
- seedMessage("msg-assistant-6", daemonConvId, "assistant", "world", now);
217
-
218
- // Client sends the conversation key (client-side UUID), not the daemon ID
219
- const res = await callExport({ conversationId: clientKey });
220
- expect(res.status).toBe(200);
221
- const json = (await res.json()) as { success: boolean; filePath: string };
222
- expect(json.success).toBe(true);
223
- // The export should find the messages via the resolved daemon ID
224
- expect(json.filePath).toContain("diagnostics-");
225
- });
226
-
227
- test("falls back to any-message when anchorMessageId is provided but not found", async () => {
228
- const convId = "conv-5";
229
- const now = Date.now();
230
-
231
- seedConversation(convId);
232
- seedMessage("msg-user-1", convId, "user", "hello", now);
233
-
234
- // anchorMessageId doesn't exist — should fall through all the way
235
- const res = await callExport({
236
- conversationId: convId,
237
- anchorMessageId: "nonexistent-msg-id",
238
- });
239
- expect(res.status).toBe(200);
240
- const json = (await res.json()) as { success: boolean };
241
- expect(json.success).toBe(true);
242
- });
243
-
244
- test("preserves llm request provider identity in the exported JSONL", async () => {
245
- const convId = "conv-7";
246
- const now = Date.now();
247
-
248
- seedConversation(convId);
249
- seedMessage("msg-user-7", convId, "user", "hello", now - 1000);
250
- seedMessage("msg-assistant-7", convId, "assistant", "world", now);
251
- seedLlmRequestLog(
252
- "log-7",
253
- convId,
254
- "openrouter",
255
- { model: "openai/gpt-4.1-mini", input: "hello" },
256
- { output: "world" },
257
- now,
258
- );
259
-
260
- const res = await callExport({ conversationId: convId });
261
- expect(res.status).toBe(200);
262
- const json = (await res.json()) as { success: boolean; filePath: string };
263
- expect(json.success).toBe(true);
264
-
265
- const zip = await JSZip.loadAsync(readFileSync(json.filePath));
266
- const llmRequests = zip.file("llm_requests.jsonl");
267
- expect(llmRequests).not.toBeNull();
268
-
269
- const lines = (await llmRequests!.async("string")).trim().split("\n");
270
- expect(lines).toHaveLength(1);
271
-
272
- const row = JSON.parse(lines[0]) as {
273
- id: string;
274
- conversationId: string;
275
- provider?: string | null;
276
- request: unknown;
277
- response: unknown;
278
- };
279
-
280
- expect(row).toMatchObject({
281
- id: "log-7",
282
- conversationId: convId,
283
- provider: "openrouter",
284
- });
285
-
286
- rmSync(json.filePath, { force: true });
287
- });
288
- });