@mycadet/assistant 0.12.9 → 0.12.10

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 (651) hide show
  1. package/Dockerfile +0 -8
  2. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +1 -1
  3. package/openapi.yaml +62 -910
  4. package/package.json +3 -7
  5. package/scripts/build-test-fixtures.ts +1 -1
  6. package/scripts/generate-openapi.ts +22 -6
  7. package/scripts/postinstall.ts +4 -10
  8. package/scripts/run-tests.ts +0 -21
  9. package/src/__tests__/adaptive-thinking-repair.test.ts +19 -57
  10. package/src/__tests__/agent-loop.test.ts +0 -76
  11. package/src/__tests__/anthropic-provider.test.ts +6 -6
  12. package/src/__tests__/assert-not-live-db.ts +1 -1
  13. package/src/__tests__/assistant-id-boundary-guard.test.ts +3 -3
  14. package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
  15. package/src/__tests__/attachments-store.test.ts +1 -1
  16. package/src/__tests__/audit-log-rotation.test.ts +1 -1
  17. package/src/__tests__/btw-routes.test.ts +1 -1
  18. package/src/__tests__/byok-default-profile-ensure.test.ts +21 -27
  19. package/src/__tests__/call-conversation-messages.test.ts +0 -5
  20. package/src/__tests__/call-recovery.test.ts +2 -7
  21. package/src/__tests__/call-routes-http.test.ts +0 -12
  22. package/src/__tests__/call-store.test.ts +2 -7
  23. package/src/__tests__/catalog-files.test.ts +7 -5
  24. package/src/__tests__/checker.test.ts +1 -1
  25. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +4 -4
  26. package/src/__tests__/cloud-client-verify-credential.test.ts +0 -4
  27. package/src/__tests__/cloud-credential-catalog-cli.test.ts +14 -14
  28. package/src/__tests__/cloud-profile-guard.test.ts +48 -401
  29. package/src/__tests__/compaction-events.test.ts +2 -2
  30. package/src/__tests__/config-loader-backfill.test.ts +74 -163
  31. package/src/__tests__/config-loader-cloud-defaults.test.ts +11 -11
  32. package/src/__tests__/config-loader-corrupt.test.ts +10 -10
  33. package/src/__tests__/config-loader-quarantine-notice.test.ts +7 -7
  34. package/src/__tests__/config-loader-validation-reset-notice.test.ts +11 -11
  35. package/src/__tests__/config-schema.test.ts +26 -27
  36. package/src/__tests__/config-set-route.test.ts +2 -6
  37. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +28 -300
  38. package/src/__tests__/conversation-agent-loop-overflow.test.ts +39 -246
  39. package/src/__tests__/conversation-agent-loop.test.ts +2 -58
  40. package/src/__tests__/conversation-clear-safety.test.ts +0 -30
  41. package/src/__tests__/conversation-inference-profile-route.test.ts +1 -1
  42. package/src/__tests__/conversation-queue.test.ts +1 -1
  43. package/src/__tests__/conversation-runtime-assembly.test.ts +1 -1
  44. package/src/__tests__/conversation-skill-tools.test.ts +4 -444
  45. package/src/__tests__/conversation-slash-commands.test.ts +1 -1
  46. package/src/__tests__/conversation-store.test.ts +1 -84
  47. package/src/__tests__/conversation-surface-state.test.ts +0 -16
  48. package/src/__tests__/credential-health-service.test.ts +14 -1
  49. package/src/__tests__/credential-record-retire.test.ts +1 -1
  50. package/src/__tests__/credential-record-write-through.test.ts +1 -1
  51. package/src/__tests__/credential-security-invariants.test.ts +0 -145
  52. package/src/__tests__/custom-profile-ensure.test.ts +1 -1
  53. package/src/__tests__/default-profile-catalog-fallback.test.ts +5 -15
  54. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -2
  55. package/src/__tests__/dictation-profile-store.test.ts +6 -6
  56. package/src/__tests__/disk-usage.test.ts +28 -164
  57. package/src/__tests__/docker-signing-key-bootstrap.test.ts +1 -1
  58. package/src/__tests__/embedding-cloud-proxy-selection.test.ts +1 -1
  59. package/src/__tests__/emit-event-signal.test.ts +1 -1
  60. package/src/__tests__/gateway-client-cloud-outbound.test.ts +3 -3
  61. package/src/__tests__/gateway-only-enforcement.test.ts +1 -1
  62. package/src/__tests__/guardian-dispatch.test.ts +0 -13
  63. package/src/__tests__/helpers/seed-app.ts +1 -1
  64. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -1
  65. package/src/__tests__/host-shell-tool.test.ts +1 -1
  66. package/src/__tests__/identity-routes.test.ts +1 -1
  67. package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
  68. package/src/__tests__/inference-profile-session-ipc.test.ts +1 -1
  69. package/src/__tests__/injector-chain.test.ts +1 -1
  70. package/src/__tests__/injector-config-quarantine-notice.test.ts +1 -1
  71. package/src/__tests__/injector-config-validation-reset-notice.test.ts +1 -1
  72. package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
  73. package/src/__tests__/{platform.test.ts → layout.test.ts} +1 -1
  74. package/src/__tests__/list-all-apps.test.ts +1 -1
  75. package/src/__tests__/live-workspace-guard.test.ts +2 -2
  76. package/src/__tests__/llm-resolver-override-or-default.test.ts +5 -6
  77. package/src/__tests__/llm-resolver.test.ts +7 -7
  78. package/src/__tests__/llm-schema-fallback-profile.test.ts +9 -10
  79. package/src/__tests__/llm-usage-store.test.ts +0 -495
  80. package/src/__tests__/log-export-routes.test.ts +1 -1
  81. package/src/__tests__/mcp-config-secret-boundary.test.ts +12 -53
  82. package/src/__tests__/mcp-list-plugin-servers.test.ts +1 -1
  83. package/src/__tests__/memory-config-file-parity.test.ts +1 -1
  84. package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
  85. package/src/__tests__/messages-read-boundary-guard.test.ts +0 -2
  86. package/src/__tests__/notification-guardian-path.test.ts +0 -7
  87. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  88. package/src/__tests__/openai-provider.test.ts +8 -8
  89. package/src/__tests__/path-policy.test.ts +1 -1
  90. package/src/__tests__/persona-resolver.test.ts +1 -1
  91. package/src/__tests__/plugin-app-serve-routes.test.ts +1 -1
  92. package/src/__tests__/plugin-effective-enabled-set.test.ts +1 -1
  93. package/src/__tests__/plugin-import-boundary-guard.test.ts +4 -6
  94. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -3
  95. package/src/__tests__/provider-cloud-proxy-integration.test.ts +1 -5
  96. package/src/__tests__/provider-connections-backfill.test.ts +10 -10
  97. package/src/__tests__/prune-old-conversations-job.test.ts +6 -31
  98. package/src/__tests__/resolve-app.test.ts +1 -1
  99. package/src/__tests__/run-due-schedules.test.ts +1 -1
  100. package/src/__tests__/scaffold-managed-skill-tool.test.ts +0 -53
  101. package/src/__tests__/schedule-routes.test.ts +1 -1
  102. package/src/__tests__/schedule-store.test.ts +1 -1
  103. package/src/__tests__/secret-routes-cloud-proxy.test.ts +1 -1
  104. package/src/__tests__/skill-load-plugin-scope.test.ts +1 -1
  105. package/src/__tests__/surface-completion-compaction-boundary.test.ts +1 -1
  106. package/src/__tests__/tool-approval-handler.test.ts +1 -1
  107. package/src/__tests__/tool-audit.test.ts +5 -235
  108. package/src/__tests__/tool-executor-lifecycle-events.test.ts +54 -114
  109. package/src/__tests__/tools-get-route.test.ts +1 -1
  110. package/src/__tests__/turn-outcome-read.test.ts +1 -1
  111. package/src/__tests__/twilio-routes.test.ts +0 -1
  112. package/src/__tests__/usage-attribution.test.ts +0 -31
  113. package/src/__tests__/verification-control-plane-policy.test.ts +3 -10
  114. package/src/__tests__/visible-app-context.test.ts +1 -1
  115. package/src/__tests__/voice-ingress-preflight.test.ts +1 -1
  116. package/src/__tests__/workspace-git-service.test.ts +1 -1
  117. package/src/__tests__/workspace-greetings.test.ts +1 -1
  118. package/src/acp/__tests__/helpers/acp-config-stub.ts +1 -1
  119. package/src/activation/progress-store.ts +1 -1
  120. package/src/activation/turn-hooks.ts +1 -1
  121. package/src/agent/loop.ts +0 -51
  122. package/src/apps/app-store.ts +1 -1
  123. package/src/avatar/__tests__/avatar-store.test.ts +2 -172
  124. package/src/avatar/accent-backfill.ts +1 -1
  125. package/src/avatar/avatar-manifest.ts +1 -1
  126. package/src/avatar/avatar-store.ts +10 -69
  127. package/src/avatar/ensure-raster.ts +1 -1
  128. package/src/avatar/identity-avatar.ts +1 -1
  129. package/src/avatar/sync-avatar.ts +1 -1
  130. package/src/avatar/traits-png-sync.ts +1 -1
  131. package/src/bundler/compiler-tools.ts +1 -1
  132. package/src/bundler/package-resolver.ts +1 -1
  133. package/src/calls/__tests__/call-controller.test.ts +25 -27
  134. package/src/calls/active-call-lease.ts +1 -1
  135. package/src/calls/call-controller.ts +3 -3
  136. package/src/calls/call-domain.ts +4 -8
  137. package/src/calls/call-store.ts +2 -76
  138. package/src/calls/resolve-call-tts-provider.ts +3 -3
  139. package/src/calls/twilio-config.ts +3 -3
  140. package/src/calls/twilio-rest.ts +2 -2
  141. package/src/calls/voice-ingress-preflight.ts +2 -2
  142. package/src/cli/bundled-modules.ts +0 -2
  143. package/src/cli/commands/__tests__/apps.test.ts +1 -1
  144. package/src/cli/commands/__tests__/plugins.test.ts +9 -66
  145. package/src/cli/commands/__tests__/ps.test.ts +67 -0
  146. package/src/cli/commands/auth.help.ts +2 -2
  147. package/src/cli/commands/avatar.ts +1 -1
  148. package/src/cli/commands/changelog.ts +1 -1
  149. package/src/cli/commands/cloud/__tests__/subscription.test.ts +2 -2
  150. package/src/cli/commands/cloud/index.help.ts +1 -1
  151. package/src/cli/commands/cloud/index.ts +2 -2
  152. package/src/cli/commands/db/refresh.ts +1 -1
  153. package/src/cli/commands/db/repair-step-conversation-backfill.ts +1 -1
  154. package/src/cli/commands/db/repair.ts +1 -1
  155. package/src/cli/commands/db/status.ts +1 -1
  156. package/src/cli/commands/inference-profiles.ts +1 -12
  157. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +1 -116
  158. package/src/cli/commands/memory/index.help.ts +1 -43
  159. package/src/cli/commands/memory/memory-v3.ts +0 -64
  160. package/src/cli/commands/plugins.ts +54 -205
  161. package/src/cli/commands/ps.ts +5 -2
  162. package/src/cli/commands/status.ts +1 -1
  163. package/src/cli/index.help.ts +0 -2
  164. package/src/cli/lib/__tests__/inspect-plugin.test.ts +36 -66
  165. package/src/cli/lib/__tests__/install-from-cloud.test.ts +1 -1
  166. package/src/cli/lib/__tests__/install-from-github.test.ts +41 -117
  167. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +2 -185
  168. package/src/cli/lib/__tests__/plugin-catalog-resolve.test.ts +36 -66
  169. package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +4 -6
  170. package/src/cli/lib/__tests__/plugin-details.test.ts +113 -96
  171. package/src/cli/lib/__tests__/search-plugins.test.ts +9 -16
  172. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +1 -1
  173. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +38 -15
  174. package/src/cli/lib/diff-plugin.ts +1 -1
  175. package/src/cli/lib/inspect-plugin.ts +13 -97
  176. package/src/cli/lib/install-from-cloud.ts +1 -1
  177. package/src/cli/lib/install-from-github.ts +75 -154
  178. package/src/cli/lib/install-plugin-dependencies.ts +1 -1
  179. package/src/cli/lib/list-installed-plugins.ts +1 -1
  180. package/src/cli/lib/open-browser.ts +1 -1
  181. package/src/cli/lib/plugin-catalog-cache.ts +8 -51
  182. package/src/cli/lib/plugin-catalog-resolve.ts +3 -22
  183. package/src/cli/lib/plugin-details.ts +5 -27
  184. package/src/cli/lib/plugin-marketplace.ts +18 -266
  185. package/src/cli/lib/search-plugins.ts +23 -43
  186. package/src/cli/lib/toggle-plugin.ts +1 -1
  187. package/src/cli/lib/uninstall-plugin.ts +1 -1
  188. package/src/cli/lib/upgrade-plugin.ts +3 -49
  189. package/src/cli/program.ts +1 -3
  190. package/src/cli.ts +1 -1
  191. package/src/cloud/client.ts +0 -6
  192. package/src/cloud/consent-cache.test.ts +22 -201
  193. package/src/cloud/consent-cache.ts +16 -111
  194. package/src/cloud/sync-identity.ts +1 -1
  195. package/src/config/__tests__/chat-settings.test.ts +1 -1
  196. package/src/config/__tests__/cloud-identity.test.ts +15 -2
  197. package/src/config/__tests__/default-plugin-skill-discovery.test.ts +1 -1
  198. package/src/config/__tests__/default-profile-catalog.test.ts +12 -51
  199. package/src/config/__tests__/deployment-context-defaults.test.ts +13 -13
  200. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +4 -4
  201. package/src/config/__tests__/loader-last-known-good.test.ts +9 -9
  202. package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +7 -7
  203. package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +1 -1
  204. package/src/config/__tests__/profile-materialization.test.ts +2 -4
  205. package/src/config/__tests__/sync-gated-profiles.test.ts +1 -106
  206. package/src/config/acp-agent-write.ts +1 -1
  207. package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +1 -1
  208. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
  209. package/src/config/call-site-defaults.ts +0 -6
  210. package/src/config/cloud-identity.ts +12 -5
  211. package/src/config/composer-preferences.ts +1 -1
  212. package/src/config/default-profile-catalog.ts +8 -75
  213. package/src/config/default-profile-names.ts +2 -25
  214. package/src/config/env-registry.ts +1 -14
  215. package/src/config/env.ts +8 -6
  216. package/src/config/llm-resolver.ts +1 -2
  217. package/src/config/loader.ts +13 -17
  218. package/src/config/profile-materialization.ts +1 -1
  219. package/src/config/schema.ts +1 -7
  220. package/src/config/schemas/call-site-catalog.ts +0 -7
  221. package/src/config/schemas/llm.ts +22 -27
  222. package/src/config/seed-inference-profiles.ts +16 -17
  223. package/src/config/skills.ts +2 -2
  224. package/src/config/sync-gated-profiles.ts +6 -24
  225. package/src/credential-health/credential-health-service.ts +10 -3
  226. package/src/daemon/approval-generators.ts +3 -3
  227. package/src/daemon/config-watcher.ts +2 -2
  228. package/src/daemon/conversation-agent-loop-handlers.ts +0 -18
  229. package/src/daemon/conversation-agent-loop.ts +6 -97
  230. package/src/daemon/conversation-process.ts +1 -1
  231. package/src/daemon/conversation-skill-tools.ts +4 -72
  232. package/src/daemon/conversation-store.ts +1 -1
  233. package/src/daemon/conversation-surface-state.ts +0 -33
  234. package/src/daemon/conversation-surfaces.ts +1 -86
  235. package/src/daemon/conversation-tool-setup.test.ts +1 -1
  236. package/src/daemon/conversation-tool-setup.ts +0 -23
  237. package/src/daemon/conversation.ts +3 -5
  238. package/src/daemon/daemon-control.ts +3 -3
  239. package/src/daemon/dictation-profile-store.ts +3 -3
  240. package/src/daemon/event-loop-watchdog.test.ts +2 -198
  241. package/src/daemon/event-loop-watchdog.ts +0 -185
  242. package/src/daemon/external-plugins-bootstrap.ts +1 -1
  243. package/src/daemon/first-greeting.ts +1 -1
  244. package/src/daemon/handlers/skills.ts +1 -1
  245. package/src/daemon/host-app-control-proxy.ts +1 -1
  246. package/src/daemon/host-cu-proxy.ts +2 -2
  247. package/src/daemon/identity-helpers.ts +1 -1
  248. package/src/daemon/lifecycle.ts +11 -15
  249. package/src/daemon/main.ts +1 -1
  250. package/src/daemon/now-scratchpad.ts +1 -1
  251. package/src/daemon/process-message.ts +4 -4
  252. package/src/daemon/profiler-run-store.ts +2 -2
  253. package/src/daemon/providers-setup.ts +2 -2
  254. package/src/daemon/shutdown-handlers.ts +0 -10
  255. package/src/daemon/sqlite-corruption-watchdog.test.ts +2 -142
  256. package/src/daemon/sqlite-corruption-watchdog.ts +17 -81
  257. package/src/daemon/tool-side-effects.ts +1 -1
  258. package/src/desktop/desktop-session-manager.ts +1 -1
  259. package/src/desktop/desktop-wallpaper-renderer.ts +1 -1
  260. package/src/heartbeat/heartbeat-service.ts +1 -1
  261. package/src/home/feed-writer.ts +1 -1
  262. package/src/home/home-greeting-cache.ts +1 -1
  263. package/src/home/relationship-state-writer.ts +2 -2
  264. package/src/hooks/__tests__/hook-live-reload.test.ts +1 -1
  265. package/src/hooks/hook-loader.ts +2 -2
  266. package/src/inbound/__tests__/oauth-callback-url.test.ts +2 -2
  267. package/src/inbound/oauth-callback-url.ts +3 -3
  268. package/src/ipc/__tests__/attachment-ipc.test.ts +1 -1
  269. package/src/ipc/socket-path.ts +1 -1
  270. package/src/live-voice/__tests__/live-voice-archive.test.ts +1 -1
  271. package/src/live-voice/__tests__/live-voice-vad.test.ts +1 -1
  272. package/src/live-voice/live-voice-session.ts +3 -66
  273. package/src/mcp/__tests__/effective-config.test.ts +1 -1
  274. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +2 -2
  275. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +2 -3
  276. package/src/mcp/__tests__/reload-signal-emission.test.ts +1 -1
  277. package/src/mcp/__tests__/reload-signal.test.ts +1 -1
  278. package/src/mcp/__tests__/workspace-mcp-config.test.ts +19 -21
  279. package/src/mcp/reload-signal.ts +1 -1
  280. package/src/mcp/workspace-mcp-config.ts +1 -23
  281. package/src/messaging/draft-store.ts +1 -1
  282. package/src/monitoring/__tests__/active-conversations.test.ts +1 -1
  283. package/src/monitoring/__tests__/page-cache.test.ts +1 -1
  284. package/src/monitoring/__tests__/stall-capture.test.ts +1 -1
  285. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +1 -1
  286. package/src/monitoring/active-conversations.ts +1 -1
  287. package/src/monitoring/control.ts +1 -1
  288. package/src/monitoring/daemon-boot-time.ts +1 -1
  289. package/src/monitoring/daemon-heartbeat.ts +1 -1
  290. package/src/monitoring/page-cache.ts +1 -1
  291. package/src/monitoring/plugin-auto-update.ts +1 -7
  292. package/src/monitoring/plugin-source-watch.ts +1 -1
  293. package/src/monitoring/recovery/db.ts +1 -1
  294. package/src/monitoring/recovery/inflight-content.ts +1 -1
  295. package/src/monitoring/resource-sampler.ts +3 -14
  296. package/src/monitoring/stall-capture.ts +1 -1
  297. package/src/monitoring/worker.ts +9 -76
  298. package/src/monitoring/workspace-git-heartbeat.ts +1 -1
  299. package/src/oauth/connect-orchestrator.ts +4 -4
  300. package/src/permissions/checker.ts +1 -1
  301. package/src/permissions/workspace-policy.ts +1 -1
  302. package/src/persistence/__tests__/main-db-readonly-probe.ts +27 -52
  303. package/src/persistence/__tests__/message-content-file.test.ts +1 -1
  304. package/src/persistence/__tests__/slow-sync-log.test.ts +0 -2
  305. package/src/persistence/attachments-store.ts +1 -1
  306. package/src/persistence/conversation-crud.ts +1 -38
  307. package/src/persistence/conversation-directories.ts +1 -1
  308. package/src/persistence/conversation-types.ts +0 -8
  309. package/src/persistence/db-async-query.ts +1 -1
  310. package/src/persistence/db-connection.ts +11 -1
  311. package/src/persistence/db-init.ts +1 -1
  312. package/src/persistence/db-maintenance.ts +1 -1
  313. package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +1 -1
  314. package/src/persistence/embeddings/embedding-local.ts +4 -4
  315. package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -1
  316. package/src/persistence/embeddings/qdrant-client.ts +1 -1
  317. package/src/persistence/embeddings/qdrant-manager.ts +1 -1
  318. package/src/persistence/job-handlers/__tests__/message-lexical-backfill.test.ts +1 -1
  319. package/src/persistence/job-handlers/__tests__/message-lexical-enqueue.test.ts +1 -1
  320. package/src/persistence/job-handlers/cleanup.ts +8 -17
  321. package/src/persistence/llm-usage-store.ts +2 -229
  322. package/src/persistence/message-content-file.ts +1 -1
  323. package/src/persistence/migrations/209-strip-thinking-from-consolidated.ts +1 -1
  324. package/src/persistence/migrations/222-strip-placeholder-sentinels-from-messages.ts +1 -1
  325. package/src/persistence/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +1 -1
  326. package/src/persistence/migrations/306-rewrite-frontier-profile-pins.test.ts +1 -1
  327. package/src/persistence/migrations/329-move-onboarding-events-to-telemetry-db.test.ts +13 -0
  328. package/src/persistence/migrations/330-move-auth-fallback-events-to-telemetry-db.test.ts +13 -0
  329. package/src/persistence/migrations/331-move-lifecycle-events-to-telemetry-db.test.ts +13 -0
  330. package/src/persistence/migrations/332-move-skill-loaded-events-to-telemetry-db.test.ts +13 -0
  331. package/src/persistence/migrations/333-create-telemetry-events-table.test.ts +7 -4
  332. package/src/persistence/migrations/334-backfill-telemetry-events-outbox.test.ts +13 -0
  333. package/src/persistence/migrations/387-drop-telemetry-tables.ts +51 -0
  334. package/src/persistence/migrations/__tests__/327-create-flush-checkpoints.test.ts +9 -1
  335. package/src/persistence/migrations/__tests__/387-drop-telemetry-tables.test.ts +79 -0
  336. package/src/persistence/migrations/helpers/relocation.ts +1 -1
  337. package/src/persistence/migrations/run-migrations.ts +1 -1
  338. package/src/persistence/migrations/validate-migration-state.ts +1 -1
  339. package/src/persistence/raw-query.ts +1 -29
  340. package/src/persistence/slow-sync-log.ts +0 -23
  341. package/src/persistence/steps.ts +2 -0
  342. package/src/{telemetry → persistence}/tool-audit.ts +6 -121
  343. package/src/plugin-api/index.ts +1 -1
  344. package/src/plugin-api/plugin-name-env.ts +1 -1
  345. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +1 -1
  346. package/src/plugins/collect-source-versions.ts +1 -1
  347. package/src/plugins/defaults/channel/package.json +1 -1
  348. package/src/plugins/defaults/cloud-hosted/package.json +1 -1
  349. package/src/plugins/defaults/compaction/package.json +1 -1
  350. package/src/plugins/defaults/documents/package.json +1 -1
  351. package/src/plugins/defaults/empty-response/package.json +1 -1
  352. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  353. package/src/plugins/defaults/image-fallback/package.json +1 -1
  354. package/src/plugins/defaults/image-recovery/package.json +1 -1
  355. package/src/plugins/defaults/max-tokens-continue/package.json +1 -1
  356. package/src/plugins/defaults/memory/__tests__/db-async-query.test.ts +1 -1
  357. package/src/plugins/defaults/memory/__tests__/db-maintenance.test.ts +1 -1
  358. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +0 -82
  359. package/src/plugins/defaults/memory/__tests__/memory-retrospective-skill-card.test.ts +0 -41
  360. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -2
  361. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -52
  362. package/src/plugins/defaults/memory/memory-retrospective-skill-card.ts +0 -18
  363. package/src/plugins/defaults/memory/package.json +1 -1
  364. package/src/plugins/defaults/memory/paths.ts +4 -4
  365. package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +1 -1
  366. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +5 -5
  367. package/src/plugins/defaults/memory/src/memory-v3-routes.ts +0 -207
  368. package/src/plugins/defaults/memory/src/memory-worker-routes.ts +1 -1
  369. package/src/plugins/defaults/memory/startup.ts +1 -22
  370. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +2 -253
  371. package/src/plugins/defaults/memory/v3/orchestrate.ts +5 -129
  372. package/src/plugins/defaults/memory/worker-control.ts +1 -1
  373. package/src/plugins/defaults/memory/worker.ts +2 -2
  374. package/src/plugins/defaults/session/package.json +1 -1
  375. package/src/plugins/defaults/surface-completion-nudge/package.json +1 -1
  376. package/src/plugins/defaults/task-progress/package.json +1 -1
  377. package/src/plugins/defaults/task-progress-nudge/package.json +1 -1
  378. package/src/plugins/defaults/title-generate/package.json +1 -1
  379. package/src/plugins/defaults/tool-error/package.json +1 -1
  380. package/src/plugins/defaults/tool-result-truncate/package.json +1 -1
  381. package/src/plugins/defaults/turn-context/package.json +1 -1
  382. package/src/plugins/defaults/workspace/injectors.ts +1 -1
  383. package/src/plugins/defaults/workspace/package.json +1 -1
  384. package/src/plugins/disabled-state.ts +1 -1
  385. package/src/plugins/ensure-plugin-api-shim.ts +1 -1
  386. package/src/plugins/ensure-shared-dep-links.ts +1 -1
  387. package/src/plugins/installed-plugin-dirs.ts +1 -1
  388. package/src/plugins/mcp-servers.ts +7 -9
  389. package/src/plugins/mtime-cache.ts +2 -2
  390. package/src/plugins/source-versions.ts +1 -1
  391. package/src/prompts/bootstrap-cleanup.ts +1 -1
  392. package/src/prompts/persona-resolver.ts +1 -1
  393. package/src/prompts/sections.ts +2 -2
  394. package/src/prompts/system-prompt.ts +10 -42
  395. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +0 -37
  396. package/src/providers/__tests__/cadet-connection-routing.test.ts +2 -2
  397. package/src/providers/cadet/client.ts +1 -1
  398. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -1
  399. package/src/providers/inference/adapter-factory.ts +1 -1
  400. package/src/providers/inference/backfill.ts +3 -10
  401. package/src/providers/inference/profile-probe.ts +1 -1
  402. package/src/providers/retry.ts +0 -2
  403. package/src/providers/types.ts +0 -7
  404. package/src/providers/usage-tracking.ts +1 -1
  405. package/src/routes/__tests__/route-host.test.ts +1 -1
  406. package/src/routes/control.ts +1 -1
  407. package/src/routes/route-host-client.ts +1 -1
  408. package/src/routes/worker.ts +2 -2
  409. package/src/runtime/agent-wake.ts +1 -1
  410. package/src/runtime/assistant-stream-state.ts +1 -1
  411. package/src/runtime/auth/token-service.ts +1 -1
  412. package/src/runtime/background-job-runner.ts +1 -1
  413. package/src/runtime/middleware/twilio-validation.ts +2 -2
  414. package/src/runtime/routes/__tests__/app-pin-routes.test.ts +1 -1
  415. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +1 -1
  416. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +3 -33
  417. package/src/runtime/routes/__tests__/cloud-credits-routes.test.ts +5 -4
  418. package/src/runtime/routes/__tests__/cloud-invoice-routes.test.ts +4 -3
  419. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +59 -287
  420. package/src/runtime/routes/__tests__/default-plugin-routes.test.ts +1 -1
  421. package/src/runtime/routes/__tests__/deprecated-route-context.test.ts +15 -62
  422. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +105 -12
  423. package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +1 -1
  424. package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -1
  425. package/src/runtime/routes/__tests__/plugins-routes.test.ts +47 -211
  426. package/src/runtime/routes/__tests__/route-host-worker-routes.test.ts +1 -1
  427. package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +1 -1
  428. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +1 -1
  429. package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +1 -1
  430. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +1 -1
  431. package/src/runtime/routes/__tests__/user-routes-cli.test.ts +1 -1
  432. package/src/runtime/routes/__tests__/user-routes-notices.test.ts +1 -1
  433. package/src/runtime/routes/__tests__/user-routes-request.test.ts +1 -1
  434. package/src/runtime/routes/app-management-routes.ts +1 -1
  435. package/src/runtime/routes/attachment-routes.ts +1 -1
  436. package/src/runtime/routes/audit-routes.ts +1 -1
  437. package/src/runtime/routes/avatar-routes.ts +3 -10
  438. package/src/runtime/routes/cloud-routes.ts +3 -3
  439. package/src/runtime/routes/content-source-routes.ts +1 -1
  440. package/src/runtime/routes/conversation-query-routes.ts +60 -621
  441. package/src/runtime/routes/conversation-routes.ts +1 -38
  442. package/src/runtime/routes/debug-routes.ts +1 -1
  443. package/src/runtime/routes/deprecated-route-context.ts +5 -61
  444. package/src/runtime/routes/heartbeat-routes.ts +1 -1
  445. package/src/runtime/routes/identity-routes.ts +1 -1
  446. package/src/runtime/routes/index.ts +0 -6
  447. package/src/runtime/routes/inference-profile-session-handler.ts +3 -3
  448. package/src/runtime/routes/inference-profiles-routes.ts +48 -21
  449. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -1
  450. package/src/runtime/routes/llm-call-sites-routes.ts +3 -3
  451. package/src/runtime/routes/log-export/__tests__/installed-inventory.test.ts +1 -1
  452. package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +1 -1
  453. package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +1 -1
  454. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  455. package/src/runtime/routes/log-export-routes.ts +17 -15
  456. package/src/runtime/routes/migration-rollback-routes.ts +1 -1
  457. package/src/runtime/routes/migration-routes.ts +2 -3
  458. package/src/runtime/routes/monitoring-routes.ts +2 -2
  459. package/src/runtime/routes/oauth-providers.ts +5 -5
  460. package/src/runtime/routes/plugins-routes.ts +38 -163
  461. package/src/runtime/routes/profiler-routes.ts +1 -1
  462. package/src/runtime/routes/route-host-worker-routes.ts +1 -1
  463. package/src/runtime/routes/sanity-routes.ts +1 -1
  464. package/src/runtime/routes/schedule-routes.ts +1 -1
  465. package/src/runtime/routes/schedule-worker-routes.ts +1 -1
  466. package/src/runtime/routes/settings-routes.ts +1 -1
  467. package/src/runtime/routes/sounds-config-routes.ts +1 -1
  468. package/src/runtime/routes/surface-conversation-resolver.ts +1 -7
  469. package/src/runtime/routes/user-route-dispatcher.ts +1 -3
  470. package/src/runtime/routes/user-route-import.ts +1 -1
  471. package/src/runtime/routes/user-route-resolution.ts +1 -1
  472. package/src/runtime/routes/user-routes-cli.ts +1 -1
  473. package/src/runtime/routes/workspace-commit-routes.ts +1 -1
  474. package/src/runtime/routes/workspace-greetings.ts +1 -1
  475. package/src/runtime/routes/workspace-routes.ts +1 -1
  476. package/src/runtime/routes/workspace-utils.ts +1 -1
  477. package/src/runtime/sync/resource-sync-events.ts +1 -1
  478. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +1 -1
  479. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -1
  480. package/src/schedule/plugin-schedule-availability.ts +1 -1
  481. package/src/schedule/plugin-schedule-declarations.ts +1 -1
  482. package/src/schedule/run-script.ts +1 -1
  483. package/src/schedule/scheduler.ts +1 -11
  484. package/src/schedule/worker-control.ts +1 -1
  485. package/src/schedule/worker.ts +3 -3
  486. package/src/security/oauth2.ts +2 -2
  487. package/src/security/secret-allowlist.ts +1 -1
  488. package/src/signals/cancel.ts +1 -1
  489. package/src/signals/conversation-undo.ts +1 -1
  490. package/src/signals/emit-event.ts +1 -1
  491. package/src/signals/event-stream.ts +1 -1
  492. package/src/signals/user-message.ts +1 -1
  493. package/src/skills/catalog-install.ts +2 -1
  494. package/src/skills/clawhub.ts +1 -1
  495. package/src/skills/cloud-store.ts +1 -1
  496. package/src/skills/path-classifier.ts +1 -1
  497. package/src/skills/skill-history.ts +1 -1
  498. package/src/skills/skillssh-registry.ts +1 -1
  499. package/src/skills/uninstall.ts +1 -1
  500. package/src/skills/workspace-skill-md-signature.ts +1 -1
  501. package/src/subagent/manager.ts +1 -1
  502. package/src/theme/workspace-theme.ts +1 -1
  503. package/src/tools/browser/auth-cache.ts +1 -1
  504. package/src/tools/browser/browser-manager.ts +1 -1
  505. package/src/tools/browser/runtime-check.ts +1 -1
  506. package/src/tools/executor.ts +4 -4
  507. package/src/tools/filesystem/write.ts +1 -1
  508. package/src/tools/permission-checker.ts +2 -51
  509. package/src/tools/run-standalone.ts +1 -1
  510. package/src/tools/skills/format-input-schema.ts +1 -1
  511. package/src/tools/skills/load.ts +1 -1
  512. package/src/tools/skills/scaffold-cloud.ts +0 -32
  513. package/src/tools/terminal/safe-env.ts +1 -2
  514. package/src/tools/terminal/sanitized-bash.ts +1 -1
  515. package/src/tools/terminal/shell.ts +1 -1
  516. package/src/tools/tool-approval-handler.ts +4 -1
  517. package/src/tools/workflows/manage-workflows.ts +2 -2
  518. package/src/tools/workflows/run-workflow.test.ts +1 -1
  519. package/src/tools/workspace-tools/loader.ts +1 -1
  520. package/src/tts/synthesis-stream.ts +2 -2
  521. package/src/usage/attribution.ts +1 -11
  522. package/src/util/browser.ts +1 -1
  523. package/src/util/bun-runtime.ts +2 -1
  524. package/src/util/device-id.ts +1 -1
  525. package/src/util/disk-usage.ts +4 -139
  526. package/src/util/layout.ts +3 -3
  527. package/src/util/logger.ts +1 -1
  528. package/src/util/logs-db-path.ts +1 -1
  529. package/src/util/memory-db-path.ts +1 -1
  530. package/src/util/{platform.ts → os.ts} +5 -30
  531. package/src/util/process-tree.ts +19 -0
  532. package/src/util/sqlite3-runtime.ts +1 -1
  533. package/src/util/telemetry-db-path.ts +1 -1
  534. package/src/watcher/__tests__/engine.test.ts +0 -23
  535. package/src/watcher/engine.ts +0 -7
  536. package/src/windows-compiled-cli.ts +0 -4
  537. package/src/windows-compiled-worker-entry.ts +0 -6
  538. package/src/workflows/leaf-runner.test.ts +1 -1
  539. package/src/workflows/leaf-runner.ts +1 -1
  540. package/src/workflows/library.ts +1 -1
  541. package/src/workspace/adaptive-thinking-repair.ts +5 -24
  542. package/src/workspace/byok-default-profile-ensure.ts +24 -26
  543. package/src/workspace/custom-profile-ensure.ts +2 -2
  544. package/src/workspace/git-service.ts +1 -1
  545. package/src/workspace/hatched-date.ts +1 -1
  546. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +2 -2
  547. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +1 -1
  548. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +1 -1
  549. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +1 -1
  550. package/src/workspace/migrations/registry.ts +0 -4
  551. package/src/workspace/turn-commit.ts +1 -1
  552. package/docs/activation-funnel-telemetry.md +0 -324
  553. package/scripts/bundled-plugin-packages.ts +0 -154
  554. package/scripts/generate-bundled-plugin-packages.ts +0 -29
  555. package/src/__tests__/activation-early-marking.test.ts +0 -114
  556. package/src/__tests__/auth-fallback-events-store.test.ts +0 -99
  557. package/src/__tests__/config-get-vision-flag.test.ts +0 -139
  558. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +0 -400
  559. package/src/__tests__/internal-telemetry-routes.test.ts +0 -213
  560. package/src/__tests__/telemetry-routes.test.ts +0 -182
  561. package/src/__tests__/turn-events-store.test.ts +0 -376
  562. package/src/__tests__/ui-channel-variants.test.ts +0 -53
  563. package/src/avatar/__tests__/avatar-changed-telemetry.test.ts +0 -217
  564. package/src/avatar/avatar-changed-telemetry.ts +0 -128
  565. package/src/calls/__tests__/call-funnel-telemetry.test.ts +0 -204
  566. package/src/cli/commands/telemetry.help.ts +0 -27
  567. package/src/cli/commands/telemetry.ts +0 -83
  568. package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +0 -251
  569. package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +0 -113
  570. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +0 -384
  571. package/src/cli/lib/__tests__/plugin-pin-history.test.ts +0 -164
  572. package/src/cli/lib/__tests__/plugins-install-offline.test.ts +0 -228
  573. package/src/cli/lib/bundled-marketplace.json +0 -1575
  574. package/src/cli/lib/bundled-plugin-packages.json +0 -827
  575. package/src/cli/lib/bundled-plugin-packages.ts +0 -87
  576. package/src/cli/lib/plugin-catalog-local.ts +0 -94
  577. package/src/cli/lib/plugin-pin-history.ts +0 -276
  578. package/src/cloud/feature-gate.ts +0 -8
  579. package/src/config/__tests__/cloud-rehydration.test.ts +0 -180
  580. package/src/config/cloud-rehydration.ts +0 -35
  581. package/src/daemon/__tests__/auto-profile-router.test.ts +0 -380
  582. package/src/daemon/auto-profile-router.ts +0 -428
  583. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +0 -391
  584. package/src/monitoring/__tests__/db-integrity-sample.test.ts +0 -113
  585. package/src/monitoring/__tests__/oom-kill-reporter.test.ts +0 -374
  586. package/src/monitoring/db-integrity-check.ts +0 -163
  587. package/src/monitoring/db-integrity-sample.ts +0 -176
  588. package/src/monitoring/oom-kill-reporter.ts +0 -469
  589. package/src/onboarding/__tests__/onboarding-events-store.test.ts +0 -158
  590. package/src/onboarding/onboarding-events-store.ts +0 -296
  591. package/src/persistence/__tests__/raw-query-telemetry.test.ts +0 -76
  592. package/src/persistence/lifecycle-events-store.test.ts +0 -134
  593. package/src/persistence/lifecycle-events-store.ts +0 -110
  594. package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +0 -76
  595. package/src/plugins/defaults/memory/activation-session-store.ts +0 -50
  596. package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +0 -281
  597. package/src/runtime/routes/auto-profile-routes.ts +0 -83
  598. package/src/runtime/routes/internal-telemetry-routes.ts +0 -142
  599. package/src/runtime/routes/telemetry-routes.ts +0 -274
  600. package/src/security/auth-fallback-events-store.ts +0 -57
  601. package/src/telemetry/__tests__/activation-funnel.test.ts +0 -95
  602. package/src/telemetry/__tests__/config-setting-events-store.test.ts +0 -63
  603. package/src/telemetry/__tests__/config-setting-snapshot.test.ts +0 -143
  604. package/src/telemetry/__tests__/flush-checkpoints.test.ts +0 -56
  605. package/src/telemetry/__tests__/live-voice-funnel.test.ts +0 -174
  606. package/src/telemetry/__tests__/outbox-test-harness.ts +0 -85
  607. package/src/telemetry/__tests__/phone-call-funnel.test.ts +0 -72
  608. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +0 -282
  609. package/src/telemetry/__tests__/watchdog-events-store.test.ts +0 -71
  610. package/src/telemetry/activation-funnel.ts +0 -172
  611. package/src/telemetry/config-setting-events-store.ts +0 -40
  612. package/src/telemetry/config-setting-snapshot.ts +0 -170
  613. package/src/telemetry/flush-checkpoints.ts +0 -70
  614. package/src/telemetry/live-voice-funnel.ts +0 -185
  615. package/src/telemetry/memory-corpus-size.test.ts +0 -120
  616. package/src/telemetry/memory-corpus-size.ts +0 -184
  617. package/src/telemetry/memory-tier-reporter.test.ts +0 -195
  618. package/src/telemetry/memory-tier-reporter.ts +0 -130
  619. package/src/telemetry/phone-call-funnel.ts +0 -144
  620. package/src/telemetry/skill-loaded-events-store.test.ts +0 -93
  621. package/src/telemetry/skill-loaded-events-store.ts +0 -39
  622. package/src/telemetry/telemetry-event-sources.test.ts +0 -223
  623. package/src/telemetry/telemetry-event-sources.ts +0 -597
  624. package/src/telemetry/telemetry-events-outbox.test.ts +0 -375
  625. package/src/telemetry/telemetry-events-outbox.ts +0 -237
  626. package/src/telemetry/telemetry-main-db.ts +0 -48
  627. package/src/telemetry/telemetry-wire-source.json +0 -4
  628. package/src/telemetry/telemetry-wire-validation.test.ts +0 -157
  629. package/src/telemetry/telemetry-wire-validation.ts +0 -162
  630. package/src/telemetry/telemetry-wire.generated.ts +0 -483
  631. package/src/telemetry/tool-executed-events-store.test.ts +0 -210
  632. package/src/telemetry/tool-executed-events-store.ts +0 -102
  633. package/src/telemetry/trace-collection-policy.test.ts +0 -28
  634. package/src/telemetry/trace-collection-policy.ts +0 -30
  635. package/src/telemetry/turn-events-store.ts +0 -216
  636. package/src/telemetry/turn-trace-store.test.ts +0 -884
  637. package/src/telemetry/turn-trace-store.ts +0 -399
  638. package/src/telemetry/types.test.ts +0 -38
  639. package/src/telemetry/types.ts +0 -780
  640. package/src/telemetry/usage-telemetry-reporter.test.ts +0 -3445
  641. package/src/telemetry/usage-telemetry-reporter.ts +0 -566
  642. package/src/telemetry/watchdog-direct-emit.test.ts +0 -112
  643. package/src/telemetry/watchdog-direct-emit.ts +0 -104
  644. package/src/telemetry/watchdog-events-store.ts +0 -31
  645. package/src/tools/ui-surface/channel-variants.ts +0 -52
  646. package/src/watcher/__tests__/telemetry.test.ts +0 -178
  647. package/src/watcher/telemetry.ts +0 -93
  648. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +0 -165
  649. package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +0 -151
  650. /package/src/{telemetry → persistence}/tool-usage-store.ts +0 -0
  651. /package/src/{telemetry → persistence}/turn-outcome.ts +0 -0
