@vellumai/assistant 0.6.5 → 0.6.6

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 (443) hide show
  1. package/AGENTS.md +9 -1
  2. package/ARCHITECTURE.md +15 -17
  3. package/Dockerfile +6 -4
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
  5. package/docs/architecture/integrations.md +32 -39
  6. package/docs/architecture/memory.md +25 -30
  7. package/docs/architecture/security.md +7 -6
  8. package/docs/browser-use-architecture-phase2.md +63 -20
  9. package/docs/plugins.md +761 -0
  10. package/examples/plugins/echo/README.md +132 -0
  11. package/examples/plugins/echo/package.json +17 -0
  12. package/examples/plugins/echo/register.ts +187 -0
  13. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  14. package/openapi.yaml +212 -68
  15. package/package.json +1 -1
  16. package/src/__tests__/app-compiler.test.ts +57 -0
  17. package/src/__tests__/approval-cascade.test.ts +7 -2
  18. package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
  19. package/src/__tests__/avatar-generator.test.ts +4 -2
  20. package/src/__tests__/bundled-asset.test.ts +6 -6
  21. package/src/__tests__/catalog-cache.test.ts +69 -0
  22. package/src/__tests__/checker.test.ts +459 -171
  23. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  24. package/src/__tests__/compaction-events.test.ts +501 -0
  25. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  26. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  27. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  28. package/src/__tests__/config-model-image-provider.test.ts +110 -0
  29. package/src/__tests__/config-schema.test.ts +22 -9
  30. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  31. package/src/__tests__/contacts-tools.test.ts +26 -0
  32. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  33. package/src/__tests__/context-window-manager.test.ts +355 -4
  34. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  35. package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
  36. package/src/__tests__/conversation-agent-loop.test.ts +30 -141
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
  38. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
  40. package/src/__tests__/conversation-pairing.test.ts +174 -10
  41. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
  42. package/src/__tests__/conversation-process-callsite.test.ts +3 -0
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
  44. package/src/__tests__/conversation-queue.test.ts +29 -14
  45. package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
  47. package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
  48. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  49. package/src/__tests__/conversation-slash-queue.test.ts +7 -2
  50. package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
  51. package/src/__tests__/conversation-speed-override.test.ts +6 -1
  52. package/src/__tests__/conversation-title-service.test.ts +116 -0
  53. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
  54. package/src/__tests__/conversation-usage.test.ts +1 -1
  55. package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
  56. package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
  57. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
  58. package/src/__tests__/credential-health-service.test.ts +78 -9
  59. package/src/__tests__/credential-security-invariants.test.ts +2 -2
  60. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  61. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  62. package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
  63. package/src/__tests__/first-greeting.test.ts +247 -5
  64. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  65. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  66. package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
  67. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
  68. package/src/__tests__/host-proxy-interface.test.ts +36 -2
  69. package/src/__tests__/image-credentials.test.ts +137 -0
  70. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  71. package/src/__tests__/injector-chain.test.ts +526 -0
  72. package/src/__tests__/intent-routing.test.ts +0 -26
  73. package/src/__tests__/llm-call-pipeline.test.ts +285 -0
  74. package/src/__tests__/llm-schema.test.ts +1 -1
  75. package/src/__tests__/media-generate-image.test.ts +119 -13
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
  77. package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
  78. package/src/__tests__/migration-import-from-url.test.ts +5 -68
  79. package/src/__tests__/model-intents.test.ts +4 -2
  80. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  81. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  82. package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
  83. package/src/__tests__/oauth-apps-routes.test.ts +1 -1
  84. package/src/__tests__/oauth-cli.test.ts +14 -12
  85. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  86. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  87. package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
  88. package/src/__tests__/oauth-providers-routes.test.ts +3 -2
  89. package/src/__tests__/oauth-store.test.ts +41 -76
  90. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  91. package/src/__tests__/openai-image-service.test.ts +368 -0
  92. package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
  93. package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
  94. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  95. package/src/__tests__/persistence-pipeline.test.ts +377 -0
  96. package/src/__tests__/pipeline-runner.test.ts +565 -0
  97. package/src/__tests__/platform.test.ts +5 -2
  98. package/src/__tests__/plugin-bootstrap.test.ts +483 -0
  99. package/src/__tests__/plugin-registry.test.ts +273 -0
  100. package/src/__tests__/plugin-route-contribution.test.ts +288 -0
  101. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  102. package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
  103. package/src/__tests__/plugin-types.test.ts +320 -0
  104. package/src/__tests__/pricing.test.ts +44 -12
  105. package/src/__tests__/proxy-approval-callback.test.ts +69 -8
  106. package/src/__tests__/reaction-persistence.test.ts +1 -0
  107. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  108. package/src/__tests__/registry.test.ts +0 -2
  109. package/src/__tests__/schedule-routes.test.ts +131 -1
  110. package/src/__tests__/scheduler-recurrence.test.ts +14 -70
  111. package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
  112. package/src/__tests__/secret-detection-handler.test.ts +0 -10
  113. package/src/__tests__/shell-identity.test.ts +0 -134
  114. package/src/__tests__/suggestion-routes.test.ts +103 -4
  115. package/src/__tests__/task-memory-cleanup.test.ts +1 -0
  116. package/src/__tests__/task-scheduler.test.ts +3 -15
  117. package/src/__tests__/test-preload.ts +11 -0
  118. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  119. package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
  120. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  121. package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
  122. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
  123. package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
  124. package/src/__tests__/tool-executor.test.ts +141 -0
  125. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  126. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  127. package/src/__tests__/user-plugin-loader.test.ts +191 -0
  128. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  129. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  130. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  131. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  132. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  133. package/src/__tests__/workspace-policy.test.ts +21 -3
  134. package/src/agent/loop.ts +340 -102
  135. package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
  136. package/src/approvals/guardian-request-resolvers.ts +80 -0
  137. package/src/backup/__tests__/backup-worker.test.ts +2 -13
  138. package/src/backup/backup-worker.ts +3 -15
  139. package/src/bundler/app-compiler.ts +84 -1
  140. package/src/calls/call-state.ts +2 -2
  141. package/src/channels/__tests__/types.test.ts +3 -3
  142. package/src/channels/types.ts +6 -4
  143. package/src/cli/__tests__/notifications.test.ts +87 -211
  144. package/src/cli/commands/__tests__/backup.test.ts +1 -1
  145. package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
  146. package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
  147. package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
  148. package/src/cli/commands/backup.ts +2 -2
  149. package/src/cli/commands/clients.ts +138 -0
  150. package/src/cli/commands/completions.ts +2 -9
  151. package/src/cli/commands/conversations.ts +55 -7
  152. package/src/cli/commands/image-generation.ts +33 -34
  153. package/src/cli/commands/notifications.ts +68 -103
  154. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  155. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  156. package/src/cli/commands/oauth/connect.ts +2 -2
  157. package/src/cli/commands/oauth/providers.ts +176 -8
  158. package/src/cli/commands/oauth/status.ts +46 -36
  159. package/src/cli/commands/skills.ts +3 -4
  160. package/src/cli/program.ts +25 -29
  161. package/src/config/__tests__/backup-schema.test.ts +7 -2
  162. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  163. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  164. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  165. package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
  166. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  170. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  171. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  172. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
  174. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  175. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  176. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  177. package/src/config/bundled-tool-registry.ts +0 -15
  178. package/src/config/feature-flag-registry.json +17 -1
  179. package/src/config/schema.ts +19 -0
  180. package/src/config/schemas/backup.ts +1 -1
  181. package/src/config/schemas/conversations.ts +16 -0
  182. package/src/config/schemas/llm.ts +2 -3
  183. package/src/config/schemas/security.ts +6 -6
  184. package/src/config/schemas/tts.ts +11 -0
  185. package/src/config/skill-state.ts +6 -2
  186. package/src/config/skills.ts +94 -5
  187. package/src/context/__tests__/compact-prompt.test.ts +27 -9
  188. package/src/context/prompts/compact.md +26 -12
  189. package/src/context/tool-result-truncation.ts +3 -63
  190. package/src/context/window-manager.ts +190 -16
  191. package/src/credential-health/credential-health-service.ts +19 -6
  192. package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
  193. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  194. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  195. package/src/daemon/config-watcher.ts +0 -2
  196. package/src/daemon/context-overflow-policy.ts +4 -13
  197. package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
  198. package/src/daemon/conversation-agent-loop.ts +984 -683
  199. package/src/daemon/conversation-history.ts +10 -19
  200. package/src/daemon/conversation-lifecycle.ts +37 -19
  201. package/src/daemon/conversation-notifiers.ts +2 -110
  202. package/src/daemon/conversation-process.ts +14 -7
  203. package/src/daemon/conversation-runtime-assembly.ts +532 -411
  204. package/src/daemon/conversation-tool-setup.ts +41 -4
  205. package/src/daemon/conversation.ts +80 -35
  206. package/src/daemon/external-plugins-bootstrap.ts +478 -0
  207. package/src/daemon/first-greeting.ts +191 -14
  208. package/src/daemon/handlers/config-model.ts +11 -0
  209. package/src/daemon/handlers/skills.ts +5 -1
  210. package/src/daemon/lifecycle.ts +33 -68
  211. package/src/daemon/message-types/computer-use.ts +2 -34
  212. package/src/daemon/message-types/conversations.ts +49 -0
  213. package/src/daemon/message-types/messages.ts +12 -0
  214. package/src/daemon/server.ts +5 -3
  215. package/src/daemon/shutdown-handlers.ts +2 -12
  216. package/src/daemon/tool-side-effects.ts +14 -56
  217. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
  218. package/src/heartbeat/heartbeat-service.ts +24 -1
  219. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  220. package/src/home/emit-feed-event.ts +7 -0
  221. package/src/home/feed-types.ts +41 -2
  222. package/src/home/rewrite-command-preview.ts +66 -0
  223. package/src/ipc/__tests__/socket-path.test.ts +11 -50
  224. package/src/ipc/cli-client.ts +1 -1
  225. package/src/ipc/cli-server.ts +3 -3
  226. package/src/ipc/gateway-client.ts +4 -1
  227. package/src/ipc/routes/browser-context.ts +2 -0
  228. package/src/ipc/routes/browser.ts +1 -0
  229. package/src/ipc/routes/get-contact.ts +16 -0
  230. package/src/ipc/routes/index.ts +14 -0
  231. package/src/ipc/routes/list-clients.ts +31 -0
  232. package/src/ipc/routes/merge-contacts.ts +17 -0
  233. package/src/ipc/routes/notification.ts +133 -0
  234. package/src/ipc/routes/rename-conversation.ts +59 -0
  235. package/src/ipc/routes/search-contacts.ts +19 -0
  236. package/src/ipc/routes/upsert-contact.ts +25 -0
  237. package/src/ipc/socket-path.ts +14 -38
  238. package/src/media/app-icon-generator.ts +23 -46
  239. package/src/media/avatar-router.ts +26 -41
  240. package/src/media/gemini-image-service.ts +8 -41
  241. package/src/media/image-credentials.ts +73 -0
  242. package/src/media/image-service.ts +85 -0
  243. package/src/media/openai-image-service.ts +131 -0
  244. package/src/media/types.ts +46 -0
  245. package/src/memory/conversation-crud.ts +48 -18
  246. package/src/memory/conversation-queries.ts +57 -4
  247. package/src/memory/conversation-title-service.ts +25 -0
  248. package/src/memory/db-init.ts +8 -0
  249. package/src/memory/embedding-gemini.test.ts +41 -2
  250. package/src/memory/embedding-gemini.ts +6 -1
  251. package/src/memory/graph/bootstrap.test.ts +282 -0
  252. package/src/memory/graph/bootstrap.ts +8 -5
  253. package/src/memory/graph/extraction.ts +10 -2
  254. package/src/memory/graph/graph-search.test.ts +1 -0
  255. package/src/memory/graph/inspect.ts +2 -2
  256. package/src/memory/graph/retriever.ts +10 -3
  257. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  258. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  259. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  260. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  261. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  262. package/src/memory/migrations/index.ts +4 -0
  263. package/src/memory/pkb/pkb-index.test.ts +1 -0
  264. package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
  265. package/src/memory/pkb/pkb-search.test.ts +65 -4
  266. package/src/memory/pkb/pkb-search.ts +40 -18
  267. package/src/memory/qdrant-client.test.ts +60 -0
  268. package/src/memory/qdrant-client.ts +25 -0
  269. package/src/memory/schema/infrastructure.ts +1 -0
  270. package/src/memory/schema/oauth.ts +4 -1
  271. package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
  272. package/src/messaging/providers/slack/render-transcript.ts +58 -0
  273. package/src/notifications/conversation-pairing.ts +78 -19
  274. package/src/notifications/copy-composer.ts +0 -5
  275. package/src/notifications/emit-signal.ts +1 -1
  276. package/src/notifications/signal.ts +1 -2
  277. package/src/oauth/AGENTS.md +1 -1
  278. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  279. package/src/oauth/connect-orchestrator.ts +8 -34
  280. package/src/oauth/connect-types.ts +6 -10
  281. package/src/oauth/manual-token-connection.ts +23 -0
  282. package/src/oauth/oauth-store.ts +30 -14
  283. package/src/oauth/provider-serializer.ts +6 -1
  284. package/src/oauth/seed-providers.ts +56 -108
  285. package/src/outbound-proxy/http-forwarder.ts +9 -0
  286. package/src/permissions/approval-policy.test.ts +293 -18
  287. package/src/permissions/approval-policy.ts +110 -58
  288. package/src/permissions/arg-parser.test.ts +161 -0
  289. package/src/permissions/arg-parser.ts +141 -0
  290. package/src/permissions/bash-risk-classifier.test.ts +414 -2
  291. package/src/permissions/bash-risk-classifier.ts +303 -60
  292. package/src/permissions/checker.ts +157 -29
  293. package/src/permissions/command-registry.test.ts +239 -0
  294. package/src/permissions/command-registry.ts +234 -54
  295. package/src/permissions/defaults.ts +5 -4
  296. package/src/permissions/gateway-threshold-reader.ts +196 -0
  297. package/src/permissions/prompter.ts +4 -0
  298. package/src/permissions/risk-types.ts +61 -4
  299. package/src/permissions/schedule-risk-classifier.test.ts +129 -0
  300. package/src/permissions/schedule-risk-classifier.ts +85 -0
  301. package/src/permissions/shell-identity.ts +2 -42
  302. package/src/permissions/types.ts +2 -0
  303. package/src/permissions/workspace-policy.ts +8 -3
  304. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  305. package/src/plugins/defaults/compaction.ts +145 -0
  306. package/src/plugins/defaults/empty-response.ts +126 -0
  307. package/src/plugins/defaults/history-repair.ts +85 -0
  308. package/src/plugins/defaults/index.ts +116 -0
  309. package/src/plugins/defaults/injectors.ts +491 -0
  310. package/src/plugins/defaults/llm-call.ts +82 -0
  311. package/src/plugins/defaults/memory-retrieval.ts +226 -0
  312. package/src/plugins/defaults/overflow-reduce.ts +181 -0
  313. package/src/plugins/defaults/persistence.ts +129 -0
  314. package/src/plugins/defaults/title-generate.ts +95 -0
  315. package/src/plugins/defaults/token-estimate.ts +104 -0
  316. package/src/plugins/defaults/tool-error.ts +126 -0
  317. package/src/plugins/defaults/tool-execute.ts +89 -0
  318. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  319. package/src/plugins/pipeline.ts +316 -0
  320. package/src/plugins/plugin-skill-contributions.ts +292 -0
  321. package/src/plugins/registry.ts +241 -0
  322. package/src/plugins/types.ts +1134 -0
  323. package/src/plugins/user-loader.ts +177 -0
  324. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  325. package/src/providers/model-catalog.ts +52 -29
  326. package/src/providers/model-intents.ts +1 -1
  327. package/src/providers/openrouter/client.ts +5 -1
  328. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  329. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  330. package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
  331. package/src/providers/speech-to-text/xai-realtime.ts +39 -14
  332. package/src/runtime/AGENTS.md +25 -16
  333. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
  334. package/src/runtime/__tests__/client-registry.test.ts +293 -0
  335. package/src/runtime/client-registry.ts +261 -0
  336. package/src/runtime/http-server.ts +77 -8
  337. package/src/runtime/http-types.ts +0 -2
  338. package/src/runtime/migrations/vbundle-builder.ts +1 -22
  339. package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
  340. package/src/runtime/routes/approval-routes.ts +17 -0
  341. package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
  342. package/src/runtime/routes/conversation-routes.ts +223 -116
  343. package/src/runtime/routes/inbound-message-handler.ts +88 -13
  344. package/src/runtime/routes/memory-item-routes.test.ts +1 -0
  345. package/src/runtime/routes/migration-routes.ts +0 -3
  346. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
  347. package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
  348. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
  349. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
  350. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
  351. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
  352. package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
  353. package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
  354. package/src/runtime/routes/playground/deps.ts +56 -0
  355. package/src/runtime/routes/playground/force-compact.ts +73 -0
  356. package/src/runtime/routes/playground/guard.ts +37 -0
  357. package/src/runtime/routes/playground/index.ts +28 -0
  358. package/src/runtime/routes/playground/inject-failures.ts +159 -0
  359. package/src/runtime/routes/playground/reset-circuit.ts +115 -0
  360. package/src/runtime/routes/playground/seed-conversation.ts +139 -0
  361. package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
  362. package/src/runtime/routes/playground/state.ts +78 -0
  363. package/src/runtime/routes/schedule-routes.ts +89 -8
  364. package/src/runtime/skill-route-registry.ts +75 -15
  365. package/src/schedule/run-script.ts +68 -0
  366. package/src/schedule/schedule-store.ts +7 -1
  367. package/src/schedule/scheduler.ts +48 -8
  368. package/src/skills/catalog-cache.ts +12 -5
  369. package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
  370. package/src/tools/browser/browser-execution.ts +88 -19
  371. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  372. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  373. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  374. package/src/tools/browser/cdp-client/factory.ts +15 -4
  375. package/src/tools/executor.ts +126 -74
  376. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  377. package/src/tools/permission-checker.ts +98 -49
  378. package/src/tools/policy-context.ts +4 -0
  379. package/src/tools/registry.ts +140 -3
  380. package/src/tools/schedule/create.ts +23 -8
  381. package/src/tools/schedule/update.ts +3 -1
  382. package/src/tools/secret-detection-handler.ts +0 -51
  383. package/src/tools/system/avatar-generator.ts +6 -2
  384. package/src/tools/types.ts +28 -2
  385. package/src/util/platform.ts +7 -2
  386. package/src/util/pricing.ts +26 -3
  387. package/src/workspace/migrations/006-services-config.ts +2 -4
  388. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  389. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
  390. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  391. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  392. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  393. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  394. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  395. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  396. package/src/workspace/migrations/registry.ts +12 -0
  397. package/tsconfig.json +1 -1
  398. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  399. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  400. package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
  401. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  402. package/src/__tests__/hooks-blocking.test.ts +0 -178
  403. package/src/__tests__/hooks-cli.test.ts +0 -182
  404. package/src/__tests__/hooks-config.test.ts +0 -108
  405. package/src/__tests__/hooks-discovery.test.ts +0 -211
  406. package/src/__tests__/hooks-integration.test.ts +0 -196
  407. package/src/__tests__/hooks-manager.test.ts +0 -226
  408. package/src/__tests__/hooks-runner.test.ts +0 -175
  409. package/src/__tests__/hooks-settings.test.ts +0 -160
  410. package/src/__tests__/hooks-templates.test.ts +0 -169
  411. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  412. package/src/__tests__/hooks-watch.test.ts +0 -112
  413. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  414. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  415. package/src/__tests__/send-notification-tool.test.ts +0 -83
  416. package/src/cli/commands/shotgun.ts +0 -266
  417. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  418. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  419. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
  420. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  421. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  422. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  423. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  424. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  425. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  426. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  427. package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
  428. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  429. package/src/daemon/context-overflow-approval.ts +0 -52
  430. package/src/daemon/watch-handler.ts +0 -399
  431. package/src/hooks/cli.ts +0 -253
  432. package/src/hooks/config.ts +0 -100
  433. package/src/hooks/discovery.ts +0 -135
  434. package/src/hooks/manager.ts +0 -179
  435. package/src/hooks/runner.ts +0 -117
  436. package/src/hooks/templates.ts +0 -77
  437. package/src/hooks/types.ts +0 -75
  438. package/src/oauth/scope-policy.ts +0 -89
  439. package/src/runtime/gateway-internal-client.ts +0 -94
  440. package/src/runtime/routes/watch-routes.ts +0 -156
  441. package/src/signals/shotgun.ts +0 -203
  442. package/src/tools/watch/screen-watch.ts +0 -144
  443. package/src/tools/watch/watch-state.ts +0 -142
