@vellumai/assistant 0.8.1 → 0.8.3

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 (630) hide show
  1. package/ARCHITECTURE.md +13 -19
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +17 -0
  5. package/docker-init-apt-root.sh +167 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +642 -5
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
  21. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  23. package/src/__tests__/anthropic-provider.test.ts +45 -0
  24. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  25. package/src/__tests__/app-executors.test.ts +220 -4
  26. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  27. package/src/__tests__/bundled-asset.test.ts +6 -6
  28. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  29. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  30. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  31. package/src/__tests__/clawhub.test.ts +75 -16
  32. package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
  33. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  34. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  35. package/src/__tests__/config-schema.test.ts +21 -0
  36. package/src/__tests__/config-set-route.test.ts +80 -0
  37. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  38. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  39. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  40. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  41. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  42. package/src/__tests__/context-token-estimator.test.ts +31 -65
  43. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  44. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  45. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  46. package/src/__tests__/conversation-agent-loop.test.ts +59 -1
  47. package/src/__tests__/conversation-error.test.ts +42 -3
  48. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  49. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  50. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  51. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  52. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  53. package/src/__tests__/conversation-pairing.test.ts +54 -0
  54. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  55. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  56. package/src/__tests__/conversation-queue.test.ts +4 -1
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
  58. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  59. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  60. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  61. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  64. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  65. package/src/__tests__/date-context.test.ts +45 -0
  66. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  67. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  68. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  69. package/src/__tests__/dm-backfill.test.ts +121 -10
  70. package/src/__tests__/document-tool-security.test.ts +258 -0
  71. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  72. package/src/__tests__/edit-propagation.test.ts +33 -0
  73. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  74. package/src/__tests__/external-plugin-loader.test.ts +151 -55
  75. package/src/__tests__/filing-service.test.ts +140 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  77. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  78. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  79. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  80. package/src/__tests__/heartbeat-service.test.ts +24 -164
  81. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  82. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  83. package/src/__tests__/helpers/wait-for.ts +21 -0
  84. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  85. package/src/__tests__/history-repair.test.ts +73 -0
  86. package/src/__tests__/host-app-control-proxy.test.ts +507 -10
  87. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  88. package/src/__tests__/image-credentials.test.ts +1 -1
  89. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  90. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  91. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  92. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  93. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  94. package/src/__tests__/injector-background-turn.test.ts +153 -0
  95. package/src/__tests__/injector-chain.test.ts +15 -8
  96. package/src/__tests__/install-skill-routing.test.ts +155 -37
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
  98. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  99. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  100. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  101. package/src/__tests__/llm-catalog-parity.test.ts +58 -13
  102. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  103. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  104. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
  105. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  106. package/src/__tests__/llm-resolver.test.ts +255 -2
  107. package/src/__tests__/llm-usage-store.test.ts +114 -0
  108. package/src/__tests__/managed-profile-guard.test.ts +41 -29
  109. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  110. package/src/__tests__/managed-store.test.ts +84 -192
  111. package/src/__tests__/media-generate-image.test.ts +1 -1
  112. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  113. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  114. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  115. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  116. package/src/__tests__/notification-deep-link.test.ts +15 -0
  117. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  118. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  119. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  120. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  121. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  122. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  123. package/src/__tests__/openai-provider.test.ts +242 -3
  124. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  125. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  126. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  127. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  128. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  129. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
  130. package/src/__tests__/platform.test.ts +2 -0
  131. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  132. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  133. package/src/__tests__/plugin-external-api.test.ts +68 -0
  134. package/src/__tests__/plugin-registry.test.ts +0 -77
  135. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  136. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  137. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  138. package/src/__tests__/plugin-types.test.ts +3 -13
  139. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  140. package/src/__tests__/process-message-display-content.test.ts +421 -0
  141. package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  143. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
  144. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  145. package/src/__tests__/schedule-routes.test.ts +50 -3
  146. package/src/__tests__/schedule-store.test.ts +94 -0
  147. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  148. package/src/__tests__/schema-transforms.test.ts +20 -0
  149. package/src/__tests__/search-skills-unified.test.ts +0 -5
  150. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
  151. package/src/__tests__/server-history-render.test.ts +43 -0
  152. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  153. package/src/__tests__/skill-load-tool.test.ts +27 -89
  154. package/src/__tests__/skill-memory.test.ts +23 -3
  155. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  156. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  157. package/src/__tests__/skills-install-extract.test.ts +49 -38
  158. package/src/__tests__/skills-install-staging.test.ts +159 -0
  159. package/src/__tests__/skills-uninstall.test.ts +9 -41
  160. package/src/__tests__/skills.test.ts +51 -58
  161. package/src/__tests__/slack-channel-config.test.ts +9 -0
  162. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  163. package/src/__tests__/system-prompt.test.ts +670 -63
  164. package/src/__tests__/terminal-tools.test.ts +28 -1
  165. package/src/__tests__/thread-backfill.test.ts +557 -27
  166. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  167. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  168. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  169. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  170. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  171. package/src/__tests__/tool-executor.test.ts +16 -4
  172. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  173. package/src/__tests__/turn-events-store.test.ts +256 -0
  174. package/src/__tests__/twilio-routes.test.ts +4 -0
  175. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  176. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  177. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  178. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  179. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  180. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  181. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  182. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  183. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  184. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  185. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  186. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  187. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  188. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  189. package/src/a2a/__tests__/task-store.test.ts +246 -0
  190. package/src/a2a/agent-card.ts +58 -0
  191. package/src/a2a/feature-gate.ts +8 -0
  192. package/src/a2a/protocol-constants.ts +21 -0
  193. package/src/a2a/protocol-errors.ts +50 -0
  194. package/src/a2a/protocol-types.ts +162 -0
  195. package/src/a2a/task-store.ts +168 -0
  196. package/src/acp/resolve-agent.ts +1 -1
  197. package/src/agent/image-optimize.ts +13 -5
  198. package/src/agent/loop.ts +167 -18
  199. package/src/calls/voice-session-bridge.ts +61 -42
  200. package/src/channels/config.ts +9 -0
  201. package/src/channels/types.ts +122 -0
  202. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  203. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  204. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  205. package/src/cli/commands/__tests__/schedules.test.ts +960 -0
  206. package/src/cli/commands/changelog.ts +106 -42
  207. package/src/cli/commands/conversations.ts +102 -17
  208. package/src/cli/commands/default-action.ts +10 -53
  209. package/src/cli/commands/notifications.ts +388 -346
  210. package/src/cli/commands/plugins.ts +252 -0
  211. package/src/cli/commands/schedules.ts +683 -0
  212. package/src/cli/commands/telemetry.ts +40 -0
  213. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  214. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  215. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  216. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  217. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  218. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  219. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  220. package/src/cli/lib/cli-colors.ts +12 -0
  221. package/src/cli/lib/confirm-prompt.ts +79 -0
  222. package/src/cli/lib/install-from-github.ts +303 -0
  223. package/src/cli/lib/list-installed-plugins.ts +137 -0
  224. package/src/cli/lib/search-plugins.ts +163 -0
  225. package/src/cli/lib/uninstall-plugin.ts +82 -0
  226. package/src/cli/lib/unknown-command.ts +111 -0
  227. package/src/cli/program.ts +52 -2
  228. package/src/config/assistant-feature-flags.ts +24 -54
  229. package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
  230. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  231. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  232. package/src/config/bundled-skills/document/SKILL.md +23 -3
  233. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  234. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  235. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  236. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  237. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  238. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  239. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  240. package/src/config/bundled-tool-registry.ts +6 -0
  241. package/src/config/call-site-defaults.ts +105 -0
  242. package/src/config/feature-flag-registry.json +41 -9
  243. package/src/config/llm-resolver.ts +52 -1
  244. package/src/config/loader.ts +64 -38
  245. package/src/config/schema.ts +9 -10
  246. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  247. package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
  248. package/src/config/schemas/channels.ts +17 -0
  249. package/src/config/schemas/compaction.ts +28 -0
  250. package/src/config/schemas/conversations.ts +10 -0
  251. package/src/config/schemas/heartbeat.ts +23 -0
  252. package/src/config/schemas/llm-request-logs.ts +31 -7
  253. package/src/config/schemas/llm.ts +1 -0
  254. package/src/config/schemas/memory-retrieval.ts +18 -0
  255. package/src/config/schemas/memory-retrospective.ts +1 -1
  256. package/src/config/schemas/memory-v2.ts +4 -4
  257. package/src/config/schemas/memory.ts +3 -1
  258. package/src/config/schemas/tools.ts +14 -0
  259. package/src/config/seed-inference-profiles.ts +99 -29
  260. package/src/config/skills.ts +3 -96
  261. package/src/context/compactor.ts +1107 -0
  262. package/src/context/token-estimator.ts +34 -36
  263. package/src/context/window-manager.ts +197 -1520
  264. package/src/credential-execution/managed-catalog.ts +37 -0
  265. package/src/credential-health/credential-health-service.ts +280 -19
  266. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
  267. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  268. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  269. package/src/daemon/approval-generators.ts +8 -6
  270. package/src/daemon/config-watcher.ts +94 -31
  271. package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
  272. package/src/daemon/conversation-agent-loop.ts +198 -11
  273. package/src/daemon/conversation-error.ts +171 -37
  274. package/src/daemon/conversation-lifecycle.ts +53 -40
  275. package/src/daemon/conversation-messaging.ts +25 -6
  276. package/src/daemon/conversation-process.ts +49 -12
  277. package/src/daemon/conversation-runtime-assembly.ts +25 -1
  278. package/src/daemon/conversation-slash.ts +12 -5
  279. package/src/daemon/conversation-store.ts +11 -4
  280. package/src/daemon/conversation-tool-setup.ts +39 -7
  281. package/src/daemon/conversation.ts +33 -8
  282. package/src/daemon/date-context.ts +40 -0
  283. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  284. package/src/daemon/first-greeting.ts +22 -2
  285. package/src/daemon/guardian-action-generators.ts +1 -125
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  287. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  288. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  289. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  290. package/src/daemon/handlers/config-a2a.ts +289 -0
  291. package/src/daemon/handlers/config-model.ts +6 -5
  292. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  293. package/src/daemon/handlers/conversations.ts +1 -0
  294. package/src/daemon/handlers/shared.ts +14 -5
  295. package/src/daemon/handlers/skills.ts +111 -108
  296. package/src/daemon/history-repair.ts +28 -1
  297. package/src/daemon/host-app-control-proxy.ts +153 -27
  298. package/src/daemon/host-proxy-preactivation.ts +85 -18
  299. package/src/daemon/lifecycle.ts +89 -91
  300. package/src/daemon/meet-host-supervisor.ts +5 -4
  301. package/src/daemon/memory-v2-startup.ts +85 -0
  302. package/src/daemon/message-protocol.ts +1 -0
  303. package/src/daemon/message-types/conversations.ts +25 -0
  304. package/src/daemon/message-types/messages.ts +61 -0
  305. package/src/daemon/message-types/notifications.ts +21 -0
  306. package/src/daemon/message-types/subagents.ts +1 -0
  307. package/src/daemon/message-types/sync.ts +1 -0
  308. package/src/daemon/pkb-reminder-builder.test.ts +11 -54
  309. package/src/daemon/pkb-reminder-builder.ts +5 -20
  310. package/src/daemon/plugin-source-watcher.ts +146 -0
  311. package/src/daemon/process-message.ts +24 -3
  312. package/src/daemon/server.ts +11 -2
  313. package/src/daemon/skill-memory-refresh.ts +33 -0
  314. package/src/daemon/wake-target-adapter.ts +2 -0
  315. package/src/documents/document-store.ts +221 -3
  316. package/src/embedded/plugin-api.ts +40 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  318. package/src/export/transcript-formatter.ts +54 -20
  319. package/src/filing/filing-service.ts +39 -0
  320. package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
  321. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  322. package/src/heartbeat/heartbeat-service.ts +73 -189
  323. package/src/home/__tests__/feed-types.test.ts +80 -0
  324. package/src/home/feed-types.ts +36 -2
  325. package/src/home/post-connect-feed.ts +1 -0
  326. package/src/index.ts +18 -1
  327. package/src/ipc/cli-client.ts +147 -45
  328. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  329. package/src/mcp/client.ts +20 -4
  330. package/src/media/image-credentials.ts +3 -3
  331. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  332. package/src/memory/__tests__/conversation-queries.test.ts +483 -0
  333. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  334. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  335. package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
  336. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  337. package/src/memory/__tests__/message-content.test.ts +35 -0
  338. package/src/memory/bookmark-crud.ts +42 -10
  339. package/src/memory/context-search/sources/conversations.ts +62 -2
  340. package/src/memory/context-search/sources/workspace.ts +4 -0
  341. package/src/memory/conversation-crud.ts +63 -19
  342. package/src/memory/conversation-queries.ts +197 -11
  343. package/src/memory/conversation-title-service.ts +26 -4
  344. package/src/memory/db-init.ts +12 -0
  345. package/src/memory/delivery-crud.ts +152 -5
  346. package/src/memory/embedding-backend.ts +4 -4
  347. package/src/memory/external-conversation-store.ts +66 -5
  348. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
  349. package/src/memory/graph/conversation-graph-memory.ts +49 -21
  350. package/src/memory/graph/tools.ts +9 -40
  351. package/src/memory/indexer.ts +34 -29
  352. package/src/memory/invite-store.ts +53 -0
  353. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  354. package/src/memory/jobs/embed-concept-page.ts +20 -11
  355. package/src/memory/jobs-worker.ts +6 -1
  356. package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
  357. package/src/memory/llm-request-log-source.ts +19 -52
  358. package/src/memory/llm-request-log-store.ts +92 -1
  359. package/src/memory/llm-usage-store.ts +125 -5
  360. package/src/memory/memory-retrospective-enqueue.ts +1 -20
  361. package/src/memory/memory-retrospective-job.ts +33 -6
  362. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  363. package/src/memory/message-content.ts +1 -1
  364. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  365. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  366. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  367. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  368. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  369. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  370. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  371. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  372. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  373. package/src/memory/migrations/index.ts +9 -0
  374. package/src/memory/migrations/registry.ts +16 -0
  375. package/src/memory/onboarding-events-store.ts +106 -0
  376. package/src/memory/schema/a2a.ts +15 -0
  377. package/src/memory/schema/bookmarks.ts +0 -2
  378. package/src/memory/schema/calls.ts +1 -0
  379. package/src/memory/schema/index.ts +1 -0
  380. package/src/memory/schema/inference.ts +3 -3
  381. package/src/memory/schema/infrastructure.ts +13 -0
  382. package/src/memory/turn-events-store.ts +127 -2
  383. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  384. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  385. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  386. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  387. package/src/memory/v2/__tests__/injection.test.ts +288 -11
  388. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  389. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  390. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  391. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  392. package/src/memory/v2/__tests__/router.test.ts +15 -0
  393. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  394. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  395. package/src/memory/v2/activation-store.ts +14 -16
  396. package/src/memory/v2/cli-command-content.ts +19 -0
  397. package/src/memory/v2/cli-command-store.ts +304 -0
  398. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  399. package/src/memory/v2/injection.ts +81 -26
  400. package/src/memory/v2/migration.ts +49 -19
  401. package/src/memory/v2/page-index.ts +63 -8
  402. package/src/memory/v2/prompts/router.ts +11 -8
  403. package/src/memory/v2/prompts/sweep.ts +2 -2
  404. package/src/memory/v2/qdrant.ts +135 -7
  405. package/src/memory/v2/router.ts +9 -8
  406. package/src/memory/v2/skill-store.ts +120 -35
  407. package/src/memory/v2/static-context.ts +4 -4
  408. package/src/memory/v2/types.ts +23 -0
  409. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  410. package/src/messaging/providers/a2a/deliver.ts +156 -0
  411. package/src/messaging/providers/gmail/client.ts +9 -2
  412. package/src/messaging/providers/index.ts +11 -2
  413. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  414. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  415. package/src/messaging/providers/slack/adapter.ts +43 -5
  416. package/src/messaging/providers/slack/client.ts +27 -0
  417. package/src/messaging/providers/slack/deep-link.ts +65 -0
  418. package/src/messaging/providers/slack/download.ts +104 -0
  419. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  420. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  421. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  422. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  423. package/src/messaging/providers/slack/types.ts +20 -1
  424. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  425. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  426. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  427. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  428. package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
  429. package/src/notifications/adapters/macos.ts +12 -2
  430. package/src/notifications/broadcaster.ts +29 -4
  431. package/src/notifications/conversation-pairing.ts +2 -1
  432. package/src/notifications/copy-composer.ts +17 -64
  433. package/src/notifications/decision-engine.ts +113 -45
  434. package/src/notifications/deterministic-checks.ts +96 -0
  435. package/src/notifications/emit-signal.ts +21 -1
  436. package/src/notifications/home-feed-side-effect.ts +138 -5
  437. package/src/notifications/signal.ts +3 -5
  438. package/src/notifications/types.ts +8 -0
  439. package/src/oauth/connection-resolver.ts +8 -4
  440. package/src/oauth/platform-connection.test.ts +43 -3
  441. package/src/oauth/platform-connection.ts +19 -6
  442. package/src/oauth/seed-providers.ts +10 -1
  443. package/src/permissions/checker.ts +2 -0
  444. package/src/permissions/ipc-risk-types.ts +1 -0
  445. package/src/permissions/question-prompter.test.ts +416 -0
  446. package/src/permissions/question-prompter.ts +294 -0
  447. package/src/platform/client.test.ts +1 -1
  448. package/src/platform/client.ts +1 -1
  449. package/src/plugin-api/constants.ts +26 -0
  450. package/src/plugin-api/index.ts +34 -1
  451. package/src/plugin-api/types.ts +104 -22
  452. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  453. package/src/plugins/defaults/compaction.ts +0 -4
  454. package/src/plugins/defaults/empty-response.ts +0 -2
  455. package/src/plugins/defaults/history-repair.ts +0 -2
  456. package/src/plugins/defaults/injectors.ts +74 -22
  457. package/src/plugins/defaults/llm-call.ts +0 -2
  458. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  459. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  460. package/src/plugins/defaults/persistence.ts +0 -2
  461. package/src/plugins/defaults/title-generate.ts +0 -5
  462. package/src/plugins/defaults/token-estimate.ts +0 -2
  463. package/src/plugins/defaults/tool-error.ts +0 -7
  464. package/src/plugins/defaults/tool-execute.ts +0 -2
  465. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  466. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  467. package/src/plugins/external-api.ts +104 -0
  468. package/src/plugins/external-plugin-loader.ts +187 -42
  469. package/src/plugins/feature-gate.ts +22 -0
  470. package/src/plugins/pipeline.ts +37 -0
  471. package/src/plugins/registry.ts +48 -80
  472. package/src/plugins/types.ts +40 -26
  473. package/src/plugins/user-loader.ts +21 -2
  474. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  475. package/src/proactive-artifact/job.test.ts +37 -5
  476. package/src/prompts/__tests__/system-prompt.test.ts +10 -43
  477. package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
  478. package/src/prompts/normalize-onboarding.ts +27 -0
  479. package/src/prompts/sections.ts +302 -0
  480. package/src/prompts/system-prompt.ts +63 -174
  481. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  482. package/src/prompts/templates/system-sections.ts +164 -0
  483. package/src/providers/__tests__/inference.test.ts +24 -7
  484. package/src/providers/anthropic/client.ts +28 -28
  485. package/src/providers/call-site-routing.ts +24 -6
  486. package/src/providers/connection-resolution.ts +68 -11
  487. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  488. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  489. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  490. package/src/providers/inference/adapter-factory.ts +32 -6
  491. package/src/providers/inference/auth.ts +12 -0
  492. package/src/providers/inference/backfill.ts +14 -1
  493. package/src/providers/inference/connections.ts +159 -34
  494. package/src/providers/inference/resolve-auth.ts +14 -4
  495. package/src/providers/model-catalog.ts +249 -12
  496. package/src/providers/model-intents.ts +3 -3
  497. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  498. package/src/providers/openai/chat-completions-provider.ts +169 -8
  499. package/src/providers/openrouter/client.ts +49 -4
  500. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
  501. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  502. package/src/providers/provider-availability.ts +17 -2
  503. package/src/providers/provider-catalog-visibility.ts +38 -0
  504. package/src/providers/provider-send-message.ts +27 -12
  505. package/src/providers/registry.ts +52 -15
  506. package/src/providers/retry.ts +47 -1
  507. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  508. package/src/runtime/agent-wake.ts +103 -15
  509. package/src/runtime/auth/route-policy.ts +21 -1
  510. package/src/runtime/btw-sidechain.ts +2 -0
  511. package/src/runtime/http-server.ts +7 -16
  512. package/src/runtime/http-types.ts +19 -47
  513. package/src/runtime/migrations/origin-mode.ts +1 -1
  514. package/src/runtime/pending-interactions.ts +1 -0
  515. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  516. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  517. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  518. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
  519. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  520. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  521. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  522. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  523. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  524. package/src/runtime/routes/acp-routes.ts +5 -3
  525. package/src/runtime/routes/auth-routes.ts +1 -1
  526. package/src/runtime/routes/bookmark-routes.ts +5 -3
  527. package/src/runtime/routes/btw-routes.ts +5 -1
  528. package/src/runtime/routes/channel-availability-routes.ts +126 -0
  529. package/src/runtime/routes/consolidation-routes.ts +100 -0
  530. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  531. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  532. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  533. package/src/runtime/routes/conversation-query-routes.ts +99 -35
  534. package/src/runtime/routes/conversation-routes.ts +97 -11
  535. package/src/runtime/routes/documents-routes.ts +25 -86
  536. package/src/runtime/routes/group-routes.ts +5 -0
  537. package/src/runtime/routes/inbound-conversation.ts +28 -8
  538. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  539. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  540. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  541. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  542. package/src/runtime/routes/index.ts +8 -0
  543. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  544. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  545. package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
  546. package/src/runtime/routes/integrations/a2a.ts +235 -0
  547. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  548. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  549. package/src/runtime/routes/integrations/twilio.ts +6 -13
  550. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  551. package/src/runtime/routes/notification-routes.ts +1 -1
  552. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  553. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  554. package/src/runtime/routes/question-routes.ts +259 -0
  555. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  556. package/src/runtime/routes/schedule-routes.ts +4 -7
  557. package/src/runtime/routes/subagents-routes.ts +98 -18
  558. package/src/runtime/routes/telemetry-routes.ts +27 -0
  559. package/src/runtime/routes/tts-routes.ts +27 -2
  560. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  561. package/src/runtime/routes/workspace-routes.ts +28 -0
  562. package/src/runtime/services/conversation-serializer.ts +39 -7
  563. package/src/runtime/sync/resource-sync-events.ts +93 -1
  564. package/src/schedule/schedule-store.ts +27 -2
  565. package/src/schedule/scheduler.ts +9 -1
  566. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  567. package/src/security/untrusted-content.ts +93 -8
  568. package/src/skills/catalog-files.ts +1 -1
  569. package/src/skills/catalog-install.ts +233 -116
  570. package/src/skills/clawhub.ts +70 -13
  571. package/src/skills/managed-store.ts +4 -119
  572. package/src/skills/skillssh-registry.ts +27 -48
  573. package/src/subagent/manager.ts +17 -7
  574. package/src/telemetry/types.ts +113 -1
  575. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  576. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  577. package/src/tools/apps/executors.ts +58 -7
  578. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  579. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  580. package/src/tools/browser/browser-execution.ts +15 -11
  581. package/src/tools/computer-use/definitions.ts +3 -3
  582. package/src/tools/credentials/vault.ts +1 -1
  583. package/src/tools/document/document-tool.ts +124 -1
  584. package/src/tools/filesystem/edit.ts +1 -1
  585. package/src/tools/filesystem/list.ts +1 -1
  586. package/src/tools/filesystem/read.ts +1 -1
  587. package/src/tools/filesystem/write.ts +5 -2
  588. package/src/tools/host-filesystem/transfer.ts +1 -1
  589. package/src/tools/host-terminal/host-shell.ts +1 -1
  590. package/src/tools/memory/register.ts +1 -9
  591. package/src/tools/permission-checker.ts +1 -1
  592. package/src/tools/registry.ts +17 -7
  593. package/src/tools/schedule/create.ts +2 -2
  594. package/src/tools/schema-transforms.ts +7 -2
  595. package/src/tools/side-effects.ts +1 -0
  596. package/src/tools/skills/delete-managed.ts +4 -4
  597. package/src/tools/skills/execute.ts +1 -1
  598. package/src/tools/skills/scaffold-managed.ts +3 -2
  599. package/src/tools/subagent/notify-parent.ts +1 -1
  600. package/src/tools/system/request-permission.ts +2 -2
  601. package/src/tools/terminal/safe-env.ts +60 -1
  602. package/src/tools/tool-manifest.ts +2 -0
  603. package/src/tools/types.ts +107 -21
  604. package/src/tools/ui-surface/definitions.ts +6 -5
  605. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  606. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  607. package/src/types/onboarding-context.ts +2 -0
  608. package/src/util/errors.ts +17 -0
  609. package/src/util/platform.ts +10 -0
  610. package/src/watcher/__tests__/engine.test.ts +22 -0
  611. package/src/watcher/engine.ts +6 -2
  612. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  613. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  614. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  615. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  616. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  617. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  618. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  619. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  620. package/src/workspace/migrations/registry.ts +10 -0
  621. package/src/workspace/migrations/runner.ts +39 -9
  622. package/src/workspace/migrations/types.ts +4 -0
  623. package/examples/plugins/echo/bun.lock +0 -25
  624. package/src/__tests__/context-window-manager.test.ts +0 -2481
  625. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  626. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  627. package/src/context/prompts/compact.md +0 -26
  628. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  629. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  630. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