@@ -19,8 +19,6 @@
19
19
  * POST /v1/messages/queued/:id/steer — steer to a queued message
20
20
  */
21
21
 
22
- import { isDeepStrictEqual } from "node:util";
23
-
24
22
  import { z } from "zod";
25
23
 
26
24
  import { LlmContextResponseSchema } from "../../api/responses/llm-context-response.js";
@@ -35,35 +33,24 @@ import {
35
33
  scrubNulledChatSettings,
36
34
  validateChatSettingsWrite,
37
35
  } from "../../config/chat-settings.js";
38
- import {
39
- catalogEntryFor,
40
- modalitiesOf,
41
- resolveModalityOverride,
42
- } from "../../config/input-modalities.js";
43
36
  import {
44
37
  deepMergeOverwrite,
45
38
  getConfig,
46
39
  invalidateConfigCache,
47
- loadConfig,
48
40
  loadRawConfig,
49
41
  saveRawConfig,
50
42
  setNestedValue,
51
43
  withSuppressedConfigDiskWrites,
52
44
  withSuppressedConfigDiskWritesSync,
53
45
  } from "../../config/loader.js";
54
- import {
55
- completeCustomProfile,
56
- mergePreservingUnknownKeys,
57
- } from "../../config/profile-materialization.js";
58
46
  import {
59
47
  type AssistantConfig,
60
48
  AssistantConfigSchema,
61
49
  } from "../../config/schema.js";
