@vellumai/assistant 0.4.41 → 0.4.43

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 (843) hide show
  1. package/.env.example +1 -6
  2. package/.prettierignore +3 -0
  3. package/ARCHITECTURE.md +131 -393
  4. package/Dockerfile +0 -1
  5. package/README.md +73 -83
  6. package/bun.lock +8 -2
  7. package/docs/architecture/integrations.md +16 -21
  8. package/docs/architecture/memory.md +1 -1
  9. package/docs/architecture/scheduling.md +63 -63
  10. package/docs/architecture/security.md +3 -3
  11. package/docs/runbook-trusted-contacts.md +11 -12
  12. package/docs/trusted-contact-access.md +39 -39
  13. package/package.json +5 -8
  14. package/src/__tests__/access-request-decision.test.ts +4 -4
  15. package/src/__tests__/active-skill-tools.test.ts +49 -34
  16. package/src/__tests__/actor-token-service.test.ts +55 -85
  17. package/src/__tests__/amazon-cdp-integration.test.ts +14 -26
  18. package/src/__tests__/app-bundler.test.ts +14 -368
  19. package/src/__tests__/app-compiler.test.ts +0 -1
  20. package/src/__tests__/app-executors.test.ts +10 -1
  21. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +1 -1
  22. package/src/__tests__/approval-primitive.test.ts +2 -4
  23. package/src/__tests__/approval-routes-http.test.ts +1 -1
  24. package/src/__tests__/asset-materialize-tool.test.ts +1 -4
  25. package/src/__tests__/asset-search-tool.test.ts +1 -4
  26. package/src/__tests__/assistant-attachments.test.ts +23 -0
  27. package/src/__tests__/assistant-feature-flags-integration.test.ts +4 -8
  28. package/src/__tests__/assistant-id-boundary-guard.test.ts +5 -5
  29. package/src/__tests__/attachments-store.test.ts +1 -4
  30. package/src/__tests__/avatar-e2e.test.ts +43 -23
  31. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  32. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -9
  33. package/src/__tests__/call-controller.test.ts +4 -8
  34. package/src/__tests__/call-conversation-messages.test.ts +1 -1
  35. package/src/__tests__/call-domain.test.ts +250 -8
  36. package/src/__tests__/call-pointer-message-composer.test.ts +14 -14
  37. package/src/__tests__/call-pointer-messages.test.ts +7 -11
  38. package/src/__tests__/call-recovery.test.ts +47 -0
  39. package/src/__tests__/call-routes-http.test.ts +13 -0
  40. package/src/__tests__/call-start-guardian-guard.test.ts +1 -1
  41. package/src/__tests__/callback-handoff-copy.test.ts +5 -5
  42. package/src/__tests__/canonical-guardian-store.test.ts +3 -3
  43. package/src/__tests__/channel-approval-routes.test.ts +101 -134
  44. package/src/__tests__/channel-approval.test.ts +0 -201
  45. package/src/__tests__/channel-approvals.test.ts +2 -2
  46. package/src/__tests__/channel-delivery-store.test.ts +16 -24
  47. package/src/__tests__/channel-guardian.test.ts +641 -740
  48. package/src/__tests__/channel-invite-transport.test.ts +1 -2
  49. package/src/__tests__/channel-policy.test.ts +9 -12
  50. package/src/__tests__/channel-readiness-service.test.ts +156 -45
  51. package/src/__tests__/channel-reply-delivery.test.ts +3 -3
  52. package/src/__tests__/channel-retry-sweep.test.ts +7 -7
  53. package/src/__tests__/checker.test.ts +10 -7
  54. package/src/__tests__/chrome-cdp.test.ts +57 -17
  55. package/src/__tests__/cli-help-reference-sync.test.ts +26 -0
  56. package/src/__tests__/compaction.benchmark.test.ts +25 -5
  57. package/src/__tests__/computer-use-session-lifecycle.test.ts +1 -1
  58. package/src/__tests__/computer-use-session-working-dir.test.ts +2 -6
  59. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
  60. package/src/__tests__/config-loader-backfill.test.ts +310 -0
  61. package/src/__tests__/config-watcher.test.ts +1 -5
  62. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +3 -5
  63. package/src/__tests__/connection-policy.test.ts +3 -62
  64. package/src/__tests__/contacts-tools.test.ts +0 -2
  65. package/src/__tests__/context-memory-e2e.test.ts +11 -7
  66. package/src/__tests__/context-overflow-policy.test.ts +2 -2
  67. package/src/__tests__/context-window-manager.test.ts +220 -61
  68. package/src/__tests__/conversation-attention-store.test.ts +178 -2
  69. package/src/__tests__/conversation-attention-telegram.test.ts +8 -11
  70. package/src/__tests__/conversation-pairing.test.ts +14 -14
  71. package/src/__tests__/conversation-routes-guardian-reply.test.ts +1 -1
  72. package/src/__tests__/conversation-store.test.ts +2 -2
  73. package/src/__tests__/conversation-unread-route.test.ts +155 -0
  74. package/src/__tests__/credential-metadata-store.test.ts +0 -2
  75. package/src/__tests__/credential-security-invariants.test.ts +9 -16
  76. package/src/__tests__/credentials-cli.test.ts +49 -5
  77. package/src/__tests__/daemon-assistant-events.test.ts +4 -22
  78. package/src/__tests__/db-migration-rollback.test.ts +2 -2
  79. package/src/__tests__/deterministic-verification-control-plane.test.ts +19 -19
  80. package/src/__tests__/dictation-mode-detection.test.ts +1 -1
  81. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  82. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -6
  83. package/src/__tests__/email-cli.test.ts +12 -12
  84. package/src/__tests__/email-service-config-fallback.test.ts +1 -1
  85. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -18
  86. package/src/__tests__/event-bus.test.ts +0 -1
  87. package/src/__tests__/followup-tools.test.ts +0 -2
  88. package/src/__tests__/gateway-client-managed-outbound.test.ts +6 -6
  89. package/src/__tests__/gateway-only-enforcement.test.ts +13 -77
  90. package/src/__tests__/gateway-only-guard.test.ts +5 -0
  91. package/src/__tests__/guardian-action-conversation-turn.test.ts +3 -3
  92. package/src/__tests__/guardian-action-followup-executor.test.ts +29 -94
  93. package/src/__tests__/guardian-action-followup-store.test.ts +2 -12
  94. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +48 -194
  95. package/src/__tests__/guardian-action-late-reply.test.ts +12 -12
  96. package/src/__tests__/guardian-action-store.test.ts +2 -2
  97. package/src/__tests__/guardian-action-sweep.test.ts +5 -5
  98. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +1 -3
  99. package/src/__tests__/guardian-dispatch.test.ts +5 -46
  100. package/src/__tests__/guardian-grant-minting.test.ts +5 -44
  101. package/src/__tests__/guardian-outbound-http.test.ts +95 -114
  102. package/src/__tests__/guardian-question-mode.test.ts +1 -4
  103. package/src/__tests__/guardian-routing-invariants.test.ts +5 -13
  104. package/src/__tests__/guardian-routing-state.test.ts +3 -3
  105. package/src/__tests__/guardian-verification-voice-binding.test.ts +64 -7
  106. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +2 -2
  107. package/src/__tests__/handle-user-message-secret-resume.test.ts +3 -5
  108. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +16 -34
  109. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  110. package/src/__tests__/headless-browser-navigate.test.ts +1 -1
  111. package/src/__tests__/headless-browser-read-tools.test.ts +1 -1
  112. package/src/__tests__/headless-browser-snapshot.test.ts +1 -1
  113. package/src/__tests__/heartbeat-service.test.ts +1 -1
  114. package/src/__tests__/home-base-bootstrap.test.ts +0 -2
  115. package/src/__tests__/host-shell-tool.test.ts +3 -12
  116. package/src/__tests__/inbound-invite-redemption.test.ts +2 -2
  117. package/src/__tests__/ingress-url-consistency.test.ts +0 -64
  118. package/src/__tests__/integration-status.test.ts +8 -8
  119. package/src/__tests__/intent-routing.test.ts +9 -13
  120. package/src/__tests__/invite-redemption-service.test.ts +4 -4
  121. package/src/__tests__/invite-routes-http.test.ts +10 -10
  122. package/src/__tests__/llm-usage-store.test.ts +45 -9
  123. package/src/__tests__/local-gateway-health.test.ts +209 -0
  124. package/src/__tests__/managed-avatar-client.test.ts +23 -12
  125. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -2
  126. package/src/__tests__/managed-store.test.ts +29 -12
  127. package/src/__tests__/managed-twitter-guardrails.test.ts +353 -0
  128. package/src/__tests__/mcp-cli.test.ts +1 -1
  129. package/src/__tests__/mcp-health-check.test.ts +1 -1
  130. package/src/__tests__/media-generate-image.test.ts +1 -1
  131. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -4
  132. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +9 -6
  133. package/src/__tests__/memory-regressions.test.ts +1 -166
  134. package/src/__tests__/messaging-send-tool.test.ts +8 -4
  135. package/src/__tests__/migration-export-http.test.ts +2 -2
  136. package/src/__tests__/migration-transport.test.ts +44 -0
  137. package/src/__tests__/non-member-access-request.test.ts +49 -36
  138. package/src/__tests__/notification-broadcaster.test.ts +15 -15
  139. package/src/__tests__/notification-decision-fallback.test.ts +2 -2
  140. package/src/__tests__/notification-decision-strategy.test.ts +4 -4
  141. package/src/__tests__/notification-deep-link.test.ts +3 -3
  142. package/src/__tests__/notification-guardian-path.test.ts +6 -44
  143. package/src/__tests__/notification-routing-intent.test.ts +11 -7
  144. package/src/__tests__/oauth-cli.test.ts +1 -1
  145. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -6
  146. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  147. package/src/__tests__/platform.test.ts +168 -5
  148. package/src/__tests__/playbook-execution.test.ts +0 -2
  149. package/src/__tests__/playbook-tools.test.ts +0 -2
  150. package/src/__tests__/pricing.test.ts +125 -0
  151. package/src/__tests__/provider-error-scenarios.test.ts +9 -3
  152. package/src/__tests__/recording-handler.test.ts +46 -80
  153. package/src/__tests__/recording-state-machine.test.ts +112 -183
  154. package/src/__tests__/registry.test.ts +1 -1
  155. package/src/__tests__/relay-server.test.ts +69 -71
  156. package/src/__tests__/reminder-store.test.ts +3 -3
  157. package/src/__tests__/request-file-tool.test.ts +2 -2
  158. package/src/__tests__/ride-shotgun-handler.test.ts +2 -33
  159. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -3
  160. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  161. package/src/__tests__/scaffold-managed-skill-tool.test.ts +4 -4
  162. package/src/__tests__/schedule-store.test.ts +13 -4
  163. package/src/__tests__/schedule-tools.test.ts +0 -2
  164. package/src/__tests__/scheduler-recurrence.test.ts +3 -4
  165. package/src/__tests__/scoped-approval-grants.test.ts +3 -5
  166. package/src/__tests__/scoped-grant-security-matrix.test.ts +6 -8
  167. package/src/__tests__/secret-prompt-log-hygiene.test.ts +1 -1
  168. package/src/__tests__/secret-response-routing.test.ts +1 -1
  169. package/src/__tests__/send-endpoint-busy.test.ts +1 -1
  170. package/src/__tests__/sequence-store.test.ts +0 -2
  171. package/src/__tests__/server-history-render.test.ts +2 -199
  172. package/src/__tests__/session-abort-tool-results.test.ts +9 -3
  173. package/src/__tests__/session-agent-loop.test.ts +107 -3
  174. package/src/__tests__/session-confirmation-signals.test.ts +10 -4
  175. package/src/__tests__/session-conflict-gate.test.ts +9 -3
  176. package/src/__tests__/session-init.benchmark.test.ts +22 -13
  177. package/src/__tests__/session-load-history-repair.test.ts +6 -3
  178. package/src/__tests__/session-pre-run-repair.test.ts +9 -3
  179. package/src/__tests__/session-profile-injection.test.ts +9 -3
  180. package/src/__tests__/session-provider-retry-repair.test.ts +10 -4
  181. package/src/__tests__/session-queue.test.ts +10 -4
  182. package/src/__tests__/session-runtime-assembly.test.ts +28 -18
  183. package/src/__tests__/session-skill-tools.test.ts +2 -3
  184. package/src/__tests__/session-slash-known.test.ts +11 -4
  185. package/src/__tests__/session-slash-queue.test.ts +11 -4
  186. package/src/__tests__/session-slash-unknown.test.ts +12 -4
  187. package/src/__tests__/session-surfaces-deselection.test.ts +2 -2
  188. package/src/__tests__/session-surfaces-task-progress.test.ts +3 -3
  189. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -1
  190. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -1
  191. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -1
  192. package/src/__tests__/session-usage.test.ts +180 -0
  193. package/src/__tests__/session-workspace-cache-state.test.ts +8 -2
  194. package/src/__tests__/session-workspace-injection.test.ts +8 -2
  195. package/src/__tests__/session-workspace-tool-tracking.test.ts +8 -2
  196. package/src/__tests__/skill-feature-flags-integration.test.ts +5 -11
  197. package/src/__tests__/skill-feature-flags.test.ts +1 -0
  198. package/src/__tests__/skill-include-graph.test.ts +1 -0
  199. package/src/__tests__/skill-load-feature-flag.test.ts +3 -9
  200. package/src/__tests__/skill-load-tool.test.ts +90 -12
  201. package/src/__tests__/skill-projection-feature-flag.test.ts +14 -15
  202. package/src/__tests__/skills-uninstall.test.ts +131 -0
  203. package/src/__tests__/skills.test.ts +32 -16
  204. package/src/__tests__/slack-block-formatting.test.ts +1 -1
  205. package/src/__tests__/slack-channel-config.test.ts +71 -12
  206. package/src/__tests__/slack-inbound-verification.test.ts +7 -7
  207. package/src/__tests__/slack-share-routes.test.ts +1 -1
  208. package/src/__tests__/slack-skill.test.ts +2 -2
  209. package/src/__tests__/slash-commands-catalog.test.ts +1 -0
  210. package/src/__tests__/slash-commands-resolver.test.ts +1 -0
  211. package/src/__tests__/starter-task-flow.test.ts +1 -1
  212. package/src/__tests__/subagent-manager-notify.test.ts +1 -1
  213. package/src/__tests__/subagent-tools.test.ts +2 -2
  214. package/src/__tests__/system-prompt.test.ts +4 -8
  215. package/src/__tests__/task-compiler.test.ts +0 -2
  216. package/src/__tests__/task-management-tools.test.ts +0 -2
  217. package/src/__tests__/task-runner.test.ts +0 -2
  218. package/src/__tests__/task-scheduler.test.ts +2 -2
  219. package/src/__tests__/telegram-bot-username-resolution.test.ts +46 -44
  220. package/src/__tests__/terminal-tools.test.ts +1 -11
  221. package/src/__tests__/thread-seed-composer.test.ts +3 -1
  222. package/src/__tests__/tool-approval-handler.test.ts +5 -7
  223. package/src/__tests__/tool-executor.test.ts +2 -2
  224. package/src/__tests__/tool-grant-request-escalation.test.ts +3 -5
  225. package/src/__tests__/tool-notification-listener.test.ts +1 -1
  226. package/src/__tests__/tool-profiling-listener.test.ts +1 -1
  227. package/src/__tests__/tool-trace-listener.test.ts +1 -2
  228. package/src/__tests__/trace-emitter.test.ts +1 -1
  229. package/src/__tests__/trust-context-guards.test.ts +1 -1
  230. package/src/__tests__/trust-store.test.ts +44 -395
  231. package/src/__tests__/trusted-contact-approval-notifier.test.ts +6 -8
  232. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +5 -7
  233. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +6 -6
  234. package/src/__tests__/trusted-contact-multichannel.test.ts +54 -47
  235. package/src/__tests__/trusted-contact-verification.test.ts +12 -12
  236. package/src/__tests__/twilio-config.test.ts +11 -2
  237. package/src/__tests__/twilio-provider.test.ts +6 -4
  238. package/src/__tests__/twilio-routes.test.ts +408 -86
  239. package/src/__tests__/twitter-platform-proxy-client.test.ts +450 -0
  240. package/src/__tests__/update-bulletin-format.test.ts +1 -1
  241. package/src/__tests__/update-bulletin-state.test.ts +1 -1
  242. package/src/__tests__/update-bulletin.test.ts +4 -8
  243. package/src/__tests__/update-template-contract.test.ts +1 -1
  244. package/src/__tests__/usage-cache-backfill-migration.test.ts +406 -0
  245. package/src/__tests__/usage-routes.test.ts +23 -5
  246. package/src/__tests__/user-reference.test.ts +1 -1
  247. package/src/__tests__/{guardian-control-plane-policy.test.ts → verification-control-plane-policy.test.ts} +142 -170
  248. package/src/__tests__/{guardian-verification-intent-routing.test.ts → verification-session-intent-routing.test.ts} +16 -16
  249. package/src/__tests__/view-image-tool.test.ts +0 -2
  250. package/src/__tests__/voice-ingress-preflight.test.ts +36 -0
  251. package/src/__tests__/voice-invite-redemption.test.ts +18 -18
  252. package/src/__tests__/voice-scoped-grant-consumer.test.ts +7 -7
  253. package/src/__tests__/voice-session-bridge.test.ts +14 -16
  254. package/src/__tests__/workspace-policy.test.ts +1 -1
  255. package/src/approvals/AGENTS.md +4 -4
  256. package/src/approvals/approval-primitive.ts +2 -2
  257. package/src/approvals/guardian-decision-primitive.ts +1 -1
  258. package/src/approvals/guardian-request-resolvers.ts +3 -4
  259. package/src/bundler/app-bundler.ts +29 -217
  260. package/src/bundler/app-compiler.ts +131 -103
  261. package/src/bundler/compiler-tools.ts +248 -0
  262. package/src/calls/active-call-lease.ts +207 -0
  263. package/src/calls/call-constants.ts +0 -7
  264. package/src/calls/call-controller.ts +1 -1
  265. package/src/calls/call-conversation-messages.ts +6 -6
  266. package/src/calls/call-domain.ts +73 -38
  267. package/src/calls/call-pointer-message-composer.ts +6 -6
  268. package/src/calls/call-pointer-messages.ts +14 -13
  269. package/src/calls/call-recovery.ts +2 -0
  270. package/src/calls/call-store.ts +21 -28
  271. package/src/calls/guardian-action-sweep.ts +6 -8
  272. package/src/calls/guardian-dispatch.ts +2 -6
  273. package/src/calls/relay-access-wait.ts +4 -4
  274. package/src/calls/relay-server.ts +69 -80
  275. package/src/calls/relay-setup-router.ts +16 -21
  276. package/src/calls/relay-verification.ts +27 -28
  277. package/src/calls/twilio-config.ts +28 -3
  278. package/src/calls/twilio-provider.ts +5 -5
  279. package/src/calls/twilio-rest.ts +26 -27
  280. package/src/calls/twilio-routes.ts +67 -54
  281. package/src/calls/types.ts +8 -8
  282. package/src/calls/voice-ingress-preflight.ts +110 -0
  283. package/src/calls/voice-session-bridge.ts +7 -7
  284. package/src/channels/config.ts +1 -10
  285. package/src/{config/channel-permission-profiles.ts → channels/permission-profiles.ts} +1 -1
  286. package/src/channels/types.ts +2 -13
  287. package/src/cli/__tests__/notifications.test.ts +1 -1
  288. package/src/{amazon → cli/commands/amazon}/client.ts +99 -42
  289. package/src/cli/{amazon.ts → commands/amazon/index.ts} +12 -17
  290. package/src/{amazon → cli/commands/amazon}/request-extractor.ts +39 -3
  291. package/src/cli/commands/amazon/session.ts +116 -0
  292. package/src/cli/{audit.ts → commands/audit.ts} +2 -4
  293. package/src/cli/{autonomy.ts → commands/autonomy.ts} +1 -3
  294. package/src/cli/commands/browser-relay.ts +520 -0
  295. package/src/cli/commands/channel-verification-sessions.ts +442 -0
  296. package/src/cli/{completions.ts → commands/completions.ts} +1 -3
  297. package/src/cli/{config.ts → commands/config.ts} +3 -5
  298. package/src/cli/{contacts.ts → commands/contacts.ts} +263 -16
  299. package/src/cli/{credentials.ts → commands/credentials.ts} +9 -10
  300. package/src/cli/{default-action.ts → commands/default-action.ts} +3 -3
  301. package/src/cli/{dev.ts → commands/dev.ts} +4 -6
  302. package/src/cli/{doctor.ts → commands/doctor.ts} +36 -60
  303. package/src/cli/{email.ts → commands/email.ts} +2 -2
  304. package/src/cli/{keys.ts → commands/keys.ts} +6 -6
  305. package/src/cli/{map.ts → commands/map.ts} +85 -93
  306. package/src/cli/{mcp.ts → commands/mcp.ts} +5 -7
  307. package/src/cli/{memory.ts → commands/memory.ts} +6 -7
  308. package/src/cli/{notifications.ts → commands/notifications.ts} +8 -10
  309. package/src/cli/{oauth.ts → commands/oauth.ts} +2 -2
  310. package/src/cli/commands/platform.ts +176 -0
  311. package/src/cli/{sequence.ts → commands/sequence.ts} +3 -3
  312. package/src/cli/{sessions.ts → commands/sessions.ts} +32 -52
  313. package/src/cli/commands/skills.ts +498 -0
  314. package/src/cli/{trust.ts → commands/trust.ts} +2 -4
  315. package/src/{__tests__/twitter-cli-error-shaping.test.ts → cli/commands/twitter/__tests__/cli-error-shaping.test.ts} +43 -2
  316. package/src/cli/commands/twitter/__tests__/cli-read-routing.test.ts +483 -0
  317. package/src/{__tests__/twitter-cli-routing.test.ts → cli/commands/twitter/__tests__/cli-routing.test.ts} +130 -4
  318. package/src/{__tests__/twitter-oauth-client.test.ts → cli/commands/twitter/__tests__/oauth-client.test.ts} +2 -2
  319. package/src/{twitter → cli/commands/twitter}/client.ts +17 -7
  320. package/src/cli/{twitter.ts → commands/twitter/index.ts} +322 -273
  321. package/src/cli/commands/twitter/router.ts +396 -0
  322. package/src/cli/commands/twitter/session.ts +121 -0
  323. package/src/cli/db.ts +1 -0
  324. package/src/cli/http-client.ts +87 -0
  325. package/src/cli/logger.ts +6 -0
  326. package/src/cli/main-screen.tsx +4 -3
  327. package/src/cli/output.ts +19 -0
  328. package/src/cli/program.ts +29 -27
  329. package/src/cli/reference.ts +27 -37
  330. package/src/cli.ts +452 -240
  331. package/src/config/assistant-feature-flags.ts +3 -15
  332. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +3 -6
  333. package/src/config/bundled-skills/agentmail/SKILL.md +4 -4
  334. package/src/config/bundled-skills/amazon/SKILL.md +15 -5
  335. package/src/config/bundled-skills/api-mapping/SKILL.md +4 -4
  336. package/src/config/bundled-skills/app-builder/SKILL.md +21 -6
  337. package/src/config/bundled-skills/browser/SKILL.md +4 -5
  338. package/src/config/bundled-skills/chatgpt-import/SKILL.md +4 -4
  339. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  340. package/src/config/bundled-skills/claude-code/SKILL.md +4 -4
  341. package/src/config/bundled-skills/cli-discover/SKILL.md +4 -4
  342. package/src/config/bundled-skills/computer-use/SKILL.md +4 -4
  343. package/src/config/bundled-skills/contacts/SKILL.md +87 -229
  344. package/src/config/bundled-skills/deploy-fullstack-vercel/SKILL.md +4 -4
  345. package/src/config/bundled-skills/document/SKILL.md +4 -3
  346. package/src/config/bundled-skills/document-writer/SKILL.md +4 -4
  347. package/src/config/bundled-skills/doordash/SKILL.md +4 -11
  348. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +8 -16
  349. package/src/config/bundled-skills/doordash/doordash-cli.ts +120 -86
  350. package/src/config/bundled-skills/doordash/lib/session.ts +1 -2
  351. package/src/config/bundled-skills/doordash/lib/shared/platform.ts +26 -9
  352. package/src/config/bundled-skills/elevenlabs-voice/SKILL.md +140 -0
  353. package/src/config/bundled-skills/email-setup/SKILL.md +4 -4
  354. package/src/config/bundled-skills/followups/SKILL.md +4 -3
  355. package/src/config/bundled-skills/frontend-design/SKILL.md +2 -0
  356. package/src/config/bundled-skills/google-calendar/SKILL.md +4 -4
  357. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +4 -6
  358. package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +26 -41
  359. package/src/config/bundled-skills/image-studio/SKILL.md +4 -5
  360. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -1
  361. package/src/config/bundled-skills/influencer/SKILL.md +19 -19
  362. package/src/{influencer → config/bundled-skills/influencer/scripts}/client.ts +73 -56
  363. package/src/config/bundled-skills/influencer/scripts/influencer.ts +267 -0
  364. package/src/config/bundled-skills/knowledge-graph/SKILL.md +4 -2
  365. package/src/config/bundled-skills/macos-automation/SKILL.md +4 -5
  366. package/src/config/bundled-skills/mcp-setup/SKILL.md +4 -4
  367. package/src/config/bundled-skills/media-processing/SKILL.md +3 -2
  368. package/src/config/bundled-skills/messaging/SKILL.md +6 -33
  369. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -5
  370. package/src/config/bundled-skills/notifications/SKILL.md +4 -4
  371. package/src/config/bundled-skills/notion/SKILL.md +4 -4
  372. package/src/config/bundled-skills/notion-oauth-setup/SKILL.md +4 -5
  373. package/src/config/bundled-skills/oauth-setup/SKILL.md +4 -5
  374. package/src/config/bundled-skills/phone-calls/SKILL.md +24 -458
  375. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +83 -0
  376. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +57 -0
  377. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +67 -0
  378. package/src/config/bundled-skills/playbooks/SKILL.md +4 -3
  379. package/src/config/bundled-skills/public-ingress/SKILL.md +65 -14
  380. package/src/config/bundled-skills/reminder/SKILL.md +4 -3
  381. package/src/config/bundled-skills/restaurant-reservation/SKILL.md +4 -6
  382. package/src/config/bundled-skills/schedule/SKILL.md +4 -3
  383. package/src/config/bundled-skills/screen-recording/SKILL.md +4 -3
  384. package/src/config/bundled-skills/self-upgrade/SKILL.md +4 -4
  385. package/src/config/bundled-skills/skills-catalog/SKILL.md +4 -4
  386. package/src/config/bundled-skills/slack/SKILL.md +4 -8
  387. package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +1 -1
  388. package/src/config/bundled-skills/slack-app-setup/SKILL.md +66 -88
  389. package/src/config/bundled-skills/slack-digest-setup/SKILL.md +4 -5
  390. package/src/config/bundled-skills/slack-oauth-setup/SKILL.md +4 -5
  391. package/src/config/bundled-skills/start-the-day/SKILL.md +4 -4
  392. package/src/config/bundled-skills/subagent/SKILL.md +4 -3
  393. package/src/config/bundled-skills/tasks/SKILL.md +4 -3
  394. package/src/config/bundled-skills/telegram-setup/SKILL.md +63 -112
  395. package/src/config/bundled-skills/time-based-actions/SKILL.md +4 -3
  396. package/src/config/bundled-skills/transcribe/SKILL.md +4 -3
  397. package/src/config/bundled-skills/twilio-setup/SKILL.md +23 -50
  398. package/src/config/bundled-skills/twitter/SKILL.md +56 -14
  399. package/src/config/bundled-skills/typescript-eval/SKILL.md +4 -4
  400. package/src/config/bundled-skills/vercel-token-setup/SKILL.md +4 -5
  401. package/src/config/bundled-skills/voice-setup/SKILL.md +19 -45
  402. package/src/config/bundled-skills/watcher/SKILL.md +4 -3
  403. package/src/config/env-registry.ts +1 -10
  404. package/src/config/feature-flag-registry.json +0 -16
  405. package/src/config/loader.ts +78 -38
  406. package/src/config/schema.ts +143 -106
  407. package/src/config/schemas/channels.ts +80 -0
  408. package/src/config/schemas/heartbeat.ts +51 -0
  409. package/src/config/schemas/inference.ts +136 -0
  410. package/src/config/schemas/ingress.ts +81 -0
  411. package/src/config/schemas/logging.ts +21 -0
  412. package/src/config/schemas/memory-lifecycle.ts +67 -0
  413. package/src/config/schemas/memory-processing.ts +215 -0
  414. package/src/config/schemas/memory-retrieval.ts +222 -0
  415. package/src/config/schemas/memory-storage.ts +83 -0
  416. package/src/config/schemas/memory.ts +58 -0
  417. package/src/config/schemas/platform.ts +64 -0
  418. package/src/config/schemas/security.ts +54 -0
  419. package/src/config/schemas/swarm.ts +50 -0
  420. package/src/config/schemas/timeouts.ts +47 -0
  421. package/src/config/{agent-schema.ts → schemas/workspace-git.ts} +0 -97
  422. package/src/config/skill-state.ts +3 -13
  423. package/src/config/skills.ts +233 -75
  424. package/src/config/types.ts +1 -20
  425. package/src/contacts/contact-store.ts +12 -49
  426. package/src/contacts/contacts-write.ts +1 -5
  427. package/src/contacts/index.ts +0 -2
  428. package/src/contacts/types.ts +0 -8
  429. package/src/context/window-manager.ts +73 -14
  430. package/src/daemon/assistant-attachments.ts +9 -0
  431. package/src/daemon/computer-use-session.ts +3 -3
  432. package/src/daemon/connection-policy.ts +6 -21
  433. package/src/daemon/context-overflow-policy.ts +1 -1
  434. package/src/daemon/daemon-control.ts +46 -54
  435. package/src/daemon/doordash-steps.ts +1 -1
  436. package/src/daemon/handlers/config-channels.ts +407 -71
  437. package/src/daemon/handlers/config-ingress.ts +17 -85
  438. package/src/daemon/handlers/config-model.ts +145 -123
  439. package/src/daemon/handlers/config-slack-channel.ts +43 -29
  440. package/src/daemon/handlers/config-telegram.ts +32 -27
  441. package/src/daemon/handlers/config-voice.ts +1 -4
  442. package/src/daemon/handlers/dictation.ts +11 -16
  443. package/src/daemon/handlers/identity.ts +5 -6
  444. package/src/daemon/handlers/pairing.ts +5 -13
  445. package/src/daemon/handlers/recording.ts +97 -199
  446. package/src/daemon/handlers/session-history.ts +110 -96
  447. package/src/daemon/handlers/session-user-message.ts +29 -57
  448. package/src/daemon/handlers/sessions.ts +240 -137
  449. package/src/daemon/handlers/shared.ts +62 -95
  450. package/src/daemon/handlers/skills.ts +492 -543
  451. package/src/daemon/lifecycle.ts +168 -55
  452. package/src/daemon/main.ts +1 -0
  453. package/src/daemon/{ipc-contract.ts → message-protocol.ts} +49 -49
  454. package/src/daemon/{ipc-contract → message-types}/computer-use.ts +0 -3
  455. package/src/daemon/{ipc-contract → message-types}/diagnostics.ts +0 -16
  456. package/src/daemon/{ipc-contract → message-types}/integrations.ts +29 -13
  457. package/src/daemon/{ipc-contract → message-types}/memory.ts +8 -0
  458. package/src/daemon/{ipc-contract → message-types}/notifications.ts +15 -1
  459. package/src/daemon/{ipc-contract → message-types}/sessions.ts +1 -0
  460. package/src/daemon/{ipc-contract → message-types}/shared.ts +0 -8
  461. package/src/daemon/{ipc-contract → message-types}/workspace.ts +2 -2
  462. package/src/daemon/providers-setup.ts +0 -5
  463. package/src/daemon/recording-executor.ts +0 -7
  464. package/src/daemon/ride-shotgun-handler.ts +9 -13
  465. package/src/daemon/server.ts +136 -510
  466. package/src/daemon/session-agent-loop-handlers.ts +22 -7
  467. package/src/daemon/session-agent-loop.ts +86 -24
  468. package/src/daemon/session-attachments.ts +1 -1
  469. package/src/daemon/session-error.ts +1 -1
  470. package/src/daemon/session-history.ts +20 -15
  471. package/src/daemon/session-lifecycle.ts +9 -7
  472. package/src/daemon/session-memory.ts +15 -1
  473. package/src/daemon/session-messaging.ts +10 -6
  474. package/src/daemon/session-notifiers.ts +10 -8
  475. package/src/daemon/session-process.ts +34 -25
  476. package/src/daemon/session-queue-manager.ts +1 -1
  477. package/src/daemon/session-runtime-assembly.ts +6 -25
  478. package/src/daemon/session-surfaces.ts +2 -2
  479. package/src/daemon/session-tool-setup.ts +1 -1
  480. package/src/daemon/session-usage.ts +119 -18
  481. package/src/daemon/session.ts +13 -9
  482. package/src/daemon/tool-side-effects.ts +6 -5
  483. package/src/daemon/trace-emitter.ts +1 -1
  484. package/src/daemon/{guardian-verification-intent.ts → verification-session-intent.ts} +16 -16
  485. package/src/daemon/watch-handler.ts +2 -5
  486. package/src/email/service.ts +8 -8
  487. package/src/events/domain-events.ts +0 -1
  488. package/src/events/tool-notification-listener.ts +1 -1
  489. package/src/followups/followup-store.ts +1 -2
  490. package/src/followups/types.ts +0 -6
  491. package/src/heartbeat/heartbeat-service.ts +1 -1
  492. package/src/inbound/platform-callback-registration.ts +1 -1
  493. package/src/inbound/public-ingress-urls.ts +0 -8
  494. package/src/index.ts +12 -0
  495. package/src/mcp/client.ts +1 -1
  496. package/src/mcp/manager.ts +1 -1
  497. package/src/memory/app-store.ts +1 -42
  498. package/src/memory/{guardian-verification.ts → channel-verification-sessions.ts} +110 -93
  499. package/src/memory/conversation-attention-store.ts +154 -0
  500. package/src/memory/conversation-bootstrap.ts +1 -1
  501. package/src/memory/conversation-crud.ts +53 -1
  502. package/src/memory/conversation-display-order-migration.ts +2 -3
  503. package/src/memory/conversation-queries.ts +1 -29
  504. package/src/memory/conversation-title-service.ts +26 -21
  505. package/src/memory/db-connection.ts +1 -8
  506. package/src/memory/db-init.ts +20 -0
  507. package/src/memory/delivery-crud.ts +4 -34
  508. package/src/memory/external-conversation-store.ts +1 -1
  509. package/src/memory/format-recall.ts +47 -0
  510. package/src/memory/guardian-action-store.ts +4 -5
  511. package/src/memory/guardian-rate-limits.ts +0 -3
  512. package/src/memory/invite-store.ts +1 -1
  513. package/src/memory/job-handlers/backfill.ts +9 -2
  514. package/src/memory/job-handlers/extraction.ts +2 -7
  515. package/src/memory/job-handlers/summarization.ts +1 -1
  516. package/src/memory/llm-usage-store.ts +11 -0
  517. package/src/memory/migrations/114-notifications.ts +12 -40
  518. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +357 -0
  519. package/src/memory/migrations/141-rename-verification-table.ts +55 -0
  520. package/src/memory/migrations/142-rename-verification-session-id-column.ts +32 -0
  521. package/src/memory/migrations/143-rename-guardian-verification-values.ts +48 -0
  522. package/src/memory/migrations/144-rename-voice-to-phone.ts +147 -0
  523. package/src/memory/migrations/index.ts +5 -0
  524. package/src/memory/migrations/registry.ts +30 -0
  525. package/src/memory/qdrant-circuit-breaker.ts +5 -0
  526. package/src/memory/retriever.test.ts +707 -0
  527. package/src/memory/retriever.ts +120 -116
  528. package/src/memory/schema/calls.ts +3 -7
  529. package/src/memory/schema/guardian.ts +2 -2
  530. package/src/memory/search/lexical.ts +4 -1
  531. package/src/memory/search/query-expansion.test.ts +70 -0
  532. package/src/memory/search/query-expansion.ts +118 -0
  533. package/src/memory/search/types.ts +18 -17
  534. package/src/messaging/providers/telegram-bot/adapter.ts +1 -1
  535. package/src/messaging/providers/whatsapp/adapter.ts +1 -4
  536. package/src/messaging/registry.ts +0 -1
  537. package/src/notifications/README.md +13 -22
  538. package/src/notifications/adapters/macos.ts +1 -1
  539. package/src/notifications/conversation-pairing.ts +2 -2
  540. package/src/notifications/copy-composer.ts +2 -2
  541. package/src/notifications/decision-engine.ts +1 -10
  542. package/src/notifications/destination-resolver.ts +2 -3
  543. package/src/notifications/emit-signal.ts +2 -8
  544. package/src/notifications/guardian-question-mode.ts +5 -8
  545. package/src/notifications/signal.ts +1 -2
  546. package/src/notifications/types.ts +1 -1
  547. package/src/oauth/token-persistence.ts +25 -1
  548. package/src/permissions/checker.ts +4 -29
  549. package/src/permissions/defaults.ts +6 -6
  550. package/src/permissions/prompter.ts +1 -1
  551. package/src/permissions/secret-prompter.ts +1 -1
  552. package/src/permissions/shell-identity.ts +1 -1
  553. package/src/permissions/trust-store.ts +13 -76
  554. package/src/permissions/workspace-policy.ts +1 -1
  555. package/src/{config → prompts}/computer-use-prompt.ts +1 -1
  556. package/src/{config → prompts}/system-prompt.ts +40 -21
  557. package/src/runtime/AGENTS.md +6 -8
  558. package/src/runtime/access-request-helper.ts +36 -55
  559. package/src/runtime/actor-trust-resolver.ts +1 -24
  560. package/src/runtime/approval-message-composer.ts +6 -2
  561. package/src/runtime/assistant-event.ts +1 -1
  562. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +1 -1
  563. package/src/runtime/auth/__tests__/subject.test.ts +32 -0
  564. package/src/runtime/auth/route-policy.ts +140 -24
  565. package/src/runtime/auth/subject.ts +9 -0
  566. package/src/runtime/auth/token-service.ts +11 -0
  567. package/src/runtime/auth/types.ts +1 -1
  568. package/src/runtime/channel-approval-types.ts +1 -1
  569. package/src/runtime/channel-approvals.ts +1 -1
  570. package/src/runtime/channel-invite-transport.ts +0 -2
  571. package/src/runtime/channel-invite-transports/slack.ts +5 -19
  572. package/src/runtime/channel-invite-transports/telegram.ts +17 -34
  573. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  574. package/src/runtime/channel-readiness-service.ts +24 -159
  575. package/src/runtime/channel-readiness-types.ts +5 -1
  576. package/src/runtime/channel-reply-delivery.ts +43 -3
  577. package/src/runtime/channel-retry-sweep.ts +14 -22
  578. package/src/runtime/{channel-guardian-service.ts → channel-verification-service.ts} +50 -53
  579. package/src/runtime/confirmation-request-guardian-bridge.ts +2 -3
  580. package/src/runtime/gateway-client.ts +12 -15
  581. package/src/runtime/guardian-action-followup-executor.ts +8 -73
  582. package/src/runtime/guardian-action-grant-minter.ts +45 -61
  583. package/src/runtime/guardian-action-message-composer.ts +4 -4
  584. package/src/runtime/guardian-reply-router.ts +3 -3
  585. package/src/runtime/http-server.ts +133 -24
  586. package/src/runtime/http-types.ts +34 -1
  587. package/src/runtime/invite-instruction-generator.ts +1 -3
  588. package/src/runtime/invite-redemption-service.ts +5 -5
  589. package/src/runtime/invite-service.ts +7 -7
  590. package/src/runtime/local-actor-identity.ts +28 -2
  591. package/src/runtime/local-gateway-health.ts +275 -0
  592. package/src/runtime/middleware/twilio-validation.ts +3 -3
  593. package/src/runtime/migrations/migration-transport.ts +18 -3
  594. package/src/runtime/migrations/rebind-secrets-screen.ts +2 -2
  595. package/src/runtime/nl-approval-parser.ts +2 -3
  596. package/src/runtime/routes/access-request-decision.ts +2 -2
  597. package/src/runtime/routes/app-management-routes.ts +921 -0
  598. package/src/runtime/routes/approval-routes.ts +76 -7
  599. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +38 -203
  600. package/src/runtime/routes/channel-delivery-routes.ts +5 -4
  601. package/src/runtime/routes/channel-route-shared.ts +1 -3
  602. package/src/runtime/routes/channel-routes.ts +1 -4
  603. package/src/runtime/routes/channel-verification-routes.ts +257 -0
  604. package/src/runtime/routes/computer-use-routes.ts +595 -0
  605. package/src/runtime/routes/contact-routes.ts +1 -317
  606. package/src/runtime/routes/conversation-attention-routes.ts +6 -5
  607. package/src/runtime/routes/conversation-routes.ts +11 -18
  608. package/src/runtime/routes/debug-routes.ts +1 -1
  609. package/src/runtime/routes/diagnostics-routes.ts +813 -0
  610. package/src/runtime/routes/documents-routes.ts +227 -0
  611. package/src/runtime/routes/guardian-approval-interception.ts +25 -48
  612. package/src/runtime/routes/guardian-bootstrap-routes.ts +3 -3
  613. package/src/runtime/routes/guardian-expiry-sweep.ts +2 -2
  614. package/src/runtime/routes/guardian-refresh-routes.ts +11 -6
  615. package/src/runtime/routes/inbound-conversation.ts +3 -10
  616. package/src/runtime/routes/inbound-message-handler.ts +7 -6
  617. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +22 -22
  618. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +44 -0
  619. package/src/runtime/routes/inbound-stages/background-dispatch.ts +140 -22
  620. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +4 -4
  621. package/src/runtime/routes/inbound-stages/edit-intercept.ts +5 -5
  622. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +3 -3
  623. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -4
  624. package/src/runtime/routes/inbound-stages/verification-intercept.ts +13 -14
  625. package/src/runtime/routes/integrations/slack/channel.ts +72 -0
  626. package/src/runtime/routes/{slack-share-routes.ts → integrations/slack/share.ts} +9 -9
  627. package/src/runtime/routes/integrations/telegram.ts +111 -0
  628. package/src/runtime/routes/integrations/twilio.ts +451 -0
  629. package/src/runtime/routes/invite-routes.ts +2 -2
  630. package/src/runtime/routes/pairing-routes.ts +1 -1
  631. package/src/runtime/routes/recording-routes.ts +332 -0
  632. package/src/{daemon/handlers/config-scheduling.ts → runtime/routes/schedule-routes.ts} +91 -106
  633. package/src/runtime/routes/session-management-routes.ts +167 -0
  634. package/src/runtime/routes/session-query-routes.ts +204 -0
  635. package/src/runtime/routes/settings-routes.ts +977 -0
  636. package/src/runtime/routes/skills-routes.ts +266 -0
  637. package/src/runtime/routes/subagents-routes.ts +246 -0
  638. package/src/runtime/routes/surface-action-routes.ts +100 -10
  639. package/src/runtime/routes/surface-content-routes.ts +1 -1
  640. package/src/runtime/routes/work-items-routes.ts +809 -0
  641. package/src/runtime/routes/workspace-routes.test.ts +778 -0
  642. package/src/runtime/routes/workspace-routes.ts +410 -0
  643. package/src/runtime/routes/workspace-utils.ts +88 -0
  644. package/src/runtime/telegram-streaming-delivery.test.ts +597 -0
  645. package/src/runtime/telegram-streaming-delivery.ts +380 -0
  646. package/src/runtime/tool-grant-request-helper.ts +1 -2
  647. package/src/runtime/trust-context-resolver.ts +0 -1
  648. package/src/runtime/{guardian-outbound-actions.ts → verification-outbound-actions.ts} +23 -188
  649. package/src/runtime/verification-rate-limiter.ts +2 -2
  650. package/src/runtime/{guardian-verification-templates.ts → verification-templates.ts} +2 -28
  651. package/src/schedule/integration-status.ts +2 -2
  652. package/src/schedule/schedule-store.ts +7 -9
  653. package/src/sequence/engine.ts +1 -1
  654. package/src/skills/active-skill-tools.ts +0 -8
  655. package/src/skills/clawhub.ts +1 -10
  656. package/src/skills/managed-store.ts +14 -4
  657. package/src/skills/slash-commands.ts +1 -1
  658. package/src/subagent/manager.ts +1 -1
  659. package/src/subagent/types.ts +1 -1
  660. package/src/tasks/SPEC.md +10 -10
  661. package/src/tasks/task-scheduler.ts +1 -1
  662. package/src/telegram/bot-username.ts +13 -0
  663. package/src/tools/assets/materialize.ts +1 -1
  664. package/src/tools/assets/search.ts +1 -1
  665. package/src/tools/browser/browser-execution.ts +2 -2
  666. package/src/tools/browser/browser-manager.ts +88 -11
  667. package/src/tools/browser/browser-screencast.ts +1 -1
  668. package/src/tools/browser/headless-browser.ts +0 -17
  669. package/src/tools/browser/jit-auth.ts +1 -1
  670. package/src/tools/browser/recording-store.ts +19 -1
  671. package/src/tools/browser/runtime-check.ts +4 -2
  672. package/src/tools/calls/call-start.ts +3 -3
  673. package/src/tools/credentials/metadata-store.ts +0 -13
  674. package/src/tools/credentials/vault.ts +7 -31
  675. package/src/tools/followups/followup_create.ts +0 -8
  676. package/src/tools/mcp/mcp-tool-factory.ts +1 -1
  677. package/src/tools/memory/definitions.ts +32 -10
  678. package/src/tools/memory/handlers.test.ts +573 -0
  679. package/src/tools/memory/handlers.ts +222 -65
  680. package/src/tools/memory/register.ts +53 -24
  681. package/src/tools/network/script-proxy/session-manager.ts +1 -12
  682. package/src/tools/schedule/update.ts +0 -8
  683. package/src/tools/skills/load.ts +3 -3
  684. package/src/tools/subagent/read.ts +1 -1
  685. package/src/tools/system/voice-config.ts +2 -14
  686. package/src/tools/terminal/safe-env.ts +5 -18
  687. package/src/tools/tool-approval-handler.ts +4 -4
  688. package/src/tools/tool-manifest.ts +4 -2
  689. package/src/tools/types.ts +1 -1
  690. package/src/tools/{guardian-control-plane-policy.ts → verification-control-plane-policy.ts} +37 -39
  691. package/src/twitter/platform-proxy-client.ts +405 -0
  692. package/src/usage/types.ts +21 -0
  693. package/src/util/canonicalize-identity.ts +2 -6
  694. package/src/util/cookie-session.ts +35 -51
  695. package/src/util/platform.ts +93 -86
  696. package/src/util/pricing.ts +180 -43
  697. package/src/work-items/work-item-runner.ts +1 -1
  698. package/scripts/ipc/check-contract-inventory.ts +0 -107
  699. package/scripts/ipc/check-swift-decoder-drift.ts +0 -184
  700. package/scripts/ipc/generate-swift.ts +0 -528
  701. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -3043
  702. package/src/__tests__/app-migration.test.ts +0 -148
  703. package/src/__tests__/config-loader-migration.test.ts +0 -85
  704. package/src/__tests__/daemon-lifecycle.test.ts +0 -715
  705. package/src/__tests__/daemon-server-session-init.test.ts +0 -864
  706. package/src/__tests__/guardian-actions-endpoint.test.ts +0 -1452
  707. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +0 -228
  708. package/src/__tests__/handlers-cu-observation-blob.test.ts +0 -397
  709. package/src/__tests__/handlers-ipc-blob-probe.test.ts +0 -218
  710. package/src/__tests__/handlers-slack-config.test.ts +0 -140
  711. package/src/__tests__/handlers-telegram-config.test.ts +0 -1317
  712. package/src/__tests__/handlers-twitter-config.test.ts +0 -1145
  713. package/src/__tests__/ingress-reconcile.test.ts +0 -606
  714. package/src/__tests__/integrations-cli.test.ts +0 -232
  715. package/src/__tests__/ipc-blob-store.test.ts +0 -329
  716. package/src/__tests__/ipc-contract-inventory.test.ts +0 -69
  717. package/src/__tests__/ipc-contract.test.ts +0 -76
  718. package/src/__tests__/ipc-protocol.test.ts +0 -120
  719. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +0 -250
  720. package/src/__tests__/ipc-snapshot.test.ts +0 -2197
  721. package/src/__tests__/ipc-validate.test.ts +0 -471
  722. package/src/__tests__/migration-cli-flows.test.ts +0 -186
  723. package/src/__tests__/migration-ordering.test.ts +0 -267
  724. package/src/__tests__/oauth-connect-handler.test.ts +0 -361
  725. package/src/__tests__/platform-move-helper.test.ts +0 -108
  726. package/src/__tests__/platform-socket-path.test.ts +0 -52
  727. package/src/__tests__/platform-workspace-migration.test.ts +0 -1051
  728. package/src/__tests__/recording-intent-handler.test.ts +0 -1155
  729. package/src/__tests__/script-proxy-profile-template-fallback.test.ts +0 -127
  730. package/src/__tests__/sms-messaging-provider.test.ts +0 -156
  731. package/src/__tests__/tool-permission-simulate-handler.test.ts +0 -367
  732. package/src/__tests__/twitter-auth-handler.test.ts +0 -561
  733. package/src/__tests__/work-item-output.test.ts +0 -150
  734. package/src/amazon/session.ts +0 -58
  735. package/src/cli/channels.ts +0 -51
  736. package/src/cli/influencer.ts +0 -319
  737. package/src/cli/integrations.ts +0 -372
  738. package/src/cli/ipc-client.ts +0 -88
  739. package/src/config/bundled-skills/configure-settings/SKILL.md +0 -86
  740. package/src/config/bundled-skills/doordash/lib/shared/ipc.ts +0 -32
  741. package/src/config/bundled-skills/sms-setup/SKILL.md +0 -210
  742. package/src/config/core-schema.ts +0 -434
  743. package/src/config/memory-schema.ts +0 -617
  744. package/src/daemon/auth-manager.ts +0 -106
  745. package/src/daemon/handlers/apps.ts +0 -758
  746. package/src/daemon/handlers/avatar.ts +0 -73
  747. package/src/daemon/handlers/browser.ts +0 -3
  748. package/src/daemon/handlers/computer-use.ts +0 -231
  749. package/src/daemon/handlers/config-dispatch.ts +0 -29
  750. package/src/daemon/handlers/config-heartbeat.ts +0 -299
  751. package/src/daemon/handlers/config-inbox.ts +0 -457
  752. package/src/daemon/handlers/config-integrations.ts +0 -409
  753. package/src/daemon/handlers/config-platform.ts +0 -77
  754. package/src/daemon/handlers/config-slack.ts +0 -41
  755. package/src/daemon/handlers/config-tools.ts +0 -226
  756. package/src/daemon/handlers/config-trust.ts +0 -135
  757. package/src/daemon/handlers/config.ts +0 -64
  758. package/src/daemon/handlers/contacts.ts +0 -193
  759. package/src/daemon/handlers/diagnostics.ts +0 -382
  760. package/src/daemon/handlers/documents.ts +0 -188
  761. package/src/daemon/handlers/guardian-actions.ts +0 -82
  762. package/src/daemon/handlers/home-base.ts +0 -82
  763. package/src/daemon/handlers/index.ts +0 -222
  764. package/src/daemon/handlers/misc.ts +0 -1139
  765. package/src/daemon/handlers/navigate-settings.ts +0 -29
  766. package/src/daemon/handlers/oauth-connect.ts +0 -202
  767. package/src/daemon/handlers/open-bundle-handler.ts +0 -88
  768. package/src/daemon/handlers/publish.ts +0 -176
  769. package/src/daemon/handlers/signing.ts +0 -56
  770. package/src/daemon/handlers/subagents.ts +0 -286
  771. package/src/daemon/handlers/twitter-auth.ts +0 -220
  772. package/src/daemon/handlers/work-items.ts +0 -796
  773. package/src/daemon/handlers/workspace-files.ts +0 -84
  774. package/src/daemon/handlers.ts +0 -16
  775. package/src/daemon/ipc-blob-store.ts +0 -246
  776. package/src/daemon/ipc-contract-inventory.json +0 -348
  777. package/src/daemon/ipc-contract-inventory.ts +0 -202
  778. package/src/daemon/ipc-handler.ts +0 -120
  779. package/src/daemon/ipc-protocol.ts +0 -85
  780. package/src/daemon/ipc-validate.ts +0 -254
  781. package/src/memory/app-migration.ts +0 -114
  782. package/src/memory/channel-delivery-store.ts +0 -40
  783. package/src/memory/channel-guardian-store.ts +0 -83
  784. package/src/memory/conversation-store.ts +0 -102
  785. package/src/memory/schema-migration.ts +0 -38
  786. package/src/messaging/providers/sms/adapter.ts +0 -232
  787. package/src/messaging/providers/sms/client.ts +0 -93
  788. package/src/messaging/providers/sms/types.ts +0 -7
  789. package/src/migrations/config-merge.ts +0 -62
  790. package/src/migrations/data-layout.ts +0 -89
  791. package/src/migrations/data-merge.ts +0 -44
  792. package/src/migrations/hooks-merge.ts +0 -118
  793. package/src/migrations/index.ts +0 -6
  794. package/src/migrations/log.ts +0 -28
  795. package/src/migrations/skills-merge.ts +0 -44
  796. package/src/migrations/workspace-layout.ts +0 -94
  797. package/src/notifications/adapters/sms.ts +0 -94
  798. package/src/runtime/channel-approval-parser.ts +0 -123
  799. package/src/runtime/channel-invite-transports/sms.ts +0 -53
  800. package/src/runtime/routes/approval-strategies/guardian-legacy-fallback-strategy.ts +0 -82
  801. package/src/runtime/routes/integration-routes.ts +0 -381
  802. package/src/runtime/routes/twilio-routes.ts +0 -1251
  803. package/src/twitter/router.ts +0 -131
  804. package/src/twitter/session.ts +0 -54
  805. package/src/watcher/providers/slack.ts +0 -282
  806. /package/src/{amazon → cli/commands/amazon}/cart.ts +0 -0
  807. /package/src/{amazon → cli/commands/amazon}/checkout.ts +0 -0
  808. /package/src/{amazon → cli/commands/amazon}/product-details.ts +0 -0
  809. /package/src/{amazon → cli/commands/amazon}/search.ts +0 -0
  810. /package/src/{twitter → cli/commands/twitter}/oauth-client.ts +0 -0
  811. /package/src/config/{calls-schema.ts → schemas/calls.ts} +0 -0
  812. /package/src/config/{elevenlabs-schema.ts → schemas/elevenlabs.ts} +0 -0
  813. /package/src/config/{mcp-schema.ts → schemas/mcp.ts} +0 -0
  814. /package/src/config/{notifications-schema.ts → schemas/notifications.ts} +0 -0
  815. /package/src/config/{sandbox-schema.ts → schemas/sandbox.ts} +0 -0
  816. /package/src/config/{skills-schema.ts → schemas/skills.ts} +0 -0
  817. /package/src/daemon/{ipc-contract → message-types}/apps.ts +0 -0
  818. /package/src/daemon/{ipc-contract → message-types}/browser.ts +0 -0
  819. /package/src/daemon/{ipc-contract → message-types}/contacts.ts +0 -0
  820. /package/src/daemon/{ipc-contract → message-types}/documents.ts +0 -0
  821. /package/src/daemon/{ipc-contract → message-types}/guardian-actions.ts +0 -0
  822. /package/src/daemon/{ipc-contract → message-types}/inbox.ts +0 -0
  823. /package/src/daemon/{ipc-contract → message-types}/messages.ts +0 -0
  824. /package/src/daemon/{ipc-contract → message-types}/pairing.ts +0 -0
  825. /package/src/daemon/{ipc-contract → message-types}/schedules.ts +0 -0
  826. /package/src/daemon/{ipc-contract → message-types}/settings.ts +0 -0
  827. /package/src/daemon/{ipc-contract → message-types}/skills.ts +0 -0
  828. /package/src/daemon/{ipc-contract → message-types}/subagents.ts +0 -0
  829. /package/src/daemon/{ipc-contract → message-types}/surfaces.ts +0 -0
  830. /package/src/daemon/{ipc-contract → message-types}/trust.ts +0 -0
  831. /package/src/daemon/{ipc-contract → message-types}/work-items.ts +0 -0
  832. /package/src/{cli/email-guardrails.ts → email/guardrails.ts} +0 -0
  833. /package/src/{config → prompts}/__tests__/build-cli-reference-section.test.ts +0 -0
  834. /package/src/{config → prompts}/templates/BOOTSTRAP.md +0 -0
  835. /package/src/{config → prompts}/templates/IDENTITY.md +0 -0
  836. /package/src/{config → prompts}/templates/SOUL.md +0 -0
  837. /package/src/{config → prompts}/templates/UPDATES.md +0 -0
  838. /package/src/{config → prompts}/templates/USER.md +0 -0
  839. /package/src/{config → prompts}/update-bulletin-format.ts +0 -0
  840. /package/src/{config → prompts}/update-bulletin-state.ts +0 -0
  841. /package/src/{config → prompts}/update-bulletin-template-path.ts +0 -0
  842. /package/src/{config → prompts}/update-bulletin.ts +0 -0
  843. /package/src/{config → prompts}/user-reference.ts +0 -0