@@ -1,12 +1,16 @@
1
1
  /**
2
- * Tests for `preactivateHostProxySkills` and `shouldAttachHostProxyForCapability`
3
- * in `host-proxy-preactivation.ts`.
2
+ * Tests for `evaluateHostProxyAttachment`, `preactivateHostProxySkills`, and
3
+ * `shouldAttachHostProxyForCapability` in `host-proxy-preactivation.ts`.
4
4
  *
5
5
  * Covers:
6
6
  * - Source interface natively supports capability → preactivate (regression)
7
7
  * - Source interface doesn't support but capable client connected → preactivate
8
8
  * - Source interface doesn't support and no capable client → don't preactivate
9
9
  * - chrome-extension source + capable client connected → don't preactivate (security boundary)
10
+ * - `evaluateHostProxyAttachment` returns the correct `reason` for each branch
11
+ * - `preactivateHostProxySkills` emits one structured log line per call with
12
+ * conversationId, sourceInterface, per-capability decisions, and final
13
+ * preactivatedSkillIds (used by ATL-609-class silent-gate diagnosis)
10
14
  */
11
15
 
12
16
  import { beforeEach, describe, expect, mock, test } from "bun:test";
@@ -26,31 +30,74 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
26
30
  broadcastMessage: () => {},
27
31
  }));
