@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e

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 (456) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +8 -0
  3. package/docs/activation-funnel-telemetry.md +13 -7
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  12. package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  13. package/openapi.yaml +530 -107
  14. package/package.json +1 -1
  15. package/scripts/generate-openapi.ts +8 -0
  16. package/src/__tests__/activation-early-marking.test.ts +6 -5
  17. package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
  18. package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
  19. package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
  20. package/src/__tests__/app-bundler.test.ts +4 -12
  21. package/src/__tests__/app-executors.test.ts +2 -49
  22. package/src/__tests__/app-routes-csp.test.ts +114 -146
  23. package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
  24. package/src/__tests__/build-persisted-content.test.ts +96 -0
  25. package/src/__tests__/bundle-scanner.test.ts +27 -1
  26. package/src/__tests__/call-controller.test.ts +291 -0
  27. package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
  28. package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
  29. package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
  30. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  31. package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
  32. package/src/__tests__/config-loader-backfill.test.ts +9 -4
  33. package/src/__tests__/config-schema-cmd.test.ts +10 -11
  34. package/src/__tests__/config-schema.test.ts +190 -257
  35. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
  37. package/src/__tests__/conversation-agent-loop.test.ts +22 -9
  38. package/src/__tests__/conversation-error.test.ts +31 -0
  39. package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
  40. package/src/__tests__/conversation-process-callsite.test.ts +12 -19
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
  42. package/src/__tests__/conversation-summarize-route.test.ts +36 -44
  43. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  44. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
  45. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
  46. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
  47. package/src/__tests__/conversation-usage.test.ts +4 -12
  48. package/src/__tests__/credential-routes.test.ts +250 -0
  49. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  50. package/src/__tests__/db-migration-rollback.test.ts +22 -0
  51. package/src/__tests__/empty-response-hook.test.ts +186 -1
  52. package/src/__tests__/external-plugin-loader.test.ts +11 -5
  53. package/src/__tests__/heartbeat-service.test.ts +0 -28
  54. package/src/__tests__/host-shell-tool.test.ts +2 -0
  55. package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
  56. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
  57. package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
  58. package/src/__tests__/invite-routes-http.test.ts +12 -16
  59. package/src/__tests__/list-all-apps.test.ts +0 -4
  60. package/src/__tests__/llm-context-resolution.test.ts +32 -56
  61. package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
  62. package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
  63. package/src/__tests__/llm-resolver.test.ts +342 -602
  64. package/src/__tests__/llm-schema.test.ts +79 -37
  65. package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
  66. package/src/__tests__/media-stream-output.test.ts +259 -3
  67. package/src/__tests__/media-stream-server-integration.test.ts +22 -1
  68. package/src/__tests__/media-stream-stt-session.test.ts +47 -0
  69. package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
  70. package/src/__tests__/memory-recall-log-store.test.ts +47 -13
  71. package/src/__tests__/mock-gateway-ipc.ts +46 -1
  72. package/src/__tests__/mtime-cache.test.ts +61 -0
  73. package/src/__tests__/navigate-settings-tab.test.ts +2 -0
  74. package/src/__tests__/normalize-onboarding.test.ts +33 -0
  75. package/src/__tests__/onboarding-persona-write.test.ts +26 -0
  76. package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
  77. package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
  78. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
  79. package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
  80. package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
  81. package/src/__tests__/provider-connections-backfill.test.ts +138 -0
  82. package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
  83. package/src/__tests__/provider-registry-ollama.test.ts +8 -18
  84. package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
  85. package/src/__tests__/provider-usage-tracking.test.ts +9 -19
  86. package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
  87. package/src/__tests__/published-app-updater.test.ts +22 -16
  88. package/src/__tests__/registry.test.ts +5 -16
  89. package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
  90. package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
  91. package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
  92. package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
  93. package/src/__tests__/reveal-success-registry.test.ts +123 -0
  94. package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
  95. package/src/__tests__/secret-fixtures.ts +9 -0
  96. package/src/__tests__/server-history-render.test.ts +28 -0
  97. package/src/__tests__/skills.test.ts +9 -4
  98. package/src/__tests__/slack-share-routes.test.ts +0 -1
  99. package/src/__tests__/stt-stream-session.test.ts +6 -5
  100. package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
  101. package/src/__tests__/subagent-disposal.test.ts +2 -0
  102. package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
  103. package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
  104. package/src/__tests__/subagent-manager-notify.test.ts +2 -0
  105. package/src/__tests__/subagent-role-registry.test.ts +37 -0
  106. package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
  107. package/src/__tests__/subagent-terminal-message.test.ts +50 -0
  108. package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
  109. package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
  110. package/src/__tests__/telemetry-routes.test.ts +99 -19
  111. package/src/__tests__/tool-audit.test.ts +34 -4
  112. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  113. package/src/__tests__/tool-profiler.test.ts +72 -1
  114. package/src/__tests__/tool-result-spool.test.ts +49 -4
  115. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
  116. package/src/__tests__/ui-channel-variants.test.ts +108 -0
  117. package/src/__tests__/ui-shape-teaching.test.ts +255 -0
  118. package/src/__tests__/usage-attribution.test.ts +18 -41
  119. package/src/__tests__/user-plugin-loader.test.ts +4 -4
  120. package/src/__tests__/voice-config-update.test.ts +46 -0
  121. package/src/__tests__/voice-session-bridge.test.ts +216 -84
  122. package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
  123. package/src/agent/loop.ts +4 -3
  124. package/src/api/events/open-conversation.test.ts +64 -0
  125. package/src/api/events/open-conversation.ts +33 -0
  126. package/src/api/index.ts +6 -0
  127. package/src/api/responses/conversation-message.ts +5 -0
  128. package/src/apps/app-store.ts +25 -35
  129. package/src/bundler/app-bundler.ts +34 -48
  130. package/src/bundler/app-compiler.ts +39 -4
  131. package/src/bundler/bundle-scanner.ts +13 -0
  132. package/src/bundler/manifest.ts +1 -1
  133. package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
  134. package/src/calls/call-constants.ts +5 -0
  135. package/src/calls/call-controller.ts +100 -32
  136. package/src/calls/call-transport.ts +9 -0
  137. package/src/calls/media-stream-output.ts +107 -4
  138. package/src/calls/media-stream-server.ts +29 -9
  139. package/src/calls/media-stream-stt-session.ts +7 -1
  140. package/src/calls/media-turn-detector.ts +11 -1
  141. package/src/calls/voice-session-bridge.ts +84 -63
  142. package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
  143. package/src/cli/commands/__tests__/notifications.test.ts +24 -3
  144. package/src/cli/commands/config.help.ts +6 -6
  145. package/src/cli/commands/credentials.help.ts +13 -0
  146. package/src/cli/commands/credentials.ts +6 -1
  147. package/src/cli/commands/email.help.ts +7 -0
  148. package/src/cli/commands/email.ts +35 -1
  149. package/src/cli/commands/inference-providers.ts +168 -107
  150. package/src/cli/commands/inference.help.ts +118 -46
  151. package/src/cli/commands/memory/index.help.ts +23 -0
  152. package/src/cli/commands/memory/nodes.ts +146 -0
  153. package/src/cli/commands/notifications.help.ts +10 -10
  154. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
  155. package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
  156. package/src/cli/commands/oauth/connect.test.ts +126 -0
  157. package/src/cli/commands/oauth/connect.ts +29 -6
  158. package/src/cli/commands/oauth/index.help.ts +7 -1
  159. package/src/cli/commands/oauth/status.test.ts +69 -3
  160. package/src/cli/commands/oauth/status.ts +50 -12
  161. package/src/cli/commands/plugins.help.ts +13 -2
  162. package/src/cli/commands/plugins.ts +61 -7
  163. package/src/cli/commands/telemetry.help.ts +13 -0
  164. package/src/cli/commands/telemetry.ts +45 -5
  165. package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
  166. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
  167. package/src/cli/lib/bundled-marketplace.json +93 -36
  168. package/src/cli/lib/inspect-plugin.ts +5 -1
  169. package/src/cli/lib/install-from-github.ts +68 -4
  170. package/src/cli/lib/plugin-catalog-local.ts +6 -2
  171. package/src/cli/lib/plugin-fingerprint.ts +3 -3
  172. package/src/cli/lib/upgrade-plugin.ts +236 -35
  173. package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
  174. package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
  175. package/src/config/__tests__/profile-materialization.test.ts +1 -88
  176. package/src/config/bundled-skills/AGENTS.md +3 -30
  177. package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
  178. package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
  180. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  181. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
  182. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  183. package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
  184. package/src/config/bundled-skills/settings/TOOLS.json +3 -1
  185. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
  186. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
  187. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  188. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  189. package/src/config/call-site-defaults.ts +3 -3
  190. package/src/config/feature-flag-registry.json +24 -39
  191. package/src/config/llm-resolver.ts +88 -475
  192. package/src/config/profile-materialization.ts +11 -11
  193. package/src/config/schema.ts +93 -127
  194. package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
  195. package/src/config/schemas/__tests__/stt.test.ts +31 -3
  196. package/src/config/schemas/live-voice.ts +10 -4
  197. package/src/config/schemas/llm.ts +52 -15
  198. package/src/config/schemas/memory-lifecycle.ts +1 -1
  199. package/src/config/schemas/memory-retrospective.ts +8 -0
  200. package/src/config/schemas/memory-v2.ts +2 -2
  201. package/src/config/schemas/memory-v3.ts +1 -1
  202. package/src/config/schemas/services.ts +6 -3
  203. package/src/config/schemas/stt.ts +38 -21
  204. package/src/config/schemas/tts.ts +13 -18
  205. package/src/config/skills.ts +31 -21
  206. package/src/context/compactor.ts +44 -7
  207. package/src/context/post-turn-tool-result-truncation.ts +4 -2
  208. package/src/context/tool-result-spool.ts +26 -5
  209. package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
  210. package/src/conversations/message-consolidation.ts +22 -2
  211. package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
  212. package/src/daemon/app-source-watcher.ts +17 -23
  213. package/src/daemon/chat-credential-redaction.ts +1365 -0
  214. package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
  215. package/src/daemon/conversation-agent-loop.ts +71 -31
  216. package/src/daemon/conversation-error.ts +36 -14
  217. package/src/daemon/conversation-process.ts +22 -0
  218. package/src/daemon/conversation-store.ts +35 -0
  219. package/src/daemon/conversation-surfaces.ts +25 -25
  220. package/src/daemon/conversation-tool-setup.ts +33 -7
  221. package/src/daemon/conversation.ts +55 -5
  222. package/src/daemon/handlers/shared.ts +33 -2
  223. package/src/daemon/lifecycle.ts +4 -4
  224. package/src/daemon/message-types/conversations.ts +6 -17
  225. package/src/daemon/providers-setup.ts +8 -0
  226. package/src/daemon/tool-setup-types.ts +7 -1
  227. package/src/daemon/wake-conversation-ops.ts +10 -1
  228. package/src/hooks/types.ts +9 -0
  229. package/src/ipc/__tests__/email-ipc.test.ts +90 -0
  230. package/src/ipc/gateway-client.test.ts +59 -0
  231. package/src/ipc/gateway-client.ts +70 -27
  232. package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
  233. package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
  234. package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
  235. package/src/live-voice/__tests__/protocol.test.ts +122 -0
  236. package/src/live-voice/live-voice-session.ts +443 -40
  237. package/src/live-voice/protocol.ts +143 -1
  238. package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
  239. package/src/monitoring/plugin-source-watch.ts +3 -62
  240. package/src/notifications/README.md +1 -1
  241. package/src/permissions/checker.ts +8 -4
  242. package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
  243. package/src/persistence/conversation-crud.ts +104 -2
  244. package/src/persistence/db-init.ts +14 -3
  245. package/src/persistence/job-handlers/cleanup.ts +15 -7
  246. package/src/persistence/llm-request-log-store.ts +88 -52
  247. package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
  248. package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
  249. package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
  250. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
  251. package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
  252. package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
  253. package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
  254. package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
  255. package/src/persistence/migrations/helpers/relocation.ts +44 -1
  256. package/src/persistence/migrations/run-migrations.ts +25 -1
  257. package/src/persistence/schema/infrastructure.ts +9 -0
  258. package/src/persistence/schema/memory-core.ts +3 -0
  259. package/src/persistence/schema/memory-injection.ts +2 -0
  260. package/src/persistence/steps.ts +36 -0
  261. package/src/platform/client.test.ts +1 -44
  262. package/src/platform/client.ts +10 -20
  263. package/src/platform/consent-cache.test.ts +89 -23
  264. package/src/platform/consent-cache.ts +71 -33
  265. package/src/plugin-api/constants.ts +12 -0
  266. package/src/plugin-api/conversation-turn.ts +37 -14
  267. package/src/plugin-api/index.ts +11 -1
  268. package/src/plugin-api/resolve-credential.ts +75 -0
  269. package/src/plugin-api/vision-support.test.ts +8 -19
  270. package/src/plugin-api/vision-support.ts +26 -17
  271. package/src/plugins/collect-source-versions.ts +77 -0
  272. package/src/plugins/defaults/compaction/compact.ts +6 -0
  273. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
  275. package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
  276. package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
  277. package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
  278. package/src/plugins/defaults/index.ts +8 -1
  279. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
  280. package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
  281. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
  282. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
  283. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
  284. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
  285. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
  286. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
  287. package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
  288. package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
  289. package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
  290. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
  291. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
  292. package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
  293. package/src/plugins/defaults/memory/graph/store.ts +114 -0
  294. package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
  295. package/src/plugins/defaults/memory/graph/tools.ts +47 -13
  296. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
  297. package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
  298. package/src/plugins/defaults/memory/memory-db.ts +3 -2
  299. package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
  300. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
  301. package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
  302. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
  303. package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
  304. package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
  305. package/src/plugins/defaults/memory/path-containment.ts +21 -0
  306. package/src/plugins/defaults/memory/prompt-override.ts +51 -13
  307. package/src/plugins/defaults/memory/tools.test.ts +34 -0
  308. package/src/plugins/defaults/memory/tools.ts +12 -2
  309. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
  310. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
  311. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
  312. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
  313. package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
  314. package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
  315. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
  316. package/src/plugins/defaults/memory/v2/migration.ts +46 -16
  317. package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
  318. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
  319. package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
  320. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
  321. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
  322. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
  323. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
  324. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
  325. package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
  326. package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
  327. package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
  328. package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
  329. package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
  330. package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
  331. package/src/plugins/defaults/memory/v3/prune.ts +17 -11
  332. package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
  333. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
  334. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
  335. package/src/plugins/external-plugin-loader.ts +15 -15
  336. package/src/plugins/mtime-cache.ts +47 -0
  337. package/src/plugins/pipeline.ts +9 -1
  338. package/src/plugins/plugin-execution-context.ts +44 -0
  339. package/src/plugins/plugin-tree-walk.ts +31 -24
  340. package/src/plugins/source-fingerprint.ts +3 -4
  341. package/src/prompts/normalize-onboarding.ts +12 -0
  342. package/src/prompts/persona-resolver.ts +8 -0
  343. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
  344. package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
  345. package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
  346. package/src/providers/__tests__/retry-callsite.test.ts +235 -215
  347. package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
  348. package/src/providers/atlascloud/client.ts +10 -49
  349. package/src/providers/baseten/client.ts +43 -0
  350. package/src/providers/call-site-routing.ts +26 -13
  351. package/src/providers/connection-resolution.ts +9 -11
  352. package/src/providers/fetch-provider-catalog.ts +4 -2
  353. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
  354. package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
  355. package/src/providers/inference/adapter-factory.ts +27 -6
  356. package/src/providers/inference/auth.ts +29 -1
  357. package/src/providers/inference/backfill.ts +61 -10
  358. package/src/providers/inference/connection-availability.ts +3 -2
  359. package/src/providers/inference/resolve-auth.ts +9 -1
  360. package/src/providers/model-catalog.ts +37 -0
  361. package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
  362. package/src/providers/openai/api-key-validation.ts +70 -0
  363. package/src/providers/retry.ts +2 -0
  364. package/src/providers/types.ts +11 -4
  365. package/src/providers/vellum-model-routing.test.ts +26 -0
  366. package/src/providers/vellum-model-routing.ts +30 -0
  367. package/src/providers/voice-error-copy.ts +47 -0
  368. package/src/runtime/agent-wake.ts +40 -24
  369. package/src/runtime/for-chat-mint-registry.ts +118 -0
  370. package/src/runtime/reveal-nonce.ts +49 -0
  371. package/src/runtime/reveal-success-registry.ts +306 -0
  372. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
  373. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
  374. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
  375. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
  376. package/src/runtime/routes/app-management-routes.ts +152 -45
  377. package/src/runtime/routes/app-routes.ts +16 -77
  378. package/src/runtime/routes/canned-message-complete.ts +16 -16
  379. package/src/runtime/routes/conversation-management-routes.ts +17 -14
  380. package/src/runtime/routes/conversation-query-routes.ts +54 -6
  381. package/src/runtime/routes/conversation-routes.ts +48 -2
  382. package/src/runtime/routes/credential-routes.ts +116 -3
  383. package/src/runtime/routes/email-routes.ts +17 -1
  384. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
  385. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
  386. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
  387. package/src/runtime/routes/inference-send-routes.ts +1 -1
  388. package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
  389. package/src/runtime/routes/plugins-routes.ts +37 -6
  390. package/src/runtime/routes/publish-routes.ts +15 -18
  391. package/src/runtime/routes/schedule-worker-routes.ts +9 -0
  392. package/src/runtime/routes/secret-routes.ts +10 -0
  393. package/src/runtime/routes/telemetry-routes.ts +149 -45
  394. package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
  395. package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
  396. package/src/schedule/schedule-store.ts +12 -1
  397. package/src/schedule/schedule-timezone.ts +63 -0
  398. package/src/schedule/scheduler.ts +100 -6
  399. package/src/schedule/worker-control.ts +19 -0
  400. package/src/security/auth-fallback-events-store.ts +7 -6
  401. package/src/security/secret-scanner.ts +26 -1
  402. package/src/services/published-app-updater.ts +6 -11
  403. package/src/stt/stt-stream-session.ts +36 -16
  404. package/src/subagent/manager.ts +34 -8
  405. package/src/telemetry/AGENTS.md +30 -1
  406. package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
  407. package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
  408. package/src/telemetry/config-setting-snapshot.ts +44 -10
  409. package/src/telemetry/telemetry-event-sources.test.ts +124 -30
  410. package/src/telemetry/telemetry-event-sources.ts +137 -83
  411. package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
  412. package/src/telemetry/telemetry-events-outbox.ts +60 -9
  413. package/src/telemetry/telemetry-wire-source.json +1 -1
  414. package/src/telemetry/telemetry-wire-validation.ts +39 -2
  415. package/src/telemetry/telemetry-wire.generated.ts +8 -0
  416. package/src/telemetry/tool-audit.ts +15 -9
  417. package/src/telemetry/tool-executed-events-store.test.ts +1 -1
  418. package/src/telemetry/turn-events-store.ts +20 -0
  419. package/src/telemetry/types.ts +45 -12
  420. package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
  421. package/src/telemetry/usage-telemetry-reporter.ts +117 -18
  422. package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
  423. package/src/telemetry/watchdog-direct-emit.ts +11 -6
  424. package/src/tools/apps/executors.ts +11 -36
  425. package/src/tools/executor.ts +14 -1
  426. package/src/tools/host-terminal/host-shell.ts +6 -2
  427. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
  428. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
  429. package/src/tools/network/web-fetch.ts +6 -2
  430. package/src/tools/skills/sandbox-runner.ts +5 -2
  431. package/src/tools/subagent/spawn.ts +7 -11
  432. package/src/tools/terminal/shell.ts +5 -2
  433. package/src/tools/tool-manifest.ts +0 -2
  434. package/src/tools/tool-profiler.ts +37 -6
  435. package/src/tools/ui-surface/channel-variants.ts +101 -0
  436. package/src/tools/ui-surface/definitions.ts +23 -67
  437. package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
  438. package/src/tts/__tests__/provider-adapters.test.ts +18 -0
  439. package/src/tts/provider-catalog.ts +2 -4
  440. package/src/tts/providers/deepgram-provider.ts +10 -1
  441. package/src/types/onboarding-context.ts +8 -0
  442. package/src/usage/attribution.ts +18 -112
  443. package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
  444. package/src/util/provider-error-patterns.ts +7 -1
  445. package/src/util/worker-process.ts +105 -0
  446. package/src/watcher/__tests__/telemetry.test.ts +17 -4
  447. package/src/watcher/telemetry.ts +5 -5
  448. package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
  449. package/src/workspace/migrations/registry.ts +2 -0
  450. package/src/workspace/provider-commit-message-generator.ts +6 -4
  451. package/src/__tests__/app-open-proxy.test.ts +0 -67
  452. package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
  453. package/src/onboarding/onboarding-research-events-store.ts +0 -104
  454. package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
  455. package/src/tools/apps/definitions.ts +0 -73
  456. package/src/tools/apps/open-proxy.ts +0 -43
