@vellumai/assistant 0.9.0 → 0.10.0-dev.202606192226.8ed8a6a

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 (624) hide show
  1. package/ARCHITECTURE.md +19 -35
  2. package/bun.lock +7 -8
  3. package/docs/activation-funnel-telemetry.md +28 -22
  4. package/docs/architecture/security.md +29 -28
  5. package/docs/stt-provider-onboarding.md +3 -5
  6. package/docs/workflows-testing.md +13 -44
  7. package/docs/workflows.md +3 -5
  8. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
  9. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
  10. package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
  11. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  12. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  13. package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
  14. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
  15. package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
  16. package/openapi.yaml +1128 -119
  17. package/package.json +2 -1
  18. package/scripts/sync-llm-catalog.ts +6 -15
  19. package/scripts/sync-web-search-catalog.ts +3 -11
  20. package/src/__tests__/access-request-card-view.test.ts +98 -0
  21. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  22. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
  23. package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
  24. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  25. package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
  26. package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
  27. package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
  28. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  29. package/src/__tests__/app-compiler.test.ts +15 -1
  30. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  31. package/src/__tests__/approval-cascade.test.ts +6 -0
  32. package/src/__tests__/approval-routes-http.test.ts +128 -0
  33. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  34. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  35. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  36. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  37. package/src/__tests__/btw-routes.test.ts +6 -8
  38. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  39. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  40. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  41. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  42. package/src/__tests__/checker.test.ts +0 -3
  43. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  44. package/src/__tests__/compaction-events.test.ts +6 -0
  45. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  46. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  47. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  48. package/src/__tests__/config-schema.test.ts +35 -0
  49. package/src/__tests__/config-watcher.test.ts +0 -18
  50. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  51. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  52. package/src/__tests__/contacts-tools.test.ts +29 -0
  53. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  54. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  56. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  57. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
  59. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  60. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  61. package/src/__tests__/conversation-process-callsite.test.ts +6 -0
  62. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  63. package/src/__tests__/conversation-queue.test.ts +6 -0
  64. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  65. package/src/__tests__/conversation-slash-queue.test.ts +6 -0
  66. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  67. package/src/__tests__/conversation-speed-override.test.ts +6 -0
  68. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  69. package/src/__tests__/conversation-title-service.test.ts +62 -0
  70. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
  71. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  72. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  73. package/src/__tests__/credential-broker.test.ts +449 -1
  74. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  75. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  76. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  77. package/src/__tests__/credential-routes.test.ts +360 -0
  78. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  79. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  80. package/src/__tests__/disk-pressure-tools.test.ts +41 -1
  81. package/src/__tests__/disk-usage.test.ts +65 -0
  82. package/src/__tests__/document-tool-security.test.ts +9 -6
  83. package/src/__tests__/document-update-default-surface.test.ts +202 -0
  84. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  85. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  86. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  87. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  88. package/src/__tests__/gemini-count-tokens.test.ts +70 -0
  89. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  90. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  91. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  92. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  93. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  94. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  95. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  96. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  97. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  98. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  99. package/src/__tests__/identity-routes.test.ts +0 -189
  100. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  101. package/src/__tests__/injector-disk-pressure.test.ts +10 -5
  102. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  103. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  104. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  105. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  106. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  107. package/src/__tests__/llm-resolver.test.ts +70 -24
  108. package/src/__tests__/llm-schema.test.ts +1 -0
  109. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  110. package/src/__tests__/mcp-health-check.test.ts +6 -7
  111. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  112. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  113. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  114. package/src/__tests__/path-policy.test.ts +34 -0
  115. package/src/__tests__/persona-resolver.test.ts +49 -14
  116. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  118. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  119. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  120. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  121. package/src/__tests__/reaction-persistence.test.ts +150 -29
  122. package/src/__tests__/registry.test.ts +2 -7
  123. package/src/__tests__/relay-server.test.ts +285 -0
  124. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  125. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  126. package/src/__tests__/schedule-routes.test.ts +0 -30
  127. package/src/__tests__/schedule-tools.test.ts +2 -18
  128. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  129. package/src/__tests__/secret-response-routing.test.ts +35 -0
  130. package/src/__tests__/secret-routes-platform-proxy.test.ts +16 -2
  131. package/src/__tests__/skill-execute-input.test.ts +177 -1
  132. package/src/__tests__/skill-load-tool.test.ts +72 -0
  133. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  134. package/src/__tests__/skills.test.ts +51 -0
  135. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  136. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  137. package/src/__tests__/subagent-tools.test.ts +266 -0
  138. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  139. package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
  140. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  141. package/src/__tests__/title-generate-hook.test.ts +100 -3
  142. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  143. package/src/__tests__/token-manager.test.ts +519 -0
  144. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  145. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  146. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  147. package/src/__tests__/tool-executor.test.ts +0 -79
  148. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  149. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  150. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  151. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  152. package/src/__tests__/twilio-routes.test.ts +81 -1
  153. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  154. package/src/__tests__/weak-open-model.test.ts +30 -0
  155. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  156. package/src/__tests__/workspace-greetings.test.ts +152 -0
  157. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  158. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  159. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  160. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  161. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  162. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  163. package/src/agent/loop.ts +75 -29
  164. package/src/api/README.md +6 -6
  165. package/src/api/events/tool-result.ts +6 -0
  166. package/src/api/events/workflow-completed.ts +53 -0
  167. package/src/api/events/workflow-leaf-finished.ts +38 -0
  168. package/src/api/events/workflow-leaf-started.ts +35 -0
  169. package/src/api/events/workflow-progress.ts +32 -0
  170. package/src/api/events/workflow-started.ts +31 -0
  171. package/src/api/index.ts +40 -0
  172. package/src/api/responses/conversation-message.ts +28 -4
  173. package/src/api/responses/home.ts +26 -4
  174. package/src/api/responses/workflow-journal.ts +53 -0
  175. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  176. package/src/approvals/guardian-decision-primitive.ts +26 -3
  177. package/src/approvals/guardian-request-resolvers.ts +183 -80
  178. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  179. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  180. package/src/calls/call-pointer-messages.ts +10 -4
  181. package/src/calls/channel-admission-reader.ts +104 -0
  182. package/src/calls/guardian-dispatch.ts +17 -45
  183. package/src/calls/media-stream-server.ts +84 -2
  184. package/src/calls/relay-access-wait.ts +1 -1
  185. package/src/calls/relay-server.ts +66 -0
  186. package/src/calls/relay-setup-router.ts +82 -1
  187. package/src/calls/twilio-routes.ts +17 -8
  188. package/src/calls/voice-session-bridge.ts +2 -2
  189. package/src/cli/commands/clients.ts +3 -0
  190. package/src/cli/commands/db/status.ts +37 -3
  191. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  192. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  193. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  194. package/src/cli/commands/memory/index.ts +30 -0
  195. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  196. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  197. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  198. package/src/cli/commands/oauth/status.test.ts +36 -0
  199. package/src/cli/commands/oauth/status.ts +23 -3
  200. package/src/cli/commands/plugins.ts +400 -14
  201. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  202. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  203. package/src/cli/lib/__tests__/install-from-github.test.ts +31 -0
  204. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  205. package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
  206. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  207. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  208. package/src/cli/lib/diff-plugin.ts +346 -0
  209. package/src/cli/lib/inspect-plugin.ts +12 -1
  210. package/src/cli/lib/install-from-github.ts +128 -19
  211. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  212. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  213. package/src/cli/lib/plugin-marketplace.ts +11 -0
  214. package/src/cli/lib/plugin-pin-history.ts +257 -0
  215. package/src/cli/lib/plugin-surfaces.ts +104 -0
  216. package/src/cli/lib/upgrade-plugin.ts +298 -10
  217. package/src/cli/program.ts +2 -6
  218. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  219. package/src/config/assistant-feature-flags.ts +22 -7
  220. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  221. package/src/config/bundled-skills/document-editor/SKILL.md +3 -3
  222. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  223. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  224. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  225. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  226. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  227. package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
  228. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  229. package/src/config/bundled-tool-registry.ts +2 -7
  230. package/src/config/call-site-defaults.ts +15 -2
  231. package/src/config/feature-flag-registry.json +46 -31
  232. package/src/config/inference-profile-validation.ts +26 -0
  233. package/src/config/llm-resolver.ts +3 -0
  234. package/src/config/loader.ts +4 -0
  235. package/src/config/memory-v3-gate.ts +11 -0
  236. package/src/config/profile-order.ts +28 -0
  237. package/src/config/schema.ts +8 -6
  238. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  239. package/src/config/schemas/call-site-catalog.ts +7 -0
  240. package/src/config/schemas/channels.ts +11 -0
  241. package/src/config/schemas/elevenlabs.ts +0 -1
  242. package/src/config/schemas/llm.ts +31 -0
  243. package/src/config/schemas/memory-lifecycle.ts +3 -7
  244. package/src/config/schemas/memory-v3.ts +6 -0
  245. package/src/config/schemas/platform.ts +0 -8
  246. package/src/config/schemas/services.ts +18 -0
  247. package/src/config/seed-inference-profiles.ts +109 -44
  248. package/src/config/skills.ts +21 -0
  249. package/src/config/sync-gated-profiles.ts +220 -0
  250. package/src/contacts/contact-store.ts +89 -106
  251. package/src/contacts/contacts-write.ts +5 -22
  252. package/src/contacts/types.ts +0 -1
  253. package/src/context/compactor.ts +88 -54
  254. package/src/context/strip-injections.ts +58 -10
  255. package/src/context/token-estimator.ts +1 -1
  256. package/src/credential-execution/process-manager.ts +55 -14
  257. package/src/credential-execution/prompted-credential.ts +2 -3
  258. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  259. package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
  260. package/src/daemon/config-watcher.ts +0 -4
  261. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  262. package/src/daemon/conversation-agent-loop.ts +114 -22
  263. package/src/daemon/conversation-history.ts +1 -1
  264. package/src/daemon/conversation-lifecycle.ts +3 -5
  265. package/src/daemon/conversation-process.ts +13 -5
  266. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  267. package/src/daemon/conversation-slash.ts +2 -23
  268. package/src/daemon/conversation-surfaces.ts +26 -0
  269. package/src/daemon/conversation-tool-setup.ts +53 -19
  270. package/src/daemon/conversation.ts +131 -20
  271. package/src/daemon/disk-pressure-policy.ts +5 -3
  272. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  273. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  274. package/src/daemon/handlers/config-a2a.ts +0 -2
  275. package/src/daemon/handlers/config-channels.ts +15 -16
  276. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  277. package/src/daemon/handlers/conversations.ts +107 -0
  278. package/src/daemon/host-browser-proxy.ts +41 -0
  279. package/src/daemon/lifecycle.ts +55 -27
  280. package/src/daemon/memory-v2-startup.test.ts +131 -0
  281. package/src/daemon/memory-v2-startup.ts +100 -1
  282. package/src/daemon/message-provenance.ts +2 -0
  283. package/src/daemon/message-types/contacts.ts +0 -1
  284. package/src/daemon/message-types/conversations.ts +3 -3
  285. package/src/daemon/message-types/sync.ts +0 -1
  286. package/src/daemon/message-types/web-activity.ts +7 -1
  287. package/src/daemon/message-types/workflows.ts +83 -1
  288. package/src/daemon/orphan-reaper.test.ts +0 -19
  289. package/src/daemon/orphan-reaper.ts +2 -24
  290. package/src/daemon/server.ts +0 -10
  291. package/src/daemon/tool-setup-types.ts +4 -0
  292. package/src/daemon/trust-context.ts +1 -1
  293. package/src/daemon/wake-conversation-ops.ts +1 -0
  294. package/src/events/tool-audit-listener.ts +2 -2
  295. package/src/home/feed-source-enrichment.test.ts +151 -0
  296. package/src/home/feed-source-enrichment.ts +176 -0
  297. package/src/home/relationship-state.ts +2 -4
  298. package/src/instrument.ts +18 -6
  299. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  300. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  301. package/src/ipc/assistant-server.ts +37 -4
  302. package/src/ipc/gateway-flag-listener.ts +18 -2
  303. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  304. package/src/memory/__tests__/db-logs-attach.test.ts +122 -0
  305. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  306. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  307. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  308. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  309. package/src/memory/auth-fallback-events-store.ts +2 -2
  310. package/src/memory/auto-analysis-enqueue.ts +3 -5
  311. package/src/memory/bookmark-crud.ts +1 -2
  312. package/src/memory/canonical-guardian-store.ts +39 -1
  313. package/src/memory/conversation-crud.ts +9 -4
  314. package/src/memory/conversation-key-store.ts +17 -2
  315. package/src/memory/conversation-title-service.ts +64 -7
  316. package/src/memory/db-async-query.ts +20 -2
  317. package/src/memory/db-connection.ts +49 -5
  318. package/src/memory/db-init.ts +17 -17
  319. package/src/memory/embedding-backend.ts +38 -1
  320. package/src/memory/embedding-billing-breaker.ts +96 -0
  321. package/src/memory/jobs-store.ts +25 -13
  322. package/src/memory/jobs-worker.ts +54 -1
  323. package/src/memory/lifecycle-events-store.ts +2 -2
  324. package/src/memory/memory-retrospective-constants.ts +4 -4
  325. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  326. package/src/memory/memory-retrospective-job.ts +28 -227
  327. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  328. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  329. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  330. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  331. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  332. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  333. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  334. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  335. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  336. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  337. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  338. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  339. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  340. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  341. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  342. package/src/memory/migrations/index.ts +6 -0
  343. package/src/memory/migrations/run-migrations.ts +41 -0
  344. package/src/memory/migrations/validate-migration-state.ts +1 -1
  345. package/src/memory/onboarding-events-store.ts +3 -3
  346. package/src/memory/schema/contacts.ts +0 -5
  347. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  348. package/src/memory/skill-loaded-events-store.ts +2 -2
  349. package/src/memory/tool-executed-events-store.test.ts +7 -7
  350. package/src/memory/turn-trace-store.test.ts +736 -0
  351. package/src/memory/turn-trace-store.ts +364 -0
  352. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  353. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  354. package/src/memory/v2/consolidation-job.ts +2 -2
  355. package/src/memory/v2/skill-content.ts +25 -7
  356. package/src/memory/v2/skill-store.ts +7 -1
  357. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  358. package/src/memory/v3-eval/eval-packets.ts +546 -0
  359. package/src/messaging/providers/slack/adapter.ts +1 -1
  360. package/src/messaging/providers/slack/api.ts +31 -0
  361. package/src/messaging/providers/slack/send.test.ts +114 -2
  362. package/src/messaging/providers/slack/send.ts +30 -7
  363. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  364. package/src/messaging/providers/slack/withdraw.ts +161 -0
  365. package/src/notifications/AGENTS.md +2 -0
  366. package/src/notifications/access-request-copy.ts +72 -59
  367. package/src/notifications/adapters/shared.ts +29 -0
  368. package/src/notifications/adapters/slack.ts +58 -103
  369. package/src/notifications/adapters/telegram.ts +2 -20
  370. package/src/notifications/approval-card-data.ts +333 -0
  371. package/src/notifications/broadcaster.ts +16 -3
  372. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  373. package/src/notifications/copy-composer.ts +3 -3
  374. package/src/notifications/decision-engine.ts +4 -2
  375. package/src/notifications/destination-resolver.ts +4 -6
  376. package/src/notifications/guardian-question-mode.ts +10 -0
  377. package/src/notifications/home-feed-side-effect.ts +7 -16
  378. package/src/notifications/notification-utils.ts +19 -20
  379. package/src/notifications/signal.ts +79 -43
  380. package/src/notifications/types.ts +98 -121
  381. package/src/oauth/AGENTS.md +5 -24
  382. package/src/permissions/checker.test.ts +51 -0
  383. package/src/permissions/checker.ts +185 -26
  384. package/src/permissions/ipc-risk-types.ts +24 -0
  385. package/src/permissions/question-prompter.test.ts +27 -0
  386. package/src/permissions/question-prompter.ts +4 -0
  387. package/src/permissions/secret-prompter.ts +15 -1
  388. package/src/platform/client.test.ts +119 -0
  389. package/src/platform/client.ts +66 -0
  390. package/src/platform/consent-cache.test.ts +267 -0
  391. package/src/platform/consent-cache.ts +174 -0
  392. package/src/plugin-api/constants.ts +1 -1
  393. package/src/plugin-api/index.ts +33 -1
  394. package/src/plugin-api/model-profiles.ts +33 -0
  395. package/src/plugin-api/types.ts +50 -2
  396. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  397. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  398. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  399. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  400. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  401. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  402. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  403. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  404. package/src/plugins/defaults/advisor/config.ts +21 -0
  405. package/src/plugins/defaults/advisor/consult.ts +93 -0
  406. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  407. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  408. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  409. package/src/plugins/defaults/advisor/package.json +14 -0
  410. package/src/plugins/defaults/advisor/steering.ts +67 -0
  411. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  412. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  413. package/src/plugins/defaults/compaction/window-manager.ts +18 -0
  414. package/src/plugins/defaults/index.ts +60 -0
  415. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  416. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  417. package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
  418. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  419. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  420. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  421. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  422. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  423. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  424. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  425. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  426. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  427. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  428. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  429. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  430. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  431. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  432. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  433. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  434. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  435. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  436. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  437. package/src/prompts/persona-resolver.ts +14 -4
  438. package/src/prompts/templates/system-sections.ts +7 -2
  439. package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
  440. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  441. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  442. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  443. package/src/providers/anthropic/client.ts +230 -175
  444. package/src/providers/atlascloud/client.ts +85 -0
  445. package/src/providers/call-site-routing.ts +10 -0
  446. package/src/providers/fetch-provider-catalog.ts +85 -0
  447. package/src/providers/gemini/client.ts +43 -0
  448. package/src/providers/inference/adapter-factory.ts +3 -0
  449. package/src/providers/model-catalog.ts +58 -0
  450. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  451. package/src/providers/openai/chat-completions-provider.ts +7 -0
  452. package/src/providers/openai/responses-provider.ts +10 -0
  453. package/src/providers/provider-send-message.ts +11 -3
  454. package/src/providers/ratelimit.ts +9 -0
  455. package/src/providers/retry.ts +62 -12
  456. package/src/providers/search-provider-catalog.ts +10 -0
  457. package/src/providers/types.ts +16 -0
  458. package/src/providers/usage-tracking.ts +7 -0
  459. package/src/providers/weak-open-model.ts +22 -0
  460. package/src/runtime/AGENTS.md +0 -1
  461. package/src/runtime/__tests__/agent-wake.test.ts +227 -0
  462. package/src/runtime/__tests__/client-health.test.ts +44 -0
  463. package/src/runtime/access-request-helper.ts +21 -53
  464. package/src/runtime/actor-trust-resolver.ts +59 -63
  465. package/src/runtime/agent-wake.ts +71 -0
  466. package/src/runtime/assistant-event-hub.ts +18 -4
  467. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  468. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  469. package/src/runtime/btw-sidechain.ts +3 -6
  470. package/src/runtime/capabilities.test.ts +120 -0
  471. package/src/runtime/capabilities.ts +197 -0
  472. package/src/runtime/channel-approval-types.ts +22 -45
  473. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  474. package/src/runtime/channel-retry-sweep.ts +1 -0
  475. package/src/runtime/channel-verification-service.ts +3 -3
  476. package/src/runtime/client-health.ts +26 -0
  477. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  478. package/src/runtime/effective-capabilities.test.ts +128 -0
  479. package/src/runtime/effective-capabilities.ts +84 -0
  480. package/src/runtime/guardian-reply-router.ts +106 -21
  481. package/src/runtime/invite-redemption-service.ts +9 -25
  482. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +126 -0
  483. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  484. package/src/runtime/pending-interactions.ts +35 -0
  485. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  486. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  487. package/src/runtime/routes/__tests__/plugins-routes.test.ts +383 -1
  488. package/src/runtime/routes/app-routes.ts +1 -1
  489. package/src/runtime/routes/approval-routes.ts +40 -9
  490. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  491. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  492. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  493. package/src/runtime/routes/btw-routes.ts +1 -27
  494. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  495. package/src/runtime/routes/client-routes.ts +10 -0
  496. package/src/runtime/routes/contact-routes.ts +31 -8
  497. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  498. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  499. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  500. package/src/runtime/routes/conversation-routes.ts +39 -14
  501. package/src/runtime/routes/credential-routes.ts +40 -16
  502. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  503. package/src/runtime/routes/events-routes.ts +1 -3
  504. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  505. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  506. package/src/runtime/routes/home-feed-routes.ts +8 -3
  507. package/src/runtime/routes/identity-routes.ts +1 -296
  508. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  509. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  510. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  511. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  512. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  513. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  514. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  515. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  516. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  517. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  518. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  519. package/src/runtime/routes/index.ts +2 -0
  520. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  521. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  522. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  523. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  524. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  525. package/src/runtime/routes/notification-routes.ts +122 -133
  526. package/src/runtime/routes/platform-routes.ts +2 -2
  527. package/src/runtime/routes/plugins-routes.ts +366 -11
  528. package/src/runtime/routes/schedule-routes.ts +0 -22
  529. package/src/runtime/routes/secret-routes.ts +15 -0
  530. package/src/runtime/routes/surface-action-routes.ts +2 -1
  531. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  532. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  533. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  534. package/src/runtime/routes/workflow-routes.ts +131 -29
  535. package/src/runtime/routes/workspace-greetings.ts +55 -0
  536. package/src/runtime/sync/resource-sync-events.ts +1 -11
  537. package/src/runtime/tool-grant-request-helper.ts +18 -16
  538. package/src/runtime/trust-context-resolver.ts +8 -5
  539. package/src/schedule/inference-profile.ts +2 -14
  540. package/src/schedule/schedule-store.ts +1 -1
  541. package/src/schedule/scheduler-types.ts +5 -1
  542. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  543. package/src/security/secret-patterns.ts +3 -0
  544. package/src/subagent/manager.ts +17 -4
  545. package/src/subagent/types.ts +6 -0
  546. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  547. package/src/telemetry/trace-collection-policy.ts +30 -0
  548. package/src/telemetry/types.ts +89 -0
  549. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  550. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  551. package/src/tools/AGENTS.md +3 -3
  552. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  553. package/src/tools/browser/browser-execution.ts +30 -19
  554. package/src/tools/document/document-tool.ts +30 -6
  555. package/src/tools/executor.ts +5 -3
  556. package/src/tools/host-terminal/host-shell.ts +5 -4
  557. package/src/tools/memory/register.ts +2 -2
  558. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  559. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  560. package/src/tools/network/web-fetch.ts +372 -1
  561. package/src/tools/network/web-search-error.ts +1 -1
  562. package/src/tools/network/web-search.ts +213 -10
  563. package/src/tools/permission-checker.ts +4 -3
  564. package/src/tools/registry.ts +20 -0
  565. package/src/tools/schedule/create.ts +7 -12
  566. package/src/tools/schedule/update.ts +4 -11
  567. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  568. package/src/tools/side-effects.ts +2 -17
  569. package/src/tools/skills/execute.ts +99 -2
  570. package/src/tools/skills/load.ts +43 -2
  571. package/src/tools/subagent/spawn.ts +64 -15
  572. package/src/tools/terminal/shell.ts +10 -4
  573. package/src/tools/tool-approval-handler.ts +18 -13
  574. package/src/tools/tool-manifest.ts +0 -2
  575. package/src/tools/types.ts +10 -0
  576. package/src/tools/ui-surface/definitions.ts +64 -3
  577. package/src/tools/verification-control-plane-policy.ts +3 -1
  578. package/src/tools/workflows/run-workflow.test.ts +8 -18
  579. package/src/tools/workflows/run-workflow.ts +1 -0
  580. package/src/util/disk-usage.ts +78 -23
  581. package/src/util/logs-db-path.ts +22 -0
  582. package/src/util/platform.ts +10 -3
  583. package/src/watcher/telemetry.ts +2 -2
  584. package/src/workflows/capabilities.ts +2 -3
  585. package/src/workflows/engine.test.ts +175 -1
  586. package/src/workflows/engine.ts +82 -0
  587. package/src/workflows/journal-store.test.ts +70 -0
  588. package/src/workflows/journal-store.ts +18 -3
  589. package/src/workflows/run-manager.test.ts +171 -28
  590. package/src/workflows/run-manager.ts +66 -24
  591. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  592. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  593. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  594. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  595. package/src/workspace/migrations/registry.ts +8 -0
  596. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  597. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  598. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  599. package/src/__tests__/credential-vault.test.ts +0 -1706
  600. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  601. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  602. package/src/cli/commands/__tests__/task.test.ts +0 -914
  603. package/src/cli/commands/task.ts +0 -771
  604. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  605. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  606. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  607. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  608. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  609. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  610. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  611. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  612. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  613. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  614. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  615. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  616. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  617. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  618. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  619. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  620. package/src/memory/preloaded-apps.ts +0 -116
  621. package/src/notifications/tool-approval-copy.ts +0 -142
  622. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  623. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  624. package/src/tools/credentials/vault.ts +0 -712
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Tests for render-time enrichment of history tool calls with the in-flight
3
+ * `ask_question` prompt read from the pending-interactions registry.
4
+ */
5
+ import { afterEach, describe, expect, test } from "bun:test";
6
+
7
+ import type { QuestionEntry } from "../../api/events/question-request.js";
8
+ import type { ConversationMessageToolCall } from "../../api/responses/conversation-message.js";
9
+ import { clear, register } from "../pending-interactions.js";
10
+ import {
11
+ collectPendingQuestions,
12
+ enrichToolCallsWithQuestion,
13
+ } from "./tool-call-question-enrichment.js";
14
+
15
+ function toolCall(
16
+ overrides: Partial<ConversationMessageToolCall>,
17
+ ): ConversationMessageToolCall {
18
+ return {
19
+ name: "ask_question",
20
+ input: {},
21
+ ...overrides,
22
+ };
23
+ }
24
+
25
+ const ENTRIES: QuestionEntry[] = [
26
+ {
27
+ id: "q1",
28
+ question: "What's the email about?",
29
+ options: [
30
+ { id: "a", label: "iOS app is live" },
31
+ { id: "b", label: "Open source" },
32
+ ],
33
+ },
34
+ ];
35
+
36
+ afterEach(() => {
37
+ clear();
38
+ });
39
+
40
+ describe("collectPendingQuestions", () => {
41
+ test("keys question interactions by toolUseId", () => {
42
+ // GIVEN a question interaction registered for a conversation with a
43
+ // tool-use id and the full question entries
44
+ register("req-1", {
45
+ conversationId: "conv-1",
46
+ kind: "question",
47
+ toolUseId: "tool-abc",
48
+ questionDetails: { entries: ENTRIES },
49
+ });
50
+
51
+ // WHEN we collect the conversation's pending questions
52
+ const byToolUseId = collectPendingQuestions("conv-1");
53
+
54
+ // THEN the interaction is keyed by its tool-use id
55
+ expect(byToolUseId.size).toBe(1);
56
+ expect(byToolUseId.get("tool-abc")?.requestId).toBe("req-1");
57
+ expect(byToolUseId.get("tool-abc")?.entries).toEqual(ENTRIES);
58
+ });
59
+
60
+ test("ignores interactions lacking a toolUseId, details, or the question kind", () => {
61
+ // GIVEN a question without a toolUseId, a question without details, AND a
62
+ // non-question interaction in the same conversation
63
+ register("req-no-tool", {
64
+ conversationId: "conv-2",
65
+ kind: "question",
66
+ questionDetails: { entries: ENTRIES },
67
+ });
68
+ register("req-no-details", {
69
+ conversationId: "conv-2",
70
+ kind: "question",
71
+ toolUseId: "tool-no-details",
72
+ });
73
+ register("req-confirmation", {
74
+ conversationId: "conv-2",
75
+ kind: "confirmation",
76
+ toolUseId: "tool-xyz",
77
+ confirmationDetails: {
78
+ toolName: "file_read",
79
+ input: {},
80
+ riskLevel: "low",
81
+ allowlistOptions: [],
82
+ scopeOptions: [],
83
+ },
84
+ });
85
+
86
+ // WHEN we collect the conversation's pending questions
87
+ const byToolUseId = collectPendingQuestions("conv-2");
88
+
89
+ // THEN none is included
90
+ expect(byToolUseId.size).toBe(0);
91
+ });
92
+ });
93
+
94
+ describe("enrichToolCallsWithQuestion", () => {
95
+ test("stamps the pending question when the registry has a match", () => {
96
+ // GIVEN a registry entry matching the tool call by id
97
+ register("req-1", {
98
+ conversationId: "conv-fixture",
99
+ kind: "question",
100
+ toolUseId: "tool-abc",
101
+ questionDetails: { entries: ENTRIES },
102
+ });
103
+ const pendingQuestions = collectPendingQuestions("conv-fixture");
104
+ const calls = [toolCall({ id: "tool-abc" })];
105
+
106
+ // WHEN we enrich it
107
+ const [enriched] = enrichToolCallsWithQuestion(calls, { pendingQuestions });
108
+
109
+ // THEN the outstanding prompt is projected onto the tool call so a cold
110
+ // reconnect can rehydrate the question card
111
+ expect(enriched?.pendingQuestion?.requestId).toBe("req-1");
112
+ expect(enriched?.pendingQuestion?.entries).toEqual(ENTRIES);
113
+ });
114
+
115
+ test("leaves tool calls without a match untouched (same reference)", () => {
116
+ // GIVEN a tool call with no matching registry entry
117
+ const original = toolCall({ id: "tool-unmatched" });
118
+ register("req-other", {
119
+ conversationId: "conv-fixture",
120
+ kind: "question",
121
+ toolUseId: "tool-abc",
122
+ questionDetails: { entries: ENTRIES },
123
+ });
124
+
125
+ // WHEN we enrich it
126
+ const [enriched] = enrichToolCallsWithQuestion([original], {
127
+ pendingQuestions: collectPendingQuestions("conv-fixture"),
128
+ });
129
+
130
+ // THEN the tool call is returned unchanged
131
+ expect(enriched).toBe(original);
132
+ });
133
+
134
+ test("returns the input array untouched when there are no pending questions", () => {
135
+ // GIVEN tool calls and an empty registry
136
+ const calls = [toolCall({ id: "tool-abc" })];
137
+
138
+ // WHEN we enrich with an empty lookup
139
+ const enriched = enrichToolCallsWithQuestion(calls, {
140
+ pendingQuestions: new Map(),
141
+ });
142
+
143
+ // THEN the original array is returned
144
+ expect(enriched).toBe(calls);
145
+ });
146
+ });
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Render-time enrichment of history tool calls with in-flight question context.
3
+ *
4
+ * `pendingQuestion` — the outstanding `ask_question` prompt for a tool call
5
+ * still awaiting a user answer — is read from the in-memory
6
+ * `pending-interactions` registry (the authoritative store of unresolved
7
+ * prompts) and stamped onto its tool call so the web/API clients can restore
8
+ * the same question card on a cold reconnect (or a history reopen after the
9
+ * live event buffer has aged out) that the live `question_request` SSE stream
10
+ * would have produced. It appears only while the prompt is genuinely
11
+ * outstanding. Mirrors the confirmation enrichment in
12
+ * `tool-call-confirmation-enrichment.ts`.
13
+ */
14
+
15
+ import type { QuestionEntry } from "../../api/events/question-request.js";
16
+ import type { ConversationMessageToolCall } from "../../api/responses/conversation-message.js";
17
+ import { getByConversation } from "../pending-interactions.js";
18
+
19
+ /** A pending question matched to the tool call it prompts for, keyed by `toolUseId`. */
20
+ interface PendingQuestionMatch {
21
+ requestId: string;
22
+ entries: QuestionEntry[];
23
+ }
24
+
25
+ /**
26
+ * Build the `toolUseId → pending question` lookup for a conversation from the
27
+ * registry. Only question interactions that carry both a `toolUseId` and
28
+ * `questionDetails` can be stamped onto a wire tool call.
29
+ */
30
+ export function collectPendingQuestions(
31
+ conversationId: string,
32
+ ): Map<string, PendingQuestionMatch> {
33
+ const byToolUseId = new Map<string, PendingQuestionMatch>();
34
+ for (const interaction of getByConversation(conversationId)) {
35
+ if (
36
+ interaction.kind === "question" &&
37
+ interaction.questionDetails &&
38
+ interaction.toolUseId
39
+ ) {
40
+ byToolUseId.set(interaction.toolUseId, {
41
+ requestId: interaction.requestId,
42
+ entries: interaction.questionDetails.entries,
43
+ });
44
+ }
45
+ }
46
+ return byToolUseId;
47
+ }
48
+
49
+ /**
50
+ * Layer any outstanding `pendingQuestion` onto a message's rendered tool calls.
51
+ * Returns a new array; tool calls without a match are returned unchanged.
52
+ */
53
+ export function enrichToolCallsWithQuestion(
54
+ toolCalls: ConversationMessageToolCall[],
55
+ opts: { pendingQuestions: ReadonlyMap<string, PendingQuestionMatch> },
56
+ ): ConversationMessageToolCall[] {
57
+ if (opts.pendingQuestions.size === 0) return toolCalls;
58
+ return toolCalls.map((tc) => {
59
+ const match = tc.id ? opts.pendingQuestions.get(tc.id) : undefined;
60
+ if (!match) return tc;
61
+ return {
62
+ ...tc,
63
+ pendingQuestion: { requestId: match.requestId, entries: match.entries },
64
+ };
65
+ });
66
+ }
@@ -1,7 +1,9 @@
1
1
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
- import type { AssistantConfig } from "../../config/schema.js";
4
- import type { WorkflowRun } from "../../workflows/journal-store.js";
3
+ import type {
4
+ WorkflowJournalEntry,
5
+ WorkflowRun,
6
+ } from "../../workflows/journal-store.js";
5
7
  import type { SavedWorkflowEntry } from "../../workflows/library.js";
