@vellumai/assistant 0.11.7 → 0.11.8-staging.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 (446) hide show
  1. package/ARCHITECTURE.md +10 -10
  2. package/Dockerfile +1 -0
  3. package/docs/architecture/security.md +1 -1
  4. package/docs/error-handling.md +1 -2
  5. package/docs/guardian-request-flow.md +51 -5
  6. package/docs/trusted-contact-access.md +13 -12
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/avatar-catalog/package.json +19 -0
  9. package/{src/avatar/character-components.ts → node_modules/@vellumai/avatar-catalog/src/catalog.ts} +12 -44
  10. package/node_modules/@vellumai/avatar-catalog/src/colors.ts +19 -0
  11. package/node_modules/@vellumai/avatar-catalog/src/index.ts +25 -0
  12. package/node_modules/@vellumai/avatar-catalog/src/types.ts +47 -0
  13. package/node_modules/@vellumai/avatar-catalog/tsconfig.json +20 -0
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
  18. package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +43 -12
  19. package/node_modules/@vellumai/environments/package.json +2 -1
  20. package/node_modules/@vellumai/environments/src/__tests__/test-path-guard.test.ts +89 -0
  21. package/node_modules/@vellumai/environments/src/test-path-guard.ts +113 -0
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  23. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
  24. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  25. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
  26. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +8 -18
  27. package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +46 -0
  28. package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +1 -0
  29. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +46 -0
  30. package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +62 -21
  31. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +14 -0
  32. package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +53 -0
  33. package/node_modules/@vellumai/gateway-client/src/index.ts +11 -5
  34. package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +1 -0
  35. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  36. package/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
  37. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  38. package/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
  39. package/openapi.yaml +481 -9
  40. package/package.json +3 -1
  41. package/scripts/sync-llm-catalog.ts +3 -0
  42. package/src/__tests__/agent-loop-callsite-precedence.test.ts +16 -0
  43. package/src/__tests__/agent-loop-compacted-history-transform.test.ts +304 -0
  44. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +3 -0
  45. package/src/__tests__/agent-wake-override-profile.test.ts +3 -0
  46. package/src/__tests__/anthropic-provider.test.ts +56 -0
  47. package/src/__tests__/approval-cascade.test.ts +0 -13
  48. package/src/__tests__/attachments-store.test.ts +117 -6
  49. package/src/__tests__/attachments.test.ts +122 -0
  50. package/src/__tests__/bundled-asset.test.ts +22 -1
  51. package/src/__tests__/channel-approval.test.ts +14 -7
  52. package/src/__tests__/channel-approvals.test.ts +3 -3
  53. package/src/__tests__/channel-delivery-store.test.ts +58 -0
  54. package/src/__tests__/channel-inbound-disk-pressure.test.ts +1 -0
  55. package/src/__tests__/channel-invite-transport.test.ts +1 -0
  56. package/src/__tests__/channel-readiness-slack-remote.test.ts +3 -3
  57. package/src/__tests__/compaction-events.test.ts +0 -12
  58. package/src/__tests__/compactor-call-site-logging.test.ts +7 -1
  59. package/src/__tests__/compactor-retained-image-validation.test.ts +15 -0
  60. package/src/__tests__/config-schema.test.ts +35 -0
  61. package/src/__tests__/contacts-write.test.ts +141 -3
  62. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -13
  63. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -12
  64. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -13
  65. package/src/__tests__/conversation-agent-loop.test.ts +95 -13
  66. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -13
  67. package/src/__tests__/conversation-load-history-repair.test.ts +418 -0
  68. package/src/__tests__/conversation-media-retry.test.ts +33 -2
  69. package/src/__tests__/conversation-pairing.test.ts +54 -0
  70. package/src/__tests__/conversation-process-callsite.test.ts +0 -12
  71. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -13
  72. package/src/__tests__/conversation-queue.test.ts +4 -13
  73. package/src/__tests__/conversation-retry-route.test.ts +5 -0
  74. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
  75. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
  76. package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
  77. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
  78. package/src/__tests__/conversation-sight-frames.test.ts +615 -0
  79. package/src/__tests__/conversation-slash-queue.test.ts +0 -13
  80. package/src/__tests__/conversation-slash-unknown.test.ts +0 -13
  81. package/src/__tests__/conversation-speed-override.test.ts +0 -12
  82. package/src/__tests__/conversation-store-ephemeral.test.ts +0 -12
  83. package/src/__tests__/conversation-summarize-route.test.ts +5 -0
  84. package/src/__tests__/conversation-summarize-up-to.test.ts +102 -0
  85. package/src/__tests__/conversation-wait-for-idle.test.ts +0 -12
  86. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -5
  87. package/src/__tests__/conversation-workspace-injection.test.ts +0 -17
  88. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -17
  89. package/src/__tests__/credential-health-service.test.ts +45 -0
  90. package/src/__tests__/credential-record-retire.test.ts +256 -0
  91. package/src/__tests__/credential-record-write-through.test.ts +8 -4
  92. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +0 -4
  93. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +0 -4
  94. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +0 -8
  95. package/src/__tests__/delete-propagation.test.ts +121 -3
  96. package/src/__tests__/evict-conversations-for-reload.test.ts +46 -6
  97. package/src/__tests__/file-ops-service.test.ts +8 -3
  98. package/src/__tests__/gemini-provider.test.ts +64 -0
  99. package/src/__tests__/guardian-action-service-scope.test.ts +118 -0
  100. package/src/__tests__/guardian-card-withdrawal.test.ts +299 -5
  101. package/src/__tests__/guardian-expiry-notifier.test.ts +117 -13
  102. package/src/__tests__/guardian-gateway-sim.test.ts +119 -0
  103. package/src/__tests__/guardian-gateway-sim.ts +56 -20
  104. package/src/__tests__/guardian-reply-router-answer-mode.test.ts +6 -0
  105. package/src/__tests__/guardian-request-status-sync.test.ts +162 -0
  106. package/src/__tests__/guardian-wait-controller.test.ts +6 -0
  107. package/src/__tests__/headless-browser-mode.test.ts +27 -0
  108. package/src/__tests__/helpers/mock-conversation.ts +2 -0
  109. package/src/__tests__/list-messages-system-card.test.ts +70 -0
  110. package/src/__tests__/llm-catalog-parity.test.ts +2 -0
  111. package/src/__tests__/managed-profile-guard.test.ts +2 -2
  112. package/src/__tests__/media-resolve-image-validation.test.ts +106 -0
  113. package/src/__tests__/media-stream-server-integration.test.ts +6 -0
  114. package/src/__tests__/non-member-access-request.test.ts +12 -0
  115. package/src/__tests__/notification-discord-adapter.test.ts +76 -4
  116. package/src/__tests__/oauth-commands-routes.test.ts +21 -0
  117. package/src/__tests__/openai-provider.test.ts +63 -0
  118. package/src/__tests__/outbound-slack-persistence.test.ts +114 -2
  119. package/src/__tests__/persist-unsendable-image-downscale.test.ts +63 -1
  120. package/src/__tests__/persist-unsendable-image.test.ts +19 -0
  121. package/src/__tests__/plugin-app-serve-routes.test.ts +2 -2
  122. package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -0
  123. package/src/__tests__/process-message-background-slack.test.ts +1 -1
  124. package/src/__tests__/process-message-display-content.test.ts +1 -1
  125. package/src/__tests__/processing-flag-persist-failure.test.ts +0 -5
  126. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +0 -17
  127. package/src/__tests__/qdrant-manager.test.ts +6 -0
  128. package/src/__tests__/reaction-persistence.test.ts +58 -62
  129. package/src/__tests__/retry-thinking-adaptive-only.test.ts +67 -0
  130. package/src/__tests__/server-history-render.test.ts +22 -0
  131. package/src/__tests__/sight-frame-retention.test.ts +362 -0
  132. package/src/__tests__/slack-reaction-approvals.test.ts +44 -24
  133. package/src/__tests__/slack-reaction-guardian-approval.test.ts +29 -1
  134. package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
  135. package/src/__tests__/subagent-manager-notify.test.ts +2 -0
  136. package/src/__tests__/subagent-notify-parent.test.ts +2 -0
  137. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +85 -0
  138. package/src/__tests__/subagent-tool-gate-mode.test.ts +4 -4
  139. package/src/__tests__/terminal-tools.test.ts +9 -9
  140. package/src/__tests__/tool-result-spool.test.ts +5 -8
  141. package/src/__tests__/usage-attribution.test.ts +26 -0
  142. package/src/acp/__tests__/acp-auth-marker-store.test.ts +191 -0
  143. package/src/acp/__tests__/acp-claude-oauth.test.ts +262 -0
  144. package/src/acp/__tests__/acp-connect-card-state.test.ts +17 -0
  145. package/src/acp/__tests__/helpers/acp-history-db.ts +10 -3
  146. package/src/acp/acp-auth-marker-store.ts +196 -0
  147. package/src/acp/acp-claude-oauth.ts +154 -4
  148. package/src/acp/acp-connect-card-state.ts +49 -8
  149. package/src/acp/agent-process.ts +1 -0
  150. package/src/acp/auto-install.ts +1 -0
  151. package/src/acp/client-handler.ts +1 -0
  152. package/src/acp/prepare-agent-env.ts +79 -1
  153. package/src/acp/resolve-agent.ts +12 -0
  154. package/src/acp/session-manager.test.ts +183 -0
  155. package/src/acp/session-manager.ts +110 -6
  156. package/src/acp/types.ts +17 -0
  157. package/src/agent/attachments.ts +7 -2
  158. package/src/agent/loop.ts +59 -1
  159. package/src/api/events/contact-form-closed.ts +32 -0
  160. package/src/api/events/contact-record-request.ts +63 -0
  161. package/src/api/events/contact-request.ts +10 -3
  162. package/src/api/index.ts +20 -0
  163. package/src/api/responses/conversation-message.ts +20 -0
  164. package/src/api/responses/home.ts +101 -0
  165. package/src/approvals/AGENTS.md +1 -1
  166. package/src/approvals/guardian-card-withdrawal.ts +127 -30
  167. package/src/approvals/guardian-decision-primitive.ts +9 -0
  168. package/src/approvals/guardian-expiry-notifier.ts +22 -16
  169. package/src/approvals/guardian-request-status-sync.ts +98 -0
  170. package/src/avatar/svg-compositor.ts +1 -1
  171. package/src/avatar/traits-png-sync.ts +1 -1
  172. package/src/bundler/app-compiler.ts +1 -0
  173. package/src/bundler/compiler-tools.ts +1 -0
  174. package/src/bundler/package-resolver.ts +1 -0
  175. package/src/calls/__tests__/voice-session-bridge.test.ts +183 -3
  176. package/src/calls/guardian-action-sweep.ts +6 -1
  177. package/src/calls/voice-session-bridge.ts +46 -1
  178. package/src/channels/__tests__/gateway-guardian-requests.test.ts +13 -18
  179. package/src/channels/gateway-guardian-requests.ts +37 -18
  180. package/src/cli/__tests__/contacts-record-prompt-cli.test.ts +329 -0
  181. package/src/cli/commands/__tests__/browser.test.ts +65 -1
  182. package/src/cli/commands/browser.ts +30 -10
  183. package/src/cli/commands/contacts.help.ts +173 -11
  184. package/src/cli/commands/contacts.ts +343 -4
  185. package/src/cli/commands/db/refresh.ts +1 -0
  186. package/src/cli/commands/oauth/index.help.ts +6 -3
  187. package/src/cli/commands/oauth/request.test.ts +37 -0
  188. package/src/cli/commands/oauth/request.ts +7 -6
  189. package/src/cli/lib/bundled-marketplace.json +1 -1
  190. package/src/cli/lib/install-from-github.ts +20 -19
  191. package/src/cli/lib/install-plugin-dependencies.ts +3 -5
  192. package/src/cli/lib/merge-plugin-tree.ts +1 -0
  193. package/src/cli/lib/publish-plugin.ts +4 -1
  194. package/src/config/__tests__/balanced-model-experiment.test.ts +63 -31
  195. package/src/config/__tests__/default-profile-catalog.test.ts +2 -2
  196. package/src/config/balanced-model-experiment.ts +15 -9
  197. package/src/config/bundled-skills/contacts/SKILL.md +61 -18
  198. package/src/config/default-profile-catalog.ts +12 -11
  199. package/src/config/feature-flag-registry.json +22 -13
  200. package/src/config/schema.ts +2 -0
  201. package/src/config/schemas/__tests__/live-voice.test.ts +13 -0
  202. package/src/config/schemas/live-voice.ts +7 -0
  203. package/src/config/schemas/sight.ts +45 -0
  204. package/src/config/skills.ts +2 -1
  205. package/src/contacts/__tests__/mirror-reconciler.test.ts +352 -0
  206. package/src/contacts/contact-store.ts +47 -4
  207. package/src/contacts/contacts-write.ts +5 -2
  208. package/src/contacts/mirror-reconciler.ts +210 -0
  209. package/src/contacts/mirror-tombstones.ts +42 -0
  210. package/src/contacts/notify-contacts-changed.ts +11 -2
  211. package/src/context/compactor.ts +47 -19
  212. package/src/context/tool-result-spool.ts +21 -14
  213. package/src/conversations/message-consolidation.ts +2 -2
  214. package/src/credential-health/credential-health-service.ts +37 -6
  215. package/src/daemon/conversation-agent-loop-handlers.ts +19 -2
  216. package/src/daemon/conversation-agent-loop.ts +100 -1
  217. package/src/daemon/conversation-media-retry.ts +11 -6
  218. package/src/daemon/conversation-messaging.ts +293 -30
  219. package/src/daemon/conversation-runtime-assembly.ts +67 -0
  220. package/src/daemon/conversation-sight-frames.ts +187 -0
  221. package/src/daemon/conversation-store.ts +18 -28
  222. package/src/daemon/conversation-surfaces.ts +12 -2
  223. package/src/daemon/conversation-tool-setup.ts +39 -10
  224. package/src/daemon/conversation.ts +159 -16
  225. package/src/daemon/daemon-control.ts +6 -17
  226. package/src/daemon/handlers/config-model.test.ts +1 -0
  227. package/src/daemon/handlers/conversations.ts +10 -11
  228. package/src/daemon/install-assistant-command.ts +1 -0
  229. package/src/daemon/lifecycle.ts +10 -13
  230. package/src/daemon/message-types/sync.ts +4 -0
  231. package/src/daemon/process-message.ts +2 -17
  232. package/src/daemon/reaction-history-render.test.ts +162 -0
  233. package/src/daemon/reaction-history-render.ts +115 -0
  234. package/src/daemon/reaction-record.test.ts +162 -0
  235. package/src/daemon/reaction-record.ts +83 -0
  236. package/src/daemon/shutdown-handlers.ts +0 -2
  237. package/src/daemon/summarize-boundary.ts +12 -0
  238. package/src/daemon/video-thumbnail.ts +1 -1
  239. package/src/home/feed-types.ts +4 -0
  240. package/src/home/feed-writer.ts +25 -0
  241. package/src/live-voice/__tests__/live-voice-attach-frame.test.ts +679 -0
  242. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +8 -5
  243. package/src/live-voice/__tests__/live-voice-photo.test.ts +637 -1
  244. package/src/live-voice/__tests__/live-voice-sight-frame-inline.test.ts +787 -0
  245. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +347 -0
  246. package/src/live-voice/__tests__/live-voice-vad.test.ts +109 -0
  247. package/src/live-voice/live-voice-photo.ts +420 -65
  248. package/src/live-voice/live-voice-session.ts +311 -27
  249. package/src/live-voice/protocol.ts +147 -11
  250. package/src/messaging/provider-message-metadata.ts +7 -0
  251. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +113 -1
  252. package/src/messaging/providers/channel-transport.ts +37 -0
  253. package/src/messaging/providers/discord/api.ts +4 -4
  254. package/src/messaging/providers/discord/send.test.ts +177 -2
  255. package/src/messaging/providers/discord/send.ts +197 -10
  256. package/src/messaging/providers/discord/transport.ts +10 -0
  257. package/src/messaging/providers/index.ts +42 -0
  258. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +129 -3
  259. package/src/messaging/providers/slack/__tests__/auth.test.ts +35 -0
  260. package/src/messaging/providers/slack/__tests__/message-content.test.ts +152 -0
  261. package/src/messaging/providers/slack/adapter.ts +112 -27
  262. package/src/messaging/providers/slack/api.test.ts +10 -0
  263. package/src/messaging/providers/slack/auth.ts +32 -6
  264. package/src/messaging/providers/slack/client.ts +0 -15
  265. package/src/messaging/providers/slack/message-content.ts +137 -0
  266. package/src/messaging/providers/slack/send.test.ts +43 -2
  267. package/src/messaging/providers/slack/send.ts +18 -5
  268. package/src/messaging/providers/slack/transport.ts +10 -0
  269. package/src/messaging/providers/slack/types.ts +21 -2
  270. package/src/messaging/providers/telegram-bot/adapter.ts +5 -1
  271. package/src/messaging/providers/telegram-bot/send.test.ts +49 -2
  272. package/src/messaging/providers/telegram-bot/send.ts +41 -1
  273. package/src/messaging/providers/telegram-bot/transport.ts +10 -0
  274. package/src/messaging/providers/telegram-bot/withdraw.ts +13 -5
  275. package/src/messaging/reaction-envelopes.ts +79 -0
  276. package/src/monitoring/control.ts +2 -2
  277. package/src/monitoring/page-cache.ts +5 -1
  278. package/src/notifications/AGENTS.md +1 -1
  279. package/src/notifications/__tests__/guardian-feed-projection.test.ts +360 -0
  280. package/src/notifications/__tests__/home-feed-side-effect.test.ts +8 -0
  281. package/src/notifications/adapters/discord.ts +62 -9
  282. package/src/notifications/conversation-pairing.ts +31 -4
  283. package/src/notifications/emit-signal.ts +8 -5
  284. package/src/notifications/guardian-delivery-recorder.ts +47 -6
  285. package/src/notifications/guardian-feed-projection.ts +492 -0
  286. package/src/notifications/home-feed-side-effect.ts +59 -11
  287. package/src/oauth/byo-connection.test.ts +22 -0
  288. package/src/oauth/byo-connection.ts +11 -5
  289. package/src/oauth/connection.ts +3 -2
  290. package/src/oauth/platform-connection.test.ts +35 -0
  291. package/src/oauth/platform-connection.ts +18 -13
  292. package/src/oauth/scope-utils.ts +48 -0
  293. package/src/oauth/seed-providers.ts +12 -0
  294. package/src/outbound-proxy/certs.ts +5 -4
  295. package/src/permissions/question-guardian-request.test.ts +7 -1
  296. package/src/persistence/attachments-store.ts +134 -31
  297. package/src/persistence/conversation-crud.ts +137 -4
  298. package/src/persistence/conversation-types.ts +109 -0
  299. package/src/persistence/db-connection.ts +3 -21
  300. package/src/persistence/delivery-crud.ts +42 -12
  301. package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +15 -2
  302. package/src/persistence/embeddings/embedding-local.ts +7 -112
  303. package/src/persistence/embeddings/qdrant-manager.ts +4 -1
  304. package/src/persistence/llm-request-log-store.ts +1 -1
  305. package/src/persistence/migrations/370-acp-session-history-auth-error-code.ts +36 -0
  306. package/src/persistence/migrations/371-acp-session-history-auth-error-credential.ts +40 -0
  307. package/src/persistence/migrations/372-create-acp-refused-credentials.ts +34 -0
  308. package/src/persistence/migrations/373-acp-auth-marker-index.ts +27 -0
  309. package/src/persistence/migrations/__tests__/370-acp-session-history-auth-error-code.test.ts +65 -0
  310. package/src/persistence/migrations/__tests__/371-acp-session-history-auth-error-credential.test.ts +68 -0
  311. package/src/persistence/migrations/__tests__/372-create-acp-refused-credentials.test.ts +64 -0
  312. package/src/persistence/migrations/__tests__/373-acp-auth-marker-index.test.ts +82 -0
  313. package/src/persistence/schema/acp.ts +25 -0
  314. package/src/persistence/steps.ts +8 -0
  315. package/src/platform/platform-patch-queue.test.ts +43 -41
  316. package/src/plugin-api/index.ts +12 -0
  317. package/src/plugins/defaults/compaction/window-manager.ts +2 -0
  318. package/src/plugins/defaults/image-recovery/recover.ts +12 -1
  319. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +166 -1
  320. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +3 -0
  321. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +3 -0
  322. package/src/plugins/defaults/memory/jobs-worker.ts +1 -2
  323. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +53 -9
  324. package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +4 -7
  325. package/src/plugins/defaults/memory/src/memory-worker-routes.ts +1 -1
  326. package/src/plugins/defaults/memory/v1/job-handlers/index-maintenance.ts +15 -1
  327. package/src/plugins/defaults/memory/worker-control.ts +1 -2
  328. package/src/providers/content-block-schema.ts +1 -0
  329. package/src/providers/fireworks/client.ts +9 -1
  330. package/src/providers/gemini/client.ts +22 -15
  331. package/src/providers/media-resolve.ts +24 -10
  332. package/src/providers/model-catalog.ts +91 -0
  333. package/src/providers/openai/chat-completions-provider.ts +44 -5
  334. package/src/providers/retry.ts +16 -11
  335. package/src/providers/types.ts +56 -0
  336. package/src/routes/__tests__/control.test.ts +4 -3
  337. package/src/routes/control.ts +1 -1
  338. package/src/routes/route-host-client.ts +1 -1
  339. package/src/runtime/AGENTS.md +30 -12
  340. package/src/runtime/__tests__/agent-wake.test.ts +71 -6
  341. package/src/runtime/__tests__/guardian-form-registry.test.ts +242 -0
  342. package/src/runtime/access-request-helper.ts +15 -7
  343. package/src/runtime/agent-wake.ts +16 -4
  344. package/src/runtime/channel-approval-types.ts +8 -5
  345. package/src/runtime/channel-invite-transport.ts +2 -0
  346. package/src/runtime/channel-invite-transports/discord.ts +27 -0
  347. package/src/runtime/channel-readiness-service.ts +4 -4
  348. package/src/runtime/channel-reply-delivery.ts +72 -28
  349. package/src/runtime/guardian-action-service.ts +9 -5
  350. package/src/runtime/guardian-form-registry.ts +256 -0
  351. package/src/runtime/guardian-reply-router.ts +33 -14
  352. package/src/runtime/invite-instruction-generator.ts +14 -2
  353. package/src/runtime/migrations/vbundle-metadata-merge.ts +4 -7
  354. package/src/runtime/no-response.ts +13 -74
  355. package/src/runtime/routes/__tests__/acp-routes.test.ts +527 -0
  356. package/src/runtime/routes/__tests__/app-management-shared-apps.test.ts +74 -0
  357. package/src/runtime/routes/__tests__/contact-form-kinds.test.ts +22 -0
  358. package/src/runtime/routes/__tests__/contact-record-prompt-route.test.ts +303 -0
  359. package/src/runtime/routes/__tests__/monitoring-routes.test.ts +3 -4
  360. package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +6 -0
  361. package/src/runtime/routes/__tests__/route-host-worker-routes.test.ts +3 -4
  362. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +5 -6
  363. package/src/runtime/routes/acp-routes-list.test.ts +58 -0
  364. package/src/runtime/routes/acp-routes.ts +253 -47
  365. package/src/runtime/routes/app-management-routes.ts +80 -47
  366. package/src/runtime/routes/approval-routes.ts +9 -0
  367. package/src/runtime/routes/archive-utils.ts +6 -1
  368. package/src/runtime/routes/avatar-routes.ts +1 -1
  369. package/src/runtime/routes/channel-route-shared.ts +18 -20
  370. package/src/runtime/routes/contact-prompt-routes.ts +271 -105
  371. package/src/runtime/routes/conversation-routes.ts +41 -32
  372. package/src/runtime/routes/credential-prompt-routes.ts +10 -1
  373. package/src/runtime/routes/debug-bash-routes.ts +1 -0
  374. package/src/runtime/routes/guardian-expiry-sweep.ts +89 -31
  375. package/src/runtime/routes/guardian-form-routes.ts +115 -0
  376. package/src/runtime/routes/inbound-message-handler.ts +82 -29
  377. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -3
  378. package/src/runtime/routes/inbound-stages/edit-intercept.ts +6 -0
  379. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +6 -0
  380. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +6 -1
  381. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +50 -17
  382. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +97 -94
  383. package/src/runtime/routes/index.ts +2 -0
  384. package/src/runtime/routes/integrations/slack/channel.ts +39 -18
  385. package/src/runtime/routes/migration-routes.ts +1 -3
  386. package/src/runtime/routes/monitoring-routes.ts +0 -3
  387. package/src/runtime/routes/oauth-commands-routes.ts +18 -5
  388. package/src/runtime/routes/route-host-worker-routes.ts +1 -1
  389. package/src/runtime/routes/schedule-worker-routes.ts +1 -1
  390. package/src/runtime/tool-grant-request-helper.ts +4 -0
  391. package/src/schedule/run-script.ts +1 -0
  392. package/src/schedule/scheduler.ts +1 -1
  393. package/src/schedule/worker-control.ts +2 -2
  394. package/src/security/oauth2.ts +2 -11
  395. package/src/security/secure-keys.ts +55 -8
  396. package/src/skills/catalog-install.ts +7 -4
  397. package/src/skills/clawhub.ts +1 -0
  398. package/src/skills/inline-command-runner.ts +1 -0
  399. package/src/stt/__tests__/room-noise-floor.test.ts +108 -0
  400. package/src/stt/room-noise-floor.ts +140 -0
  401. package/src/subagent/notify.ts +49 -3
  402. package/src/tools/__tests__/tool-input-schemas.test.ts +2 -0
  403. package/src/tools/acp/spawn.ts +2 -2
  404. package/src/tools/browser/__tests__/browser-status.test.ts +9 -0
  405. package/src/tools/browser/browser-execution.ts +10 -43
  406. package/src/tools/browser/browser-manager.ts +1 -0
  407. package/src/tools/browser/browser-status-constants.ts +6 -5
  408. package/src/tools/browser/runtime-check.ts +2 -0
  409. package/src/tools/channel/react-to-message.test.ts +193 -0
  410. package/src/tools/channel/react-to-message.ts +159 -0
  411. package/src/tools/credentials/metadata-store.ts +115 -19
  412. package/src/tools/credentials/store.ts +0 -2
  413. package/src/tools/filesystem/read.ts +2 -2
  414. package/src/tools/host-filesystem/read.ts +2 -2
  415. package/src/tools/host-terminal/host-shell.test.ts +24 -0
  416. package/src/tools/host-terminal/host-shell.ts +11 -38
  417. package/src/tools/shared/filesystem/file-ops-service.ts +10 -7
  418. package/src/tools/shared/shell-output.test.ts +104 -0
  419. package/src/tools/shared/shell-output.ts +112 -39
  420. package/src/tools/skills/sandbox-runner.ts +1 -0
  421. package/src/tools/system/request-permission.test.ts +34 -0
  422. package/src/tools/system/request-permission.ts +29 -5
  423. package/src/tools/terminal/shell.test.ts +63 -0
  424. package/src/tools/terminal/shell.ts +12 -44
  425. package/src/tools/tool-input-schemas.ts +2 -0
  426. package/src/tools/tool-manifest.ts +2 -0
  427. package/src/usage/attribution.ts +12 -4
  428. package/src/util/__tests__/worker-ownership.test.ts +77 -0
  429. package/src/util/__tests__/worker-process-command.test.ts +246 -1
  430. package/src/util/bun-runtime.ts +44 -13
  431. package/src/util/bundled-asset.ts +9 -1
  432. package/src/util/disk-usage.ts +1 -0
  433. package/src/util/file-use.ts +4 -1
  434. package/src/util/guardian-form-timeouts.ts +50 -0
  435. package/src/util/image-conversion.ts +1 -0
  436. package/src/util/oauth-request-body.test.ts +59 -0
  437. package/src/util/oauth-request-body.ts +56 -5
  438. package/src/util/platform.ts +69 -73
  439. package/src/util/process-table.ts +16 -5
  440. package/src/util/read-stdin.ts +5 -0
  441. package/src/util/spawn.ts +1 -0
  442. package/src/util/sqlite3-runtime.ts +25 -5
  443. package/src/util/worker-ownership.ts +193 -0
  444. package/src/util/worker-process.ts +375 -37
  445. package/src/workspace/git-service.ts +4 -12
  446. package/src/workspace/migrations/010-app-dir-rename.ts +1 -0
