@vellumai/assistant 0.8.0 → 0.8.1

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 (692) hide show
  1. package/AGENTS.md +11 -0
  2. package/Dockerfile +5 -4
  3. package/README.md +2 -2
  4. package/docker-entrypoint.sh +16 -0
  5. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  6. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  7. package/eslint.config.mjs +12 -0
  8. package/knip.json +2 -1
  9. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  10. package/openapi.yaml +4847 -1698
  11. package/package.json +3 -1
  12. package/scripts/generate-openapi.ts +52 -4
  13. package/scripts/sync-llm-catalog.ts +165 -0
  14. package/scripts/sync-web-search-catalog.ts +107 -0
  15. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  17. package/src/__tests__/anthropic-provider.test.ts +92 -2
  18. package/src/__tests__/app-control-flow.test.ts +7 -0
  19. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  20. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  22. package/src/__tests__/btw-routes.test.ts +1 -0
  23. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  25. package/src/__tests__/channel-policy.test.ts +12 -0
  26. package/src/__tests__/checker.test.ts +89 -0
  27. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  28. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  29. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  30. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  31. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  32. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  33. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  34. package/src/__tests__/config-schema.test.ts +14 -3
  35. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  36. package/src/__tests__/config-set-route.test.ts +198 -0
  37. package/src/__tests__/config-watcher.test.ts +6 -0
  38. package/src/__tests__/contacts-tools.test.ts +51 -199
  39. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  40. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  41. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  42. package/src/__tests__/context-search-fanout.test.ts +20 -157
  43. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  44. package/src/__tests__/context-search-types.test.ts +7 -2
  45. package/src/__tests__/context-window-manager.test.ts +389 -1
  46. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  47. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  48. package/src/__tests__/conversation-error.test.ts +38 -0
  49. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  50. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  51. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  52. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  54. package/src/__tests__/conversation-process-callsite.test.ts +21 -1
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
  56. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  57. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  58. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  59. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  60. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  61. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  62. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  63. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  64. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  65. package/src/__tests__/filing-service.test.ts +23 -3
  66. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  67. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  68. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  69. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
  70. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  71. package/src/__tests__/heartbeat-service.test.ts +50 -233
  72. package/src/__tests__/history-repair.test.ts +89 -0
  73. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  74. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  75. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  76. package/src/__tests__/host-browser-routes.test.ts +325 -33
  77. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  78. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  79. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  80. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  81. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  82. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  83. package/src/__tests__/install-skill-routing.test.ts +2 -2
  84. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
  85. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  86. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  87. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  88. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  89. package/src/__tests__/llm-resolver.test.ts +46 -0
  90. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  91. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  92. package/src/__tests__/mcp-cli.test.ts +182 -220
  93. package/src/__tests__/mcp-health-check.test.ts +56 -27
  94. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  95. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  96. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  97. package/src/__tests__/oauth-cli.test.ts +38 -2009
  98. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  99. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  100. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  102. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  103. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  104. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  105. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  106. package/src/__tests__/plugin-types.test.ts +13 -11
  107. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  108. package/src/__tests__/profile-entry-status.test.ts +43 -0
  109. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  110. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  111. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  112. package/src/__tests__/relay-server.test.ts +118 -0
  113. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  114. package/src/__tests__/schedule-retry.test.ts +56 -4
  115. package/src/__tests__/schedule-routes.test.ts +104 -0
  116. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  117. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  118. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  119. package/src/__tests__/scheduler-wake.test.ts +0 -63
  120. package/src/__tests__/secret-allowlist.test.ts +1 -0
  121. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  122. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  123. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  124. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  125. package/src/__tests__/skill-load-tool.test.ts +2 -4
  126. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  127. package/src/__tests__/suggestion-routes.test.ts +3 -3
  128. package/src/__tests__/sync-message-contract.test.ts +63 -0
  129. package/src/__tests__/task-scheduler.test.ts +88 -23
  130. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  131. package/src/__tests__/usage-cli.test.ts +11 -73
  132. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  133. package/src/__tests__/vercel-config.test.ts +168 -0
  134. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  135. package/src/__tests__/web-search.test.ts +303 -2
  136. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  137. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  138. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  139. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  140. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  141. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  142. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  143. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  144. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  145. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  146. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  147. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  148. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  149. package/src/acp/resolve-agent.test.ts +25 -0
  150. package/src/acp/resolve-agent.ts +13 -2
  151. package/src/acp/session-manager.ts +14 -0
  152. package/src/approvals/guardian-request-resolvers.ts +32 -87
  153. package/src/calls/relay-server.ts +35 -0
  154. package/src/calls/relay-setup-router.ts +36 -0
  155. package/src/calls/types.ts +1 -0
  156. package/src/calls/voice-session-bridge.ts +23 -4
  157. package/src/channels/config.ts +14 -1
  158. package/src/channels/types.ts +1 -0
  159. package/src/cli/AGENTS.md +164 -4
  160. package/src/cli/__tests__/notifications.test.ts +54 -0
  161. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  162. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  163. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  164. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  165. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  166. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  167. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  168. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  169. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  170. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  171. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  172. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  173. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  174. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  175. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  176. package/src/cli/commands/__tests__/status.test.ts +249 -0
  177. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  178. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  179. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  180. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  181. package/src/cli/commands/attachment.ts +8 -3
  182. package/src/cli/commands/audit.ts +95 -64
  183. package/src/cli/commands/auth.ts +61 -58
  184. package/src/cli/commands/avatar.ts +276 -390
  185. package/src/cli/commands/backup.ts +409 -505
  186. package/src/cli/commands/bash.ts +9 -5
  187. package/src/cli/commands/browser.ts +28 -9
  188. package/src/cli/commands/cache.ts +9 -4
  189. package/src/cli/commands/changelog.ts +414 -0
  190. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  191. package/src/cli/commands/clients.ts +8 -3
  192. package/src/cli/commands/completions.ts +9 -9
  193. package/src/cli/commands/config.ts +102 -72
  194. package/src/cli/commands/contacts.ts +575 -696
  195. package/src/cli/commands/conversations-defer.ts +17 -69
  196. package/src/cli/commands/conversations-import.ts +90 -253
  197. package/src/cli/commands/conversations.ts +346 -436
  198. package/src/cli/commands/credential-execution.ts +9 -6
  199. package/src/cli/commands/credentials.ts +456 -736
  200. package/src/cli/commands/domain.ts +128 -206
  201. package/src/cli/commands/email.ts +606 -794
  202. package/src/cli/commands/gateway.ts +8 -1
  203. package/src/cli/commands/image-generation.ts +157 -205
  204. package/src/cli/commands/inference-providers.ts +352 -0
  205. package/src/cli/commands/inference-session.ts +415 -0
  206. package/src/cli/commands/inference.ts +87 -65
  207. package/src/cli/commands/keys.ts +8 -3
  208. package/src/cli/commands/mcp.ts +103 -287
  209. package/src/cli/commands/memory-v2.ts +162 -516
  210. package/src/cli/commands/notifications.ts +33 -7
  211. package/src/cli/commands/oauth/apps.ts +292 -261
  212. package/src/cli/commands/oauth/connect.ts +176 -297
  213. package/src/cli/commands/oauth/disconnect.ts +16 -215
  214. package/src/cli/commands/oauth/index.ts +49 -45
  215. package/src/cli/commands/oauth/mode.ts +43 -199
  216. package/src/cli/commands/oauth/ping.ts +17 -125
  217. package/src/cli/commands/oauth/providers.ts +732 -921
  218. package/src/cli/commands/oauth/request.ts +60 -350
  219. package/src/cli/commands/oauth/shared.ts +11 -121
  220. package/src/cli/commands/oauth/status.ts +31 -121
  221. package/src/cli/commands/oauth/token.ts +13 -55
  222. package/src/cli/commands/pending.ts +19 -10
  223. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  224. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  225. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  226. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  227. package/src/cli/commands/platform/connect.ts +16 -80
  228. package/src/cli/commands/platform/disconnect.ts +14 -112
  229. package/src/cli/commands/platform/index.ts +177 -246
  230. package/src/cli/commands/routes.ts +153 -336
  231. package/src/cli/commands/sequence.ts +316 -360
  232. package/src/cli/commands/skills.ts +449 -671
  233. package/src/cli/commands/status.ts +58 -37
  234. package/src/cli/commands/stt.ts +94 -262
  235. package/src/cli/commands/task.ts +14 -40
  236. package/src/cli/commands/trust.ts +8 -3
  237. package/src/cli/commands/tts.ts +162 -167
  238. package/src/cli/commands/ui.ts +35 -42
  239. package/src/cli/commands/usage.ts +188 -126
  240. package/src/cli/commands/watchers.ts +8 -3
  241. package/src/cli/commands/webhooks.ts +99 -193
  242. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  243. package/src/cli/lib/daemon-credential-client.ts +4 -5
  244. package/src/cli/lib/nested-value.ts +44 -0
  245. package/src/cli/lib/open-browser.ts +36 -0
  246. package/src/cli/lib/register-command.ts +19 -0
  247. package/src/cli/lib/time-ago.ts +34 -0
  248. package/src/cli/program.ts +2 -4
  249. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  250. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  251. package/src/cli/utils/conversation-id.ts +30 -0
  252. package/src/cli/utils/parse-duration.ts +41 -0
  253. package/src/config/acp-defaults.test.ts +5 -1
  254. package/src/config/acp-defaults.ts +11 -4
  255. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  256. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  258. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  259. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  260. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  261. package/src/config/bundled-tool-registry.ts +0 -2
  262. package/src/config/feature-flag-registry.json +16 -0
  263. package/src/config/llm-resolver.ts +16 -1
  264. package/src/config/loader.ts +76 -14
  265. package/src/config/raw-config-utils.ts +2 -30
  266. package/src/config/schema.ts +4 -0
  267. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  268. package/src/config/schemas/call-site-catalog.ts +29 -7
  269. package/src/config/schemas/llm-request-logs.ts +57 -0
  270. package/src/config/schemas/llm.ts +52 -2
  271. package/src/config/schemas/memory-retrospective.ts +48 -0
  272. package/src/config/schemas/memory-v2.ts +32 -1
  273. package/src/config/schemas/memory.ts +4 -0
  274. package/src/config/schemas/services.ts +15 -12
  275. package/src/config/seed-inference-profiles.ts +195 -134
  276. package/src/contacts/contact-store.ts +0 -61
  277. package/src/context/window-manager.ts +191 -5
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
  279. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  280. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  281. package/src/daemon/approval-generators.ts +23 -29
  282. package/src/daemon/config-watcher.ts +2 -0
  283. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  284. package/src/daemon/conversation-agent-loop.ts +127 -97
  285. package/src/daemon/conversation-error.ts +21 -0
  286. package/src/daemon/conversation-lifecycle.ts +46 -5
  287. package/src/daemon/conversation-process.ts +36 -19
  288. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  289. package/src/daemon/conversation-slash.ts +175 -23
  290. package/src/daemon/conversation-store.ts +17 -10
  291. package/src/daemon/conversation-surfaces.ts +76 -12
  292. package/src/daemon/conversation-tool-setup.ts +24 -14
  293. package/src/daemon/conversation.ts +48 -9
  294. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  295. package/src/daemon/guardian-action-generators.ts +7 -22
  296. package/src/daemon/handlers/config-model.ts +8 -126
  297. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  298. package/src/daemon/handlers/config-vercel.ts +3 -1
  299. package/src/daemon/handlers/skills.ts +84 -5
  300. package/src/daemon/history-repair.ts +33 -6
  301. package/src/daemon/host-app-control-proxy.ts +44 -19
  302. package/src/daemon/host-bash-proxy.ts +85 -158
  303. package/src/daemon/host-browser-proxy.ts +96 -35
  304. package/src/daemon/host-proxy-base.ts +13 -1
  305. package/src/daemon/host-proxy-preactivation.ts +25 -1
  306. package/src/daemon/identity-helpers.ts +19 -0
  307. package/src/daemon/lifecycle.ts +42 -43
  308. package/src/daemon/meet-host-supervisor.ts +15 -15
  309. package/src/daemon/memory-v2-startup.ts +9 -2
  310. package/src/daemon/message-protocol.ts +6 -0
  311. package/src/daemon/message-types/bookmarks.ts +18 -0
  312. package/src/daemon/message-types/conversations.ts +12 -9
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/sync.ts +60 -0
  315. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  316. package/src/daemon/pkb-reminder-builder.ts +21 -7
  317. package/src/daemon/process-message.ts +56 -23
  318. package/src/daemon/server.ts +23 -18
  319. package/src/daemon/shutdown-handlers.ts +0 -2
  320. package/src/daemon/tool-setup-types.ts +9 -0
  321. package/src/daemon/tool-side-effects.ts +6 -4
  322. package/src/daemon/wake-target-adapter.ts +11 -0
  323. package/src/export/transcript-formatter.ts +61 -2
  324. package/src/filing/filing-service.ts +40 -53
  325. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  326. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  327. package/src/heartbeat/heartbeat-service.ts +148 -127
  328. package/src/home/__tests__/feed-types.test.ts +63 -131
  329. package/src/home/__tests__/feed-writer.test.ts +77 -278
  330. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  331. package/src/home/feed-types.ts +19 -73
  332. package/src/home/feed-writer.ts +25 -156
  333. package/src/home/post-connect-feed.ts +1 -3
  334. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  335. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  336. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  337. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  338. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  339. package/src/ipc/assistant-server.ts +55 -6
  340. package/src/ipc/cli-client.ts +370 -50
  341. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  342. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  343. package/src/ipc/skill-routes/events.ts +30 -3
  344. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  345. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  346. package/src/live-voice/live-voice-session-manager.ts +11 -4
  347. package/src/live-voice/live-voice-session.ts +14 -6
  348. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  349. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  350. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  351. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  352. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  353. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  354. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  355. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  356. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  357. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  358. package/src/memory/bookmark-crud.ts +179 -0
  359. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  360. package/src/memory/context-search/agent-protocol.ts +5 -1
  361. package/src/memory/context-search/agent-runner.ts +60 -85
  362. package/src/memory/context-search/limits.ts +1 -4
  363. package/src/memory/context-search/search.ts +23 -113
  364. package/src/memory/context-search/sources/conversations.ts +18 -6
  365. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  366. package/src/memory/context-search/sources/memory.ts +7 -0
  367. package/src/memory/context-search/sources/workspace.ts +13 -10
  368. package/src/memory/context-search/types.ts +1 -1
  369. package/src/memory/conversation-bootstrap.ts +11 -0
  370. package/src/memory/conversation-crud.ts +312 -10
  371. package/src/memory/conversation-queries.ts +9 -5
  372. package/src/memory/conversation-title-service.ts +1 -0
  373. package/src/memory/conversation-types.ts +16 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +2 -1
  376. package/src/memory/embedding-runtime-manager.ts +1 -2
  377. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  378. package/src/memory/graph/conversation-graph-memory.ts +76 -5
  379. package/src/memory/graph/extraction.ts +4 -0
  380. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  381. package/src/memory/graph/tool-handlers.ts +17 -7
  382. package/src/memory/graph/tools.ts +44 -5
  383. package/src/memory/indexer.ts +17 -0
  384. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
  385. package/src/memory/jobs/embed-concept-page.ts +45 -9
  386. package/src/memory/jobs-store.ts +51 -1
  387. package/src/memory/jobs-worker.ts +52 -3
  388. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  389. package/src/memory/llm-request-log-source-local.ts +26 -0
  390. package/src/memory/llm-request-log-source.ts +97 -0
  391. package/src/memory/llm-request-log-store.ts +1 -1
  392. package/src/memory/memory-retrospective-constants.ts +13 -0
  393. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  394. package/src/memory/memory-retrospective-job.ts +351 -0
  395. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  396. package/src/memory/memory-retrospective-state.ts +162 -0
  397. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  398. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  399. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  400. package/src/memory/message-content.ts +38 -1
  401. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  402. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  403. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  404. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  405. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  406. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  407. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  408. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  409. package/src/memory/migrations/243-provider-connections.ts +68 -0
  410. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  411. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  412. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  413. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  414. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  415. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  416. package/src/memory/migrations/index.ts +7 -0
  417. package/src/memory/published-pages-store.ts +16 -0
  418. package/src/memory/schema/bookmarks.ts +38 -0
  419. package/src/memory/schema/conversations.ts +2 -0
  420. package/src/memory/schema/index.ts +2 -0
  421. package/src/memory/schema/inference.ts +29 -0
  422. package/src/memory/schema/memory-core.ts +9 -0
  423. package/src/memory/search/semantic.ts +1 -4
  424. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  425. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  426. package/src/memory/v2/__tests__/activation.test.ts +11 -4
  427. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  428. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  429. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  430. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  431. package/src/memory/v2/__tests__/injection.test.ts +628 -10
  432. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  433. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  434. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  435. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  436. package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
  437. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  438. package/src/memory/v2/__tests__/router.test.ts +516 -0
  439. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  440. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  441. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  442. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  443. package/src/memory/v2/activation-store.ts +34 -5
  444. package/src/memory/v2/activation.ts +40 -27
  445. package/src/memory/v2/backfill-jobs.ts +17 -84
  446. package/src/memory/v2/consolidation-job.ts +85 -78
  447. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  448. package/src/memory/v2/injection.ts +440 -109
  449. package/src/memory/v2/migration.ts +117 -20
  450. package/src/memory/v2/page-index.ts +191 -0
  451. package/src/memory/v2/page-store.ts +3 -0
  452. package/src/memory/v2/prompts/consolidation.ts +9 -7
  453. package/src/memory/v2/prompts/router.ts +192 -0
  454. package/src/memory/v2/qdrant.ts +100 -87
  455. package/src/memory/v2/reranker.ts +14 -7
  456. package/src/memory/v2/router.ts +322 -0
  457. package/src/memory/v2/sim.ts +25 -12
  458. package/src/memory/v2/skill-store.ts +118 -29
  459. package/src/memory/v2/static-context.ts +16 -9
  460. package/src/memory/v2/sweep-job.ts +122 -96
  461. package/src/memory/v2/types.ts +10 -6
  462. package/src/memory/validation.ts +13 -0
  463. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  464. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  465. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  466. package/src/notifications/adapters/platform.ts +171 -0
  467. package/src/notifications/conversation-pairing.ts +2 -2
  468. package/src/notifications/copy-composer.ts +15 -0
  469. package/src/notifications/destination-resolver.ts +21 -0
  470. package/src/notifications/emit-signal.ts +28 -1
  471. package/src/notifications/home-feed-side-effect.ts +111 -0
  472. package/src/notifications/signal.ts +5 -0
  473. package/src/permissions/checker.ts +12 -0
  474. package/src/permissions/ipc-risk-types.ts +2 -0
  475. package/src/plugin-api/index.ts +13 -0
  476. package/src/plugin-api/package.json +12 -0
  477. package/src/plugin-api/types.ts +62 -0
  478. package/src/plugins/defaults/injectors.ts +19 -3
  479. package/src/plugins/external-plugin-loader.ts +294 -0
  480. package/src/plugins/types.ts +46 -30
  481. package/src/plugins/user-loader.ts +64 -41
  482. package/src/proactive-artifact/job.test.ts +12 -4
  483. package/src/proactive-artifact/job.ts +4 -0
  484. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  485. package/src/proactive-artifact/trigger-state.ts +4 -0
  486. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  487. package/src/prompts/system-prompt.ts +22 -1
  488. package/src/prompts/update-bulletin-job.ts +61 -73
  489. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  490. package/src/providers/__tests__/inference.test.ts +288 -0
  491. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  492. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  493. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  494. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  495. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  496. package/src/providers/anthropic/client.ts +95 -26
  497. package/src/providers/call-site-routing.ts +94 -16
  498. package/src/providers/connection-resolution.ts +163 -0
  499. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  500. package/src/providers/inference/adapter-factory.ts +173 -0
  501. package/src/providers/inference/auth.ts +112 -0
  502. package/src/providers/inference/backfill.ts +196 -0
  503. package/src/providers/inference/connections.ts +356 -0
  504. package/src/providers/inference/resolve-auth.ts +65 -0
  505. package/src/providers/model-catalog.ts +104 -6
  506. package/src/providers/openai/responses-provider.ts +4 -2
  507. package/src/providers/provider-env-vars.ts +17 -7
  508. package/src/providers/provider-secret-catalog.ts +49 -30
  509. package/src/providers/provider-send-message.ts +41 -20
  510. package/src/providers/registry.ts +143 -159
  511. package/src/providers/retry.ts +18 -10
  512. package/src/providers/search-provider-catalog.ts +121 -0
  513. package/src/runtime/AGENTS.md +18 -5
  514. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  515. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  516. package/src/runtime/actor-trust-resolver.ts +32 -10
  517. package/src/runtime/agent-wake.ts +35 -6
  518. package/src/runtime/assistant-event-hub.ts +3 -85
  519. package/src/runtime/auth/route-policy.ts +303 -8
  520. package/src/runtime/auth/same-actor.ts +2 -0
  521. package/src/runtime/background-job-runner.ts +339 -0
  522. package/src/runtime/btw-sidechain.ts +1 -0
  523. package/src/runtime/http-router.ts +36 -1
  524. package/src/runtime/http-server.ts +31 -5
  525. package/src/runtime/http-types.ts +2 -0
  526. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  527. package/src/runtime/middleware/request-logger.ts +62 -1
  528. package/src/runtime/pre-first-message-gate.ts +83 -0
  529. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  530. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  531. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  532. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  533. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  534. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  535. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  536. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  537. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  538. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  539. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  540. package/src/runtime/routes/acp-routes.ts +10 -8
  541. package/src/runtime/routes/app-management-routes.ts +228 -3
  542. package/src/runtime/routes/approval-routes.ts +0 -18
  543. package/src/runtime/routes/audit-routes.ts +43 -0
  544. package/src/runtime/routes/auth-routes.ts +72 -0
  545. package/src/runtime/routes/avatar-routes.ts +273 -20
  546. package/src/runtime/routes/backup-routes.ts +406 -2
  547. package/src/runtime/routes/bookmark-routes.ts +154 -0
  548. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  549. package/src/runtime/routes/contact-routes.ts +0 -160
  550. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  551. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  552. package/src/runtime/routes/conversation-query-routes.ts +334 -86
  553. package/src/runtime/routes/conversation-routes.ts +31 -10
  554. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  555. package/src/runtime/routes/credential-routes.ts +540 -0
  556. package/src/runtime/routes/debug-routes.ts +2 -2
  557. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  558. package/src/runtime/routes/domain-routes.ts +167 -0
  559. package/src/runtime/routes/email-routes.ts +603 -0
  560. package/src/runtime/routes/errors.ts +2 -2
  561. package/src/runtime/routes/events-routes.ts +192 -0
  562. package/src/runtime/routes/home-feed-routes.ts +6 -78
  563. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  564. package/src/runtime/routes/host-browser-routes.ts +103 -22
  565. package/src/runtime/routes/http-adapter.ts +2 -0
  566. package/src/runtime/routes/identity-routes.ts +5 -0
  567. package/src/runtime/routes/image-generation-routes.ts +99 -0
  568. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  569. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  570. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  571. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  572. package/src/runtime/routes/index.ts +36 -0
  573. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  574. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  575. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  576. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  577. package/src/runtime/routes/inference-send-routes.ts +115 -0
  578. package/src/runtime/routes/integrations/twilio.ts +1 -0
  579. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  580. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  581. package/src/runtime/routes/notification-routes.ts +2 -0
  582. package/src/runtime/routes/oauth-apps.ts +112 -7
  583. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  584. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  585. package/src/runtime/routes/oauth-providers.ts +298 -8
  586. package/src/runtime/routes/platform-routes.ts +336 -0
  587. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  588. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  589. package/src/runtime/routes/playground/state.ts +2 -1
  590. package/src/runtime/routes/publish-routes.ts +221 -0
  591. package/src/runtime/routes/schedule-routes.ts +82 -0
  592. package/src/runtime/routes/sequence-routes.ts +291 -0
  593. package/src/runtime/routes/settings-routes.ts +2 -10
  594. package/src/runtime/routes/skills-routes.ts +31 -1
  595. package/src/runtime/routes/stt-routes.ts +240 -3
  596. package/src/runtime/routes/surface-action-routes.ts +43 -7
  597. package/src/runtime/routes/tts-routes.ts +67 -0
  598. package/src/runtime/routes/types.ts +32 -0
  599. package/src/runtime/routes/user-routes-cli.ts +243 -0
  600. package/src/runtime/routes/webhook-routes.ts +165 -0
  601. package/src/runtime/sync/resource-sync-events.ts +25 -0
  602. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  603. package/src/runtime/sync/sync-publisher.ts +21 -0
  604. package/src/schedule/scheduler.ts +200 -123
  605. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  606. package/src/security/secret-patterns.ts +3 -0
  607. package/src/sequence/engine.ts +38 -40
  608. package/src/subagent/manager.ts +20 -15
  609. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  610. package/src/tools/browser/browser-execution.ts +15 -4
  611. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  612. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  613. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  614. package/src/tools/browser/cdp-client/factory.ts +66 -5
  615. package/src/tools/browser/runtime-check.ts +77 -0
  616. package/src/tools/memory/register.test.ts +3 -3
  617. package/src/tools/memory/register.ts +9 -1
  618. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  619. package/src/tools/network/web-search.ts +280 -37
  620. package/src/tools/permission-checker.ts +13 -5
  621. package/src/tools/subagent/spawn.ts +3 -3
  622. package/src/tools/terminal/shell.ts +44 -0
  623. package/src/usage/attribution.ts +3 -2
  624. package/src/util/pricing.ts +86 -160
  625. package/src/watcher/__tests__/engine.test.ts +301 -0
  626. package/src/watcher/constants.ts +7 -0
  627. package/src/watcher/engine.ts +90 -90
  628. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  629. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  630. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  631. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  632. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  633. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  634. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  635. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  636. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  637. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  638. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  639. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  640. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  641. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  642. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  643. package/src/workspace/migrations/registry.ts +22 -0
  644. package/src/workspace/migrations/runner.ts +13 -2
  645. package/src/workspace/migrations/types.ts +13 -3
  646. package/src/workspace/provider-commit-message-generator.ts +3 -2
  647. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  648. package/src/__tests__/credentials-cli.test.ts +0 -1225
  649. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  650. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  651. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  652. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  653. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  654. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  655. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  656. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  657. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  658. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  659. package/src/cli/commands/autonomy.ts +0 -365
  660. package/src/cli/commands/memory.ts +0 -424
  661. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  662. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  663. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  664. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  665. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  666. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  667. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  668. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  669. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  670. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  671. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  672. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  673. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  674. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  675. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  676. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  677. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  678. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  679. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  680. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  681. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  682. package/src/home/assistant-feed-authoring.ts +0 -135
  683. package/src/home/emit-feed-event.ts +0 -169
  684. package/src/home/feed-scheduler.ts +0 -281
  685. package/src/home/platform-gmail-digest.ts +0 -163
  686. package/src/home/rewrite-command-preview.ts +0 -66
  687. package/src/home/rewrite-feed-title.ts +0 -58
  688. package/src/home/rollup-producer.ts +0 -426
  689. package/src/memory/admin.ts +0 -326
  690. package/src/memory/context-search/sources/pkb.ts +0 -476
  691. package/src/memory/graph/compaction.ts +0 -299
  692. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Centralized boundary wrapper for background-conversation jobs.