@@ -1,6 +1,4 @@
1
- import { getEffectiveProfiles } from "../config/default-profile-catalog.js";
2
1
  import {
3
- isOverrideOrDefaultResolutionEnabled,
4
2
  resolveCallSiteConfig,
5
3
  selectWinningProfile,
6
4
  } from "../config/llm-resolver.js";
@@ -87,11 +85,17 @@ export function toAttributionColumns(
87
85
  * values are capped so later forwarding cannot create unbounded headers.
88
86
  */
89
87
  export function sanitizeUsageMetadataValue(value: unknown): string | null {
90
- if (typeof value !== "string") return null;
88
+ if (typeof value !== "string") {
89
+ return null;
90
+ }
91
91
 
92
92
  const trimmed = value.trim();
93
- if (trimmed.length === 0) return null;
94
- if (containsControlCharacter(trimmed)) return null;
93
+ if (trimmed.length === 0) {
94
+ return null;
95
+ }
96
+ if (containsControlCharacter(trimmed)) {
97
+ return null;
98
+ }
95
99
 
96
100
  return safeStringSlice(trimmed, 0, MAX_METADATA_VALUE_LENGTH);
97
101
  }
@@ -140,19 +144,15 @@ export function resolveUsageAttribution(
140
144
  const callSiteProfile = normalizeProfileId(
141
145
  llm.callSites?.[callSite]?.profile,
142
146
  );
143
- // Under override-or-default semantics the resolver's own winner selection
144
- // is the single source of truth for which profile applied — attribution
145
- // must never re-derive precedence and drift from dispatch.
146
- const profile = isOverrideOrDefaultResolutionEnabled()
147
- ? appliedProfileFromWinnerSelection(callSite, llm, overrideProfile, input)
148
- : resolveAppliedProfile({
149
- callSite,
150
- profiles: getEffectiveProfiles(llm.profiles),
151
- activeProfile,
152
- overrideProfile,
153
- forceOverrideProfile: input.forceOverrideProfile === true,
154
- callSiteProfile,
155
- });
147
+ // The resolver's own winner selection is the single source of truth for which
148
+ // profile applied attribution must never re-derive precedence and drift
149
+ // from dispatch.
150
+ const profile = appliedProfileFromWinnerSelection(
151
+ callSite,
152
+ llm,
153
+ overrideProfile,
154
+ input,
155
+ );
156
156
 
157
157
  return {
158
158
  callSite,
@@ -194,100 +194,6 @@ function appliedProfileFromWinnerSelection(
194
194
  };
195
195
  }
196
196
 
197
- function resolveAppliedProfile(input: {
198
- callSite: LLMCallSite;
199
- profiles: Record<string, unknown>;
200
- activeProfile: string | null;
201
- overrideProfile: string | null;
202
- forceOverrideProfile: boolean;
203
- callSiteProfile: string | null;
204
- }): Pick<UsageAttributionSnapshot, "appliedProfile" | "profileSource"> {
205
- if (input.callSite === "mainAgent") {
206
- if (
207
- input.overrideProfile != null &&
208
- input.profiles[input.overrideProfile] != null
209
- ) {
210
- return {
211
- appliedProfile: input.overrideProfile,
212
- profileSource: "conversation",
213
- };
214
- }
215
-
216
- if (
217
- input.activeProfile != null &&
218
- input.profiles[input.activeProfile] != null
219
- ) {
220
- return {
221
- appliedProfile: input.activeProfile,
222
- profileSource: "active",
223
- };
224
- }
225
-
226
- if (
227
- input.callSiteProfile != null &&
228
- input.profiles[input.callSiteProfile] != null
229
- ) {
230
- return {
231
- appliedProfile: input.callSiteProfile,
232
- profileSource: "call_site",
233
- };
234
- }
235
-
236
- return {
237
- appliedProfile: null,
238
- profileSource: "default",
239
- };
240
- }
241
-
242
- // Forced override floats above the call-site profile (the resolver's
243
- // `forceOverrideProfile` escape hatch), so it wins attribution too.
244
- if (
245
- input.forceOverrideProfile &&
246
- input.overrideProfile != null &&
247
- input.profiles[input.overrideProfile] != null
248
- ) {
249
- return {
250
- appliedProfile: input.overrideProfile,
251
- profileSource: "conversation",
252
- };
253
- }
254
-
255
- if (
256
- input.callSiteProfile != null &&
257
- input.profiles[input.callSiteProfile] != null
258
- ) {
259
- return {
260
- appliedProfile: input.callSiteProfile,
261
- profileSource: "call_site",
262
- };
263
- }
264
-
265
- if (
266
- input.overrideProfile != null &&
267
- input.profiles[input.overrideProfile] != null
268
- ) {
269
- return {
270
- appliedProfile: input.overrideProfile,
271
- profileSource: "conversation",
272
- };
273
- }
274
-
275
- if (
276
- input.activeProfile != null &&
277
- input.profiles[input.activeProfile] != null
278
- ) {
279
- return {
280
- appliedProfile: input.activeProfile,
281
- profileSource: "active",
282
- };
283
- }
284
-
285
- return {
286
- appliedProfile: null,
287
- profileSource: "default",
288
- };
289
- }
290
-
291
197
  function normalizeProfileId(value: string | null | undefined): string | null {
292
198
  return value ?? null;
293
199
  }
@@ -1,5 +1,8 @@
1
1
  /**
2
- * Shared MIME type guessing for Gmail attachment support.
2
+ * Best-effort MIME type inference from a file path's extension.
3
+ *
4
+ * Used to label outbound attachments (Gmail/Outlook multipart parts, native
5
+ * email attachments) when the source is a local file with no declared type.
3
6
  */
4
7
 
5
8
  import { extname } from "node:path";
@@ -14,8 +14,14 @@ export const VISION_NOT_SUPPORTED_PATTERNS = [
14
14
  /multi-?modal.*not.*support/i,
15
15
  ];
16
16
 
17
- // Vendor-neutral (OpenRouter/Anthropic-style) credit-exhaustion prose.
17
+ // Vendor-neutral (OpenRouter/Anthropic-style) credit-exhaustion prose. Also
18
+ // covers per-key spend caps: OpenRouter returns 403 "Key limit exceeded" when a
19
+ // key's configured credit limit is reached — a billing/budget condition that
20
+ // routes to the billing classification alongside other credit exhaustion. The
21
+ // phrase is anchored on the contiguous "key limit" so it cannot swallow
22
+ // rate-limit prose ("rate limit exceeded") or generic invalid-key text.
18
23
  export const INSUFFICIENT_CREDITS_PATTERNS = [
19
24
  /credit balance is too low/i,
20
25
  /insufficient.*credits?/i,
26
+ /key limit (?:has been )?(?:exceeded|reached)/i,
21
27
  ];
@@ -273,3 +273,108 @@ export function stopWorkerProcess(pidPath: string): WorkerProcessStatus {
273
273
  }
274
274
  return current;
275
275
  }
276
+
277
+ // ---------------------------------------------------------------------------
278
+ // Liveness supervisor
279
+ // ---------------------------------------------------------------------------
280
+
281
+ /**
282
+ * A liveness supervisor that probes a worker process and respawns it when it
283
+ * has died, subject to exponential backoff and a suppression hook. Detects
284
+ * process *death* (via the PID-file probe) — not a worker that is alive but
285
+ * wedged.
286
+ */
287
+ export interface WorkerSupervisor {
288
+ /** Probe liveness; respawn if dead (subject to backoff/suppression). Never throws. */
289
+ ensureAlive(now?: number): Promise<void>;
290
+ /**
291
+ * Stop supervising: no further respawns, and a respawn already in flight is
292
+ * killed via {@link WorkerSupervisorOptions.killChild} when it resolves.
293
+ */
294
+ dispose(): void;
295
+ }
296
+
297
+ export interface WorkerSupervisorOptions {
298
+ label: string;
299
+ probe: () => WorkerProcessStatus;
300
+ respawn: () => Promise<{ pid: number; alreadyRunning: boolean }>;
301
+ /**
302
+ * Kill a child respawned after {@link WorkerSupervisor.dispose} was called
303
+ * (closes the shutdown race where a respawn was already in flight).
304
+ */
305
+ killChild?: (pid: number) => void;
306
+ /**
307
+ * When it returns true, `ensureAlive` skips respawning — e.g. an operator
308
+ * administratively stopped the worker and the watchdog must not undo that.
309
+ */
310
+ isSuppressed?: () => boolean;
311
+ /** Fired after a respawn actually started a new process (not `alreadyRunning`). */
312
+ onRespawn?: (pid: number) => void;
313
+ /** Fires once when consecutiveFailures first reaches the threshold. */
314
+ onPersistentFailure?: (consecutiveFailures: number, err: unknown) => void;
315
+ minBackoffMs?: number; // default 15_000 (one tick)
316
+ maxBackoffMs?: number; // default 300_000 (5 min)
317
+ persistentFailureThreshold?: number; // default 3
318
+ }
319
+
320
+ export function createWorkerSupervisor(
321
+ opts: WorkerSupervisorOptions,
322
+ ): WorkerSupervisor {
323
+ let consecutiveFailures = 0;
324
+ let nextAttemptAt = 0;
325
+ let inFlight = false;
326
+ let stopping = false;
327
+ const minBackoff = opts.minBackoffMs ?? 15_000;
328
+ const maxBackoff = opts.maxBackoffMs ?? 300_000;
329
+ const threshold = opts.persistentFailureThreshold ?? 3;
330
+
331
+ return {
332
+ async ensureAlive(now = Date.now()): Promise<void> {
333
+ if (stopping || inFlight) {
334
+ return;
335
+ }
336
+ // Operator stop wins over the watchdog — never respawn while suppressed.
337
+ if (opts.isSuppressed?.()) {
338
+ return;
339
+ }
340
+ if (opts.probe().status === "running") {
341
+ consecutiveFailures = 0;
342
+ nextAttemptAt = 0;
343
+ return;
344
+ }
345
+ if (now < nextAttemptAt) {
346
+ return; // in a backoff window after a failed respawn
347
+ }
348
+ inFlight = true;
349
+ try {
350
+ const { pid, alreadyRunning } = await opts.respawn();
351
+ consecutiveFailures = 0;
352
+ nextAttemptAt = 0;
353
+ if (stopping) {
354
+ // Disposed mid-respawn: kill the child we just created so it is not
355
+ // orphaned past shutdown.
356
+ opts.killChild?.(pid);
357
+ return;
358
+ }
359
+ if (!alreadyRunning) {
360
+ opts.onRespawn?.(pid);
361
+ }
362
+ } catch (err) {
363
+ consecutiveFailures += 1;
364
+ const backoff = Math.min(
365
+ maxBackoff,
366
+ minBackoff * 2 ** (consecutiveFailures - 1),
367
+ );
368
+ nextAttemptAt = now + backoff;
369
+ if (consecutiveFailures === threshold) {
370
+ opts.onPersistentFailure?.(consecutiveFailures, err);
371
+ }
372
+ } finally {
373
+ inFlight = false;
374
+ }
375
+ },
376
+ dispose(): void {
377
+ stopping = true;
378
+ },
379
+ };
380
+ }
@@ -9,11 +9,12 @@
9
9
  import { beforeEach, describe, expect, mock, test } from "bun:test";
10
10
 
11
11
  import type { LifecycleEvent } from "../../persistence/lifecycle-events-store.js";
12
+ import type { ConsentState } from "../../platform/consent-cache.js";
12
13
 
13
14
  const checkpoints = new Map<string, string>();
14
15
  const recordedEvents: string[] = [];
15
16
  let fakeEnabledWatchers: Array<{ providerId: string }> = [];
16
- let shareAnalytics = true;
17
+ let shareAnalytics: ConsentState = true;
17
18
  const recordAndReturnEvent = (name: string): LifecycleEvent => {
18
19
  recordedEvents.push(name);
19
20
  return { id: "evt-1", eventName: name, createdAt: 0 };
@@ -32,7 +33,7 @@ mock.module("../../persistence/lifecycle-events-store.js", () => ({
32
33
  }));
33
34
 
34
35
  mock.module("../../platform/consent-cache.js", () => ({
35
- getCachedShareAnalytics: () => shareAnalytics,
36
+ getRawShareAnalytics: () => shareAnalytics,
36
37
  }));
37
38
 
38
39
  mock.module("../watcher-store.js", () => ({
@@ -135,10 +136,10 @@ describe("recordWatcherInventoryIfDue", () => {
135
136
  expect(checkpoints.get(INVENTORY_KEY)).toBe(String(BASE + 3));
136
137
  });
137
138
 
138
- test("advances the checkpoint when consent is off", () => {
139
+ test("advances the checkpoint when consent is a confirmed opt-out", () => {
139
140
  fakeEnabledWatchers = [{ providerId: "gmail" }];
140
141
  shareAnalytics = false;
141
- // Consent-off skip: the store records nothing and returns null.
142
+ // Confirmed-opt-out skip: the store records nothing and returns null.
142
143
  recordImpl = () => null;
143
144
 
144
145
  recordWatcherInventoryIfDue(BASE + 4);
@@ -146,6 +147,18 @@ describe("recordWatcherInventoryIfDue", () => {
146
147
  expect(recordedEvents).toEqual([]);
147
148
  expect(checkpoints.get(INVENTORY_KEY)).toBe(String(BASE + 4));
148
149
  });
150
+
151
+ test("leaves the checkpoint for retry when the store returns null while consent is unknown", () => {
152
+ fakeEnabledWatchers = [{ providerId: "gmail" }];
153
+ shareAnalytics = "unknown";
154
+ // Unknown consent never drops at record time, so a null here means the
155
+ // telemetry DB is unavailable — retry, don't skip a day.
156
+ recordImpl = () => null;
157
+
158
+ recordWatcherInventoryIfDue(BASE + 4);
159
+
160
+ expect(checkpoints.get(INVENTORY_KEY)).toBeUndefined();
161
+ });
149
162
  });
150
163
 
151
164
  describe("recordWatcherLlmProcessed", () => {
@@ -24,7 +24,7 @@ import {
24
24
  setMemoryCheckpoint,
25
25
  } from "../persistence/checkpoints.js";
26
26
  import { recordLifecycleEvent } from "../persistence/lifecycle-events-store.js";
27
- import { getCachedShareAnalytics } from "../platform/consent-cache.js";
27
+ import { getRawShareAnalytics } from "../platform/consent-cache.js";
28
28
  import { getLogger } from "../util/logger.js";
29
29
  import { listWatchers } from "./watcher-store.js";
30
30
 
@@ -39,9 +39,9 @@ let warnedInventoryDbUnavailable = false;
39
39
  * Record one `watcher_enabled:<providerId>` lifecycle event per enabled
40
40
  * watcher, at most once per 24h. Called from every watcher tick; the
41
41
  * checkpoint advances only after the events are recorded, so a transient
42
- * storage failure (a throw, or a degraded-mode `null` while analytics
43
- * consent is granted) retries on the next tick instead of skipping a day.
44
- * A consent-off `null` is a deliberate skip and still advances. Retries
42
+ * storage failure (a throw, or a degraded-mode `null` while consent is not
43
+ * a confirmed opt-out) retries on the next tick instead of skipping a day.
44
+ * A confirmed-opt-out `null` is a deliberate skip and still advances. Retries
45
45
  * can duplicate events for watchers recorded before the failure point —
46
46
  * an acceptable overcount for best-effort telemetry, where losing a full
47
47
  * day's inventory would not be.
@@ -54,7 +54,7 @@ export function recordWatcherInventoryIfDue(now: number): void {
54
54
  const recorded = recordLifecycleEvent(
55
55
  `watcher_enabled:${watcher.providerId}`,
56
56
  );
57
- if (recorded === null && getCachedShareAnalytics()) {
57
+ if (recorded === null && getRawShareAnalytics() !== false) {
58
58
  if (!warnedInventoryDbUnavailable) {
59
59
  warnedInventoryDbUnavailable = true;
60
60
  log.warn(
@@ -0,0 +1,61 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ /**
7
+ * Strip the now-removed `services.web-fetch.mode` field from existing
8
+ * `config.json` files. Web fetch has no managed proxy, so the field never
9
+ * governed anything — provider is the only axis — and the schema no longer
10
+ * recognizes it.
11
+ *
12
+ * Purely tidying: the schema strips unknown keys, so a lingering `mode` is
13
+ * inert at runtime. This exists so upgraded configs on disk match the schema
14
+ * rather than carrying a dead key indefinitely.
15
+ *
16
+ * Idempotent: re-running when `mode` is already absent is a no-op.
17
+ */
18
+ export const dropWebFetchModeMigration: WorkspaceMigration = {
19
+ id: "131-drop-web-fetch-mode",
20
+ description:
21
+ "Strip services.web-fetch.mode from config.json (mode field removed from schema)",
22
+ run(workspaceDir: string): void {
23
+ const configPath = join(workspaceDir, "config.json");
24
+ if (!existsSync(configPath)) return;
25
+
26
+ let config: Record<string, unknown>;
27
+ try {
28
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
29
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
30
+ config = raw as Record<string, unknown>;
31
+ } catch {
32
+ return;
33
+ }
34
+
35
+ const services = config.services;
36
+ if (
37
+ services === null ||
38
+ typeof services !== "object" ||
39
+ Array.isArray(services)
40
+ )
41
+ return;
42
+
43
+ const webFetch = (services as Record<string, unknown>)["web-fetch"];
44
+ if (
45
+ webFetch === null ||
46
+ typeof webFetch !== "object" ||
47
+ Array.isArray(webFetch)
48
+ )
49
+ return;
50
+
51
+ const webFetchObj = webFetch as Record<string, unknown>;
52
+ if (!("mode" in webFetchObj)) return;
53
+
54
+ delete webFetchObj.mode;
55
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
56
+ },
57
+ down(_workspaceDir: string): void {
58
+ // Forward-only: re-adding mode would reintroduce a field the schema no
59
+ // longer recognizes.
60
+ },
61
+ };
@@ -127,6 +127,7 @@ import { stripManagedProfileBodiesMigration } from "./126-strip-managed-profile-
127
127
  import { backfillDefaultProviderMigration } from "./127-backfill-default-provider.js";
128
128
  import { repairStaleOpenrouterGrokModelIdsMigration } from "./128-repair-stale-openrouter-grok-model-ids.js";
129
129
  import { removeAnalyzeConversationConfigMigration } from "./129-remove-analyze-conversation-config.js";
130
+ import { dropWebFetchModeMigration } from "./131-drop-web-fetch-mode.js";
130
131
  import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
131
132
  import type { WorkspaceMigration } from "./types.js";
132
133
 
@@ -265,4 +266,5 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
265
266
  backfillDefaultProviderMigration,
266
267
  repairStaleOpenrouterGrokModelIdsMigration,
267
268
  removeAnalyzeConversationConfigMigration,
269
+ dropWebFetchModeMigration,
268
270
  ];
@@ -67,7 +67,9 @@ class ProviderCommitMessageGenerator {
67
67
  private isBreakerOpen(): boolean {
68
68
  const config = getConfig();
69
69
  const { openAfterFailures } = config.workspaceGit.commitMessageLLM.breaker;
70
- if (this.consecutiveFailures < openAfterFailures) return false;
70
+ if (this.consecutiveFailures < openAfterFailures) {
71
+ return false;
72
+ }
71
73
  return Date.now() < this.nextAllowedAttemptMs;
72
74
  }
73
75
 
@@ -122,7 +124,7 @@ class ProviderCommitMessageGenerator {
122
124
 
123
125
  // Step 2: Resolve configured provider via the commit-message call site,
124
126
  // so model + maxTokens + temperature come from `llm.callSites.commitMessage`
125
- // (with `llm.default` as the fallback). Operational fields (`enabled`,
127
+ // (with the shipped call-site default as the fallback). Operational fields (`enabled`,
126
128
  // `timeoutMs`, `breaker`, `maxFilesInPrompt`, `maxDiffBytes`,
127
129
  // `minRemainingTurnBudgetMs`) remain on `workspaceGit.commitMessageLLM`
128
130
  // and are read above. If nothing is resolvable, differentiate likely
@@ -241,8 +243,8 @@ class ProviderCommitMessageGenerator {
241
243
  signal: ac.signal,
242
244
  config: {
243
245
  // `callSite` lets the provider resolve model, max_tokens, and
244
- // temperature from `llm.callSites.commitMessage` (with
245
- // `llm.default` as the fallback). Operational fields
246
+ // temperature from `llm.callSites.commitMessage` (with the
247
+ // shipped call-site default as the fallback). Operational fields
246
248
  // (`enabled`, `timeoutMs`, `breaker`, `maxFilesInPrompt`,
247
249
  // `maxDiffBytes`, `minRemainingTurnBudgetMs`) remain on
248
250
  // `workspaceGit.commitMessageLLM` and are read above.
@@ -1,67 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import type { ProxyResolver } from "../tools/apps/executors.js";
4
- import { openAppViaSurface } from "../tools/apps/open-proxy.js";
5
-
6
- describe("openAppViaSurface", () => {
7
- test("returns proxy result content on success", async () => {
8
- const resolver: ProxyResolver = async (_name, _input) => ({
9
- content: "opened-surface-123",
10
- isError: false,
11
- });
12
-
13
- const result = await openAppViaSurface("app-1", resolver);
14
- expect(result).toBe("opened-surface-123");
15
- });
16
-
17
- test("passes app_id and extraInput to the resolver", async () => {
18
- let capturedName: string | undefined;
19
- let capturedInput: Record<string, unknown> | undefined;
20
-
21
- const resolver: ProxyResolver = async (name, input) => {
22
- capturedName = name;
23
- capturedInput = input;
24
- return { content: "ok", isError: false };
25
- };
26
-
27
- await openAppViaSurface("my-app", resolver, {
28
- preview: { title: "Hello" },
29
- });
30
-
31
- expect(capturedName).toBe("app_open");
32
- expect(capturedInput).toEqual({
33
- app_id: "my-app",
34
- preview: { title: "Hello" },
35
- });
36
- });
37
-
38
- test("returns informational message when resolver is undefined", async () => {
39
- const result = await openAppViaSurface("app-1", undefined);
40
- expect(result).toBe(
41
- "App created but could not be opened (no connected client). Use app_open to open it manually.",
42
- );
43
- });
44
-
45
- test("returns fallback text when resolver throws", async () => {
46
- const resolver: ProxyResolver = async () => {
47
- throw new Error("connection lost");
48
- };
49
-
50
- const result = await openAppViaSurface("app-1", resolver);
51
- expect(result).toBe(
52
- "Failed to auto-open app. Use app_open to open it manually.",
53
- );
54
- });
55
-
56
- test("omits extraInput fields when not provided", async () => {
57
- let capturedInput: Record<string, unknown> | undefined;
58
-
59
- const resolver: ProxyResolver = async (_name, input) => {
60
- capturedInput = input;
61
- return { content: "ok", isError: false };
62
- };
63
-
64
- await openAppViaSurface("app-2", resolver);
65
- expect(capturedInput).toEqual({ app_id: "app-2" });
66
- });
67
- });