@vellumai/assistant 0.10.0 → 0.10.1-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 (824) hide show
  1. package/ARCHITECTURE.md +36 -37
  2. package/bun.lock +3 -0
  3. package/docs/workflows.md +12 -7
  4. package/eslint-rules/cli-no-daemon-internals.js +6 -0
  5. package/node_modules/@slack/types/LICENSE +23 -0
  6. package/node_modules/@slack/types/README.md +32 -0
  7. package/node_modules/@slack/types/dist/block-kit/block-elements.d.ts +953 -0
  8. package/node_modules/@slack/types/dist/block-kit/block-elements.d.ts.map +1 -0
  9. package/node_modules/@slack/types/dist/block-kit/block-elements.js +4 -0
  10. package/node_modules/@slack/types/dist/block-kit/block-elements.js.map +1 -0
  11. package/node_modules/@slack/types/dist/block-kit/blocks.d.ts +474 -0
  12. package/node_modules/@slack/types/dist/block-kit/blocks.d.ts.map +1 -0
  13. package/node_modules/@slack/types/dist/block-kit/blocks.js +3 -0
  14. package/node_modules/@slack/types/dist/block-kit/blocks.js.map +1 -0
  15. package/node_modules/@slack/types/dist/block-kit/composition-objects.d.ts +237 -0
  16. package/node_modules/@slack/types/dist/block-kit/composition-objects.d.ts.map +1 -0
  17. package/node_modules/@slack/types/dist/block-kit/composition-objects.js +4 -0
  18. package/node_modules/@slack/types/dist/block-kit/composition-objects.js.map +1 -0
  19. package/node_modules/@slack/types/dist/block-kit/extensions.d.ts +88 -0
  20. package/node_modules/@slack/types/dist/block-kit/extensions.d.ts.map +1 -0
  21. package/node_modules/@slack/types/dist/block-kit/extensions.js +3 -0
  22. package/node_modules/@slack/types/dist/block-kit/extensions.js.map +1 -0
  23. package/node_modules/@slack/types/dist/calls.d.ts +26 -0
  24. package/node_modules/@slack/types/dist/calls.d.ts.map +1 -0
  25. package/node_modules/@slack/types/dist/calls.js +6 -0
  26. package/node_modules/@slack/types/dist/calls.js.map +1 -0
  27. package/node_modules/@slack/types/dist/chunk.d.ts +52 -0
  28. package/node_modules/@slack/types/dist/chunk.d.ts.map +1 -0
  29. package/node_modules/@slack/types/dist/chunk.js +3 -0
  30. package/node_modules/@slack/types/dist/chunk.js.map +1 -0
  31. package/node_modules/@slack/types/dist/common/bot-profile.d.ts +12 -0
  32. package/node_modules/@slack/types/dist/common/bot-profile.d.ts.map +1 -0
  33. package/node_modules/@slack/types/dist/common/bot-profile.js +3 -0
  34. package/node_modules/@slack/types/dist/common/bot-profile.js.map +1 -0
  35. package/node_modules/@slack/types/dist/common/status-emoji-display-info.d.ts +6 -0
  36. package/node_modules/@slack/types/dist/common/status-emoji-display-info.d.ts.map +1 -0
  37. package/node_modules/@slack/types/dist/common/status-emoji-display-info.js +3 -0
  38. package/node_modules/@slack/types/dist/common/status-emoji-display-info.js.map +1 -0
  39. package/node_modules/@slack/types/dist/dialog.d.ts +36 -0
  40. package/node_modules/@slack/types/dist/dialog.d.ts.map +1 -0
  41. package/node_modules/@slack/types/dist/dialog.js +3 -0
  42. package/node_modules/@slack/types/dist/dialog.js.map +1 -0
  43. package/node_modules/@slack/types/dist/events/app.d.ts +204 -0
  44. package/node_modules/@slack/types/dist/events/app.d.ts.map +1 -0
  45. package/node_modules/@slack/types/dist/events/app.js +3 -0
  46. package/node_modules/@slack/types/dist/events/app.js.map +1 -0
  47. package/node_modules/@slack/types/dist/events/assistant.d.ts +29 -0
  48. package/node_modules/@slack/types/dist/events/assistant.d.ts.map +1 -0
  49. package/node_modules/@slack/types/dist/events/assistant.js +3 -0
  50. package/node_modules/@slack/types/dist/events/assistant.js.map +1 -0
  51. package/node_modules/@slack/types/dist/events/call.d.ts +8 -0
  52. package/node_modules/@slack/types/dist/events/call.d.ts.map +1 -0
  53. package/node_modules/@slack/types/dist/events/call.js +3 -0
  54. package/node_modules/@slack/types/dist/events/call.js.map +1 -0
  55. package/node_modules/@slack/types/dist/events/channel.d.ts +85 -0
  56. package/node_modules/@slack/types/dist/events/channel.d.ts.map +1 -0
  57. package/node_modules/@slack/types/dist/events/channel.js +3 -0
  58. package/node_modules/@slack/types/dist/events/channel.js.map +1 -0
  59. package/node_modules/@slack/types/dist/events/dnd.d.ts +24 -0
  60. package/node_modules/@slack/types/dist/events/dnd.d.ts.map +1 -0
  61. package/node_modules/@slack/types/dist/events/dnd.js +3 -0
  62. package/node_modules/@slack/types/dist/events/dnd.js.map +1 -0
  63. package/node_modules/@slack/types/dist/events/email.d.ts +6 -0
  64. package/node_modules/@slack/types/dist/events/email.d.ts.map +1 -0
  65. package/node_modules/@slack/types/dist/events/email.js +3 -0
  66. package/node_modules/@slack/types/dist/events/email.js.map +1 -0
  67. package/node_modules/@slack/types/dist/events/emoji.d.ts +11 -0
  68. package/node_modules/@slack/types/dist/events/emoji.d.ts.map +1 -0
  69. package/node_modules/@slack/types/dist/events/emoji.js +3 -0
  70. package/node_modules/@slack/types/dist/events/emoji.js.map +1 -0
  71. package/node_modules/@slack/types/dist/events/entity-details-requested.d.ts +21 -0
  72. package/node_modules/@slack/types/dist/events/entity-details-requested.d.ts.map +1 -0
  73. package/node_modules/@slack/types/dist/events/entity-details-requested.js +3 -0
  74. package/node_modules/@slack/types/dist/events/entity-details-requested.js.map +1 -0
  75. package/node_modules/@slack/types/dist/events/file.d.ts +60 -0
  76. package/node_modules/@slack/types/dist/events/file.d.ts.map +1 -0
  77. package/node_modules/@slack/types/dist/events/file.js +4 -0
  78. package/node_modules/@slack/types/dist/events/file.js.map +1 -0
  79. package/node_modules/@slack/types/dist/events/function.d.ts +33 -0
  80. package/node_modules/@slack/types/dist/events/function.d.ts.map +1 -0
  81. package/node_modules/@slack/types/dist/events/function.js +3 -0
  82. package/node_modules/@slack/types/dist/events/function.js.map +1 -0
  83. package/node_modules/@slack/types/dist/events/grid-migration.d.ts +9 -0
  84. package/node_modules/@slack/types/dist/events/grid-migration.d.ts.map +1 -0
  85. package/node_modules/@slack/types/dist/events/grid-migration.js +3 -0
  86. package/node_modules/@slack/types/dist/events/grid-migration.js.map +1 -0
  87. package/node_modules/@slack/types/dist/events/group.d.ts +55 -0
  88. package/node_modules/@slack/types/dist/events/group.d.ts.map +1 -0
  89. package/node_modules/@slack/types/dist/events/group.js +3 -0
  90. package/node_modules/@slack/types/dist/events/group.js.map +1 -0
  91. package/node_modules/@slack/types/dist/events/im.d.ts +26 -0
  92. package/node_modules/@slack/types/dist/events/im.d.ts.map +1 -0
  93. package/node_modules/@slack/types/dist/events/im.js +3 -0
  94. package/node_modules/@slack/types/dist/events/im.js.map +1 -0
  95. package/node_modules/@slack/types/dist/events/index.d.ts +60 -0
  96. package/node_modules/@slack/types/dist/events/index.d.ts.map +1 -0
  97. package/node_modules/@slack/types/dist/events/index.js +43 -0
  98. package/node_modules/@slack/types/dist/events/index.js.map +1 -0
  99. package/node_modules/@slack/types/dist/events/invite.d.ts +20 -0
  100. package/node_modules/@slack/types/dist/events/invite.d.ts.map +1 -0
  101. package/node_modules/@slack/types/dist/events/invite.js +3 -0
  102. package/node_modules/@slack/types/dist/events/invite.js.map +1 -0
  103. package/node_modules/@slack/types/dist/events/link-shared.d.ts +16 -0
  104. package/node_modules/@slack/types/dist/events/link-shared.d.ts.map +1 -0
  105. package/node_modules/@slack/types/dist/events/link-shared.js +3 -0
  106. package/node_modules/@slack/types/dist/events/link-shared.js.map +1 -0
  107. package/node_modules/@slack/types/dist/events/member.d.ts +19 -0
  108. package/node_modules/@slack/types/dist/events/member.d.ts.map +1 -0
  109. package/node_modules/@slack/types/dist/events/member.js +3 -0
  110. package/node_modules/@slack/types/dist/events/member.js.map +1 -0
  111. package/node_modules/@slack/types/dist/events/message-metadata.d.ts +38 -0
  112. package/node_modules/@slack/types/dist/events/message-metadata.d.ts.map +1 -0
  113. package/node_modules/@slack/types/dist/events/message-metadata.js +3 -0
  114. package/node_modules/@slack/types/dist/events/message-metadata.js.map +1 -0
  115. package/node_modules/@slack/types/dist/events/message.d.ts +306 -0
  116. package/node_modules/@slack/types/dist/events/message.d.ts.map +1 -0
  117. package/node_modules/@slack/types/dist/events/message.js +3 -0
  118. package/node_modules/@slack/types/dist/events/message.js.map +1 -0
  119. package/node_modules/@slack/types/dist/events/pin.d.ts +60 -0
  120. package/node_modules/@slack/types/dist/events/pin.d.ts.map +1 -0
  121. package/node_modules/@slack/types/dist/events/pin.js +3 -0
  122. package/node_modules/@slack/types/dist/events/pin.js.map +1 -0
  123. package/node_modules/@slack/types/dist/events/reaction.d.ts +23 -0
  124. package/node_modules/@slack/types/dist/events/reaction.d.ts.map +1 -0
  125. package/node_modules/@slack/types/dist/events/reaction.js +3 -0
  126. package/node_modules/@slack/types/dist/events/reaction.js.map +1 -0
  127. package/node_modules/@slack/types/dist/events/shared-channel.d.ts +134 -0
  128. package/node_modules/@slack/types/dist/events/shared-channel.d.ts.map +1 -0
  129. package/node_modules/@slack/types/dist/events/shared-channel.js +3 -0
  130. package/node_modules/@slack/types/dist/events/shared-channel.js.map +1 -0
  131. package/node_modules/@slack/types/dist/events/star.d.ts +13 -0
  132. package/node_modules/@slack/types/dist/events/star.d.ts.map +1 -0
  133. package/node_modules/@slack/types/dist/events/star.js +3 -0
  134. package/node_modules/@slack/types/dist/events/star.js.map +1 -0
  135. package/node_modules/@slack/types/dist/events/steps-from-apps.d.ts +82 -0
  136. package/node_modules/@slack/types/dist/events/steps-from-apps.d.ts.map +1 -0
  137. package/node_modules/@slack/types/dist/events/steps-from-apps.js +3 -0
  138. package/node_modules/@slack/types/dist/events/steps-from-apps.js.map +1 -0
  139. package/node_modules/@slack/types/dist/events/subteam.d.ts +66 -0
  140. package/node_modules/@slack/types/dist/events/subteam.d.ts.map +1 -0
  141. package/node_modules/@slack/types/dist/events/subteam.js +3 -0
  142. package/node_modules/@slack/types/dist/events/subteam.js.map +1 -0
  143. package/node_modules/@slack/types/dist/events/team.d.ts +99 -0
  144. package/node_modules/@slack/types/dist/events/team.d.ts.map +1 -0
  145. package/node_modules/@slack/types/dist/events/team.js +3 -0
  146. package/node_modules/@slack/types/dist/events/team.js.map +1 -0
  147. package/node_modules/@slack/types/dist/events/token.d.ts +8 -0
  148. package/node_modules/@slack/types/dist/events/token.d.ts.map +1 -0
  149. package/node_modules/@slack/types/dist/events/token.js +3 -0
  150. package/node_modules/@slack/types/dist/events/token.js.map +1 -0
  151. package/node_modules/@slack/types/dist/events/user.d.ts +313 -0
  152. package/node_modules/@slack/types/dist/events/user.d.ts.map +1 -0
  153. package/node_modules/@slack/types/dist/events/user.js +3 -0
  154. package/node_modules/@slack/types/dist/events/user.js.map +1 -0
  155. package/node_modules/@slack/types/dist/index.d.ts +12 -0
  156. package/node_modules/@slack/types/dist/index.d.ts.map +1 -0
  157. package/node_modules/@slack/types/dist/index.js +28 -0
  158. package/node_modules/@slack/types/dist/index.js.map +1 -0
  159. package/node_modules/@slack/types/dist/message-attachments.d.ts +171 -0
  160. package/node_modules/@slack/types/dist/message-attachments.d.ts.map +1 -0
  161. package/node_modules/@slack/types/dist/message-attachments.js +3 -0
  162. package/node_modules/@slack/types/dist/message-attachments.js.map +1 -0
  163. package/node_modules/@slack/types/dist/message-metadata.d.ts +281 -0
  164. package/node_modules/@slack/types/dist/message-metadata.d.ts.map +1 -0
  165. package/node_modules/@slack/types/dist/message-metadata.js +27 -0
  166. package/node_modules/@slack/types/dist/message-metadata.js.map +1 -0
  167. package/node_modules/@slack/types/dist/views.d.ts +71 -0
  168. package/node_modules/@slack/types/dist/views.d.ts.map +1 -0
  169. package/node_modules/@slack/types/dist/views.js +3 -0
  170. package/node_modules/@slack/types/dist/views.js.map +1 -0
  171. package/node_modules/@slack/types/package.json +47 -0
  172. package/node_modules/@vellumai/gateway-client/bun.lock +3 -0
  173. package/node_modules/@vellumai/gateway-client/package.json +1 -0
  174. package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +69 -0
  175. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +65 -0
  176. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +162 -0
  177. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -0
  178. package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
  179. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +4 -2
  180. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +3 -2
  181. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +78 -0
  182. package/openapi.yaml +345 -18
  183. package/package.json +2 -1
  184. package/scripts/memory-inspect.ts +24 -14
  185. package/src/__tests__/access-request-seed-content-blocks.test.ts +83 -103
  186. package/src/__tests__/activation-early-marking.test.ts +1 -1
  187. package/src/__tests__/actor-token-service.test.ts +3 -3
  188. package/src/__tests__/agent-loop-callsite-precedence.test.ts +1 -40
  189. package/src/__tests__/agent-loop-compaction-events.test.ts +0 -1
  190. package/src/__tests__/agent-loop-compaction-strip.test.ts +0 -1
  191. package/src/__tests__/agent-loop-exit-reason.test.ts +0 -1
  192. package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
  193. package/src/__tests__/agent-loop-regrowth-guard.test.ts +0 -1
  194. package/src/__tests__/agent-loop.test.ts +3 -0
  195. package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
  196. package/src/__tests__/anthropic-provider.test.ts +143 -9
  197. package/src/__tests__/app-builder-skill-instructions.test.ts +47 -5
  198. package/src/__tests__/app-conversation-ids-backfill.test.ts +1 -1
  199. package/src/__tests__/app-source-watcher.test.ts +30 -10
  200. package/src/__tests__/approval-cascade.test.ts +6 -0
  201. package/src/__tests__/approval-interception-trust-gates.test.ts +151 -0
  202. package/src/__tests__/approval-primitive.test.ts +1 -1
  203. package/src/__tests__/approval-routes-http.test.ts +1 -1
  204. package/src/__tests__/assistant-attachments.test.ts +155 -0
  205. package/src/__tests__/assistant-event-hub-machine-name.test.ts +2 -4
  206. package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
  207. package/src/__tests__/assistant-events-sse-shed.test.ts +1 -1
  208. package/src/__tests__/attachment-upload-trusted-source.test.ts +13 -8
  209. package/src/__tests__/attachments-store.test.ts +1 -1
  210. package/src/__tests__/audit-log-rotation.test.ts +50 -54
  211. package/src/__tests__/auth-fallback-events-store.test.ts +1 -1
  212. package/src/__tests__/auto-analysis-end-to-end.test.ts +9 -14
  213. package/src/__tests__/background-shell-bash.test.ts +4 -1
  214. package/src/__tests__/background-shell-host-bash.test.ts +17 -3
  215. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -0
  216. package/src/__tests__/call-controller.test.ts +1 -1
  217. package/src/__tests__/call-conversation-messages.test.ts +1 -1
  218. package/src/__tests__/call-domain.test.ts +1 -1
  219. package/src/__tests__/call-pointer-messages.test.ts +3 -4
  220. package/src/__tests__/call-recovery.test.ts +1 -1
  221. package/src/__tests__/call-routes-http.test.ts +1 -1
  222. package/src/__tests__/call-store.test.ts +1 -1
  223. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  224. package/src/__tests__/canonical-guardian-store.test.ts +24 -1
  225. package/src/__tests__/channel-approval-routes.test.ts +73 -1119
  226. package/src/__tests__/channel-delivery-store.test.ts +1 -1
  227. package/src/__tests__/channel-guardian.test.ts +265 -641
  228. package/src/__tests__/channel-inbound-disk-pressure.test.ts +1 -2
  229. package/src/__tests__/channel-retry-sweep.test.ts +1 -1
  230. package/src/__tests__/compaction-events.test.ts +6 -0
  231. package/src/__tests__/compaction-trail-store.test.ts +6 -5
  232. package/src/__tests__/compaction.benchmark.test.ts +0 -1
  233. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  234. package/src/__tests__/config-loader-backfill.test.ts +183 -51
  235. package/src/__tests__/config-schema.test.ts +34 -0
  236. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -2
  237. package/src/__tests__/contact-store-user-file.test.ts +2 -2
  238. package/src/__tests__/contacts-relay-reads.test.ts +409 -0
  239. package/src/__tests__/contacts-tools.test.ts +4 -4
  240. package/src/__tests__/contacts-write.test.ts +1 -2
  241. package/src/__tests__/context-search-conversations-source.test.ts +1 -1
  242. package/src/__tests__/context-window-manager-compact-retry.test.ts +6 -2
  243. package/src/__tests__/context-window-manager-overflow-rung.test.ts +6 -2
  244. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  245. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -0
  246. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +3 -0
  247. package/src/__tests__/conversation-agent-loop-overflow.test.ts +3 -0
  248. package/src/__tests__/conversation-agent-loop.test.ts +3 -0
  249. package/src/__tests__/conversation-attachments.test.ts +2 -5
  250. package/src/__tests__/conversation-attention-store.test.ts +1 -1
  251. package/src/__tests__/conversation-attention-telegram.test.ts +1 -2
  252. package/src/__tests__/conversation-clear-safety.test.ts +1 -1
  253. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
  254. package/src/__tests__/conversation-crud-inference-profile.test.ts +1 -1
  255. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +12 -19
  256. package/src/__tests__/conversation-disk-view-integration.test.ts +1 -1
  257. package/src/__tests__/conversation-disk-view.test.ts +1 -1
  258. package/src/__tests__/conversation-fork-crud.test.ts +10 -8
  259. package/src/__tests__/conversation-fork-retrospective.test.ts +250 -0
  260. package/src/__tests__/conversation-fork-route.test.ts +1 -1
  261. package/src/__tests__/conversation-inference-profile-list.test.ts +1 -1
  262. package/src/__tests__/conversation-inference-profile-route.test.ts +1 -1
  263. package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
  264. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  265. package/src/__tests__/conversation-lifecycle.test.ts +117 -0
  266. package/src/__tests__/conversation-list-source.test.ts +3 -3
  267. package/src/__tests__/conversation-process-callsite.test.ts +6 -14
  268. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  269. package/src/__tests__/conversation-queue.test.ts +6 -0
  270. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
  271. package/src/__tests__/conversation-runtime-assembly.test.ts +115 -12
  272. package/src/__tests__/conversation-slash-queue.test.ts +6 -0
  273. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  274. package/src/__tests__/conversation-speed-override.test.ts +6 -0
  275. package/src/__tests__/conversation-starter-routes.test.ts +5 -5
  276. package/src/__tests__/conversation-store.test.ts +1 -1
  277. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -1
  278. package/src/__tests__/conversation-sync-tags.test.ts +1 -1
  279. package/src/__tests__/conversation-usage.test.ts +1 -1
  280. package/src/__tests__/conversation-wipe.test.ts +9 -8
  281. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
  282. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  283. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  284. package/src/__tests__/conversations-import-system-filter.test.ts +1 -1
  285. package/src/__tests__/copy-composer-tc-templates.test.ts +17 -0
  286. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  287. package/src/__tests__/db-acp-history.test.ts +2 -2
  288. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +5 -7
  289. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +6 -7
  290. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +5 -10
  291. package/src/__tests__/db-migration-rollback.test.ts +129 -39
  292. package/src/__tests__/db-proxy-transaction.test.ts +1 -1
  293. package/src/__tests__/db-schedule-syntax-migration.test.ts +0 -11
  294. package/src/__tests__/db-test-helpers.ts +36 -19
  295. package/src/__tests__/delete-propagation.test.ts +1 -1
  296. package/src/__tests__/deterministic-verification-control-plane.test.ts +26 -8
  297. package/src/__tests__/disk-pressure-tools.test.ts +41 -1
  298. package/src/__tests__/dm-backfill.test.ts +1 -1
  299. package/src/__tests__/drop-capability-card-state-migration.test.ts +0 -8
  300. package/src/__tests__/edit-propagation.test.ts +1 -1
  301. package/src/__tests__/emit-signal-routing-intent.test.ts +83 -0
  302. package/src/__tests__/empty-response-hook.test.ts +42 -0
  303. package/src/__tests__/events-client-registration.test.ts +1 -1
  304. package/src/__tests__/followup-tools.test.ts +1 -1
  305. package/src/__tests__/gemini-count-tokens.test.ts +70 -0
  306. package/src/__tests__/guardian-action-sweep.test.ts +9 -2
  307. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  308. package/src/__tests__/guardian-card-withdrawal.test.ts +1 -1
  309. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +1 -1
  310. package/src/__tests__/guardian-dispatch.test.ts +1 -1
  311. package/src/__tests__/guardian-outbound-http.test.ts +7 -12
  312. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +1 -1
  313. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  314. package/src/__tests__/guardian-routing-state.test.ts +1 -2
  315. package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
  316. package/src/__tests__/headless-browser-mode.test.ts +2 -2
  317. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  318. package/src/__tests__/heartbeat-service.test.ts +6 -0
  319. package/src/__tests__/helpers/channel-test-adapter.ts +98 -0
  320. package/src/__tests__/http-conversation-lineage.test.ts +1 -1
  321. package/src/__tests__/image-recovery-hook.test.ts +1 -1
  322. package/src/__tests__/inbound-invite-redemption.test.ts +1 -2
  323. package/src/__tests__/inbound-trust-verdict.test.ts +254 -0
  324. package/src/__tests__/inference-profile-reaper.test.ts +1 -1
  325. package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
  326. package/src/__tests__/inference-profile-session-ipc.test.ts +1 -1
  327. package/src/__tests__/injector-chain.test.ts +1 -1
  328. package/src/__tests__/injector-disk-pressure.test.ts +11 -6
  329. package/src/__tests__/internal-telemetry-routes.test.ts +1 -1
  330. package/src/__tests__/invite-redemption-service.test.ts +244 -43
  331. package/src/__tests__/invite-routes-http.test.ts +35 -186
  332. package/src/__tests__/invite-service-ipc.test.ts +287 -0
  333. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +5 -5
  334. package/src/__tests__/jobs-store-upsert-debounced.test.ts +9 -12
  335. package/src/__tests__/list-messages-attachments.test.ts +42 -1
  336. package/src/__tests__/list-messages-client-message-id.test.ts +1 -1
  337. package/src/__tests__/list-messages-hidden-metadata.test.ts +1 -1
  338. package/src/__tests__/list-messages-page-latest.test.ts +1 -1
  339. package/src/__tests__/list-messages-tool-merge.test.ts +1 -1
  340. package/src/__tests__/llm-context-route-provider.test.ts +69 -4
  341. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +9 -5
  342. package/src/__tests__/llm-request-log-call-site.test.ts +6 -6
  343. package/src/__tests__/llm-request-log-turn-query.test.ts +27 -13
  344. package/src/__tests__/llm-usage-store.test.ts +40 -1
  345. package/src/__tests__/log-export-routes.test.ts +1 -1
  346. package/src/__tests__/log-export-workspace.test.ts +3 -3
  347. package/src/__tests__/memory-jobs-worker-lanes.test.ts +5 -5
  348. package/src/__tests__/memory-recall-log-store.test.ts +1 -1
  349. package/src/__tests__/memory-upsert-concurrency.test.ts +3 -4
  350. package/src/__tests__/messages-after-tiebreaker.test.ts +1 -1
  351. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  352. package/src/__tests__/mtime-cache.test.ts +375 -0
  353. package/src/__tests__/non-member-access-request.test.ts +1 -2
  354. package/src/__tests__/notification-candidate-guardian-context.test.ts +203 -0
  355. package/src/__tests__/notification-guardian-path.test.ts +1 -1
  356. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  357. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  358. package/src/__tests__/oauth-provider-visibility.test.ts +1 -1
  359. package/src/__tests__/oauth-store.test.ts +1 -1
  360. package/src/__tests__/persist-unsendable-image-downscale.test.ts +1 -1
  361. package/src/__tests__/persist-unsendable-image.test.ts +1 -1
  362. package/src/__tests__/persona-resolver.test.ts +39 -1
  363. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  364. package/src/__tests__/playbook-execution.test.ts +1 -1
  365. package/src/__tests__/playbook-tools.test.ts +1 -1
  366. package/src/__tests__/plugin-api-model-profiles.test.ts +74 -21
  367. package/src/__tests__/plugin-bootstrap.test.ts +78 -0
  368. package/src/__tests__/provider-platform-proxy-integration.test.ts +25 -5
  369. package/src/__tests__/provider-usage-tracking.test.ts +1 -1
  370. package/src/__tests__/prune-old-conversations-job.test.ts +1 -1
  371. package/src/__tests__/reaction-persistence.test.ts +1 -1
  372. package/src/__tests__/relay-server.test.ts +357 -56
  373. package/src/__tests__/runtime-attachment-metadata.test.ts +10 -1
  374. package/src/__tests__/runtime-events-sse-bilingual.test.ts +7 -9
  375. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  376. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  377. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  378. package/src/__tests__/schedule-retry.test.ts +1 -1
  379. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -1
  380. package/src/__tests__/schedule-routes.test.ts +1 -1
  381. package/src/__tests__/schedule-store.test.ts +1 -1
  382. package/src/__tests__/schedule-tools.test.ts +1 -1
  383. package/src/__tests__/scheduler-disk-pressure.test.ts +1 -1
  384. package/src/__tests__/scheduler-recurrence.test.ts +1 -1
  385. package/src/__tests__/scheduler-reuse-conversation.test.ts +1 -1
  386. package/src/__tests__/scheduler-wake.test.ts +2 -1
  387. package/src/__tests__/scoped-approval-grants.test.ts +1 -1
  388. package/src/__tests__/scoped-grant-security-matrix.test.ts +5 -5
  389. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +0 -8
  390. package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -0
  391. package/src/__tests__/send-endpoint-busy.test.ts +1 -1
  392. package/src/__tests__/sequence-store.test.ts +1 -1
  393. package/src/__tests__/server-history-render.test.ts +40 -1
  394. package/src/__tests__/settings-routes.test.ts +11 -10
  395. package/src/__tests__/skill-load-tool.test.ts +72 -0
  396. package/src/__tests__/slack-inbound-verification.test.ts +1 -3
  397. package/src/__tests__/slack-messaging-token-resolution.test.ts +13 -2
  398. package/src/__tests__/slack-reaction-canonical-approval.test.ts +1 -1
  399. package/src/__tests__/subagent-tool-gate-mode.test.ts +2 -73
  400. package/src/__tests__/subagent-tools.test.ts +1 -31
  401. package/src/__tests__/system-prompt.test.ts +1 -1
  402. package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
  403. package/src/__tests__/task-compiler.test.ts +1 -1
  404. package/src/__tests__/task-management-tools.test.ts +1 -1
  405. package/src/__tests__/task-memory-cleanup.test.ts +9 -6
  406. package/src/__tests__/task-scheduler.test.ts +1 -1
  407. package/src/__tests__/thread-backfill.test.ts +1 -1
  408. package/src/__tests__/tool-approval-handler.test.ts +1 -1
  409. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +2 -0
  410. package/src/__tests__/tool-executor.test.ts +32 -1
  411. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -2
  412. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +73 -1
  413. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +34 -34
  414. package/src/__tests__/trusted-contact-multichannel.test.ts +1 -2
  415. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  416. package/src/__tests__/turn-boundary-resolution.test.ts +3 -3
  417. package/src/__tests__/turn-events-store.test.ts +1 -1
  418. package/src/__tests__/twilio-routes.test.ts +2 -3
  419. package/src/__tests__/usage-cache-backfill-migration.test.ts +20 -10
  420. package/src/__tests__/usage-routes.test.ts +1 -1
  421. package/src/__tests__/user-plugin-loader.test.ts +34 -29
  422. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  423. package/src/__tests__/voice-invite-redemption.test.ts +134 -36
  424. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
  425. package/src/__tests__/voice-session-bridge.test.ts +1 -1
  426. package/src/__tests__/workspace-git-service.test.ts +114 -1
  427. package/src/__tests__/workspace-heartbeat-service.test.ts +45 -0
  428. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +1 -1
  429. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
  430. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +88 -18
  431. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +6 -6
  432. package/src/__tests__/workspace-migration-109-swap-quality-profile-to-glm-5p2.test.ts +281 -0
  433. package/src/__tests__/workspace-migration-110-flip-balanced-profile-to-together.test.ts +167 -0
  434. package/src/__tests__/workspace-migrations-runner.test.ts +55 -0
  435. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +1 -1
  436. package/src/a2a/__tests__/task-store.test.ts +1 -1
  437. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -1
  438. package/src/acp/__tests__/session-manager-resume.test.ts +22 -11
  439. package/src/acp/__tests__/session-manager-startup.test.ts +1 -1
  440. package/src/acp/__tests__/session-manager.test.ts +72 -1
  441. package/src/acp/index.ts +10 -0
  442. package/src/acp/session-manager.ts +35 -0
  443. package/src/agent/loop.ts +45 -27
  444. package/src/api/index.ts +0 -6
  445. package/src/approvals/AGENTS.md +1 -2
  446. package/src/approvals/guardian-decision-primitive.ts +13 -210
  447. package/src/approvals/guardian-request-resolvers.ts +104 -58
  448. package/src/background-wake/wake-intent-hooks.test.ts +1 -1
  449. package/src/calls/__tests__/inbound-trust-reader.test.ts +110 -0
  450. package/src/calls/__tests__/relay-setup-router.test.ts +88 -62
  451. package/src/calls/inbound-trust-reader.ts +40 -0
  452. package/src/calls/relay-server.ts +65 -23
  453. package/src/calls/relay-setup-router.ts +20 -6
  454. package/src/calls/relay-verification.ts +7 -7
  455. package/src/cli/commands/contacts.ts +6 -24
  456. package/src/cli/commands/db/__tests__/repair.test.ts +15 -6
  457. package/src/cli/commands/db/__tests__/status.test.ts +7 -3
  458. package/src/cli/commands/db/status.ts +212 -33
  459. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +6 -1
  460. package/src/cli/commands/memory/index.ts +2 -0
  461. package/src/cli/commands/memory/memory-retrospective.ts +129 -0
  462. package/src/cli/commands/memory/memory-v3.ts +176 -4
  463. package/src/cli/commands/plugins.ts +268 -11
  464. package/src/cli/lib/__tests__/install-from-github.test.ts +40 -0
  465. package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
  466. package/src/cli/lib/__tests__/toggle-plugin.test.ts +158 -0
  467. package/src/cli/lib/install-from-github.ts +47 -6
  468. package/src/cli/lib/plugin-marketplace.ts +11 -0
  469. package/src/cli/lib/plugin-pin-history.ts +257 -0
  470. package/src/cli/lib/toggle-plugin.ts +146 -0
  471. package/src/config/__tests__/sync-gated-profiles.test.ts +2 -2
  472. package/src/config/bundled-skills/app-builder/SKILL.md +15 -33
  473. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +3 -8
  474. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +64 -37
  475. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +1 -1
  476. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +14 -72
  477. package/src/config/bundled-skills/app-builder/references/examples/README.md +1 -2
  478. package/src/config/bundled-skills/contacts/SKILL.md +7 -12
  479. package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
  480. package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
  481. package/src/config/bundled-skills/workflows/SKILL.md +4 -3
  482. package/src/config/call-site-defaults.ts +11 -2
  483. package/src/config/feature-flag-registry.json +0 -8
  484. package/src/config/profile-dispatchability.ts +11 -0
  485. package/src/config/schemas/call-site-catalog.ts +7 -0
  486. package/src/config/schemas/llm.ts +2 -0
  487. package/src/config/schemas/memory-lifecycle.ts +5 -3
  488. package/src/config/schemas/timeouts.ts +24 -0
  489. package/src/config/seed-inference-profiles.ts +133 -45
  490. package/src/config/sync-gated-profiles.ts +13 -1
  491. package/src/contacts/contact-store.ts +21 -0
  492. package/src/contacts/member-status.ts +9 -0
  493. package/src/credential-health/credential-health-service.ts +1 -5
  494. package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
  495. package/src/daemon/app-source-watcher.ts +31 -18
  496. package/src/daemon/assistant-attachments.ts +94 -4
  497. package/src/daemon/conversation-agent-loop-handlers.ts +3 -0
  498. package/src/daemon/conversation-agent-loop.ts +9 -36
  499. package/src/daemon/conversation-runtime-assembly.ts +91 -66
  500. package/src/daemon/conversation-tool-setup.ts +20 -63
  501. package/src/daemon/conversation.ts +144 -52
  502. package/src/daemon/event-loop-watchdog.test.ts +85 -0
  503. package/src/daemon/event-loop-watchdog.ts +133 -0
  504. package/src/daemon/external-plugins-bootstrap.ts +26 -80
  505. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -1
  506. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +1 -1
  507. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +1 -1
  508. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +1 -1
  509. package/src/daemon/handlers/__tests__/config-a2a.test.ts +1 -1
  510. package/src/daemon/handlers/config-channels.ts +32 -18
  511. package/src/daemon/handlers/conversations.ts +7 -0
  512. package/src/daemon/handlers/shared.ts +7 -0
  513. package/src/daemon/lifecycle.ts +16 -3
  514. package/src/daemon/message-types/inbox.ts +0 -6
  515. package/src/daemon/message-types/messages.ts +0 -4
  516. package/src/daemon/message-types/surfaces.ts +18 -8
  517. package/src/daemon/server.ts +0 -4
  518. package/src/daemon/tool-setup-types.ts +0 -7
  519. package/src/daemon/trust-context.ts +6 -0
  520. package/src/daemon/wake-conversation-ops.ts +70 -0
  521. package/src/daemon/workspace-tools-watcher.ts +7 -3
  522. package/src/documents/document-comments-store.test.ts +1 -1
  523. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +1 -1
  524. package/src/heartbeat/__tests__/heartbeat-service.test.ts +6 -0
  525. package/src/heartbeat/heartbeat-service.ts +3 -4
  526. package/src/ipc/__tests__/attachment-ipc.test.ts +1 -1
  527. package/src/ipc/__tests__/browser-ipc.test.ts +73 -2
  528. package/src/ipc/__tests__/watcher-ipc.test.ts +59 -39
  529. package/src/ipc/assistant-server.ts +8 -0
  530. package/src/ipc/gateway-client.ts +2 -1
  531. package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +58 -0
  532. package/src/ipc/routes/invite-ipc-routes.ts +66 -0
  533. package/src/live-voice/__tests__/live-voice-archive.test.ts +1 -1
  534. package/src/memory/__tests__/activation-session-store.test.ts +1 -1
  535. package/src/memory/__tests__/auto-analysis-guard.test.ts +1 -1
  536. package/src/memory/__tests__/conversation-group-migration.test.ts +1 -1
  537. package/src/memory/__tests__/conversation-queries.test.ts +1 -1
  538. package/src/memory/__tests__/db-async-query.test.ts +1 -1
  539. package/src/memory/__tests__/db-logs-attach.test.ts +110 -0
  540. package/src/memory/__tests__/db-maintenance.test.ts +28 -36
  541. package/src/memory/__tests__/db-memory-attach.test.ts +113 -0
  542. package/src/memory/__tests__/find-analysis-conversation.test.ts +1 -1
  543. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +1 -1
  544. package/src/memory/__tests__/fork-message-copy.test.ts +232 -0
  545. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +3 -0
  546. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +5 -5
  547. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +8 -6
  548. package/src/memory/__tests__/memory-retrospective-job.test.ts +30 -37
  549. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +69 -66
  550. package/src/memory/__tests__/memory-retrospective-state.test.ts +1 -1
  551. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +1 -1
  552. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +1 -1
  553. package/src/memory/__tests__/onboarding-events-store.test.ts +1 -1
  554. package/src/memory/__tests__/table-relocation.test.ts +129 -0
  555. package/src/memory/conversation-crud.ts +461 -152
  556. package/src/memory/db-async-query.ts +89 -5
  557. package/src/memory/db-connection.ts +101 -18
  558. package/src/memory/db-init.ts +409 -234
  559. package/src/memory/db-maintenance.ts +43 -38
  560. package/src/memory/db-singleton.ts +45 -19
  561. package/src/memory/fork-message-copy.ts +170 -0
  562. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +92 -0
  563. package/src/memory/graph/bootstrap.test.ts +6 -3
  564. package/src/memory/graph/retriever.test.ts +12 -12
  565. package/src/memory/graph/store.test.ts +15 -25
  566. package/src/memory/graph/store.ts +23 -14
  567. package/src/memory/graph/tool-handlers.ts +34 -5
  568. package/src/memory/graph/tools.ts +5 -2
  569. package/src/memory/indexer.ts +21 -9
  570. package/src/memory/job-handlers/cleanup.ts +10 -3
  571. package/src/memory/job-handlers/embedding.test.ts +4 -4
  572. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +4 -4
  573. package/src/memory/jobs/embed-pkb-file.test.ts +7 -7
  574. package/src/memory/jobs-store.ts +36 -24
  575. package/src/memory/llm-request-log-store.ts +51 -19
  576. package/src/memory/llm-usage-store.ts +31 -1
  577. package/src/memory/memory-retrospective-job.ts +27 -19
  578. package/src/memory/memory-retrospective-startup-cleanup.ts +10 -2
  579. package/src/memory/migrations/{100-core-tables.ts → 000-core-tables.ts} +6 -10
  580. package/src/memory/migrations/104-core-indexes.ts +1 -1
  581. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +189 -196
  582. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +98 -105
  583. package/src/memory/migrations/134-contacts-notes-column.ts +66 -69
  584. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +19 -22
  585. package/src/memory/migrations/136-drop-assistant-id-columns.ts +227 -230
  586. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +204 -209
  587. package/src/memory/migrations/141-rename-verification-table.ts +45 -48
  588. package/src/memory/migrations/142-rename-verification-session-id-column.ts +16 -23
  589. package/src/memory/migrations/143-rename-guardian-verification-values.ts +23 -30
  590. package/src/memory/migrations/144-rename-voice-to-phone.ts +133 -136
  591. package/src/memory/migrations/145-drop-accounts-table.ts +4 -7
  592. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +79 -82
  593. package/src/memory/migrations/148-drop-reminders-table.ts +3 -6
  594. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +71 -78
  595. package/src/memory/migrations/157-invite-contact-id.ts +73 -76
  596. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +44 -58
  597. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +36 -43
  598. package/src/memory/migrations/174-rename-thread-starters-table.ts +30 -37
  599. package/src/memory/migrations/176-drop-capability-card-state.ts +17 -22
  600. package/src/memory/migrations/177-create-trace-events-table.ts +23 -28
  601. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +36 -43
  602. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +14 -21
  603. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +17 -24
  604. package/src/memory/migrations/192-contacts-user-file-column.ts +6 -9
  605. package/src/memory/migrations/193-add-source-type-columns.ts +33 -36
  606. package/src/memory/migrations/194-memory-recall-logs.ts +34 -39
  607. package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +59 -66
  608. package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +41 -48
  609. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +11 -18
  610. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +76 -83
  611. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +50 -57
  612. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +6 -11
  613. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +4 -9
  614. package/src/memory/migrations/217-conversation-host-access.ts +13 -18
  615. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +86 -93
  616. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +41 -48
  617. package/src/memory/migrations/230-acp-session-history.ts +23 -28
  618. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +58 -62
  619. package/src/memory/migrations/232-activation-state.ts +11 -16
  620. package/src/memory/migrations/233-document-conversations.ts +20 -25
  621. package/src/memory/migrations/234-memory-v2-activation-logs.ts +26 -31
  622. package/src/memory/migrations/235-slack-compaction-watermark.ts +5 -10
  623. package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +6 -11
  624. package/src/memory/migrations/237-heartbeat-runs.ts +22 -27
  625. package/src/memory/migrations/239-trace-events-created-at-index.ts +4 -9
  626. package/src/memory/migrations/242-message-bookmarks.ts +17 -22
  627. package/src/memory/migrations/245-memory-retrospective-state.ts +8 -13
  628. package/src/memory/migrations/249-normalize-slack-external-content.ts +37 -41
  629. package/src/memory/migrations/251-a2a-tasks.ts +27 -32
  630. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +12 -17
  631. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +10 -15
  632. package/src/memory/migrations/256-memory-v2-injection-events.ts +70 -74
  633. package/src/memory/migrations/259-conversation-cleaned-at.ts +4 -9
  634. package/src/memory/migrations/260-rename-cleaned-at.ts +11 -16
  635. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +3 -8
  636. package/src/memory/migrations/262-memory-v3-coactivation.ts +21 -26
  637. package/src/memory/migrations/263-memory-v3-auto-edges.ts +14 -19
  638. package/src/memory/migrations/270-schedule-description.ts +7 -12
  639. package/src/memory/migrations/272-acp-session-history-cwd.ts +8 -13
  640. package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +8 -13
  641. package/src/memory/migrations/297-move-llm-request-logs-to-logs-db.ts +111 -0
  642. package/src/memory/migrations/298-move-memory-jobs-to-memory-db.ts +128 -0
  643. package/src/memory/migrations/299-canonical-guardian-deliveries-conversation-index.ts +19 -0
  644. package/src/memory/migrations/__tests__/297-move-llm-request-logs.test.ts +180 -0
  645. package/src/memory/migrations/__tests__/run-migrations.test.ts +333 -7
  646. package/src/memory/migrations/helpers/relocation.ts +227 -0
  647. package/src/memory/migrations/registry.ts +63 -0
  648. package/src/memory/migrations/run-migrations.ts +187 -16
  649. package/src/memory/migrations/validate-migration-state.ts +50 -145
  650. package/src/memory/raw-query.ts +47 -2
  651. package/src/memory/skill-loaded-events-store.test.ts +1 -1
  652. package/src/memory/task-memory-cleanup.ts +62 -41
  653. package/src/memory/tool-executed-events-store.test.ts +1 -1
  654. package/src/memory/turn-trace-store.test.ts +1 -1
  655. package/src/memory/v2/__tests__/backfill-jobs.test.ts +16 -15
  656. package/src/memory/v2/__tests__/harness-compare.test.ts +1 -1
  657. package/src/memory/v2/__tests__/harness-oracle.test.ts +1 -1
  658. package/src/memory/v2/__tests__/harness-replay-input.test.ts +1 -1
  659. package/src/memory/v2/__tests__/sweep-job.test.ts +2 -2
  660. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +38 -0
  661. package/src/memory/v3-eval/__tests__/eval-tally.test.ts +139 -0
  662. package/src/memory/v3-eval/eval-packets.ts +197 -12
  663. package/src/memory/v3-eval/eval-tally.ts +234 -0
  664. package/src/messaging/provider.ts +10 -0
  665. package/src/messaging/providers/gmail/adapter.ts +1 -0
  666. package/src/messaging/providers/gmail/client.ts +14 -0
  667. package/src/messaging/providers/index.ts +1 -1
  668. package/src/messaging/providers/slack/send.test.ts +87 -39
  669. package/src/messaging/providers/slack/send.ts +84 -105
  670. package/src/notifications/README.md +9 -5
  671. package/src/notifications/__tests__/deterministic-checks.test.ts +43 -1
  672. package/src/notifications/adapters/slack.ts +12 -10
  673. package/src/notifications/approval-card-builder.ts +81 -20
  674. package/src/notifications/approval-card-data.ts +8 -5
  675. package/src/notifications/canonical-delivery-recorder.ts +7 -5
  676. package/src/notifications/conversation-candidates.ts +24 -59
  677. package/src/notifications/copy-composer.ts +48 -68
  678. package/src/notifications/deterministic-checks.ts +19 -16
  679. package/src/notifications/emit-signal.ts +29 -1
  680. package/src/notifications/trusted-contact-payloads.ts +70 -0
  681. package/src/oauth/byo-connection.test.ts +9 -0
  682. package/src/oauth/connection-resolver.test.ts +146 -6
  683. package/src/oauth/connection-resolver.ts +132 -5
  684. package/src/oauth/oauth-store.ts +16 -3
  685. package/src/oauth/scope-utils.ts +21 -0
  686. package/src/plugin-api/index.ts +9 -4
  687. package/src/plugin-api/model-profiles.test.ts +123 -0
  688. package/src/plugin-api/model-profiles.ts +5 -1
  689. package/src/plugin-api/vision-support.test.ts +149 -0
  690. package/src/plugin-api/vision-support.ts +78 -0
  691. package/src/plugins/defaults/compaction/window-manager.ts +45 -64
  692. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +13 -4
  693. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +302 -0
  694. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +103 -0
  695. package/src/plugins/defaults/image-fallback/package.json +14 -0
  696. package/src/plugins/defaults/image-fallback/src/caption-cache.ts +49 -0
  697. package/src/plugins/defaults/image-fallback/src/image-persist.ts +59 -0
  698. package/src/plugins/defaults/image-fallback/src/vision-caption.ts +120 -0
  699. package/src/plugins/defaults/index.ts +23 -0
  700. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +14 -1
  701. package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
  702. package/src/plugins/external-plugin-loader.ts +47 -6
  703. package/src/plugins/mtime-cache.ts +772 -0
  704. package/src/plugins/pipeline.ts +7 -2
  705. package/src/plugins/registry.ts +16 -5
  706. package/src/plugins/user-loader.ts +22 -76
  707. package/src/prompts/persona-resolver.ts +29 -11
  708. package/src/prompts/system-prompt.ts +1 -1
  709. package/src/prompts/templates/system-sections.ts +4 -4
  710. package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
  711. package/src/providers/anthropic/client.ts +254 -185
  712. package/src/providers/call-site-routing.ts +10 -0
  713. package/src/providers/gemini/client.ts +43 -0
  714. package/src/providers/inference/adapter-factory.ts +6 -0
  715. package/src/providers/inference/connections.ts +6 -1
  716. package/src/providers/model-catalog.ts +37 -0
  717. package/src/providers/platform-proxy/constants.ts +5 -0
  718. package/src/providers/ratelimit.ts +9 -0
  719. package/src/providers/retry.ts +10 -0
  720. package/src/providers/together/client.ts +35 -0
  721. package/src/providers/types.ts +16 -0
  722. package/src/providers/usage-tracking.ts +7 -0
  723. package/src/runtime/AGENTS.md +9 -1
  724. package/src/runtime/__tests__/agent-wake.test.ts +259 -4
  725. package/src/runtime/__tests__/slack-block-formatting.test.ts +39 -10
  726. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +417 -0
  727. package/src/runtime/actor-trust-resolver.ts +8 -16
  728. package/src/runtime/agent-wake.ts +183 -60
  729. package/src/runtime/channel-reply-delivery.ts +6 -3
  730. package/src/runtime/guardian-decision-types.ts +3 -22
  731. package/src/runtime/http-server.ts +1 -15
  732. package/src/runtime/invite-redemption-service.ts +155 -6
  733. package/src/runtime/invite-service.ts +113 -62
  734. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +3 -0
  735. package/src/runtime/routes/__tests__/acp-routes.test.ts +1 -1
  736. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -1
  737. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +277 -0
  738. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +140 -0
  739. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +26 -7
  740. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +14 -10
  741. package/src/runtime/routes/__tests__/contact-routes-update-channel-relay.test.ts +164 -0
  742. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  743. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +1 -1
  744. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +8 -8
  745. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +1 -1
  746. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +1 -3
  747. package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +240 -0
  748. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +4 -0
  749. package/src/runtime/routes/__tests__/plugins-routes.test.ts +143 -0
  750. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +1 -1
  751. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +1 -1
  752. package/src/runtime/routes/acp-routes-list.test.ts +4 -0
  753. package/src/runtime/routes/acp-routes.test.ts +5 -6
  754. package/src/runtime/routes/attachment-routes.ts +21 -17
  755. package/src/runtime/routes/browser-routes.ts +19 -1
  756. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -9
  757. package/src/runtime/routes/channel-verification-routes.ts +12 -1
  758. package/src/runtime/routes/contact-routes.ts +275 -164
  759. package/src/runtime/routes/conversation-query-routes.ts +15 -5
  760. package/src/runtime/routes/conversation-routes.ts +24 -3
  761. package/src/runtime/routes/conversation-starter-routes.ts +7 -8
  762. package/src/runtime/routes/guardian-approval-interception.ts +13 -274
  763. package/src/runtime/routes/inbound-message-handler.ts +20 -15
  764. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +285 -0
  765. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +45 -34
  766. package/src/runtime/routes/inbound-stages/admission-policy.ts +20 -5
  767. package/src/runtime/routes/log-export-routes.ts +2 -2
  768. package/src/runtime/routes/memory-eval-routes.ts +92 -0
  769. package/src/runtime/routes/memory-item-routes.test.ts +12 -11
  770. package/src/runtime/routes/migration-routes.ts +51 -40
  771. package/src/runtime/routes/plugins-routes.ts +164 -8
  772. package/src/runtime/routes/schedule-routes.ts +1 -0
  773. package/src/runtime/routes/usage-routes.ts +3 -0
  774. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  775. package/src/runtime/slack-block-formatting.ts +46 -48
  776. package/src/runtime/trust-verdict-consumer.ts +172 -0
  777. package/src/schedule/scheduler.ts +6 -9
  778. package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
  779. package/src/tools/ask-question/ask-question-tool.test.ts +60 -52
  780. package/src/tools/ask-question/ask-question-tool.ts +14 -73
  781. package/src/tools/browser/__tests__/browser-status.test.ts +20 -0
  782. package/src/tools/browser/browser-execution.ts +16 -4
  783. package/src/tools/document/document-comment-tool.test.ts +1 -1
  784. package/src/tools/executor.ts +15 -3
  785. package/src/tools/host-terminal/host-shell.ts +28 -9
  786. package/src/tools/memory/register.test.ts +32 -0
  787. package/src/tools/skills/load.ts +43 -2
  788. package/src/tools/subagent/spawn.ts +4 -10
  789. package/src/tools/terminal/shell.ts +16 -5
  790. package/src/tools/types.ts +1 -0
  791. package/src/util/fs-watcher-error.ts +36 -0
  792. package/src/util/logs-db-path.ts +22 -0
  793. package/src/util/memory-db-path.ts +23 -0
  794. package/src/watcher/providers/gmail.ts +7 -2
  795. package/src/workflows/engine-integration.test.ts +1 -1
  796. package/src/workflows/engine.test.ts +1 -1
  797. package/src/workflows/engine.ts +22 -0
  798. package/src/workflows/fanout-load.test.ts +1 -1
  799. package/src/workflows/journal-store.test.ts +1 -1
  800. package/src/workflows/leaf-runner.test.ts +40 -1
  801. package/src/workflows/leaf-runner.ts +26 -1
  802. package/src/workspace/git-service.ts +144 -29
  803. package/src/workspace/migrations/109-swap-quality-profile-to-glm-5p2.ts +121 -0
  804. package/src/workspace/migrations/110-flip-balanced-profile-to-together.ts +82 -0
  805. package/src/workspace/migrations/registry.ts +4 -0
  806. package/src/workspace/migrations/runner.ts +32 -2
  807. package/src/__tests__/access-request-decision.test.ts +0 -375
  808. package/src/__tests__/guardian-grant-minting.test.ts +0 -607
  809. package/src/__tests__/plugin-source-watcher.test.ts +0 -302
  810. package/src/api/events/turn-profile-auto-routed.ts +0 -28
  811. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +0 -107
  812. package/src/daemon/plugin-source-watcher.ts +0 -278
  813. package/src/daemon/switch-inference-profile-tool.ts +0 -62
  814. package/src/memory/guardian-approvals.ts +0 -361
  815. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +0 -66
  816. package/src/memory/migrations/038-actor-token-records.ts +0 -45
  817. package/src/memory/migrations/039-actor-refresh-token-records.ts +0 -57
  818. package/src/memory/migrations/103-complex-migrations.ts +0 -23
  819. package/src/memory/migrations/113-late-migrations.ts +0 -30
  820. package/src/memory/migrations/index.ts +0 -301
  821. package/src/runtime/routes/access-request-decision.ts +0 -297
  822. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +0 -963
  823. package/src/runtime/routes/channel-guardian-routes.ts +0 -19
  824. package/src/runtime/routes/guardian-expiry-sweep.ts +0 -132
