@vellumai/assistant 0.9.0 → 0.10.0-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (572) hide show
  1. package/ARCHITECTURE.md +18 -34
  2. package/bun.lock +7 -8
  3. package/docs/activation-funnel-telemetry.md +28 -22
  4. package/docs/architecture/security.md +29 -28
  5. package/docs/stt-provider-onboarding.md +3 -5
  6. package/docs/workflows-testing.md +13 -44
  7. package/docs/workflows.md +3 -5
  8. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
  9. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
  10. package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
  11. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  12. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  13. package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
  14. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
  15. package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
  16. package/openapi.yaml +976 -63
  17. package/package.json +2 -1
  18. package/scripts/sync-llm-catalog.ts +6 -15
  19. package/scripts/sync-web-search-catalog.ts +3 -11
  20. package/src/__tests__/access-request-card-view.test.ts +98 -0
  21. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  22. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
  23. package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
  24. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  25. package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
  26. package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
  27. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  28. package/src/__tests__/app-compiler.test.ts +15 -1
  29. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  30. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  31. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  32. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  33. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  34. package/src/__tests__/btw-routes.test.ts +6 -8
  35. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  36. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  37. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  38. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  39. package/src/__tests__/checker.test.ts +0 -3
  40. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  41. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  43. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  44. package/src/__tests__/config-schema.test.ts +35 -0
  45. package/src/__tests__/config-watcher.test.ts +0 -18
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  47. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  48. package/src/__tests__/contacts-tools.test.ts +29 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  52. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  53. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  54. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  55. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  56. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  57. package/src/__tests__/conversation-title-service.test.ts +62 -0
  58. package/src/__tests__/credential-broker.test.ts +449 -1
  59. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  60. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  61. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  62. package/src/__tests__/credential-routes.test.ts +360 -0
  63. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  64. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  65. package/src/__tests__/disk-usage.test.ts +65 -0
  66. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  67. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  68. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  69. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  71. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  72. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  73. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  74. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  75. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  76. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  77. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  78. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  79. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  80. package/src/__tests__/identity-routes.test.ts +0 -189
  81. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  82. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  83. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  84. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  85. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  86. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  87. package/src/__tests__/llm-resolver.test.ts +70 -24
  88. package/src/__tests__/llm-schema.test.ts +1 -0
  89. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  90. package/src/__tests__/mcp-health-check.test.ts +6 -7
  91. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  92. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  93. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  94. package/src/__tests__/path-policy.test.ts +34 -0
  95. package/src/__tests__/persona-resolver.test.ts +49 -14
  96. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  97. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  98. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  99. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  100. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  101. package/src/__tests__/reaction-persistence.test.ts +150 -29
  102. package/src/__tests__/registry.test.ts +2 -7
  103. package/src/__tests__/relay-server.test.ts +285 -0
  104. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  105. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  106. package/src/__tests__/schedule-routes.test.ts +0 -30
  107. package/src/__tests__/schedule-tools.test.ts +2 -18
  108. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  109. package/src/__tests__/skill-execute-input.test.ts +51 -1
  110. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  111. package/src/__tests__/skills.test.ts +51 -0
  112. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  113. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  114. package/src/__tests__/subagent-tools.test.ts +266 -0
  115. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  116. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  117. package/src/__tests__/title-generate-hook.test.ts +100 -3
  118. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  119. package/src/__tests__/token-manager.test.ts +519 -0
  120. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  121. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  122. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  123. package/src/__tests__/tool-executor.test.ts +0 -79
  124. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  125. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  126. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  127. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  128. package/src/__tests__/twilio-routes.test.ts +81 -1
  129. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  130. package/src/__tests__/weak-open-model.test.ts +30 -0
  131. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  132. package/src/__tests__/workspace-greetings.test.ts +152 -0
  133. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  134. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  136. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  137. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  138. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  139. package/src/agent/loop.ts +49 -29
  140. package/src/api/README.md +6 -6
  141. package/src/api/events/tool-result.ts +6 -0
  142. package/src/api/events/workflow-completed.ts +53 -0
  143. package/src/api/events/workflow-leaf-finished.ts +38 -0
  144. package/src/api/events/workflow-leaf-started.ts +35 -0
  145. package/src/api/events/workflow-progress.ts +32 -0
  146. package/src/api/events/workflow-started.ts +31 -0
  147. package/src/api/index.ts +40 -0
  148. package/src/api/responses/conversation-message.ts +28 -4
  149. package/src/api/responses/home.ts +26 -4
  150. package/src/api/responses/workflow-journal.ts +53 -0
  151. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  152. package/src/approvals/guardian-decision-primitive.ts +26 -3
  153. package/src/approvals/guardian-request-resolvers.ts +183 -80
  154. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  155. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  156. package/src/calls/call-pointer-messages.ts +10 -4
  157. package/src/calls/channel-admission-reader.ts +104 -0
  158. package/src/calls/guardian-dispatch.ts +17 -45
  159. package/src/calls/media-stream-server.ts +84 -2
  160. package/src/calls/relay-access-wait.ts +1 -1
  161. package/src/calls/relay-server.ts +66 -0
  162. package/src/calls/relay-setup-router.ts +82 -1
  163. package/src/calls/twilio-routes.ts +17 -8
  164. package/src/calls/voice-session-bridge.ts +2 -2
  165. package/src/cli/commands/clients.ts +3 -0
  166. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  167. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  168. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  169. package/src/cli/commands/memory/index.ts +30 -0
  170. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  171. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  172. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  173. package/src/cli/commands/oauth/status.test.ts +36 -0
  174. package/src/cli/commands/oauth/status.ts +23 -3
  175. package/src/cli/commands/plugins.ts +197 -4
  176. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  177. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  178. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  179. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  180. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  181. package/src/cli/lib/diff-plugin.ts +346 -0
  182. package/src/cli/lib/inspect-plugin.ts +12 -1
  183. package/src/cli/lib/install-from-github.ts +105 -17
  184. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  185. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  186. package/src/cli/lib/plugin-surfaces.ts +104 -0
  187. package/src/cli/lib/upgrade-plugin.ts +298 -10
  188. package/src/cli/program.ts +2 -6
  189. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  190. package/src/config/assistant-feature-flags.ts +22 -7
  191. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  192. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  193. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  194. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  195. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  196. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  197. package/src/config/bundled-tool-registry.ts +2 -7
  198. package/src/config/call-site-defaults.ts +15 -2
  199. package/src/config/feature-flag-registry.json +46 -31
  200. package/src/config/inference-profile-validation.ts +26 -0
  201. package/src/config/llm-resolver.ts +3 -0
  202. package/src/config/loader.ts +4 -0
  203. package/src/config/memory-v3-gate.ts +11 -0
  204. package/src/config/profile-order.ts +28 -0
  205. package/src/config/schema.ts +8 -6
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  207. package/src/config/schemas/call-site-catalog.ts +7 -0
  208. package/src/config/schemas/channels.ts +11 -0
  209. package/src/config/schemas/elevenlabs.ts +0 -1
  210. package/src/config/schemas/llm.ts +31 -0
  211. package/src/config/schemas/memory-lifecycle.ts +3 -7
  212. package/src/config/schemas/memory-v3.ts +6 -0
  213. package/src/config/schemas/platform.ts +0 -8
  214. package/src/config/schemas/services.ts +18 -0
  215. package/src/config/seed-inference-profiles.ts +109 -44
  216. package/src/config/skills.ts +21 -0
  217. package/src/config/sync-gated-profiles.ts +220 -0
  218. package/src/contacts/contact-store.ts +89 -106
  219. package/src/contacts/contacts-write.ts +5 -22
  220. package/src/contacts/types.ts +0 -1
  221. package/src/context/compactor.ts +88 -54
  222. package/src/context/strip-injections.ts +58 -10
  223. package/src/context/token-estimator.ts +1 -1
  224. package/src/credential-execution/process-manager.ts +55 -14
  225. package/src/credential-execution/prompted-credential.ts +2 -3
  226. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  227. package/src/daemon/config-watcher.ts +0 -4
  228. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  229. package/src/daemon/conversation-agent-loop.ts +114 -22
  230. package/src/daemon/conversation-history.ts +1 -1
  231. package/src/daemon/conversation-lifecycle.ts +3 -5
  232. package/src/daemon/conversation-process.ts +13 -5
  233. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  234. package/src/daemon/conversation-slash.ts +2 -23
  235. package/src/daemon/conversation-surfaces.ts +26 -0
  236. package/src/daemon/conversation-tool-setup.ts +27 -14
  237. package/src/daemon/conversation.ts +66 -14
  238. package/src/daemon/disk-pressure-policy.ts +5 -3
  239. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  240. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  241. package/src/daemon/handlers/config-a2a.ts +0 -2
  242. package/src/daemon/handlers/config-channels.ts +15 -16
  243. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  244. package/src/daemon/handlers/conversations.ts +107 -0
  245. package/src/daemon/host-browser-proxy.ts +41 -0
  246. package/src/daemon/lifecycle.ts +55 -27
  247. package/src/daemon/message-provenance.ts +2 -0
  248. package/src/daemon/message-types/contacts.ts +0 -1
  249. package/src/daemon/message-types/conversations.ts +3 -3
  250. package/src/daemon/message-types/sync.ts +0 -1
  251. package/src/daemon/message-types/web-activity.ts +7 -1
  252. package/src/daemon/message-types/workflows.ts +83 -1
  253. package/src/daemon/orphan-reaper.test.ts +0 -19
  254. package/src/daemon/orphan-reaper.ts +2 -24
  255. package/src/daemon/server.ts +0 -10
  256. package/src/daemon/tool-setup-types.ts +4 -0
  257. package/src/daemon/trust-context.ts +1 -1
  258. package/src/events/tool-audit-listener.ts +2 -2
  259. package/src/home/feed-source-enrichment.test.ts +151 -0
  260. package/src/home/feed-source-enrichment.ts +176 -0
  261. package/src/home/relationship-state.ts +2 -4
  262. package/src/instrument.ts +18 -6
  263. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  264. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  265. package/src/ipc/assistant-server.ts +37 -4
  266. package/src/ipc/gateway-flag-listener.ts +18 -2
  267. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  268. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  269. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  270. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  271. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  272. package/src/memory/auth-fallback-events-store.ts +2 -2
  273. package/src/memory/auto-analysis-enqueue.ts +3 -5
  274. package/src/memory/bookmark-crud.ts +1 -2
  275. package/src/memory/canonical-guardian-store.ts +39 -1
  276. package/src/memory/conversation-crud.ts +9 -4
  277. package/src/memory/conversation-key-store.ts +17 -2
  278. package/src/memory/conversation-title-service.ts +64 -7
  279. package/src/memory/db-init.ts +17 -17
  280. package/src/memory/embedding-backend.ts +38 -1
  281. package/src/memory/embedding-billing-breaker.ts +96 -0
  282. package/src/memory/jobs-store.ts +25 -13
  283. package/src/memory/jobs-worker.ts +54 -1
  284. package/src/memory/lifecycle-events-store.ts +2 -2
  285. package/src/memory/memory-retrospective-constants.ts +4 -4
  286. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  287. package/src/memory/memory-retrospective-job.ts +28 -227
  288. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  289. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  290. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  291. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  292. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  293. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  294. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  295. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  296. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  297. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  298. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  299. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  300. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  301. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  302. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  303. package/src/memory/migrations/index.ts +6 -0
  304. package/src/memory/migrations/run-migrations.ts +41 -0
  305. package/src/memory/migrations/validate-migration-state.ts +1 -1
  306. package/src/memory/onboarding-events-store.ts +3 -3
  307. package/src/memory/schema/contacts.ts +0 -5
  308. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  309. package/src/memory/skill-loaded-events-store.ts +2 -2
  310. package/src/memory/tool-executed-events-store.test.ts +7 -7
  311. package/src/memory/turn-trace-store.test.ts +736 -0
  312. package/src/memory/turn-trace-store.ts +364 -0
  313. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  314. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  315. package/src/memory/v2/consolidation-job.ts +2 -2
  316. package/src/memory/v2/skill-content.ts +25 -7
  317. package/src/memory/v2/skill-store.ts +7 -1
  318. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  319. package/src/memory/v3-eval/eval-packets.ts +546 -0
  320. package/src/messaging/providers/slack/adapter.ts +1 -1
  321. package/src/messaging/providers/slack/api.ts +31 -0
  322. package/src/messaging/providers/slack/send.test.ts +114 -2
  323. package/src/messaging/providers/slack/send.ts +30 -7
  324. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  325. package/src/messaging/providers/slack/withdraw.ts +161 -0
  326. package/src/notifications/AGENTS.md +2 -0
  327. package/src/notifications/access-request-copy.ts +72 -59
  328. package/src/notifications/adapters/shared.ts +29 -0
  329. package/src/notifications/adapters/slack.ts +58 -103
  330. package/src/notifications/adapters/telegram.ts +2 -20
  331. package/src/notifications/approval-card-data.ts +333 -0
  332. package/src/notifications/broadcaster.ts +16 -3
  333. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  334. package/src/notifications/copy-composer.ts +3 -3
  335. package/src/notifications/decision-engine.ts +4 -2
  336. package/src/notifications/destination-resolver.ts +4 -6
  337. package/src/notifications/guardian-question-mode.ts +10 -0
  338. package/src/notifications/home-feed-side-effect.ts +7 -16
  339. package/src/notifications/notification-utils.ts +19 -20
  340. package/src/notifications/signal.ts +79 -43
  341. package/src/notifications/types.ts +98 -121
  342. package/src/oauth/AGENTS.md +5 -24
  343. package/src/permissions/checker.test.ts +51 -0
  344. package/src/permissions/checker.ts +185 -26
  345. package/src/permissions/ipc-risk-types.ts +24 -0
  346. package/src/permissions/question-prompter.test.ts +27 -0
  347. package/src/permissions/question-prompter.ts +4 -0
  348. package/src/platform/client.test.ts +119 -0
  349. package/src/platform/client.ts +66 -0
  350. package/src/platform/consent-cache.test.ts +267 -0
  351. package/src/platform/consent-cache.ts +174 -0
  352. package/src/plugin-api/constants.ts +1 -1
  353. package/src/plugin-api/index.ts +33 -1
  354. package/src/plugin-api/model-profiles.ts +33 -0
  355. package/src/plugin-api/types.ts +50 -2
  356. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  357. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  358. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  359. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  360. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  361. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  362. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  363. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  364. package/src/plugins/defaults/advisor/config.ts +21 -0
  365. package/src/plugins/defaults/advisor/consult.ts +93 -0
  366. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  367. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  368. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  369. package/src/plugins/defaults/advisor/package.json +14 -0
  370. package/src/plugins/defaults/advisor/steering.ts +67 -0
  371. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  372. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  373. package/src/plugins/defaults/index.ts +60 -0
  374. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  375. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  376. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  377. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  378. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  379. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  380. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  381. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  382. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  384. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  385. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  386. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  387. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  388. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  389. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  390. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  391. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  392. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  393. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  394. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  395. package/src/prompts/persona-resolver.ts +14 -4
  396. package/src/prompts/templates/system-sections.ts +7 -2
  397. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  398. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  399. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  400. package/src/providers/atlascloud/client.ts +85 -0
  401. package/src/providers/fetch-provider-catalog.ts +85 -0
  402. package/src/providers/inference/adapter-factory.ts +3 -0
  403. package/src/providers/model-catalog.ts +58 -0
  404. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  405. package/src/providers/openai/chat-completions-provider.ts +7 -0
  406. package/src/providers/openai/responses-provider.ts +10 -0
  407. package/src/providers/provider-send-message.ts +11 -3
  408. package/src/providers/retry.ts +53 -12
  409. package/src/providers/search-provider-catalog.ts +10 -0
  410. package/src/providers/weak-open-model.ts +22 -0
  411. package/src/runtime/AGENTS.md +0 -1
  412. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  413. package/src/runtime/__tests__/client-health.test.ts +44 -0
  414. package/src/runtime/access-request-helper.ts +21 -53
  415. package/src/runtime/actor-trust-resolver.ts +59 -63
  416. package/src/runtime/agent-wake.ts +52 -0
  417. package/src/runtime/assistant-event-hub.ts +18 -4
  418. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  419. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  420. package/src/runtime/btw-sidechain.ts +3 -6
  421. package/src/runtime/capabilities.test.ts +120 -0
  422. package/src/runtime/capabilities.ts +197 -0
  423. package/src/runtime/channel-approval-types.ts +22 -45
  424. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  425. package/src/runtime/channel-retry-sweep.ts +1 -0
  426. package/src/runtime/channel-verification-service.ts +3 -3
  427. package/src/runtime/client-health.ts +26 -0
  428. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  429. package/src/runtime/effective-capabilities.test.ts +128 -0
  430. package/src/runtime/effective-capabilities.ts +84 -0
  431. package/src/runtime/guardian-reply-router.ts +106 -21
  432. package/src/runtime/invite-redemption-service.ts +9 -25
  433. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  434. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  435. package/src/runtime/pending-interactions.ts +15 -0
  436. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  437. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  438. package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
  439. package/src/runtime/routes/app-routes.ts +1 -1
  440. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  441. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  442. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  443. package/src/runtime/routes/btw-routes.ts +1 -27
  444. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  445. package/src/runtime/routes/client-routes.ts +10 -0
  446. package/src/runtime/routes/contact-routes.ts +31 -8
  447. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  448. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  449. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  450. package/src/runtime/routes/conversation-routes.ts +39 -14
  451. package/src/runtime/routes/credential-routes.ts +40 -16
  452. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  453. package/src/runtime/routes/events-routes.ts +1 -3
  454. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  455. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  456. package/src/runtime/routes/home-feed-routes.ts +8 -3
  457. package/src/runtime/routes/identity-routes.ts +1 -296
  458. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  459. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  460. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  461. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  462. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  463. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  464. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  465. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  466. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  467. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  468. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  469. package/src/runtime/routes/index.ts +2 -0
  470. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  471. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  472. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  473. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  474. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  475. package/src/runtime/routes/notification-routes.ts +122 -133
  476. package/src/runtime/routes/platform-routes.ts +2 -2
  477. package/src/runtime/routes/plugins-routes.ts +202 -3
  478. package/src/runtime/routes/schedule-routes.ts +0 -22
  479. package/src/runtime/routes/secret-routes.ts +10 -0
  480. package/src/runtime/routes/surface-action-routes.ts +2 -1
  481. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  482. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  483. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  484. package/src/runtime/routes/workflow-routes.ts +131 -29
  485. package/src/runtime/routes/workspace-greetings.ts +55 -0
  486. package/src/runtime/sync/resource-sync-events.ts +1 -11
  487. package/src/runtime/tool-grant-request-helper.ts +18 -16
  488. package/src/runtime/trust-context-resolver.ts +8 -5
  489. package/src/schedule/inference-profile.ts +2 -14
  490. package/src/schedule/schedule-store.ts +1 -1
  491. package/src/schedule/scheduler-types.ts +5 -1
  492. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  493. package/src/security/secret-patterns.ts +3 -0
  494. package/src/subagent/manager.ts +17 -4
  495. package/src/subagent/types.ts +6 -0
  496. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  497. package/src/telemetry/trace-collection-policy.ts +30 -0
  498. package/src/telemetry/types.ts +89 -0
  499. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  500. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  501. package/src/tools/AGENTS.md +3 -3
  502. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  503. package/src/tools/browser/browser-execution.ts +30 -19
  504. package/src/tools/document/document-tool.ts +2 -3
  505. package/src/tools/executor.ts +5 -3
  506. package/src/tools/host-terminal/host-shell.ts +5 -4
  507. package/src/tools/memory/register.ts +2 -2
  508. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  509. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  510. package/src/tools/network/web-fetch.ts +372 -1
  511. package/src/tools/network/web-search-error.ts +1 -1
  512. package/src/tools/network/web-search.ts +213 -10
  513. package/src/tools/permission-checker.ts +4 -3
  514. package/src/tools/registry.ts +20 -0
  515. package/src/tools/schedule/create.ts +7 -12
  516. package/src/tools/schedule/update.ts +4 -11
  517. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  518. package/src/tools/side-effects.ts +2 -17
  519. package/src/tools/skills/execute.ts +33 -0
  520. package/src/tools/subagent/spawn.ts +61 -12
  521. package/src/tools/terminal/shell.ts +10 -4
  522. package/src/tools/tool-approval-handler.ts +18 -13
  523. package/src/tools/tool-manifest.ts +0 -2
  524. package/src/tools/types.ts +9 -0
  525. package/src/tools/ui-surface/definitions.ts +64 -3
  526. package/src/tools/verification-control-plane-policy.ts +3 -1
  527. package/src/tools/workflows/run-workflow.test.ts +8 -18
  528. package/src/tools/workflows/run-workflow.ts +1 -0
  529. package/src/util/disk-usage.ts +78 -23
  530. package/src/util/platform.ts +10 -3
  531. package/src/watcher/telemetry.ts +2 -2
  532. package/src/workflows/capabilities.ts +2 -3
  533. package/src/workflows/engine.test.ts +175 -1
  534. package/src/workflows/engine.ts +82 -0
  535. package/src/workflows/journal-store.test.ts +70 -0
  536. package/src/workflows/journal-store.ts +18 -3
  537. package/src/workflows/run-manager.test.ts +171 -28
  538. package/src/workflows/run-manager.ts +66 -24
  539. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  540. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  541. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  542. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  543. package/src/workspace/migrations/registry.ts +8 -0
  544. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  545. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  546. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  547. package/src/__tests__/credential-vault.test.ts +0 -1706
  548. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  549. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  550. package/src/cli/commands/__tests__/task.test.ts +0 -914
  551. package/src/cli/commands/task.ts +0 -771
  552. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  553. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  554. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  555. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  556. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  557. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  558. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  559. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  560. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  561. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  562. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  563. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  564. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  565. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  566. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  567. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  568. package/src/memory/preloaded-apps.ts +0 -116
  569. package/src/notifications/tool-approval-copy.ts +0 -142
  570. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  571. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  572. package/src/tools/credentials/vault.ts +0 -712