28
32
 
33
+ // ---------------------------------------------------------------------------
34
+ // Mock the logger so we can assert on the structured info call.
35
+ // `info` calls are pushed into `loggedInfoCalls` for inspection.
36
+ // `child()` is exposed for callers that wrap the logger that way.
37
+ // ---------------------------------------------------------------------------
38
+
39
+ interface LoggedCall {
40
+ fields: Record<string, unknown>;
41
+ message: string;
42
+ }
43
+ const loggedInfoCalls: LoggedCall[] = [];
44
+ function captureInfo(fields: unknown, message: unknown) {
45
+ loggedInfoCalls.push({
46
+ fields: fields as Record<string, unknown>,
47
+ message: message as string,
48
+ });
49
+ }
50
+
29
51
  mock.module("../util/logger.js", () => ({
30
- getLogger: () =>
31
- new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
52
+ getLogger: () => ({
53
+ info: captureInfo,
54
+ warn: () => {},
55
+ error: () => {},
56
+ debug: () => {},
57
+ trace: () => {},
58
+ fatal: () => {},
59
+ child: () => ({
60
+ info: captureInfo,
61
+ warn: () => {},
62
+ error: () => {},
63
+ debug: () => {},
64
+ trace: () => {},
65
+ fatal: () => {},
66
+ }),
67
+ }),
32
68
  }));