@@ -1,963 +0,0 @@
1
- /**
2
- * Guardian callback decision strategy: handles inbound messages from a
3
- * guardian sender who has pending guardian approval requests. Routes through
4
- * callback buttons or the conversational engine.
5
- */
6
- import { applyGuardianDecision } from "../../../approvals/guardian-decision-primitive.js";
7
- import type { ChannelId } from "../../../channels/types.js";
8
- import { findContactChannel } from "../../../contacts/contact-store.js";
9
- import {
10
- getAllPendingApprovalsByGuardianChat,
11
- getApprovalRequestById,
12
- getPendingApprovalByRequestAndGuardianChat,
13
- type GuardianApprovalRequest,
14
- } from "../../../memory/guardian-approvals.js";
15
- import { emitNotificationSignal } from "../../../notifications/emit-signal.js";
16
- import type { NotificationSourceChannel } from "../../../notifications/signal.js";
17
- import { getLogger } from "../../../util/logger.js";
18
- import { runApprovalConversationTurn } from "../../approval-conversation-turn.js";
19
- import { composeApprovalMessageGenerative } from "../../approval-message-composer.js";
20
- import type {
21
- ApprovalAction,
22
- ApprovalDecisionResult,
23
- } from "../../channel-approval-types.js";
24
- import { deliverChannelReply } from "../../gateway-client.js";
25
- import type {
26
- ApprovalConversationContext,
27
- ApprovalConversationGenerator,
28
- ApprovalCopyGenerator,
29
- } from "../../http-types.js";
30
- import {
31
- deliverVerificationCodeToGuardian,
32
- deliverVerificationCodeToRequester,
33
- type DeliveryResult,
34
- handleAccessRequestDecision,
35
- notifyRequesterOfApproval,
36
- notifyRequesterOfDeliveryFailure,
37
- notifyRequesterOfDenial,
38
- } from "../access-request-decision.js";
39
- import { parseCallbackData } from "../channel-route-shared.js";
40
- import {
41
- deliverIdentityMismatchReply,
42
- deliverStaleApprovalReply,
43
- } from "../guardian-approval-reply-helpers.js";
44
-
45
- const log = getLogger("runtime-http");
46
-
47
- /**
48
- * Resolve the Slack ephemeral user ID when the source channel is Slack.
49
- * Returns `undefined` for non-Slack channels.
50
- */
51
- function slackEphemeralUserId(
52
- sourceChannel: ChannelId,
53
- userId: string | undefined,
54
- ): string | undefined {
55
- return sourceChannel === "slack" && userId ? userId : undefined;
56
- }
57
-
58
- export interface GuardianCallbackDecisionParams {
59
- content: string;
60
- callbackData?: string;
61
- conversationExternalId: string;
62
- sourceChannel: ChannelId;
63
- actorExternalId: string;
64
- replyCallbackUrl: string;
65
- assistantId: string;
66
- approvalCopyGenerator?: ApprovalCopyGenerator;
67
- approvalConversationGenerator?: ApprovalConversationGenerator;
68
- /** Original approval message timestamp (Slack ts) for editing after resolution. */
69
- approvalMessageTs?: string;
70
- }
71
-
72
- export interface ApprovalInterceptionResult {
73
- handled: boolean;
74
- type?:
75
- | "decision_applied"
76
- | "assistant_turn"
77
- | "guardian_decision_applied"
78
- | "stale_ignored";
79
- }
80
-
81
- /**
82
- * Handle a guardian sender's message when there are pending guardian approval
83
- * requests targeting this chat. Returns `{ handled: true }` when the message
84
- * was consumed, or `null` when no guardian approval was found and the caller
85
- * should fall through to standard approval interception.
86
- */
87
- export async function handleGuardianCallbackDecision(
88
- params: GuardianCallbackDecisionParams,
89
- ): Promise<ApprovalInterceptionResult | null> {
90
- const {
91
- content,
92
- callbackData,
93
- conversationExternalId,
94
- sourceChannel,
95
- actorExternalId,
96
- replyCallbackUrl,
97
- assistantId,
98
- approvalCopyGenerator,
99
- approvalConversationGenerator,
100
- approvalMessageTs,
101
- } = params;
102
-
103
- // Reactions have their own deterministic emoji-to-action mapping in
104
- // `handleApprovalInterception`. Return null immediately so reaction
105
- // callbackData never enters the conversational engine below, which would
106
- // misclassify `reaction:white_check_mark` etc. as plain text and only
107
- // ever produce `approve_once`/`reject`.
108
- if (callbackData?.startsWith("reaction:")) {
109
- return null;
110
- }
111
-
112
- // Callback/button path: deterministic and takes priority.
113
- let callbackDecision: ApprovalDecisionResult | null = null;
114
- if (callbackData) {
115
- callbackDecision = parseCallbackData(callbackData, sourceChannel);
116
- }
117
-
118
- // When a callback button provides a request ID, use the scoped lookup so
119
- // the decision resolves to exactly the right approval even when
120
- // multiple approvals target the same guardian chat.
121
- let guardianApproval = callbackDecision?.requestId
122
- ? getPendingApprovalByRequestAndGuardianChat(
123
- callbackDecision.requestId,
124
- sourceChannel,
125
- conversationExternalId,
126
- )
127
- : null;
128
-
129
- // When the scoped lookup didn't resolve an approval (either because
130
- // there was no callback or the requestId pointed to a stale/expired request),
131
- // fall back to checking all pending approvals for this guardian chat.
132
- if (!guardianApproval && callbackDecision) {
133
- const allPending = getAllPendingApprovalsByGuardianChat(
134
- sourceChannel,
135
- conversationExternalId,
136
- );
137
- if (allPending.length === 1) {
138
- guardianApproval = allPending[0];
139
- } else if (allPending.length > 1) {
140
- // The callback targeted a stale/expired request but the guardian has other
141
- // pending approvals. Inform them the clicked approval is no longer valid.
142
- await deliverStaleApprovalReply({
143
- scenario: "guardian_disambiguation",
144
- sourceChannel,
145
- replyCallbackUrl,
146
- chatId: conversationExternalId,
147
- assistantId,
148
- approvalCopyGenerator,
149
- logger: log,
150
- errorLogMessage:
151
- "Failed to deliver stale callback disambiguation notice",
152
- extraContext: { pendingCount: allPending.length },
153
- errorLogContext: { conversationExternalId },
154
- ephemeralUserId: slackEphemeralUserId(sourceChannel, actorExternalId),
155
- });
156
- return { handled: true, type: "stale_ignored" };
157
- }
158
- }
159
-
160
- // For plain-text messages (no callback), check if there are any pending
161
- // approvals for this guardian chat to route through the conversation engine.
162
- if (!guardianApproval && !callbackDecision) {
163
- const allPending = getAllPendingApprovalsByGuardianChat(
164
- sourceChannel,
165
- conversationExternalId,
166
- );
167
- if (allPending.length === 1) {
168
- guardianApproval = allPending[0];
169
- } else if (allPending.length > 1) {
170
- // Multiple pending — pick the first approval matching this sender as
171
- // primary context. The conversation engine sees all matching approvals
172
- // via pendingApprovals and can disambiguate.
173
- guardianApproval =
174
- allPending.find((a) => a.guardianExternalUserId === actorExternalId) ??
175
- allPending[0];
176
- }
177
- }
178
-
179
- if (!guardianApproval) {
180
- return null;
181
- }
182
-
183
- // Validate that the sender is the specific guardian who was assigned
184
- // this approval request. This is a defense-in-depth check — the
185
- // trustClass check above already verifies the sender is a guardian,
186
- // but this catches edge cases like binding rotation between request
187
- // creation and decision.
188
- if (actorExternalId !== guardianApproval.guardianExternalUserId) {
189
- log.warn(
190
- {
191
- conversationExternalId,
192
- actorExternalId,
193
- expectedGuardian: guardianApproval.guardianExternalUserId,
194
- },
195
- "Non-guardian sender attempted to act on guardian approval request",
196
- );
197
- await deliverIdentityMismatchReply({
198
- sourceChannel,
199
- replyCallbackUrl,
200
- chatId: conversationExternalId,
201
- assistantId,
202
- approvalCopyGenerator,
203
- logger: log,
204
- errorLogMessage: "Failed to deliver guardian identity rejection notice",
205
- errorLogContext: { conversationExternalId },
206
- ephemeralUserId: slackEphemeralUserId(sourceChannel, actorExternalId),
207
- });
208
- return { handled: true, type: "guardian_decision_applied" };
209
- }
210
-
211
- if (callbackDecision) {
212
- return handleCallbackDecision({
213
- guardianApproval,
214
- callbackDecision,
215
- actorExternalId,
216
- sourceChannel,
217
- replyCallbackUrl,
218
- assistantId,
219
- approvalCopyGenerator,
220
- approvalMessageTs,
221
- });
222
- }
223
-
224
- // ── Conversational engine for guardian plain-text messages ──
225
- const allGuardianPending = getAllPendingApprovalsByGuardianChat(
226
- sourceChannel,
227
- conversationExternalId,
228
- );
229
- // Only present approvals that belong to this sender so the engine
230
- // does not offer disambiguation for requests assigned to a rotated
231
- // guardian the sender cannot act on.
232
- const senderPending = allGuardianPending.filter(
233
- (a) => a.guardianExternalUserId === actorExternalId,
234
- );
235
- const effectivePending =
236
- senderPending.length > 0 ? senderPending : allGuardianPending;
237
-
238
- if (effectivePending.length > 0 && content && approvalConversationGenerator) {
239
- return handleConversationalDecision({
240
- guardianApproval,
241
- allGuardianPending,
242
- effectivePending,
243
- actorExternalId,
244
- sourceChannel,
245
- conversationExternalId,
246
- replyCallbackUrl,
247
- content,
248
- assistantId,
249
- approvalCopyGenerator,
250
- approvalConversationGenerator,
251
- });
252
- }
253
-
254
- // Guardian sent a plain-text message with pending approvals but the
255
- // conversational engine is unavailable. Return a handled result with a
256
- // generative reply so the guardian gets feedback instead of the message being
257
- // silently swallowed by the standard approval flow.
258
- //
259
- // Exclude callback/reaction payloads (e.g. `reaction:+1`) — these carry
260
- // `callbackData` and must fall through so `handleApprovalInterception` can
261
- // route them to the deterministic reaction handler.
262
- if (effectivePending.length > 0 && content && !callbackData) {
263
- try {
264
- const text = await composeApprovalMessageGenerative(
265
- {
266
- scenario: "guardian_text_unavailable",
267
- channel: sourceChannel,
268
- },
269
- {},
270
- approvalCopyGenerator,
271
- );
272
- const fallbackPayload: Parameters<typeof deliverChannelReply>[1] = {
273
- chatId: conversationExternalId,
274
- text,
275
- assistantId,
276
- };
277
- const guardianFallbackEphemeral = slackEphemeralUserId(
278
- sourceChannel,
279
- actorExternalId,
280
- );
281
- if (guardianFallbackEphemeral) {
282
- fallbackPayload.ephemeral = true;
283
- fallbackPayload.user = guardianFallbackEphemeral;
284
- }
285
- await deliverChannelReply(replyCallbackUrl, fallbackPayload);
286
- } catch (err) {
287
- log.error(
288
- { err, conversationExternalId },
289
- "Failed to deliver guardian fallback reply",
290
- );
291
- }
292
- return { handled: true, type: "assistant_turn" };
293
- }
294
-
295
- // No content — nothing actionable.
296
- return null;
297
- }
298
-
299
- // ---------------------------------------------------------------------------
300
- // Callback decision handler
301
- // ---------------------------------------------------------------------------
302
-
303
- async function handleCallbackDecision(params: {
304
- guardianApproval: GuardianApprovalRequest;
305
- callbackDecision: ApprovalDecisionResult;
306
- actorExternalId: string;
307
- sourceChannel: ChannelId;
308
- replyCallbackUrl: string;
309
- assistantId: string;
310
- approvalCopyGenerator?: ApprovalCopyGenerator;
311
- approvalMessageTs?: string;
312
- }): Promise<ApprovalInterceptionResult> {
313
- const {
314
- guardianApproval,
315
- callbackDecision,
316
- actorExternalId,
317
- sourceChannel,
318
- replyCallbackUrl,
319
- assistantId,
320
- approvalCopyGenerator,
321
- approvalMessageTs,
322
- } = params;
323
-
324
- // Access request approvals don't have a pending interaction in the
325
- // session tracker, so they need a separate decision path that creates
326
- // a verification session instead of resuming an agent loop.
327
- if (guardianApproval.toolName === "ingress_access_request") {
328
- const accessResult = await handleAccessRequestApproval(
329
- guardianApproval,
330
- callbackDecision.action === "reject" ? "deny" : "approve",
331
- actorExternalId,
332
- replyCallbackUrl,
333
- assistantId,
334
- );
335
- return accessResult;
336
- }
337
-
338
- // Apply the decision through the unified guardian decision primitive.
339
- // The primitive handles approval info capture, record update, and scoped grant minting.
340
- const result = await applyGuardianDecision({
341
- approval: guardianApproval,
342
- decision: callbackDecision,
343
- actorPrincipalId: undefined, // Callback path — principal not available at this layer
344
- actorExternalUserId: actorExternalId, // Channel-native ID (Telegram user ID, phone, etc.)
345
- actorChannel: sourceChannel,
346
- });
347
-
348
- if (result.applied) {
349
- // Notify the requester's chat about the outcome with the tool name
350
- const decisionOutcome: "approved" | "denied" =
351
- callbackDecision.action === "reject" ? "denied" : "approved";
352
- const outcomeText = await composeApprovalMessageGenerative(
353
- {
354
- scenario: "guardian_decision_outcome",
355
- decision: decisionOutcome,
356
- toolName: guardianApproval.toolName,
357
- channel: sourceChannel,
358
- },
359
- {},
360
- approvalCopyGenerator,
361
- );
362
- try {
363
- const outcomePayload: Parameters<typeof deliverChannelReply>[1] = {
364
- chatId: guardianApproval.requesterChatId,
365
- text: outcomeText,
366
- assistantId,
367
- };
368
- const requesterEphemeral = slackEphemeralUserId(
369
- sourceChannel,
370
- guardianApproval.requesterExternalUserId,
371
- );
372
- if (requesterEphemeral) {
373
- outcomePayload.ephemeral = true;
374
- outcomePayload.user = requesterEphemeral;
375
- }
376
- await deliverChannelReply(replyCallbackUrl, outcomePayload);
377
- } catch (err) {
378
- log.error(
379
- { err, conversationId: guardianApproval.conversationId },
380
- "Failed to notify requester of guardian decision",
381
- );
382
- }
383
-
384
- // Edit the original Slack approval message to show the decision and
385
- // remove stale action buttons. This prevents users from clicking
386
- // buttons that have already been resolved.
387
- if (sourceChannel === "slack" && approvalMessageTs) {
388
- editSlackApprovalMessage({
389
- replyCallbackUrl,
390
- chatId: guardianApproval.guardianChatId,
391
- messageTs: approvalMessageTs,
392
- decision: decisionOutcome,
393
- assistantId,
394
- conversationId: guardianApproval.conversationId,
395
- });
396
- }
397
-
398
- // Post-decision delivery is handled by the onEvent callback
399
- // in the session that registered the pending interaction.
400
- return { handled: true, type: "guardian_decision_applied" };
401
- }
402
-
403
- // Race condition: callback arrived after request was already resolved.
404
- // On Slack, edit the original message to show it's resolved and remove
405
- // stale buttons so the guardian isn't left with actionable UI that does
406
- // nothing. Also send an ephemeral error message for visibility.
407
- if (sourceChannel === "slack" && approvalMessageTs) {
408
- // Re-read the approval from DB to get the actual resolved status.
409
- // The in-memory `guardianApproval` was loaded via a pending-status
410
- // filter and is still "pending" even though it was resolved by
411
- // another process.
412
- const refreshed = getApprovalRequestById(guardianApproval.id);
413
- const resolvedStatus =
414
- refreshed?.status === "approved" ? "approved" : "denied";
415
- editSlackApprovalMessage({
416
- replyCallbackUrl,
417
- chatId: guardianApproval.guardianChatId,
418
- messageTs: approvalMessageTs,
419
- decision: resolvedStatus,
420
- assistantId,
421
- conversationId: guardianApproval.conversationId,
422
- });
423
- }
424
-
425
- // Deliver a visible ephemeral error so the user sees feedback (JARVIS-299).
426
- if (sourceChannel === "slack") {
427
- try {
428
- await deliverChannelReply(replyCallbackUrl, {
429
- chatId: guardianApproval.guardianChatId,
430
- text: "This approval request has already been resolved.",
431
- assistantId,
432
- ephemeral: true,
433
- user: actorExternalId,
434
- });
435
- } catch (err) {
436
- log.error(
437
- { err, conversationId: guardianApproval.conversationId },
438
- "Failed to deliver stale approval ephemeral notice",
439
- );
440
- }
441
- }
442
-
443
- return { handled: true, type: "stale_ignored" };
444
- }
445
-
446
- // ---------------------------------------------------------------------------
447
- // Conversational engine decision handler
448
- // ---------------------------------------------------------------------------
449
-
450
- async function handleConversationalDecision(params: {
451
- guardianApproval: GuardianApprovalRequest;
452
- allGuardianPending: GuardianApprovalRequest[];
453
- effectivePending: GuardianApprovalRequest[];
454
- actorExternalId: string;
455
- sourceChannel: ChannelId;
456
- conversationExternalId: string;
457
- replyCallbackUrl: string;
458
- content: string;
459
- assistantId: string;
460
- approvalCopyGenerator?: ApprovalCopyGenerator;
461
- approvalConversationGenerator: ApprovalConversationGenerator;
462
- }): Promise<ApprovalInterceptionResult> {
463
- const {
464
- guardianApproval,
465
- allGuardianPending,
466
- effectivePending,
467
- actorExternalId,
468
- sourceChannel,
469
- conversationExternalId,
470
- replyCallbackUrl,
471
- content,
472
- assistantId,
473
- approvalCopyGenerator,
474
- approvalConversationGenerator,
475
- } = params;
476
-
477
- const guardianAllowedActions = ["approve_once", "reject"];
478
- const engineContext: ApprovalConversationContext = {
479
- toolName: guardianApproval.toolName,
480
- allowedActions: guardianAllowedActions,
481
- role: "guardian",
482
- pendingApprovals: effectivePending.map((a) => ({
483
- requestId: a.requestId!,
484
- toolName: a.toolName,
485
- })),
486
- userMessage: content,
487
- };
488
-
489
- const engineResult = await runApprovalConversationTurn(
490
- engineContext,
491
- approvalConversationGenerator,
492
- );
493
-
494
- if (engineResult.disposition === "keep_pending") {
495
- // Non-decision follow-up (clarification, disambiguation, etc.)
496
- try {
497
- const keepPendingPayload: Parameters<typeof deliverChannelReply>[1] = {
498
- chatId: conversationExternalId,
499
- text: engineResult.replyText,
500
- assistantId,
501
- };
502
- const guardianEphemeral = slackEphemeralUserId(
503
- sourceChannel,
504
- actorExternalId,
505
- );
506
- if (guardianEphemeral) {
507
- keepPendingPayload.ephemeral = true;
508
- keepPendingPayload.user = guardianEphemeral;
509
- }
510
- await deliverChannelReply(replyCallbackUrl, keepPendingPayload);
511
- } catch (err) {
512
- log.error(
513
- { err, conversationId: guardianApproval.conversationId },
514
- "Failed to deliver guardian conversation reply",
515
- );
516
- }
517
- return { handled: true, type: "assistant_turn" };
518
- }
519
-
520
- // Decision-bearing disposition from the engine
521
- const decisionAction = engineResult.disposition as ApprovalAction;
522
-
523
- // Resolve the target approval: use targetRequestId from the engine if
524
- // provided, otherwise use the single guardian approval.
525
- const targetApproval = engineResult.targetRequestId
526
- ? (allGuardianPending.find(
527
- (a) => a.requestId === engineResult.targetRequestId,
528
- ) ?? guardianApproval)
529
- : guardianApproval;
530
-
531
- // Re-validate guardian identity against the resolved target. The
532
- // engine may select a different pending approval (via targetRequestId)
533
- // that was assigned to a different guardian. Without this check a
534
- // currently bound guardian could act on a request assigned to a
535
- // previous guardian after a binding rotation.
536
- if (actorExternalId !== targetApproval.guardianExternalUserId) {
537
- log.warn(
538
- {
539
- conversationExternalId,
540
- actorExternalId,
541
- expectedGuardian: targetApproval.guardianExternalUserId,
542
- targetRequestId: engineResult.targetRequestId,
543
- },
544
- "Guardian identity mismatch on engine-selected target approval",
545
- );
546
- await deliverIdentityMismatchReply({
547
- sourceChannel,
548
- replyCallbackUrl,
549
- chatId: conversationExternalId,
550
- assistantId,
551
- approvalCopyGenerator,
552
- logger: log,
553
- errorLogMessage:
554
- "Failed to deliver guardian identity mismatch notice for engine target",
555
- errorLogContext: { conversationExternalId },
556
- ephemeralUserId: slackEphemeralUserId(sourceChannel, actorExternalId),
557
- });
558
- return { handled: true, type: "guardian_decision_applied" };
559
- }
560
-
561
- // Access request approvals need a separate decision path.
562
- if (targetApproval.toolName === "ingress_access_request") {
563
- const accessResult = await handleAccessRequestApproval(
564
- targetApproval,
565
- decisionAction === "reject" ? "deny" : "approve",
566
- actorExternalId,
567
- replyCallbackUrl,
568
- assistantId,
569
- );
570
- return accessResult;
571
- }
572
-
573
- const engineDecision: ApprovalDecisionResult = {
574
- action: decisionAction,
575
- source: "plain_text",
576
- ...(engineResult.targetRequestId
577
- ? { requestId: engineResult.targetRequestId }
578
- : {}),
579
- };
580
-
581
- // Apply the decision through the unified guardian decision primitive.
582
- const result = await applyGuardianDecision({
583
- approval: targetApproval,
584
- decision: engineDecision,
585
- actorPrincipalId: undefined, // Callback path — principal not available at this layer
586
- actorExternalUserId: actorExternalId, // Channel-native ID (Telegram user ID, phone, etc.)
587
- actorChannel: sourceChannel,
588
- });
589
-
590
- if (result.applied) {
591
- // Notify the requester's chat about the outcome
592
- const outcomeText = await composeApprovalMessageGenerative(
593
- {
594
- scenario: "guardian_decision_outcome",
595
- decision: decisionAction === "reject" ? "denied" : "approved",
596
- toolName: targetApproval.toolName,
597
- channel: sourceChannel,
598
- },
599
- {},
600
- approvalCopyGenerator,
601
- );
602
- try {
603
- const requesterOutcomePayload: Parameters<typeof deliverChannelReply>[1] =
604
- {
605
- chatId: targetApproval.requesterChatId,
606
- text: outcomeText,
607
- assistantId,
608
- };
609
- const requesterEphemeral = slackEphemeralUserId(
610
- sourceChannel,
611
- targetApproval.requesterExternalUserId,
612
- );
613
- if (requesterEphemeral) {
614
- requesterOutcomePayload.ephemeral = true;
615
- requesterOutcomePayload.user = requesterEphemeral;
616
- }
617
- await deliverChannelReply(replyCallbackUrl, requesterOutcomePayload);
618
- } catch (err) {
619
- log.error(
620
- { err, conversationId: targetApproval.conversationId },
621
- "Failed to notify requester of guardian decision",
622
- );
623
- }
624
-
625
- // Deliver the engine's reply to the guardian
626
- try {
627
- const guardianReplyPayload: Parameters<typeof deliverChannelReply>[1] = {
628
- chatId: conversationExternalId,
629
- text: engineResult.replyText,
630
- assistantId,
631
- };
632
- const guardianEphemeral = slackEphemeralUserId(
633
- sourceChannel,
634
- actorExternalId,
635
- );
636
- if (guardianEphemeral) {
637
- guardianReplyPayload.ephemeral = true;
638
- guardianReplyPayload.user = guardianEphemeral;
639
- }
640
- await deliverChannelReply(replyCallbackUrl, guardianReplyPayload);
641
- } catch (err) {
642
- log.error(
643
- { err, conversationId: targetApproval.conversationId },
644
- "Failed to deliver guardian decision reply",
645
- );
646
- }
647
-
648
- return { handled: true, type: "guardian_decision_applied" };
649
- }
650
-
651
- // Race condition: request was already resolved. Deliver a stale notice
652
- // instead of the engine's optimistic reply.
653
- await deliverStaleApprovalReply({
654
- scenario: "approval_already_resolved",
655
- sourceChannel,
656
- replyCallbackUrl,
657
- chatId: conversationExternalId,
658
- assistantId,
659
- approvalCopyGenerator,
660
- logger: log,
661
- errorLogMessage: "Failed to deliver stale guardian approval notice",
662
- errorLogContext: { conversationId: targetApproval.conversationId },
663
- ephemeralUserId: slackEphemeralUserId(sourceChannel, actorExternalId),
664
- });
665
-
666
- return { handled: true, type: "stale_ignored" };
667
- }
668
-
669
- // ---------------------------------------------------------------------------
670
- // Slack approval message edit helper
671
- // ---------------------------------------------------------------------------
672
-
673
- /**
674
- * Fire-and-forget: edit the original Slack approval message to show the
675
- * decision outcome and remove stale action buttons. Uses `chat.update` via
676
- * the gateway deliver endpoint with `messageTs`.
677
- *
678
- * The status line replaces the inline buttons so users see the result
679
- * inline without any actionable UI remaining.
680
- */
681
- function editSlackApprovalMessage(params: {
682
- replyCallbackUrl: string;
683
- chatId: string;
684
- messageTs: string;
685
- decision: "approved" | "denied";
686
- assistantId: string;
687
- conversationId: string;
688
- }): void {
689
- const {
690
- replyCallbackUrl,
691
- chatId,
692
- messageTs,
693
- decision,
694
- assistantId,
695
- conversationId,
696
- } = params;
697
-
698
- const statusEmoji = decision === "approved" ? "\u2713" : "\u2717";
699
- const statusLabel = decision === "approved" ? "Approved" : "Denied";
700
- const statusText = `${statusEmoji} ${statusLabel}`;
701
-
702
- // Build Block Kit blocks matching the resolved approval layout:
703
- // a section with the status text and a context line with the decision.
704
- // This replaces the original approval prompt's action buttons with a
705
- // read-only status display.
706
- const blocks = [
707
- {
708
- type: "section",
709
- text: { type: "mrkdwn", text: statusText },
710
- },
711
- {
712
- type: "context",
713
- elements: [{ type: "mrkdwn", text: `${statusEmoji} ${statusLabel}` }],
714
- },
715
- ];
716
-
717
- deliverChannelReply(replyCallbackUrl, {
718
- chatId,
719
- text: statusText,
720
- blocks,
721
- messageTs,
722
- assistantId,
723
- }).catch((err) => {
724
- log.error(
725
- { err, conversationId, messageTs },
726
- "Failed to edit Slack approval message after resolution",
727
- );
728
- });
729
- }
730
-
731
- // ---------------------------------------------------------------------------
732
- // Access request decision helper
733
- // ---------------------------------------------------------------------------
734
-
735
- /**
736
- * Handle a guardian's decision on an `ingress_access_request` approval.
737
- * Delegates to the access-request-decision module and orchestrates
738
- * notification delivery.
739
- *
740
- * On approve: creates a verification session, delivers the code to the
741
- * guardian, and notifies the requester to expect a code.
742
- *
743
- * On deny: marks the request as denied and notifies the requester.
744
- */
745
- async function handleAccessRequestApproval(
746
- approval: GuardianApprovalRequest,
747
- action: "approve" | "deny",
748
- decidedByExternalUserId: string,
749
- replyCallbackUrl: string,
750
- assistantId: string,
751
- ): Promise<ApprovalInterceptionResult> {
752
- const decisionResult = handleAccessRequestDecision(
753
- approval,
754
- action,
755
- decidedByExternalUserId,
756
- );
757
-
758
- if (decisionResult.type === "stale" || decisionResult.type === "idempotent") {
759
- return { handled: true, type: "stale_ignored" };
760
- }
761
-
762
- // Resolve display names from the contacts database for enriched payloads
763
- const requesterContactResult = approval.requesterExternalUserId
764
- ? findContactChannel({
765
- channelType: approval.channel,
766
- address: approval.requesterExternalUserId,
767
- })
768
- : null;
769
- const requesterDisplayName =
770
- requesterContactResult?.contact.displayName ?? null;
771
-
772
- const decidedByContactResult = decidedByExternalUserId
773
- ? findContactChannel({
774
- channelType: approval.channel,
775
- address: decidedByExternalUserId,
776
- })
777
- : null;
778
- const decidedByDisplayName =
779
- decidedByContactResult?.contact.displayName ?? null;
780
-
781
- if (decisionResult.type === "denied") {
782
- await notifyRequesterOfDenial({
783
- replyCallbackUrl,
784
- requesterChatId: approval.requesterChatId,
785
- assistantId,
786
- channel: approval.channel,
787
- requesterExternalUserId: approval.requesterExternalUserId,
788
- });
789
-
790
- // Emit both guardian_decision and denied signals so all lifecycle
791
- // observers are notified of the denial.
792
- const deniedPayload = {
793
- sourceChannel: approval.channel as NotificationSourceChannel,
794
- requesterExternalUserId: approval.requesterExternalUserId,
795
- requesterChatId: approval.requesterChatId,
796
- decidedByExternalUserId,
797
- requesterDisplayName,
798
- decidedByDisplayName,
799
- decision: "denied" as const,
800
- };
801
-
802
- void emitNotificationSignal({
803
- sourceEventName: "ingress.trusted_contact.guardian_decision",
804
- sourceChannel: approval.channel as NotificationSourceChannel,
805
- sourceContextId: approval.conversationId,
806
- attentionHints: {
807
- requiresAction: false,
808
- urgency: "medium",
809
- isAsyncBackground: false,
810
- visibleInSourceNow: false,
811
- },
812
- contextPayload: deniedPayload,
813
- dedupeKey: `trusted-contact:guardian-decision:${approval.id}`,
814
- });
815
-
816
- void emitNotificationSignal({
817
- sourceEventName: "ingress.trusted_contact.denied",
818
- sourceChannel: approval.channel as NotificationSourceChannel,
819
- sourceContextId: approval.conversationId,
820
- attentionHints: {
821
- requiresAction: false,
822
- urgency: "low",
823
- isAsyncBackground: false,
824
- visibleInSourceNow: false,
825
- },
826
- contextPayload: deniedPayload,
827
- dedupeKey: `trusted-contact:denied:${approval.id}`,
828
- });
829
-
830
- return { handled: true, type: "guardian_decision_applied" };
831
- }
832
-
833
- // Approved: deliver the verification code to the guardian and notify the requester.
834
- const requesterIdentifier =
835
- requesterDisplayName || approval.requesterExternalUserId;
836
-
837
- let codeDelivered = true;
838
- if (decisionResult.verificationCode) {
839
- const deliveryResult: DeliveryResult =
840
- await deliverVerificationCodeToGuardian({
841
- replyCallbackUrl,
842
- guardianChatId: approval.guardianChatId,
843
- requesterIdentifier,
844
- verificationCode: decisionResult.verificationCode,
845
- assistantId,
846
- });
847
- if (!deliveryResult.ok) {
848
- log.error(
849
- { reason: deliveryResult.reason, approvalId: approval.id },
850
- "Skipping requester notification — verification code was not delivered to guardian",
851
- );
852
- codeDelivered = false;
853
- }
854
- }
855
-
856
- // On Slack, auto-deliver the verification code directly to the requester's
857
- // DM so the guardian doesn't have to manually share it. The identity binding
858
- // still protects against abuse — only the bound user can consume the code.
859
- let requesterCodeDelivered = false;
860
- if (
861
- codeDelivered &&
862
- approval.channel === "slack" &&
863
- decisionResult.verificationCode
864
- ) {
865
- const requesterCodeResult = await deliverVerificationCodeToRequester({
866
- replyCallbackUrl,
867
- requesterChatId: approval.requesterChatId,
868
- verificationCode: decisionResult.verificationCode,
869
- assistantId,
870
- channel: approval.channel,
871
- requesterExternalUserId: approval.requesterExternalUserId,
872
- });
873
- if (requesterCodeResult.ok) {
874
- requesterCodeDelivered = true;
875
- } else {
876
- log.error(
877
- { reason: requesterCodeResult.reason, approvalId: approval.id },
878
- "Failed to auto-deliver verification code to requester on Slack",
879
- );
880
- }
881
- }
882
-
883
- // Skip the separate approval notification when the requester already
884
- // received the verification code directly (on Slack both messages go
885
- // to the same DM, so sending both is redundant).
886
- if (codeDelivered && !requesterCodeDelivered) {
887
- await notifyRequesterOfApproval({
888
- replyCallbackUrl,
889
- requesterChatId: approval.requesterChatId,
890
- assistantId,
891
- channel: approval.channel,
892
- requesterExternalUserId: approval.requesterExternalUserId,
893
- });
894
- } else if (!codeDelivered) {
895
- // Let the requester know something went wrong without revealing details
896
- await notifyRequesterOfDeliveryFailure({
897
- replyCallbackUrl,
898
- requesterChatId: approval.requesterChatId,
899
- assistantId,
900
- channel: approval.channel,
901
- requesterExternalUserId: approval.requesterExternalUserId,
902
- });
903
- }
904
-
905
- // Don't emit guardian_decision for approvals that still require code
906
- // verification — the guardian already received the code, and emitting
907
- // this signal prematurely causes the notification pipeline to deliver
908
- // a confusing "approved" message before the requester has verified.
909
- // The guardian_decision signal should only fire once access is fully granted
910
- // (i.e. after code consumption), which is handled in the verification path.
911
- if (!decisionResult.verificationSessionId) {
912
- void emitNotificationSignal({
913
- sourceEventName: "ingress.trusted_contact.guardian_decision",
914
- sourceChannel: approval.channel as NotificationSourceChannel,
915
- sourceContextId: approval.conversationId,
916
- attentionHints: {
917
- requiresAction: false,
918
- urgency: "medium",
919
- isAsyncBackground: false,
920
- visibleInSourceNow: false,
921
- },
922
- contextPayload: {
923
- sourceChannel: approval.channel as NotificationSourceChannel,
924
- requesterExternalUserId: approval.requesterExternalUserId,
925
- requesterChatId: approval.requesterChatId,
926
- decidedByExternalUserId,
927
- requesterDisplayName,
928
- decidedByDisplayName,
929
- decision: "approved",
930
- },
931
- dedupeKey: `trusted-contact:guardian-decision:${approval.id}`,
932
- });
933
- }
934
-
935
- // Emit verification_sent with visibleInSourceNow=true so the notification
936
- // pipeline suppresses delivery — the guardian already received the
937
- // verification code directly. Without this flag, the pipeline generates
938
- // a redundant LLM message like "Good news! Your request has been approved."
939
- if (decisionResult.verificationSessionId && codeDelivered) {
940
- void emitNotificationSignal({
941
- sourceEventName: "ingress.trusted_contact.verification_sent",
942
- sourceChannel: approval.channel as NotificationSourceChannel,
943
- sourceContextId: approval.conversationId,
944
- attentionHints: {
945
- requiresAction: false,
946
- urgency: "low",
947
- isAsyncBackground: true,
948
- visibleInSourceNow: true,
949
- },
950
- contextPayload: {
951
- sourceChannel: approval.channel as NotificationSourceChannel,
952
- requesterExternalUserId: approval.requesterExternalUserId,
953
- requesterChatId: approval.requesterChatId,
954
- requesterDisplayName,
955
- decidedByDisplayName,
956
- verificationSessionId: decisionResult.verificationSessionId,
957
- },
958
- dedupeKey: `trusted-contact:verification-sent:${decisionResult.verificationSessionId}`,
959
- });
960
- }
961
-
962
- return { handled: true, type: "guardian_decision_applied" };
963
- }