@vellumai/assistant 0.8.12 → 0.9.0-staging.2

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 (442) hide show
  1. package/AGENTS.md +0 -14
  2. package/ARCHITECTURE.md +45 -45
  3. package/README.md +1 -1
  4. package/bun.lock +200 -154
  5. package/docs/architecture/integrations.md +3 -3
  6. package/docs/architecture/memory.md +2 -2
  7. package/docs/architecture/security.md +10 -10
  8. package/docs/runbook-trusted-contacts.md +12 -12
  9. package/docs/skills.md +6 -6
  10. package/docs/workflows-testing.md +221 -0
  11. package/docs/workflows.md +510 -0
  12. package/examples/plugins/echo/README.md +5 -5
  13. package/knip.json +2 -0
  14. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
  15. package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
  16. package/openapi.yaml +7197 -5708
  17. package/package.json +8 -4
  18. package/scripts/generate-openapi.ts +66 -114
  19. package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
  20. package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
  21. package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
  22. package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  24. package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
  25. package/src/__tests__/app-compiler.test.ts +7 -1
  26. package/src/__tests__/app-dir-path-guard.test.ts +27 -3
  27. package/src/__tests__/app-executors.test.ts +43 -0
  28. package/src/__tests__/approval-cascade.test.ts +0 -5
  29. package/src/__tests__/approval-routes-http.test.ts +91 -0
  30. package/src/__tests__/assistant-stream-state.test.ts +107 -0
  31. package/src/__tests__/browser-fill-credential.test.ts +3 -3
  32. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
  33. package/src/__tests__/compaction-events.test.ts +63 -7
  34. package/src/__tests__/compaction-trail-store.test.ts +74 -1
  35. package/src/__tests__/compaction.benchmark.test.ts +63 -41
  36. package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
  37. package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
  39. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
  40. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  41. package/src/__tests__/conversation-process-callsite.test.ts +0 -5
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
  43. package/src/__tests__/conversation-queue.test.ts +0 -5
  44. package/src/__tests__/conversation-slash-queue.test.ts +0 -5
  45. package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
  46. package/src/__tests__/conversation-speed-override.test.ts +0 -5
  47. package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
  48. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  49. package/src/__tests__/conversation-usage.test.ts +2 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
  52. package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
  53. package/src/__tests__/credential-broker-server-use.test.ts +2 -2
  54. package/src/__tests__/credential-broker.test.ts +1 -1
  55. package/src/__tests__/credential-prompt-route.test.ts +417 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault.test.ts +37 -0
  58. package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
  59. package/src/__tests__/dynamic-page-surface.test.ts +219 -0
  60. package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
  61. package/src/__tests__/gateway-flag-listener.test.ts +24 -7
  62. package/src/__tests__/guardian-action-sweep.test.ts +56 -219
  63. package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
  64. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  65. package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
  66. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
  67. package/src/__tests__/llm-resolver.test.ts +115 -0
  68. package/src/__tests__/managed-profile-guard.test.ts +6 -5
  69. package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
  70. package/src/__tests__/media-generate-image.test.ts +20 -9
  71. package/src/__tests__/mock-gateway-ipc.ts +23 -0
  72. package/src/__tests__/model-intents.test.ts +1 -1
  73. package/src/__tests__/normalize-onboarding.test.ts +26 -0
  74. package/src/__tests__/notification-decision-strategy.test.ts +4 -2
  75. package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
  76. package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
  77. package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
  78. package/src/__tests__/require-fresh-approval.test.ts +425 -1
  79. package/src/__tests__/resolve-app-id.test.ts +56 -0
  80. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
  81. package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
  82. package/src/__tests__/schedule-routes.test.ts +257 -4
  83. package/src/__tests__/schedule-store.test.ts +60 -0
  84. package/src/__tests__/schedule-tools.test.ts +247 -2
  85. package/src/__tests__/skill-execute-input.test.ts +85 -0
  86. package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
  87. package/src/__tests__/skills.test.ts +3 -3
  88. package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
  89. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  90. package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
  91. package/src/__tests__/system-prompt.test.ts +1 -1
  92. package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
  93. package/src/__tests__/task-scheduler.test.ts +299 -0
  94. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
  95. package/src/__tests__/tool-result-spool.test.ts +3 -1
  96. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
  97. package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
  98. package/src/agent/compaction-circuit.ts +11 -0
  99. package/src/agent/loop.ts +181 -12
  100. package/src/api/constants/call-sites.ts +12 -0
  101. package/src/api/events/assistant-thinking-delta.ts +10 -0
  102. package/src/api/events/usage-update.ts +7 -0
  103. package/src/api/index.ts +4 -1
  104. package/src/api/responses/memory-v3-selection-log.ts +18 -11
  105. package/src/approvals/approval-primitive.ts +2 -2
  106. package/src/background-wake/background-wake-routes.test.ts +5 -2
  107. package/src/bundler/compiler-tools.ts +1 -1
  108. package/src/bundler/package-resolver.ts +0 -1
  109. package/src/calls/call-domain.ts +1 -1
  110. package/src/calls/guardian-action-sweep.ts +16 -93
  111. package/src/cli/AGENTS.md +4 -0
  112. package/src/cli/commands/__tests__/schedules.test.ts +430 -1
  113. package/src/cli/commands/credentials.ts +28 -24
  114. package/src/cli/commands/image-generation.ts +23 -9
  115. package/src/cli/commands/notifications.ts +1 -1
  116. package/src/cli/commands/plugins.ts +89 -46
  117. package/src/cli/commands/schedules.ts +384 -11
  118. package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
  119. package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
  120. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
  121. package/src/cli/lib/inspect-plugin.ts +62 -1
  122. package/src/cli/lib/install-from-github.ts +52 -5
  123. package/src/cli/lib/upgrade-plugin.ts +18 -0
  124. package/src/config/__tests__/workflows-schema.test.ts +60 -0
  125. package/src/config/bundled-skills/acp/SKILL.md +2 -2
  126. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  127. package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
  128. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
  129. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
  130. package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
  131. package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
  132. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
  133. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
  134. package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
  135. package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
  136. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
  137. package/src/config/bundled-skills/schedule/SKILL.md +7 -2
  138. package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
  139. package/src/config/bundled-skills/workflows/SKILL.md +214 -0
  140. package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
  141. package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
  142. package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
  143. package/src/config/bundled-tool-registry.ts +14 -2
  144. package/src/config/call-site-defaults.ts +5 -0
  145. package/src/config/feature-flag-registry.json +12 -4
  146. package/src/config/llm-context-resolution.ts +8 -0
  147. package/src/config/llm-resolver.ts +30 -0
  148. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
  149. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
  150. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
  151. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
  152. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
  153. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
  154. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
  155. package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
  156. package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
  157. package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
  158. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
  159. package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
  160. package/src/config/schema.ts +2 -0
  161. package/src/config/schemas/call-site-catalog.ts +7 -0
  162. package/src/config/schemas/heartbeat.ts +4 -1
  163. package/src/config/schemas/llm.ts +33 -26
  164. package/src/config/schemas/memory-retrospective.ts +19 -0
  165. package/src/config/schemas/platform.ts +8 -0
  166. package/src/config/schemas/services.ts +5 -2
  167. package/src/config/schemas/workflows.ts +42 -0
  168. package/src/config/skills.ts +3 -3
  169. package/src/context/compactor.ts +273 -39
  170. package/src/context/post-turn-tool-result-truncation.ts +23 -6
  171. package/src/context/tool-result-spool.ts +12 -17
  172. package/src/credential-execution/executable-discovery.ts +1 -1
  173. package/src/credential-execution/process-manager.ts +37 -3
  174. package/src/credential-execution/prompted-credential.ts +205 -0
  175. package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
  176. package/src/daemon/conversation-process.ts +11 -2
  177. package/src/daemon/conversation-surfaces.ts +167 -3
  178. package/src/daemon/conversation-tool-setup.ts +103 -26
  179. package/src/daemon/conversation-usage.ts +2 -0
  180. package/src/daemon/conversation.ts +115 -11
  181. package/src/daemon/handlers/shared.ts +26 -14
  182. package/src/daemon/host-cu-proxy.ts +15 -12
  183. package/src/daemon/host-file-proxy.ts +15 -12
  184. package/src/daemon/host-transfer-proxy.ts +30 -24
  185. package/src/daemon/lifecycle.ts +40 -3
  186. package/src/daemon/message-protocol.ts +3 -0
  187. package/src/daemon/message-types/messages.ts +2 -10
  188. package/src/daemon/message-types/workflows.ts +49 -0
  189. package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
  190. package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
  191. package/src/daemon/process-message.ts +6 -0
  192. package/src/daemon/tool-setup-types.ts +57 -0
  193. package/src/daemon/wake-conversation-ops.ts +18 -0
  194. package/src/heartbeat/heartbeat-run-store.ts +8 -2
  195. package/src/home/feed-types.ts +1 -1
  196. package/src/ipc/gateway-flag-listener.ts +28 -6
  197. package/src/mcp/mcp-auth-state.ts +8 -20
  198. package/src/media/__tests__/image-models.test.ts +57 -0
  199. package/src/media/image-models.ts +66 -0
  200. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
  201. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
  202. package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
  203. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
  204. package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
  205. package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
  206. package/src/memory/auto-analysis-enqueue.ts +14 -1
  207. package/src/memory/compaction-log-store-clickhouse.ts +6 -4
  208. package/src/memory/conversation-crud.ts +9 -2
  209. package/src/memory/conversation-disk-view.ts +1 -1
  210. package/src/memory/conversation-queries.ts +22 -7
  211. package/src/memory/db-init.ts +20 -0
  212. package/src/memory/db-maintenance.ts +16 -0
  213. package/src/memory/embedding-runtime-manager.ts +1 -1
  214. package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
  215. package/src/memory/llm-request-log-source-local.ts +19 -1
  216. package/src/memory/llm-request-log-source.ts +35 -6
  217. package/src/memory/llm-request-log-store.ts +90 -2
  218. package/src/memory/memory-retrospective-constants.ts +9 -0
  219. package/src/memory/memory-retrospective-enqueue.ts +3 -6
  220. package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
  221. package/src/memory/memory-retrospective-job.ts +500 -208
  222. package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
  223. package/src/memory/memory-retrospective-state.ts +85 -2
  224. package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
  225. package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
  226. package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
  227. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
  228. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
  229. package/src/memory/migrations/284-workflow-runs.ts +51 -0
  230. package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
  231. package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
  232. package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
  233. package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
  234. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
  235. package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
  236. package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
  237. package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
  238. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
  239. package/src/memory/migrations/index.ts +10 -0
  240. package/src/memory/preloaded-apps.ts +116 -0
  241. package/src/memory/schema/infrastructure.ts +4 -0
  242. package/src/memory/schema/memory-core.ts +4 -0
  243. package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
  244. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
  245. package/src/memory/v2/__tests__/page-store.test.ts +13 -2
  246. package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
  247. package/src/memory/v2/frontmatter-sweep.ts +7 -6
  248. package/src/memory/v2/page-store.ts +4 -3
  249. package/src/memory/v2/qdrant.ts +42 -3
  250. package/src/memory/v2/types.ts +16 -10
  251. package/src/messaging/draft-store.ts +1 -1
  252. package/src/notifications/access-request-copy.ts +200 -113
  253. package/src/notifications/adapters/slack.ts +250 -111
  254. package/src/notifications/adapters/telegram.ts +7 -44
  255. package/src/notifications/approval-card-builder.ts +93 -0
  256. package/src/notifications/broadcaster.ts +74 -0
  257. package/src/notifications/conversation-pairing.ts +8 -6
  258. package/src/notifications/copy-composer.ts +32 -26
  259. package/src/notifications/decision-engine.ts +59 -7
  260. package/src/notifications/guardian-question-mode.ts +145 -155
  261. package/src/notifications/home-feed-side-effect.ts +28 -11
  262. package/src/notifications/notification-utils.ts +66 -0
  263. package/src/notifications/signal.ts +6 -0
  264. package/src/notifications/tool-approval-copy.ts +142 -0
  265. package/src/notifications/types.ts +19 -0
  266. package/src/permissions/threshold.ts +11 -0
  267. package/src/plugin-api/types.ts +16 -4
  268. package/src/plugins/defaults/compaction/window-manager.ts +44 -0
  269. package/src/plugins/defaults/index.ts +46 -0
  270. package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
  271. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
  272. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
  273. package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
  274. package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
  275. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
  276. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
  277. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
  278. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
  282. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
  283. package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
  284. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
  285. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
  286. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
  287. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
  288. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
  289. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
  290. package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
  291. package/src/prompts/__tests__/system-prompt.test.ts +100 -1
  292. package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
  293. package/src/prompts/normalize-onboarding.ts +2 -0
  294. package/src/prompts/persona-resolver.ts +3 -0
  295. package/src/prompts/system-prompt.ts +51 -2
  296. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
  297. package/src/prompts/templates/system-sections.ts +8 -3
  298. package/src/providers/call-site-routing.ts +6 -3
  299. package/src/providers/fireworks/client.ts +3 -0
  300. package/src/providers/inference/auth.ts +52 -46
  301. package/src/providers/model-intents.ts +2 -2
  302. package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
  303. package/src/providers/openai/chat-completions-provider.ts +47 -9
  304. package/src/providers/openai/coerce-object-args.ts +104 -0
  305. package/src/providers/retry.ts +8 -5
  306. package/src/providers/types.ts +10 -0
  307. package/src/runtime/__tests__/agent-wake.test.ts +629 -7
  308. package/src/runtime/access-request-helper.ts +16 -9
  309. package/src/runtime/agent-wake.ts +302 -51
  310. package/src/runtime/assistant-stream-state.ts +141 -8
  311. package/src/runtime/background-job-runner.ts +9 -0
  312. package/src/runtime/channel-approval-types.ts +1 -0
  313. package/src/runtime/channel-invite-transports/telegram.ts +6 -5
  314. package/src/runtime/channel-invite-transports/voice.ts +2 -2
  315. package/src/runtime/channel-invite-types.ts +4 -2
  316. package/src/runtime/channel-retry-sweep.ts +19 -41
  317. package/src/runtime/finalize-event-delivery.ts +72 -0
  318. package/src/runtime/guardian-action-message-composer.ts +0 -54
  319. package/src/runtime/http-server.ts +6 -14
  320. package/src/runtime/http-types.ts +0 -1
  321. package/src/runtime/message-composer-types.ts +0 -9
  322. package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
  323. package/src/runtime/middleware/auth.ts +27 -3
  324. package/src/runtime/middleware/rate-limiter.ts +28 -1
  325. package/src/runtime/migrations/vbundle-builder.ts +6 -5
  326. package/src/runtime/pending-interactions.ts +20 -1
  327. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
  328. package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
  329. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
  330. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
  331. package/src/runtime/routes/approval-routes.ts +35 -8
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
  333. package/src/runtime/routes/btw-routes.ts +37 -1
  334. package/src/runtime/routes/channel-delivery-routes.ts +11 -7
  335. package/src/runtime/routes/channel-route-definitions.ts +3 -0
  336. package/src/runtime/routes/channel-route-shared.ts +3 -1
  337. package/src/runtime/routes/consolidation-routes.ts +17 -13
  338. package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
  339. package/src/runtime/routes/conversation-list-routes.ts +41 -4
  340. package/src/runtime/routes/conversation-query-routes.ts +196 -11
  341. package/src/runtime/routes/conversation-routes.ts +15 -1
  342. package/src/runtime/routes/credential-prompt-routes.ts +39 -17
  343. package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
  344. package/src/runtime/routes/heartbeat-routes.ts +18 -13
  345. package/src/runtime/routes/image-generation-routes.ts +20 -2
  346. package/src/runtime/routes/inbound-message-handler.ts +13 -12
  347. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
  348. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
  349. package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
  350. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  351. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
  352. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
  353. package/src/runtime/routes/index.ts +6 -0
  354. package/src/runtime/routes/log-export/AGENTS.md +1 -1
  355. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  356. package/src/runtime/routes/migration-routes.ts +5 -9
  357. package/src/runtime/routes/plugins-routes.ts +26 -0
  358. package/src/runtime/routes/ps-routes.ts +10 -8
  359. package/src/runtime/routes/retrospective-routes.ts +235 -0
  360. package/src/runtime/routes/runs-pagination.ts +75 -0
  361. package/src/runtime/routes/schedule-routes.ts +367 -59
  362. package/src/runtime/routes/sounds-config-routes.ts +239 -0
  363. package/src/runtime/routes/surface-action-routes.ts +84 -4
  364. package/src/runtime/routes/workflow-routes.test.ts +372 -0
  365. package/src/runtime/routes/workflow-routes.ts +363 -0
  366. package/src/runtime/routes/workspace-routes.test.ts +61 -1
  367. package/src/runtime/routes/workspace-routes.ts +26 -1
  368. package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
  369. package/src/runtime/services/analyze-conversation.ts +26 -13
  370. package/src/schedule/inference-profile.ts +28 -0
  371. package/src/schedule/schedule-store.ts +152 -4
  372. package/src/schedule/scheduler-types.ts +6 -0
  373. package/src/schedule/scheduler.ts +96 -0
  374. package/src/security/secret-allowlist.ts +1 -1
  375. package/src/skills/path-classifier.ts +1 -1
  376. package/src/tools/apps/executors.ts +23 -0
  377. package/src/tools/apps/resolve-app-id.ts +42 -0
  378. package/src/tools/browser/browser-execution.ts +9 -11
  379. package/src/tools/credentials/broker.ts +4 -4
  380. package/src/tools/credentials/vault.ts +26 -137
  381. package/src/tools/executor.ts +69 -0
  382. package/src/tools/flag-gated-tools.test.ts +76 -0
  383. package/src/tools/permission-checker.ts +8 -1
  384. package/src/tools/registry.ts +51 -0
  385. package/src/tools/schedule/create.ts +77 -1
  386. package/src/tools/schedule/list.ts +1 -0
  387. package/src/tools/schedule/update.ts +73 -1
  388. package/src/tools/skills/execute.ts +56 -0
  389. package/src/tools/terminal/shell.ts +1 -1
  390. package/src/tools/ui-surface/definitions.ts +91 -2
  391. package/src/tools/workflows/manage-workflows.ts +183 -0
  392. package/src/tools/workflows/run-workflow.test.ts +442 -0
  393. package/src/tools/workflows/run-workflow.ts +88 -0
  394. package/src/types/onboarding-context.ts +2 -0
  395. package/src/usage/attribution.ts +24 -0
  396. package/src/util/canonicalize-identity.ts +12 -3
  397. package/src/util/platform.ts +17 -17
  398. package/src/watcher/__tests__/engine.test.ts +24 -0
  399. package/src/watcher/__tests__/telemetry.test.ts +135 -0
  400. package/src/watcher/engine.ts +7 -0
  401. package/src/watcher/telemetry.ts +74 -0
  402. package/src/workflows/capabilities.test.ts +365 -0
  403. package/src/workflows/capabilities.ts +359 -0
  404. package/src/workflows/deterministic-stringify.ts +27 -0
  405. package/src/workflows/engine-integration.test.ts +656 -0
  406. package/src/workflows/engine.test.ts +1144 -0
  407. package/src/workflows/engine.ts +1078 -0
  408. package/src/workflows/fanout-load.test.ts +168 -0
  409. package/src/workflows/journal-store.test.ts +369 -0
  410. package/src/workflows/journal-store.ts +470 -0
  411. package/src/workflows/leaf-runner.test.ts +704 -0
  412. package/src/workflows/leaf-runner.ts +589 -0
  413. package/src/workflows/library.test.ts +134 -0
  414. package/src/workflows/library.ts +124 -0
  415. package/src/workflows/run-manager.test.ts +711 -0
  416. package/src/workflows/run-manager.ts +593 -0
  417. package/src/workflows/sandbox-escape.test.ts +339 -0
  418. package/src/workflows/sandbox.test.ts +251 -0
  419. package/src/workflows/sandbox.ts +447 -0
  420. package/src/workspace/adaptive-thinking-repair.ts +33 -11
  421. package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
  422. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
  423. package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
  424. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
  425. package/src/workspace/migrations/031-drop-user-md.ts +1 -4
  426. package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
  427. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
  428. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
  429. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
  430. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
  431. package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
  432. package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
  433. package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
  434. package/src/workspace/migrations/registry.ts +6 -0
  435. package/src/workspace/migrations/runner.ts +1 -1
  436. package/tsconfig.json +1 -1
  437. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
  438. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
  439. package/src/__tests__/guardian-action-store.test.ts +0 -106
  440. package/src/daemon/guardian-action-generators.ts +0 -71
  441. package/src/memory/guardian-action-store.ts +0 -484
  442. package/src/runtime/guardian-action-grant-minter.ts +0 -150