@@ -9,6 +9,7 @@
9
9
  import type { Button, KnownBlock } from "@slack/types";
10
10
  import type {
11
11
  ApprovalUIMetadata,
12
+ ChannelDeliveryResult,
12
13
  MessageAudience,
13
14
  StreamOp,
14
15
  } from "@vellumai/gateway-client";
@@ -382,30 +383,42 @@ function imageBlocksFor(text: string): KnownBlock[] | undefined {
382
383
 
383
384
  /**
384
385
  * Add or remove an emoji reaction on a Slack message.
385
- * Non-throwing: logs errors but returns silently.
386
+ *
387
+ * Non-throwing: failures are logged and reported through the result so a
388
+ * tool-driven react can tell the model the truth, while the activity
389
+ * fallback is free to ignore it. Colons are stripped from the name because
390
+ * `reactions.add` takes the bare emoji name, and `already_reacted` /
391
+ * `no_reaction` count as success: the requested end state already holds.
386
392
  */
387
393
  export async function sendSlackReaction(
388
394
  channel: string,
389
395
  name: string,
390
396
  messageTs: string,
391
397
  action: "add" | "remove",
392
- ): Promise<void> {
398
+ ): Promise<ChannelDeliveryResult> {
393
399
  const method = action === "add" ? "reactions.add" : "reactions.remove";
400
+ const bareName = name.replace(/^:+|:+$/g, "");
394
401
  try {
395
- await callSlackApi(method, { channel, name, timestamp: messageTs });
402
+ await callSlackApi(method, {
403
+ channel,
404
+ name: bareName,
405
+ timestamp: messageTs,
406
+ });
407
+ return { ok: true };
396
408
  } catch (err) {
397
409
  if (err instanceof SlackApiError) {
398
410
  if (
399
411
  err.slackError === "already_reacted" ||
400
412
  err.slackError === "no_reaction"
401
413
  ) {
402
- return;
414
+ return { ok: true };
403
415
  }
404
416
  }
405
417
  log.warn(
406
- { err, channel, method, name },
418
+ { err, channel, method, name: bareName },
407
419
  "Failed to deliver Slack reaction",
408
420
  );
421
+ return { ok: false };
409
422
  }
410
423
  }
411
424
 
@@ -6,6 +6,7 @@ import type { ChannelTransport } from "../channel-transport.js";
6
6
  import {
7
7
  sendSlackAgentSessionStatus,
8
8
  sendSlackAttachments,
9
+ sendSlackReaction,
9
10
  sendSlackReply,
10
11
  sendSlackStreamOp,
11
12
  updateSlackMessage,
@@ -21,6 +22,15 @@ function mutedBlocks(text: string): KnownBlock[] {
21
22
  export const slackTransport: ChannelTransport = {
22
23
  channel: "slack",
23
24
 
25
+ async react(target) {
26
+ return sendSlackReaction(
27
+ target.chatId,
28
+ target.emoji,
29
+ target.messageId,
30
+ target.action,
31
+ );
32
+ },
33
+
24
34
  async deliver(ctx, payload) {
25
35
  const { chatId, text, attachments } = payload;
26
36
  const threadTs = ctx.params.threadTs;
@@ -1,5 +1,11 @@
1
1
  /** Slack Web API response types. */
2
2
 
3
+ import type {
4
+ AnyBlock,
5
+ GenericMessageEvent,
6
+ MessageAttachment,
7
+ } from "@slack/types";
8
+
3
9
  export interface SlackApiResponse {
4
10
  ok: boolean;
5
11
  error?: string;
@@ -61,7 +67,22 @@ export interface SlackMessage {
61
67
  ts: string;
62
68
  user?: string;
63
69
  bot_id?: string;
70
+ /**
71
+ * Display name for `bot_message`-subtype rows (incoming webhooks and
72
+ * legacy bot posts, which carry no `user` to resolve a name from).
73
+ */
74
+ username?: string;
75
+ /** Profile of the posting app, attached to bot-authored rows. The type is
76
+ * indexed off the official message event because `@slack/types` does not
77
+ * export `BotProfile` from its root. */
78
+ bot_profile?: GenericMessageEvent["bot_profile"];
64
79
  text: string;
80
+ /**
81
+ * Bot/webhook posts routinely leave `text` empty and put the visible
82
+ * content here or in `blocks`; see `slackMessageRawText`.
83
+ */
84
+ attachments?: MessageAttachment[];
85
+ blocks?: AnyBlock[];
65
86
  thread_ts?: string;
66
87
  reply_count?: number;
67
88
  reactions?: Array<{ name: string; count: number; users: string[] }>;
@@ -144,8 +165,6 @@ export interface SlackConversationsOpenResponse extends SlackApiResponse {
144
165
 
145
166
  export type SlackConversationMarkResponse = SlackApiResponse;
146
167
 
147
- export type SlackReactionsAddResponse = SlackApiResponse;
148
-
149
168
  export interface SlackUsersListResponse extends SlackApiResponse {
150
169
  members: SlackUser[];
151
170
  response_metadata?: { next_cursor?: string };
@@ -7,6 +7,7 @@
7
7
  import type { OAuthConnection } from "../../../oauth/connection.js";
8
8
  import { getConnectionByProvider } from "../../../oauth/oauth-store.js";
9
9
  import { getOrCreateConversation } from "../../../persistence/conversation-key-store.js";
10
+ import { buildScopedConversationKey } from "../../../persistence/delivery-crud.js";
10
11
  import { upsertOutboundBinding } from "../../../persistence/external-conversation-store.js";
11
12
  import { credentialKey } from "../../../security/credential-key.js";
12
13
  import { getSecureKeyAsync } from "../../../security/secure-keys.js";
@@ -107,7 +108,10 @@ export const telegramBotMessagingProvider: MessagingProvider = {
107
108
  // conversation key mapping and binding exist for the next inbound.
108
109
  try {
109
110
  const sourceChannel = "telegram";
110
- const conversationKey = `asst:self:${sourceChannel}:${conversationId}`;
111
+ const conversationKey = buildScopedConversationKey(
112
+ sourceChannel,
113
+ conversationId,
114
+ );
111
115
  const { conversationId: internalId } =
112
116
  getOrCreateConversation(conversationKey);
113
117
  upsertOutboundBinding({
@@ -30,8 +30,12 @@ mock.module("./api.js", () => ({
30
30
  }));
31
31
 
32
32
  const { TelegramNonRetryableError } = await import("./api.js");
33
- const { editTelegramMessage, sendTelegramReply, sendTelegramRichReply } =
34
- await import("./send.js");
33
+ const {
34
+ editTelegramMessage,
35
+ sendTelegramReaction,
36
+ sendTelegramReply,
37
+ sendTelegramRichReply,
38
+ } = await import("./send.js");
35
39
  const { telegramTransport } = await import("./transport.js");
36
40
 
37
41
  const approval: ApprovalUIMetadata = {
@@ -323,6 +327,49 @@ describe("telegramTransport topic targeting", () => {
323
327
  });
324
328
  });
325
329
 
330
+ describe("sendTelegramReaction", () => {
331
+ test("add sends setMessageReaction with a single emoji entry", async () => {
332
+ const result = await sendTelegramReaction("12345", "👍", "678", "add");
333
+ expect(result).toEqual({ ok: true });
334
+ const calls = callsTo("setMessageReaction");
335
+ expect(calls).toHaveLength(1);
336
+ expect(calls[0][1]).toEqual({
337
+ chat_id: "12345",
338
+ message_id: 678,
339
+ reaction: [{ type: "emoji", emoji: "👍" }],
340
+ });
341
+ });
342
+
343
+ test("remove clears the bot's reaction with the empty list", async () => {
344
+ await sendTelegramReaction("12345", "👍", "678", "remove");
345
+ const calls = callsTo("setMessageReaction");
346
+ expect(calls[0][1]).toEqual({
347
+ chat_id: "12345",
348
+ message_id: 678,
349
+ reaction: [],
350
+ });
351
+ });
352
+
353
+ test("a non-numeric message id reports ok false without calling the API", async () => {
354
+ const result = await sendTelegramReaction(
355
+ "12345",
356
+ "👍",
357
+ "not-an-id",
358
+ "add",
359
+ );
360
+ expect(result).toEqual({ ok: false });
361
+ expect(callsTo("setMessageReaction")).toHaveLength(0);
362
+ });
363
+
364
+ test("an API rejection reports ok false without throwing", async () => {
365
+ callTelegramBotApiMock.mockImplementationOnce(async () => {
366
+ throw new TelegramNonRetryableError("400", "REACTION_INVALID");
367
+ });
368
+ const result = await sendTelegramReaction("12345", "👍", "678", "add");
369
+ expect(result).toEqual({ ok: false });
370
+ });
371
+ });
372
+
326
373
  describe("editTelegramMessage", () => {
327
374
  const ctx = { callbackUrl: "http://gw/deliver/telegram", params: {} };
328
375
 
@@ -5,7 +5,10 @@
5
5
  * and typing indicators by calling the Telegram Bot API directly via ./api.ts.
6
6
  */
7
7
 
8
- import type { ApprovalUIMetadata } from "@vellumai/gateway-client";
8
+ import type {
9
+ ApprovalUIMetadata,
10
+ ChannelDeliveryResult,
11
+ } from "@vellumai/gateway-client";
9
12
 
10
13
  import { getAttachmentContent } from "../../../persistence/attachments-store.js";
11
14
  import type { RuntimeAttachmentMetadata } from "../../../runtime/http-types.js";
@@ -388,6 +391,43 @@ export async function sendTelegramAttachments(
388
391
  * Send a typing indicator ("chat action") to a Telegram chat.
389
392
  * Returns true on success, false on failure (non-throwing).
390
393
  */
394
+ /**
395
+ * Set or clear the bot's emoji reaction on a message.
396
+ *
397
+ * `setMessageReaction` semantics (Bot API): a bot holds at most one
398
+ * reaction per message, so `add` replaces any prior one and `remove`
399
+ * clears it by sending the empty reaction list. `emoji` is the unicode
400
+ * emoji itself, and Telegram accepts only its allowed set (the standard
401
+ * reaction emoji, narrowed further by a chat's allowed-reactions
402
+ * setting); a rejected emoji reports `ok: false` rather than throwing.
403
+ */
404
+ export async function sendTelegramReaction(
405
+ chatId: string,
406
+ emoji: string,
407
+ messageId: string,
408
+ action: "add" | "remove",
409
+ ): Promise<ChannelDeliveryResult> {
410
+ const messageIdNum = Number.parseInt(messageId, 10);
411
+ if (!Number.isFinite(messageIdNum)) {
412
+ log.warn({ chatId, messageId }, "Non-numeric Telegram reaction target");
413
+ return { ok: false };
414
+ }
415
+ try {
416
+ await callTelegramBotApi("setMessageReaction", {
417
+ chat_id: chatId,
418
+ message_id: messageIdNum,
419
+ reaction: action === "add" ? [{ type: "emoji", emoji }] : [],
420
+ });
421
+ return { ok: true };
422
+ } catch (err) {
423
+ log.warn(
424
+ { err, chatId, messageId, action },
425
+ "Failed to deliver Telegram reaction",
426
+ );
427
+ return { ok: false };
428
+ }
429
+ }
430
+
391
431
  export async function sendTelegramTypingIndicator(
392
432
  chatId: string,
393
433
  opts?: TelegramSendOptions,
@@ -10,6 +10,7 @@ import type { TelegramSendOptions } from "./send.js";
10
10
  import {
11
11
  editTelegramMessage,
12
12
  sendTelegramAttachments,
13
+ sendTelegramReaction,
13
14
  sendTelegramReply,
14
15
  sendTelegramRichReply,
15
16
  sendTelegramTypingIndicator,
@@ -32,6 +33,15 @@ export const telegramTransport: ChannelTransport = {
32
33
  // Telegram clears a chat action after about five seconds.
33
34
  activityRefreshMs: 4_000,
34
35
 
36
+ async react(target) {
37
+ return sendTelegramReaction(
38
+ target.chatId,
39
+ target.emoji,
40
+ target.messageId,
41
+ target.action,
42
+ );
43
+ },
44
+
35
45
  async deliver(ctx, payload) {
36
46
  const { chatId, text, attachments, approval } = payload;
37
47
  const opts = threadOptions(ctx);
@@ -132,24 +132,31 @@ async function clearInlineKeyboard(
132
132
  *
133
133
  * Both steps are attempted independently, so a failed keyboard edit does not
134
134
  * lose the durable outcome notice. Throws only if the status reply send
135
- * fails, which the caller treats as non-fatal.
135
+ * fails, which decision-path callers treat as non-fatal. `complete` reports
136
+ * whether the keyboard is durably gone: false keeps the expiry sweep's
137
+ * receipt held back so the still-actionable card is retried. An invalid
138
+ * recorded message id reports complete, because no retry can ever address
139
+ * it; a tap on its leftover buttons gets the stale-request handling.
136
140
  */
137
141
  export async function withdrawTelegramApprovalCard(
138
142
  params: WithdrawTelegramApprovalCardParams,
139
- ): Promise<void> {
143
+ ): Promise<{ complete: boolean }> {
140
144
  const parsedMessageId = Number(params.messageId);
141
145
  if (!Number.isFinite(parsedMessageId)) {
142
146
  log.warn(
143
147
  { chatId: params.chatId, messageId: params.messageId },
144
148
  "Skipping Telegram card withdrawal due to invalid message id",
145
149
  );
146
- return;
150
+ return { complete: true };
147
151
  }
148
152
 
149
- await clearInlineKeyboard(params.chatId, parsedMessageId);
153
+ const keyboardCleared = await clearInlineKeyboard(
154
+ params.chatId,
155
+ parsedMessageId,
156
+ );
150
157
 
151
158
  if (!params.postStatusReply) {
152
- return;
159
+ return { complete: keyboardCleared };
153
160
  }
154
161
 
155
162
  // Silent (no push) reply quoting the card. `allow_sending_without_reply`
@@ -163,4 +170,5 @@ export async function withdrawTelegramApprovalCard(
163
170
  },
164
171
  disable_notification: true,
165
172
  });
173
+ return { complete: keyboardCleared };
166
174
  }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The two persisted envelope shapes of a reaction row, built from one set of
3
+ * facts so the inbound intercept and the assistant's own reaction records
4
+ * cannot drift apart.
5
+ *
6
+ * Slack keeps its own envelope because its transcript context builds
7
+ * provider history from rows and reads only `slackMeta`; every other channel
8
+ * writes the neutral shape `readProviderMetadata` serves to channel-agnostic
9
+ * readers.
10
+ */
11
+ import type { ChannelId } from "../channels/types.js";
12
+ import type { ProviderMessageMetadata } from "./provider-message-metadata.js";
13
+ import type { SlackMessageMetadata } from "./providers/slack/message-metadata.js";
14
+
15
+ export interface ReactionEnvelopeFacts {
16
+ channel: ChannelId;
17
+ /** Provider id of the chat the reaction belongs to. */
18
+ chatId: string;
19
+ /** Target message id, in the channel's own id space. */
20
+ targetMessageId: string;
21
+ emoji: string;
22
+ op: "added" | "removed";
23
+ /** Absent on the assistant's own reactions. */
24
+ actorExternalId?: string;
25
+ /** Absent on the assistant's own reactions. */
26
+ actorDisplayName?: string;
27
+ }
28
+
29
+ export function buildNeutralReactionMeta(
30
+ facts: ReactionEnvelopeFacts,
31
+ ): ProviderMessageMetadata {
32
+ return {
33
+ source: facts.channel,
34
+ conversationExternalId: facts.chatId,
35
+ eventKind: "reaction",
36
+ ...(facts.actorExternalId
37
+ ? { actorExternalId: facts.actorExternalId }
38
+ : {}),
39
+ ...(facts.actorDisplayName ? { displayName: facts.actorDisplayName } : {}),
40
+ reaction: {
41
+ targetMessageId: facts.targetMessageId,
42
+ emoji: facts.emoji,
43
+ op: facts.op,
44
+ ...(facts.actorDisplayName
45
+ ? { actorDisplayName: facts.actorDisplayName }
46
+ : {}),
47
+ },
48
+ };
49
+ }
50
+
51
+ export function buildSlackReactionMeta(
52
+ facts: ReactionEnvelopeFacts,
53
+ ): SlackMessageMetadata {
54
+ return {
55
+ source: "slack",
56
+ channelId: facts.chatId,
57
+ // A reaction row stores the reacted message's ts in `channelTs`, which
58
+ // is its target rather than an id of its own.
59
+ channelTs: facts.targetMessageId,
60
+ eventKind: "reaction",
61
+ // No `threadTs`: Slack sends none on a reaction, so any thread id in
62
+ // scope is the reacted message's own ts. It equals `channelTs`, which
63
+ // every reader treats as "not in a thread" anyway, and storing it makes
64
+ // the row false evidence that a thread belongs to this conversation
65
+ // (`legacySlackConversationHasThreadEvidence`).
66
+ ...(facts.actorExternalId
67
+ ? { actorExternalUserId: facts.actorExternalId }
68
+ : {}),
69
+ ...(facts.actorDisplayName ? { displayName: facts.actorDisplayName } : {}),
70
+ reaction: {
71
+ emoji: facts.emoji,
72
+ targetChannelTs: facts.targetMessageId,
73
+ op: facts.op,
74
+ ...(facts.actorDisplayName
75
+ ? { actorDisplayName: facts.actorDisplayName }
76
+ : {}),
77
+ },
78
+ };
79
+ }
@@ -68,13 +68,13 @@ export function stopMonitoringWorkerProcess(): WorkerProcessStatus {
68
68
 
69
69
  /**
70
70
  * Daemon-lifecycle entry point: spawn the monitor as a child of the daemon
71
- * (`detached: false`, so it appears in `assistant ps` and is torn down on
71
+ * (so it appears in the daemon's process tree and is torn down on
72
72
  * shutdown). Runs on every boot — the monitor is platform infrastructure,
73
73
  * not an opt-in feature. Fire-and-forget — a monitor failure must never
74
74
  * block boot.
75
75
  */
76
76
  export function startMonitoring(): void {
77
- void spawnMonitoringWorkerProcess({ detached: false })
77
+ void spawnMonitoringWorkerProcess()
78
78
  .then((r) =>
79
79
  log.info(
80
80
  { pid: r.pid, alreadyRunning: r.alreadyRunning },
@@ -143,7 +143,11 @@ export async function readFileResidency(
143
143
  const { stdout } = await execFileAsync(
144
144
  "fincore",
145
145
  ["--bytes", "--json", ...paths],
146
- { timeout: 10_000, maxBuffer: 4 * 1024 * 1024 },
146
+ {
147
+ timeout: 10_000,
148
+ maxBuffer: 4 * 1024 * 1024,
149
+ windowsHide: true,
150
+ },
147
151
  );
148
152
  return parseFincoreJson(stdout);
149
153
  } catch {
@@ -10,4 +10,4 @@ Guardian-request producers (access requests, tool approvals, tool-grant escalati
10
10
 
11
11
  Approval-card **source references** (the link back to the channel message that triggered a request) resolve only through `resolveApprovalSourceReference()` in `runtime/approval-source-link.ts` -- producers spread the result into the `guardian.question` context payload and never hand-build links. Channel-format knowledge (id shapes, permalinks, mrkdwn) lives only in `messaging/providers/<channel>/` and `notifications/adapters/<channel>`; the four-layer ownership map is documented at the top of `approval-source-link.ts`. Exception: access-request cards predate the registry and still derive their Slack permalink from payload `messageTs` in `access-request-copy.ts` -- converge them onto the registry rather than adding a third resolution path.
12
12
 
13
- Guardian-request card rows are **not conversation history**. `pairDeliveryWithConversation` persists a message row per delivery, and for a guardian card that row is addressed to a conversation the request is _about_ (the vellum card is pinned to the originating conversation; a channel card lands in whatever conversation the guardian's chat binds to). `isGuardianCardRow` in `approval-card-data.ts` is the single definition of which rows those are, derived from the card's own `ui_surface` id rather than a stored marker so old rows need no backfill. **Both** history assemblers must consult it -- `Conversation.loadFromDb` and `loadSlackChronologicalContext`, which re-reads rows rather than using `this.messages` -- or the unfiltered one replays the card between a parked turn's `tool_use` and its `tool_result` and history repair destroys the real result. Surface state is exempt on purpose: the card's buttons must still route after a restart. Full rationale in [docs/guardian-request-flow.md](../../docs/guardian-request-flow.md).
13
+ Guardian-request card rows are **not conversation history**. Only the vellum delivery persists a message row (`pairDeliveryWithConversation` pins it to the conversation the request is _about_, via `buildVellumCardAffinity`); channel guardian cards are delivery projections and pair no conversation at all, with the gateway delivery row (chat id + channel-native message id) as their only persisted envelope. `isGuardianCardRow` in `approval-card-data.ts` is the single definition of which rows are guardian cards (including rows channel deliveries paired before the projection-only policy), derived from the card's own `ui_surface` id rather than a stored marker so old rows need no backfill. **Both** history assemblers must consult it -- `Conversation.loadFromDb` and `loadSlackChronologicalContext`, which re-reads rows rather than using `this.messages` -- or the unfiltered one replays the card between a parked turn's `tool_use` and its `tool_result` and history repair destroys the real result. Surface state is exempt on purpose: the card's buttons must still route after a restart. Full rationale in [docs/guardian-request-flow.md](../../docs/guardian-request-flow.md).