@@ -8,6 +8,7 @@ import {
8
8
  setServiceField,
9
9
  } from "../../config/raw-config-utils.js";
10
10
  import { VALID_INFERENCE_PROVIDERS } from "../../config/schemas/services.js";
11
+ import { providerForImageModelPrefix } from "../../media/types.js";
11
12
  import type { ProviderCatalogEntry } from "../../providers/model-catalog.js";
12
13
  import {
13
14
  isModelInCatalog,
@@ -219,6 +220,16 @@ export async function setModel(
219
220
  export function setImageGenModel(modelId: string, ctx: ModelSetContext): void {
220
221
  const raw = loadRawConfig();
221
222
  setServiceField(raw, "image-generation", "model", modelId);
223
+ // Keep the derived provider in sync with the selected model so downstream
224
+ // routing never sends a Gemini request to an OpenAI model (or vice versa).
225
+ // The prefix logic is shared with workspace migration 006-services-config
226
+ // via providerForImageModelPrefix().
227
+ setServiceField(
228
+ raw,
229
+ "image-generation",
230
+ "provider",
231
+ providerForImageModelPrefix(modelId),
232
+ );
222
233
 
223
234
  const wasSuppressed = ctx.suppressConfigReload;
224
235
  ctx.setSuppressConfigReload(true);
@@ -326,10 +326,14 @@ export function postInstallSkill(
326
326
 
327
327
  /** Map the old `source` field to the new `kind` axis. */
328
328
  function deriveKind(
329
- source: "bundled" | "managed" | "workspace" | "extra" | "catalog",
329
+ source: "bundled" | "managed" | "workspace" | "extra" | "catalog" | "plugin",
330
330
  ): SlimSkillResponse["kind"] {
331
331
  if (source === "bundled") return "bundled";
332
332
  if (source === "catalog") return "catalog";
333
+ // Plugin-contributed skills are framework-provided like bundled skills —
334
+ // expose them under the same "bundled" kind so the UI doesn't invent a
335
+ // new category that existing clients don't know how to render.
336
+ if (source === "plugin") return "bundled";
333
337
  return "installed"; // managed, workspace, extra
334
338
  }
335
339
 
@@ -12,8 +12,6 @@ import { setVoiceBridgeDeps } from "../calls/voice-session-bridge.js";
12
12
  import { initFeatureFlagOverrides } from "../config/assistant-feature-flags.js";
13
13
  import {
14
14
  getPlatformAssistantId,
15
- getQdrantHttpPortEnv,
16
- getQdrantUrlEnv,
17
15
  getRuntimeHttpHost,
18
16
  getRuntimeHttpPort,
19
17
  setIngressPublicBaseUrl,
@@ -43,8 +41,6 @@ import {
43
41
  startFeedScheduler,
44
42
  } from "../home/feed-scheduler.js";
45
43
  import { backfillRelationshipStateIfMissing } from "../home/relationship-state-writer.js";
46
- import { getHookManager } from "../hooks/manager.js";
47
- import { installTemplates } from "../hooks/templates.js";
48
44
  import { closeSentry, initSentry, setSentryDeviceId } from "../instrument.js";
49
45
  import { getMcpServerManager } from "../mcp/manager.js";
50
46
  import * as attachmentsStore from "../memory/attachments-store.js";
@@ -63,7 +59,7 @@ import {
63
59
  } from "../memory/embedding-backend.js";
64
60
  import { enqueueMemoryJob } from "../memory/jobs-store.js";
65
61
  import { startMemoryJobsWorker } from "../memory/jobs-worker.js";
66
- import { initQdrantClient } from "../memory/qdrant-client.js";
62
+ import { initQdrantClient, resolveQdrantUrl } from "../memory/qdrant-client.js";
67
63
  import { QdrantManager } from "../memory/qdrant-manager.js";
68
64
  import { rotateToolInvocations } from "../memory/tool-usage-store.js";
69
65
  import { deleteOldTraceEvents } from "../memory/trace-event-store.js";
@@ -73,6 +69,7 @@ import {
73
69
  } from "../notifications/emit-signal.js";
74
70
  import { backfillManualTokenConnections } from "../oauth/manual-token-connection.js";
75
71
  import { seedOAuthProviders } from "../oauth/seed-providers.js";
72
+ import { loadUserPlugins } from "../plugins/user-loader.js";
76
73
  import { ensurePromptFiles } from "../prompts/system-prompt.js";
77
74
  import { resolveManagedProxyContext } from "../providers/managed-proxy/context.js";
78
75
  import { buildAssistantEvent } from "../runtime/assistant-event.js";
@@ -107,6 +104,7 @@ import {
107
104
  getInterfacesDir,
108
105
  getWorkspaceDir,
109
106
  } from "../util/platform.js";
107
+ import { APP_VERSION } from "../version.js";
110
108
  import {
111
109
  listWorkItems,
112
110
  updateWorkItem,
@@ -124,6 +122,7 @@ import {
124
122
  cleanupPidFileIfOwner,
125
123
  writePid,
126
124
  } from "./daemon-control.js";
125
+ import { bootstrapPlugins } from "./external-plugins-bootstrap.js";
127
126
  import {
128
127
  createGuardianActionCopyGenerator,
129
128
  createGuardianFollowUpConversationGenerator,
@@ -148,7 +147,6 @@ import {
148
147
  import { seedInterfaceFiles } from "./seed-files.js";
149
148
  import { DaemonServer } from "./server.js";
150
149
  import { installShutdownHandlers } from "./shutdown-handlers.js";
151
- import { handleWatchObservation } from "./watch-handler.js";
152
150
 
153
151
  // Re-export public API so existing consumers don't need to change imports
154
152
  export type { StopResult } from "./daemon-control.js";
@@ -340,8 +338,7 @@ export async function runDaemon(): Promise<void> {
340
338
 
341
339
  seedInterfaceFiles();
342
340
 
343
- log.info("Daemon startup: installing templates and initializing DB");
344
- installTemplates();
341
+ log.info("Daemon startup: initializing DB");
345
342
  ensurePromptFiles();
346
343
 
347
344
  // DB must be initialized before workspace migrations because some
@@ -699,6 +696,33 @@ export async function runDaemon(): Promise<void> {
699
696
  }
700
697
  }
701
698
 
699
+ // Populate the registry with user plugins from `~/.vellum/plugins/*`
700
+ // AFTER first-party plugins have already registered via their static
701
+ // side-effect imports. User plugins may fail to load individually; a
702
+ // failing user plugin is logged and skipped so one bad install can't
703
+ // prevent the daemon from starting. Ordering is load-bearing:
704
+ // first-party registrations → user registrations → bootstrap (init).
705
+ // Both groups are fully registered before any `init()` runs so plugins
706
+ // that depend on each other's registration observably see a stable
707
+ // registry at init time.
708
+ await loadUserPlugins();
709
+
710
+ // Bootstrap registered plugins. Runs after the plugin registry is
711
+ // populated (first-party static side-effect imports + user plugins
712
+ // loaded above) and before the DaemonServer starts handling
713
+ // conversations. Credential resolution + per-plugin storage directory
714
+ // creation happen here. Wrapped in try/catch so a failing plugin can't
715
+ // block daemon startup — bootstrapPlugins internally tears down any
716
+ // partially-initialized plugins before throwing.
717
+ try {
718
+ await bootstrapPlugins({ config, assistantVersion: APP_VERSION });
719
+ } catch (err) {
720
+ log.warn(
721
+ { err },
722
+ "Plugin bootstrap failed — continuing startup with degraded plugin functionality",
723
+ );
724
+ }
725
+
702
726
  // Start the DaemonServer (conversation manager) before Qdrant so HTTP
703
727
  // routes can begin accepting requests while Qdrant initializes.
704
728
  log.info("Daemon startup: starting DaemonServer");
@@ -741,13 +765,7 @@ export async function runDaemon(): Promise<void> {
741
765
  // Initialize Qdrant vector store and memory worker in the background so the
742
766
  // RuntimeHttpServer can start accepting requests without waiting for Qdrant.
743
767
  async function initializeQdrantAndMemory(): Promise<void> {
744
- // Prefer QDRANT_HTTP_PORT (locally-spawned Qdrant on a specific port) over
745
- // QDRANT_URL (external Qdrant instance) so the CLI can set the port without
746
- // triggering QdrantManager's external mode which skips local process spawn.
747
- const qdrantHttpPort = getQdrantHttpPortEnv();
748
- const qdrantUrl = qdrantHttpPort
749
- ? `http://127.0.0.1:${qdrantHttpPort}`
750
- : getQdrantUrlEnv() || config.memory.qdrant.url;
768
+ const qdrantUrl = resolveQdrantUrl(config);
751
769
  log.info({ qdrantUrl }, "Daemon startup: initializing Qdrant");
752
770
  const manager = new QdrantManager({ url: qdrantUrl });
753
771
  bgRefs.qdrantManager = manager;
@@ -941,29 +959,6 @@ export async function runDaemon(): Promise<void> {
941
959
  throwOnError: true,
942
960
  });
943
961
  },
944
- (schedule) => {
945
- void emitNotificationSignal({
946
- sourceEventName: "schedule.complete",
947
- sourceChannel: "scheduler",
948
- sourceContextId: schedule.id,
949
- attentionHints: {
950
- requiresAction: false,
951
- urgency: "medium",
952
- isAsyncBackground: true,
953
- visibleInSourceNow: false,
954
- },
955
- contextPayload: {
956
- scheduleId: schedule.id,
957
- name: schedule.name,
958
- },
959
- conversationMetadata: {
960
- groupId: "system:scheduled",
961
- scheduleJobId: schedule.id,
962
- source: "schedule",
963
- },
964
- dedupeKey: `schedule:complete:${schedule.id}:${Date.now()}`,
965
- });
966
- },
967
962
  (notification) => {
968
963
  void emitNotificationSignal({
969
964
  sourceEventName: "watcher.notification",
@@ -1132,28 +1127,6 @@ export async function runDaemon(): Promise<void> {
1132
1127
  );
1133
1128
  },
1134
1129
  },
1135
- getWatchDeps: () => {
1136
- const ctx = server.getHandlerContext();
1137
- return {
1138
- handleWatchObservation: async (params) => {
1139
- await handleWatchObservation(
1140
- {
1141
- type: "watch_observation",
1142
- watchId: params.watchId,
1143
- conversationId: params.conversationId,
1144
- ocrText: params.ocrText,
1145
- appName: params.appName,
1146
- windowTitle: params.windowTitle,
1147
- bundleIdentifier: params.bundleIdentifier,
1148
- timestamp: params.timestamp,
1149
- captureIndex: params.captureIndex,
1150
- totalExpected: params.totalExpected,
1151
- },
1152
- ctx,
1153
- );
1154
- },
1155
- };
1156
- },
1157
1130
  getRecordingDeps: () => ({
1158
1131
  getHandlerContext: () => server.getHandlerContext(),
1159
1132
  }),
@@ -1390,13 +1363,6 @@ export async function runDaemon(): Promise<void> {
1390
1363
  log.warn({ err }, "Assistant symlink installation failed — continuing");
1391
1364
  }
1392
1365
 
1393
- const hookManager = getHookManager();
1394
- hookManager.watch();
1395
-
1396
- void hookManager.trigger("daemon-start", {
1397
- pid: process.pid,
1398
- });
1399
-
1400
1366
  // Download embedding runtime in background (non-blocking).
1401
1367
  // If download fails, local embeddings gracefully fall back to cloud backends.
1402
1368
  void (async () => {
@@ -1510,7 +1476,6 @@ export async function runDaemon(): Promise<void> {
1510
1476
  workspaceHeartbeat,
1511
1477
  heartbeat,
1512
1478
  filing,
1513
- hookManager,
1514
1479
  runtimeHttp,
1515
1480
  scheduler,
1516
1481
  feedScheduler,
@@ -1,4 +1,4 @@
1
- // Computer use and watch observation types.
1
+ // Computer use and recording types.
2
2
 
3
3
  import type { CommandIntent, UserMessageAttachment } from "./shared.js";
4
4
 
@@ -15,19 +15,6 @@ export interface TaskSubmit {
15
15
  commandIntent?: CommandIntent;
16
16
  }
17
17
 
18
- export interface WatchObservation {
19
- type: "watch_observation";
20
- watchId: string;
21
- conversationId: string;
22
- ocrText: string;
23
- appName?: string;
24
- windowTitle?: string;
25
- bundleIdentifier?: string;
26
- timestamp: number;
27
- captureIndex: number;
28
- totalExpected: number;
29
- }
30
-
31
18
  // === Recording ===
32
19
 
33
20
  /** Recording options shared across standalone and CU recording flows. */
@@ -89,30 +76,11 @@ export interface RecordingResume {
89
76
  recordingId: string;
90
77
  }
91
78
 
92
- export interface WatchStarted {
93
- type: "watch_started";
94
- conversationId: string;
95
- watchId: string;
96
- durationSeconds: number;
97
- intervalSeconds: number;
98
- }
99
-
100
- export interface WatchCompleteRequest {
101
- type: "watch_complete_request";
102
- conversationId: string;
103
- watchId: string;
104
- }
105
-
106
79
  // --- Domain-level union aliases (consumed by the barrel file) ---
107
80
 
108
- export type _ComputerUseClientMessages =
109
- | TaskSubmit
110
- | WatchObservation
111
- | RecordingStatus;
81
+ export type _ComputerUseClientMessages = TaskSubmit | RecordingStatus;
112
82
 
113
83
  export type _ComputerUseServerMessages =
114
- | WatchStarted
115
- | WatchCompleteRequest
116
84
  | RecordingStart
117
85
  | RecordingStop
118
86
  | RecordingPause
@@ -421,8 +421,16 @@ export interface UsageResponse {
421
421
  model: string;
422
422
  }
423
423
 
424
+ /**
425
+ * Emitted after a compaction turn completes (both auto-compaction and
426
+ * `/compact`). Carries the fresh `estimatedInputTokens` so clients can refresh
427
+ * the context-window indicator without waiting for the next `usage_update`.
428
+ *
429
+ * Scoped per-conversation — see `CompactionCircuitOpen` doc for why.
430
+ */
424
431
  export interface ContextCompacted {
425
432
  type: "context_compacted";
433
+ conversationId: string;
426
434
  previousEstimatedInputTokens: number;
427
435
  estimatedInputTokens: number;
428
436
  maxInputTokens: number;
@@ -432,6 +440,22 @@ export interface ContextCompacted {
432
440
  summaryInputTokens: number;
433
441
  summaryOutputTokens: number;
434
442
  summaryModel: string;
443
+ /**
444
+ * Quality signals for the generated summary. Emitted for every
445
+ * compaction (including truncation-only paths where the summary text
446
+ * is unchanged from the prior pass). Consumers can use these to detect
447
+ * regressions without needing to read the summary text itself.
448
+ *
449
+ * - `summaryCharCount`: length of the produced summary text.
450
+ * - `summaryHeaderCount`: number of `## ` section headers in the summary.
451
+ * - `summaryHadMemoryEcho`: `true` if the summary contains any runtime
452
+ * injection tag (e.g. `<memory`, `<turn_context>`, `<workspace>`).
453
+ * Should always be `false` — `true` indicates the compaction strip
454
+ * logic failed to remove an injected block from the summarizer input.
455
+ */
456
+ summaryCharCount?: number;
457
+ summaryHeaderCount?: number;
458
+ summaryHadMemoryEcho?: boolean;
435
459
  }
436
460
 
437
461
  /**
@@ -439,14 +463,38 @@ export interface ContextCompacted {
439
463
  * summary-LLM failures (with local fallback covering each), auto-compaction is
440
464
  * suspended until `openUntil` to avoid repeatedly hammering a broken provider.
441
465
  * User-initiated compaction (`/compact`, `force: true`) bypasses the breaker.
466
+ *
467
+ * `conversationId` scopes the event so clients can ignore breaker trips from
468
+ * other conversations — `EventStreamClient` broadcasts every parsed server
469
+ * message to all subscribers, so without this field a breaker trip in one
470
+ * conversation would set the "auto-compaction paused" banner on every open
471
+ * `ChatViewModel`.
442
472
  */
443
473
  export interface CompactionCircuitOpen {
444
474
  type: "compaction_circuit_open";
475
+ conversationId: string;
445
476
  reason: "3_consecutive_failures";
446
477
  /** Timestamp (ms since epoch) when the breaker will allow auto-compaction again. */
447
478
  openUntil: number;
448
479
  }
449
480
 
481
+ /**
482
+ * Emitted when the compaction circuit breaker transitions from open → closed
483
+ * because a successful compaction reset
484
+ * `ctx.compactionCircuitOpenUntil`. The Swift client clears its banner state
485
+ * on receipt so the "auto-compaction paused" indicator dismisses immediately
486
+ * instead of lingering until the original `openUntil` deadline (up to 1h).
487
+ *
488
+ * Only fires on the open→closed transition — successful compactions while
489
+ * the breaker was already closed would be noise.
490
+ *
491
+ * Scoped per-conversation — see `CompactionCircuitOpen` doc for why.
492
+ */
493
+ export interface CompactionCircuitClosed {
494
+ type: "compaction_circuit_closed";
495
+ conversationId: string;
496
+ }
497
+
450
498
  export type ConversationErrorCode =
451
499
  | "PROVIDER_NETWORK"
452
500
  | "PROVIDER_RATE_LIMIT"
@@ -545,6 +593,7 @@ export type _ConversationsServerMessages =
545
593
  | UsageResponse
546
594
  | ContextCompacted
547
595
  | CompactionCircuitOpen
596
+ | CompactionCircuitClosed
548
597
  | ConversationErrorMessage
549
598
  | ConversationInfo
550
599
  | ConversationTitleUpdated
@@ -145,6 +145,14 @@ export interface ToolResult {
145
145
  imageDataList?: string[];
146
146
  /** The tool_use block ID for client-side correlation. */
147
147
  toolUseId?: string;
148
+ /** Risk level from the classifier ("low" | "medium" | "high" | "unknown"). */
149
+ riskLevel?: string;
150
+ /** Human-readable reason for the risk classification. */
151
+ riskReason?: string;
152
+ /** Whether the daemon is running in a containerized (Docker) environment. */
153
+ isContainerized?: boolean;
154
+ /** Scope options ladder for the rule editor modal (narrowest to broadest). */
155
+ riskScopeOptions?: Array<{ pattern: string; label: string }>;
148
156
  }
149
157
 
150
158
  export interface ConfirmationRequest {
@@ -153,6 +161,10 @@ export interface ConfirmationRequest {
153
161
  toolName: string;
154
162
  input: Record<string, unknown>;
155
163
  riskLevel: string;
164
+ /** Human-readable reason for the risk classification (e.g. "Modifies remote repository state"). */
165
+ riskReason?: string;
166
+ /** Whether the daemon is running in a containerized (Docker) environment. */
167
+ isContainerized?: boolean;
156
168
  executionTarget?: "sandbox" | "host";
157
169
  allowlistOptions: Array<{
158
170
  label: string;
@@ -886,6 +886,7 @@ export class DaemonServer {
886
886
  trustClass: conversation.trustContext?.trustClass ?? "guardian",
887
887
  hostBrowserProxy: conversation.hostBrowserProxy,
888
888
  transportInterface: conversation.transportInterface,
889
+ hostBrowserRegistryRouted: !!conversation.hostBrowserSenderOverride,
889
890
  };
890
891
  });
891
892
 
@@ -1302,9 +1303,10 @@ export class DaemonServer {
1302
1303
  // Chrome-extension host_browser wiring is intentionally not supported
1303
1304
  // through this entry point. `prepareConversationForMessage` constructs
1304
1305
  // host_browser proxies that capture `conversation.getCurrentSender()`
1305
- // directly, which would route browser frames through the daemon SSE
1306
- // channel instead of the `ChromeExtensionRegistry`. Chrome-extension
1307
- // flows reach host_browser exclusively through the
1306
+ // directly, which routes browser frames through the daemon SSE channel.
1307
+ // This is correct for macOS (SSE-based host proxy), but chrome-extension
1308
+ // requires the `ChromeExtensionRegistry` WebSocket transport instead.
1309
+ // Chrome-extension flows reach host_browser exclusively through the
1308
1310
  // `/v1/messages` flow in `conversation-routes.ts`, which wires a
1309
1311
  // registry-aware sender and sets `hostBrowserSenderOverride`.
1310
1312
  //
@@ -3,7 +3,6 @@ import * as Sentry from "@sentry/node";
3
3
  import type { BackupWorkerHandle } from "../backup/backup-worker.js";
4
4
  import type { FilingService } from "../filing/filing-service.js";
5
5
  import type { HeartbeatService } from "../heartbeat/heartbeat-service.js";
6
- import type { HookManager } from "../hooks/manager.js";
7
6
  import type { McpServerManager } from "../mcp/manager.js";
8
7
  import { getSqlite, resetDb } from "../memory/db.js";
9
8
  import type { QdrantManager } from "../memory/qdrant-manager.js";
@@ -23,7 +22,6 @@ export interface ShutdownDeps {
23
22
  workspaceHeartbeat: WorkspaceHeartbeatService;
24
23
  heartbeat: HeartbeatService;
25
24
  filing: FilingService;
26
- hookManager: HookManager;
27
25
  runtimeHttp: RuntimeHttpServer | null;
28
26
  scheduler: { stop(): void };
29
27
  feedScheduler: { stop(): void } | null;
@@ -44,11 +42,9 @@ export function installShutdownHandlers(deps: ShutdownDeps): void {
44
42
  shuttingDown = true;
45
43
  log.info("Shutting down daemon...");
46
44
 
47
- deps.hookManager.stopWatching();
48
-
49
45
  // Force exit if graceful shutdown takes too long.
50
- // Set this BEFORE awaiting heartbeat stop and triggering daemon-stop hooks
51
- // so it covers all potentially-blocking async shutdown work.
46
+ // Set this BEFORE awaiting heartbeat stop so it covers all
47
+ // potentially-blocking async shutdown work.
52
48
  //
53
49
  // 20s budget: 15s reserved for Meet session teardown
54
50
  // (`MeetSessionManager.shutdownAll`), plus ~5s for the remaining
@@ -67,12 +63,6 @@ export function installShutdownHandlers(deps: ShutdownDeps): void {
67
63
  await deps.heartbeat.stop();
68
64
  await deps.filing.stop();
69
65
 
70
- try {
71
- await deps.hookManager.trigger("daemon-stop", { pid: process.pid });
72
- } catch {
73
- // Don't let hook failures block shutdown
74
- }
75
-
76
66
  // Run registered skill shutdown hooks (e.g. meet-join session teardown)
77
67
  // before stopping the server so any HTTP round-trips and SSE emissions
78
68
  // still have live transports.
@@ -7,9 +7,7 @@
7
7
  * registry entry instead of another if/else branch.
8
8
  */
9
9
 
10
- import { compileApp } from "../bundler/app-compiler.js";
11
10
  import { generateAppIcon } from "../media/app-icon-generator.js";
12
- import { getApp, getAppDirPath, isMultifileApp } from "../memory/app-store.js";
13
11
  import { findActiveSession } from "../runtime/channel-verification-service.js";
14
12
  import { deliverVerificationSlack } from "../runtime/verification-outbound-actions.js";
15
13
  import { updatePublishedAppDeployment } from "../services/published-app-updater.js";
@@ -39,41 +37,22 @@ export type PostExecutionHook = (
39
37
 
40
38
  // ── Helpers ──────────────────────────────────────────────────────────
41
39
 
42
- /** Shared logic for refreshing app surfaces, broadcasting changes, and triggering auto-deploy. */
43
- function handleAppChange(
40
+ /**
41
+ * Propagate an app change to connected clients and the publish pipeline.
42
+ *
43
+ * Compilation is the responsibility of the tool executor (see
44
+ * `executeAppCreate`, `executeAppRefresh`): executors own the source→dist
45
+ * transform and surface `compile_errors` in their result when it fails.
46
+ * Post-execution hooks only observe the outcome and notify — they must
47
+ * not re-run a compile because `compileApp()` begins with `rm -rf dist/`
48
+ * and would race with the executor's own output (LUM-1153).
49
+ */
50
+ function notifyAppChanged(
44
51
  ctx: ToolSetupContext,
45
52
  appId: string,
46
53
  broadcastToAllClients: ((msg: ServerMessage) => void) | undefined,
47
54
  opts?: { fileChange?: boolean; status?: string },
48
55
  ): void {
49
- const app = getApp(appId);
50
-
51
- // Multifile apps need a recompile before refreshing surfaces so the
52
- // WebView picks up the latest compiled output.
53
- if (app && isMultifileApp(app)) {
54
- const appDir = getAppDirPath(appId);
55
- void compileApp(appDir)
56
- .then((result) => {
57
- if (!result.ok) {
58
- log.warn(
59
- { appId, errors: result.errors },
60
- "Recompile failed on app change, serving stale dist/",
61
- );
62
- }
63
- refreshSurfacesForApp(ctx, appId, opts);
64
- broadcastToAllClients?.({ type: "app_files_changed", appId });
65
- void updatePublishedAppDeployment(appId);
66
- })
67
- .catch((err) => {
68
- log.warn({ appId, err }, "Recompile threw on app change");
69
- // Still refresh surfaces with stale output
70
- refreshSurfacesForApp(ctx, appId, opts);
71
- broadcastToAllClients?.({ type: "app_files_changed", appId });
72
- void updatePublishedAppDeployment(appId);
73
- });
74
- return;
75
- }
76
-
77
56
  refreshSurfacesForApp(ctx, appId, opts);
78
57
  broadcastToAllClients?.({ type: "app_files_changed", appId });
79
58
  void updatePublishedAppDeployment(appId);
@@ -115,7 +94,7 @@ registerHook(
115
94
  // trigger live reload.
116
95
  ensureAppSourceWatcher();
117
96
 
118
- handleAppChange(ctx, parsed.id, broadcastToAllClients);
97
+ notifyAppChanged(ctx, parsed.id, broadcastToAllClients);
119
98
 
120
99
  // Fire-and-forget: generate an app icon in the background.
121
100
  // When complete, broadcast again so clients pick up the new icon.
@@ -165,33 +144,12 @@ registerHook(
165
144
  );
166
145
 
167
146
  // Trigger surface refresh + broadcast when an app is refreshed.
168
- // If the executor already compiled (multifile path), skip the redundant
169
- // recompile and just refresh surfaces / broadcast / deploy directly.
170
147
  registerHook(
171
148
  "app_refresh",
172
- (_name, input, result, { ctx, broadcastToAllClients }) => {
149
+ (_name, input, _result, { ctx, broadcastToAllClients }) => {
173
150
  const appId = input.app_id as string | undefined;
174
151
  if (!appId) return;
175
-
176
- // executeAppRefresh already compiled multifile apps and included a
177
- // "compiled" field in the result. Skip the expensive recompile and
178
- // go straight to surface refresh + broadcast + deploy.
179
- let alreadyCompiled = false;
180
- try {
181
- const parsed = JSON.parse(result.content) as { compiled?: boolean };
182
- alreadyCompiled = parsed.compiled !== undefined;
183
- } catch {
184
- // Result wasn't valid JSON — fall through to handleAppChange.
185
- }
186
-
187
- if (alreadyCompiled) {
188
- const opts = { fileChange: true };
189
- refreshSurfacesForApp(ctx, appId, opts);
190
- broadcastToAllClients?.({ type: "app_files_changed", appId });
191
- void updatePublishedAppDeployment(appId);
192
- } else {
193
- handleAppChange(ctx, appId, broadcastToAllClients, { fileChange: true });
194
- }
152
+ notifyAppChanged(ctx, appId, broadcastToAllClients, { fileChange: true });
195
153
  },
196
154
  );
197
155