@@ -1,579 +0,0 @@
1
- /**
2
- * Integration test: guardian-action answer resolution mints a scoped grant
3
- * that the voice consumer can consume exactly once.
4
- *
5
- * Exercises the original voice bug scenario end-to-end:
6
- * 1. Voice ASK_GUARDIAN fires -> guardian action request created with tool metadata
7
- * 2. Guardian answers via desktop/Telegram -> request resolved
8
- * 3. tryMintGuardianActionGrant mints a tool_signature grant
9
- * 4. Voice consumer can consume the grant for the same tool+input
10
- * 5. Second consume attempt is denied (one-time use)
11
- */
12
-
13
- import { beforeEach, describe, expect, mock, test } from "bun:test";
14
-
15
- // ── Platform + logger mocks ─────────────────────────────────────────
16
-
17
- mock.module("../util/logger.js", () => ({
18
- getLogger: () =>
19
- new Proxy({} as Record<string, unknown>, {
20
- get: () => () => {},
21
- }),
22
- truncateForLog: (value: string) => value,
23
- }));
24
-
25
- // ── Imports (after mocks) ───────────────────────────────────────────
26
-
27
- import {
28
- createCallSession,
29
- createPendingQuestion,
30
- } from "../calls/call-store.js";
31
- import { getDb } from "../memory/db-connection.js";
32
- import { initializeDb } from "../memory/db-init.js";
33
- import {
34
- createGuardianActionRequest,
35
- resolveGuardianActionRequest,
36
- } from "../memory/guardian-action-store.js";
37
- import { conversations, scopedApprovalGrants } from "../memory/schema.js";
38
- import { _internal } from "../memory/scoped-approval-grants.js";
39
-
40
- const { consumeScopedApprovalGrantByToolSignature } = _internal;
41
- import { tryMintGuardianActionGrant } from "../runtime/guardian-action-grant-minter.js";
42
- import type { ApprovalConversationGenerator } from "../runtime/http-types.js";
43
- import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
44
-
45
- initializeDb();
46
-
47
- // ── Constants ───────────────────────────────────────────────────────
48
-
49
- const TOOL_NAME = "execute_shell";
50
- const TOOL_INPUT = { command: "rm -rf /tmp/test" };
51
- const CONVERSATION_ID = "conv-e2e";
52
-
53
- // Mutable references populated by ensureFkParents()
54
- let CALL_SESSION_ID = "";
55
- let PENDING_QUESTION_IDS: string[] = [];
56
- let pqIndex = 0;
57
-
58
- function ensureConversation(id: string): void {
59
- const db = getDb();
60
- const now = Date.now();
61
- db.insert(conversations)
62
- .values({
63
- id,
64
- title: `Conversation ${id}`,
65
- createdAt: now,
66
- updatedAt: now,
67
- })
68
- .run();
69
- }
70
-
71
- /** Create the FK parent rows required by guardian_action_requests. */
72
- function ensureFkParents(): void {
73
- ensureConversation(CONVERSATION_ID);
74
- const session = createCallSession({
75
- conversationId: CONVERSATION_ID,
76
- provider: "twilio",
77
- fromNumber: "+15550001111",
78
- toNumber: "+15550002222",
79
- });
80
- CALL_SESSION_ID = session.id;
81
-
82
- // Pre-create enough pending questions for all tests in a suite run
83
- PENDING_QUESTION_IDS = [];
84
- pqIndex = 0;
85
- for (let i = 0; i < 20; i++) {
86
- const pq = createPendingQuestion(session.id, `Question ${i}`);
87
- PENDING_QUESTION_IDS.push(pq.id);
88
- }
89
- }
90
-
91
- function nextPendingQuestionId(): string {
92
- return PENDING_QUESTION_IDS[pqIndex++];
93
- }
94
-
95
- function clearTables(): void {
96
- const db = getDb();
97
- try {
98
- db.run("DELETE FROM scoped_approval_grants");
99
- } catch {
100
- /* table may not exist */
101
- }
102
- try {
103
- db.run("DELETE FROM guardian_action_deliveries");
104
- } catch {
105
- /* table may not exist */
106
- }
107
- try {
108
- db.run("DELETE FROM guardian_action_requests");
109
- } catch {
110
- /* table may not exist */
111
- }
112
- try {
113
- db.run("DELETE FROM call_pending_questions");
114
- } catch {
115
- /* table may not exist */
116
- }
117
- try {
118
- db.run("DELETE FROM call_events");
119
- } catch {
120
- /* table may not exist */
121
- }
122
- try {
123
- db.run("DELETE FROM call_sessions");
124
- } catch {
125
- /* table may not exist */
126
- }
127
- try {
128
- db.run("DELETE FROM conversations");
129
- } catch {
130
- /* table may not exist */
131
- }
132
- }
133
-
134
- // ── Shared mock generators ──────────────────────────────────────────
135
-
136
- const approveOnceGenerator: ApprovalConversationGenerator = async () => ({
137
- disposition: "approve_once",
138
- replyText: "Approved.",
139
- });
140
-
141
- const rejectGenerator: ApprovalConversationGenerator = async () => ({
142
- disposition: "reject",
143
- replyText: "Denied.",
144
- });
145
-
146
- const keepPendingGenerator: ApprovalConversationGenerator = async () => ({
147
- disposition: "keep_pending",
148
- replyText: "Could you clarify?",
149
- });
150
-
151
- // ── Tests ───────────────────────────────────────────────────────────
152
-
153
- describe("guardian-action grant mint -> voice consume integration", () => {
154
- beforeEach(() => {
155
- clearTables();
156
- ensureFkParents();
157
- });
158
-
159
- test("full flow: resolve guardian action with tool metadata -> mint grant -> voice consume succeeds once", async () => {
160
- const inputDigest = computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT);
161
-
162
- // Step 1: Create a guardian action request with tool metadata
163
- // (simulates the voice ASK_GUARDIAN path)
164
- const request = createGuardianActionRequest({
165
- kind: "ask_guardian",
166
- sourceChannel: "phone",
167
- sourceConversationId: CONVERSATION_ID,
168
- callSessionId: CALL_SESSION_ID,
169
- pendingQuestionId: nextPendingQuestionId(),
170
- questionText: "Can I run rm -rf /tmp/test?",
171
- expiresAt: Date.now() + 60_000,
172
- toolName: TOOL_NAME,
173
- inputDigest,
174
- });
175
-
176
- expect(request.toolName).toBe(TOOL_NAME);
177
- expect(request.inputDigest).toBe(inputDigest);
178
- expect(request.status).toBe("pending");
179
-
180
- // Step 2: Guardian answers -> resolve the request
181
- const resolved = resolveGuardianActionRequest(
182
- request.id,
183
- "yes",
184
- "telegram",
185
- "guardian-user-123",
186
- );
187
- expect(resolved).not.toBeNull();
188
- expect(resolved!.status).toBe("answered");
189
-
190
- // Step 3: Mint a scoped grant from the resolved request
191
- await tryMintGuardianActionGrant({
192
- request: resolved!,
193
- answerText: "yes",
194
- decisionChannel: "telegram",
195
- guardianExternalUserId: "guardian-user-123",
196
- approvalConversationGenerator: approveOnceGenerator,
197
- });
198
-
199
- // Verify the grant was created
200
- const db = getDb();
201
- const grants = db.select().from(scopedApprovalGrants).all();
202
- expect(grants.length).toBe(1);
203
- expect(grants[0].toolName).toBe(TOOL_NAME);
204
- expect(grants[0].inputDigest).toBe(inputDigest);
205
- expect(grants[0].scopeMode).toBe("tool_signature");
206
- expect(grants[0].status).toBe("active");
207
- expect(grants[0].callSessionId).toBe(CALL_SESSION_ID);
208
-
209
- // Step 4: Voice consumer consumes the grant
210
- const consumeResult = consumeScopedApprovalGrantByToolSignature({
211
- toolName: TOOL_NAME,
212
- inputDigest,
213
- consumingRequestId: "voice-req-1",
214
- executionChannel: "phone",
215
- callSessionId: CALL_SESSION_ID,
216
- conversationId: CONVERSATION_ID,
217
- });
218
- expect(consumeResult.ok).toBe(true);
219
- expect(consumeResult.grant).not.toBeNull();
220
- expect(consumeResult.grant!.status).toBe("consumed");
221
- expect(consumeResult.grant!.consumedByRequestId).toBe("voice-req-1");
222
-
223
- // Step 5: Second consume attempt fails (one-time use)
224
- const secondConsume = consumeScopedApprovalGrantByToolSignature({
225
- toolName: TOOL_NAME,
226
- inputDigest,
227
- consumingRequestId: "voice-req-2",
228
- executionChannel: "phone",
229
- callSessionId: CALL_SESSION_ID,
230
- conversationId: CONVERSATION_ID,
231
- });
232
- expect(secondConsume.ok).toBe(false);
233
- expect(secondConsume.grant).toBeNull();
234
- });
235
-
236
- test("no grant minted when guardian action request lacks tool metadata", async () => {
237
- // Create a request without toolName/inputDigest (informational consult)
238
- const request = createGuardianActionRequest({
239
- kind: "ask_guardian",
240
- sourceChannel: "phone",
241
- sourceConversationId: CONVERSATION_ID,
242
- callSessionId: CALL_SESSION_ID,
243
- pendingQuestionId: nextPendingQuestionId(),
244
- questionText: "What should I tell the caller?",
245
- expiresAt: Date.now() + 60_000,
246
- // No toolName or inputDigest
247
- });
248
-
249
- const resolved = resolveGuardianActionRequest(
250
- request.id,
251
- "Tell them to call back",
252
- "vellum",
253
- );
254
- expect(resolved).not.toBeNull();
255
-
256
- await tryMintGuardianActionGrant({
257
- request: resolved!,
258
- answerText: "Tell them to call back",
259
- decisionChannel: "vellum",
260
- approvalConversationGenerator: approveOnceGenerator,
261
- });
262
-
263
- // No grant should have been created
264
- const db = getDb();
265
- const grants = db.select().from(scopedApprovalGrants).all();
266
- expect(grants.length).toBe(0);
267
- });
268
-
269
- test("grant minted via desktop/vellum channel also consumable by voice", async () => {
270
- const inputDigest = computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT);
271
-
272
- const request = createGuardianActionRequest({
273
- kind: "ask_guardian",
274
- sourceChannel: "phone",
275
- sourceConversationId: CONVERSATION_ID,
276
- callSessionId: CALL_SESSION_ID,
277
- pendingQuestionId: nextPendingQuestionId(),
278
- questionText: "Permission to execute?",
279
- expiresAt: Date.now() + 60_000,
280
- toolName: TOOL_NAME,
281
- inputDigest,
282
- });
283
-
284
- // Guardian answers via desktop (vellum channel)
285
- const resolved = resolveGuardianActionRequest(
286
- request.id,
287
- "approve",
288
- "vellum",
289
- );
290
- expect(resolved).not.toBeNull();
291
-
292
- // Mint with decisionChannel: 'vellum' (desktop path)
293
- await tryMintGuardianActionGrant({
294
- request: resolved!,
295
- answerText: "approve",
296
- decisionChannel: "vellum",
297
- approvalConversationGenerator: approveOnceGenerator,
298
- });
299
-
300
- // The grant should have executionChannel: null (wildcard), so voice can consume
301
- const consumeResult = consumeScopedApprovalGrantByToolSignature({
302
- toolName: TOOL_NAME,
303
- inputDigest,
304
- consumingRequestId: "voice-req-desktop",
305
- executionChannel: "phone",
306
- callSessionId: CALL_SESSION_ID,
307
- conversationId: CONVERSATION_ID,
308
- });
309
- expect(consumeResult.ok).toBe(true);
310
- });
311
-
312
- test("no grant minted when guardian answer is a denial", async () => {
313
- const inputDigest = computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT);
314
-
315
- const request = createGuardianActionRequest({
316
- kind: "ask_guardian",
317
- sourceChannel: "phone",
318
- sourceConversationId: CONVERSATION_ID,
319
- callSessionId: CALL_SESSION_ID,
320
- pendingQuestionId: nextPendingQuestionId(),
321
- questionText: "Can I run rm -rf /tmp/test?",
322
- expiresAt: Date.now() + 60_000,
323
- toolName: TOOL_NAME,
324
- inputDigest,
325
- });
326
-
327
- // Guardian explicitly denies the action
328
- const resolved = resolveGuardianActionRequest(
329
- request.id,
330
- "No",
331
- "telegram",
332
- "guardian-user-456",
333
- );
334
- expect(resolved).not.toBeNull();
335
-
336
- await tryMintGuardianActionGrant({
337
- request: resolved!,
338
- answerText: "No",
339
- decisionChannel: "telegram",
340
- guardianExternalUserId: "guardian-user-456",
341
- approvalConversationGenerator: rejectGenerator,
342
- });
343
-
344
- // No grant should have been created for a denial
345
- const db = getDb();
346
- const grants = db.select().from(scopedApprovalGrants).all();
347
- expect(grants.length).toBe(0);
348
- });
349
-
350
- test("no grant minted when classifier returns reject", async () => {
351
- const inputDigest = computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT);
352
-
353
- const request = createGuardianActionRequest({
354
- kind: "ask_guardian",
355
- sourceChannel: "phone",
356
- sourceConversationId: CONVERSATION_ID,
357
- callSessionId: CALL_SESSION_ID,
358
- pendingQuestionId: nextPendingQuestionId(),
359
- questionText: "Permission to execute?",
360
- expiresAt: Date.now() + 60_000,
361
- toolName: TOOL_NAME,
362
- inputDigest,
363
- });
364
-
365
- const resolved = resolveGuardianActionRequest(
366
- request.id,
367
- "deny",
368
- "telegram",
369
- );
370
- expect(resolved).not.toBeNull();
371
-
372
- await tryMintGuardianActionGrant({
373
- request: resolved!,
374
- answerText: "deny",
375
- decisionChannel: "telegram",
376
- approvalConversationGenerator: rejectGenerator,
377
- });
378
-
379
- const db = getDb();
380
- const grants = db.select().from(scopedApprovalGrants).all();
381
- expect(grants.length).toBe(0);
382
- });
383
- });
384
-
385
- // ---------------------------------------------------------------------------
386
- // Conversational engine classification tests
387
- // ---------------------------------------------------------------------------
388
-
389
- describe("guardian-action grant minter: conversational engine classification", () => {
390
- beforeEach(() => {
391
- clearTables();
392
- ensureFkParents();
393
- });
394
-
395
- test("approval via conversational engine mints a grant", async () => {
396
- const inputDigest = computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT);
397
-
398
- const request = createGuardianActionRequest({
399
- kind: "ask_guardian",
400
- sourceChannel: "phone",
401
- sourceConversationId: CONVERSATION_ID,
402
- callSessionId: CALL_SESSION_ID,
403
- pendingQuestionId: nextPendingQuestionId(),
404
- questionText: "Can I run the command?",
405
- expiresAt: Date.now() + 60_000,
406
- toolName: TOOL_NAME,
407
- inputDigest,
408
- });
409
-
410
- const resolved = resolveGuardianActionRequest(
411
- request.id,
412
- "yes",
413
- "telegram",
414
- );
415
- expect(resolved).not.toBeNull();
416
-
417
- await tryMintGuardianActionGrant({
418
- request: resolved!,
419
- answerText: "yes",
420
- decisionChannel: "telegram",
421
- approvalConversationGenerator: approveOnceGenerator,
422
- });
423
-
424
- const db = getDb();
425
- const grants = db.select().from(scopedApprovalGrants).all();
426
- expect(grants.length).toBe(1);
427
- expect(grants[0].toolName).toBe(TOOL_NAME);
428
- });
429
-
430
- test("free-form approval via conversational engine mints a grant", async () => {
431
- const inputDigest = computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT);
432
-
433
- const request = createGuardianActionRequest({
434
- kind: "ask_guardian",
435
- sourceChannel: "phone",
436
- sourceConversationId: CONVERSATION_ID,
437
- callSessionId: CALL_SESSION_ID,
438
- pendingQuestionId: nextPendingQuestionId(),
439
- questionText: "Can I run the command?",
440
- expiresAt: Date.now() + 60_000,
441
- toolName: TOOL_NAME,
442
- inputDigest,
443
- });
444
-
445
- const resolved = resolveGuardianActionRequest(
446
- request.id,
447
- "Sure, go ahead and run it",
448
- "telegram",
449
- );
450
- expect(resolved).not.toBeNull();
451
-
452
- await tryMintGuardianActionGrant({
453
- request: resolved!,
454
- answerText: "Sure, go ahead and run it",
455
- decisionChannel: "telegram",
456
- approvalConversationGenerator: approveOnceGenerator,
457
- });
458
-
459
- const db = getDb();
460
- const grants = db.select().from(scopedApprovalGrants).all();
461
- expect(grants.length).toBe(1);
462
- expect(grants[0].toolName).toBe(TOOL_NAME);
463
- });
464
-
465
- test("ambiguous text returns keep_pending from generator, no grant minted", async () => {
466
- const inputDigest = computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT);
467
-
468
- const request = createGuardianActionRequest({
469
- kind: "ask_guardian",
470
- sourceChannel: "phone",
471
- sourceConversationId: CONVERSATION_ID,
472
- callSessionId: CALL_SESSION_ID,
473
- pendingQuestionId: nextPendingQuestionId(),
474
- questionText: "Can I run the command?",
475
- expiresAt: Date.now() + 60_000,
476
- toolName: TOOL_NAME,
477
- inputDigest,
478
- });
479
-
480
- const resolved = resolveGuardianActionRequest(
481
- request.id,
482
- "I'm not sure about this",
483
- "telegram",
484
- );
485
- expect(resolved).not.toBeNull();
486
-
487
- await tryMintGuardianActionGrant({
488
- request: resolved!,
489
- answerText: "I'm not sure about this",
490
- decisionChannel: "telegram",
491
- approvalConversationGenerator: keepPendingGenerator,
492
- });
493
-
494
- const db = getDb();
495
- const grants = db.select().from(scopedApprovalGrants).all();
496
- expect(grants.length).toBe(0);
497
- });
498
-
499
- test("generator failure falls back to no grant (fail-closed)", async () => {
500
- const inputDigest = computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT);
501
-
502
- const request = createGuardianActionRequest({
503
- kind: "ask_guardian",
504
- sourceChannel: "phone",
505
- sourceConversationId: CONVERSATION_ID,
506
- callSessionId: CALL_SESSION_ID,
507
- pendingQuestionId: nextPendingQuestionId(),
508
- questionText: "Can I run the command?",
509
- expiresAt: Date.now() + 60_000,
510
- toolName: TOOL_NAME,
511
- inputDigest,
512
- });
513
-
514
- const resolved = resolveGuardianActionRequest(
515
- request.id,
516
- "Sure, go ahead and run it",
517
- "telegram",
518
- );
519
- expect(resolved).not.toBeNull();
520
-
521
- const failingGenerator: ApprovalConversationGenerator = async () => {
522
- throw new Error("LLM provider unavailable");
523
- };
524
-
525
- await tryMintGuardianActionGrant({
526
- request: resolved!,
527
- answerText: "Sure, go ahead and run it",
528
- decisionChannel: "telegram",
529
- approvalConversationGenerator: failingGenerator,
530
- });
531
-
532
- const db = getDb();
533
- const grants = db.select().from(scopedApprovalGrants).all();
534
- expect(grants.length).toBe(0);
535
- });
536
-
537
- test("invalid disposition from generator does not produce a grant (fail-closed)", async () => {
538
- const inputDigest = computeToolApprovalDigest(TOOL_NAME, TOOL_INPUT);
539
-
540
- const request = createGuardianActionRequest({
541
- kind: "ask_guardian",
542
- sourceChannel: "phone",
543
- sourceConversationId: CONVERSATION_ID,
544
- callSessionId: CALL_SESSION_ID,
545
- pendingQuestionId: nextPendingQuestionId(),
546
- questionText: "Can I run the command?",
547
- expiresAt: Date.now() + 60_000,
548
- toolName: TOOL_NAME,
549
- inputDigest,
550
- });
551
-
552
- const resolved = resolveGuardianActionRequest(
553
- request.id,
554
- "Sure, go ahead and run it",
555
- "telegram",
556
- );
557
- expect(resolved).not.toBeNull();
558
-
559
- // Generator returns an unknown/invalid disposition — the approval-
560
- // conversation-turn layer will fail-closed and return keep_pending.
561
- const mockGenerator: ApprovalConversationGenerator = async () => ({
562
- disposition: "approve_always" as "approve_once",
563
- replyText: "Approved.",
564
- });
565
-
566
- await tryMintGuardianActionGrant({
567
- request: resolved!,
568
- answerText: "Sure, go ahead and run it",
569
- decisionChannel: "telegram",
570
- approvalConversationGenerator: mockGenerator,
571
- });
572
-
573
- // No grant -- invalid disposition causes fail-closed (keep_pending),
574
- // so isApproval is false and no grant is minted.
575
- const db = getDb();
576
- const grants = db.select().from(scopedApprovalGrants).all();
577
- expect(grants.length).toBe(0);
578
- });
579
- });
@@ -1,106 +0,0 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
2
-
3
- mock.module("../util/logger.js", () => ({
4
- getLogger: () =>
5
- new Proxy({} as Record<string, unknown>, {
6
- get: () => () => {},
7
- }),
8
- }));
9
-
10
- import {
11
- createCallSession,
12
- createPendingQuestion,
13
- } from "../calls/call-store.js";
14
- import { getDb } from "../memory/db-connection.js";
15
- import { initializeDb } from "../memory/db-init.js";
16
- import {
17
- cancelGuardianActionRequest,
18
- createGuardianActionDelivery,
19
- createGuardianActionRequest,
20
- getDeliveriesByRequestId,
21
- getGuardianActionRequest,
22
- updateDeliveryStatus,
23
- } from "../memory/guardian-action-store.js";
24
- import { conversations } from "../memory/schema.js";
25
-
26
- initializeDb();
27
-
28
- function ensureConversation(id: string): void {
29
- const db = getDb();
30
- const now = Date.now();
31
- db.insert(conversations)
32
- .values({
33
- id,
34
- title: `Conversation ${id}`,
35
- createdAt: now,
36
- updatedAt: now,
37
- })
38
- .run();
39
- }
40
-
41
- function resetTables(): void {
42
- const db = getDb();
43
- db.run("DELETE FROM guardian_action_deliveries");
44
- db.run("DELETE FROM guardian_action_requests");
45
- db.run("DELETE FROM call_pending_questions");
46
- db.run("DELETE FROM call_events");
47
- db.run("DELETE FROM call_sessions");
48
- db.run("DELETE FROM conversations");
49
- }
50
-
51
- describe("guardian-action-store", () => {
52
- beforeEach(() => {
53
- resetTables();
54
- });
55
-
56
- test("cancelGuardianActionRequest cancels both pending and sent deliveries", () => {
57
- const conversationId = "conv-guardian-cancel";
58
- ensureConversation(conversationId);
59
-
60
- const session = createCallSession({
61
- conversationId,
62
- provider: "twilio",
63
- fromNumber: "+15550001111",
64
- toNumber: "+15550002222",
65
- });
66
- const pendingQuestion = createPendingQuestion(
67
- session.id,
68
- "What is our gate code?",
69
- );
70
-
71
- const request = createGuardianActionRequest({
72
- kind: "ask_guardian",
73
- sourceChannel: "phone",
74
- sourceConversationId: conversationId,
75
- callSessionId: session.id,
76
- pendingQuestionId: pendingQuestion.id,
77
- questionText: pendingQuestion.questionText,
78
- expiresAt: Date.now() + 60_000,
79
- });
80
-
81
- const pendingDelivery = createGuardianActionDelivery({
82
- requestId: request.id,
83
- destinationChannel: "vellum",
84
- destinationConversationId: "conv-mac-guardian",
85
- });
86
- const sentDelivery = createGuardianActionDelivery({
87
- requestId: request.id,
88
- destinationChannel: "telegram",
89
- destinationChatId: "chat-guardian",
90
- destinationExternalUserId: "guardian-user",
91
- });
92
- updateDeliveryStatus(sentDelivery.id, "sent");
93
-
94
- cancelGuardianActionRequest(request.id);
95
-
96
- const updatedRequest = getGuardianActionRequest(request.id);
97
- expect(updatedRequest).not.toBeNull();
98
- expect(updatedRequest!.status).toBe("cancelled");
99
-
100
- const deliveries = getDeliveriesByRequestId(request.id);
101
- const pendingAfter = deliveries.find((d) => d.id === pendingDelivery.id);
102
- const sentAfter = deliveries.find((d) => d.id === sentDelivery.id);
103
- expect(pendingAfter?.status).toBe("cancelled");
104
- expect(sentAfter?.status).toBe("cancelled");
105
- });
106
- });