33
69
 
34
70
  // ---------------------------------------------------------------------------
35
- // Imports under test (after mocks are registered)
71
+ // Imports under test
72
+ //
73
+ // Type-only imports are erased at runtime and safe to hoist. The value import
74
+ // of `host-proxy-preactivation` must be dynamic (`await import`) so it
75
+ // resolves AFTER the `mock.module(...)` calls above — otherwise ES module
76
+ // hoisting loads the real logger before the mock registers, the production
77
+ // `const log = getLogger(...)` binds to real pino, and assertions against
78
+ // `loggedInfoCalls` see an empty array. Same pattern as
79
+ // `secret-prompt-log-hygiene.test.ts`.
36
80
  // ---------------------------------------------------------------------------
37
81
 
38
82
  import type { HostProxyCapability } from "../channels/types.js";
39
- import {
40
- type HostProxyPreactivationTarget,
83
+ import type { HostProxyPreactivationTarget } from "../daemon/host-proxy-preactivation.js";
84
+
85
+ const {
86
+ evaluateHostProxyAttachment,
41
87
  preactivateHostProxySkills,
42
88
  shouldAttachHostProxyForCapability,
43
- } from "../daemon/host-proxy-preactivation.js";
89
+ } = await import("../daemon/host-proxy-preactivation.js");
44
90
 