@@ -5,98 +5,120 @@
5
5
  import { eq } from "drizzle-orm";
6
6
  import { z } from "zod";
7
7
 
8
+ import { FeedItemSchema } from "../../api/responses/home.js";
8
9
  import { getDb } from "../../memory/db-connection.js";
9
10
  import { notificationDeliveries } from "../../memory/schema.js";
10
11
  import { bufferIfDeferred } from "../../notifications/deferred-emit.js";
11
12
  import { editNotification } from "../../notifications/edit-notification.js";
12
13
  import { emitNotificationSignal } from "../../notifications/emit-signal.js";
13
14
  import { listEvents } from "../../notifications/events-store.js";
14
- import type { AttentionHints } from "../../notifications/signal.js";
15
+ import {
16
+ AttentionHintsSchema,
17
+ NotificationSourceChannelSchema,
18
+ RoutingIntentSchema,
19
+ UrgencySchema,
20
+ } from "../../notifications/signal.js";
15
21
  import { ACTOR_PRINCIPALS, LOCAL_PRINCIPALS } from "../auth/route-policy.js";
16
22
  import { BadRequestError, NotFoundError } from "./errors.js";
17
23
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
18
24
 
19
- function handleNotificationIntentResult({ body = {} }: RouteHandlerArgs) {
20
- const { deliveryId, success, errorMessage, errorCode } = body as {
21
- deliveryId?: string;
22
- success?: boolean;
23
- errorMessage?: string;
24
- errorCode?: string;
25
- };
25
+ // ── Notification intent result (client delivery ack) ──────────────────
26
+
27
+ const NotificationIntentResultParams = z.object({
28
+ deliveryId: z.string().min(1).describe("Notification delivery ID"),
29
+ success: z.boolean().describe("Whether delivery succeeded").optional(),
30
+ errorMessage: z
31
+ .string()
32
+ .describe("Error message if delivery failed")
33
+ .optional(),
34
+ errorCode: z.string().describe("Error code if delivery failed").optional(),
35
+ });
26
36
 
27
- if (!deliveryId || typeof deliveryId !== "string") {
37
+ function handleNotificationIntentResult({ body = {} }: RouteHandlerArgs) {
38
+ const parsed = NotificationIntentResultParams.safeParse(body);
39
+ if (!parsed.success) {
28
40
  throw new BadRequestError("deliveryId is required");
29
41
  }
42
+ const validated = parsed.data;
30
43
 
31
44
  const db = getDb();
32
45
  const now = Date.now();
33
46
 
34
- const updates: Record<string, unknown> = {
35
- clientDeliveryStatus: success ? "delivered" : "client_failed",
36
- clientDeliveryAt: now,
37
- updatedAt: now,
38
- };
39
- if (errorMessage) {
40
- updates.clientDeliveryError = errorMessage;
41
- }
42
- if (errorCode) {
43
- updates.errorCode = errorCode;
44
- }
45
-
46
47
  db.update(notificationDeliveries)
47
- .set(updates)
48
- .where(eq(notificationDeliveries.id, deliveryId))
48
+ .set({
49
+ clientDeliveryStatus: validated.success ? "delivered" : "client_failed",
50
+ clientDeliveryAt: now,
51
+ updatedAt: now,
52
+ ...(validated.errorMessage
53
+ ? { clientDeliveryError: validated.errorMessage }
54
+ : {}),
55
+ ...(validated.errorCode ? { errorCode: validated.errorCode } : {}),
56
+ })
57
+ .where(eq(notificationDeliveries.id, validated.deliveryId))
49
58
  .run();
50
59
 
51
60
  return { ok: true };
52
61
  }
53
62
 
54
- // ── Notification pipeline schemas ─────────────────────────────────────
55
-
56
- const AttentionHintsSchema = z.object({
57
- requiresAction: z.boolean(),
58
- urgency: z.enum(["low", "medium", "high", "critical"]),
59
- deadlineAt: z.number().optional(),
60
- isAsyncBackground: z.boolean(),
61
- visibleInSourceNow: z.boolean(),
62
- });
63
+ // ── Emit signal ───────────────────────────────────────────────────────
63
64
 
64
65
  const EmitSignalParams = z.object({
65
66
  sourceEventName: z.string().min(1),
66
- sourceChannel: z.enum([
67
- "assistant_tool",
68
- "vellum",
69
- "phone",
70
- "telegram",
71
- "slack",
72
- "scheduler",
73
- "watcher",
74
- ]),
67
+ sourceChannel: NotificationSourceChannelSchema,
75
68
  sourceContextId: z.string().min(1),
76
69
  attentionHints: AttentionHintsSchema,
77
70
  contextPayload: z.record(z.string(), z.unknown()).optional(),
78
- routingIntent: z
79
- .enum(["single_channel", "multi_channel", "all_channels"])
80
- .optional(),
71
+ routingIntent: RoutingIntentSchema.optional(),
81
72
  conversationAffinityHint: z.record(z.string(), z.string()).optional(),
82
73
  dedupeKey: z.string().optional(),
83
74
  throwOnError: z.boolean().optional(),
84
- // Conversation that originated this signal — used by `deferred-emit` to
85
- // buffer notifications during in-band background-job tool calls.
86
75
  originatingConversationId: z.string().optional(),
87
76
  });
88
77
 
89
- const ListNotificationEventsParams = z.object({
90
- limit: z.number().int().positive().optional(),
91
- sourceEventName: z.string().optional(),
78
+ const EmitSignalResponse = z.object({
79
+ signalId: z.string(),
80
+ dispatched: z.boolean(),
81
+ deduplicated: z.boolean(),
82
+ reason: z.string(),
92
83
  });
93
84
 
85
+ async function handleEmitSignal({ body = {} }: RouteHandlerArgs) {
86
+ const parsed = EmitSignalParams.safeParse(body);
87
+ if (!parsed.success) {
88
+ throw new BadRequestError(
89
+ parsed.error.issues[0]?.message ?? "Invalid signal parameters",
90
+ );
91
+ }
92
+ const validated = parsed.data;
93
+ const buffered = bufferIfDeferred(
94
+ validated.originatingConversationId,
95
+ validated,
96
+ );
97
+ if (buffered) {
98
+ return {
99
+ signalId: buffered.signalId,
100
+ dispatched: buffered.dispatched,
101
+ deduplicated: buffered.deduplicated,
102
+ reason: buffered.reason,
103
+ };
104
+ }
105
+ const result = await emitNotificationSignal(validated);
106
+ return {
107
+ signalId: result.signalId,
108
+ dispatched: result.dispatched,
109
+ deduplicated: result.deduplicated,
110
+ reason: result.reason,
111
+ };
112
+ }
113
+
114
+ // ── Edit notification ─────────────────────────────────────────────────
115
+
94
116
  const EditNotificationParams = z
95
117
  .object({
96
118
  id: z.string().min(1).describe("Feed item id (notif:<uuid>) or bare uuid"),
97
119
  title: z.string().optional(),
98
120
  body: z.string().optional(),
99
- urgency: z.enum(["low", "medium", "high", "critical"]).optional(),
121
+ urgency: UrgencySchema.optional(),
100
122
  status: z.enum(["new", "seen", "acted_on", "dismissed"]).optional(),
101
123
  })
102
124
  .refine(
@@ -111,44 +133,27 @@ const EditNotificationParams = z
111
133
  },
112
134
  );
113
135
 
114
- // ── Notification pipeline handlers ───────────────────────────────────
136
+ const ChannelEditOutcomeSchema = z.object({
137
+ channel: z.string(),
138
+ deliveryId: z.string(),
139
+ outcome: z.enum(["updated", "unsupported", "skipped", "failed"]),
140
+ reason: z.string().optional(),
141
+ });
115
142
 
116
- async function handleEmitSignal({ body = {} }: RouteHandlerArgs) {
117
- const validated = EmitSignalParams.parse(body);
118
- const params = {
119
- sourceEventName: validated.sourceEventName,
120
- sourceChannel: validated.sourceChannel,
121
- sourceContextId: validated.sourceContextId,
122
- attentionHints: validated.attentionHints as AttentionHints,
123
- contextPayload: validated.contextPayload as Record<string, unknown>,
124
- routingIntent: validated.routingIntent,
125
- conversationAffinityHint: validated.conversationAffinityHint,
126
- dedupeKey: validated.dedupeKey,
127
- throwOnError: validated.throwOnError,
128
- };
129
- const buffered = bufferIfDeferred(
130
- validated.originatingConversationId,
131
- params,
132
- );
133
- if (buffered) {
134
- return {
135
- signalId: buffered.signalId,
136
- dispatched: buffered.dispatched,
137
- deduplicated: buffered.deduplicated,
138
- reason: buffered.reason,
139
- };
140
- }
141
- const result = await emitNotificationSignal(params);
142
- return {
143
- signalId: result.signalId,
144
- dispatched: result.dispatched,
145
- deduplicated: result.deduplicated,
146
- reason: result.reason,
147
- };
148
- }
143
+ const EditNotificationResponse = z.object({
144
+ ok: z.boolean(),
145
+ feedItem: FeedItemSchema,
146
+ channels: z.array(ChannelEditOutcomeSchema),
147
+ });
149
148
 
150
149
  async function handleEditNotification({ body = {} }: RouteHandlerArgs) {
151
- const validated = EditNotificationParams.parse(body);
150
+ const parsed = EditNotificationParams.safeParse(body);
151
+ if (!parsed.success) {
152
+ throw new BadRequestError(
153
+ parsed.error.issues[0]?.message ?? "Invalid notification parameters",
154
+ );
155
+ }
156
+ const validated = parsed.data;
152
157
  const result = await editNotification(validated);
153
158
  if (!result) {
154
159
  throw new NotFoundError(`No notification found for id ${validated.id}`);
@@ -160,8 +165,31 @@ async function handleEditNotification({ body = {} }: RouteHandlerArgs) {
160
165
  };
161
166
  }
162
167
 
168
+ // ── List events ───────────────────────────────────────────────────────
169
+
170
+ const ListNotificationEventsParams = z.object({
171
+ limit: z.number().int().positive().optional(),
172
+ sourceEventName: z.string().optional(),
173
+ });
174
+
175
+ const NotificationEventSchema = z.object({
176
+ id: z.string(),
177
+ sourceEventName: z.string(),
178
+ sourceChannel: z.string(),
179
+ sourceContextId: z.string(),
180
+ urgency: z.string(),
181
+ dedupeKey: z.string().nullable(),
182
+ createdAt: z.string(),
183
+ });
184
+
163
185
  function handleListEvents({ body = {} }: RouteHandlerArgs) {
164
- const validated = ListNotificationEventsParams.parse(body);
186
+ const parsed = ListNotificationEventsParams.safeParse(body);
187
+ if (!parsed.success) {
188
+ throw new BadRequestError(
189
+ parsed.error.issues[0]?.message ?? "Invalid query parameters",
190
+ );
191
+ }
192
+ const validated = parsed.data;
165
193
  const rows = listEvents({
166
194
  limit: validated.limit,
167
195
  sourceEventName: validated.sourceEventName,
@@ -169,12 +197,10 @@ function handleListEvents({ body = {} }: RouteHandlerArgs) {
169
197
  return rows.map((row) => {
170
198
  let urgency = "unknown";
171
199
  try {
172
- const hints = JSON.parse(row.attentionHintsJson) as {
173
- urgency?: string;
174
- };
175
- if (hints.urgency) {
176
- urgency = hints.urgency;
177
- }
200
+ const hints = AttentionHintsSchema.parse(
201
+ JSON.parse(row.attentionHintsJson),
202
+ );
203
+ urgency = hints.urgency;
178
204
  } catch {
179
205
  // Leave urgency as "unknown" if parsing fails.
180
206
  }
@@ -207,12 +233,7 @@ export const ROUTES: RouteDefinition[] = [
207
233
  "Emit a notification signal into the pipeline for routing and delivery.",
208
234
  tags: ["notifications"],
209
235
  requestBody: EmitSignalParams,
210
- responseBody: z.object({
211
- signalId: z.string(),
212
- dispatched: z.boolean(),
213
- deduplicated: z.boolean(),
214
- reason: z.string(),
215
- }),
236
+ responseBody: EmitSignalResponse,
216
237
  },
217
238
  {
218
239
  operationId: "edit_notification",
@@ -228,18 +249,7 @@ export const ROUTES: RouteDefinition[] = [
228
249
  "Patch the home-feed entry for a notification and, where supported (Slack today), update the delivered message in place.",
229
250
  tags: ["notifications"],
230
251
  requestBody: EditNotificationParams,
231
- responseBody: z.object({
232
- ok: z.boolean(),
233
- feedItem: z.record(z.string(), z.unknown()),
234
- channels: z.array(
235
- z.object({
236
- channel: z.string(),
237
- deliveryId: z.string(),
238
- outcome: z.enum(["updated", "unsupported", "skipped", "failed"]),
239
- reason: z.string().optional(),
240
- }),
241
- ),
242
- }),
252
+ responseBody: EditNotificationResponse,
243
253
  additionalResponses: {
244
254
  "404": {
245
255
  description: "No notification found for the supplied id",
@@ -260,17 +270,7 @@ export const ROUTES: RouteDefinition[] = [
260
270
  "List recent notification events, optionally filtered by source event name.",
261
271
  tags: ["notifications"],
262
272
  requestBody: ListNotificationEventsParams,
263
- responseBody: z.array(
264
- z.object({
265
- id: z.string(),
266
- sourceEventName: z.string(),
267
- sourceChannel: z.string(),
268
- sourceContextId: z.string(),
269
- urgency: z.string(),
270
- dedupeKey: z.string().nullable(),
271
- createdAt: z.string(),
272
- }),
273
- ),
273
+ responseBody: z.array(NotificationEventSchema),
274
274
  },
275
275
  {
276
276
  operationId: "notificationintentresult_post",
@@ -285,18 +285,7 @@ export const ROUTES: RouteDefinition[] = [
285
285
  "Client acknowledgment for local notification delivery outcome.",
286
286
  tags: ["notifications"],
287
287
  handler: handleNotificationIntentResult,
288
- requestBody: z.object({
289
- deliveryId: z.string().describe("Notification delivery ID"),
290
- success: z.boolean().describe("Whether delivery succeeded").optional(),
291
- errorMessage: z
292
- .string()
293
- .describe("Error message if delivery failed")
294
- .optional(),
295
- errorCode: z
296
- .string()
297
- .describe("Error code if delivery failed")
298
- .optional(),
299
- }),
288
+ requestBody: NotificationIntentResultParams,
300
289
  responseBody: z.object({
301
290
  ok: z.boolean(),
302
291
  }),
@@ -29,7 +29,7 @@ import {
29
29
  } from "../../security/secure-keys.js";
30
30
  import { buildAssistantEvent } from "../assistant-event.js";
31
31
  import { assistantEventHub } from "../assistant-event-hub.js";
32
- import { LOCAL_PRINCIPALS } from "../auth/route-policy.js";
32
+ import { ACTOR_PRINCIPALS, LOCAL_PRINCIPALS } from "../auth/route-policy.js";
33
33
  import {
34
34
  BadRequestError,
35
35
  InternalError,
@@ -360,7 +360,7 @@ export const ROUTES: RouteDefinition[] = [
360
360
  method: "GET",
361
361
  policy: {
362
362
  requiredScopes: ["settings.read"],
363
- allowedPrincipalTypes: LOCAL_PRINCIPALS,
363
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
364
364
  },
365
365
  summary: "Get platform deployment context and connection status",
366
366
  description:
@@ -25,6 +25,10 @@
25
25
 
26
26
  import { z } from "zod";
27
27
 
28
+ import {
29
+ diffPlugin,
30
+ PluginDiffUnavailableError,
31
+ } from "../../cli/lib/diff-plugin.js";
28
32
  import {
29
33
  inspectPlugin,
30
34
  PluginInspectNotFoundError,
@@ -58,7 +62,9 @@ import {
58
62
  uninstallPlugin,
59
63
  } from "../../cli/lib/uninstall-plugin.js";
60
64
  import {
65
+ PluginMergeBaselineError,
61
66
  PluginNotUpgradableError,
67
+ type PluginUpgradeStrategy,
62
68
  upgradePlugin,
63
69
  } from "../../cli/lib/upgrade-plugin.js";
64
70
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
@@ -366,6 +372,26 @@ const pluginRemoteInfoSchema = z
366
372
  })
367
373
  .describe("The marketplace's current pin and advertised metadata.");
368
374
 
375
+ const pluginSurfacesSchema = z
376
+ .object({
377
+ skills: z
378
+ .array(z.string())
379
+ .describe("Skill ids shipped at `skills/<id>/SKILL.md`."),
380
+ hooks: z
381
+ .array(z.string())
382
+ .describe(
383
+ "Lifecycle hook names from `hooks/<name>.{ts,js}` (e.g. `pre-model-call`).",
384
+ ),
385
+ tools: z
386
+ .array(z.string())
387
+ .describe(
388
+ "Registered tool names from `tools/<name>.{ts,js}` (filenames derived to tool names, e.g. `create-issue` \u2192 `create_issue`).",
389
+ ),
390
+ })
391
+ .describe(
392
+ "Surfaces the installed copy contributes, read from its on-disk tree.",
393
+ );
394
+
369
395
  const pluginInspectResponseSchema = z.object({
370
396
  name: z
371
397
  .string()
@@ -401,6 +427,11 @@ const pluginInspectResponseSchema = z.object({
401
427
  .describe(
402
428
  "Marketplace fetch error message, when the catalog could not be read.",
403
429
  ),
430
+ surfaces: pluginSurfacesSchema
431
+ .nullable()
432
+ .describe(
433
+ "Surfaces the installed copy contributes (skills, hooks, tools); null when the plugin is not installed.",
434
+ ),
404
435
  });
405
436
 
406
437
  const pluginUpgradeRequestSchema = z.object({
@@ -410,6 +441,12 @@ const pluginUpgradeRequestSchema = z.object({
410
441
  .describe(
411
442
  "Report what would change without modifying the install. Defaults to false.",
412
443
  ),
444
+ strategy: z
445
+ .enum(["ours", "theirs", "overwrite", "assistant"])
446
+ .optional()
447
+ .describe(
448
+ "How to reconcile local edits with the pin. `overwrite` (default) discards local edits and re-installs the pin; `ours`/`theirs` three-way merge, resolving conflicting hunks toward the local edit or the pin respectively; `assistant` is not yet supported.",
449
+ ),
413
450
  });
414
451
 
415
452
  const pluginUpgradeResponseSchema = z.object({
@@ -452,6 +489,19 @@ const pluginUpgradeResponseSchema = z.object({
452
489
  "Files materialized by the upgrade; null for a no-op or dry run.",
453
490
  ),
454
491
  dryRun: z.boolean().describe("Whether this was a dry run (no changes made)."),
492
+ strategy: z
493
+ .enum(["ours", "theirs", "overwrite", "assistant"])
494
+ .describe("Conflict-resolution strategy the upgrade applied."),
495
+ conflicts: z
496
+ .array(z.string())
497
+ .describe(
498
+ "Paths left for the assistant to resolve under the `assistant` strategy: text files carry git conflict markers, modify/delete divergences keep the surviving content. Empty for other strategies.",
499
+ ),
500
+ binaryConflicts: z
501
+ .array(z.string())
502
+ .describe(
503
+ "Binary files that conflicted under the `assistant` strategy; the local copy was kept since markers cannot be written into binary content. Empty for other strategies.",
504
+ ),
455
505
  provenanceWasUnknown: z
456
506
  .boolean()
457
507
  .describe(
@@ -459,6 +509,62 @@ const pluginUpgradeResponseSchema = z.object({
459
509
  ),
460
510
  });
461
511
 
512
+ const pluginFileDiffSchema = z
513
+ .object({
514
+ path: z.string().describe("POSIX-relative path within the plugin root."),
515
+ status: z
516
+ .enum(["modified", "added", "removed"])
517
+ .describe(
518
+ "Whether the file was edited, newly added, or deleted since install.",
519
+ ),
520
+ diff: z
521
+ .string()
522
+ .describe(
523
+ "Unified diff (`--- a/… / +++ b/…`) of the file. A short `Binary files differ` marker for binary files, or a `Baseline unavailable` marker when `reconstructed` is false.",
524
+ ),
525
+ binary: z
526
+ .boolean()
527
+ .describe(
528
+ "True when either side was detected as binary (NUL byte present).",
529
+ ),
530
+ reconstructed: z
531
+ .boolean()
532
+ .describe(
533
+ "True when the install-time baseline for this file was faithfully recovered (re-materialized bytes hash-match the install fingerprint). False when it could not be reconstructed (e.g. the curated adapter overlay changed since install), in which case `diff` is a marker, not a patch. Always true for added files.",
534
+ ),
535
+ })
536
+ .describe("Unified diff of a single drifted file.");
537
+
538
+ const pluginDiffResponseSchema = z.object({
539
+ name: z
540
+ .string()
541
+ .describe("Install name. Matches `assistant plugins install <name>`."),
542
+ target: z
543
+ .string()
544
+ .describe("Absolute path to the installed plugin directory on the host."),
545
+ commit: z
546
+ .string()
547
+ .describe(
548
+ "Commit the baseline was re-materialized from (the recorded install SHA).",
549
+ ),
550
+ committedAt: z
551
+ .string()
552
+ .nullable()
553
+ .describe(
554
+ "ISO-8601 committer timestamp (UTC) of `commit`; null when not recorded.",
555
+ ),
556
+ clean: z
557
+ .boolean()
558
+ .describe(
559
+ "True when the on-disk tree exactly matches the re-materialized baseline.",
560
+ ),
561
+ files: z
562
+ .array(pluginFileDiffSchema)
563
+ .describe(
564
+ "One entry per drifted file, sorted by path. Empty when `clean`.",
565
+ ),
566
+ });
567
+
462
568
  // ---------------------------------------------------------------------------
463
569
  // Helpers
464
570
  // ---------------------------------------------------------------------------
@@ -740,6 +846,45 @@ async function handleInspectPlugin({ pathParams = {} }: RouteHandlerArgs) {
740
846
  }
741
847
  }
742
848
 
849
+ // ---------------------------------------------------------------------------
850
+ // Handler — diff
851
+ // ---------------------------------------------------------------------------
852
+
853
+ async function handleDiffPlugin({ pathParams = {} }: RouteHandlerArgs) {
854
+ const rawName = pathParams.name ?? "";
855
+
856
+ try {
857
+ return await diffPlugin(
858
+ { name: rawName },
859
+ { fetch: globalThis.fetch.bind(globalThis) },
860
+ );
861
+ } catch (err) {
862
+ if (err instanceof InvalidPluginNameError) {
863
+ throw new BadRequestError(err.message);
864
+ }
865
+ if (
866
+ err instanceof PluginNotInstalledError ||
867
+ err instanceof PluginNotFoundError
868
+ ) {
869
+ throw new NotFoundError(err.message);
870
+ }
871
+ // The install exists but recorded no commit to re-materialize a baseline
872
+ // from — a permanent state the caller cannot resolve by retrying. 409
873
+ // marks the request as well-formed but not actionable in the current state.
874
+ if (err instanceof PluginDiffUnavailableError) {
875
+ throw new ConflictError(err.message);
876
+ }
877
+ // A rate-limited or temporarily-down source (the plugin repo) is a
878
+ // retryable outage, not a conflict — 503.
879
+ if (err instanceof PluginSourceUnavailableError) {
880
+ throw new ServiceUnavailableError(err.message);
881
+ }
882
+ throw new InternalError(
883
+ err instanceof Error ? err.message : "plugin diff failed",
884
+ );
885
+ }
886
+ }
887
+
743
888
  // ---------------------------------------------------------------------------
744
889
  // Handler — upgrade
745
890
  // ---------------------------------------------------------------------------
@@ -750,6 +895,10 @@ async function handleUpgradePlugin({
750
895
  }: RouteHandlerArgs) {
751
896
  const rawName = pathParams.name ?? "";
752
897
  const dryRun = typeof body.dryRun === "boolean" ? body.dryRun : undefined;
898
+ const strategy =
899
+ typeof body.strategy === "string"
900
+ ? (body.strategy as PluginUpgradeStrategy)
901
+ : undefined;
753
902
 
754
903
  // Like install, the upgrade target ref is the curated marketplace pin
755
904
  // (resolved inside `upgradePlugin` via `inspectPlugin`), never a
@@ -757,7 +906,7 @@ async function handleUpgradePlugin({
757
906
  // upgrade at an unreviewed revision.
758
907
  try {
759
908
  const result = await upgradePlugin(
760
- { name: rawName, dryRun },
909
+ { name: rawName, dryRun, strategy },
761
910
  { fetch: globalThis.fetch.bind(globalThis) },
762
911
  );
763
912
  return {
@@ -770,6 +919,9 @@ async function handleUpgradePlugin({
770
919
  target: result.target,
771
920
  fileCount: result.fileCount,
772
921
  dryRun: result.dryRun,
922
+ strategy: result.strategy,
923
+ conflicts: result.conflicts,
924
+ binaryConflicts: result.binaryConflicts,
773
925
  provenanceWasUnknown: result.provenanceWasUnknown,
774
926
  };
775
927
  } catch (err) {
@@ -782,6 +934,12 @@ async function handleUpgradePlugin({
782
934
  if (err instanceof PluginNotFoundError) {
783
935
  throw new NotFoundError(err.message);
784
936
  }
937
+ // A merge strategy was requested but the install-time baseline can't be
938
+ // reconstructed — a well-formed request that isn't actionable in the
939
+ // current state (the caller can retry with `overwrite` or reinstall).
940
+ if (err instanceof PluginMergeBaselineError) {
941
+ throw new ConflictError(err.message);
942
+ }
785
943
  // The install exists but has no marketplace entry to advance to — a
786
944
  // permanent state the caller cannot resolve by retrying. 409 marks the
787
945
  // request as well-formed but not actionable in the current state.
@@ -992,6 +1150,47 @@ export const ROUTES: RouteDefinition[] = [
992
1150
  },
993
1151
  handler: handleInspectPlugin,
994
1152
  },
1153
+ {
1154
+ operationId: "plugins_diff",
1155
+ endpoint: "plugins/:name/diff",
1156
+ method: "POST",
1157
+ policy: {
1158
+ requiredScopes: ["settings.read"],
1159
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
1160
+ },
1161
+ summary: "Diff a plugin against its install commit",
1162
+ description:
1163
+ "Show a unified diff of local edits to an installed plugin against the exact commit it was installed at (recorded in its `install-meta.json`). Computing the diff re-materializes the baseline through the install pipeline — a network clone plus a temp-dir write — so this is a POST: it is not a safe, cacheable GET even though it leaves persistent state untouched (requires only `settings.read`). Drift is classified against the install-time fingerprint so an adapter overlay that moved since install never reads as a local change. Returns the baseline `commit`, a `clean` flag, and a `files` array of `{ path, status, diff, binary, reconstructed }` for each modified/added/removed file. The baseline is the install commit, not the marketplace pin — comparing against the current pin is `POST /v1/plugins/:name/upgrade` with `dryRun`. A name with no installed copy returns 404; an install that recorded no commit or fingerprint returns 409; an unreachable source returns 503. Mirrors the CLI's `assistant plugins diff <name>`.",
1164
+ tags: ["plugins"],
1165
+ pathParams: [
1166
+ {
1167
+ name: "name",
1168
+ type: "string",
1169
+ description:
1170
+ "Install name. Must match the kebab-case name accepted by `assistant plugins install`.",
1171
+ },
1172
+ ],
1173
+ responseBody: pluginDiffResponseSchema,
1174
+ additionalResponses: {
1175
+ "400": {
1176
+ description:
1177
+ "The plugin name failed sanitization (e.g. contained slashes, dots, or uppercase letters).",
1178
+ },
1179
+ "404": {
1180
+ description:
1181
+ "No copy of the plugin is installed, or its recorded commit resolves to nothing.",
1182
+ },
1183
+ "409": {
1184
+ description:
1185
+ "The install recorded no commit or no fingerprint to re-materialize and verify a baseline from.",
1186
+ },
1187
+ "503": {
1188
+ description:
1189
+ "The plugin source (GitHub) was temporarily unavailable; the diff is retryable.",
1190
+ },
1191
+ },
1192
+ handler: handleDiffPlugin,
1193
+ },
995
1194
  {
996
1195
  operationId: "plugins_upgrade",
997
1196
  endpoint: "plugins/:name/upgrade",
@@ -1002,7 +1201,7 @@ export const ROUTES: RouteDefinition[] = [
1002
1201
  },
1003
1202
  summary: "Upgrade a plugin to the marketplace pin",
1004
1203
  description:
1005
- 'Move an installed plugin to the marketplace\'s current pinned commit, re-materializing it under `<workspaceDir>/plugins/<name>/`. Always resolves against the curated marketplace pin (no caller-supplied ref), mirroring `plugins install`\'s curation boundary. A no-op (`outcome: "already-up-to-date"`) when the installed commit already equals the pin; pass `dryRun` to preview the move (`outcome: "would-upgrade"`) without touching the install. Installs lacking provenance are re-pinned to the current SHA. The assistant must be restarted to load the upgraded code. This does not gate on local edits callers should consult `GET /v1/plugins/:name/inspect` (`local.localChanges`) first and confirm before overwriting. Mirrors the CLI\'s `assistant plugins upgrade <name>`.',
1204
+ 'Move an installed plugin to the marketplace\'s current pinned commit, re-materializing it under `<workspaceDir>/plugins/<name>/`. Always resolves against the curated marketplace pin (no caller-supplied ref), mirroring `plugins install`\'s curation boundary. A no-op (`outcome: "already-up-to-date"`) when the installed commit already equals the pin; pass `dryRun` to preview the move (`outcome: "would-upgrade"`) without touching the install. Installs lacking provenance are re-pinned to the current SHA. The assistant must be restarted to load the upgraded code. `strategy` controls how local edits are reconciled: `overwrite` (default) discards them and re-installs the pin wholesale; `ours`/`theirs`/`assistant` perform a three-way merge against the re-materialized install commit, carrying non-conflicting edits from both sides forward and resolving conflicting hunks toward the local edit (`ours`) or the pin (`theirs`), or writing git conflict markers into the file and reporting them in `conflicts`/`binaryConflicts` for the assistant to resolve (`assistant`). A merge strategy whose install-time baseline cannot be reconstructed returns 409. Mirrors the CLI\'s `assistant plugins upgrade <name> [--strategy <s>]`.',
1006
1205
  tags: ["plugins"],
1007
1206
  pathParams: [
1008
1207
  {
@@ -1025,7 +1224,7 @@ export const ROUTES: RouteDefinition[] = [
1025
1224
  },
1026
1225
  "409": {
1027
1226
  description:
1028
- "The install exists but has no marketplace entry to advance to.",
1227
+ "The install exists but has no marketplace entry to advance to, or a merge strategy was requested whose install-time baseline cannot be reconstructed.",
1029
1228
  },
1030
1229
  "503": {
1031
1230
  description: