@vellumai/assistant 0.11.6 → 0.11.7-staging.2

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 (478) hide show
  1. package/ARCHITECTURE.md +31 -19
  2. package/Dockerfile +1 -0
  3. package/docs/architecture/security.md +3 -3
  4. package/docs/flux-turn-detection-spike.md +8 -7
  5. package/docs/runbook-trusted-contacts.md +14 -0
  6. package/docs/stt-provider-onboarding.md +8 -4
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
  9. package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
  10. package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
  11. package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
  12. package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
  13. package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
  14. package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
  15. package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  18. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  19. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  20. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  21. package/node_modules/@vellumai/ces-client/src/http-credentials.ts +161 -0
  22. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  23. package/node_modules/@vellumai/environments/package.json +2 -1
  24. package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
  25. package/node_modules/@vellumai/environments/src/shell.ts +65 -0
  26. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  27. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  28. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  29. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  30. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  31. package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +32 -0
  32. package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -0
  33. package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +82 -0
  34. package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +56 -0
  35. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +18 -0
  36. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -11
  37. package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +3 -1
  38. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +9 -0
  39. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +4 -0
  40. package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +75 -0
  41. package/node_modules/@vellumai/gateway-client/src/index.ts +17 -0
  42. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +8 -0
  43. package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +47 -0
  44. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +11 -0
  45. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  46. package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  47. package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  48. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  49. package/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  50. package/openapi.yaml +419 -55
  51. package/package.json +4 -1
  52. package/scripts/sync-llm-catalog.ts +6 -0
  53. package/src/__tests__/approval-card-edit-inbound-handoff.test.ts +199 -0
  54. package/src/__tests__/approval-card-edit.test.ts +153 -0
  55. package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
  56. package/src/__tests__/available-skills.test.ts +54 -0
  57. package/src/__tests__/avatar-identity-sync.test.ts +5 -1
  58. package/src/__tests__/catalog-install-normalize.test.ts +9 -1
  59. package/src/__tests__/channel-policy.test.ts +1 -3
  60. package/src/__tests__/channel-readiness-discord.test.ts +9 -70
  61. package/src/__tests__/compactor-call-site-logging.test.ts +61 -0
  62. package/src/__tests__/config-loader-backfill.test.ts +69 -0
  63. package/src/__tests__/config-schema.test.ts +2 -3
  64. package/src/__tests__/conversation-agent-loop.test.ts +238 -0
  65. package/src/__tests__/conversation-attachments.test.ts +0 -1
  66. package/src/__tests__/conversation-error.test.ts +44 -0
  67. package/src/__tests__/conversation-evictor.test.ts +19 -0
  68. package/src/__tests__/conversation-fork-referential.test.ts +73 -63
  69. package/src/__tests__/conversation-fork-retrospective.test.ts +99 -74
  70. package/src/__tests__/conversation-runtime-assembly.test.ts +23 -0
  71. package/src/__tests__/conversation-slash.test.ts +9 -0
  72. package/src/__tests__/conversation-title-service.test.ts +97 -0
  73. package/src/__tests__/conversation-tool-setup-attribution.test.ts +16 -0
  74. package/src/__tests__/credential-broker-server-use.test.ts +0 -218
  75. package/src/__tests__/credential-broker.test.ts +43 -420
  76. package/src/__tests__/credential-record-write-through.test.ts +78 -0
  77. package/src/__tests__/credential-routes.test.ts +17 -1
  78. package/src/__tests__/credential-security-invariants.test.ts +8 -9
  79. package/src/__tests__/default-profile-catalog-fallback.test.ts +289 -0
  80. package/src/__tests__/delete-propagation.test.ts +92 -2
  81. package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
  82. package/src/__tests__/discord-channel-config.test.ts +237 -0
  83. package/src/__tests__/edit-propagation.test.ts +42 -2
  84. package/src/__tests__/evict-conversations-for-reload.test.ts +51 -1
  85. package/src/__tests__/fallback-breaker.test.ts +1335 -0
  86. package/src/__tests__/gateway-threshold-reader-mock.ts +6 -0
  87. package/src/__tests__/guardian-card-withdrawal.test.ts +30 -0
  88. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -2
  89. package/src/__tests__/image-conversion.test.ts +38 -4
  90. package/src/__tests__/install-skill-routing.test.ts +25 -0
  91. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  92. package/src/__tests__/llm-resolver.test.ts +74 -0
  93. package/src/__tests__/llm-schema-fallback-profile.test.ts +359 -0
  94. package/src/__tests__/managed-fallback-dispatch.test.ts +430 -0
  95. package/src/__tests__/managed-profile-guard.test.ts +28 -0
  96. package/src/__tests__/managed-speech-defaults.test.ts +196 -1
  97. package/src/__tests__/mcp-cli.test.ts +23 -1
  98. package/src/__tests__/mcp-tool-annotations-risk.test.ts +43 -34
  99. package/src/__tests__/media-stream-stt-session.test.ts +2 -0
  100. package/src/__tests__/notification-decision-fallback.test.ts +106 -0
  101. package/src/__tests__/notification-discord-adapter.test.ts +162 -0
  102. package/src/__tests__/oauth-connect-orchestrator.test.ts +95 -0
  103. package/src/__tests__/persist-unsendable-image-downscale.test.ts +6 -8
  104. package/src/__tests__/persist-unsendable-image.test.ts +3 -3
  105. package/src/__tests__/platform.test.ts +19 -1
  106. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
  107. package/src/__tests__/provider-catalog-visibility.test.ts +17 -0
  108. package/src/__tests__/provider-platform-proxy-integration.test.ts +8 -1
  109. package/src/__tests__/provider-usage-tracking.test.ts +56 -0
  110. package/src/__tests__/require-fresh-approval.test.ts +5 -2
  111. package/src/__tests__/retry-fallback-escalation.test.ts +1093 -0
  112. package/src/__tests__/schedule-retry.test.ts +183 -4
  113. package/src/__tests__/scheduler-reuse-conversation.test.ts +6 -6
  114. package/src/__tests__/secret-routes-platform-proxy.test.ts +55 -0
  115. package/src/__tests__/skill-load-tool.test.ts +34 -0
  116. package/src/__tests__/skill-tool-factory.test.ts +6 -1
  117. package/src/__tests__/skill-tool-manifest.test.ts +20 -0
  118. package/src/__tests__/skills.test.ts +25 -0
  119. package/src/__tests__/subagent-manager-notify.test.ts +38 -0
  120. package/src/__tests__/terminal-tools.test.ts +2 -0
  121. package/src/__tests__/tool-approval-handler.test.ts +146 -2
  122. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +108 -2
  123. package/src/__tests__/tool-audit.test.ts +111 -40
  124. package/src/__tests__/tool-executor-lifecycle-events.test.ts +228 -18
  125. package/src/__tests__/ui-shape-teaching.test.ts +1 -1
  126. package/src/__tests__/verification-control-plane-policy.test.ts +6 -2
  127. package/src/__tests__/voice-config-update.test.ts +43 -0
  128. package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +480 -0
  129. package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +167 -0
  130. package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +144 -0
  131. package/src/agent/loop.ts +31 -2
  132. package/src/approvals/guardian-card-withdrawal.ts +22 -0
  133. package/src/avatar/__tests__/ensure-raster.test.ts +291 -0
  134. package/src/avatar/avatar-manifest.ts +48 -100
  135. package/src/avatar/avatar-store.ts +7 -7
  136. package/src/avatar/ensure-raster.ts +146 -0
  137. package/src/avatar/resvg-lazy.test.ts +0 -1
  138. package/src/avatar/traits-png-sync.ts +9 -10
  139. package/src/background-wake/wake-intent-hooks.test.ts +0 -1
  140. package/src/backup/__tests__/paths.test.ts +6 -2
  141. package/src/backup/paths.ts +6 -4
  142. package/src/bundler/app-compiler.ts +2 -2
  143. package/src/bundler/compiler-tools.test.ts +36 -0
  144. package/src/bundler/compiler-tools.ts +54 -18
  145. package/src/calls/__tests__/telephony-synthesis-language.test.ts +81 -0
  146. package/src/calls/__tests__/voice-session-bridge.test.ts +57 -0
  147. package/src/calls/media-stream-stt-session.ts +2 -1
  148. package/src/calls/telephony-synthesis-language.ts +13 -5
  149. package/src/calls/voice-session-bridge.ts +19 -4
  150. package/src/channels/config.ts +23 -12
  151. package/src/cli/AGENTS.md +3 -1
  152. package/src/cli/commands/backup.help.ts +10 -13
  153. package/src/cli/commands/contacts.help.ts +16 -4
  154. package/src/cli/commands/contacts.ts +5 -1
  155. package/src/cli/commands/credentials.help.ts +1 -1
  156. package/src/cli/commands/inference-providers.ts +18 -53
  157. package/src/cli/commands/mcp.help.ts +6 -5
  158. package/src/cli/commands/mcp.ts +1 -1
  159. package/src/cli/commands/memory/memory-retrospective.ts +1 -0
  160. package/src/cli/commands/stt.help.ts +2 -2
  161. package/src/config/__tests__/backup-schema.test.ts +1 -1
  162. package/src/config/__tests__/default-profile-catalog.test.ts +83 -2
  163. package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
  164. package/src/config/__tests__/memory-retrospective-schema.test.ts +12 -15
  165. package/src/config/bundled-skills/computer-use/SKILL.md +6 -6
  166. package/src/config/bundled-skills/computer-use/TOOLS.json +9 -6
  167. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +3 -1
  168. package/src/config/bundled-skills/media-processing/services/preprocess.ts +1 -1
  169. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  170. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +16 -15
  171. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -1
  172. package/src/config/bundled-skills/settings/TOOLS.json +11 -5
  173. package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +85 -0
  174. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +33 -8
  175. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +54 -3
  176. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +1 -1
  177. package/src/config/default-profile-catalog.ts +195 -10
  178. package/src/config/default-profile-names.ts +41 -0
  179. package/src/config/feature-flag-registry.json +34 -2
  180. package/src/config/inference-profile-validation.ts +11 -2
  181. package/src/config/llm-resolver.ts +25 -0
  182. package/src/config/loader.ts +3 -0
  183. package/src/config/managed-speech-defaults.ts +160 -10
  184. package/src/config/schema.ts +2 -0
  185. package/src/config/schemas/__tests__/live-voice.test.ts +7 -6
  186. package/src/config/schemas/__tests__/stt.test.ts +94 -0
  187. package/src/config/schemas/backup.ts +1 -1
  188. package/src/config/schemas/channels.ts +32 -0
  189. package/src/config/schemas/live-voice.ts +13 -4
  190. package/src/config/schemas/llm.ts +263 -15
  191. package/src/config/schemas/mcp.ts +10 -2
  192. package/src/config/schemas/memory-retrospective.ts +1 -19
  193. package/src/config/schemas/services.ts +24 -0
  194. package/src/config/schemas/stt.ts +128 -5
  195. package/src/config/seed-inference-profiles.ts +32 -3
  196. package/src/config/skill-state.ts +5 -0
  197. package/src/config/skills.ts +21 -0
  198. package/src/contacts/notify-contacts-changed.ts +5 -2
  199. package/src/context/compactor.ts +74 -1
  200. package/src/daemon/__tests__/conversation-tool-setup.test.ts +138 -1
  201. package/src/daemon/config-watcher.ts +2 -1
  202. package/src/daemon/conversation-agent-loop-handlers.ts +27 -0
  203. package/src/daemon/conversation-agent-loop.ts +86 -10
  204. package/src/daemon/conversation-error.ts +25 -0
  205. package/src/daemon/conversation-evictor.ts +2 -4
  206. package/src/daemon/conversation-runtime-assembly.ts +19 -7
  207. package/src/daemon/conversation-skill-tools.ts +5 -1
  208. package/src/daemon/conversation-slash.ts +2 -2
  209. package/src/daemon/conversation-store.ts +8 -1
  210. package/src/daemon/conversation-tool-setup.ts +37 -0
  211. package/src/daemon/conversation.ts +0 -4
  212. package/src/daemon/handlers/channel-config-result.ts +30 -0
  213. package/src/daemon/handlers/config-discord-channel.ts +301 -0
  214. package/src/daemon/handlers/config-ingress.ts +9 -0
  215. package/src/daemon/handlers/config-slack-channel.ts +13 -15
  216. package/src/daemon/handlers/config-telegram.ts +2 -5
  217. package/src/daemon/handlers/conversations.ts +0 -2
  218. package/src/daemon/handlers/skills.ts +25 -7
  219. package/src/daemon/lifecycle.ts +3 -1
  220. package/src/daemon/message-types/messages.ts +0 -2
  221. package/src/daemon/tool-setup-types.ts +5 -3
  222. package/src/daemon/trust-context-types.test.ts +33 -0
  223. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  224. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +58 -4
  225. package/src/live-voice/__tests__/live-voice-managed-speech-fallback.test.ts +8 -3
  226. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +73 -11
  227. package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +148 -2
  228. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +64 -0
  229. package/src/live-voice/__tests__/live-voice-stt.test.ts +3 -0
  230. package/src/live-voice/__tests__/live-voice-text-turn.test.ts +391 -0
  231. package/src/live-voice/__tests__/protocol.test.ts +224 -0
  232. package/src/live-voice/live-voice-credential-preflight.ts +5 -1
  233. package/src/live-voice/live-voice-session-manager.ts +39 -2
  234. package/src/live-voice/live-voice-session.ts +224 -9
  235. package/src/live-voice/protocol.ts +187 -1
  236. package/src/messaging/providers/discord/api.ts +2 -1
  237. package/src/messaging/providers/discord/withdraw.ts +62 -0
  238. package/src/messaging/read-provider-metadata.ts +32 -0
  239. package/src/monitoring/__tests__/file-descriptors.test.ts +32 -0
  240. package/src/monitoring/__tests__/process-memory.test.ts +34 -1
  241. package/src/monitoring/db-integrity-sample.ts +1 -0
  242. package/src/monitoring/file-descriptors.ts +39 -6
  243. package/src/monitoring/process-memory.ts +37 -5
  244. package/src/monitoring/resource-sampler.ts +25 -8
  245. package/src/notifications/__tests__/activity-failed-dedupe.test.ts +161 -0
  246. package/src/notifications/__tests__/background-failure-signal.test.ts +107 -0
  247. package/src/notifications/__tests__/connected-channels.test.ts +12 -0
  248. package/src/notifications/__tests__/copy-composer.test.ts +83 -0
  249. package/src/notifications/__tests__/destination-resolver.test.ts +38 -0
  250. package/src/notifications/activity-failed-dedupe.ts +95 -0
  251. package/src/notifications/adapters/discord.ts +132 -0
  252. package/src/notifications/adapters/shared.ts +16 -0
  253. package/src/notifications/adapters/slack.ts +6 -1
  254. package/src/notifications/adapters/telegram.ts +5 -8
  255. package/src/notifications/approval-card-data.ts +94 -1
  256. package/src/notifications/background-failure-signal.ts +116 -0
  257. package/src/notifications/broadcaster.ts +3 -9
  258. package/src/notifications/copy-composer.ts +41 -8
  259. package/src/notifications/decision-engine.ts +55 -0
  260. package/src/notifications/destination-resolver.ts +45 -5
  261. package/src/notifications/emit-signal.ts +19 -5
  262. package/src/notifications/guardian-question-mode.ts +57 -0
  263. package/src/notifications/types.ts +2 -8
  264. package/src/oauth/__tests__/seed-providers-managed.test.ts +18 -0
  265. package/src/oauth/connect-orchestrator.ts +16 -0
  266. package/src/oauth/seed-providers.ts +262 -0
  267. package/src/permissions/checker.test.ts +21 -0
  268. package/src/permissions/checker.ts +6 -0
  269. package/src/permissions/gateway-threshold-reader.test.ts +184 -0
  270. package/src/permissions/gateway-threshold-reader.ts +144 -10
  271. package/src/permissions/prompter.ts +0 -6
  272. package/src/permissions/types.ts +5 -4
  273. package/src/persistence/__tests__/bulk-write-gate.test.ts +0 -38
  274. package/src/persistence/bulk-write-gate.ts +9 -10
  275. package/src/persistence/conversation-crud.ts +88 -134
  276. package/src/persistence/conversation-title-service.ts +43 -29
  277. package/src/persistence/embeddings/embedding-local.ts +14 -85
  278. package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -0
  279. package/src/persistence/lifecycle-events-store.test.ts +50 -0
  280. package/src/persistence/lifecycle-events-store.ts +75 -5
  281. package/src/platform/platform-patch-queue.test.ts +368 -0
  282. package/src/platform/platform-patch-queue.ts +190 -0
  283. package/src/platform/sync-avatar.test.ts +527 -0
  284. package/src/platform/sync-avatar.ts +209 -0
  285. package/src/platform/sync-identity.test.ts +75 -0
  286. package/src/platform/sync-identity.ts +17 -110
  287. package/src/plugin-api/model-profiles.test.ts +2 -2
  288. package/src/plugin-api/model-profiles.ts +7 -7
  289. package/src/plugin-api/transcription-session.ts +1 -1
  290. package/src/plugins/defaults/compaction/window-manager.ts +4 -0
  291. package/src/plugins/defaults/image-recovery/recover.ts +2 -2
  292. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +133 -34
  293. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +21 -31
  294. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +13 -9
  295. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +31 -4
  296. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +0 -11
  297. package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +27 -13
  298. package/src/plugins/defaults/memory/memory-retrospective-job.ts +183 -113
  299. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
  300. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
  301. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +1 -1
  302. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +4 -2
  303. package/src/plugins/defaults/memory/substrate/sweep-job.ts +9 -42
  304. package/src/plugins/defaults/memory/v2/rerank-local.ts +1 -0
  305. package/src/plugins/defaults/memory/worker.ts +7 -0
  306. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  307. package/src/providers/__tests__/retry-callsite.test.ts +55 -0
  308. package/src/providers/__tests__/vellum-connection-routing.test.ts +21 -3
  309. package/src/providers/connection-resolution.ts +2 -17
  310. package/src/providers/fallback-breaker.ts +389 -0
  311. package/src/providers/inference/__tests__/adapter-factory-opencode.test.ts +92 -0
  312. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +16 -0
  313. package/src/providers/inference/__tests__/base-url-security.test.ts +12 -0
  314. package/src/providers/inference/adapter-factory.ts +246 -3
  315. package/src/providers/inference/auth.ts +15 -14
  316. package/src/providers/inference/chatgpt-subscription-auth.ts +70 -0
  317. package/src/providers/inference/chatgpt-subscription-credentials.ts +84 -0
  318. package/src/providers/inference/resolve-auth.ts +1 -1
  319. package/src/providers/media-resolve.ts +2 -3
  320. package/src/providers/model-catalog.ts +100 -0
  321. package/src/providers/openai/__tests__/api-error-normalization.test.ts +22 -0
  322. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +198 -0
  323. package/src/providers/openai/api-error-normalization.ts +13 -0
  324. package/src/providers/openai/chat-completions-provider.ts +84 -0
  325. package/src/providers/opencode/client.test.ts +150 -0
  326. package/src/providers/opencode/client.ts +61 -0
  327. package/src/providers/platform-proxy/constants.ts +5 -0
  328. package/src/providers/provider-resolvability.ts +20 -0
  329. package/src/providers/provider-secret-catalog.ts +3 -2
  330. package/src/providers/retry.ts +956 -68
  331. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +42 -0
  332. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +136 -0
  333. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +120 -0
  334. package/src/providers/speech-to-text/__tests__/resolve.test.ts +316 -12
  335. package/src/providers/speech-to-text/deepgram-flux-frames.ts +83 -3
  336. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +132 -4
  337. package/src/providers/speech-to-text/provider-catalog.ts +191 -3
  338. package/src/providers/speech-to-text/resolve.ts +132 -24
  339. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +296 -0
  340. package/src/providers/types.ts +39 -0
  341. package/src/providers/usage-tracking.ts +12 -1
  342. package/src/providers/vellum/client.ts +29 -0
  343. package/src/providers/vellum-model-routing.test.ts +2 -0
  344. package/src/providers/vellum-model-routing.ts +3 -3
  345. package/src/runtime/AGENTS.md +1 -2
  346. package/src/runtime/__tests__/agent-wake.test.ts +105 -3
  347. package/src/runtime/__tests__/background-job-runner.test.ts +89 -3
  348. package/src/runtime/agent-wake.ts +44 -5
  349. package/src/runtime/background-job-runner.ts +68 -50
  350. package/src/runtime/channel-readiness-service.ts +0 -39
  351. package/src/runtime/http-server.ts +12 -4
  352. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +18 -18
  353. package/src/runtime/routes/__tests__/backup-routes.test.ts +9 -5
  354. package/src/runtime/routes/__tests__/chatgpt-subscription-auth-routes.test.ts +372 -0
  355. package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -0
  356. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +96 -0
  357. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +37 -0
  358. package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +2 -1
  359. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +87 -0
  360. package/src/runtime/routes/acp-claude-auth-routes.ts +25 -54
  361. package/src/runtime/routes/approval-routes.ts +0 -12
  362. package/src/runtime/routes/avatar-routes.ts +17 -30
  363. package/src/runtime/routes/backup-routes.ts +2 -2
  364. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +296 -113
  365. package/src/runtime/routes/contact-prompt-routes.ts +65 -6
  366. package/src/runtime/routes/contact-routes.ts +20 -15
  367. package/src/runtime/routes/conversation-query-routes.ts +98 -10
  368. package/src/runtime/routes/conversation-routes.ts +2 -2
  369. package/src/runtime/routes/credential-routes.ts +2 -1
  370. package/src/runtime/routes/guardian-approval-interception.ts +12 -9
  371. package/src/runtime/routes/inbound-message-handler.ts +47 -48
  372. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +18 -13
  373. package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -2
  374. package/src/runtime/routes/inbound-stages/edit-intercept.ts +20 -2
  375. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +9 -8
  376. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -1
  377. package/src/runtime/routes/index.ts +2 -0
  378. package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
  379. package/src/runtime/routes/inference-profiles-routes.ts +12 -7
  380. package/src/runtime/routes/inference-provider-connection-routes.ts +2 -2
  381. package/src/runtime/routes/inference-send-routes.ts +2 -2
  382. package/src/runtime/routes/ingress-status-routes.ts +2 -2
  383. package/src/runtime/routes/integrations/discord.ts +119 -0
  384. package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
  385. package/src/runtime/routes/mcp-auth-routes.ts +12 -6
  386. package/src/runtime/routes/migration-routes.ts +3 -1
  387. package/src/runtime/routes/oauth-pending-flows.ts +136 -0
  388. package/src/runtime/routes/secret-routes.ts +7 -0
  389. package/src/runtime/routes/stt-routes.ts +38 -6
  390. package/src/runtime/routes/wake-conversation-routes.ts +8 -1
  391. package/src/runtime/routes/watch-routes.ts +9 -5
  392. package/src/runtime/sync/resource-sync-events.ts +2 -0
  393. package/src/runtime/trust-verdict-consumer.ts +3 -3
  394. package/src/schedule/__tests__/worker-feature-flags.test.ts +58 -0
  395. package/src/schedule/retry-policy.ts +8 -8
  396. package/src/schedule/run-script.ts +14 -23
  397. package/src/schedule/schedule-recovery.ts +0 -1
  398. package/src/schedule/scheduler.ts +89 -51
  399. package/src/schedule/worker.ts +57 -0
  400. package/src/security/__tests__/openai-device-auth.test.ts +355 -0
  401. package/src/security/ces-rpc-record-backend.ts +123 -0
  402. package/src/security/oauth2.ts +7 -0
  403. package/src/security/openai-device-auth.ts +364 -0
  404. package/src/security/secure-keys.ts +15 -0
  405. package/src/skills/available-skills.ts +10 -1
  406. package/src/skills/catalog-install.ts +19 -1
  407. package/src/skills/inline-command-runner.ts +6 -2
  408. package/src/skills/platform-compatibility.test.ts +49 -0
  409. package/src/skills/platform-compatibility.ts +65 -0
  410. package/src/skills/tool-manifest.ts +20 -0
  411. package/src/stt/__tests__/roles.test.ts +152 -0
  412. package/src/stt/daemon-batch-transcriber.ts +4 -1
  413. package/src/stt/roles.ts +180 -0
  414. package/src/stt/stt-stream-session.ts +3 -2
  415. package/src/stt/types.ts +33 -3
  416. package/src/subagent/manager.ts +12 -0
  417. package/src/telemetry/__tests__/live-voice-funnel.test.ts +34 -0
  418. package/src/telemetry/live-voice-funnel.ts +12 -2
  419. package/src/telemetry/telemetry-wire.generated.ts +6 -0
  420. package/src/telemetry/tool-audit.ts +83 -30
  421. package/src/telemetry/tool-executed-events-store.test.ts +0 -1
  422. package/src/telemetry/turn-outcome.ts +75 -4
  423. package/src/tools/client-os.ts +115 -0
  424. package/src/tools/computer-use/definitions.ts +11 -7
  425. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -2
  426. package/src/tools/credentials/broker-types.ts +0 -71
  427. package/src/tools/credentials/broker.ts +6 -201
  428. package/src/tools/credentials/metadata-store.ts +65 -16
  429. package/src/tools/credentials/store.ts +2 -0
  430. package/src/tools/executor.ts +2 -8
  431. package/src/tools/host-shell.test.ts +75 -0
  432. package/src/tools/host-shell.ts +24 -0
  433. package/src/tools/host-terminal/host-shell.ts +15 -44
  434. package/src/tools/mcp/mcp-tool-factory.ts +44 -9
  435. package/src/tools/permission-checker.ts +73 -21
  436. package/src/tools/policy-context.ts +1 -0
  437. package/src/tools/shared/filesystem/image-read.ts +1 -1
  438. package/src/tools/skills/find-similar-skills.ts +10 -2
  439. package/src/tools/skills/load.ts +41 -6
  440. package/src/tools/skills/sandbox-runner.ts +8 -16
  441. package/src/tools/skills/skill-tool-factory.ts +1 -0
  442. package/src/tools/terminal/__tests__/safe-env.test.ts +64 -1
  443. package/src/tools/terminal/safe-env.ts +39 -9
  444. package/src/tools/terminal/shell.ts +12 -31
  445. package/src/tools/tool-approval-handler.ts +64 -10
  446. package/src/tools/tool-types.ts +10 -0
  447. package/src/tools/types.ts +19 -3
  448. package/src/tools/ui-surface/surface-shape-docs.ts +4 -3
  449. package/src/tools/workflows/manage-workflows.ts +2 -2
  450. package/src/tools/workflows/run-workflow.test.ts +2 -1
  451. package/src/util/__tests__/file-use.test.ts +61 -0
  452. package/src/util/__tests__/process-table.test.ts +66 -0
  453. package/src/util/__tests__/process-tree.test.ts +46 -0
  454. package/src/util/errors.ts +1 -0
  455. package/src/util/file-use.ts +91 -0
  456. package/src/util/host-process.test.ts +17 -0
  457. package/src/util/host-process.ts +75 -0
  458. package/src/util/image-conversion.ts +39 -7
  459. package/src/util/platform.ts +32 -13
  460. package/src/util/process-table.ts +249 -0
  461. package/src/util/process-tree.ts +85 -129
  462. package/src/util/provider-error-patterns.ts +20 -0
  463. package/src/util/spawn.ts +9 -2
  464. package/src/watch/__tests__/watch-retro.test.ts +48 -1
  465. package/src/watch/watch-retro.ts +46 -4
  466. package/src/workspace/git-service.ts +4 -14
  467. package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +360 -0
  468. package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +117 -0
  469. package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +204 -0
  470. package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +139 -0
  471. package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +160 -0
  472. package/src/workspace/migrations/registry.ts +8 -0
  473. package/src/__tests__/permission-types.test.ts +0 -17
  474. package/src/channels/gateway-discord-admission.ts +0 -32
  475. package/src/persistence/fork-message-copy.ts +0 -239
  476. package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -221
  477. package/src/security/__tests__/oauth2-device-code.test.ts +0 -431
  478. package/src/security/oauth2-device-code.ts +0 -312
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Sync the avatar raster to the platform Assistant record.
3
+ *
4
+ * Every avatar publish (routes and the fs watcher) and daemon startup enqueue
5
+ * a sync through the shared platform PATCH queue. The current avatar state is
6
+ * read when the request runs, so rapid changes collapse into one PATCH
7
+ * carrying the newest raster. The dedup key comes from the raster file itself,
8
+ * not the manifest, so a raster rewritten in place (fs watcher path) still
9
+ * syncs. The last synced key is persisted outside the workspace repo at
10
+ * `<protected dir>/platform-sync/avatar.json` so a daemon restart does not
11
+ * re-upload an unchanged raster and the record never dirties the workspace.
12
+ * A synced key older than `AVATAR_SYNC_KEY_TTL_MS` does not dedup, whether
13
+ * seeded from disk or set in-process, and the queue re-enqueues itself when
14
+ * the key expires, so an avatar lost server-side while id and base URL are
15
+ * unchanged is re-pushed within a week even by a daemon that never restarts
16
+ * (API-key auth cannot read the record to verify it).
17
+ * `avatar_base64: null` is sent only when the avatar is actually removed; a
18
+ * non-none avatar whose raster is missing (image PNG gone, character
19
+ * re-render unavailable) is skipped so the platform keeps the last synced
20
+ * copy.
21
+ */
22
+
23
+ import { createHash } from "node:crypto";
24
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
25
+ import { dirname, join } from "node:path";
26
+
27
+ import { readAvatarState } from "../avatar/avatar-manifest.js";
28
+ import {
29
+ ensureAvatarRasterPath,
30
+ readContainedAvatarRaster,
31
+ } from "../avatar/ensure-raster.js";
32
+ import { getResvg, isResvgAvailable } from "../avatar/resvg-lazy.js";
33
+ import { detectMediaType } from "../tools/shared/filesystem/image-read.js";
34
+ import { getLogger } from "../util/logger.js";
35
+ import { getProtectedDir } from "../util/platform.js";
36
+ import {
37
+ createPlatformPatchQueue,
38
+ type PatchPayload,
39
+ type PlatformPatchQueue,
40
+ type SyncedKey,
41
+ } from "./platform-patch-queue.js";
42
+
43
+ const log = getLogger("sync-avatar");
44
+
45
+ /** Largest raster sent as-is; bigger ones are downscaled first. */
46
+ const MAX_AVATAR_UPLOAD_BYTES = 256 * 1024;
47
+ const DOWNSCALE_PX = 128;
48
+ const NONE_KEY = "none";
49
+ /** Raster formats resvg decodes inside an `<image>`; anything else renders blank. */
50
+ const RESVG_DECODABLE_TYPES: ReadonlySet<string> = new Set([
51
+ "image/png",
52
+ "image/jpeg",
53
+ "image/gif",
54
+ ]);
55
+ /** Only bytes that sniff as a raster image are ever uploaded. */
56
+ const UPLOADABLE_TYPES: ReadonlySet<string> = new Set([
57
+ ...RESVG_DECODABLE_TYPES,
58
+ "image/webp",
59
+ ]);
60
+ const SYNC_STATE_SUBPATH = ["platform-sync", "avatar.json"];
61
+ export const AVATAR_SYNC_KEY_TTL_MS = 7 * 24 * 60 * 60 * 1000;
62
+
63
+ let queue: PlatformPatchQueue<void> | null = null;
64
+
65
+ /** Recreates the queue so the next sync re-seeds its dedup key from disk. */
66
+ export function _resetSyncAvatarStateForTests(): void {
67
+ queue?.dispose();
68
+ queue = null;
69
+ }
70
+
71
+ /**
72
+ * Enqueue a best-effort push of the current avatar raster to the platform.
73
+ * No-op when the platform client cannot be created or no assistant id is
74
+ * configured, or when the raster's etag matches the last successful sync.
75
+ */
76
+ export function syncAvatarToPlatform(): void {
77
+ queue ??= createPlatformPatchQueue({
78
+ log,
79
+ label: "avatar",
80
+ buildPayload,
81
+ loadSyncedKey: readPersistedKey,
82
+ saveSyncedKey: persistKey,
83
+ maxAgeMs: AVATAR_SYNC_KEY_TTL_MS,
84
+ });
85
+ queue.enqueue();
86
+ }
87
+
88
+ function syncStatePath(): string {
89
+ return join(getProtectedDir(), ...SYNC_STATE_SUBPATH);
90
+ }
91
+
92
+ /** Returns the persisted key, or null when missing or malformed. */
93
+ function readPersistedKey(): SyncedKey | null {
94
+ try {
95
+ const parsed: unknown = JSON.parse(readFileSync(syncStatePath(), "utf-8"));
96
+ const { key, syncedAt } = (parsed ?? {}) as {
97
+ key?: unknown;
98
+ syncedAt?: unknown;
99
+ };
100
+ if (typeof key !== "string" || typeof syncedAt !== "number") {
101
+ return null;
102
+ }
103
+ return { key, syncedAt };
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ function persistKey(synced: SyncedKey): void {
110
+ try {
111
+ const path = syncStatePath();
112
+ mkdirSync(dirname(path), { recursive: true });
113
+ const tmpPath = `${path}.tmp.${process.pid}`;
114
+ writeFileSync(tmpPath, JSON.stringify(synced));
115
+ renameSync(tmpPath, path);
116
+ } catch (err) {
117
+ log.warn({ err }, "Failed to persist avatar sync state");
118
+ }
119
+ }
120
+
121
+ async function buildPayload(): Promise<PatchPayload | undefined> {
122
+ const state = readAvatarState();
123
+ if (state.kind === "none") {
124
+ return { key: NONE_KEY, body: { avatar_base64: null } };
125
+ }
126
+ const path = await ensureAvatarRasterPath(state);
127
+ if (!path) {
128
+ log.warn({ kind: state.kind }, "Avatar raster missing; skipping sync");
129
+ return undefined;
130
+ }
131
+ const bytes = readRaster(path);
132
+ if (!bytes) {
133
+ return undefined;
134
+ }
135
+ // Keyed on content so a same-size, same-mtime rewrite still re-syncs.
136
+ const digest = createHash("sha256").update(bytes).digest("hex");
137
+ return {
138
+ key: `${state.kind}:${digest}`,
139
+ body: async () => {
140
+ const encoded = encodeForUpload(bytes);
141
+ if (encoded === undefined) {
142
+ log.warn(
143
+ { bytes: bytes.length, cap: MAX_AVATAR_UPLOAD_BYTES },
144
+ "Avatar raster exceeds upload cap and could not be downscaled; skipping sync",
145
+ );
146
+ return undefined;
147
+ }
148
+ return { avatar_base64: encoded };
149
+ },
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Reads the raster through the fd-validated, avatar-dir-contained path and
155
+ * refuses anything that does not sniff as an image, so a symlinked or
156
+ * swapped `avatar-image.png` never ships host bytes to the platform.
157
+ */
158
+ function readRaster(path: string): Buffer | undefined {
159
+ const bytes = readContainedAvatarRaster(path);
160
+ if (!bytes) {
161
+ log.warn(
162
+ { path },
163
+ "Avatar raster unreadable or not contained; skipping sync",
164
+ );
165
+ return undefined;
166
+ }
167
+ const mediaType = detectMediaType(bytes);
168
+ if (mediaType === null || !UPLOADABLE_TYPES.has(mediaType)) {
169
+ log.warn({ mediaType }, "Avatar raster is not an image; skipping sync");
170
+ return undefined;
171
+ }
172
+ return bytes;
173
+ }
174
+
175
+ function downscaleRaster(bytes: Buffer): Buffer | null {
176
+ if (!isResvgAvailable()) {
177
+ return null;
178
+ }
179
+ const mediaType = detectMediaType(bytes);
180
+ if (mediaType === null || !RESVG_DECODABLE_TYPES.has(mediaType)) {
181
+ log.warn(
182
+ { mediaType },
183
+ "Avatar raster format is not decodable by resvg; skipping downscale",
184
+ );
185
+ return null;
186
+ }
187
+ const href = `data:${mediaType};base64,${bytes.toString("base64")}`;
188
+ const svg =
189
+ `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ` +
190
+ `width="${DOWNSCALE_PX}" height="${DOWNSCALE_PX}" viewBox="0 0 ${DOWNSCALE_PX} ${DOWNSCALE_PX}">` +
191
+ `<image width="${DOWNSCALE_PX}" height="${DOWNSCALE_PX}" preserveAspectRatio="xMidYMid meet" href="${href}" xlink:href="${href}"/></svg>`;
192
+ const Resvg = getResvg();
193
+ const resvg = new Resvg(svg, {
194
+ fitTo: { mode: "width", value: DOWNSCALE_PX },
195
+ });
196
+ return Buffer.from(resvg.render().asPng());
197
+ }
198
+
199
+ /** Returns the base64 payload, or undefined when the raster cannot fit the cap. */
200
+ function encodeForUpload(bytes: Buffer): string | undefined {
201
+ let upload: Buffer | null = bytes;
202
+ if (upload.length > MAX_AVATAR_UPLOAD_BYTES) {
203
+ upload = downscaleRaster(upload);
204
+ }
205
+ if (!upload || upload.length > MAX_AVATAR_UPLOAD_BYTES) {
206
+ return undefined;
207
+ }
208
+ return upload.toString("base64");
209
+ }
@@ -0,0 +1,75 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ let mockClient: {
4
+ baseUrl: string;
5
+ platformAssistantId: string;
6
+ fetch: (path: string, init: RequestInit) => Promise<Response>;
7
+ } | null;
8
+
9
+ mock.module("./client.js", () => ({
10
+ VellumPlatformClient: { create: async () => mockClient },
11
+ }));
12
+
13
+ import { syncIdentityNameToPlatform } from "./sync-identity.js";
14
+
15
+ interface Patch {
16
+ path: string;
17
+ body: { name: string };
18
+ }
19
+
20
+ let patches: Patch[];
21
+
22
+ function makeClient(assistantId = "asst-1") {
23
+ return {
24
+ baseUrl: "https://platform.a",
25
+ platformAssistantId: assistantId,
26
+ fetch: async (path: string, init: RequestInit) => {
27
+ patches.push({ path, body: JSON.parse(init.body as string) });
28
+ return new Response("{}", { status: 200 });
29
+ },
30
+ };
31
+ }
32
+
33
+ async function settle(): Promise<void> {
34
+ for (let i = 0; i < 10; i++) {
35
+ await new Promise((r) => setTimeout(r, 5));
36
+ }
37
+ }
38
+
39
+ // The module owns one queue, so names are unique per test to avoid dedup
40
+ // across tests. Queue semantics are covered by platform-patch-queue.test.ts.
41
+ describe("syncIdentityNameToPlatform", () => {
42
+ beforeEach(() => {
43
+ patches = [];
44
+ mockClient = makeClient();
45
+ });
46
+
47
+ test("PATCHes the name and dedups an unchanged name", async () => {
48
+ syncIdentityNameToPlatform("Ada");
49
+ await settle();
50
+ syncIdentityNameToPlatform("Ada");
51
+ await settle();
52
+
53
+ expect(patches).toEqual([
54
+ { path: "/v1/assistants/asst-1/", body: { name: "Ada" } },
55
+ ]);
56
+ });
57
+
58
+ test("rapid changes collapse into one PATCH carrying the newest name", async () => {
59
+ syncIdentityNameToPlatform("Bea");
60
+ syncIdentityNameToPlatform("Cy");
61
+ syncIdentityNameToPlatform("Dee");
62
+ await settle();
63
+
64
+ expect(patches.map((p) => p.body.name)).toEqual(["Dee"]);
65
+ });
66
+
67
+ test("empty names and a missing client are no-ops", async () => {
68
+ syncIdentityNameToPlatform("");
69
+ mockClient = null;
70
+ syncIdentityNameToPlatform("Eve");
71
+ await settle();
72
+
73
+ expect(patches).toHaveLength(0);
74
+ });
75
+ });
@@ -2,16 +2,11 @@
2
2
  * Sync assistant identity fields to the platform Assistant record.
3
3
  *
4
4
  * When IDENTITY.md changes on disk the daemon broadcasts an
5
- * `identity_changed` event to connected clients. This module hooks into
6
- * that same change signal and PATCHes the platform `Assistant` record so
7
- * the name (and, in future, other fields) stays in sync.
8
- *
9
- * Requests are serialized so that rapid name changes (A → B) never race:
10
- * only the most recently requested name is sent, and a stale in-flight
11
- * response cannot overwrite a newer value.
12
- *
13
- * The sync is best-effort and fire-and-forget — network failures are
14
- * logged but never surface to callers.
5
+ * `identity_changed` event to connected clients. This module hooks into that
6
+ * same change signal and PATCHes the platform `Assistant` record through the
7
+ * shared platform PATCH queue so the name stays in sync: rapid changes
8
+ * collapse to the newest name, and an unchanged name is not re-sent to the
9
+ * same platform destination.
15
10
  */
16
11
 
17
12
  import { existsSync, readFileSync } from "node:fs";
@@ -19,49 +14,27 @@ import { existsSync, readFileSync } from "node:fs";
19
14
  import { parseIdentityFields } from "../daemon/handlers/identity.js";
20
15
  import { getLogger } from "../util/logger.js";
21
16
  import { getWorkspacePromptPath } from "../util/platform.js";
22
- import { VellumPlatformClient } from "./client.js";
17
+ import { createPlatformPatchQueue } from "./platform-patch-queue.js";
23
18
 
24
19
  const log = getLogger("sync-identity");
25
20
 
26
- /** Track the last successfully synced name (used inside doSync to skip redundant PATCHes). */
27
- let lastSyncedName: string | null = null;
28
-
29
- /** Track the last requested name (used for dedup at enqueue time). */
30
- let lastRequestedName: string | null = null;
31
-
32
- /**
33
- * Monotonically increasing sequence number. Each call to
34
- * `syncIdentityNameToPlatform` bumps this; after a PATCH completes we
35
- * only update `lastSyncedName` when `seq` still matches, guaranteeing
36
- * the newest name always wins.
37
- */
38
- let seq = 0;
39
-
40
- /** Chain promise that serializes in-flight PATCH requests. */
41
- let pending: Promise<void> = Promise.resolve();
21
+ const queue = createPlatformPatchQueue<string>({
22
+ log,
23
+ label: "assistant name",
24
+ buildPayload: (name) => ({
25
+ key: name,
26
+ body: { name },
27
+ }),
28
+ });
42
29
 
43
30
  /**
44
31
  * Push the current assistant name to the platform `Assistant` record.
45
- *
46
- * No-op when:
47
- * - The platform client cannot be created (not platform-hosted / missing creds).
48
- * - No assistant ID is configured.
49
- * - The name is empty or unchanged since the last request.
32
+ * Empty names are ignored.
50
33
  */
51
34
  export function syncIdentityNameToPlatform(name: string): void {
52
- if (!name || name === lastRequestedName) {
53
- return;
35
+ if (name) {
36
+ queue.enqueue(name);
54
37
  }
55
-
56
- lastRequestedName = name;
57
-
58
- const mySeq = ++seq;
59
-
60
- pending = pending
61
- .then(() => doSync(name, mySeq))
62
- .catch(() => {
63
- // swallowed — doSync already logs internally
64
- });
65
38
  }
66
39
 
67
40
  /**
@@ -83,69 +56,3 @@ export function syncWorkspaceIdentityToPlatform(): void {
83
56
  log.error({ err }, "Failed to sync identity to platform at startup");
84
57
  }
85
58
  }
86
-
87
- async function doSync(name: string, requestSeq: number): Promise<void> {
88
- try {
89
- // A newer call has already been enqueued — skip this stale request.
90
- if (requestSeq !== seq) {
91
- return;
92
- }
93
-
94
- // Re-check after awaiting the previous request in the chain.
95
- if (name === lastSyncedName) {
96
- return;
97
- }
98
-
99
- const client = await VellumPlatformClient.create();
100
- if (!client) {
101
- clearRequestedIfLatest(requestSeq);
102
- return;
103
- }
104
-
105
- const assistantId = client.platformAssistantId;
106
- if (!assistantId) {
107
- clearRequestedIfLatest(requestSeq);
108
- return;
109
- }
110
-
111
- const resp = await client.fetch(
112
- `/v1/assistants/${encodeURIComponent(assistantId)}/`,
113
- {
114
- method: "PATCH",
115
- headers: { "Content-Type": "application/json" },
116
- body: JSON.stringify({ name }),
117
- signal: AbortSignal.timeout(15_000),
118
- },
119
- );
120
-
121
- if (resp.ok) {
122
- // Only update cache if no newer request has been enqueued since we
123
- // started this PATCH — prevents a slow response from overwriting a
124
- // fresher value.
125
- if (requestSeq === seq) {
126
- lastSyncedName = name;
127
- }
128
- log.info({ name, assistantId }, "Synced assistant name to platform");
129
- } else {
130
- clearRequestedIfLatest(requestSeq);
131
- const text = await resp.text();
132
- log.warn(
133
- { status: resp.status, body: text, assistantId },
134
- "Failed to sync assistant name to platform",
135
- );
136
- }
137
- } catch (err) {
138
- clearRequestedIfLatest(requestSeq);
139
- log.warn({ err }, "Error syncing assistant name to platform");
140
- }
141
- }
142
-
143
- /**
144
- * Reset `lastRequestedName` when the latest request failed, so that the
145
- * next call with the same name is allowed through instead of being deduped.
146
- */
147
- function clearRequestedIfLatest(requestSeq: number): void {
148
- if (requestSeq === seq) {
149
- lastRequestedName = lastSyncedName;
150
- }
151
- }
@@ -61,8 +61,8 @@ describe("getModelProfiles", () => {
61
61
 
62
62
  const result = listProfiles();
63
63
  // The code-catalog defaults are always present; the two workspace
64
- // entries shadow their catalog counterparts, and the remaining catalog
65
- // default sorts after the explicit order.
64
+ // entries shadow their catalog counterparts, and managed backups remain
65
+ // internal fallback routes rather than picker options.
66
66
  expect(result.map((p) => p.key)).toEqual([
67
67
  "balanced",
68
68
  "quality-optimized",
@@ -1,15 +1,15 @@
1
- import { getEffectiveProfilesForProvider } from "../config/default-profile-catalog.js";
1
+ import { getUserSelectableProfilesForProvider } from "../config/default-profile-catalog.js";
2
2
  import { getConfig } from "../config/loader.js";
3
3
  import { isDispatchableProfile } from "../config/profile-dispatchability.js";
4
4
  import { orderProfileKeys } from "../config/profile-order.js";
5
5
  import type { ModelProfileInfo } from "./types.js";
6
6
 
7
7
  /**
8
- * List the workspace inference profiles a plugin can route to, in the order the
9
- * `/model` picker presents them (`llm.profileOrder` first, then the rest
10
- * alphabetically). Metadata-only entries without a provider, model, or mix are
11
- * not routing targets, so plugins never see them. Disabled profiles are
12
- * included and flagged via
8
+ * List the workspace inference profiles a plugin can route to, in the order a
9
+ * user-facing model picker presents them (`llm.profileOrder` first, then the
10
+ * rest alphabetically). Managed backup routes are internal and omitted.
11
+ * Metadata-only entries without a provider, model, or mix are not routing
12
+ * targets, so plugins never see them. Disabled profiles are included and flagged via
13
13
  * {@link ModelProfileInfo.isDisabled}; weighted "mix" profiles are included and
14
14
  * flagged via {@link ModelProfileInfo.isMix}, since a mix is itself a valid
15
15
  * routing target (it resolves to one constituent per conversation).
@@ -20,7 +20,7 @@ import type { ModelProfileInfo } from "./types.js";
20
20
  export function getModelProfiles(): ModelProfileInfo[] {
21
21
  const { llm } = getConfig();
22
22
  const { activeProfile } = llm;
23
- const profiles = getEffectiveProfilesForProvider(
23
+ const profiles = getUserSelectableProfilesForProvider(
24
24
  llm.profiles,
25
25
  llm.defaultProvider ?? null,
26
26
  );
@@ -35,5 +35,5 @@ import type { StreamingTranscriber } from "../stt/types.js";
35
35
  export async function openTranscriptionSession(): Promise<StreamingTranscriber | null> {
36
36
  const { resolveStreamingTranscriber } =
37
37
  await import("../providers/speech-to-text/resolve.js");
38
- return resolveStreamingTranscriber();
38
+ return resolveStreamingTranscriber({ role: "dictation" });
39
39
  }
@@ -84,6 +84,10 @@ export interface ContextWindowResult {
84
84
  summaryModel: string;
85
85
  summaryCallSite?: LLMCallSite;
86
86
  summaryOverrideProfile?: string | null;
87
+ /** See {@link CompactionRunResult.summaryActualProvider}. */
88
+ summaryActualProvider?: string;
89
+ /** See {@link CompactionRunResult.summaryActualInferenceProfile}. */
90
+ summaryActualInferenceProfile?: string;
87
91
  summaryCacheCreationInputTokens?: number;
88
92
  summaryCacheReadInputTokens?: number;
89
93
  summaryRawResponses?: unknown[];
@@ -66,8 +66,8 @@ export const UNSENDABLE_IMAGE_NOTE =
66
66
  * bytes untouched. An unsendable image that can be resized is rewritten to
67
67
  * its resized form (downscaled when oversized, upscaled to the minimum floor
68
68
  * when undersized); one that cannot be resized on this host (resize is a
69
- * no-op, e.g. `sips` is absent off macOS or the format is unsupported) is
70
- * replaced with a text note.
69
+ * no-op because the encoder cannot decode the format) is replaced with a text
70
+ * note.
71
71
  *
72
72
  * Shared by the in-memory recovery transform and the durable persist pass so
73
73
  * both apply the identical rule. Persisting the resized form is what lets a