45
91
  // ---------------------------------------------------------------------------
46
92
  // Helpers
47
93
  // ---------------------------------------------------------------------------
48
94
 
49
- function makeTarget(): HostProxyPreactivationTarget & {
50
- preactivatedSkillIds: string[];
51
- } {
95
+ function makeTarget(
96
+ conversationId = "conv-test",
97
+ ): HostProxyPreactivationTarget & { preactivatedSkillIds: string[] } {
52
98
  const preactivatedSkillIds: string[] = [];
53
99
  return {
100
+ conversationId,
54
101
  preactivatedSkillIds,
55
102
  addPreactivatedSkillId(id: string) {
56
103
  preactivatedSkillIds.push(id);
@@ -73,6 +120,7 @@ function setCapableClient(
73
120
 
74
121
  beforeEach(() => {
75
122
  mockClientsByCapability = new Map();
123
+ loggedInfoCalls.length = 0;
76
124
  });
77
125
 
78
126
  // ---------------------------------------------------------------------------
@@ -143,7 +191,10 @@ describe("shouldAttachHostProxyForCapability", () => {
143
191
  test("returns false for chrome-extension source even when a capable client is connected", () => {
144
192
  setCapableClient("host_app_control", true);
145
193
  expect(
146
- shouldAttachHostProxyForCapability("host_app_control", "chrome-extension"),
194
+ shouldAttachHostProxyForCapability(
195
+ "host_app_control",
196
+ "chrome-extension",
197
+ ),
147
198
  ).toBe(false);
148
199
  });
149
200
  });
@@ -154,7 +205,7 @@ describe("shouldAttachHostProxyForCapability", () => {
154
205
  // ---------------------------------------------------------------------------
155
206
 
156
207
  describe("preactivateHostProxySkills", () => {
157
- test("no-ops when sourceInterface is undefined", () => {
208
+ test("preactivates no skills when sourceInterface is undefined", () => {
158
209
  const target = makeTarget();
159
210
  preactivateHostProxySkills(target, undefined);
160
211
  expect(target.preactivatedSkillIds).toEqual([]);
@@ -209,3 +260,139 @@ describe("preactivateHostProxySkills", () => {
209
260
  expect(target.preactivatedSkillIds).toEqual([]);
210
261
  });
211
262
  });
263
+
264
+ // ---------------------------------------------------------------------------
265
+ // evaluateHostProxyAttachment — reason coverage
266
+ // ---------------------------------------------------------------------------
267
+
268
+ describe("evaluateHostProxyAttachment", () => {
269
+ test("returns denied_no_interface when sourceInterface is undefined", () => {
270
+ expect(evaluateHostProxyAttachment("host_cu", undefined)).toEqual({
271
+ shouldAttach: false,
272
+ reason: "denied_no_interface",
273
+ });
274
+ });
275
+
276
+ test("returns native_support for macos + host_cu", () => {
277
+ expect(evaluateHostProxyAttachment("host_cu", "macos")).toEqual({
278
+ shouldAttach: true,
279
+ reason: "native_support",
280
+ });
281
+ });
282
+
283
+ test("returns denied_chrome_extension for chrome-extension source even when capable clients exist", () => {
284
+ setCapableClient("host_cu", true);
285
+ expect(evaluateHostProxyAttachment("host_cu", "chrome-extension")).toEqual({
286
+ shouldAttach: false,
287
+ reason: "denied_chrome_extension",
288
+ });
289
+ });
290
+
291
+ test("returns cross_client with clientCount when a capable client is connected", () => {
292
+ setCapableClient("host_cu", true);
293
+ expect(evaluateHostProxyAttachment("host_cu", "web")).toEqual({
294
+ shouldAttach: true,
295
+ reason: "cross_client",
296
+ clientCount: 1,
297
+ });
298
+ });
299
+
300
+ test("returns denied_no_clients with clientCount 0 when no capable client is connected", () => {
301
+ setCapableClient("host_cu", false);
302
+ expect(evaluateHostProxyAttachment("host_cu", "web")).toEqual({
303
+ shouldAttach: false,
304
+ reason: "denied_no_clients",
305
+ clientCount: 0,
306
+ });
307
+ });
308
+ });
309
+
310
+ // ---------------------------------------------------------------------------
311
+ // preactivateHostProxySkills — structured logging
312
+ // ---------------------------------------------------------------------------
313
+
314
+ describe("preactivateHostProxySkills logging", () => {
315
+ test("emits exactly one info log per call", () => {
316
+ const target = makeTarget();
317
+ preactivateHostProxySkills(target, "macos");
318
+ expect(loggedInfoCalls).toHaveLength(1);
319
+ expect(loggedInfoCalls[0].message).toBe(
320
+ "host-proxy preactivation decision",
321
+ );
322
+ });
323
+
324
+ test("log includes conversationId, sourceInterface, per-capability decisions, and preactivatedSkillIds for macos", () => {
325
+ const target = makeTarget("conv-macos-123");
326
+ preactivateHostProxySkills(target, "macos");
327
+
328
+ expect(loggedInfoCalls).toHaveLength(1);
329
+ const { fields } = loggedInfoCalls[0];
330
+ expect(fields.conversationId).toBe("conv-macos-123");
331
+ expect(fields.sourceInterface).toBe("macos");
332
+ expect(fields.decisions).toEqual({
333
+ host_cu: { shouldAttach: true, reason: "native_support" },
334
+ host_app_control: { shouldAttach: true, reason: "native_support" },
335
+ });
336
+ expect(fields.preactivatedSkillIds).toEqual([
337
+ "computer-use",
338
+ "app-control",
339
+ ]);
340
+ });
341
+
342
+ test("log captures denied_no_interface for undefined sourceInterface (silent-gate diagnostic)", () => {
343
+ const target = makeTarget("conv-no-interface");
344
+ preactivateHostProxySkills(target, undefined);
345
+
346
+ expect(loggedInfoCalls).toHaveLength(1);
347
+ const { fields } = loggedInfoCalls[0];
348
+ expect(fields.conversationId).toBe("conv-no-interface");
349
+ expect(fields.sourceInterface).toBeUndefined();
350
+ expect(fields.decisions).toEqual({
351
+ host_cu: { shouldAttach: false, reason: "denied_no_interface" },
352
+ host_app_control: { shouldAttach: false, reason: "denied_no_interface" },
353
+ });
354
+ expect(fields.preactivatedSkillIds).toEqual([]);
355
+ });
356
+
357
+ test("log captures cross_client + clientCount when a web source has a connected host_cu client", () => {
358
+ setCapableClient("host_cu", true);
359
+ const target = makeTarget();
360
+ preactivateHostProxySkills(target, "web");
361
+
362
+ expect(loggedInfoCalls).toHaveLength(1);
363
+ const decisions = loggedInfoCalls[0].fields.decisions as Record<
364
+ string,
365
+ unknown
366
+ >;
367
+ expect(decisions.host_cu).toEqual({
368
+ shouldAttach: true,
369
+ reason: "cross_client",
370
+ clientCount: 1,
371
+ });
372
+ expect(decisions.host_app_control).toEqual({
373
+ shouldAttach: false,
374
+ reason: "denied_no_clients",
375
+ clientCount: 0,
376
+ });
377
+ expect(loggedInfoCalls[0].fields.preactivatedSkillIds).toEqual([
378
+ "computer-use",
379
+ ]);
380
+ });
381
+
382
+ test("log captures denied_chrome_extension reason for chrome-extension source", () => {
383
+ setCapableClient("host_cu", true);
384
+ const target = makeTarget();
385
+ preactivateHostProxySkills(target, "chrome-extension");
386
+
387
+ expect(loggedInfoCalls).toHaveLength(1);
388
+ const decisions = loggedInfoCalls[0].fields.decisions as Record<
389
+ string,
390
+ unknown
391
+ >;
392
+ expect(decisions.host_cu).toEqual({
393
+ shouldAttach: false,
394
+ reason: "denied_chrome_extension",
395
+ });
396
+ expect(loggedInfoCalls[0].fields.preactivatedSkillIds).toEqual([]);
397
+ });
398
+ });
@@ -12,7 +12,7 @@ mock.module("../security/secure-keys.js", () => ({
12
12
  getProviderKeyAsync: async (_provider: string) => mockProviderKey,
13
13
  }));
14
14
 
15
- mock.module("../providers/managed-proxy/context.js", () => ({
15
+ mock.module("../providers/platform-proxy/context.js", () => ({
16
16
  resolveManagedProxyContext: async () => ({
17
17
  enabled: !!mockPlatformBaseUrl && !!mockAssistantApiKey,
18
18
  platformBaseUrl: mockPlatformBaseUrl,
@@ -151,6 +151,7 @@ describe("PR 11 — inbound Slack message metadata persistence", () => {
151
151
  channelTs: "1700000001.111111",
152
152
  threadTs: "1700000000.000001",
153
153
  displayName: "Alice",
154
+ actorExternalUserId: "U_ALICE",
154
155
  },
155
156
  },
156
157
  undefined,
@@ -164,6 +165,7 @@ describe("PR 11 — inbound Slack message metadata persistence", () => {
164
165
  expect(slackMeta!.channelTs).toBe("1700000001.111111");
165
166
  expect(slackMeta!.threadTs).toBe("1700000000.000001");
166
167
  expect(slackMeta!.displayName).toBe("Alice");
168
+ expect(slackMeta!.actorExternalUserId).toBe("U_ALICE");
167
169
  });
168
170
 
169
171
  test("Slack top-level message: slackMeta has no threadTs", async () => {
@@ -35,7 +35,7 @@ mock.module("../security/secure-keys.js", () => ({
35
35
  }));
36
36
 
37
37
  // Managed proxy context — always unavailable in this test (no platform auth).
38
- mock.module("../providers/managed-proxy/context.js", () => ({
38
+ mock.module("../providers/platform-proxy/context.js", () => ({
39
39
  buildManagedBaseUrl: async () => null,
40
40
  resolveManagedProxyContext: async () => {
41
41
  throw new Error("managed proxy not available in test");
@@ -1,6 +1,7 @@
1
1
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import { makeMockLogger } from "./helpers/mock-logger.js";
4
+ import { waitFor } from "./helpers/wait-for.js";
4
5
 
5
6
  mock.module("../util/logger.js", () => ({
6
7
  getLogger: () => makeMockLogger(),
@@ -86,8 +87,9 @@ describe("inference-profile-session-reaper", () => {
86
87
  });
87
88
 
88
89
  tickInferenceProfileReaper();
89
- // Allow microtasks (promise callbacks) to settle
90
- await Promise.resolve();
90
+ await waitFor(() => publishedEvents.length === 2, {
91
+ message: "Timed out waiting for inference profile reaper event",
92
+ });
91
93
 
92
94
  // Expired rows should be cleared
93
95
  expect(getConversation(conv1.id)?.inferenceProfile).toBeNull();
@@ -19,8 +19,10 @@ mock.module("../util/logger.js", () => ({
19
19
  // Stub the event hub so tests don't need a running event bus.
20
20
  // Exposed as a `mock(...)` so individual tests can assert publish calls.
21
21
  const publishMock = mock(async () => {});
22
+ const broadcastMessageMock = mock(() => {});
22
23
  mock.module("../runtime/assistant-event-hub.js", () => ({
23
24
  assistantEventHub: { publish: publishMock },
25
+ broadcastMessage: broadcastMessageMock,
24
26
  }));
25
27
 
26
28
  // Stub buildAssistantEvent to be an identity pass-through for the event object
@@ -51,7 +53,10 @@ mock.module("../config/loader.js", () => ({
51
53
  // Real DB — same pattern as conversation-crud-inference-profile.test.ts
52
54
  // ---------------------------------------------------------------------------
53
55
 
54
- import { createConversation, getConversation } from "../memory/conversation-crud.js";
56
+ import {
57
+ createConversation,
58
+ getConversation,
59
+ } from "../memory/conversation-crud.js";
55
60
  import { getDb } from "../memory/db-connection.js";
56
61
  import { initializeDb } from "../memory/db-init.js";
57
62
 
@@ -83,6 +88,7 @@ describe("setInferenceProfileSession", () => {
83
88
  mockProfiles = { balanced: {}, "cost-optimized": {} };
84
89
  mockMaxTtl = undefined; // reset to default 43200
85
90
  publishMock.mockClear();
91
+ broadcastMessageMock.mockClear();
86
92
  });
87
93
 
88
94
  test("open with ttlSeconds=600 — returns UUID sessionId and expiresAt ≈ now + 600_000", async () => {
@@ -139,7 +145,9 @@ describe("setInferenceProfileSession", () => {
139
145
  expect(result.ttlSeconds).toBe(43200);
140
146
  expect(result.expiresAt).not.toBeNull();
141
147
  expect(result.expiresAt!).toBeGreaterThanOrEqual(before + 43200 * 1000);
142
- expect(result.expiresAt!).toBeLessThanOrEqual(Date.now() + 43200 * 1000 + 1000);
148
+ expect(result.expiresAt!).toBeLessThanOrEqual(
149
+ Date.now() + 43200 * 1000 + 1000,
150
+ );
143
151
  });
144
152
 
145
153
  test("open over active session — replaced carries prior session info", async () => {
@@ -208,6 +216,7 @@ describe("setInferenceProfileSession", () => {
208
216
  const updatedAtBefore = before?.updatedAt;
209
217
 
210
218
  publishMock.mockClear();
219
+ broadcastMessageMock.mockClear();
211
220
  const result = await setInferenceProfileSession({
212
221
  conversationId: conv.id,
213
222
  profile: null,
@@ -221,7 +230,7 @@ describe("setInferenceProfileSession", () => {
221
230
 
222
231
  // No event was published — this is the load-bearing assertion for the
223
232
  // idempotency guard (Codex P2 on PR #29913).
224
- expect(publishMock).not.toHaveBeenCalled();
233
+ expect(broadcastMessageMock).not.toHaveBeenCalled();
225
234
 
226
235
  // No DB write occurred — `updatedAt` is unchanged.
227
236
  const after = getConversation(conv.id);
@@ -240,6 +249,7 @@ describe("setInferenceProfileSession", () => {
240
249
  });
241
250
 
242
251
  publishMock.mockClear();
252
+ broadcastMessageMock.mockClear();
243
253
  const result = await setInferenceProfileSession({
244
254
  conversationId: conv.id,
245
255
  profile: null,
@@ -250,8 +260,8 @@ describe("setInferenceProfileSession", () => {
250
260
  // though the sticky profile was cleared.
251
261
  expect(result.replaced).toBeNull();
252
262
 
253
- // The clear DID happen — DB row reflects it and an event was published.
254
- expect(publishMock).toHaveBeenCalledTimes(1);
263
+ // The clear DID happen — DB row reflects it and legacy+sync events were published.
264
+ expect(broadcastMessageMock).toHaveBeenCalledTimes(2);
255
265
  const row = getConversation(conv.id);
256
266
  expect(row?.inferenceProfile).toBeNull();
257
267
  });
@@ -265,7 +275,9 @@ describe("setInferenceProfileSession", () => {
265
275
  profile: "nonexistent-profile",
266
276
  ttlSeconds: 300,
267
277
  }),
268
- ).rejects.toThrow('Profile "nonexistent-profile" is not defined in llm.profiles');
278
+ ).rejects.toThrow(
279
+ 'Profile "nonexistent-profile" is not defined in llm.profiles',
280
+ );
269
281
  });
270
282
 
271
283
  test("open with ttlSeconds=null — expiresAt=null, sessionId=null, profile kept", async () => {
@@ -15,6 +15,7 @@ mock.module("../util/logger.js", () => ({
15
15
 
16
16
  mock.module("../runtime/assistant-event-hub.js", () => ({
17
17
  assistantEventHub: { publish: async () => {} },
18
+ broadcastMessage: () => {},
18
19
  }));
19
20
 
20
21
  mock.module("../runtime/assistant-event.js", () => ({
@@ -43,9 +44,15 @@ import { ROUTES } from "../runtime/routes/inference-profile-session-routes.js";
43
44
 
44
45
  initializeDb();
45
46
 
46
- const openRoute = ROUTES.find((r) => r.operationId === "inference_profile_open")!;
47
- const closeRoute = ROUTES.find((r) => r.operationId === "inference_profile_close")!;
48
- const listRoute = ROUTES.find((r) => r.operationId === "inference_profile_list")!;
47
+ const openRoute = ROUTES.find(
48
+ (r) => r.operationId === "inference_profile_open",
49
+ )!;
50
+ const closeRoute = ROUTES.find(
51
+ (r) => r.operationId === "inference_profile_close",
52
+ )!;
53
+ const listRoute = ROUTES.find(
54
+ (r) => r.operationId === "inference_profile_list",
55
+ )!;
49
56
 
50
57
  function clearTables(): void {
51
58
  const db = getDb();
@@ -77,7 +84,9 @@ describe("inference_profile_open IPC op", () => {
77
84
  replaced: null,
78
85
  });
79
86
  expect((result as { sessionId: string }).sessionId).not.toBeNull();
80
- expect((result as { expiresAt: number }).expiresAt).toBeGreaterThan(Date.now());
87
+ expect((result as { expiresAt: number }).expiresAt).toBeGreaterThan(
88
+ Date.now(),
89
+ );
81
90
  });
82
91
 
83
92
  test("opens a sticky session (no ttlSeconds) — sessionId=null, expiresAt=null", async () => {
@@ -145,7 +154,10 @@ describe("inference_profile_close IPC op", () => {
145
154
  const result = (await closeRoute.handler({
146
155
  body: { conversationId: conv.id },
147
156
  headers: {},
148
- })) as { noop: boolean; closed: { profile: string; sessionId: string } | null };
157
+ })) as {
158
+ noop: boolean;
159
+ closed: { profile: string; sessionId: string } | null;
160
+ };
149
161
 
150
162
  expect(result.noop).toBe(false);
151
163
  expect(result.closed).not.toBeNull();
@@ -0,0 +1,153 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ let configBackgroundInjection: string =
4
+ "This is a background turn — your guardian isn't watching. If anything noteworthy comes up, send them a notification so they see it when they're back by invoking the `notifications` skill (`assistant notifications send --message \"...\"`)";
5
+
6
+ const realLoaderForBackgroundTurnTest = await import("../config/loader.js");
7
+ const realGetConfigForBackgroundTurnTest =
8
+ realLoaderForBackgroundTurnTest.getConfig;
9
+ mock.module("../config/loader.js", () => ({
10
+ ...realLoaderForBackgroundTurnTest,
11
+ getConfig: () => {
12
+ const real = realGetConfigForBackgroundTurnTest();
13
+ return {
14
+ ...real,
15
+ conversations: {
16
+ ...real.conversations,
17
+ backgroundInjection: configBackgroundInjection,
18
+ },
19
+ };
20
+ },
21
+ }));
22
+
23
+ import {
24
+ DEFAULT_INJECTOR_ORDER,
25
+ defaultInjectorsPlugin,
26
+ } from "../plugins/defaults/injectors.js";
27
+ import {
28
+ registerPlugin,
29
+ resetPluginRegistryForTests,
30
+ } from "../plugins/registry.js";
31
+ import type { Injector, TurnContext } from "../plugins/types.js";
32
+
33
+ function findInjector(name: string): Injector {
34
+ const injector = defaultInjectorsPlugin.injectors?.find(
35
+ (candidate) => candidate.name === name,
36
+ );
37
+ if (!injector) {
38
+ throw new Error(`injector '${name}' not registered`);
39
+ }
40
+ return injector;
41
+ }
42
+
43
+ function makeContext(overrides: Partial<TurnContext> = {}): TurnContext {
44
+ return {
45
+ requestId: "req-test",
46
+ conversationId: "conv-test",
47
+ turnIndex: 0,
48
+ trust: { sourceChannel: "vellum", trustClass: "guardian" },
49
+ ...overrides,
50
+ };
51
+ }
52
+
53
+ const backgroundInjector = findInjector("background-turn");
54
+
55
+ const DEFAULT_INJECTION_TEXT =
56
+ "This is a background turn — your guardian isn't watching. If anything noteworthy comes up, send them a notification so they see it when they're back by invoking the `notifications` skill (`assistant notifications send --message \"...\"`)";
57
+
58
+ describe("background-turn injector", () => {
59
+ beforeEach(() => {
60
+ resetPluginRegistryForTests();
61
+ registerPlugin(defaultInjectorsPlugin);
62
+ configBackgroundInjection = DEFAULT_INJECTION_TEXT;
63
+ });
64
+
65
+ test("returns null when isBackgroundConversation is false", async () => {
66
+ const result = await backgroundInjector.produce(
67
+ makeContext({
68
+ injectionInputs: {
69
+ isBackgroundConversation: false,
70
+ isNonInteractive: true,
71
+ },
72
+ }),
73
+ );
74
+ expect(result).toBeNull();
75
+ });
76
+
77
+ test("returns null when isBackgroundConversation is unset", async () => {
78
+ const result = await backgroundInjector.produce(
79
+ makeContext({ injectionInputs: { isNonInteractive: true } }),
80
+ );
81
+ expect(result).toBeNull();
82
+ });
83
+
84
+ test("returns null when the guardian is actively connected (interactive turn)", async () => {
85
+ const result = await backgroundInjector.produce(
86
+ makeContext({
87
+ injectionInputs: {
88
+ isBackgroundConversation: true,
89
+ isNonInteractive: false,
90
+ },
91
+ }),
92
+ );
93
+ expect(result).toBeNull();
94
+ });
95
+
96
+ test("returns null when isNonInteractive is unset", async () => {
97
+ const result = await backgroundInjector.produce(
98
+ makeContext({ injectionInputs: { isBackgroundConversation: true } }),
99
+ );
100
+ expect(result).toBeNull();
101
+ });
102
+
103
+ test("wraps configured text in <background_turn> tags when active and non-interactive", async () => {
104
+ const block = await backgroundInjector.produce(
105
+ makeContext({
106
+ injectionInputs: {
107
+ isBackgroundConversation: true,
108
+ isNonInteractive: true,
109
+ },
110
+ }),
111
+ );
112
+
113
+ expect(block).toEqual({
114
+ id: "background-turn",
115
+ text: `<background_turn>\n${DEFAULT_INJECTION_TEXT}\n</background_turn>`,
116
+ placement: "prepend-user-tail",
117
+ });
118
+ expect(backgroundInjector.order).toBe(
119
+ DEFAULT_INJECTOR_ORDER.backgroundTurn,
120
+ );
121
+ });
122
+
123
+ test("returns null when configured text is the empty string", async () => {
124
+ configBackgroundInjection = "";
125
+
126
+ const result = await backgroundInjector.produce(
127
+ makeContext({
128
+ injectionInputs: {
129
+ isBackgroundConversation: true,
130
+ isNonInteractive: true,
131
+ },
132
+ }),
133
+ );
134
+ expect(result).toBeNull();
135
+ });
136
+
137
+ test("uses operator-configured override text verbatim", async () => {
138
+ configBackgroundInjection = "Custom reminder body.";
139
+
140
+ const block = await backgroundInjector.produce(
141
+ makeContext({
142
+ injectionInputs: {
143
+ isBackgroundConversation: true,
144
+ isNonInteractive: true,
145
+ },
146
+ }),
147
+ );
148
+
149
+ expect(block?.text).toBe(
150
+ "<background_turn>\nCustom reminder body.\n</background_turn>",
151
+ );
152
+ });
153
+ });