@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
@@ -9,9 +9,10 @@
9
9
  * deterministic fallback for long AF_UNIX paths.
10
10
  */
11
11
 
12
- import { connect, type Socket } from "node:net";
12
+ import { Socket } from "node:net";
13
13
 
14
14
  import { getLogger } from "../util/logger.js";
15
+ import { IpcFrameReader, writeMessage } from "./ipc-framing.js";
15
16
  import { getAssistantSocketPath } from "./socket-path.js";
16
17
 
17
18
  const log = getLogger("cli-ipc-client");
@@ -20,12 +21,6 @@ const log = getLogger("cli-ipc-client");
20
21
  // Types (mirror cli-server.ts protocol)
21
22
  // ---------------------------------------------------------------------------
22
23
 
23
- type IpcRequest = {
24
- id: string;
25
- method: string;
26
- params?: Record<string, unknown>;
27
- };
28
-
29
24
  type IpcResponse = {
30
25
  id: string;
31
26
  result?: unknown;
@@ -74,10 +69,15 @@ export interface CliIpcCallResult<T = unknown> {
74
69
  export async function cliIpcCall<T = unknown>(
75
70
  method: string,
76
71
  params?: Record<string, unknown>,
77
- options?: { timeoutMs?: number },
72
+ options?: { timeoutMs?: number; signal?: AbortSignal },
78
73
  ): Promise<CliIpcCallResult<T>> {
74
+ if (options?.signal?.aborted) {
75
+ throw options.signal.reason ?? new DOMException("Aborted", "AbortError");
76
+ }
77
+
79
78
  const socketPath = getAssistantSocketPath();
80
79
  const callTimeoutMs = options?.timeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;
80
+ const opts = options; // alias used in the Promise callback below
81
81
 
82
82
  return new Promise<CliIpcCallResult<T>>((resolve) => {
83
83
  let settled = false;
@@ -99,20 +99,65 @@ export async function cliIpcCall<T = unknown>(
99
99
  );
100
100
  finish({
101
101
  ok: false,
102
- error: "Could not connect to assistant daemon. Is it running?",
102
+ error: `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`,
103
103
  });
104
104
  }, CONNECT_TIMEOUT_MS);
105
105
 
106
- const socket: Socket = connect(socketPath);
106
+ // Create the socket without connecting first so error/close handlers are
107
+ // registered before initiating the connection. In Bun, socket errors can
108
+ // fire synchronously during connect(), before listeners added afterward.
109
+ const socket = new Socket();
107
110
  socket.unref();
108
111
 
109
- let buffer = "";
112
+ socket.on("error", (err) => {
113
+ const code = (err as NodeJS.ErrnoException).code;
114
+ log.debug({ err, code, method, socketPath }, "CLI IPC socket error");
115
+ finish({
116
+ ok: false,
117
+ error:
118
+ code === "ENOENT" || code === "ECONNREFUSED"
119
+ ? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
120
+ : `Connection error: ${code ?? err.message}`,
121
+ });
122
+ });
123
+
124
+ socket.on("close", (hadError) => {
125
+ if (!settled) {
126
+ finish({
127
+ ok: false,
128
+ // hadError is true when close follows a socket error (e.g. ENOENT).
129
+ error: hadError
130
+ ? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
131
+ : "Connection closed before response",
132
+ });
133
+ }
134
+ });
135
+
110
136
  const reqId = crypto.randomUUID();
111
137
 
138
+ opts?.signal?.addEventListener("abort", () => {
139
+ finish({ ok: false, error: "Request aborted" });
140
+ }, { once: true });
141
+
142
+ const reader = new IpcFrameReader(
143
+ (envelope) => {
144
+ if (envelope.id !== reqId) return;
145
+ const msg = envelope as IpcResponse;
146
+ if (msg.error) {
147
+ finish({ ok: false, error: msg.error,
148
+ ...(msg.statusCode != null && { statusCode: msg.statusCode }),
149
+ ...(msg.errorCode != null && { errorCode: msg.errorCode }),
150
+ ...(msg.errorDetails != null && { errorDetails: msg.errorDetails }) });
151
+ } else {
152
+ finish({ ok: true, result: msg.result as T });
153
+ }
154
+ },
155
+ (err) => finish({ ok: false, error: err.message }),
156
+ );
157
+
112
158
  socket.on("connect", () => {
113
159
  clearTimeout(connectTimer);
114
- const req: IpcRequest = { id: reqId, method, params };
115
- socket.write(JSON.stringify(req) + "\n");
160
+ writeMessage(socket, { id: reqId, method, params });
116
161
 
117
162
  callTimer = setTimeout(() => {
118
163
  log.debug(
@@ -123,61 +168,336 @@ export async function cliIpcCall<T = unknown>(
123
168
  }, callTimeoutMs);
124
169
 
125
170
  socket.on("data", (chunk) => {
126
- buffer += chunk.toString();
127
- let newlineIdx: number;
128
- while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
129
- const line = buffer.slice(0, newlineIdx).trim();
130
- buffer = buffer.slice(newlineIdx + 1);
131
- if (!line) continue;
132
-
133
- try {
134
- const msg = JSON.parse(line) as IpcResponse;
135
- if (msg.id === reqId) {
136
- if (msg.error) {
137
- finish({
138
- ok: false,
139
- error: msg.error,
140
- ...(msg.statusCode !== undefined && {
141
- statusCode: msg.statusCode,
142
- }),
143
- ...(msg.errorCode !== undefined && {
144
- errorCode: msg.errorCode,
145
- }),
146
- ...(msg.errorDetails !== undefined && {
147
- errorDetails: msg.errorDetails,
148
- }),
149
- });
150
- } else {
151
- finish({ ok: true, result: msg.result as T });
152
- }
153
- return;
154
- }
155
- } catch {
156
- // Ignore malformed lines
157
- }
158
- }
171
+ reader.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
159
172
  });
160
173
  });
161
174
 
175
+ socket.connect(socketPath);
176
+ });
177
+ }
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // Binary one-shot client
181
+ // ---------------------------------------------------------------------------
182
+
183
+ const DEFAULT_BINARY_TIMEOUT_MS = 60_000;
184
+
185
+ /**
186
+ * One-shot IPC call that expects a single binary frame response.
187
+ *
188
+ * Use when the route returns an IpcBinaryResponse (content-length header
189
+ * + one binary data frame). Returns the headers and raw bytes.
190
+ *
191
+ * @example
192
+ * const r = await cliIpcCallBinary("export_file", { id: "abc" });
193
+ * if (!r.ok) return exitFromIpcResult(r, cmd);
194
+ * fs.writeFileSync("out.bin", Buffer.from(r.bytes));
195
+ */
196
+ export async function cliIpcCallBinary(
197
+ method: string,
198
+ params?: Record<string, unknown>,
199
+ opts?: { timeoutMs?: number; signal?: AbortSignal },
200
+ ): Promise<
201
+ | { ok: true; headers: Record<string, string>; bytes: Uint8Array }
202
+ | { ok: false; error: string; statusCode?: number; errorCode?: string; errorDetails?: unknown }
203
+ > {
204
+ if (opts?.signal?.aborted) {
205
+ throw opts.signal.reason ?? new DOMException("Aborted", "AbortError");
206
+ }
207
+
208
+ const socketPath = getAssistantSocketPath();
209
+ const callTimeoutMs = opts?.timeoutMs ?? DEFAULT_BINARY_TIMEOUT_MS;
210
+
211
+ return new Promise((resolve) => {
212
+ let settled = false;
213
+ let callTimer: ReturnType<typeof setTimeout> | undefined;
214
+
215
+ const finish = (
216
+ result:
217
+ | { ok: true; headers: Record<string, string>; bytes: Uint8Array }
218
+ | { ok: false; error: string; statusCode?: number; errorCode?: string; errorDetails?: unknown },
219
+ ) => {
220
+ if (settled) return;
221
+ settled = true;
222
+ clearTimeout(connectTimer);
223
+ if (callTimer) clearTimeout(callTimer);
224
+ socket.destroy();
225
+ resolve(result);
226
+ };
227
+
228
+ const connectTimer = setTimeout(() => {
229
+ log.debug({ method, socketPath, timeoutMs: CONNECT_TIMEOUT_MS }, "CLI IPC binary connect timed out");
230
+ finish({ ok: false, error: `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.` });
231
+ }, CONNECT_TIMEOUT_MS);
232
+
233
+ const socket = new Socket();
234
+ socket.unref();
235
+
162
236
  socket.on("error", (err) => {
163
237
  const code = (err as NodeJS.ErrnoException).code;
164
- log.debug({ err, code, method, socketPath }, "CLI IPC socket error");
238
+ log.debug({ err, code, method, socketPath }, "CLI IPC binary socket error");
165
239
  finish({
166
240
  ok: false,
167
241
  error:
168
242
  code === "ENOENT" || code === "ECONNREFUSED"
169
- ? "Could not connect to assistant daemon. Is it running?"
243
+ ? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
170
244
  : `Connection error: ${code ?? err.message}`,
171
245
  });
172
246
  });
173
247
 
174
- socket.on("close", () => {
248
+ socket.on("close", (hadError) => {
175
249
  if (!settled) {
176
250
  finish({
177
251
  ok: false,
178
- error: "Connection closed before response",
252
+ error: hadError
253
+ ? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
254
+ : "Connection closed before response",
179
255
  });
180
256
  }
181
257
  });
258
+
259
+ const reqId = crypto.randomUUID();
260
+
261
+ opts?.signal?.addEventListener("abort", () => {
262
+ finish({ ok: false, error: "Request aborted" });
263
+ }, { once: true });
264
+
265
+ const reader = new IpcFrameReader(
266
+ (envelope, binary) => {
267
+ if (envelope.id !== reqId) return;
268
+ const msg = envelope as IpcResponse;
269
+ if (msg.error) {
270
+ finish({ ok: false, error: msg.error,
271
+ ...(msg.statusCode != null && { statusCode: msg.statusCode }),
272
+ ...(msg.errorCode != null && { errorCode: msg.errorCode }),
273
+ ...(msg.errorDetails != null && { errorDetails: msg.errorDetails }) });
274
+ } else if (binary === undefined) {
275
+ finish({ ok: false, error: "Expected binary frame but received JSON-only response" });
276
+ } else {
277
+ finish({ ok: true, headers: (envelope.headers ?? {}) as Record<string, string>, bytes: binary });
278
+ }
279
+ },
280
+ (err) => finish({ ok: false, error: err.message }),
281
+ );
282
+
283
+ socket.on("connect", () => {
284
+ clearTimeout(connectTimer);
285
+ writeMessage(socket, { id: reqId, method, params });
286
+
287
+ callTimer = setTimeout(() => {
288
+ log.debug({ method, socketPath, timeoutMs: callTimeoutMs }, "CLI IPC binary call timed out");
289
+ finish({ ok: false, error: "Request timed out" });
290
+ }, callTimeoutMs);
291
+
292
+ socket.on("data", (chunk) => {
293
+ reader.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
294
+ });
295
+ });
296
+
297
+ socket.connect(socketPath);
182
298
  });
183
299
  }
300
+
301
+ // ---------------------------------------------------------------------------
302
+ // Streaming client
303
+ // ---------------------------------------------------------------------------
304
+
305
+ const DEFAULT_FIRST_BYTE_TIMEOUT_MS = 30_000;
306
+
307
+ /**
308
+ * Streaming IPC call. Returns a ReadableStream of binary chunks.
309
+ *
310
+ * The promise resolves once the opening envelope arrives (or fails). The
311
+ * stream body is delivered asynchronously afterward. Call abort() to
312
+ * cancel mid-stream — this sends a $cancel envelope to the server.
313
+ *
314
+ * No total timeout. Only a first-byte timeout (default 30s).
315
+ *
316
+ * @example
317
+ * const r = await cliIpcCallStream("export_stream", { id: "abc" });
318
+ * if (!r.ok) return exitFromIpcResult(r, cmd);
319
+ * for await (const chunk of r.body) process.stdout.write(chunk);
320
+ */
321
+ export async function cliIpcCallStream(
322
+ method: string,
323
+ params?: Record<string, unknown>,
324
+ opts?: { firstByteTimeoutMs?: number; signal?: AbortSignal },
325
+ ): Promise<
326
+ | { ok: true; headers: Record<string, string>; body: ReadableStream<Uint8Array>; abort: () => void }
327
+ | { ok: false; error: string; statusCode?: number; errorCode?: string; errorDetails?: unknown }
328
+ > {
329
+ if (opts?.signal?.aborted) {
330
+ throw opts.signal.reason ?? new DOMException("Aborted", "AbortError");
331
+ }
332
+
333
+ const socketPath = getAssistantSocketPath();
334
+ const firstByteTimeoutMs = opts?.firstByteTimeoutMs ?? DEFAULT_FIRST_BYTE_TIMEOUT_MS;
335
+
336
+ return new Promise((resolve) => {
337
+ let settled = false;
338
+ let firstByteTimer: ReturnType<typeof setTimeout> | undefined;
339
+ let streamController: ReadableStreamDefaultController<Uint8Array> | undefined;
340
+
341
+ const finishError = (
342
+ result: { ok: false; error: string; statusCode?: number; errorCode?: string; errorDetails?: unknown },
343
+ ) => {
344
+ if (settled) return;
345
+ settled = true;
346
+ clearTimeout(connectTimer);
347
+ clearTimeout(firstByteTimer);
348
+ socket.destroy();
349
+ resolve(result);
350
+ };
351
+
352
+ const abort = () => {
353
+ if (settled && streamController) {
354
+ streamController.error(new DOMException("Aborted", "AbortError"));
355
+ streamController = undefined;
356
+ }
357
+ if (!socket.destroyed) {
358
+ writeMessage(socket, {
359
+ id: crypto.randomUUID(),
360
+ method: "$cancel",
361
+ params: { targetId: reqId },
362
+ });
363
+ // Use end() not destroy() so the $cancel frame flushes before the FIN.
364
+ socket.end();
365
+ }
366
+ };
367
+
368
+ const connectTimer = setTimeout(() => {
369
+ log.debug({ method, socketPath, timeoutMs: CONNECT_TIMEOUT_MS }, "CLI IPC stream connect timed out");
370
+ finishError({ ok: false, error: `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.` });
371
+ }, CONNECT_TIMEOUT_MS);
372
+
373
+ const socket = new Socket();
374
+ socket.unref();
375
+
376
+ socket.on("error", (err) => {
377
+ const code = (err as NodeJS.ErrnoException).code;
378
+ log.debug({ err, code, method, socketPath }, "CLI IPC stream socket error");
379
+ if (!settled) {
380
+ finishError({
381
+ ok: false,
382
+ error:
383
+ code === "ENOENT" || code === "ECONNREFUSED"
384
+ ? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
385
+ : `Connection error: ${code ?? err.message}`,
386
+ });
387
+ } else {
388
+ streamController?.error(err);
389
+ streamController = undefined;
390
+ }
391
+ });
392
+
393
+ socket.on("close", (hadError) => {
394
+ if (!settled) {
395
+ finishError({
396
+ ok: false,
397
+ error: hadError
398
+ ? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
399
+ : "Connection closed before response",
400
+ });
401
+ } else if (streamController) {
402
+ streamController.error(new Error("Connection closed before stream ended"));
403
+ streamController = undefined;
404
+ }
405
+ });
406
+
407
+ const reqId = crypto.randomUUID();
408
+
409
+ opts?.signal?.addEventListener("abort", () => { abort(); }, { once: true });
410
+
411
+ const reader = new IpcFrameReader(
412
+ (envelope) => {
413
+ // Non-streaming envelope with error (e.g. method not found, auth failure)
414
+ if (envelope.id !== reqId) return;
415
+ const msg = envelope as IpcResponse;
416
+ finishError({ ok: false, error: msg.error ?? "Unexpected non-streaming response",
417
+ ...(msg.statusCode != null && { statusCode: msg.statusCode }),
418
+ ...(msg.errorCode != null && { errorCode: msg.errorCode }),
419
+ ...(msg.errorDetails != null && { errorDetails: msg.errorDetails }) });
420
+ },
421
+ (err) => finishError({ ok: false, error: err.message }),
422
+ {
423
+ onStreamStart: (envelope) => {
424
+ if (envelope.id !== reqId) return;
425
+ clearTimeout(firstByteTimer);
426
+ const body = new ReadableStream<Uint8Array>({
427
+ start(ctrl) {
428
+ streamController = ctrl;
429
+ },
430
+ cancel() {
431
+ // Consumer cancelled (reader.cancel(), for-await break, pipe abort).
432
+ // Clear the controller reference first so abort() skips the
433
+ // already-closing stream's error() call, then send $cancel.
434
+ streamController = undefined;
435
+ abort();
436
+ },
437
+ });
438
+ settled = true;
439
+ clearTimeout(connectTimer);
440
+ resolve({ ok: true, headers: (envelope.headers ?? {}) as Record<string, string>, body, abort });
441
+ },
442
+ onStreamChunk: (chunk) => {
443
+ streamController?.enqueue(chunk);
444
+ },
445
+ onStreamEnd: () => {
446
+ streamController?.close();
447
+ streamController = undefined;
448
+ socket.destroy();
449
+ },
450
+ },
451
+ );
452
+
453
+ socket.on("connect", () => {
454
+ clearTimeout(connectTimer);
455
+ writeMessage(socket, { id: reqId, method, params });
456
+
457
+ firstByteTimer = setTimeout(() => {
458
+ log.debug({ method, socketPath, timeoutMs: firstByteTimeoutMs }, "CLI IPC stream first-byte timeout");
459
+ finishError({ ok: false, error: "Stream timed out waiting for first byte" });
460
+ }, firstByteTimeoutMs);
461
+
462
+ socket.on("data", (chunk) => {
463
+ reader.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
464
+ });
465
+ });
466
+
467
+ socket.connect(socketPath);
468
+ });
469
+ }
470
+
471
+ // ---------------------------------------------------------------------------
472
+ // Exit helper
473
+ // ---------------------------------------------------------------------------
474
+
475
+ /**
476
+ * Map an IPC error result to a process exit code and terminate.
477
+ *
478
+ * Exit code matrix (DESIGN.md §3.6):
479
+ * 0 — success (not reached via this helper; call process.exit(0) directly)
480
+ * 1 — generic CLI error (fallback for unexpected status codes)
481
+ * 2 — daemon returned 4xx (bad params, not found, unauthorized)
482
+ * 3 — daemon returned 5xx (server-side error)
483
+ * 10 — IPC transport error (can't connect, timeout, closed before response)
484
+ *
485
+ * @example
486
+ * const r = await cliIpcCall<FooResponse>("foo", params);
487
+ * if (!r.ok) return exitFromIpcResult(r, cmd);
488
+ */
489
+ export function exitFromIpcResult(
490
+ r: { ok: boolean; error?: string; statusCode?: number },
491
+ _cmd?: unknown,
492
+ ): never {
493
+ process.stderr.write((r.error ?? "Unknown error") + "\n");
494
+ if (r.statusCode === undefined) {
495
+ process.exit(10);
496
+ } else if (r.statusCode >= 500) {
497
+ process.exit(3);
498
+ } else if (r.statusCode >= 400) {
499
+ process.exit(2);
500
+ } else {
501
+ process.exit(1);
502
+ }
503
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * ⚠️ TEMPORARY HACK — DO NOT EXTEND ⚠️
3
+ *
4
+ * IPC route that lets the gateway execute multiple write statements against
5
+ * the assistant's SQLite database inside a single atomic transaction.
6
+ *
7
+ * Companion to db-proxy.ts. Exists because some gateway-orchestrated writes
8
+ * (e.g. invite redemption: upsert contact channel + record invite use) must
9
+ * be all-or-nothing. With the contacts/guardian/invite tables still living
10
+ * in the assistant DB, the gateway needs a way to commit several writes
11
+ * atomically there.
12
+ *
13
+ * Each step is a write (INSERT/UPDATE/DELETE). All steps run inside a
14
+ * BEGIN IMMEDIATE transaction. If any step throws — including a step whose
15
+ * `requireChanges` constraint is unmet — the entire transaction rolls back.
16
+ *
17
+ * Read-modify-write across steps is not supported (the IPC is one-shot;
18
+ * later steps cannot react to earlier step results except via SQL conditions
19
+ * embedded in the WHERE clause and the optional `requireChanges` guard).
20
+ *
21
+ * This route is intentionally NOT in the shared ROUTES array — it is a
22
+ * private implementation detail between the gateway and assistant IPC
23
+ * servers and must not be discoverable by clients or the OpenAPI spec.
24
+ *
25
+ * Remove once contacts/guardian/invite logic is fully migrated to the
26
+ * gateway's own database.
27
+ */
28
+
29
+ import { getSqlite } from "../../memory/db-connection.js";
30
+ import { RouteError } from "../../runtime/routes/errors.js";
31
+ import { getLogger } from "../../util/logger.js";
32
+
33
+ const log = getLogger("db-proxy-transaction");
34
+
35
+ /** Column value types that SQLite can return. */
36
+ type SqliteValue = string | number | null | Uint8Array;
37
+
38
+ export interface DbProxyTransactionStep {
39
+ /** The SQL write statement to execute. */
40
+ sql: string;
41
+ /** Positional bind parameters. */
42
+ bind?: SqliteValue[];
43
+ /**
44
+ * If set, abort the transaction (rollback) when this step's row-change
45
+ * count is below this threshold. Used for stale-write detection — e.g.
46
+ * "increment use_count only if status = 'active' AND use_count < max_uses",
47
+ * with `requireChanges: 1` to abort when no rows match.
48
+ */
49
+ requireChanges?: number;
50
+ }
51
+
52
+ export interface DbProxyTransactionParams {
53
+ steps: DbProxyTransactionStep[];
54
+ }
55
+
56
+ export type DbProxyTransactionResult =
57
+ | {
58
+ ok: true;
59
+ results: Array<{ changes: number; lastInsertRowid: number }>;
60
+ }
61
+ | {
62
+ ok: false;
63
+ reason: "require_changes_failed";
64
+ failedStep: number;
65
+ actualChanges: number;
66
+ requiredChanges: number;
67
+ };
68
+
69
+ export function handleDbProxyTransaction(
70
+ params: DbProxyTransactionParams,
71
+ ): DbProxyTransactionResult {
72
+ const db = getSqlite();
73
+
74
+ if (!Array.isArray(params.steps) || params.steps.length === 0) {
75
+ throw new RouteError(
76
+ "db_proxy_transaction requires at least one step",
77
+ "INVALID_PARAMS",
78
+ 400,
79
+ );
80
+ }
81
+
82
+ // Sentinel used to abort the transaction without leaking through as a generic
83
+ // SQL error. Better-sqlite3 rolls back when the inner function throws.
84
+ class RequireChangesFailure extends Error {
85
+ constructor(
86
+ public failedStep: number,
87
+ public actualChanges: number,
88
+ public requiredChanges: number,
89
+ ) {
90
+ super(
91
+ `Step ${failedStep} affected ${actualChanges} rows, requires ${requiredChanges}`,
92
+ );
93
+ }
94
+ }
95
+
96
+ const results: Array<{ changes: number; lastInsertRowid: number }> = [];
97
+
98
+ try {
99
+ db.transaction(() => {
100
+ for (let i = 0; i < params.steps.length; i++) {
101
+ const step = params.steps[i];
102
+ const stmt = db.prepare(step.sql);
103
+ const result = step.bind ? stmt.run(...step.bind) : stmt.run();
104
+ const changes = result.changes;
105
+ results.push({
106
+ changes,
107
+ lastInsertRowid: Number(result.lastInsertRowid),
108
+ });
109
+
110
+ if (
111
+ step.requireChanges !== undefined &&
112
+ changes < step.requireChanges
113
+ ) {
114
+ throw new RequireChangesFailure(i, changes, step.requireChanges);
115
+ }
116
+ }
117
+ }).immediate();
118
+ } catch (err) {
119
+ if (err instanceof RequireChangesFailure) {
120
+ log.debug(
121
+ {
122
+ failedStep: err.failedStep,
123
+ actualChanges: err.actualChanges,
124
+ requiredChanges: err.requiredChanges,
125
+ },
126
+ "db-proxy-transaction aborted by requireChanges guard",
127
+ );
128
+ return {
129
+ ok: false,
130
+ reason: "require_changes_failed",
131
+ failedStep: err.failedStep,
132
+ actualChanges: err.actualChanges,
133
+ requiredChanges: err.requiredChanges,
134
+ };
135
+ }
136
+ // Wrap raw SQL/runtime errors in RouteError so the IPC envelope carries
137
+ // a statusCode + errorCode. Without this, the gateway-side strict caller
138
+ // sees a structureless `msg.error` and misclassifies it as a transport
139
+ // failure ("assistant may not be ready"), masking the real SQL error
140
+ // and breaking debuggability + retry decisions.
141
+ const message = err instanceof Error ? err.message : String(err);
142
+ log.warn({ err }, "db-proxy-transaction execution failed");
143
+ throw new RouteError(message, "DB_PROXY_TRANSACTION_FAILED", 500);
144
+ }
145
+
146
+ log.debug(
147
+ { stepCount: params.steps.length },
148
+ "db-proxy-transaction committed",
149
+ );
150
+ return { ok: true, results };
151
+ }
@@ -192,6 +192,66 @@ describe("host.events.publish", () => {
192
192
 
193
193
  client.close();
194
194
  });
195
+
196
+ test.each([
197
+ "host_bash_request",
198
+ "host_bash_cancel",
199
+ "host_file_request",
200
+ "host_file_cancel",
201
+ "host_browser_request",
202
+ "host_browser_cancel",
203
+ "host_cu_request",
204
+ "host_transfer_request",
205
+ "confirmation_request",
206
+ "secret_request",
207
+ ])("rejects blocked event type: %s", async (blockedType) => {
208
+ const client = await openClient();
209
+ const event = {
210
+ id: "evt-blocked",
211
+ emittedAt: new Date().toISOString(),
212
+ message: { type: blockedType },
213
+ };
214
+ client.send({
215
+ id: "req-blocked",
216
+ method: "host.events.publish",
217
+ params: { event },
218
+ });
219
+
220
+ const frame = await client.nextFrame();
221
+ expect("error" in frame && frame.error).toContain("cannot publish");
222
+
223
+ client.close();
224
+ });
225
+
226
+ test("allows non-blocked event types through", async () => {
227
+ const received: AssistantEvent[] = [];
228
+ const subscription = assistantEventHub.subscribe({
229
+ type: "process",
230
+ callback: (evt) => { received.push(evt); },
231
+ });
232
+
233
+ try {
234
+ const client = await openClient();
235
+ const event = {
236
+ id: "evt-ok",
237
+ emittedAt: new Date().toISOString(),
238
+ message: { type: "skill_custom_event", data: "hello" },
239
+ };
240
+ client.send({
241
+ id: "req-ok",
242
+ method: "host.events.publish",
243
+ params: { event },
244
+ });
245
+
246
+ const frame = await client.nextFrame();
247
+ expect("result" in frame && frame.result).toEqual({ published: true });
248
+ expect(received).toHaveLength(1);
249
+
250
+ client.close();
251
+ } finally {
252
+ subscription.dispose();
253
+ }
254
+ });
195
255
  });
196
256
 
197
257
  describe("host.events.buildEvent", () => {