62
50
  import { getSchemaAtPath } from "../../config/schema-utils.js";
63
51
  import {
64
- collectFallbackProfileIssues,
65
- LLMConfigBase,
66
52
  LLMConfigFragment,
53
+ LLMDefaultConfigSchema,
67
54
  LLMSchema,
68
55
  ProfileEntry,
69
56
  routingIdentityModelIssue,
@@ -100,7 +87,6 @@ import {
100
87
  log,
101
88
  } from "../../daemon/handlers/shared.js";
102
89
  import { rescheduleHeartbeatIfTimezoneChanged } from "../../heartbeat/heartbeat-service.js";
103
- import { overlayWorkspaceMcpForConfigRead } from "../../mcp/workspace-mcp-config.js";
104
90
  import {
105
91
  getAssistantMessageIdsInTurn,
106
92
  getConversation,
@@ -173,10 +159,7 @@ type LlmContextRouteResult = Omit<LlmContextNormalizationResult, "summary"> & {
173
159
  import {
174
160
  CODE_OWNED_PROFILE_NAMES,
175
161
  getEffectiveProfilesForProvider,
176
- getUserSelectableProfilesForProvider,
177
- INVARIANT_PROFILE_NAMES,
178
162
  MANAGED_PROFILE_NAMES,
179
- resolveDefaultProfileForProvider,
180
163
  } from "../../config/default-profile-catalog.js";
181
164
  import { DEFAULT_PROFILE_KEYS } from "../../config/default-profile-names.js";
182
165
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
@@ -523,22 +506,18 @@ async function handleSetEmbeddingConfig({ body }: RouteHandlerArgs) {
523
506
  }
524
507
 
525
508
  /**
526
- * Tolerant shape parses for raw config-write fragments — zod replaces the
527
- * hand-rolled plain-object narrowing (`readPlainObject`) at every guard.
528
- * They model exactly the fields each guard reads and pass everything else
529
- * through, so a write guard sees intent even when the surrounding payload
530
- * is sparse or malformed. A parse copies, so mutating guards write the
531
- * normalized slice back onto the caller's object instead of relying on
532
- * shared references.
509
+ * The `config_set` body. `value` is `z.unknown()` — deliberately wider than
510
+ * an object shape — because the CLI contract writes scalar values and
511
+ * explicit `null`s (`set heartbeat.activeHoursStart null` clears a key), so
512
+ * the value is any JSON at all; only its PRESENCE is required, which zod
513
+ * cannot express on an unknown leaf, so the handler keeps that domain check.
533
514
  */
534
- const JsonRecordSchema = z.record(z.string(), z.unknown());
535
- const RawProfileEntrySchema = z.record(z.string(), z.unknown());
536
- const RawLlmWriteSliceSchema = z
515
+ const ConfigSetByKeyBodySchema = z
537
516
  .object({
538
- default: z.unknown().optional(),
539
- profiles: z.record(z.string(), z.unknown()).nullable().optional(),
517
+ path: z.string().min(1),
518
+ value: z.unknown(),
540
519
  })
541
- .passthrough();
520
+ .meta({ id: "ConfigSetByKeyRequest" });
542
521
 
543
522
  /**
544
523
  * Wire shape of the `memory` section in config responses. Passthrough
@@ -594,33 +573,28 @@ const CallSiteOverrideDraftSchema = nullablePartial(
594
573
  * the generated SDK produces literal-union types — no hand-written patch
595
574
  * types needed downstream.
596
575
  */
597
- const ConfigPatchRequestSchema = z
576
+ export type ConfigPatchRequest = z.infer<typeof ConfigPatchRequestSchema>;
577
+
578
+ export const ConfigPatchRequestSchema = z
598
579
  .object({
599
580
  ...ChatSettingsPatchSchema.shape,
600
581
  llm: z
601
582
  .object({
602
- default: nullablePartial(
603
- LLMConfigFragment.extend({
604
- provider_connection: z.string().optional(),
605
- }),
606
- )
583
+ default: nullablePartial(LLMDefaultConfigSchema)
607
584
  .passthrough()
608
585
  .nullable()
609
586
  .optional(),
610
587
  profiles: z
611
588
  .record(z.string(), ProfilePatchEntrySchema.nullable())
589
+ .nullable()
612
590
  .optional(),
613
- profileOrder: z.array(z.string()).optional(),
614
- activeProfile: z.string().nullable().optional(),
615
- advisorProfile: z.string().nullable().optional(),
591
+ profileOrder: z.array(z.string().min(1)).optional(),
592
+ activeProfile: z.string().min(1).nullable().optional(),
593
+ advisorProfile: z.string().min(1).nullable().optional(),
616
594
  callSites: z
617
595
  .record(z.string(), CallSiteOverrideDraftSchema.nullable())
618
596
  .optional(),
619
- profileSession: z
620
- .object({
621
- defaultTtlSeconds: z.number().optional(),
622
- maxTtlSeconds: z.number().optional(),
623
- })
597
+ profileSession: nullablePartial(LLMSchema.shape.profileSession.unwrap())
624
598
  .nullable()
625
599
  .optional(),
626
600
  })
@@ -678,253 +652,6 @@ const ConfigGetResponseSchema = AssistantConfigSchema.pick({
678
652
  services: true,
679
653
  });
680
654
 
681
- function handleGetConfig() {
682
- try {
683
- // The overlays stamp wire-only state (catalog bodies, effective chat
684
- // settings, capability flags) onto the returned object. Clone first so
685
- // the loader's cached config — and every other `getConfig()` reader,
686
- // including the config-write merge base — never sees the wire view.
687
- const config = structuredClone(loadConfig());
688
- overlayWorkspaceMcpForConfigRead(config);
689
- overlayEffectiveProfilesForWire(config);
690
- enrichProfilesForWire(config);
691
- overlayChatSettingsForWire(config);
692
- return config;
693
- } catch (err) {
694
- const message = err instanceof Error ? err.message : String(err);
695
- throw new InternalError(`Failed to read config: ${message}`);
696
- }
697
- }
698
- function overlayChatSettingsForWire(config: AssistantConfig): void {
699
- const effective = getConfig();
700
- config.conversations = {
701
- ...config.conversations,
702
- autoArchive: {
703
- ...config.conversations?.autoArchive,
704
- ...effective.conversations.autoArchive,
705
- },
706
- };
707
- config.notifications = {
708
- ...config.notifications,
709
- newMessageEnabled: effective.notifications.newMessageEnabled,
710
- };
711
- }
712
-
713
- /**
714
- * Replace `llm.profiles` in an outgoing config response with the effective
715
- * profile view (code-catalog default bodies + workspace overlays). Default
716
- * profile CONTENT is code-owned and the workspace holds at most a thin stub,
717
- * but clients (settings UI, sticky-profile pickers) need the full bodies to
718
- * render labels/models — so the wire view materializes them, resolved through
719
- * `llm.default`'s column of the intent × provider matrix so a BYO
720
- * install sees the provider/model that actually dispatches. Wire-only:
721
- * `normalizeCloudProfileWrites` reduces echoed bodies back to the
722
- * workspace-owned fields on the write paths, so a `config get` →
723
- * `config set` round-trip never persists catalog content — the two must
724
- * resolve through the same column or honest round-trips are rejected.
725
- *
726
- * The profiles come from one schema parse of the `llm` block being served
727
- * through the real `LLMSchema`, whose `isDefaultProviderChoice` clamp
728
- * shields the matrix lookup from a persisted value the matrix cannot
729
- * implement, which a raw read would feed straight into resolution.
730
- */
731
- function overlayEffectiveProfilesForWire(config: AssistantConfig): void {
732
- const parsedLlm = LLMSchema.safeParse(config.llm);
733
- config.llm.profiles = getUserSelectableProfilesForProvider(
734
- parsedLlm.success ? parsedLlm.data : undefined,
735
- );
736
- }
737
-
738
- /**
739
- * Normalize managed default-profile entries in a config-write fragment, in
740
- * place, so a `config get` → write round-trip of the enriched wire view is a
741
- * no-op while genuine edit attempts still reach the invariant guard's
742
- * precise rejections:
743
- *
744
- * - A default name backed by a user-source on-disk entry is a legacy shadow
745
- * and stays fully editable — left untouched here.
746
- * - Echo-stripping: any field whose incoming value equals the current
747
- * effective (wire) value is deleted from the fragment — it is catalog
748
- * content the client read from us, not user input. Values that DIFFER stay
749
- * in the fragment for {@link assertInvariantProfilesPreserved} to reject
750
- * (or, for `status`, to judge as the one legal transition).
751
- * - A default name with NO on-disk entry is catalog-owned: after
752
- * echo-stripping, any remaining content field, a `disabled` status, or a
753
- * non-managed `source` is rejected — default names cannot be newly
754
- * shadowed or given content. A clean echo reduces to a no-op
755
- * `{source: "managed"}` stub.
756
- *
757
- * The llm slice is parsed through {@link RawLlmWriteSliceSchema} and the
758
- * normalized slice written back — a zod parse copies, so the write-back (not
759
- * shared references) is what carries the normalization to the caller's patch.
760
- * `handleSetConfig` reads the written-back shape for entry-level SETs.
761
- */
762
- export function normalizeCloudProfileWrites(
763
- patch: Record<string, unknown>,
764
- ): void {
765
- const llm = RawLlmWriteSliceSchema.safeParse(patch.llm);
766
- if (!llm.success || !llm.data.profiles) {
767
- return;
768
- }
769
- const profiles = llm.data.profiles;
770
-
771
- const currentLlm = RawLlmWriteSliceSchema.safeParse(loadRawConfig().llm);
772
- const typedCurrent = currentLlm.success
773
- ? LLMSchema.shape.profiles.safeParse(currentLlm.data.profiles)
774
- : undefined;
775
- const currentProfiles: Record<string, unknown> | undefined =
776
- typedCurrent?.success ? typedCurrent.data : undefined;
777
-
778
- for (const name of Object.keys(profiles)) {
779
- if (!MANAGED_PROFILE_NAMES.has(name)) {
780
- continue;
781
- }
782
- const entry = RawProfileEntrySchema.safeParse(profiles[name]);
783
- if (!entry.success) {
784
- continue;
785
- }
786
-
787
- const current = RawProfileEntrySchema.safeParse(currentProfiles?.[name]);
788
-
789
- if (current.success && current.data.source !== "managed") {
790
- // Legacy user-owned shadow: fully editable, normal custom-profile rules.
791
- continue;
792
- }
793
-
794
- // Must resolve through the same provider column as
795
- // `overlayEffectiveProfilesForWire`, or an echo of the wire view would
796
- // not match `effective` and an honest round-trip would be rejected.
797
- const resolved = resolveDefaultProfileForProvider(
798
- typedCurrent?.success ? typedCurrent.data : undefined,
799
- name,
800
- getConfig().llm.default,
801
- );
802
- const effective: Record<string, unknown> | undefined = resolved
803
- ? { ...resolved }
804
- : undefined;
805
- for (const key of Object.keys(entry.data)) {
806
- if (key === "source" || key === "status") {
807
- continue;
808
- }
809
- const matchesEffective =
810
- effective != null &&
811
- key in effective &&
812
- isDeepStrictEqual(entry.data[key], effective[key]);
813
- if (!current.success || !(key in current.data)) {
814
- // Not on disk: a value matching the wire view is a client echo of
815
- // catalog content — drop it. Anything else stays for the guard.
816
- if (matchesEffective) {
817
- delete entry.data[key];
818
- }
819
- continue;
820
- }
821
- // The key exists on disk (a frozen overlay field, or stale content an
822
- // overlay persisted). Deleting it would read as a key removal on a
823
- // full-entry SET, so instead: an echo of the wire value that differs
824
- // from disk is pinned back to the on-disk value (no change for the
825
- // guard to reject); everything else is left for the guard's
826
- // frozen-field comparison.
827
- if (
828
- matchesEffective &&
829
- !isDeepStrictEqual(entry.data[key], current.data[key])
830
- ) {
831
- entry.data[key] = current.data[key];
832
- }
833
- }
834
-
835
- if (current.success) {
836
- // Existing stub: the guard compares the merged result against it and
837
- // rejects everything but the status re-enable.
838
- if (entry.data.source === undefined) {
839
- entry.data.source = "managed";
840
- }
841
- profiles[name] = entry.data;
842
- continue;
843
- }
844
-
845
- // No on-disk entry: the name is catalog-owned. Nothing but a clean echo
846
- // (which reduced to source/status above) may pass. The wire-only flags
847
- // ride echoes too — the write path's schema parse strips them before
848
- // anything persists.
849
- if ("source" in entry.data && entry.data.source !== "managed") {
850
- throw new BadRequestError(
851
- `Cannot create profile "${name}" — the name is reserved for a code-defined default profile.`,
852
- );
853
- }
854
- if (
855
- "status" in entry.data &&
856
- entry.data.status != null &&
857
- entry.data.status !== "active"
858
- ) {
859
- throw new BadRequestError(`Cannot disable cloud profile "${name}".`);
860
- }
861
- const residual = Object.keys(entry.data).filter(
862
- (key) =>
863
- key !== "source" &&
864
- key !== "status" &&
865
- key !== "invariant" &&
866
- key !== "supportsVision",
867
- );
868
- if (residual.length > 0) {
869
- throw new BadRequestError(
870
- `Cannot edit cloud profile "${name}" fields [${residual.join(", ")}]. ` +
871
- `Cloud profiles are read-only; duplicate to a custom profile to customize.`,
872
- );
873
- }
874
- entry.data.source = "managed";
875
- profiles[name] = entry.data;
876
- }
877
-
878
- patch.llm = llm.data;
879
- }
880
-
881
- /**
882
- * The enriched profile shape clients consume: {@link ProfileEntry} plus the
883
- * wire-only flags `enrichProfilesForWire` stamps onto config-read responses.
884
- * The flags are deliberately absent from `ProfileEntry` — the persisted
885
- * schema — so a config write's schema parse strips echoed copies instead of
886
- * persisting them.
887
- */
888
- type WireProfileEntry = ProfileEntry & {
889
- invariant?: boolean;
890
- supportsVision?: boolean;
891
- };
892
-
893
- /**
894
- * Annotate each profile in `config.llm.profiles` with wire-only flags —
895
- * never persisted to disk:
896
- *
897
- * - `supportsVision`: resolved from the model catalog, with a profile
898
- * `inputModalities.image` override winning when set. Unknown (provider,
899
- * model) pairs with no override default to `true` (fail-open) so image
900
- * upload remains available for custom / unlisted models.
901
- * - `invariant`: `true` for managed-source entries of the cloud profile
902
- * names (`INVARIANT_PROFILE_NAMES`); absent otherwise. Source-gated to
903
- * match `assertInvariantProfilesPreserved` — a user-owned profile sharing
904
- * a cloud name is fully editable, so it must render as a normal custom
905
- * profile.
906
- */
907
- function enrichProfilesForWire(config: AssistantConfig): void {
908
- const profiles: Record<string, WireProfileEntry> = config.llm.profiles;
909
- for (const [name, entry] of Object.entries(profiles)) {
910
- if (INVARIANT_PROFILE_NAMES.has(name) && entry.source === "managed") {
911
- entry.invariant = true;
912
- }
913
-
914
- if (entry.provider == null || entry.model == null) {
915
- continue;
916
- }
917
-
918
- const catalogModel = catalogEntryFor(entry.provider, entry.model);
919
- const catalogVision = catalogModel?.supportsVision;
920
- const imageOverride = modalitiesOf({
921
- inputModalities: entry.inputModalities,
922
- })?.image;
923
- entry.supportsVision =
924
- resolveModalityOverride(imageOverride, catalogVision) ?? true;
925
- }
926
- }
927
-
928
655
  /**
929
656
  * Return the JSON Schema for the assistant config (full or scoped).
930
657
  *
@@ -957,204 +684,6 @@ function handleGetConfigSchema({ queryParams = {} }: RouteHandlerArgs) {
957
684
  };
958
685
  }
959
686
 
960
- export function rejectCloudProfileDeletion(
961
- body: Record<string, unknown>,
962
- ): void {
963
- const llm = RawLlmWriteSliceSchema.safeParse(body.llm);
964
- if (!llm.success) {
965
- return;
966
- }
967
- if (llm.data.profiles === null) {
968
- throw new BadRequestError(
969
- "Cannot null llm.profiles — cloud profiles would be deleted.",
970
- );
971
- }
972
- const profiles = llm.data.profiles;
973
- if (!profiles) {
974
- return;
975
- }
976
- const existingLlm = RawLlmWriteSliceSchema.safeParse(getConfig().llm);
977
- const existingProfiles = existingLlm.success
978
- ? (existingLlm.data.profiles ?? {})
979
- : {};
980
- for (const name of Object.keys(profiles)) {
981
- if (profiles[name] !== null || !MANAGED_PROFILE_NAMES.has(name)) {
982
- continue;
983
- }
984
- // Only block deletion when the on-disk entry is Cadet Cloud. A
985
- // user-owned profile sharing a cloud name carries a non-managed `source`
986
- // and is freely deletable.
987
- const existing = RawProfileEntrySchema.safeParse(existingProfiles[name]);
988
- if (existing.success && existing.data.source === "managed") {
989
- throw new BadRequestError(`Cannot delete cloud profile "${name}".`);
990
- }
991
- }
992
- }
993
-
994
- /**
995
- * Enforce the cloud-profile invariants at the config-write choke point.
996
- *
997
- * Protects the thin managed stubs (`INVARIANT_PROFILE_NAMES`) that live in
998
- * workspace config. Default profile CONTENT is code-owned and only exists on
999
- * the wire, so the guard's job is the stub itself: it checks entries present
1000
- * in the OLD config, and `normalizeCloudProfileWrites` reduces incoming
1001
- * managed entries to the workspace-owned fields before this comparison runs.
1002
- *
1003
- * Invariance is gated on managed ownership: a name is enforced only when the
1004
- * OLD entry's `source` is `"managed"`. A user-owned profile sharing a
1005
- * managed name stays fully editable and deletable — for os-beta that state
1006
- * is real and supported, because the flag-gated reconcile refuses to
1007
- * overwrite a same-named user profile.
1008
- *
1009
- * For each invariant name where the OLD raw config carries a cloud-source
1010
- * plain-object entry at `llm.profiles[name]`:
1011
- *
1012
- * - The NEW raw config must still carry a plain-object entry at the same
1013
- * path — deletion, non-object overwrite, and subtree replacement are all
1014
- * rejected by this single check, regardless of route.
1015
- * - `status` is one-directional: effective status is
1016
- * `entry.status !== "disabled"` (absence/null = active). An active managed
1017
- * profile can never be disabled; a changed `status` must be `"active"`,
1018
- * `null`, or absent — re-enabling a disabled profile. Any other value is
1019
- * rejected.
1020
- * - The wire-only flags (`invariant`, `supportsVision`) are ignored on both
1021
- * sides: the write path's schema parse strips them from incoming writes,
1022
- * but configs persisted before that was true may still carry them on
1023
- * disk, and treating that stale key as a removed field would reject every
1024
- * round-trip write until reboot reseeds the profile.
1025
- * - Every other field is frozen: any changed, added, or removed key across
1026
- * the union of both entries' keys (except `status` and the wire-only keys)
1027
- * is rejected. A pre-existing on-disk override (e.g. `topP`) is preserved
1028
- * but frozen — it passes the guard only while it doesn't change.
1029
- */
1030
- function assertInvariantProfilesPreserved(
1031
- oldRaw: Record<string, unknown>,
1032
- newRaw: Record<string, unknown>,
1033
- ): void {
1034
- const oldProfiles = asMutablePlainObject(
1035
- asMutablePlainObject(oldRaw.llm)?.profiles,
1036
- );
1037
- if (!oldProfiles) {
1038
- return;
1039
- }
1040
- const newProfiles = asMutablePlainObject(
1041
- asMutablePlainObject(newRaw.llm)?.profiles,
1042
- );
1043
-
1044
- for (const name of INVARIANT_PROFILE_NAMES) {
1045
- const oldEntry = asMutablePlainObject(oldProfiles[name]);
1046
- if (!oldEntry) {
1047
- continue;
1048
- }
1049
- if (oldEntry.source !== "managed") {
1050
- continue;
1051
- }
1052
-
1053
- const newEntry = newProfiles
1054
- ? asMutablePlainObject(newProfiles[name])
1055
- : null;
1056
- if (!newEntry) {
1057
- throw new BadRequestError(
1058
- `Cannot delete or replace cloud profile "${name}". Cloud profiles are read-only.`,
1059
- );
1060
- }
1061
-
1062
- if (!isDeepStrictEqual(oldEntry.status, newEntry.status)) {
1063
- if (newEntry.status === "disabled") {
1064
- throw new BadRequestError(`Cannot disable cloud profile "${name}".`);
1065
- }
1066
- if (newEntry.status !== "active" && newEntry.status != null) {
1067
- throw new BadRequestError(
1068
- `Cannot set status ${JSON.stringify(newEntry.status)} on cloud profile "${name}". ` +
1069
- `Only re-enabling (status "active") is allowed.`,
1070
- );
1071
- }
1072
- }
1073
-
1074
- const changedKeys = [
1075
- ...new Set([...Object.keys(oldEntry), ...Object.keys(newEntry)]),
1076
- ].filter(
1077
- (key) =>
1078
- key !== "status" &&
1079
- key !== "invariant" &&
1080
- key !== "supportsVision" &&
1081
- !isDeepStrictEqual(oldEntry[key], newEntry[key]),
1082
- );
1083
- if (changedKeys.length > 0) {
1084
- throw new BadRequestError(
1085
- `Cannot edit cloud profile "${name}" fields [${changedKeys.join(", ")}]. ` +
1086
- `Cloud profiles are read-only; duplicate to a custom profile to customize.`,
1087
- );
1088
- }
1089
- }
1090
- }
1091
-
1092
- /**
1093
- * Custom profiles persist as complete overrides: any user-source, non-mix
1094
- * profile entry this write creates or changes is completed against the
1095
- * workspace default base (`completeCustomProfile`) before it lands on disk, so no
1096
- * write path can persist a new partial profile. Entries the write did not
1097
- * touch are left byte-identical — an unrelated config write must not rewrite
1098
- * profile entries. Entries that do not parse as a `ProfileEntry` are also
1099
- * left untouched; full-config parsing owns their rejection/recovery.
1100
- */
1101
- function completeChangedCustomProfiles(
1102
- preWrite: Record<string, unknown>,
1103
- raw: Record<string, unknown>,
1104
- ): void {
1105
- const llm = RawLlmWriteSliceSchema.safeParse(raw.llm);
1106
- if (!llm.success || !llm.data.profiles) {
1107
- return;
1108
- }
1109
- const profiles = llm.data.profiles;
1110
- const parsedDefault = LLMConfigBase.safeParse(llm.data.default ?? {});
1111
- if (!parsedDefault.success) {
1112
- return;
1113
- }
1114
- const priorLlm = RawLlmWriteSliceSchema.safeParse(preWrite.llm);
1115
- const prior = priorLlm.success ? (priorLlm.data.profiles ?? {}) : {};
1116
- for (const [name, entry] of Object.entries(profiles)) {
1117
- if (isDeepStrictEqual(entry, prior[name])) {
1118
- continue;
1119
- }
1120
- const parsedEntry = ProfileEntry.safeParse(entry);
1121
- if (!parsedEntry.success) {
1122
- continue;
1123
- }
1124
- // `source: "managed"` is only meaningful on catalog-owned names. On any
1125
- // other name it would skip completion (and read as a locked entry), so a
1126
- // body claiming it is normalized to user-owned before completing.
1127
- const entryData =
1128
- parsedEntry.data.source === "managed" && !MANAGED_PROFILE_NAMES.has(name)
1129
- ? { ...parsedEntry.data, source: "user" as const }
1130
- : parsedEntry.data;
1131
- // Merge the completed known fields over the original raw entry at every
1132
- // depth: `safeParse` strips keys the schema doesn't know (top-level and
1133
- // inside nested objects like `contextWindow`), and dropping them here
1134
- // would delete forward-compatible fields whenever the entry is edited.
1135
- const entryRecord = RawProfileEntrySchema.safeParse(entry);
1136
- if (!entryRecord.success) {
1137
- continue;
1138
- }
1139
- profiles[name] = mergePreservingUnknownKeys(
1140
- entryRecord.data,
1141
- completeCustomProfile(parsedDefault.data, entryData) as Record<
1142
- string,
1143
- unknown
1144
- >,
1145
- );
1146
- }
1147
- // A zod parse copies — write the completed slice back so `raw` carries it.
1148
- raw.llm = llm.data;
1149
- }
1150
-
1151
- /**
1152
- * Persist a mutated raw config object to disk and synchronize the running
1153
- * daemon (file-watcher, embedding cache, provider registry).
1154
- *
1155
- * Shared by `handlePatchConfig` and `handleSetConfig` so both write paths get
1156
- * identical post-write side effects.
1157
- */
1158
687
  /**
1159
688
  * Reject writes that would store a routing-identity provider with a missing
1160
689
  * or unroutable model (see `routingIdentityModelIssue`). saveRawConfig
@@ -1226,6 +755,17 @@ function assertRoutableIdentityEntries(
1226
755
  throw new BadRequestError(`${providerIssue} (${label})`);
1227
756
  }
1228
757
  }
758
+ // A model-less entry under a code-defined default name is the thin
759
+ // managed stub — its content is code-owned, so the routing-identity
760
+ // model check applies only to entries carrying their own body.
761
+ const profileName = label.startsWith("llm.profiles.")
762
+ ? label.slice("llm.profiles.".length)
763
+ : undefined;
764
+ const isManagedName =
765
+ profileName != null && MANAGED_PROFILE_NAMES.has(profileName);
766
+ if (entry.model == null && isManagedName) {
767
+ continue;
768
+ }
1229
769
  const issue = routingIdentityModelIssue(
1230
770
  entry.provider,
1231
771
  typeof entry.model === "string" ? entry.model : undefined,
@@ -1236,67 +776,6 @@ function assertRoutableIdentityEntries(
1236
776
  }
1237
777
  }
1238
778
 
1239
- /**
1240
- * Reject writes that would persist unsupported `fallbackProfile` metadata.
1241
- * Automatic fallbacks are code-owned for managed default profiles. The write
1242
- * paths save raw config without a full-schema parse, so this check keeps a
1243
- * custom pointer from reaching disk. It runs unconditionally so an existing
1244
- * hand-edited pointer blocks unrelated rewrites until the user clears it with
1245
- * `null`.
1246
- */
1247
- function assertCodeOwnedFallbackProfiles(raw: Record<string, unknown>): void {
1248
- const llm = RawLlmWriteSliceSchema.safeParse(raw.llm);
1249
- if (!llm.success) {
1250
- return;
1251
- }
1252
- // The sibling `llm.default` decides whether the cloud backups are valid
1253
- // fallback targets: they resolve on the cloud column alone.
1254
- const issues = collectFallbackProfileIssues(
1255
- llm.data.profiles ?? undefined,
1256
- llm.data.default,
1257
- );
1258
- if (issues.length > 0) {
1259
- throw new BadRequestError(issues.map((issue) => issue.message).join(" "));
1260
- }
1261
- }
1262
-
1263
- /**
1264
- * Stamp the managed marker onto `llm.profiles` entries of catalog-owned
1265
- * names that a write created without a `source` — a source-less entry reads
1266
- * as a user shadow that blocks the catalog body. A user-owned entry sharing
1267
- * the name (a legacy shadow) keeps its source, so its edits stay fully
1268
- * user-owned.
1269
- */
1270
- function stampManagedProfileEntries(
1271
- preWrite: Record<string, unknown>,
1272
- raw: Record<string, unknown>,
1273
- ): void {
1274
- const newLlm = RawLlmWriteSliceSchema.safeParse(raw.llm);
1275
- if (!newLlm.success || !newLlm.data.profiles) {
1276
- return;
1277
- }
1278
- const oldLlm = RawLlmWriteSliceSchema.safeParse(preWrite.llm);
1279
- let stamped = false;
1280
- for (const name of MANAGED_PROFILE_NAMES) {
1281
- const written = RawProfileEntrySchema.safeParse(newLlm.data.profiles[name]);
1282
- if (!written.success || written.data.source !== undefined) {
1283
- continue;
1284
- }
1285
- const prior = RawProfileEntrySchema.safeParse(
1286
- oldLlm.success ? oldLlm.data.profiles?.[name] : undefined,
1287
- );
1288
- if (prior.success && prior.data.source !== "managed") {
1289
- continue;
1290
- }
1291
- written.data.source = "managed";
1292
- newLlm.data.profiles[name] = written.data;
1293
- stamped = true;
1294
- }
1295
- if (stamped) {
1296
- raw.llm = newLlm.data;
1297
- }
1298
- }
1299
-
1300
779
  export async function commitConfigWrite(
1301
780
  raw: Record<string, unknown>,
1302
781
  opLabel: string,
@@ -1313,11 +792,7 @@ export async function commitConfigWrite(
1313
792
  if (!chatSettings.success) {
1314
793
  throw new BadRequestError(chatSettings.error.message);
1315
794
  }
1316
- stampManagedProfileEntries(preWrite, raw);
1317
- completeChangedCustomProfiles(preWrite, raw);
1318
- assertInvariantProfilesPreserved(preWrite, raw);
1319
795
  assertRoutableIdentityEntries(preWrite, raw);
1320
- assertCodeOwnedFallbackProfiles(raw);
1321
796
 
1322
797
  // Suppress the file-watcher callback for the duration of the debounce
1323
798
  // window. Without this, the ConfigWatcher detects the config.json write
@@ -1348,7 +823,7 @@ export async function commitConfigWrite(
1348
823
  invalidateConfigCache();
1349
824
  rescheduleHeartbeatIfTimezoneChanged(preWrite, raw);
1350
825
  // Reinitialize providers so the live registry reflects the new config.
1351
- // Suppress disk writes inside loadConfig() — we just wrote the raw config
826
+ // Suppress disk writes inside getConfig() — we just wrote the raw config
1352
827
  // and the first-launch seed path would overwrite it with full defaults.
1353
828
  try {
1354
829
  await withSuppressedConfigDiskWrites(async () => {
@@ -1369,16 +844,24 @@ export async function commitConfigWrite(
1369
844
  }
1370
845
 
1371
846
  async function handlePatchConfig({ body }: RouteHandlerArgs) {
1372
- const patch = JsonRecordSchema.safeParse(body);
1373
- if (!patch.success || Object.keys(patch.data).length === 0) {
847
+ // First-class body parse — the same schema the route spec declares — so a
848
+ // malformed subtree is rejected with path-qualified messages before any
849
+ // guard or merge runs.
850
+ const patch = ConfigPatchRequestSchema.safeParse(body);
851
+ if (!patch.success) {
852
+ throw new BadRequestError(
853
+ patch.error.issues
854
+ .map((issue) =>
855
+ issue.path.length > 0
856
+ ? `${issue.path.join(".")}: ${issue.message}`
857
+ : issue.message,
858
+ )
859
+ .join("; "),
860
+ );
861
+ }
862
+ if (Object.keys(patch.data).length === 0) {
1374
863
  throw new BadRequestError("Body must be a non-empty JSON object");
1375
864
  }
1376
- // Managed-profile normalization runs on the fragment before it merges:
1377
- // an honest `config get` → PATCH round-trip reduces to a no-op, and a
1378
- // genuine edit reaches the invariant guard's precise rejections.
1379
- normalizeCloudProfileWrites(patch.data);
1380
- rejectCloudProfileDeletion(patch.data);
1381
-
1382
865
  // A patch is partial: merge it onto the current config so the schema
1383
866
  // parse in `handleSetConfig` always sees a complete config — untouched
1384
867
  // values ride, sparse fragments are completed by the merge base.
@@ -1399,7 +882,7 @@ async function handlePatchConfig({ body }: RouteHandlerArgs) {
1399
882
 
1400
883
  await handleSetConfig(merged);
1401
884
 
1402
- return handleGetConfig();
885
+ return getConfig();
1403
886
  }
1404
887
 
1405
888
  /**
@@ -1442,20 +925,18 @@ async function handleSetConfig(merged: AssistantConfig): Promise<void> {
1442
925
  * {@link handleSetConfig} write.
1443
926
  */
1444
927
  async function handleSetConfigByKey({ body }: RouteHandlerArgs) {
1445
- const parsedBody = JsonRecordSchema.safeParse(body);
928
+ const parsedBody = ConfigSetByKeyBodySchema.safeParse(body);
1446
929
  if (!parsedBody.success) {
1447
930
  throw new BadRequestError(
1448
931
  "Body must be a JSON object with `path` and `value`",
1449
932
  );
1450
933
  }
1451
934
  const path = parsedBody.data.path;
1452
- if (typeof path !== "string" || path.length === 0) {
1453
- throw new BadRequestError("`path` must be a non-empty string");
1454
- }
1455
935
  // `value` must be present (use explicit `null` to clear a key). Without
1456
936
  // this check, `undefined` flows into `setNestedValue` and gets dropped by
1457
937
  // `JSON.stringify` at save time, silently removing the key - which is
1458
- // distinct from the documented "set to null" semantics.
938
+ // distinct from the documented "set to null" semantics. zod cannot express
939
+ // "any value, but the key must exist", so this stays a domain check.
1459
940
  if (!Object.hasOwn(parsedBody.data, "value")) {
1460
941
  throw new BadRequestError(
1461
942
  "`value` is required (use `null` to clear a key)",
@@ -1469,39 +950,7 @@ async function handleSetConfigByKey({ body }: RouteHandlerArgs) {
1469
950
  if (path === "ingress.publicBaseUrl") {
1470
951
  dropTunnelRecordsForNewUrl(merged.ingress, value);
1471
952
  }
1472
- // Any llm-touching SET rides the same normalization the profile routes
1473
- // use: `normalizeCloudProfileWrites` reduces an echo of the wire view to
1474
- // the workspace-owned stub and rejects genuine edits to managed entries.
1475
- // Its normalization lives in the parsed llm slice (a zod parse copies), so
1476
- // the written value below is the normalized slice/entry — and a leaf write
1477
- // below the entry level rides raw; the ladder stamps the managed marker
1478
- // onto an entry the write creates.
1479
- const pathSegments = path.split(".");
1480
- let writtenValue: unknown = value;
1481
- if (pathSegments[0] === "llm") {
1482
- const patchShape: Record<string, unknown> = {};
1483
- setNestedValue(patchShape, path, value);
1484
- normalizeCloudProfileWrites(patchShape);
1485
- rejectCloudProfileDeletion(patchShape);
1486
- const patchLlm = RawLlmWriteSliceSchema.safeParse(patchShape.llm);
1487
- if (patchLlm.success) {
1488
- if (pathSegments.length === 1) {
1489
- writtenValue = patchLlm.data;
1490
- } else if (pathSegments[1] === "profiles") {
1491
- if (pathSegments.length === 2) {
1492
- if (patchLlm.data.profiles) {
1493
- writtenValue = patchLlm.data.profiles;
1494
- }
1495
- } else if (pathSegments.length === 3) {
1496
- const normalized = patchLlm.data.profiles?.[pathSegments[2]!];
1497
- if (normalized !== undefined) {
1498
- writtenValue = normalized;
1499
- }
1500
- }
1501
- }
1502
- }
1503
- }
1504
- setNestedValue(merged, path, writtenValue);
953
+ setNestedValue(merged, path, value);
1505
954
  scrubNulledAcpAgentLeaves(merged);
1506
955
 
1507
956
  await handleSetConfig(merged);
@@ -1568,14 +1017,14 @@ async function handleReplaceInferenceProfile({
1568
1017
  throw new BadRequestError(`Invalid profile fragment: ${detail}`);
1569
1018
  }
1570
1019
  // A managed name with no existing entry stays protected so users can't
1571
- // shadow a seeded cloud profile. An existing entry carrying a non-managed
1572
- // `source` is user-owned and remains fully editable.
1020
+ // shadow a seeded cloud profile. An existing entry on a non-`cadet`
1021
+ // provider is user-owned and remains fully editable.
1573
1022
  const existingProfile = asMutablePlainObject(
1574
1023
  getConfig().llm.profiles?.[name],
1575
1024
  );
1576
1025
  const isCloud =
1577
1026
  MANAGED_PROFILE_NAMES.has(name) &&
1578
- (existingProfile == null || existingProfile.source === "managed");
1027
+ (existingProfile == null || existingProfile.provider === "cadet");
1579
1028
  if (CODE_OWNED_PROFILE_NAMES.has(name)) {
1580
1029
  // A code-owned profile resolves from the catalog whatever the workspace
1581
1030
  // holds, so even a status re-enable would persist a stub that never
@@ -1603,9 +1052,8 @@ async function handleReplaceInferenceProfile({
1603
1052
  );
1604
1053
  }
1605
1054
  if (isCloud) {
1606
- // Cloud profiles are daemon-seeded and read-only — the commit guard
1607
- // (`assertInvariantProfilesPreserved`) rejects every write to them
1608
- // except re-enabling a disabled profile. Enforce the same contract up
1055
+ // Cloud profiles are daemon-seeded and read-only — enforce the contract
1056
+ // up front: the only body a cloud PUT accepts
1609
1057
  // front: the only body a cloud PUT accepts is a pure status re-enable
1610
1058
  // (`{status: "active"}`, or `{status: null}` to clear back to
1611
1059
  // active-by-absence). Rejecting here keeps the error message ahead of
@@ -1633,13 +1081,7 @@ async function handleReplaceInferenceProfile({
1633
1081
  // here against the live profile set — otherwise an invalid mix would persist
1634
1082
  // and break the next full config reparse.
1635
1083
  if (parsed.data.mix != null) {
1636
- const MIX_ALLOWED_KEYS = new Set([
1637
- "mix",
1638
- "label",
1639
- "description",
1640
- "status",
1641
- "source",
1642
- ]);
1084
+ const MIX_ALLOWED_KEYS = new Set(["mix", "label", "description", "status"]);
1643
1085
  const extraneous = Object.keys(parsed.data).filter(
1644
1086
  (k) => !MIX_ALLOWED_KEYS.has(k),
1645
1087
  );
@@ -1826,10 +1268,6 @@ function applyCloudProfileReenable(
1826
1268
  const nextProfile: Record<string, unknown> = {
1827
1269
  ...(asMutablePlainObject(profiles[name]) ?? {}),
1828
1270
  };
1829
- // Only reached for managed-owned names (the route's managed gate), so a
1830
- // freshly created stub must carry the cloud marker the effective view
1831
- // and write guards key on.
1832
- nextProfile.source = "managed";
1833
1271
  if (status === null) {
1834
1272
  delete nextProfile.status;
1835
1273
  } else {
@@ -2194,7 +1632,7 @@ export const ROUTES: RouteDefinition[] = [
2194
1632
  description: "Return the raw settings.json configuration object.",
2195
1633
  tags: ["config"],
2196
1634
  responseBody: ConfigGetResponseSchema,
2197
- handler: handleGetConfig,
1635
+ handler: getConfig,
2198
1636
  },
2199
1637
  {
2200
1638
  operationId: "config_patch",
@@ -2226,6 +1664,7 @@ export const ROUTES: RouteDefinition[] = [
2226
1664
  "(preserves explicit null, replaces object subtrees instead of merging). " +
2227
1665
  "Used by the `assistant config set <key> <value>` CLI command.",
2228
1666
  tags: ["config"],
1667
+ requestBody: ConfigSetByKeyBodySchema,
2229
1668
  handler: handleSetConfigByKey,
2230
1669
  },
2231
1670
  {