3
+ *
4
+ * `runBackgroundJob()` consolidates the bootstrap → processMessage → timeout
5
+ * pattern that every background producer (heartbeat, filing, scheduler, memory
6
+ * consolidation, watcher, update-bulletin, subagent, sequence) has been
7
+ * open-coding. Wrapping it here lets us:
8
+ *
9
+ * - apply a single timeout policy
10
+ * - classify failures uniformly (timeout / model_provider / generic exception)
11
+ * - emit a single `activity.failed` notification on any failure path so the
12
+ * home feed and native notification surfaces light up automatically
13
+ * - never re-throw — the caller always gets a structured result and decides
14
+ * whether to alert further
15
+ *
16
+ * Producers that have their own bespoke failure UX (e.g. heartbeat's existing
17
+ * alerter banner) can opt out of the failure-emit via
18
+ * `suppressFailureNotifications`.
19
+ */
20
+
21
+ import type { LLMCallSite } from "../config/schemas/llm.js";
22
+ import { processMessage } from "../daemon/process-message.js";
23
+ import type { TrustContext } from "../daemon/trust-context.js";
24
+ import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
25
+ import { addMessage } from "../memory/conversation-crud.js";
26
+ import type { TitleOrigin } from "../memory/conversation-title-service.js";
27
+ import { emitNotificationSignal } from "../notifications/emit-signal.js";
28
+ import type { AttentionHints } from "../notifications/signal.js";
29
+ import { getLogger } from "../util/logger.js";
30
+ import { hasReceivedUserMessage } from "./pre-first-message-gate.js";
31
+
32
+ const log = getLogger("background-job-runner");
33
+
34
+ const DEFAULT_GROUP_ID = "system:background";
35
+
36
+ /**
37
+ * Internal-only sentinel for timeouts. Not exported — callers receive a
38
+ * `errorKind: "timeout"` instead so they don't depend on the class identity.
39
+ */
40
+ class BackgroundJobTimeoutError extends Error {
41
+ override name = "BackgroundJobTimeoutError";
42
+ }
43
+
44
+ export type BackgroundJobErrorKind = "timeout" | "model_provider" | "exception";
45
+
46
+ export interface RunBackgroundJobOptions {
47
+ /** Short stable identifier for logs/notifications, e.g. "heartbeat", "filing". */
48
+ jobName: string;
49
+ /** Conversation `source` field (free-form, propagated to clients). */
50
+ source: string;
51
+ /** Prompt sent as the first message of the conversation. */
52
+ prompt: string;
53
+ /**
54
+ * Short, human-readable hint passed to `bootstrapConversation` for title
55
+ * generation and as the fallback title. Defaults to `prompt` when omitted,
56
+ * but callers with multi-paragraph prompts should supply a concise label
57
+ * (e.g. `"Knowledge base filing"`) — otherwise a fallback title would echo
58
+ * the entire prompt and title-generation requests waste tokens.
59
+ */
60
+ systemHint?: string;
61
+ /** Trust context applied to the agent turn. */
62
+ trustContext: TrustContext;
63
+ /** LLM call-site identifier — drives provider/model/effort/etc. resolution. */
64
+ callSite: LLMCallSite;
65
+ /** Hard timeout for `processMessage` in milliseconds. */
66
+ timeoutMs: number;
67
+ /**
68
+ * When true, failures do NOT emit an `activity.failed` notification.
69
+ * Use for jobs that own their own failure UX (e.g. heartbeat's alerter)
70
+ * or for "quiet" scheduled jobs that the user has explicitly asked to
71
+ * suppress notifications for.
72
+ */
73
+ suppressFailureNotifications?: boolean;
74
+ /** Conversation grouping id. Defaults to `"system:background"`. */
75
+ groupId?: string;
76
+ /** Title origin tag for `bootstrapConversation`. */
77
+ origin: TitleOrigin;
78
+ /** Conversation type to bootstrap with. Defaults to `"background"`. */
79
+ conversationType?: "background" | "scheduled";
80
+ /**
81
+ * Schedule job id to associate with the conversation row. Only meaningful
82
+ * for `conversationType: "scheduled"` — propagated so schedule cleanup and
83
+ * sidebar grouping can find the conversation by job id.
84
+ */
85
+ scheduleJobId?: string;
86
+ /**
87
+ * Fires synchronously after `bootstrapConversation` returns and BEFORE
88
+ * `processMessage` starts. Use this to populate the macOS sidebar entry
89
+ * immediately (the SSE event fires when the job starts) rather than after
90
+ * the job finishes (which can be up to `timeoutMs` later for long jobs).
91
+ *
92
+ * Wrapped in try/catch internally — a callback throw is logged and
93
+ * swallowed so it cannot kill the job runner.
94
+ */
95
+ onConversationCreated?: (conversationId: string) => void;
96
+ /**
97
+ * Opt out of the "skip until first user message" gate. Defaults to
98
+ * `false` (gate active). Set to `true` ONLY for jobs that genuinely need
99
+ * to run pre-onboarding — there are currently none, but the escape hatch
100
+ * exists so the gate can be tightened without trapping a future caller.
101
+ *
102
+ * The gate prevents warm-pool images from generating ghost failure rows
103
+ * before the user ever sees the assistant. See `pre-first-message-gate.ts`.
104
+ */
105
+ allowPreFirstUserMessage?: boolean;
106
+ /**
107
+ * Optional prompt-injection mitigation. When set, the runner adds three
108
+ * messages to the conversation BEFORE invoking `processMessage`:
109
+ *
110
+ * 1. `user` role: `preamble` — static, trusted instructions.
111
+ * 2. `assistant` role: `content` — attacker-controllable payload (the LLM
112
+ * treats it as its own past output, not as user instructions).
113
+ * 3. `user` role: `postamble` — static, trusted action prompt.
114
+ *
115
+ * `processMessage` is then invoked with whatever `prompt` the caller set
116
+ * (often empty or a short kicker) since the conversation already carries
117
+ * the seed.
118
+ *
119
+ * Used by the watcher engine to ingest external provider events safely:
120
+ * a malicious Linear title or Gmail subject reaches the model only in
121
+ * the `assistant` role and cannot override the action prompt.
122
+ */
123
+ assistantSandwich?: { preamble: string; content: string; postamble: string };
124
+ }
125
+
126
+ export interface RunBackgroundJobResult {
127
+ conversationId: string;
128
+ ok: boolean;
129
+ error?: Error;
130
+ errorKind?: BackgroundJobErrorKind;
131
+ /**
132
+ * Set when the runner declined to execute. Callers can distinguish a
133
+ * skipped job from a successful one even though both report `ok: true`.
134
+ *
135
+ * - `"pre_first_user_message"`: gate tripped — daemon has not yet seen
136
+ * any user-authored message in a standard conversation. No conversation
137
+ * was bootstrapped; `conversationId` is the empty string.
138
+ */
139
+ skipReason?: "pre_first_user_message";
140
+ }
141
+
142
+ function classifyError(err: unknown): BackgroundJobErrorKind {
143
+ if (err instanceof BackgroundJobTimeoutError) return "timeout";
144
+ if (!(err instanceof Error)) return "exception";
145
+
146
+ const ctorName = err.constructor?.name ?? "";
147
+ const { message } = err;
148
+
149
+ if (
150
+ ctorName.includes("Anthropic") ||
151
+ ctorName.includes("OpenAI") ||
152
+ /\brate\b/i.test(message) ||
153
+ /\b5xx\b/i.test(message) ||
154
+ /\b401\b/.test(message) ||
155
+ /\b403\b/.test(message)
156
+ ) {
157
+ return "model_provider";
158
+ }
159
+
160
+ return "exception";
161
+ }
162
+
163
+ /**
164
+ * Run a background conversation job with timeout, error classification, and
165
+ * (by default) failure notification emission. Never re-throws.
166
+ */
167
+ export async function runBackgroundJob(
168
+ opts: RunBackgroundJobOptions,
169
+ ): Promise<RunBackgroundJobResult> {
170
+ // Gate: refuse to bootstrap a conversation until the user has interacted
171
+ // at least once. Warm-pool images would otherwise produce "Background job
172
+ // failed" rows visible in the sidebar the moment a real user hatches the
173
+ // assistant — see `pre-first-message-gate.ts` for the rationale.
174
+ //
175
+ // Service-level callers (heartbeat, update-bulletin) are expected to gate
176
+ // earlier and never reach this point; reaching the gate here means a
177
+ // caller either forgot to gate or deliberately opted in via
178
+ // `allowPreFirstUserMessage`. We log at `info` (not `warn`) because the
179
+ // expected steady state is "no calls reach here once onboarding is done."
180
+ if (!opts.allowPreFirstUserMessage && !hasReceivedUserMessage()) {
181
+ log.info(
182
+ { jobName: opts.jobName, source: opts.source },
183
+ "Background job skipped — daemon has not received a first user message yet",
184
+ );
185
+ return {
186
+ ok: true,
187
+ conversationId: "",
188
+ skipReason: "pre_first_user_message",
189
+ };
190
+ }
191
+
192
+ let conversation: ReturnType<typeof bootstrapConversation> | undefined;
193
+ let timer: ReturnType<typeof setTimeout> | undefined;
194
+ try {
195
+ // Bootstrap inside the try so that a `createConversation` /
196
+ // `queueGenerateConversationTitle` failure is caught and surfaced as a
197
+ // structured `{ ok: false }` result rather than re-thrown to the caller —
198
+ // the documented contract of this runner.
199
+ conversation = bootstrapConversation({
200
+ conversationType: opts.conversationType ?? "background",
201
+ source: opts.source,
202
+ origin: opts.origin,
203
+ systemHint: opts.systemHint ?? opts.prompt,
204
+ groupId: opts.groupId ?? DEFAULT_GROUP_ID,
205
+ ...(opts.scheduleJobId ? { scheduleJobId: opts.scheduleJobId } : {}),
206
+ });
207
+
208
+ // Fire the sidebar-creation callback synchronously after bootstrap so
209
+ // connected clients (macOS sidebar, etc.) see the conversation appear
210
+ // immediately rather than after `processMessage` returns. Wrapped so a
211
+ // callback throw cannot abort the job.
212
+ if (opts.onConversationCreated) {
213
+ try {
214
+ opts.onConversationCreated(conversation.id);
215
+ } catch (cbErr) {
216
+ log.warn(
217
+ {
218
+ err: cbErr instanceof Error ? cbErr.message : String(cbErr),
219
+ jobName: opts.jobName,
220
+ conversationId: conversation.id,
221
+ },
222
+ "onConversationCreated callback threw; continuing job",
223
+ );
224
+ }
225
+ }
226
+
227
+ // SECURITY: Optional anti-injection sandwich. Attacker-controllable data
228
+ // is wrapped in an assistant-role message between two static user-role
229
+ // messages. The LLM treats assistant-role content as its own prior
230
+ // output, not as user instructions, so a malicious payload (e.g. a
231
+ // crafted Linear title) cannot override the postamble's action prompt.
232
+ if (opts.assistantSandwich) {
233
+ await addMessage(
234
+ conversation.id,
235
+ "user",
236
+ opts.assistantSandwich.preamble,
237
+ undefined,
238
+ { skipIndexing: true },
239
+ );
240
+ await addMessage(
241
+ conversation.id,
242
+ "assistant",
243
+ opts.assistantSandwich.content,
244
+ undefined,
245
+ { skipIndexing: true },
246
+ );
247
+ await addMessage(
248
+ conversation.id,
249
+ "user",
250
+ opts.assistantSandwich.postamble,
251
+ undefined,
252
+ { skipIndexing: true },
253
+ );
254
+ }
255
+
256
+ const work = processMessage(conversation.id, opts.prompt, undefined, {
257
+ trustContext: opts.trustContext,
258
+ callSite: opts.callSite,
259
+ });
260
+ // Absorb late rejections: if the timeout wins the race, `work` keeps
261
+ // running and may eventually reject — swallow so it doesn't surface as
262
+ // an unhandled rejection.
263
+ work.catch(() => {});
264
+
265
+ const timeout = new Promise<never>((_, reject) => {
266
+ timer = setTimeout(() => {
267
+ reject(
268
+ new BackgroundJobTimeoutError(
269
+ `Background job '${opts.jobName}' timed out after ${opts.timeoutMs}ms`,
270
+ ),
271
+ );
272
+ }, opts.timeoutMs);
273
+ });
274
+
275
+ await Promise.race([work, timeout]);
276
+ return { conversationId: conversation.id, ok: true };
277
+ } catch (err) {
278
+ const errorKind = classifyError(err);
279
+ const error = err instanceof Error ? err : new Error(String(err));
280
+ // Bootstrap can fail before `conversation` is assigned; fall back to ""
281
+ // so the structured failure result still flows to the caller.
282
+ const conversationId = conversation?.id ?? "";
283
+
284
+ log.error(
285
+ {
286
+ err: error.message,
287
+ errorKind,
288
+ jobName: opts.jobName,
289
+ conversationId,
290
+ },
291
+ "Background job failed",
292
+ );
293
+
294
+ if (!opts.suppressFailureNotifications) {
295
+ const hints: AttentionHints = {
296
+ requiresAction: false,
297
+ urgency: "medium",
298
+ isAsyncBackground: true,
299
+ visibleInSourceNow: false,
300
+ };
301
+ // Dedupe by jobName + UTC date so repeated failures of the same
302
+ // background job (e.g. a watcher whose credentials are revoked)
303
+ // collapse into a single home-feed entry per day rather than
304
+ // spamming on every tick.
305
+ const day = new Date().toISOString().slice(0, 10);
306
+ const dedupeKey = `activity-failed:${opts.jobName}:${day}`;
307
+ emitNotificationSignal({
308
+ sourceChannel: "assistant_tool",
309
+ sourceContextId: conversationId,
310
+ sourceEventName: "activity.failed",
311
+ dedupeKey,
312
+ contextPayload: {
313
+ jobName: opts.jobName,
314
+ errorMessage: error.message,
315
+ errorKind,
316
+ },
317
+ attentionHints: hints,
318
+ }).catch((emitErr) => {
319
+ log.warn(
320
+ {
321
+ err: emitErr instanceof Error ? emitErr.message : String(emitErr),
322
+ jobName: opts.jobName,
323
+ conversationId,
324
+ },
325
+ "Failed to emit activity.failed notification for background job",
326
+ );
327
+ });
328
+ }
329
+
330
+ return {
331
+ conversationId,
332
+ ok: false,
333
+ error,
334
+ errorKind,
335
+ };
336
+ } finally {
337
+ if (timer) clearTimeout(timer);
338
+ }
339
+ }
@@ -76,6 +76,7 @@ export async function runBtwSidechain(
76
76
  ? params.conversation.systemPrompt
77
77
  : buildSystemPrompt({
78
78
  excludeBootstrap: true,
79
+ excludeCustomPrefix: true,
79
80
  userPersona: params.userPersona,
80
81
  channelPersona: params.channelPersona,
81
82
  userSlug: params.userSlug,
@@ -18,7 +18,7 @@ import { httpError } from "./http-errors.js";
18
18
  import { withErrorHandling } from "./middleware/error-handler.js";
19
19
  import { routeDefinitionsToHTTPRoutes } from "./routes/http-adapter.js";
20
20
  import { ROUTES } from "./routes/index.js";
21
- import type { RoutePathParam } from "./routes/types.js";
21
+ import type { RouteLoggingConfig, RoutePathParam } from "./routes/types.js";
22
22
 
23
23
  // ---------------------------------------------------------------------------
24
24
  // Route definition types
@@ -128,6 +128,11 @@ export interface HTTPRouteDefinition {
128
128
  responseStatus?: string;
129
129
  /** Additional response codes documented in the generated OpenAPI spec. */
130
130
  additionalResponses?: Record<string, RouteAdditionalResponse>;
131
+ /**
132
+ * Per-route request-log control. See `RouteLoggingConfig` in `routes/types.ts`.
133
+ * When omitted, the route uses the default log-every-request behavior.
134
+ */
135
+ logging?: RouteLoggingConfig;
131
136
  }
132
137
 
133
138
  // ---------------------------------------------------------------------------
@@ -155,6 +160,36 @@ export class HttpRouter {
155
160
  }
156
161
  }
157
162
 
163
+ /**
164
+ * Resolve the request-logging metadata for an incoming request without
165
+ * invoking the handler. Returns the matched route's logging config plus
166
+ * a stable counter key derived from operationId (or method+endpoint as
167
+ * a fallback when operationId is not set).
168
+ *
169
+ * Called by the HTTP server *before* `withRequestLogging` so the
170
+ * middleware can decide whether to suppress the per-request success log.
171
+ * Returns `null` when no route matches or the matched route opts out of
172
+ * custom logging — both cases mean "use the default log-every-request".
173
+ */
174
+ findLoggingMetadata(
175
+ method: string,
176
+ endpoint: string,
177
+ ): { counterKey: string; config: RouteLoggingConfig } | null {
178
+ const normalized = endpoint.endsWith("/")
179
+ ? endpoint.slice(0, -1)
180
+ : endpoint;
181
+ for (const compiled of this.compiledRoutes) {
182
+ if (compiled.def.method !== method) continue;
183
+ if (!compiled.regex.test(normalized)) continue;
184
+ const config = compiled.def.logging;
185
+ if (!config) return null;
186
+ const counterKey =
187
+ compiled.def.operationId ?? `${method} ${compiled.def.endpoint}`;
188
+ return { counterKey, config };
189
+ }
190
+ return null;
191
+ }
192
+
158
193
  /**
159
194
  * Dispatch a request to the matching route handler.
160
195
  *
@@ -86,6 +86,10 @@ import {
86
86
  } from "./routes/channel-guardian-routes.js";
87
87
  import { RouteError } from "./routes/errors.js";
88
88
  import { handleHealth, handleReadyz } from "./routes/identity-routes.js";
89
+ import {
90
+ startInferenceProfileSessionReaper,
91
+ stopInferenceProfileSessionReaper,
92
+ } from "./routes/inference-profile-session-reaper.js";
89
93
  import { matchSkillRoute } from "./skill-route-registry.js";
90
94
 
91
95
  // Re-export for consumers
@@ -492,12 +496,16 @@ export class RuntimeHttpServer {
492
496
 
493
497
  startCanonicalGuardianExpirySweep();
494
498
  log.info("Canonical guardian request expiry sweep started");
499
+
500
+ startInferenceProfileSessionReaper();
501
+ log.info("Inference profile session reaper started");
495
502
  }
496
503
 
497
504
  async stop(): Promise<void> {
498
505
  stopGuardianExpirySweep();
499
506
  stopGuardianActionSweep();
500
507
  stopCanonicalGuardianExpirySweep();
508
+ stopInferenceProfileSessionReaper();
501
509
  if (this.retrySweepTimer) {
502
510
  clearInterval(this.retrySweepTimer);
503
511
  this.retrySweepTimer = null;
@@ -531,7 +539,9 @@ export class RuntimeHttpServer {
531
539
  server: ReturnType<typeof Bun.serve>,
532
540
  ): Promise<Response> {
533
541
  server.timeout(req, 1800);
534
- // Skip request logging for health-check probes to reduce log noise.
542
+ // Skip request logging entirely for the bare-bones liveness/readiness
543
+ // probes Bun's load balancer hits — these don't go through the
544
+ // declarative router and would just clutter logs unconditionally.
535
545
  const url = new URL(req.url);
536
546
  if (
537
547
  (url.pathname === "/healthz" || url.pathname === "/readyz") &&
@@ -539,7 +549,22 @@ export class RuntimeHttpServer {
539
549
  ) {
540
550
  return this.routeRequest(req, server);
541
551
  }
542
- return withRequestLogging(req, () => this.routeRequest(req, server));
552
+ // Ask the router for any per-route logging policy *before* dispatching,
553
+ // so the middleware can decide whether to suppress the success log
554
+ // line for noisy probes (e.g. macOS app polling /v1/health every few
555
+ // seconds). Only `/v1/*` paths are registered with the declarative
556
+ // router; for everything else `meta` stays undefined and the middleware
557
+ // falls back to its default log-every-request behavior.
558
+ let meta;
559
+ if (url.pathname.startsWith("/v1/")) {
560
+ const endpoint = url.pathname.slice("/v1/".length).replace(/\/$/, "");
561
+ meta = this.router.findLoggingMetadata(req.method, endpoint) ?? undefined;
562
+ }
563
+ return withRequestLogging(
564
+ req,
565
+ () => this.routeRequest(req, server),
566
+ meta,
567
+ );
543
568
  }
544
569
 
545
570
  private async routeRequest(
@@ -557,7 +582,6 @@ export class RuntimeHttpServer {
557
582
  return handleReadyz();
558
583
  }
559
584
 
560
-
561
585
  // WebSocket upgrade for ConversationRelay — before auth check because
562
586
  // Twilio WebSocket connections don't use bearer tokens.
563
587
  if (
@@ -623,6 +647,7 @@ export class RuntimeHttpServer {
623
647
  err.code as HttpErrorCode,
624
648
  err.message,
625
649
  err.statusCode,
650
+ err.details,
626
651
  );
627
652
  }
628
653
  throw err;
@@ -1013,8 +1038,9 @@ export class RuntimeHttpServer {
1013
1038
  ws: ServerWebSocket<LiveVoiceWebSocketData>,
1014
1039
  frame: LiveVoiceServerFrame,
1015
1040
  ): void {
1016
- ws.data.lastSeq = Math.max(ws.data.lastSeq, frame.seq);
1017
- ws.send(JSON.stringify(frame));
1041
+ const seq = Math.max(ws.data.lastSeq + 1, frame.seq);
1042
+ ws.data.lastSeq = seq;
1043
+ ws.send(JSON.stringify({ ...frame, seq }));
1018
1044
  }
1019
1045
 
1020
1046
  private releaseLiveVoiceSession(
@@ -191,6 +191,8 @@ export interface RuntimeAttachmentMetadata {
191
191
 
192
192
  export interface RuntimeMessagePayload {
193
193
  id: string;
194
+ /** Concrete persisted assistant row id for row-scoped actions. */
195
+ daemonMessageId?: string;
194
196
  role: string;
195
197
  content: string;
196
198
  timestamp: string;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Tests for the per-route success-suppression behavior of `withRequestLogging`.
3
+ *
4
+ * Default behavior (log every request) is exercised end-to-end by the rest
5
+ * of the runtime test suite; the cases here pin down the opt-in
6
+ * `RouteLoggingConfig.silenceSuccessAfter` path.
7
+ */
8
+
9
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
10
+
11
+ interface CapturedLog {
12
+ level: "info" | "warn" | "error";
13
+ status?: number;
14
+ }
15
+
16
+ const captured: CapturedLog[] = [];
17
+
18
+ // Replace the module-level logger so the tests see exactly which lines
19
+ // would have been emitted, without going through pino at all. The mock
20
+ // must be installed BEFORE `request-logger.ts` is loaded — otherwise the
21
+ // real `getLogger()` is captured at module init. Dynamic-import the
22
+ // module after the mock to enforce that ordering.
23
+ mock.module("../../../util/logger.js", () => ({
24
+ getLogger: () => ({
25
+ info: (data: unknown) =>
26
+ captured.push({ level: "info", status: extractStatus(data) }),
27
+ warn: (data: unknown) =>
28
+ captured.push({ level: "warn", status: extractStatus(data) }),
29
+ error: (data: unknown) =>
30
+ captured.push({ level: "error", status: extractStatus(data) }),
31
+ debug: () => {},
32
+ trace: () => {},
33
+ fatal: () => {},
34
+ }),
35
+ }));
36
+
37
+ const {
38
+ _resetRequestLoggingCountersForTests,
39
+ withRequestLogging,
40
+ }: typeof import("../request-logger.js") = await import(
41
+ "../request-logger.js"
42
+ );
43
+
44
+ type RequestLogMetadata = import("../request-logger.js").RequestLogMetadata;
45
+
46
+ function extractStatus(data: unknown): number | undefined {
47
+ if (data && typeof data === "object" && "status" in data) {
48
+ const s = (data as { status?: unknown }).status;
49
+ return typeof s === "number" ? s : undefined;
50
+ }
51
+ return undefined;
52
+ }
53
+
54
+ function reqAt(path: string): Request {
55
+ return new Request(`http://localhost${path}`);
56
+ }
57
+
58
+ function ok(): Promise<Response> {
59
+ return Promise.resolve(new Response(null, { status: 200 }));
60
+ }
61
+
62
+ function status(code: number): () => Promise<Response> {
63
+ return () => Promise.resolve(new Response(null, { status: code }));
64
+ }
65
+
66
+ describe("withRequestLogging", () => {
67
+ beforeEach(() => {
68
+ captured.length = 0;
69
+ _resetRequestLoggingCountersForTests();
70
+ });
71
+
72
+ afterEach(() => {
73
+ _resetRequestLoggingCountersForTests();
74
+ });
75
+
76
+ test("logs every successful request when no metadata is supplied", async () => {
77
+ for (let i = 0; i < 10; i++) {
78
+ await withRequestLogging(reqAt("/v1/anything"), ok);
79
+ }
80
+ expect(captured.filter((c) => c.level === "info").length).toBe(10);
81
+ expect(captured.every((c) => c.status === 200)).toBe(true);
82
+ });
83
+
84
+ test("silences successes after the configured threshold", async () => {
85
+ const meta: RequestLogMetadata = {
86
+ counterKey: "health",
87
+ config: { silenceSuccessAfter: 3 },
88
+ };
89
+ for (let i = 0; i < 8; i++) {
90
+ await withRequestLogging(reqAt("/v1/health"), ok, meta);
91
+ }
92
+ // First 3 successes log; remaining 5 are suppressed.
93
+ const infos = captured.filter((c) => c.level === "info");
94
+ expect(infos.length).toBe(3);
95
+ expect(infos.every((c) => c.status === 200)).toBe(true);
96
+ });
97
+
98
+ test("4xx and 5xx always log, even after success suppression kicks in", async () => {
99
+ const meta: RequestLogMetadata = {
100
+ counterKey: "health",
101
+ config: { silenceSuccessAfter: 2 },
102
+ };
103
+ // Burn through the success budget.
104
+ await withRequestLogging(reqAt("/v1/health"), ok, meta);
105
+ await withRequestLogging(reqAt("/v1/health"), ok, meta);
106
+ // 3rd, 4th, 5th: should be suppressed for 200, but 4xx/5xx must log.
107
+ await withRequestLogging(reqAt("/v1/health"), ok, meta); // suppressed
108
+ await withRequestLogging(reqAt("/v1/health"), status(404), meta); // warn
109
+ await withRequestLogging(reqAt("/v1/health"), status(500), meta); // error
110
+ await withRequestLogging(reqAt("/v1/health"), ok, meta); // suppressed
111
+
112
+ expect(captured.filter((c) => c.level === "info").length).toBe(2);
113
+ expect(captured.filter((c) => c.level === "warn").length).toBe(1);
114
+ expect(captured.filter((c) => c.level === "error").length).toBe(1);
115
+ });
116
+
117
+ test("counters are isolated per counterKey", async () => {
118
+ const metaA: RequestLogMetadata = {
119
+ counterKey: "route-a",
120
+ config: { silenceSuccessAfter: 2 },
121
+ };
122
+ const metaB: RequestLogMetadata = {
123
+ counterKey: "route-b",
124
+ config: { silenceSuccessAfter: 2 },
125
+ };
126
+ // Exhaust A's budget.
127
+ await withRequestLogging(reqAt("/v1/a"), ok, metaA);
128
+ await withRequestLogging(reqAt("/v1/a"), ok, metaA);
129
+ await withRequestLogging(reqAt("/v1/a"), ok, metaA); // suppressed
130
+ // B still has full budget.
131
+ await withRequestLogging(reqAt("/v1/b"), ok, metaB);
132
+ await withRequestLogging(reqAt("/v1/b"), ok, metaB);
133
+
134
+ expect(captured.filter((c) => c.level === "info").length).toBe(4);
135
+ });
136
+
137
+ test("a route with `silenceSuccessAfter: 0` is treated as opt-out", async () => {
138
+ // Zero is reserved as "no threshold configured" so config authors can
139
+ // toggle the field without re-deriving the default. The route still
140
+ // logs every request.
141
+ const meta: RequestLogMetadata = {
142
+ counterKey: "zero",
143
+ config: { silenceSuccessAfter: 0 },
144
+ };
145
+ for (let i = 0; i < 4; i++) {
146
+ await withRequestLogging(reqAt("/v1/zero"), ok, meta);
147
+ }
148
+ expect(captured.filter((c) => c.level === "info").length).toBe(4);
149
+ });
150
+
151
+ test("errors thrown by the handler propagate and emit an error line", async () => {
152
+ const meta: RequestLogMetadata = {
153
+ counterKey: "boom",
154
+ config: { silenceSuccessAfter: 1 },
155
+ };
156
+ const thrower = () => Promise.reject(new Error("boom"));
157
+ await expect(
158
+ withRequestLogging(reqAt("/v1/boom"), thrower, meta),
159
+ ).rejects.toThrow("boom");
160
+ expect(captured.filter((c) => c.level === "error").length).toBe(1);
161
+ });
162
+ });