6
8
  import {
7
9
  WorkflowResumeNotPossibleError,
@@ -59,14 +61,31 @@ interface FakeManager {
59
61
  resume: (id: string) => { runId: string };
60
62
  }
61
63
 
64
+ function makeJournalEntry(
65
+ overrides: Partial<WorkflowJournalEntry> = {},
66
+ ): WorkflowJournalEntry {
67
+ return {
68
+ runId: "run-1",
69
+ seq: 0,
70
+ callHash: "call-0",
71
+ kind: "agent",
72
+ request: null,
73
+ result: null,
74
+ status: "completed",
75
+ createdAt: 1000,
76
+ ...overrides,
77
+ };
78
+ }
79
+
62
80
  function setup(opts: {
63
- flagEnabled?: boolean;
64
81
  runs?: WorkflowRun[];
65
82
  saved?: SavedWorkflowEntry[];
66
83
  /** Custom resume impl; defaults to a success that records the id. */
67
84
  resume?: (id: string) => { runId: string };
68
85
  /** Resolved auto-approve threshold for the resume posture gate. */
69
86
  threshold?: "none" | "low" | "medium" | "high";
87
+ /** Journal entries returned by getJournal, keyed by run id. */
88
+ journal?: Record<string, WorkflowJournalEntry[]>;
70
89
  }): {
71
90
  aborted: string[];
72
91
  resumed: string[];
@@ -98,9 +117,8 @@ function setup(opts: {
98
117
  __setWorkflowRoutesDeps({
99
118
  getManager: () => manager,
100
119
  listWorkflows: () => opts.saved ?? [],
101
- getConfig: () => ({}) as AssistantConfig,
102
- isFlagEnabled: () => opts.flagEnabled ?? true,
103
120
  getAutoApproveThreshold: async () => opts.threshold ?? "none",
121
+ getJournal: (runId) => opts.journal?.[runId] ?? [],
104
122
  });
105
123
  return { aborted, resumed, listCalls };
106
124
  }
@@ -113,10 +131,9 @@ afterEach(() => {
113
131
  // Happy paths
114
132
  // ---------------------------------------------------------------------------
115
133
 
116
- describe("workflow routes (flag on)", () => {
134
+ describe("workflow routes (happy paths)", () => {
117
135
  beforeEach(() => {
118
136
  setup({
119
- flagEnabled: true,
120
137
  runs: [
121
138
  makeRun({ id: "run-1" }),
122
139
  makeRun({ id: "run-2", status: "completed" }),
@@ -145,7 +162,6 @@ describe("workflow routes (flag on)", () => {
145
162
 
146
163
  test("listWorkflowRuns honors limit + status query params", async () => {
147
164
  const { listCalls } = setup({
148
- flagEnabled: true,
149
165
  runs: [makeRun({ id: "run-1", status: "completed" })],
150
166
  });
151
167
  await route("listWorkflowRuns").handler({
@@ -163,7 +179,6 @@ describe("workflow routes (flag on)", () => {
163
179
 
164
180
  test("abortWorkflowRun aborts a known run", async () => {
165
181
  const { aborted } = setup({
166
- flagEnabled: true,
167
182
  runs: [makeRun({ id: "run-1" })],
168
183
  });
169
184
  const result = (await route("abortWorkflowRun").handler({
@@ -175,7 +190,6 @@ describe("workflow routes (flag on)", () => {
175
190
 
176
191
  test("resumeWorkflowRun resumes an interrupted run", async () => {
177
192
  const { resumed } = setup({
178
- flagEnabled: true,
179
193
  runs: [makeRun({ id: "run-1", status: "interrupted" })],
180
194
  });
181
195
  const result = (await route("resumeWorkflowRun").handler({
@@ -192,7 +206,6 @@ describe("workflow routes (flag on)", () => {
192
206
  // full-access posture it must refuse rather than silently bypass consent —
193
207
  // resume() is never called.
194
208
  const { resumed } = setup({
195
- flagEnabled: true,
196
209
  threshold: "medium",
197
210
  runs: [
198
211
  makeRun({
@@ -213,7 +226,6 @@ describe("workflow routes (flag on)", () => {
213
226
  // high-risk tools, so no prompt is needed — the side-effecting resume
214
227
  // proceeds directly.
215
228
  const { resumed } = setup({
216
- flagEnabled: true,
217
229
  threshold: "high",
218
230
  runs: [
219
231
  makeRun({
@@ -236,7 +248,6 @@ describe("workflow routes (flag on)", () => {
236
248
  // the object shape too — a strict parse would treat it as read-only and let
237
249
  // the side-effecting resume through without approval.
238
250
  const { resumed } = setup({
239
- flagEnabled: true,
240
251
  threshold: "medium",
241
252
  runs: [
242
253
  makeRun({
@@ -259,7 +270,6 @@ describe("workflow routes (flag on)", () => {
259
270
  // An explicit empty manifest grants no side effects, so the route resumes
260
271
  // it directly — the gate keys on the stored manifest, not run existence.
261
272
  const { resumed } = setup({
262
- flagEnabled: true,
263
273
  threshold: "medium",
264
274
  runs: [
265
275
  makeRun({
@@ -275,7 +285,6 @@ describe("workflow routes (flag on)", () => {
275
285
 
276
286
  test("resumeWorkflowRun maps a non-interrupted run to a 409 ConflictError", async () => {
277
287
  setup({
278
- flagEnabled: true,
279
288
  runs: [makeRun({ id: "run-1", status: "completed" })],
280
289
  resume: (id) => {
281
290
  throw new WorkflowResumeNotPossibleError(
@@ -292,7 +301,6 @@ describe("workflow routes (flag on)", () => {
292
301
 
293
302
  test("resumeWorkflowRun maps a cap error to a 429 TooManyRequestsError", async () => {
294
303
  setup({
295
- flagEnabled: true,
296
304
  runs: [makeRun({ id: "run-1", status: "interrupted" })],
297
305
  resume: () => {
298
306
  throw new WorkflowRunCapError(3);
@@ -317,20 +325,222 @@ describe("workflow routes (flag on)", () => {
317
325
  });
318
326
  });
319
327
 
328
+ // ---------------------------------------------------------------------------
329
+ // Journal projection
330
+ // ---------------------------------------------------------------------------
331
+
332
+ interface WireLeaf {
333
+ seq: number;
334
+ kind: string;
335
+ label?: string;
336
+ phase?: string;
337
+ promptSummary?: string;
338
+ status: string;
339
+ resultSummary?: string;
340
+ inputTokens?: number;
341
+ outputTokens?: number;
342
+ createdAt: number | null;
343
+ }
344
+
345
+ interface WireJournal {
346
+ runId: string;
347
+ status: string;
348
+ agentsSpawned: number;
349
+ inputTokens: number;
350
+ outputTokens: number;
351
+ leaves: WireLeaf[];
352
+ }
353
+
354
+ describe("getWorkflowRunJournal", () => {
355
+ test("projects agent leaves with label/phase/promptSummary/resultSummary extracted", async () => {
356
+ setup({
357
+ runs: [makeRun({ id: "run-1" })],
358
+ journal: {
359
+ "run-1": [
360
+ makeJournalEntry({
361
+ seq: 0,
362
+ kind: "agent",
363
+ request: {
364
+ prompt: "Investigate the failing build",
365
+ opts: { label: "investigate", phase: "triage" },
366
+ },
367
+ result: { summary: "found the flaky test" },
368
+ status: "completed",
369
+ inputTokens: 120,
370
+ outputTokens: 45,
371
+ }),
372
+ ],
373
+ },
374
+ });
375
+ const result = (await route("getWorkflowRunJournal").handler({
376
+ pathParams: { id: "run-1" },
377
+ })) as WireJournal;
378
+
379
+ expect(result.runId).toBe("run-1");
380
+ expect(result.status).toBe("running");
381
+ expect(result.agentsSpawned).toBe(2);
382
+ expect(result.leaves.map((l) => l.seq)).toEqual([0]);
383
+
384
+ const first = result.leaves[0];
385
+ expect(first.kind).toBe("agent");
386
+ expect(first.label).toBe("investigate");
387
+ expect(first.phase).toBe("triage");
388
+ expect(first.promptSummary).toBe("Investigate the failing build");
389
+ expect(first.resultSummary).toContain("found the flaky test");
390
+ // Per-leaf token usage is projected onto the wire leaf.
391
+ expect(first.inputTokens).toBe(120);
392
+ expect(first.outputTokens).toBe(45);
393
+ // The bulky raw request/result payloads are dropped.
394
+ expect(first).not.toHaveProperty("request");
395
+ expect(first).not.toHaveProperty("result");
396
+ });
397
+
398
+ test("omits per-leaf token fields when the journal entry carries none", async () => {
399
+ setup({
400
+ runs: [makeRun({ id: "run-1" })],
401
+ journal: {
402
+ "run-1": [
403
+ makeJournalEntry({
404
+ seq: 0,
405
+ kind: "agent",
406
+ request: { prompt: "no tokens recorded", opts: {} },
407
+ status: "completed",
408
+ }),
409
+ ],
410
+ },
411
+ });
412
+ const result = (await route("getWorkflowRunJournal").handler({
413
+ pathParams: { id: "run-1" },
414
+ })) as WireJournal;
415
+
416
+ const leaf = result.leaves[0];
417
+ expect(leaf).not.toHaveProperty("inputTokens");
418
+ expect(leaf).not.toHaveProperty("outputTokens");
419
+ });
420
+
421
+ test("excludes kind:workflow entries so the backfill matches the live agent-only stream", async () => {
422
+ // The live `workflow_leaf_*` stream is emitted only for agent leaves; nested
423
+ // `workflow(name)` resolutions never reach it. The journal route must filter
424
+ // to agent leaves too, or a nested-workflow run renders a phantom unlabeled
425
+ // node on backfill and a different leaf set than live.
426
+ setup({
427
+ runs: [makeRun({ id: "run-1" })],
428
+ journal: {
429
+ "run-1": [
430
+ makeJournalEntry({
431
+ seq: 0,
432
+ kind: "agent",
433
+ request: { prompt: "first agent", opts: { label: "first" } },
434
+ status: "completed",
435
+ }),
436
+ makeJournalEntry({
437
+ seq: 1,
438
+ kind: "workflow",
439
+ request: { name: "child", args: { foo: 1 } },
440
+ result: "child done",
441
+ status: "completed",
442
+ }),
443
+ makeJournalEntry({
444
+ seq: 2,
445
+ kind: "agent",
446
+ request: { prompt: "second agent", opts: { label: "second" } },
447
+ status: "completed",
448
+ }),
449
+ ],
450
+ },
451
+ });
452
+ const result = (await route("getWorkflowRunJournal").handler({
453
+ pathParams: { id: "run-1" },
454
+ })) as WireJournal;
455
+
456
+ // Only the agent leaves survive, in seq order; the workflow-kind entry (seq
457
+ // 1) is dropped entirely.
458
+ expect(result.leaves.map((l) => l.seq)).toEqual([0, 2]);
459
+ expect(result.leaves.every((l) => l.kind === "agent")).toBe(true);
460
+ expect(result.leaves.map((l) => l.label)).toEqual(["first", "second"]);
461
+ });
462
+
463
+ test("surfaces a failed leaf's { error } result as resultSummary", async () => {
464
+ setup({
465
+ runs: [makeRun({ id: "run-1" })],
466
+ journal: {
467
+ "run-1": [
468
+ makeJournalEntry({
469
+ seq: 0,
470
+ status: "failed",
471
+ request: { prompt: "do the thing", opts: {} },
472
+ result: { error: "agent crashed: out of tokens" },
473
+ }),
474
+ ],
475
+ },
476
+ });
477
+ const result = (await route("getWorkflowRunJournal").handler({
478
+ pathParams: { id: "run-1" },
479
+ })) as WireJournal;
480
+
481
+ expect(result.leaves[0].status).toBe("failed");
482
+ expect(result.leaves[0].resultSummary).toContain(
483
+ "agent crashed: out of tokens",
484
+ );
485
+ });
486
+
487
+ test("truncates a long prompt/result to ~200 chars", async () => {
488
+ const longPrompt = "x".repeat(500);
489
+ const longResult = "y".repeat(500);
490
+ setup({
491
+ runs: [makeRun({ id: "run-1" })],
492
+ journal: {
493
+ "run-1": [
494
+ makeJournalEntry({
495
+ seq: 0,
496
+ request: { prompt: longPrompt, opts: {} },
497
+ result: longResult,
498
+ }),
499
+ ],
500
+ },
501
+ });
502
+ const result = (await route("getWorkflowRunJournal").handler({
503
+ pathParams: { id: "run-1" },
504
+ })) as WireJournal;
505
+
506
+ const leaf = result.leaves[0];
507
+ expect(leaf.promptSummary!.length).toBeLessThanOrEqual(201);
508
+ expect(leaf.promptSummary!.startsWith("x")).toBe(true);
509
+ // A truncated summary ends with the ellipsis marker.
510
+ expect(leaf.promptSummary!.endsWith("…")).toBe(true);
511
+ expect(leaf.resultSummary!.length).toBeLessThanOrEqual(201);
512
+ });
513
+
514
+ test("returns an empty leaf list for a run with no journal entries", async () => {
515
+ setup({ runs: [makeRun({ id: "run-1" })] });
516
+ const result = (await route("getWorkflowRunJournal").handler({
517
+ pathParams: { id: "run-1" },
518
+ })) as WireJournal;
519
+ expect(result.leaves).toEqual([]);
520
+ });
521
+
522
+ test("throws NotFoundError for an unknown id", () => {
523
+ setup({ runs: [] });
524
+ expect(() =>
525
+ route("getWorkflowRunJournal").handler({ pathParams: { id: "nope" } }),
526
+ ).toThrow(NotFoundError);
527
+ });
528
+ });
529
+
320
530
  // ---------------------------------------------------------------------------
321
531
  // Unknown run → 404
322
532
  // ---------------------------------------------------------------------------
323
533
 
324
534
  describe("workflow routes (unknown run)", () => {
325
535
  test("getWorkflowRun throws NotFoundError for an unknown id", () => {
326
- setup({ flagEnabled: true, runs: [] });
536
+ setup({ runs: [] });
327
537
  expect(() =>
328
538
  route("getWorkflowRun").handler({ pathParams: { id: "nope" } }),
329
539
  ).toThrow(NotFoundError);
330
540
  });
331
541
 
332
542
  test("abortWorkflowRun throws NotFoundError for an unknown id", () => {
333
- const { aborted } = setup({ flagEnabled: true, runs: [] });
543
+ const { aborted } = setup({ runs: [] });
334
544
  expect(() =>
335
545
  route("abortWorkflowRun").handler({ pathParams: { id: "nope" } }),
336
546
  ).toThrow(NotFoundError);
@@ -338,35 +548,10 @@ describe("workflow routes (unknown run)", () => {
338
548
  });
339
549
 
340
550
  test("resumeWorkflowRun throws NotFoundError for an unknown id", async () => {
341
- const { resumed } = setup({ flagEnabled: true, runs: [] });
551
+ const { resumed } = setup({ runs: [] });
342
552
  await expect(
343
553
  route("resumeWorkflowRun").handler({ pathParams: { id: "nope" } }),
344
554
  ).rejects.toThrow(NotFoundError);
345
555
  expect(resumed).toEqual([]);
346
556
  });
347
557
  });
348
-
349
- // ---------------------------------------------------------------------------
350
- // Flag off → every route 404s
351
- // ---------------------------------------------------------------------------
352
-
353
- describe("workflow routes (flag off)", () => {
354
- beforeEach(() => {
355
- setup({ flagEnabled: false, runs: [makeRun({ id: "run-1" })], saved: [] });
356
- });
357
-
358
- test.each([
359
- ["listWorkflowRuns", { queryParams: {} }],
360
- ["getWorkflowRun", { pathParams: { id: "run-1" } }],
361
- ["abortWorkflowRun", { pathParams: { id: "run-1" } }],
362
- ["listSavedWorkflows", {}],
363
- ] as const)("%s throws NotFoundError when the flag is off", (op, args) => {
364
- expect(() => route(op).handler(args)).toThrow(NotFoundError);
365
- });
366
-
367
- test("resumeWorkflowRun rejects with NotFoundError when the flag is off", async () => {
368
- await expect(
369
- route("resumeWorkflowRun").handler({ pathParams: { id: "run-1" } }),
370
- ).rejects.toThrow(NotFoundError);
371
- });
372
- });