@@ -1,1452 +0,0 @@
1
- /**
2
- * Tests for the deterministic guardian action endpoints:
3
- * - HTTP route handlers (guardian-action-routes.ts)
4
- * - IPC handlers (guardian-actions.ts)
5
- *
6
- * Covers: conversationId scoping, stale handling, access-request routing,
7
- * invalid action rejection, and not-found paths.
8
- *
9
- * All decisions now go through the canonical guardian decision primitive
10
- * (`applyCanonicalGuardianDecision`), so tests create canonical requests
11
- * and mock that function.
12
- */
13
- import { mkdtempSync, rmSync } from "node:fs";
14
- import { tmpdir } from "node:os";
15
- import { join } from "node:path";
16
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
17
-
18
- const testDir = mkdtempSync(join(tmpdir(), "guardian-actions-endpoint-test-"));
19
- const previousBaseDataDir = process.env.BASE_DATA_DIR;
20
- process.env.BASE_DATA_DIR = testDir;
21
-
22
- // eslint-disable-next-line @typescript-eslint/no-require-imports
23
- const realPlatform = require("../util/platform.js");
24
- mock.module("../util/platform.js", () => ({
25
- ...realPlatform,
26
- getDataDir: () => testDir,
27
- isMacOS: () => process.platform === "darwin",
28
- isLinux: () => process.platform === "linux",
29
- isWindows: () => process.platform === "win32",
30
- getSocketPath: () => join(testDir, "test.sock"),
31
- getPidPath: () => join(testDir, "test.pid"),
32
- getDbPath: () => join(testDir, "test.db"),
33
- getLogPath: () => join(testDir, "test.log"),
34
- ensureDataDir: () => {},
35
- }));
36
-
37
- const noopLogger = {
38
- info: () => {},
39
- warn: () => {},
40
- error: () => {},
41
- debug: () => {},
42
- trace: () => {},
43
- fatal: () => {},
44
- child: () => noopLogger,
45
- };
46
-
47
- // eslint-disable-next-line @typescript-eslint/no-require-imports
48
- const realLogger = require("../util/logger.js");
49
- mock.module("../util/logger.js", () => ({
50
- ...realLogger,
51
- getLogger: () => noopLogger,
52
- getCliLogger: () => noopLogger,
53
- isDebug: () => false,
54
- truncateForLog: (value: string) => value,
55
- initLogger: () => {},
56
- pruneOldLogFiles: () => 0,
57
- }));
58
-
59
- // Bypass HTTP auth so requireBoundGuardian does not reject the test principal.
60
- mock.module("../config/env.js", () => ({ isHttpAuthDisabled: () => true }));
61
-
62
- // Prevent the IPC handler's resolveLocalIpcTrustContext from creating a real
63
- // guardian binding via ensureVellumGuardianBinding. Return a stable trust
64
- // context that the IPC handler tests can assert against.
65
- mock.module("../runtime/guardian-vellum-migration.js", () => ({
66
- ensureVellumGuardianBinding: () => "test-principal",
67
- }));
68
- mock.module("../runtime/local-actor-identity.js", () => ({
69
- resolveLocalIpcTrustContext: () => ({
70
- trustClass: "guardian" as const,
71
- sourceChannel: "vellum",
72
- guardianExternalUserId: "test-principal",
73
- guardianPrincipalId: "test-principal",
74
- }),
75
- }));
76
-
77
- // Mock applyCanonicalGuardianDecision — the single decision write path.
78
- const mockApplyCanonicalGuardianDecision = mock(
79
- (
80
- ..._args: any[]
81
- ): Promise<{
82
- applied: boolean;
83
- requestId?: string;
84
- reason?: string;
85
- grantMinted?: boolean;
86
- }> =>
87
- Promise.resolve({
88
- applied: true,
89
- requestId: "req-123",
90
- grantMinted: false,
91
- }),
92
- );
93
- mock.module("../approvals/guardian-decision-primitive.js", () => ({
94
- applyCanonicalGuardianDecision: mockApplyCanonicalGuardianDecision,
95
- }));
96
-
97
- import { guardianActionsHandlers } from "../daemon/handlers/guardian-actions.js";
98
- import {
99
- createCanonicalGuardianDelivery,
100
- createCanonicalGuardianRequest,
101
- generateCanonicalRequestCode,
102
- } from "../memory/canonical-guardian-store.js";
103
- import { initializeDb, resetDb } from "../memory/db.js";
104
- import { getDb } from "../memory/db.js";
105
- import { conversations } from "../memory/schema.js";
106
- import type { AuthContext } from "../runtime/auth/types.js";
107
- import {
108
- handleGuardianActionDecision,
109
- handleGuardianActionsPending,
110
- listGuardianDecisionPrompts,
111
- } from "../runtime/routes/guardian-action-routes.js";
112
-
113
- /** Synthetic AuthContext for tests -- mimics a local actor with full scopes. */
114
- const mockAuthContext: AuthContext = {
115
- subject: "actor:self:test-principal",
116
- principalType: "actor",
117
- assistantId: "self",
118
- actorPrincipalId: "test-principal",
119
- scopeProfile: "actor_client_v1",
120
- scopes: new Set([
121
- "chat.read",
122
- "chat.write",
123
- "approval.read",
124
- "approval.write",
125
- ]),
126
- policyEpoch: 1,
127
- };
128
-
129
- resetDb();
130
- initializeDb();
131
-
132
- function ensureConversation(id: string): void {
133
- const db = getDb();
134
- const now = Date.now();
135
- db.insert(conversations)
136
- .values({ id, title: `Conversation ${id}`, createdAt: now, updatedAt: now })
137
- .run();
138
- }
139
-
140
- function resetTables(): void {
141
- const db = getDb();
142
- db.run("DELETE FROM canonical_guardian_deliveries");
143
- db.run("DELETE FROM canonical_guardian_requests");
144
- db.run("DELETE FROM channel_guardian_approval_requests");
145
- db.run("DELETE FROM conversations");
146
- mockApplyCanonicalGuardianDecision.mockClear();
147
- }
148
-
149
- /** Create a canonical guardian request for testing. */
150
- function createTestCanonicalRequest(overrides: {
151
- conversationId: string;
152
- requestId: string;
153
- kind?: string;
154
- toolName?: string;
155
- guardianExternalUserId?: string;
156
- guardianPrincipalId?: string;
157
- questionText?: string;
158
- expiresAt?: string;
159
- }) {
160
- ensureConversation(overrides.conversationId);
161
- return createCanonicalGuardianRequest({
162
- id: overrides.requestId,
163
- kind: overrides.kind ?? "tool_approval",
164
- sourceType: "desktop",
165
- sourceChannel: "vellum",
166
- conversationId: overrides.conversationId,
167
- guardianExternalUserId: overrides.guardianExternalUserId,
168
- guardianPrincipalId: overrides.guardianPrincipalId ?? "test-principal",
169
- toolName: overrides.toolName ?? "bash",
170
- questionText: overrides.questionText,
171
- requestCode: generateCanonicalRequestCode(),
172
- status: "pending",
173
- expiresAt:
174
- overrides.expiresAt ?? new Date(Date.now() + 60_000).toISOString(),
175
- });
176
- }
177
-
178
- // -- IPC helper ---------------------------------------------------------------
179
-
180
- /** Minimal stub for IPC socket and context to capture sent messages. */
181
- function createIpcStub() {
182
- const sent: Array<Record<string, unknown>> = [];
183
- const socket = {} as unknown; // opaque -- the handler just passes it through
184
- const ctx = {
185
- send: (_socket: unknown, msg: Record<string, unknown>) => {
186
- sent.push(msg);
187
- },
188
- };
189
- return { socket, ctx, sent };
190
- }
191
-
192
- // -- Cleanup ------------------------------------------------------------------
193
-
194
- afterAll(() => {
195
- resetDb();
196
- if (previousBaseDataDir === undefined) {
197
- delete process.env.BASE_DATA_DIR;
198
- } else {
199
- process.env.BASE_DATA_DIR = previousBaseDataDir;
200
- }
201
- try {
202
- rmSync(testDir, { recursive: true });
203
- } catch {
204
- // best-effort
205
- }
206
- });
207
-
208
- // =========================================================================
209
- // HTTP route: handleGuardianActionDecision
210
- // =========================================================================
211
-
212
- describe("HTTP handleGuardianActionDecision", () => {
213
- beforeEach(() => {
214
- resetDb();
215
- initializeDb();
216
- resetTables();
217
- });
218
-
219
- test("rejects missing requestId", async () => {
220
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
221
- method: "POST",
222
- body: JSON.stringify({ action: "approve_once" }),
223
- });
224
- const res = await handleGuardianActionDecision(req, mockAuthContext);
225
- expect(res.status).toBe(400);
226
- const body = await res.json();
227
- expect(body.error.message).toContain("requestId");
228
- });
229
-
230
- test("rejects missing action", async () => {
231
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
232
- method: "POST",
233
- body: JSON.stringify({ requestId: "req-1" }),
234
- });
235
- const res = await handleGuardianActionDecision(req, mockAuthContext);
236
- expect(res.status).toBe(400);
237
- const body = await res.json();
238
- expect(body.error.message).toContain("action");
239
- });
240
-
241
- test("rejects invalid action", async () => {
242
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
243
- method: "POST",
244
- body: JSON.stringify({ requestId: "req-1", action: "nuke_from_orbit" }),
245
- });
246
- const res = await handleGuardianActionDecision(req, mockAuthContext);
247
- expect(res.status).toBe(400);
248
- const body = await res.json();
249
- expect(body.error.message).toContain("Invalid action");
250
- });
251
-
252
- test("returns 404 when no canonical request exists (not_found from canonical primitive)", async () => {
253
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
254
- applied: false,
255
- reason: "not_found",
256
- });
257
-
258
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
259
- method: "POST",
260
- body: JSON.stringify({
261
- requestId: "nonexistent",
262
- action: "approve_once",
263
- }),
264
- });
265
- const res = await handleGuardianActionDecision(req, mockAuthContext);
266
- expect(res.status).toBe(404);
267
- });
268
-
269
- test("applies decision via applyCanonicalGuardianDecision for tool approval", async () => {
270
- createTestCanonicalRequest({
271
- conversationId: "conv-1",
272
- requestId: "req-gd-1",
273
- });
274
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
275
- applied: true,
276
- requestId: "req-gd-1",
277
- grantMinted: false,
278
- });
279
-
280
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
281
- method: "POST",
282
- body: JSON.stringify({ requestId: "req-gd-1", action: "approve_once" }),
283
- });
284
- const res = await handleGuardianActionDecision(req, mockAuthContext);
285
- expect(res.status).toBe(200);
286
- const body = await res.json();
287
- expect(body.applied).toBe(true);
288
- expect(body.requestId).toBe("req-gd-1");
289
- expect(mockApplyCanonicalGuardianDecision).toHaveBeenCalledTimes(1);
290
- });
291
-
292
- test("rejects decision when conversationId does not match canonical request", async () => {
293
- createTestCanonicalRequest({
294
- conversationId: "conv-1",
295
- requestId: "req-scope-1",
296
- });
297
-
298
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
299
- method: "POST",
300
- body: JSON.stringify({
301
- requestId: "req-scope-1",
302
- action: "approve_once",
303
- conversationId: "conv-wrong",
304
- }),
305
- });
306
- const res = await handleGuardianActionDecision(req, mockAuthContext);
307
- expect(res.status).toBe(404);
308
- const body = await res.json();
309
- expect(body.error.message).toContain("No pending guardian action");
310
- expect(mockApplyCanonicalGuardianDecision).not.toHaveBeenCalled();
311
- });
312
-
313
- test("allows decision when conversationId matches canonical request", async () => {
314
- createTestCanonicalRequest({
315
- conversationId: "conv-match",
316
- requestId: "req-scope-2",
317
- });
318
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
319
- applied: true,
320
- requestId: "req-scope-2",
321
- grantMinted: false,
322
- });
323
-
324
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
325
- method: "POST",
326
- body: JSON.stringify({
327
- requestId: "req-scope-2",
328
- action: "reject",
329
- conversationId: "conv-match",
330
- }),
331
- });
332
- const res = await handleGuardianActionDecision(req, mockAuthContext);
333
- expect(res.status).toBe(200);
334
- const body = await res.json();
335
- expect(body.applied).toBe(true);
336
- });
337
-
338
- test("allows decision when conversationId matches a delivery destination", async () => {
339
- createTestCanonicalRequest({
340
- conversationId: "conv-source-http",
341
- requestId: "req-dest-scope-http",
342
- kind: "pending_question",
343
- });
344
- createCanonicalGuardianDelivery({
345
- requestId: "req-dest-scope-http",
346
- destinationChannel: "vellum",
347
- destinationConversationId: "conv-guardian-thread",
348
- });
349
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
350
- applied: true,
351
- requestId: "req-dest-scope-http",
352
- grantMinted: false,
353
- });
354
-
355
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
356
- method: "POST",
357
- body: JSON.stringify({
358
- requestId: "req-dest-scope-http",
359
- action: "approve_once",
360
- conversationId: "conv-guardian-thread",
361
- }),
362
- });
363
- const res = await handleGuardianActionDecision(req, mockAuthContext);
364
- expect(res.status).toBe(200);
365
- const body = await res.json();
366
- expect(body.applied).toBe(true);
367
- });
368
-
369
- test("allows decision when no conversationId is provided (backward compat)", async () => {
370
- createTestCanonicalRequest({
371
- conversationId: "conv-any",
372
- requestId: "req-scope-3",
373
- });
374
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
375
- applied: true,
376
- requestId: "req-scope-3",
377
- grantMinted: false,
378
- });
379
-
380
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
381
- method: "POST",
382
- body: JSON.stringify({
383
- requestId: "req-scope-3",
384
- action: "approve_once",
385
- }),
386
- });
387
- const res = await handleGuardianActionDecision(req, mockAuthContext);
388
- expect(res.status).toBe(200);
389
- });
390
-
391
- test("applies decision for access_request kind through canonical primitive", async () => {
392
- createTestCanonicalRequest({
393
- conversationId: "conv-access",
394
- requestId: "req-access-1",
395
- kind: "access_request",
396
- toolName: "ingress_access_request",
397
- guardianExternalUserId: "guardian-42",
398
- });
399
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
400
- applied: true,
401
- requestId: "req-access-1",
402
- grantMinted: false,
403
- });
404
-
405
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
406
- method: "POST",
407
- body: JSON.stringify({
408
- requestId: "req-access-1",
409
- action: "approve_once",
410
- }),
411
- });
412
- const res = await handleGuardianActionDecision(req, mockAuthContext);
413
- expect(res.status).toBe(200);
414
- const body = await res.json();
415
- expect(body.applied).toBe(true);
416
- // All decisions go through the canonical primitive
417
- expect(mockApplyCanonicalGuardianDecision).toHaveBeenCalledTimes(1);
418
- });
419
-
420
- test("applies decision for voice access_request kind through canonical primitive", async () => {
421
- createTestCanonicalRequest({
422
- conversationId: "conv-voice-access",
423
- requestId: "req-voice-access-1",
424
- kind: "access_request",
425
- toolName: "ingress_access_request",
426
- guardianExternalUserId: "guardian-voice-42",
427
- });
428
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
429
- applied: true,
430
- requestId: "req-voice-access-1",
431
- grantMinted: false,
432
- });
433
-
434
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
435
- method: "POST",
436
- body: JSON.stringify({
437
- requestId: "req-voice-access-1",
438
- action: "approve_once",
439
- }),
440
- });
441
- const res = await handleGuardianActionDecision(req, mockAuthContext);
442
- expect(res.status).toBe(200);
443
- const body = await res.json();
444
- expect(body.applied).toBe(true);
445
- expect(mockApplyCanonicalGuardianDecision).toHaveBeenCalledTimes(1);
446
- });
447
-
448
- test("returns stale reason from canonical decision primitive", async () => {
449
- createTestCanonicalRequest({
450
- conversationId: "conv-stale",
451
- requestId: "req-stale-1",
452
- });
453
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
454
- applied: false,
455
- reason: "already_resolved",
456
- });
457
-
458
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
459
- method: "POST",
460
- body: JSON.stringify({
461
- requestId: "req-stale-1",
462
- action: "approve_once",
463
- }),
464
- });
465
- const res = await handleGuardianActionDecision(req, mockAuthContext);
466
- const body = await res.json();
467
- expect(body.applied).toBe(false);
468
- expect(body.reason).toBe("already_resolved");
469
- // requestId should fall back to the original request ID
470
- expect(body.requestId).toBe("req-stale-1");
471
- });
472
-
473
- test("passes actorContext with vellum channel and guardianPrincipalId", async () => {
474
- createTestCanonicalRequest({
475
- conversationId: "conv-actor",
476
- requestId: "req-actor-1",
477
- });
478
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
479
- applied: true,
480
- requestId: "req-actor-1",
481
- grantMinted: false,
482
- });
483
-
484
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
485
- method: "POST",
486
- body: JSON.stringify({
487
- requestId: "req-actor-1",
488
- action: "approve_once",
489
- }),
490
- });
491
- await handleGuardianActionDecision(req, mockAuthContext);
492
- const call = mockApplyCanonicalGuardianDecision.mock.calls[0]![0] as Record<
493
- string,
494
- unknown
495
- >;
496
- const actorContext = call.actorContext as Record<string, unknown>;
497
- expect(actorContext.channel).toBe("vellum");
498
- expect(actorContext.guardianPrincipalId).toBeDefined();
499
- });
500
- });
501
-
502
- // =========================================================================
503
- // HTTP route: handleGuardianActionsPending
504
- // =========================================================================
505
-
506
- describe("HTTP handleGuardianActionsPending", () => {
507
- beforeEach(() => {
508
- resetDb();
509
- initializeDb();
510
- resetTables();
511
- });
512
-
513
- test("returns 400 when conversationId is missing", () => {
514
- const url = new URL("http://localhost/v1/guardian-actions/pending");
515
- const res = handleGuardianActionsPending(url, mockAuthContext);
516
- expect(res.status).toBe(400);
517
- });
518
-
519
- test("returns prompts for a conversation with pending canonical requests", () => {
520
- createTestCanonicalRequest({
521
- conversationId: "conv-list",
522
- requestId: "req-list-1",
523
- questionText: "Run bash: ls",
524
- });
525
-
526
- const url = new URL(
527
- "http://localhost/v1/guardian-actions/pending?conversationId=conv-list",
528
- );
529
- const res = handleGuardianActionsPending(url, mockAuthContext);
530
- expect(res.status).toBe(200);
531
-
532
- // Verify the prompts directly via the shared helper
533
- const prompts = listGuardianDecisionPrompts({
534
- conversationId: "conv-list",
535
- });
536
- expect(prompts).toHaveLength(1);
537
- expect(prompts[0].requestId).toBe("req-list-1");
538
- expect(prompts[0].questionText).toBe("Run bash: ls");
539
- });
540
-
541
- test("returns empty prompts for a conversation with no pending requests", () => {
542
- const prompts = listGuardianDecisionPrompts({
543
- conversationId: "conv-empty",
544
- });
545
- expect(prompts).toHaveLength(0);
546
- });
547
- });
548
-
549
- // =========================================================================
550
- // listGuardianDecisionPrompts
551
- // =========================================================================
552
-
553
- describe("listGuardianDecisionPrompts", () => {
554
- beforeEach(() => {
555
- resetDb();
556
- initializeDb();
557
- resetTables();
558
- });
559
-
560
- test("excludes expired canonical requests", () => {
561
- ensureConversation("conv-expired");
562
- createCanonicalGuardianRequest({
563
- id: "req-expired",
564
- kind: "tool_approval",
565
- sourceType: "desktop",
566
- sourceChannel: "vellum",
567
- conversationId: "conv-expired",
568
- guardianPrincipalId: "test-principal",
569
- toolName: "bash",
570
- requestCode: generateCanonicalRequestCode(),
571
- status: "pending",
572
- expiresAt: new Date(Date.now() - 1000).toISOString(), // already expired
573
- });
574
-
575
- const prompts = listGuardianDecisionPrompts({
576
- conversationId: "conv-expired",
577
- });
578
- expect(prompts).toHaveLength(0);
579
- });
580
-
581
- test("includes pending canonical requests with toolName", () => {
582
- createTestCanonicalRequest({
583
- conversationId: "conv-tool",
584
- requestId: "req-tool-prompt",
585
- toolName: "read_file",
586
- });
587
-
588
- const prompts = listGuardianDecisionPrompts({
589
- conversationId: "conv-tool",
590
- });
591
- expect(prompts).toHaveLength(1);
592
- expect(prompts[0].toolName).toBe("read_file");
593
- expect(prompts[0].requestId).toBe("req-tool-prompt");
594
- });
595
-
596
- test("generates questionText from toolName when questionText is not set", () => {
597
- createTestCanonicalRequest({
598
- conversationId: "conv-gen-qt",
599
- requestId: "req-gen-qt",
600
- toolName: "bash",
601
- });
602
-
603
- const prompts = listGuardianDecisionPrompts({
604
- conversationId: "conv-gen-qt",
605
- });
606
- expect(prompts).toHaveLength(1);
607
- expect(prompts[0].questionText).toBe("Approve tool: bash");
608
- });
609
-
610
- test("uses questionText when it is set", () => {
611
- createTestCanonicalRequest({
612
- conversationId: "conv-qt",
613
- requestId: "req-qt",
614
- toolName: "bash",
615
- questionText: "Run bash: ls -la",
616
- });
617
-
618
- const prompts = listGuardianDecisionPrompts({ conversationId: "conv-qt" });
619
- expect(prompts).toHaveLength(1);
620
- expect(prompts[0].questionText).toBe("Run bash: ls -la");
621
- });
622
-
623
- test("returns prompt with correct shape fields", () => {
624
- createTestCanonicalRequest({
625
- conversationId: "conv-shape",
626
- requestId: "req-shape",
627
- toolName: "bash",
628
- questionText: "Test prompt",
629
- });
630
-
631
- const prompts = listGuardianDecisionPrompts({
632
- conversationId: "conv-shape",
633
- });
634
- expect(prompts).toHaveLength(1);
635
- const prompt = prompts[0];
636
- expect(prompt.requestId).toBe("req-shape");
637
- expect(prompt.state).toBe("pending");
638
- expect(prompt.conversationId).toBe("conv-shape");
639
- expect(prompt.toolName).toBe("bash");
640
- expect(prompt.actions).toBeDefined();
641
- expect(prompt.expiresAt).toBeGreaterThan(Date.now() - 5000);
642
- expect(prompt.kind).toBe("tool_approval");
643
- });
644
-
645
- test("includes access_request kind canonical requests", () => {
646
- createTestCanonicalRequest({
647
- conversationId: "conv-ar-prompt",
648
- requestId: "req-ar-prompt",
649
- kind: "access_request",
650
- toolName: "ingress_access_request",
651
- questionText: "User wants access",
652
- });
653
-
654
- const prompts = listGuardianDecisionPrompts({
655
- conversationId: "conv-ar-prompt",
656
- });
657
- expect(prompts).toHaveLength(1);
658
- expect(prompts[0].kind).toBe("access_request");
659
- // buildKindAwareQuestionText appends request-code fallback instructions
660
- // for access_request kind, so use partial matching
661
- expect(prompts[0].questionText).toContain("User wants access");
662
- expect(prompts[0].questionText).toContain("approve");
663
- expect(prompts[0].questionText).toContain("reject");
664
- expect(prompts[0].questionText).toContain("open invite flow");
665
- });
666
-
667
- test("only returns requests for the given conversationId", () => {
668
- createTestCanonicalRequest({
669
- conversationId: "conv-a",
670
- requestId: "req-a",
671
- });
672
- createTestCanonicalRequest({
673
- conversationId: "conv-b",
674
- requestId: "req-b",
675
- });
676
-
677
- const promptsA = listGuardianDecisionPrompts({ conversationId: "conv-a" });
678
- expect(promptsA).toHaveLength(1);
679
- expect(promptsA[0].requestId).toBe("req-a");
680
-
681
- const promptsB = listGuardianDecisionPrompts({ conversationId: "conv-b" });
682
- expect(promptsB).toHaveLength(1);
683
- expect(promptsB[0].requestId).toBe("req-b");
684
- });
685
-
686
- test("includes requests delivered to the queried destination conversation", () => {
687
- createTestCanonicalRequest({
688
- conversationId: "conv-source",
689
- requestId: "req-dest-1",
690
- kind: "pending_question",
691
- questionText: "What should I do?",
692
- });
693
- createCanonicalGuardianDelivery({
694
- requestId: "req-dest-1",
695
- destinationChannel: "vellum",
696
- destinationConversationId: "conv-guardian-dest",
697
- });
698
-
699
- const prompts = listGuardianDecisionPrompts({
700
- conversationId: "conv-guardian-dest",
701
- });
702
- expect(prompts).toHaveLength(1);
703
- expect(prompts[0].requestId).toBe("req-dest-1");
704
- expect(prompts[0].questionText).toBe("What should I do?");
705
- });
706
-
707
- test("normalizes prompt conversationId to the queried thread ID", () => {
708
- createTestCanonicalRequest({
709
- conversationId: "conv-source-norm",
710
- requestId: "req-norm-1",
711
- kind: "access_request",
712
- questionText: "Grant access?",
713
- });
714
- createCanonicalGuardianDelivery({
715
- requestId: "req-norm-1",
716
- destinationChannel: "vellum",
717
- destinationConversationId: "conv-dest-norm",
718
- });
719
-
720
- const prompts = listGuardianDecisionPrompts({
721
- conversationId: "conv-dest-norm",
722
- });
723
- expect(prompts).toHaveLength(1);
724
- // conversationId should be normalized to the queried thread, not the source
725
- expect(prompts[0].conversationId).toBe("conv-dest-norm");
726
- });
727
-
728
- test("deduplicates requests found by both source and destination", () => {
729
- createTestCanonicalRequest({
730
- conversationId: "conv-same",
731
- requestId: "req-dedup-1",
732
- });
733
- // Deliver to the same conversation (source == destination)
734
- createCanonicalGuardianDelivery({
735
- requestId: "req-dedup-1",
736
- destinationChannel: "vellum",
737
- destinationConversationId: "conv-same",
738
- });
739
-
740
- const prompts = listGuardianDecisionPrompts({
741
- conversationId: "conv-same",
742
- });
743
- expect(prompts).toHaveLength(1);
744
- expect(prompts[0].requestId).toBe("req-dedup-1");
745
- });
746
- });
747
-
748
- // =========================================================================
749
- // IPC handler: guardian_action_decision
750
- // =========================================================================
751
-
752
- describe("IPC guardian_action_decision", () => {
753
- beforeEach(() => {
754
- resetDb();
755
- initializeDb();
756
- resetTables();
757
- });
758
-
759
- const handler = guardianActionsHandlers.guardian_action_decision;
760
-
761
- test("rejects invalid action", async () => {
762
- const { socket, ctx, sent } = createIpcStub();
763
- await handler(
764
- {
765
- type: "guardian_action_decision",
766
- requestId: "req-ipc-1",
767
- action: "self_destruct",
768
- } as any,
769
- socket as any,
770
- ctx as any,
771
- );
772
- expect(sent).toHaveLength(1);
773
- expect(sent[0].applied).toBe(false);
774
- expect(sent[0].reason).toBe("invalid_action");
775
- expect(sent[0].requestId).toBe("req-ipc-1");
776
- });
777
-
778
- test("returns not_found when no canonical request exists", async () => {
779
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
780
- applied: false,
781
- reason: "not_found",
782
- });
783
-
784
- const { socket, ctx, sent } = createIpcStub();
785
- await handler(
786
- {
787
- type: "guardian_action_decision",
788
- requestId: "req-ghost",
789
- action: "approve_once",
790
- } as any,
791
- socket as any,
792
- ctx as any,
793
- );
794
- expect(sent).toHaveLength(1);
795
- expect(sent[0].applied).toBe(false);
796
- expect(sent[0].reason).toBe("not_found");
797
- });
798
-
799
- test("applies decision via applyCanonicalGuardianDecision for tool approval", async () => {
800
- createTestCanonicalRequest({
801
- conversationId: "conv-ipc-1",
802
- requestId: "req-ipc-gd",
803
- });
804
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
805
- applied: true,
806
- requestId: "req-ipc-gd",
807
- grantMinted: false,
808
- });
809
-
810
- const { socket, ctx, sent } = createIpcStub();
811
- await handler(
812
- {
813
- type: "guardian_action_decision",
814
- requestId: "req-ipc-gd",
815
- action: "approve_once",
816
- } as any,
817
- socket as any,
818
- ctx as any,
819
- );
820
- expect(sent).toHaveLength(1);
821
- expect(sent[0].applied).toBe(true);
822
- expect(sent[0].requestId).toBe("req-ipc-gd");
823
- expect(mockApplyCanonicalGuardianDecision).toHaveBeenCalledTimes(1);
824
- });
825
-
826
- test("rejects decision when conversationId does not match canonical request", async () => {
827
- createTestCanonicalRequest({
828
- conversationId: "conv-ipc-correct",
829
- requestId: "req-ipc-scope",
830
- });
831
-
832
- const { socket, ctx, sent } = createIpcStub();
833
- await handler(
834
- {
835
- type: "guardian_action_decision",
836
- requestId: "req-ipc-scope",
837
- action: "approve_once",
838
- conversationId: "conv-ipc-wrong",
839
- } as any,
840
- socket as any,
841
- ctx as any,
842
- );
843
- expect(sent).toHaveLength(1);
844
- expect(sent[0].applied).toBe(false);
845
- expect(sent[0].reason).toBe("not_found");
846
- expect(mockApplyCanonicalGuardianDecision).not.toHaveBeenCalled();
847
- });
848
-
849
- test("allows decision when conversationId matches", async () => {
850
- createTestCanonicalRequest({
851
- conversationId: "conv-ipc-match",
852
- requestId: "req-ipc-match",
853
- });
854
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
855
- applied: true,
856
- requestId: "req-ipc-match",
857
- grantMinted: false,
858
- });
859
-
860
- const { socket, ctx, sent } = createIpcStub();
861
- await handler(
862
- {
863
- type: "guardian_action_decision",
864
- requestId: "req-ipc-match",
865
- action: "reject",
866
- conversationId: "conv-ipc-match",
867
- } as any,
868
- socket as any,
869
- ctx as any,
870
- );
871
- expect(sent).toHaveLength(1);
872
- expect(sent[0].applied).toBe(true);
873
- });
874
-
875
- test("allows decision when conversationId matches a delivery destination", async () => {
876
- createTestCanonicalRequest({
877
- conversationId: "conv-ipc-source",
878
- requestId: "req-ipc-dest-scope",
879
- kind: "pending_question",
880
- });
881
- createCanonicalGuardianDelivery({
882
- requestId: "req-ipc-dest-scope",
883
- destinationChannel: "vellum",
884
- destinationConversationId: "conv-ipc-guardian-thread",
885
- });
886
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
887
- applied: true,
888
- requestId: "req-ipc-dest-scope",
889
- grantMinted: false,
890
- });
891
-
892
- const { socket, ctx, sent } = createIpcStub();
893
- await handler(
894
- {
895
- type: "guardian_action_decision",
896
- requestId: "req-ipc-dest-scope",
897
- action: "approve_once",
898
- conversationId: "conv-ipc-guardian-thread",
899
- } as any,
900
- socket as any,
901
- ctx as any,
902
- );
903
- expect(sent).toHaveLength(1);
904
- expect(sent[0].applied).toBe(true);
905
- });
906
-
907
- test("applies decision for access_request kind through canonical primitive", async () => {
908
- createTestCanonicalRequest({
909
- conversationId: "conv-ipc-access",
910
- requestId: "req-ipc-access",
911
- kind: "access_request",
912
- toolName: "ingress_access_request",
913
- guardianExternalUserId: "guardian-99",
914
- });
915
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
916
- applied: true,
917
- requestId: "req-ipc-access",
918
- grantMinted: false,
919
- });
920
-
921
- const { socket, ctx, sent } = createIpcStub();
922
- await handler(
923
- {
924
- type: "guardian_action_decision",
925
- requestId: "req-ipc-access",
926
- action: "approve_once",
927
- } as any,
928
- socket as any,
929
- ctx as any,
930
- );
931
- expect(sent).toHaveLength(1);
932
- expect(sent[0].applied).toBe(true);
933
- expect(mockApplyCanonicalGuardianDecision).toHaveBeenCalledTimes(1);
934
- });
935
-
936
- test("returns already_resolved for stale canonical request", async () => {
937
- createTestCanonicalRequest({
938
- conversationId: "conv-ipc-stale",
939
- requestId: "req-ipc-stale",
940
- });
941
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
942
- applied: false,
943
- reason: "already_resolved",
944
- });
945
-
946
- const { socket, ctx, sent } = createIpcStub();
947
- await handler(
948
- {
949
- type: "guardian_action_decision",
950
- requestId: "req-ipc-stale",
951
- action: "approve_once",
952
- } as any,
953
- socket as any,
954
- ctx as any,
955
- );
956
- expect(sent).toHaveLength(1);
957
- expect(sent[0].requestId).toBe("req-ipc-stale");
958
- expect(sent[0].reason).toBe("already_resolved");
959
- });
960
-
961
- test("passes actorContext with vellum channel and guardianPrincipalId", async () => {
962
- createTestCanonicalRequest({
963
- conversationId: "conv-ipc-actor",
964
- requestId: "req-ipc-actor",
965
- });
966
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
967
- applied: true,
968
- requestId: "req-ipc-actor",
969
- grantMinted: false,
970
- });
971
-
972
- const { socket, ctx } = createIpcStub();
973
- await handler(
974
- {
975
- type: "guardian_action_decision",
976
- requestId: "req-ipc-actor",
977
- action: "approve_once",
978
- } as any,
979
- socket as any,
980
- ctx as any,
981
- );
982
- const call = mockApplyCanonicalGuardianDecision.mock.calls[0]![0] as Record<
983
- string,
984
- unknown
985
- >;
986
- const actorContext = call.actorContext as Record<string, unknown>;
987
- expect(actorContext.channel).toBe("vellum");
988
- expect(actorContext.guardianPrincipalId).toBeDefined();
989
- });
990
- });
991
-
992
- // =========================================================================
993
- // IPC handler: guardian_actions_pending_request
994
- // =========================================================================
995
-
996
- describe("IPC guardian_actions_pending_request", () => {
997
- beforeEach(() => {
998
- resetDb();
999
- initializeDb();
1000
- resetTables();
1001
- });
1002
-
1003
- const handler = guardianActionsHandlers.guardian_actions_pending_request;
1004
-
1005
- test("returns prompts for a conversation", () => {
1006
- createTestCanonicalRequest({
1007
- conversationId: "conv-ipc-list",
1008
- requestId: "req-ipc-list",
1009
- questionText: "Run bash: pwd",
1010
- });
1011
-
1012
- const { socket, ctx, sent } = createIpcStub();
1013
- handler(
1014
- {
1015
- type: "guardian_actions_pending_request",
1016
- conversationId: "conv-ipc-list",
1017
- } as any,
1018
- socket as any,
1019
- ctx as any,
1020
- );
1021
- expect(sent).toHaveLength(1);
1022
- expect(sent[0].type).toBe("guardian_actions_pending_response");
1023
- expect(sent[0].conversationId).toBe("conv-ipc-list");
1024
- const prompts = sent[0].prompts as Array<{
1025
- requestId: string;
1026
- questionText: string;
1027
- }>;
1028
- expect(prompts).toHaveLength(1);
1029
- expect(prompts[0].requestId).toBe("req-ipc-list");
1030
- expect(prompts[0].questionText).toBe("Run bash: pwd");
1031
- });
1032
-
1033
- test("returns empty prompts for conversation with no pending requests", () => {
1034
- const { socket, ctx, sent } = createIpcStub();
1035
- handler(
1036
- {
1037
- type: "guardian_actions_pending_request",
1038
- conversationId: "conv-empty-ipc",
1039
- } as any,
1040
- socket as any,
1041
- ctx as any,
1042
- );
1043
- expect(sent).toHaveLength(1);
1044
- const prompts = sent[0].prompts as unknown[];
1045
- expect(prompts).toHaveLength(0);
1046
- });
1047
-
1048
- test("returns prompts delivered to the queried destination conversation", () => {
1049
- createTestCanonicalRequest({
1050
- conversationId: "conv-ipc-source-list",
1051
- requestId: "req-ipc-dest-list",
1052
- kind: "pending_question",
1053
- questionText: "Voice question?",
1054
- });
1055
- createCanonicalGuardianDelivery({
1056
- requestId: "req-ipc-dest-list",
1057
- destinationChannel: "vellum",
1058
- destinationConversationId: "conv-ipc-dest-list",
1059
- });
1060
-
1061
- const { socket, ctx, sent } = createIpcStub();
1062
- handler(
1063
- {
1064
- type: "guardian_actions_pending_request",
1065
- conversationId: "conv-ipc-dest-list",
1066
- } as any,
1067
- socket as any,
1068
- ctx as any,
1069
- );
1070
- expect(sent).toHaveLength(1);
1071
- const prompts = sent[0].prompts as Array<{
1072
- requestId: string;
1073
- questionText: string;
1074
- conversationId: string;
1075
- }>;
1076
- expect(prompts).toHaveLength(1);
1077
- expect(prompts[0].requestId).toBe("req-ipc-dest-list");
1078
- expect(prompts[0].questionText).toBe("Voice question?");
1079
- expect(prompts[0].conversationId).toBe("conv-ipc-dest-list");
1080
- });
1081
- });
1082
-
1083
- // =========================================================================
1084
- // Integration: pending_question visible/actionable in guardian thread
1085
- // =========================================================================
1086
-
1087
- describe("integration: pending_question visible and actionable in guardian thread", () => {
1088
- beforeEach(() => {
1089
- resetDb();
1090
- initializeDb();
1091
- resetTables();
1092
- });
1093
-
1094
- test("pending_question delivered to guardian thread is visible via pending endpoint", () => {
1095
- createTestCanonicalRequest({
1096
- conversationId: "conv-voice-source",
1097
- requestId: "req-pq-visible-1",
1098
- kind: "pending_question",
1099
- questionText: "What time works best for the appointment?",
1100
- });
1101
- createCanonicalGuardianDelivery({
1102
- requestId: "req-pq-visible-1",
1103
- destinationChannel: "vellum",
1104
- destinationConversationId: "conv-guardian-macos",
1105
- });
1106
-
1107
- const prompts = listGuardianDecisionPrompts({
1108
- conversationId: "conv-guardian-macos",
1109
- });
1110
- expect(prompts).toHaveLength(1);
1111
- expect(prompts[0].requestId).toBe("req-pq-visible-1");
1112
- expect(prompts[0].kind).toBe("pending_question");
1113
- expect(prompts[0].questionText).toBe(
1114
- "What time works best for the appointment?",
1115
- );
1116
- expect(prompts[0].conversationId).toBe("conv-guardian-macos");
1117
- });
1118
-
1119
- test("pending_question prompt has approve/reject actions (guardian-on-behalf)", () => {
1120
- createTestCanonicalRequest({
1121
- conversationId: "conv-voice-src-2",
1122
- requestId: "req-pq-actions-1",
1123
- kind: "pending_question",
1124
- questionText: "Should I confirm the meeting?",
1125
- });
1126
-
1127
- const prompts = listGuardianDecisionPrompts({
1128
- conversationId: "conv-voice-src-2",
1129
- });
1130
- expect(prompts).toHaveLength(1);
1131
- const actions = prompts[0].actions.map((a: { action: string }) => a.action);
1132
- expect(actions).toContain("approve_once");
1133
- expect(actions).toContain("reject");
1134
- // Guardian-on-behalf: no approve_always or temporary modes
1135
- expect(actions).not.toContain("approve_always");
1136
- expect(actions).not.toContain("approve_10m");
1137
- expect(actions).not.toContain("approve_thread");
1138
- });
1139
-
1140
- test("pending_question is actionable via HTTP decision endpoint", async () => {
1141
- createTestCanonicalRequest({
1142
- conversationId: "conv-voice-src-3",
1143
- requestId: "req-pq-action-http",
1144
- kind: "pending_question",
1145
- questionText: "Allow email to bob@example.com?",
1146
- toolName: "send_email",
1147
- });
1148
- createCanonicalGuardianDelivery({
1149
- requestId: "req-pq-action-http",
1150
- destinationChannel: "vellum",
1151
- destinationConversationId: "conv-guardian-thread-pq",
1152
- });
1153
-
1154
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
1155
- applied: true,
1156
- requestId: "req-pq-action-http",
1157
- grantMinted: false,
1158
- });
1159
-
1160
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
1161
- method: "POST",
1162
- body: JSON.stringify({
1163
- requestId: "req-pq-action-http",
1164
- action: "approve_once",
1165
- conversationId: "conv-guardian-thread-pq",
1166
- }),
1167
- });
1168
- const res = await handleGuardianActionDecision(req, mockAuthContext);
1169
- expect(res.status).toBe(200);
1170
- const body = await res.json();
1171
- expect(body.applied).toBe(true);
1172
- expect(mockApplyCanonicalGuardianDecision).toHaveBeenCalledTimes(1);
1173
- });
1174
- });
1175
-
1176
- // =========================================================================
1177
- // Integration: access_request visible/actionable in guardian thread
1178
- // =========================================================================
1179
-
1180
- describe("integration: access_request visible and actionable in guardian thread", () => {
1181
- beforeEach(() => {
1182
- resetDb();
1183
- initializeDb();
1184
- resetTables();
1185
- });
1186
-
1187
- test("access_request delivered to guardian thread is visible via pending endpoint", () => {
1188
- createTestCanonicalRequest({
1189
- conversationId: "conv-access-src-1",
1190
- requestId: "req-ar-visible-1",
1191
- kind: "access_request",
1192
- toolName: "ingress_access_request",
1193
- questionText: "Alice via Telegram is requesting access to the assistant",
1194
- });
1195
- createCanonicalGuardianDelivery({
1196
- requestId: "req-ar-visible-1",
1197
- destinationChannel: "vellum",
1198
- destinationConversationId: "conv-guardian-macos-ar",
1199
- });
1200
-
1201
- const prompts = listGuardianDecisionPrompts({
1202
- conversationId: "conv-guardian-macos-ar",
1203
- });
1204
- expect(prompts).toHaveLength(1);
1205
- expect(prompts[0].requestId).toBe("req-ar-visible-1");
1206
- expect(prompts[0].kind).toBe("access_request");
1207
- expect(prompts[0].conversationId).toBe("conv-guardian-macos-ar");
1208
- });
1209
-
1210
- test("access_request prompt includes text fallback instructions with request code", () => {
1211
- createTestCanonicalRequest({
1212
- conversationId: "conv-access-src-2",
1213
- requestId: "req-ar-fallback-1",
1214
- kind: "access_request",
1215
- toolName: "ingress_access_request",
1216
- questionText: "Bob via WhatsApp is requesting access to the assistant",
1217
- });
1218
-
1219
- const prompts = listGuardianDecisionPrompts({
1220
- conversationId: "conv-access-src-2",
1221
- });
1222
- expect(prompts).toHaveLength(1);
1223
- const qt = prompts[0].questionText;
1224
- // Must contain original question text
1225
- expect(qt).toContain(
1226
- "Bob via WhatsApp is requesting access to the assistant",
1227
- );
1228
- // Must contain request-code-based approve/reject directive
1229
- expect(qt).toContain("approve");
1230
- expect(qt).toContain("reject");
1231
- // Must contain invite flow directive
1232
- expect(qt).toContain("open invite flow");
1233
- });
1234
-
1235
- test("access_request prompt has approve/reject actions (guardian-on-behalf)", () => {
1236
- createTestCanonicalRequest({
1237
- conversationId: "conv-access-src-3",
1238
- requestId: "req-ar-actions-1",
1239
- kind: "access_request",
1240
- toolName: "ingress_access_request",
1241
- questionText: "Carol is requesting access",
1242
- });
1243
-
1244
- const prompts = listGuardianDecisionPrompts({
1245
- conversationId: "conv-access-src-3",
1246
- });
1247
- expect(prompts).toHaveLength(1);
1248
- const actions = prompts[0].actions.map((a: { action: string }) => a.action);
1249
- expect(actions).toContain("approve_once");
1250
- expect(actions).toContain("reject");
1251
- expect(actions).not.toContain("approve_always");
1252
- });
1253
-
1254
- test("access_request is actionable via HTTP decision endpoint from guardian thread", async () => {
1255
- createTestCanonicalRequest({
1256
- conversationId: "conv-access-src-4",
1257
- requestId: "req-ar-action-http",
1258
- kind: "access_request",
1259
- toolName: "ingress_access_request",
1260
- guardianExternalUserId: "guardian-88",
1261
- });
1262
- createCanonicalGuardianDelivery({
1263
- requestId: "req-ar-action-http",
1264
- destinationChannel: "vellum",
1265
- destinationConversationId: "conv-guardian-thread-ar",
1266
- });
1267
-
1268
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
1269
- applied: true,
1270
- requestId: "req-ar-action-http",
1271
- grantMinted: false,
1272
- });
1273
-
1274
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
1275
- method: "POST",
1276
- body: JSON.stringify({
1277
- requestId: "req-ar-action-http",
1278
- action: "approve_once",
1279
- conversationId: "conv-guardian-thread-ar",
1280
- }),
1281
- });
1282
- const res = await handleGuardianActionDecision(req, mockAuthContext);
1283
- expect(res.status).toBe(200);
1284
- const body = await res.json();
1285
- expect(body.applied).toBe(true);
1286
- expect(mockApplyCanonicalGuardianDecision).toHaveBeenCalledTimes(1);
1287
- });
1288
-
1289
- test("access_request reject is actionable via HTTP decision endpoint", async () => {
1290
- createTestCanonicalRequest({
1291
- conversationId: "conv-access-src-5",
1292
- requestId: "req-ar-reject-http",
1293
- kind: "access_request",
1294
- toolName: "ingress_access_request",
1295
- });
1296
-
1297
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
1298
- applied: true,
1299
- requestId: "req-ar-reject-http",
1300
- grantMinted: false,
1301
- });
1302
-
1303
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
1304
- method: "POST",
1305
- body: JSON.stringify({
1306
- requestId: "req-ar-reject-http",
1307
- action: "reject",
1308
- }),
1309
- });
1310
- const res = await handleGuardianActionDecision(req, mockAuthContext);
1311
- expect(res.status).toBe(200);
1312
- const body = await res.json();
1313
- expect(body.applied).toBe(true);
1314
- });
1315
- });
1316
-
1317
- // =========================================================================
1318
- // Integration: text/code fallback routing when buttons are not used
1319
- // =========================================================================
1320
-
1321
- describe("integration: text/code fallback routing remains functional", () => {
1322
- beforeEach(() => {
1323
- resetDb();
1324
- initializeDb();
1325
- resetTables();
1326
- });
1327
-
1328
- test("requestCode is always present in prompt for text-based fallback", () => {
1329
- createTestCanonicalRequest({
1330
- conversationId: "conv-fallback-1",
1331
- requestId: "req-fallback-code-1",
1332
- kind: "tool_approval",
1333
- toolName: "bash",
1334
- questionText: "Run bash: rm -rf /tmp/test",
1335
- });
1336
-
1337
- const prompts = listGuardianDecisionPrompts({
1338
- conversationId: "conv-fallback-1",
1339
- });
1340
- expect(prompts).toHaveLength(1);
1341
- // requestCode must be a non-empty string for text-based fallback
1342
- expect(prompts[0].requestCode).toBeTruthy();
1343
- expect(prompts[0].requestCode.length).toBeGreaterThanOrEqual(6);
1344
- });
1345
-
1346
- test("pending_question prompt includes requestCode for text fallback", () => {
1347
- createTestCanonicalRequest({
1348
- conversationId: "conv-fallback-pq",
1349
- requestId: "req-fallback-pq-1",
1350
- kind: "pending_question",
1351
- questionText: "When should I schedule the delivery?",
1352
- });
1353
-
1354
- const prompts = listGuardianDecisionPrompts({
1355
- conversationId: "conv-fallback-pq",
1356
- });
1357
- expect(prompts).toHaveLength(1);
1358
- expect(prompts[0].requestCode).toBeTruthy();
1359
- expect(prompts[0].kind).toBe("pending_question");
1360
- });
1361
-
1362
- test("access_request prompt includes requestCode and text directives for fallback", () => {
1363
- createTestCanonicalRequest({
1364
- conversationId: "conv-fallback-ar",
1365
- requestId: "req-fallback-ar-1",
1366
- kind: "access_request",
1367
- toolName: "ingress_access_request",
1368
- questionText: "Dave is requesting access",
1369
- });
1370
-
1371
- const prompts = listGuardianDecisionPrompts({
1372
- conversationId: "conv-fallback-ar",
1373
- });
1374
- expect(prompts).toHaveLength(1);
1375
- const prompt = prompts[0];
1376
- // requestCode present for code-based text fallback
1377
- expect(prompt.requestCode).toBeTruthy();
1378
- // questionText includes explicit text fallback instructions
1379
- const code = prompt.requestCode;
1380
- expect(prompt.questionText).toContain(`"${code} approve"`);
1381
- expect(prompt.questionText).toContain(`"${code} reject"`);
1382
- expect(prompt.questionText).toContain('"open invite flow"');
1383
- });
1384
-
1385
- test("mixed pending_question and access_request visible in same guardian thread", () => {
1386
- // Create a pending_question delivered to guardian thread
1387
- createTestCanonicalRequest({
1388
- conversationId: "conv-src-mixed-1",
1389
- requestId: "req-mixed-pq",
1390
- kind: "pending_question",
1391
- questionText: "What time works?",
1392
- });
1393
- createCanonicalGuardianDelivery({
1394
- requestId: "req-mixed-pq",
1395
- destinationChannel: "vellum",
1396
- destinationConversationId: "conv-guardian-mixed",
1397
- });
1398
-
1399
- // Create an access_request delivered to same guardian thread
1400
- createTestCanonicalRequest({
1401
- conversationId: "conv-src-mixed-2",
1402
- requestId: "req-mixed-ar",
1403
- kind: "access_request",
1404
- toolName: "ingress_access_request",
1405
- questionText: "Eve is requesting access",
1406
- });
1407
- createCanonicalGuardianDelivery({
1408
- requestId: "req-mixed-ar",
1409
- destinationChannel: "vellum",
1410
- destinationConversationId: "conv-guardian-mixed",
1411
- });
1412
-
1413
- const prompts = listGuardianDecisionPrompts({
1414
- conversationId: "conv-guardian-mixed",
1415
- });
1416
- expect(prompts).toHaveLength(2);
1417
- const kinds = prompts.map((p: { kind?: string }) => p.kind).sort();
1418
- expect(kinds).toEqual(["access_request", "pending_question"]);
1419
-
1420
- // Both prompts have requestCodes for text fallback
1421
- for (const prompt of prompts) {
1422
- expect(prompt.requestCode).toBeTruthy();
1423
- expect(prompt.actions.length).toBeGreaterThan(0);
1424
- }
1425
- });
1426
-
1427
- test("stale access_request decision returns reason without regression", async () => {
1428
- createTestCanonicalRequest({
1429
- conversationId: "conv-stale-ar",
1430
- requestId: "req-stale-ar-1",
1431
- kind: "access_request",
1432
- toolName: "ingress_access_request",
1433
- });
1434
- mockApplyCanonicalGuardianDecision.mockResolvedValueOnce({
1435
- applied: false,
1436
- reason: "already_resolved",
1437
- });
1438
-
1439
- const req = new Request("http://localhost/v1/guardian-actions/decision", {
1440
- method: "POST",
1441
- body: JSON.stringify({
1442
- requestId: "req-stale-ar-1",
1443
- action: "approve_once",
1444
- }),
1445
- });
1446
- const res = await handleGuardianActionDecision(req, mockAuthContext);
1447
- const body = await res.json();
1448
- expect(body.applied).toBe(false);
1449
- expect(body.reason).toBe("already_resolved");
1450
- expect(body.requestId).toBe("req-stale-ar-1");
1451
- });
1452
- });