@vellumai/assistant 0.4.56 → 0.4.57

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 (450) hide show
  1. package/ARCHITECTURE.md +10 -10
  2. package/Dockerfile +3 -0
  3. package/README.md +11 -11
  4. package/docs/architecture/integrations.md +2 -2
  5. package/docs/architecture/memory.md +3 -4
  6. package/docs/credential-execution-service.md +13 -20
  7. package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
  8. package/package.json +1 -1
  9. package/src/__tests__/actor-token-service.test.ts +7 -7
  10. package/src/__tests__/anthropic-provider.test.ts +172 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
  12. package/src/__tests__/approval-cascade.test.ts +2 -2
  13. package/src/__tests__/approval-routes-http.test.ts +3 -4
  14. package/src/__tests__/asset-materialize-tool.test.ts +5 -5
  15. package/src/__tests__/asset-search-tool.test.ts +1 -1
  16. package/src/__tests__/assistant-attachments.test.ts +5 -5
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
  19. package/src/__tests__/attachments-store.test.ts +2 -2
  20. package/src/__tests__/avatar-e2e.test.ts +5 -3
  21. package/src/__tests__/browser-skill-endstate.test.ts +0 -1
  22. package/src/__tests__/call-routes-http.test.ts +2 -2
  23. package/src/__tests__/callback-handoff-copy.test.ts +1 -1
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
  25. package/src/__tests__/channel-readiness-routes.test.ts +0 -1
  26. package/src/__tests__/channel-readiness-service.test.ts +0 -1
  27. package/src/__tests__/checker.test.ts +31 -32
  28. package/src/__tests__/chrome-cdp.test.ts +47 -18
  29. package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
  30. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  31. package/src/__tests__/config-schema.test.ts +9 -18
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
  34. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
  35. package/src/__tests__/conversation-agent-loop.test.ts +11 -4
  36. package/src/__tests__/conversation-attachments.test.ts +1 -1
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
  38. package/src/__tests__/conversation-error.test.ts +33 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
  40. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  41. package/src/__tests__/conversation-pairing.test.ts +1 -1
  42. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
  44. package/src/__tests__/conversation-queue.test.ts +23 -14
  45. package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +185 -173
  47. package/src/__tests__/conversation-seed-composer.test.ts +1 -1
  48. package/src/__tests__/conversation-slash-queue.test.ts +4 -4
  49. package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
  50. package/src/__tests__/conversation-starter-routes.test.ts +291 -0
  51. package/src/__tests__/conversation-wipe.test.ts +438 -0
  52. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
  53. package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
  54. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
  55. package/src/__tests__/credential-security-e2e.test.ts +20 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault-unit.test.ts +227 -0
  58. package/src/__tests__/credentials-cli.test.ts +3 -0
  59. package/src/__tests__/date-context.test.ts +59 -377
  60. package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
  61. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
  62. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
  63. package/src/__tests__/encrypted-store.test.ts +237 -15
  64. package/src/__tests__/ephemeral-permissions.test.ts +4 -5
  65. package/src/__tests__/event-bus.test.ts +3 -3
  66. package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
  67. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  68. package/src/__tests__/gemini-image-service.test.ts +4 -4
  69. package/src/__tests__/gemini-provider.test.ts +6 -9
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
  71. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  72. package/src/__tests__/host-shell-tool.test.ts +6 -6
  73. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  74. package/src/__tests__/intent-routing.test.ts +51 -99
  75. package/src/__tests__/invite-routes-http.test.ts +5 -0
  76. package/src/__tests__/list-messages-attachments.test.ts +1 -1
  77. package/src/__tests__/managed-proxy-context.test.ts +2 -5
  78. package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
  79. package/src/__tests__/media-generate-image.test.ts +32 -15
  80. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  81. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
  82. package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
  83. package/src/__tests__/memory-recall-quality.test.ts +4 -3
  84. package/src/__tests__/memory-regressions.test.ts +86 -90
  85. package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
  86. package/src/__tests__/migration-export-http.test.ts +26 -27
  87. package/src/__tests__/migration-import-commit-http.test.ts +165 -37
  88. package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
  89. package/src/__tests__/migration-validate-http.test.ts +16 -16
  90. package/src/__tests__/model-intents.test.ts +1 -1
  91. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
  92. package/src/__tests__/notification-broadcaster.test.ts +1 -1
  93. package/src/__tests__/notification-decision-fallback.test.ts +2 -2
  94. package/src/__tests__/notification-decision-identity.test.ts +8 -9
  95. package/src/__tests__/notification-decision-strategy.test.ts +1 -1
  96. package/src/__tests__/notification-deep-link.test.ts +1 -1
  97. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  98. package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
  99. package/src/__tests__/oauth-store.test.ts +1 -3
  100. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
  101. package/src/__tests__/onboarding-template-contract.test.ts +23 -59
  102. package/src/__tests__/provider-error-scenarios.test.ts +154 -0
  103. package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
  104. package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
  105. package/src/__tests__/provider-registry-ollama.test.ts +5 -2
  106. package/src/__tests__/qdrant-manager.test.ts +7 -7
  107. package/src/__tests__/ratelimit.test.ts +0 -74
  108. package/src/__tests__/recording-handler.test.ts +0 -1
  109. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  110. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  111. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  112. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  113. package/src/__tests__/scheduler-recurrence.test.ts +46 -2
  114. package/src/__tests__/schema-transforms.test.ts +114 -54
  115. package/src/__tests__/secret-onetime-send.test.ts +20 -0
  116. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
  117. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  118. package/src/__tests__/send-endpoint-busy.test.ts +63 -4
  119. package/src/__tests__/send-notification-tool.test.ts +2 -2
  120. package/src/__tests__/shell-credential-ref.test.ts +0 -1
  121. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
  122. package/src/__tests__/skill-memory.test.ts +547 -0
  123. package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
  124. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
  125. package/src/__tests__/slack-channel-config.test.ts +109 -94
  126. package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
  127. package/src/__tests__/swarm-recursion.test.ts +2 -2
  128. package/src/__tests__/swarm-tool.test.ts +2 -2
  129. package/src/__tests__/system-prompt.test.ts +19 -66
  130. package/src/__tests__/telegram-config.test.ts +121 -0
  131. package/src/__tests__/terminal-tools.test.ts +1 -1
  132. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
  133. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  134. package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
  135. package/src/__tests__/tool-executor.test.ts +1 -1
  136. package/src/__tests__/trace-emitter.test.ts +8 -1
  137. package/src/__tests__/trust-store.test.ts +7 -8
  138. package/src/__tests__/twilio-routes.test.ts +1 -18
  139. package/src/__tests__/user-reference.test.ts +82 -2
  140. package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
  141. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  142. package/src/approvals/guardian-request-resolvers.ts +3 -3
  143. package/src/avatar/ascii-renderer.ts +2 -2
  144. package/src/avatar/png-renderer.ts +2 -2
  145. package/src/avatar/resvg-lazy.ts +21 -0
  146. package/src/calls/guardian-dispatch.ts +1 -1
  147. package/src/calls/relay-access-wait.ts +2 -2
  148. package/src/calls/twilio-rest.ts +0 -248
  149. package/src/cli/AGENTS.md +5 -8
  150. package/src/cli/__tests__/notifications.test.ts +5 -5
  151. package/src/cli/commands/avatar.ts +64 -2
  152. package/src/cli/commands/conversations.ts +131 -1
  153. package/src/cli/commands/credentials.ts +2 -0
  154. package/src/cli/commands/notifications.ts +3 -3
  155. package/src/cli.ts +10 -0
  156. package/src/config/bundled-skills/acp/SKILL.md +5 -5
  157. package/src/config/bundled-skills/acp/TOOLS.json +6 -6
  158. package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
  159. package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
  160. package/src/config/bundled-skills/browser/SKILL.md +15 -15
  161. package/src/config/bundled-skills/browser/TOOLS.json +14 -14
  162. package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
  163. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
  164. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  165. package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
  166. package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
  167. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
  168. package/src/config/bundled-skills/contacts/SKILL.md +3 -3
  169. package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
  170. package/src/config/bundled-skills/document/SKILL.md +4 -4
  171. package/src/config/bundled-skills/document/TOOLS.json +2 -2
  172. package/src/config/bundled-skills/followups/TOOLS.json +3 -3
  173. package/src/config/bundled-skills/gmail/SKILL.md +32 -32
  174. package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
  175. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
  176. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  177. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
  178. package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
  179. package/src/config/bundled-skills/google-calendar/types.ts +1 -1
  180. package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
  181. package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
  182. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
  183. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
  184. package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
  185. package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
  186. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
  187. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
  188. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
  189. package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
  190. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
  191. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
  192. package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
  193. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  194. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
  195. package/src/config/bundled-skills/messaging/SKILL.md +29 -25
  196. package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
  197. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  198. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  199. package/src/config/bundled-skills/notifications/SKILL.md +3 -3
  200. package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
  201. package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
  202. package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
  203. package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
  204. package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
  205. package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
  206. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
  207. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
  208. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
  209. package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
  210. package/src/config/bundled-skills/schedule/SKILL.md +26 -26
  211. package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
  212. package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
  213. package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
  214. package/src/config/bundled-skills/sequences/SKILL.md +2 -2
  215. package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
  216. package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
  217. package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
  218. package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
  219. package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
  220. package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
  221. package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
  222. package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
  223. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  224. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
  225. package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
  226. package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
  227. package/src/config/bundled-skills/slack/SKILL.md +2 -2
  228. package/src/config/bundled-skills/slack/TOOLS.json +8 -8
  229. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
  230. package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
  231. package/src/config/bundled-skills/tasks/SKILL.md +1 -1
  232. package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
  233. package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
  234. package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
  235. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
  236. package/src/config/bundled-skills/watcher/SKILL.md +4 -4
  237. package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
  238. package/src/config/feature-flag-registry.json +33 -17
  239. package/src/config/schemas/sandbox.ts +1 -1
  240. package/src/config/schemas/services.ts +13 -3
  241. package/src/config/schemas/timeouts.ts +0 -10
  242. package/src/contacts/contact-store.ts +63 -0
  243. package/src/contacts/contacts-write.ts +1 -1
  244. package/src/daemon/assistant-attachments.ts +2 -2
  245. package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
  246. package/src/daemon/conversation-agent-loop.ts +7 -30
  247. package/src/daemon/conversation-error.ts +24 -0
  248. package/src/daemon/conversation-memory.ts +8 -7
  249. package/src/daemon/conversation-runtime-assembly.ts +139 -274
  250. package/src/daemon/conversation-slash.ts +7 -26
  251. package/src/daemon/conversation-surfaces.ts +14 -0
  252. package/src/daemon/conversation-tool-setup.ts +9 -8
  253. package/src/daemon/conversation.ts +2 -0
  254. package/src/daemon/daemon-control.ts +1 -1
  255. package/src/daemon/date-context.ts +10 -83
  256. package/src/daemon/handlers/config-channels.ts +12 -2
  257. package/src/daemon/handlers/config-slack-channel.ts +7 -1
  258. package/src/daemon/handlers/config-telegram.ts +6 -1
  259. package/src/daemon/handlers/conversations.ts +2 -2
  260. package/src/daemon/handlers/skills.ts +4 -0
  261. package/src/daemon/lifecycle.ts +28 -4
  262. package/src/daemon/providers-setup.ts +1 -1
  263. package/src/daemon/server.ts +1 -5
  264. package/src/daemon/shutdown-handlers.ts +9 -3
  265. package/src/daemon/tool-side-effects.ts +40 -0
  266. package/src/daemon/trace-emitter.ts +25 -2
  267. package/src/events/domain-events.ts +1 -1
  268. package/src/events/tool-permission-telemetry-listener.ts +46 -0
  269. package/src/inbound/platform-callback-registration.ts +0 -18
  270. package/src/media/app-icon-generator.ts +15 -8
  271. package/src/media/avatar-router.ts +15 -8
  272. package/src/media/gemini-image-service.ts +125 -21
  273. package/src/memory/attachments-store.ts +3 -3
  274. package/src/memory/channel-verification-sessions.ts +6 -6
  275. package/src/memory/conversation-crud.ts +196 -1
  276. package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
  277. package/src/memory/conversation-title-service.ts +2 -3
  278. package/src/memory/db-init.ts +25 -1
  279. package/src/memory/invite-store.ts +4 -4
  280. package/src/memory/items-extractor.ts +4 -4
  281. package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
  282. package/src/memory/jobs-store.ts +3 -2
  283. package/src/memory/jobs-worker.ts +7 -5
  284. package/src/memory/lifecycle-events-store.ts +63 -0
  285. package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
  286. package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
  287. package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
  288. package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
  289. package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
  290. package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
  291. package/src/memory/migrations/index.ts +6 -0
  292. package/src/memory/migrations/registry.ts +13 -0
  293. package/src/memory/retriever.test.ts +223 -96
  294. package/src/memory/retriever.ts +115 -138
  295. package/src/memory/schema/calls.ts +1 -1
  296. package/src/memory/schema/contacts.ts +1 -1
  297. package/src/memory/schema/infrastructure.ts +29 -0
  298. package/src/memory/schema/memory-core.ts +7 -17
  299. package/src/memory/schema/notifications.ts +1 -1
  300. package/src/memory/search/formatting.ts +23 -6
  301. package/src/memory/search/lexical.ts +2 -0
  302. package/src/memory/search/semantic.ts +2 -0
  303. package/src/memory/search/staleness.ts +1 -0
  304. package/src/memory/search/types.ts +4 -0
  305. package/src/memory/task-memory-cleanup.ts +96 -6
  306. package/src/memory/trace-event-store.ts +148 -0
  307. package/src/notifications/README.md +1 -1
  308. package/src/notifications/decision-engine.ts +2 -2
  309. package/src/notifications/emit-signal.ts +4 -4
  310. package/src/notifications/events-store.ts +4 -4
  311. package/src/notifications/signal.ts +1 -1
  312. package/src/oauth/manual-token-connection.ts +49 -25
  313. package/src/permissions/checker.ts +6 -5
  314. package/src/permissions/defaults.ts +4 -4
  315. package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
  316. package/src/prompts/cache-boundary.ts +8 -0
  317. package/src/prompts/system-prompt.ts +105 -634
  318. package/src/prompts/templates/BOOTSTRAP.md +166 -33
  319. package/src/prompts/templates/IDENTITY.md +8 -23
  320. package/src/prompts/templates/SOUL.md +20 -41
  321. package/src/prompts/templates/USER.md +3 -19
  322. package/src/prompts/user-reference.ts +14 -16
  323. package/src/providers/anthropic/client.ts +46 -2
  324. package/src/providers/gemini/client.ts +6 -9
  325. package/src/providers/managed-proxy/constants.ts +1 -7
  326. package/src/providers/managed-proxy/context.ts +0 -1
  327. package/src/providers/model-intents.ts +5 -5
  328. package/src/providers/openai/client.ts +10 -1
  329. package/src/providers/openrouter/client.ts +1 -0
  330. package/src/providers/ratelimit.ts +0 -35
  331. package/src/providers/registry.ts +3 -5
  332. package/src/providers/retry.ts +18 -1
  333. package/src/runtime/access-request-helper.ts +1 -1
  334. package/src/runtime/auth/route-policy.ts +7 -0
  335. package/src/runtime/channel-verification-service.ts +1 -1
  336. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  337. package/src/runtime/guardian-vellum-migration.ts +63 -1
  338. package/src/runtime/http-server.ts +8 -4
  339. package/src/runtime/migrations/vbundle-builder.ts +212 -32
  340. package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
  341. package/src/runtime/migrations/vbundle-importer.ts +66 -1
  342. package/src/runtime/migrations/vbundle-validator.ts +17 -3
  343. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
  344. package/src/runtime/routes/attachment-routes.ts +2 -2
  345. package/src/runtime/routes/btw-routes.ts +9 -0
  346. package/src/runtime/routes/channel-verification-routes.ts +19 -2
  347. package/src/runtime/routes/conversation-management-routes.ts +55 -1
  348. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  349. package/src/runtime/routes/conversation-routes.ts +49 -5
  350. package/src/runtime/routes/conversation-starter-routes.ts +207 -0
  351. package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
  352. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
  353. package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
  354. package/src/runtime/routes/migration-routes.ts +25 -13
  355. package/src/runtime/routes/secret-routes.ts +18 -0
  356. package/src/runtime/routes/settings-routes.ts +8 -8
  357. package/src/runtime/routes/telemetry-routes.ts +53 -0
  358. package/src/runtime/routes/trace-event-routes.ts +62 -0
  359. package/src/runtime/tool-grant-request-helper.ts +1 -1
  360. package/src/runtime/verification-outbound-actions.ts +47 -31
  361. package/src/security/encrypted-store.ts +263 -78
  362. package/src/skills/catalog-install.ts +10 -0
  363. package/src/skills/managed-store.ts +2 -0
  364. package/src/skills/skill-memory.ts +220 -0
  365. package/src/subagent/manager.ts +1 -4
  366. package/src/telemetry/types.ts +10 -1
  367. package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
  368. package/src/telemetry/usage-telemetry-reporter.ts +51 -4
  369. package/src/tools/AGENTS.md +11 -11
  370. package/src/tools/acp/spawn.ts +1 -1
  371. package/src/tools/apps/executors.ts +8 -8
  372. package/src/tools/apps/registry.ts +1 -1
  373. package/src/tools/assets/materialize.ts +6 -6
  374. package/src/tools/assets/search.ts +10 -10
  375. package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
  376. package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
  377. package/src/tools/browser/auth-detector.ts +6 -6
  378. package/src/tools/browser/browser-execution.ts +13 -13
  379. package/src/tools/browser/browser-manager.ts +3 -3
  380. package/src/tools/browser/chrome-cdp.ts +5 -5
  381. package/src/tools/browser/jit-auth.ts +2 -2
  382. package/src/tools/browser/network-recorder.test.ts +2 -2
  383. package/src/tools/browser/network-recorder.ts +3 -3
  384. package/src/tools/browser/runtime-check.ts +3 -3
  385. package/src/tools/claude-code/claude-code.ts +2 -2
  386. package/src/tools/computer-use/definitions.ts +18 -18
  387. package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
  388. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
  389. package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
  390. package/src/tools/credentials/broker-types.ts +5 -5
  391. package/src/tools/credentials/broker.ts +15 -15
  392. package/src/tools/credentials/metadata-store.ts +2 -2
  393. package/src/tools/credentials/resolve.ts +1 -1
  394. package/src/tools/credentials/selection.ts +1 -1
  395. package/src/tools/credentials/tool-policy.ts +1 -1
  396. package/src/tools/credentials/vault.ts +115 -25
  397. package/src/tools/execution-target.ts +2 -2
  398. package/src/tools/executor.ts +7 -7
  399. package/src/tools/filesystem/edit.ts +2 -2
  400. package/src/tools/filesystem/read.ts +1 -1
  401. package/src/tools/filesystem/write.ts +1 -1
  402. package/src/tools/host-filesystem/edit.ts +2 -1
  403. package/src/tools/host-filesystem/read.ts +2 -1
  404. package/src/tools/host-filesystem/write.ts +1 -1
  405. package/src/tools/host-terminal/host-shell.ts +9 -8
  406. package/src/tools/mcp/mcp-tool-factory.ts +7 -6
  407. package/src/tools/memory/definitions.ts +6 -5
  408. package/src/tools/memory/handlers.test.ts +1 -1
  409. package/src/tools/network/__tests__/web-search.test.ts +3 -3
  410. package/src/tools/network/domain-normalize.ts +2 -2
  411. package/src/tools/network/script-proxy/session-manager.ts +10 -10
  412. package/src/tools/network/web-fetch.ts +1 -1
  413. package/src/tools/network/web-search.ts +3 -3
  414. package/src/tools/permission-checker.ts +8 -8
  415. package/src/tools/registry.ts +7 -7
  416. package/src/tools/schedule/list.ts +2 -2
  417. package/src/tools/schema-transforms.ts +31 -21
  418. package/src/tools/secret-detection-handler.ts +1 -1
  419. package/src/tools/sensitive-output-placeholders.ts +1 -1
  420. package/src/tools/shared/filesystem/edit-engine.ts +1 -1
  421. package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
  422. package/src/tools/shared/filesystem/image-read.ts +25 -5
  423. package/src/tools/shared/filesystem/path-policy.ts +2 -2
  424. package/src/tools/shared/shell-output.ts +1 -1
  425. package/src/tools/side-effects.ts +1 -1
  426. package/src/tools/skills/execute.ts +1 -1
  427. package/src/tools/skills/load.ts +3 -3
  428. package/src/tools/skills/sandbox-runner.ts +3 -3
  429. package/src/tools/subagent/read.ts +1 -1
  430. package/src/tools/subagent/spawn.ts +2 -2
  431. package/src/tools/swarm/delegate.ts +3 -3
  432. package/src/tools/system/request-permission.ts +5 -4
  433. package/src/tools/terminal/backends/native.ts +4 -4
  434. package/src/tools/terminal/parser.ts +6 -6
  435. package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
  436. package/src/tools/terminal/shell.ts +16 -16
  437. package/src/tools/tool-approval-handler.ts +21 -12
  438. package/src/tools/tool-manifest.ts +4 -4
  439. package/src/tools/types.ts +3 -3
  440. package/src/tools/ui-surface/definitions.ts +9 -37
  441. package/src/tools/watcher/list.ts +1 -1
  442. package/src/util/logger.ts +7 -2
  443. package/src/util/retry.ts +29 -1
  444. package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
  445. package/src/workspace/migrations/registry.ts +2 -0
  446. package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
  447. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
  448. package/src/cli/reference.ts +0 -38
  449. package/src/memory/job-handlers/capability-cards.ts +0 -420
  450. package/src/runtime/routes/thread-starter-routes.ts +0 -294
@@ -69,11 +69,10 @@ mock.module("../config/loader.js", () => ({
69
69
  nonInteractiveLatestTurnCompression: "truncate",
70
70
  },
71
71
  },
72
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
72
+ rateLimit: { maxRequestsPerMinute: 0 },
73
73
  timeouts: { permissionTimeoutSec: 1 },
74
74
  skills: { entries: {}, allowBundled: true },
75
75
  permissions: { mode: "workspace" },
76
- sandbox: { enabled: false },
77
76
  daemon: {
78
77
  startupSocketWaitMs: 5000,
79
78
  stopTimeoutMs: 5000,
@@ -90,9 +89,9 @@ mock.module("../config/loader.js", () => ({
90
89
  "image-generation": {
91
90
  mode: "your-own",
92
91
  provider: "gemini",
93
- model: "gemini-2.5-flash-image",
92
+ model: "gemini-3.1-flash-image-preview",
94
93
  },
95
- "web-search": { mode: "your-own", provider: "anthropic-native" },
94
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
96
95
  },
97
96
  }),
98
97
  loadRawConfig: () => ({}),
@@ -178,7 +177,7 @@ mock.module("../memory/retriever.js", () => ({
178
177
  injectedTokens: 0,
179
178
  latencyMs: 0,
180
179
  }),
181
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
180
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
182
181
  }));
183
182
 
184
183
  mock.module("../context/window-manager.js", () => ({
@@ -1735,12 +1734,23 @@ describe("Regression: cancel semantics and error channel split", () => {
1735
1734
  });
1736
1735
 
1737
1736
  test("commitTurnChanges never resolving within budget -> turn still completes and drains queue", async () => {
1738
- const conversation = makeConversation();
1739
- await conversation.loadFromDb();
1740
-
1741
- turnCommitHangForever = true;
1737
+ // Replace setTimeout with a zero-delay version so the 4000ms
1738
+ // raceWithTimeout fires instantly instead of waiting real time.
1739
+ const origSetTimeout = globalThis.setTimeout;
1740
+ globalThis.setTimeout = ((
1741
+ fn: TimerHandler,
1742
+ _ms?: number,
1743
+ ...args: unknown[]
1744
+ ) => {
1745
+ return origSetTimeout(fn, 0, ...args);
1746
+ }) as typeof setTimeout;
1742
1747
 
1743
1748
  try {
1749
+ const conversation = makeConversation();
1750
+ await conversation.loadFromDb();
1751
+
1752
+ turnCommitHangForever = true;
1753
+
1744
1754
  const events1: ServerMessage[] = [];
1745
1755
  const events2: ServerMessage[] = [];
1746
1756
 
@@ -1761,10 +1771,8 @@ describe("Regression: cancel semantics and error channel split", () => {
1761
1771
 
1762
1772
  // The turn should still complete (timeout fires) and drain the queue
1763
1773
  // even though commitTurnChanges never resolves.
1764
- // The default turnCommitMaxWaitMs is 4000ms in the config mock,
1765
- // but the mock config doesn't set it, so it defaults to 4000ms.
1766
- // We wait for the second run to be registered, which proves the
1767
- // turn completed and the queue drained despite the hanging commit.
1774
+ // With the zero-delay setTimeout wrapper the 4000ms budget fires
1775
+ // instantly, so we only need a short wait for the second run.
1768
1776
  await waitForPendingRun(2, 10_000);
1769
1777
 
1770
1778
  // First message should have completed
@@ -1781,9 +1789,10 @@ describe("Regression: cancel semantics and error channel split", () => {
1781
1789
  // Complete the second run so the test can clean up
1782
1790
  turnCommitHangForever = false;
1783
1791
  resolveRun(1);
1784
- await new Promise((r) => setTimeout(r, 10));
1792
+ await new Promise((r) => origSetTimeout(r, 10));
1785
1793
  } finally {
1786
1794
  turnCommitHangForever = false;
1795
+ globalThis.setTimeout = origSetTimeout;
1787
1796
  }
1788
1797
  }, 15_000);
1789
1798
  });
@@ -29,7 +29,7 @@ mock.module("../config/loader.js", () => ({
29
29
  model: "claude-opus-4-6",
30
30
  provider: "anthropic",
31
31
  memory: { enabled: false },
32
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
32
+ rateLimit: { maxRequestsPerMinute: 0 },
33
33
  secretDetection: { enabled: false },
34
34
  contextWindow: { maxInputTokens: 200000 },
35
35
  services: {
@@ -41,9 +41,9 @@ mock.module("../config/loader.js", () => ({
41
41
  "image-generation": {
42
42
  mode: "your-own",
43
43
  provider: "gemini",
44
- model: "gemini-2.5-flash-image",
44
+ model: "gemini-3.1-flash-image-preview",
45
45
  },
46
- "web-search": { mode: "your-own", provider: "anthropic-native" },
46
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
47
47
  },
48
48
  }),
49
49
  }));
@@ -7,20 +7,19 @@ import type {
7
7
  } from "../daemon/conversation-runtime-assembly.js";
8
8
  import {
9
9
  applyRuntimeInjections,
10
- buildChannelTurnContextBlock,
10
+ buildTurnContextBlock,
11
11
  injectChannelCapabilityContext,
12
- injectChannelTurnContext,
12
+ injectChannelCommandContext,
13
13
  injectInboundActorContext,
14
14
  injectTemporalContext,
15
+ injectTurnContext,
15
16
  isGroupChatType,
16
17
  resolveChannelCapabilities,
17
- sanitizePttActivationKey,
18
18
  stripChannelCapabilityContext,
19
19
  stripChannelTurnContext,
20
20
  stripInboundActorContext,
21
21
  stripTemporalContext,
22
22
  } from "../daemon/conversation-runtime-assembly.js";
23
- import { buildChannelAwarenessSection } from "../prompts/system-prompt.js";
24
23
  import type { Message } from "../providers/types.js";
25
24
 
26
25
  // ---------------------------------------------------------------------------
@@ -135,7 +134,7 @@ describe("resolveChannelCapabilities", () => {
135
134
  });
136
135
 
137
136
  test("propagates chatType when provided", () => {
138
- const caps = resolveChannelCapabilities("telegram", null, null, "group");
137
+ const caps = resolveChannelCapabilities("telegram", null, "group");
139
138
  expect(caps.chatType).toBe("group");
140
139
  });
141
140
 
@@ -155,7 +154,7 @@ describe("injectChannelCapabilityContext", () => {
155
154
  content: [{ type: "text", text: "Hello" }],
156
155
  };
157
156
 
158
- test("injects channel capabilities block for dashboard channel", () => {
157
+ test("skips injection entirely for desktop happy path (all capabilities true)", () => {
159
158
  const caps: ChannelCapabilities = {
160
159
  channel: "vellum",
161
160
  dashboardCapable: true,
@@ -165,26 +164,9 @@ describe("injectChannelCapabilityContext", () => {
165
164
 
166
165
  const result = injectChannelCapabilityContext(baseUserMessage, caps);
167
166
 
168
- // Should prepend a text block with channel_capabilities
169
- expect(result.content.length).toBe(2);
170
- const injected = result.content[0];
171
- expect(injected.type).toBe("text");
172
- expect((injected as { type: "text"; text: string }).text).toContain(
173
- "<channel_capabilities>",
174
- );
175
- expect((injected as { type: "text"; text: string }).text).toContain(
176
- "dashboard_capable: true",
177
- );
178
- expect((injected as { type: "text"; text: string }).text).toContain(
179
- "supports_dynamic_ui: true",
180
- );
181
- expect((injected as { type: "text"; text: string }).text).toContain(
182
- "</channel_capabilities>",
183
- );
184
- // Should NOT contain constraint rules for dashboard
185
- expect((injected as { type: "text"; text: string }).text).not.toContain(
186
- "CHANNEL CONSTRAINTS",
187
- );
167
+ // Message returned unchanged no injection at all
168
+ expect(result).toBe(baseUserMessage);
169
+ expect(result.content.length).toBe(1);
188
170
  });
189
171
 
190
172
  test("injects constraint rules for non-dashboard channel", () => {
@@ -293,6 +275,50 @@ describe("injectChannelCapabilityContext", () => {
293
275
  expect(text).toContain("GROUP CHAT ETIQUETTE");
294
276
  expect(text).toContain("emoji reactions");
295
277
  });
278
+
279
+ test("still injects for group chats even when all capabilities are true", () => {
280
+ const caps: ChannelCapabilities = {
281
+ channel: "slack",
282
+ dashboardCapable: true,
283
+ supportsDynamicUi: true,
284
+ supportsVoiceInput: true,
285
+ chatType: "channel",
286
+ };
287
+
288
+ const result = injectChannelCapabilityContext(baseUserMessage, caps);
289
+ // Not the happy path because chatType is a group type
290
+ expect(result).not.toBe(baseUserMessage);
291
+ const text = (result.content[0] as { type: "text"; text: string }).text;
292
+ expect(text).toContain("GROUP CHAT ETIQUETTE");
293
+ });
294
+
295
+ test("injects WhatsApp formatting constraint for whatsapp channel", () => {
296
+ const caps: ChannelCapabilities = {
297
+ channel: "whatsapp",
298
+ dashboardCapable: false,
299
+ supportsDynamicUi: false,
300
+ supportsVoiceInput: false,
301
+ };
302
+
303
+ const result = injectChannelCapabilityContext(baseUserMessage, caps);
304
+ const text = (result.content[0] as { type: "text"; text: string }).text;
305
+ expect(text).toContain("Do NOT use markdown tables");
306
+ expect(text).toContain("bullet lists");
307
+ expect(text).toContain("CAPS for emphasis");
308
+ });
309
+
310
+ test("does NOT inject WhatsApp formatting for non-whatsapp channels", () => {
311
+ const caps: ChannelCapabilities = {
312
+ channel: "telegram",
313
+ dashboardCapable: false,
314
+ supportsDynamicUi: false,
315
+ supportsVoiceInput: false,
316
+ };
317
+
318
+ const result = injectChannelCapabilityContext(baseUserMessage, caps);
319
+ const text = (result.content[0] as { type: "text"; text: string }).text;
320
+ expect(text).not.toContain("Do NOT use markdown tables");
321
+ });
296
322
  });
297
323
 
298
324
  // ---------------------------------------------------------------------------
@@ -449,60 +475,12 @@ describe("applyRuntimeInjections with channelCapabilities", () => {
449
475
  });
450
476
  });
451
477
 
452
- // ---------------------------------------------------------------------------
453
- // buildChannelAwarenessSection
454
- // ---------------------------------------------------------------------------
455
-
456
- describe("buildChannelAwarenessSection", () => {
457
- test("includes channel awareness heading", () => {
458
- const section = buildChannelAwarenessSection();
459
- expect(section).toContain("## Channel Awareness & Trust Gating");
460
- });
461
-
462
- test("includes channel-specific rules", () => {
463
- const section = buildChannelAwarenessSection();
464
- expect(section).toContain("dashboard_capable");
465
- expect(section).toContain("supports_dynamic_ui");
466
- expect(section).toContain("supports_voice_input");
467
- });
468
-
469
- test("includes trust gating rules for permission asks", () => {
470
- const section = buildChannelAwarenessSection();
471
- expect(section).toContain("firstConversationComplete");
472
- expect(section).toContain("Permission ask trust gating");
473
- expect(section).toContain(
474
- "Do NOT proactively ask for elevated permissions",
475
- );
476
- });
477
-
478
- test("gates microphone permissions on voice capability", () => {
479
- const section = buildChannelAwarenessSection();
480
- expect(section).toContain("Do not ask for microphone permissions");
481
- });
482
-
483
- test("gates computer-control on dashboard channel", () => {
484
- const section = buildChannelAwarenessSection();
485
- expect(section).toContain("computer-control permissions on non-dashboard");
486
- });
487
-
488
- test("does NOT include group chat etiquette (gated per-turn instead)", () => {
489
- const section = buildChannelAwarenessSection();
490
- expect(section).not.toContain("Group chat etiquette");
491
- expect(section).not.toContain("Stay silent when");
492
- });
493
-
494
- test("does NOT include Discord references (not a supported channel)", () => {
495
- const section = buildChannelAwarenessSection();
496
- expect(section).not.toContain("Discord");
497
- });
498
- });
499
-
500
478
  // ---------------------------------------------------------------------------
501
479
  // Trust-gating behavior: channel constraints for permission asks
502
480
  // ---------------------------------------------------------------------------
503
481
 
504
482
  describe("trust-gating via channel capabilities", () => {
505
- test("vellum channel with macos interface does not add constraint rules", () => {
483
+ test("vellum channel with macos interface skips injection (happy path)", () => {
506
484
  const caps = resolveChannelCapabilities("vellum", "macos");
507
485
  const message: Message = {
508
486
  role: "user",
@@ -510,10 +488,9 @@ describe("trust-gating via channel capabilities", () => {
510
488
  };
511
489
 
512
490
  const result = injectChannelCapabilityContext(message, caps);
513
- const injected = (result.content[0] as { type: "text"; text: string }).text;
514
491
 
515
- expect(injected).not.toContain("CHANNEL CONSTRAINTS");
516
- expect(injected).toContain("dashboard_capable: true");
492
+ // Happy path: message returned unchanged
493
+ expect(result).toBe(message);
517
494
  });
518
495
 
519
496
  test("non-dashboard channel adds constraint rules preventing UI references", () => {
@@ -554,6 +531,50 @@ describe("trust-gating via channel capabilities", () => {
554
531
  });
555
532
  });
556
533
 
534
+ // ---------------------------------------------------------------------------
535
+ // injectChannelCommandContext
536
+ // ---------------------------------------------------------------------------
537
+
538
+ describe("injectChannelCommandContext", () => {
539
+ const baseUserMessage: Message = {
540
+ role: "user",
541
+ content: [{ type: "text", text: "Hello" }],
542
+ };
543
+
544
+ test("injects start command instructions when type is start", () => {
545
+ const result = injectChannelCommandContext(baseUserMessage, {
546
+ type: "start",
547
+ });
548
+ const text = (result.content[0] as { type: "text"; text: string }).text;
549
+ expect(text).toContain("command_type: start");
550
+ expect(text).toContain("warm, brief greeting");
551
+ expect(text).toContain("Treat /start as a hello");
552
+ expect(text).toContain("Do NOT reset conversation");
553
+ });
554
+
555
+ test("includes language code and payload when provided", () => {
556
+ const result = injectChannelCommandContext(baseUserMessage, {
557
+ type: "start",
558
+ payload: "ref123",
559
+ languageCode: "es",
560
+ });
561
+ const text = (result.content[0] as { type: "text"; text: string }).text;
562
+ expect(text).toContain("payload: ref123");
563
+ expect(text).toContain("language_code: es");
564
+ expect(text).toContain("warm, brief greeting");
565
+ });
566
+
567
+ test("does NOT inject start instructions for non-start commands", () => {
568
+ const result = injectChannelCommandContext(baseUserMessage, {
569
+ type: "help",
570
+ });
571
+ const text = (result.content[0] as { type: "text"; text: string }).text;
572
+ expect(text).toContain("command_type: help");
573
+ expect(text).not.toContain("warm, brief greeting");
574
+ expect(text).not.toContain("Treat /start as a hello");
575
+ });
576
+ });
577
+
557
578
  // ---------------------------------------------------------------------------
558
579
  // injectTemporalContext
559
580
  // ---------------------------------------------------------------------------
@@ -768,9 +789,13 @@ describe("injectInboundActorContext", () => {
768
789
  expect(text).toContain("trust_class: guardian");
769
790
  expect(text).toContain("source_channel: phone");
770
791
  expect(text).toContain("canonical_actor_identity: guardian-user-1");
792
+ // Display names differ from canonical, so they should appear
793
+ expect(text).toContain("actor_identifier: +15550001111");
771
794
  expect(text).toContain("actor_display_name: Guardian Name");
772
795
  expect(text).toContain("actor_sender_display_name: Guardian Name");
773
796
  expect(text).toContain("actor_member_display_name: Guardian Name");
797
+ // guardian_identity matches canonical, so it should be omitted
798
+ expect(text).not.toContain("guardian_identity:");
774
799
  expect(text).toContain("</inbound_actor_context>");
775
800
  });
776
801
 
@@ -798,6 +823,30 @@ describe("injectInboundActorContext", () => {
798
823
  );
799
824
  });
800
825
 
826
+ test("omits name_preference_note when member name matches canonical and is suppressed", () => {
827
+ const ctx: InboundActorContext = {
828
+ sourceChannel: "telegram",
829
+ canonicalActorIdentity: "Jeff",
830
+ actorIdentifier: "@jeff_handle",
831
+ actorDisplayName: "Jeff",
832
+ actorSenderDisplayName: "Jeffrey",
833
+ actorMemberDisplayName: "Jeff",
834
+ trustClass: "trusted_contact",
835
+ guardianIdentity: "guardian-user-1",
836
+ memberStatus: "active",
837
+ memberPolicy: "allow",
838
+ };
839
+
840
+ const result = injectInboundActorContext(baseUserMessage, ctx);
841
+ const text = (result.content[0] as { type: "text"; text: string }).text;
842
+ // actor_member_display_name matches canonical → omitted by differs() guard
843
+ expect(text).not.toContain("actor_member_display_name:");
844
+ // actor_sender_display_name differs from canonical → emitted
845
+ expect(text).toContain("actor_sender_display_name: Jeffrey");
846
+ // name_preference_note must NOT appear since actor_member_display_name was omitted
847
+ expect(text).not.toContain("name_preference_note:");
848
+ });
849
+
801
850
  test("sanitizes inline actor context values to prevent line injection", () => {
802
851
  const ctx: InboundActorContext = {
803
852
  sourceChannel: "telegram",
@@ -903,6 +952,38 @@ describe("injectInboundActorContext", () => {
903
952
  expect(text).not.toContain("non-guardian account");
904
953
  });
905
954
 
955
+ test("omits redundant fields when they match canonical_actor_identity", () => {
956
+ const uuid = "vellum-principal-b77e94f5-67c0-4599-8baa-871b925b3da8";
957
+ const ctx: InboundActorContext = {
958
+ sourceChannel: "vellum",
959
+ canonicalActorIdentity: uuid,
960
+ actorIdentifier: uuid,
961
+ actorDisplayName: uuid,
962
+ actorSenderDisplayName: undefined,
963
+ actorMemberDisplayName: uuid,
964
+ trustClass: "guardian",
965
+ guardianIdentity: uuid,
966
+ memberStatus: "active",
967
+ memberPolicy: "allow",
968
+ contactNotes: "guardian",
969
+ };
970
+
971
+ const result = injectInboundActorContext(baseUserMessage, ctx);
972
+ const text = (result.content[0] as { type: "text"; text: string }).text;
973
+ // Only essential fields should remain
974
+ expect(text).toContain("source_channel: vellum");
975
+ expect(text).toContain(`canonical_actor_identity: ${uuid}`);
976
+ expect(text).toContain("trust_class: guardian");
977
+ // Redundant fields should be omitted
978
+ expect(text).not.toContain("actor_identifier:");
979
+ expect(text).not.toContain("actor_display_name:");
980
+ expect(text).not.toContain("actor_sender_display_name:");
981
+ expect(text).not.toContain("actor_member_display_name:");
982
+ expect(text).not.toContain("guardian_identity:");
983
+ // contact_notes: "guardian" matches trust_class, should be omitted
984
+ expect(text).not.toContain("contact_notes:");
985
+ });
986
+
906
987
  test("omits member_status and member_policy when not provided", () => {
907
988
  const ctx: InboundActorContext = {
908
989
  sourceChannel: "phone",
@@ -969,46 +1050,44 @@ describe("applyRuntimeInjections with inboundActorContext", () => {
969
1050
  });
970
1051
 
971
1052
  // ---------------------------------------------------------------------------
972
- // buildChannelTurnContextBlock
1053
+ // buildTurnContextBlock (channel-only)
973
1054
  // ---------------------------------------------------------------------------
974
1055
 
975
- describe("buildChannelTurnContextBlock", () => {
976
- test("formats block with all three channel fields", () => {
977
- const block = buildChannelTurnContextBlock({
1056
+ describe("buildTurnContextBlock (channel-only)", () => {
1057
+ test("collapses to single field when all channels match", () => {
1058
+ const block = buildTurnContextBlock({
978
1059
  turnContext: {
979
1060
  userMessageChannel: "telegram",
980
1061
  assistantMessageChannel: "telegram",
981
1062
  },
982
1063
  conversationOriginChannel: "telegram",
983
- });
1064
+ }, undefined);
984
1065
  expect(block).toBe(
985
- "<channel_turn_context>\n" +
986
- "user_message_channel: telegram\n" +
987
- "assistant_message_channel: telegram\n" +
988
- "conversation_origin_channel: telegram\n" +
989
- "</channel_turn_context>",
1066
+ "<turn_context>\n" +
1067
+ "channel: telegram\n" +
1068
+ "</turn_context>",
990
1069
  );
991
1070
  });
992
1071
 
993
1072
  test('uses "unknown" when conversationOriginChannel is null', () => {
994
- const block = buildChannelTurnContextBlock({
1073
+ const block = buildTurnContextBlock({
995
1074
  turnContext: {
996
1075
  userMessageChannel: "vellum",
997
1076
  assistantMessageChannel: "vellum",
998
1077
  },
999
1078
  conversationOriginChannel: null,
1000
- });
1079
+ }, undefined);
1001
1080
  expect(block).toContain("conversation_origin_channel: unknown");
1002
1081
  });
1003
1082
 
1004
1083
  test("handles mixed channels", () => {
1005
- const block = buildChannelTurnContextBlock({
1084
+ const block = buildTurnContextBlock({
1006
1085
  turnContext: {
1007
1086
  userMessageChannel: "telegram",
1008
1087
  assistantMessageChannel: "vellum",
1009
1088
  },
1010
1089
  conversationOriginChannel: "vellum",
1011
- });
1090
+ }, undefined);
1012
1091
  expect(block).toContain("user_message_channel: telegram");
1013
1092
  expect(block).toContain("assistant_message_channel: vellum");
1014
1093
  expect(block).toContain("conversation_origin_channel: vellum");
@@ -1016,10 +1095,10 @@ describe("buildChannelTurnContextBlock", () => {
1016
1095
  });
1017
1096
 
1018
1097
  // ---------------------------------------------------------------------------
1019
- // injectChannelTurnContext
1098
+ // injectTurnContext (channel-only)
1020
1099
  // ---------------------------------------------------------------------------
1021
1100
 
1022
- describe("injectChannelTurnContext", () => {
1101
+ describe("injectTurnContext (channel-only)", () => {
1023
1102
  const baseUserMessage: Message = {
1024
1103
  role: "user",
1025
1104
  content: [{ type: "text", text: "Hello from telegram" }],
@@ -1033,14 +1112,14 @@ describe("injectChannelTurnContext", () => {
1033
1112
  },
1034
1113
  conversationOriginChannel: "telegram",
1035
1114
  };
1036
- const result = injectChannelTurnContext(baseUserMessage, params);
1115
+ const result = injectTurnContext(baseUserMessage, params, undefined);
1037
1116
  expect(result.content.length).toBe(2);
1038
1117
  const injected = result.content[0];
1039
1118
  expect(injected.type).toBe("text");
1040
1119
  const text = (injected as { type: "text"; text: string }).text;
1041
- expect(text).toContain("<channel_turn_context>");
1042
- expect(text).toContain("user_message_channel: telegram");
1043
- expect(text).toContain("</channel_turn_context>");
1120
+ expect(text).toContain("<turn_context>");
1121
+ expect(text).toContain("channel: telegram");
1122
+ expect(text).toContain("</turn_context>");
1044
1123
  });
1045
1124
 
1046
1125
  test("preserves original message content", () => {
@@ -1051,7 +1130,7 @@ describe("injectChannelTurnContext", () => {
1051
1130
  },
1052
1131
  conversationOriginChannel: "vellum",
1053
1132
  };
1054
- const result = injectChannelTurnContext(baseUserMessage, params);
1133
+ const result = injectTurnContext(baseUserMessage, params, undefined);
1055
1134
  const lastBlock = result.content[result.content.length - 1];
1056
1135
  expect((lastBlock as { type: "text"; text: string }).text).toBe(
1057
1136
  "Hello from telegram",
@@ -1071,7 +1150,7 @@ describe("stripChannelTurnContext", () => {
1071
1150
  content: [
1072
1151
  {
1073
1152
  type: "text",
1074
- text: "<channel_turn_context>\nuser_message_channel: telegram\n</channel_turn_context>",
1153
+ text: "<turn_context>\nuser_message_channel: telegram\n</turn_context>",
1075
1154
  },
1076
1155
  { type: "text", text: "Hello" },
1077
1156
  ],
@@ -1099,7 +1178,7 @@ describe("stripChannelTurnContext", () => {
1099
1178
  content: [
1100
1179
  {
1101
1180
  type: "text",
1102
- text: "<channel_turn_context>\nuser_message_channel: macos\n</channel_turn_context>",
1181
+ text: "<turn_context>\nuser_message_channel: macos\n</turn_context>",
1103
1182
  },
1104
1183
  ],
1105
1184
  },
@@ -1152,7 +1231,7 @@ describe("applyRuntimeInjections with channelTurnContext", () => {
1152
1231
  expect(result[0].content.length).toBe(2);
1153
1232
  const injected = result[0].content[0];
1154
1233
  expect((injected as { type: "text"; text: string }).text).toContain(
1155
- "<channel_turn_context>",
1234
+ "<turn_context>",
1156
1235
  );
1157
1236
  });
1158
1237
 
@@ -1173,73 +1252,6 @@ describe("applyRuntimeInjections with channelTurnContext", () => {
1173
1252
  });
1174
1253
  });
1175
1254
 
1176
- // ---------------------------------------------------------------------------
1177
- // sanitizePttActivationKey
1178
- // ---------------------------------------------------------------------------
1179
-
1180
- describe("sanitizePttActivationKey", () => {
1181
- test("returns undefined for null/undefined input", () => {
1182
- expect(sanitizePttActivationKey(null)).toBeUndefined();
1183
- expect(sanitizePttActivationKey(undefined)).toBeUndefined();
1184
- });
1185
-
1186
- test("passes through valid JSON PTTActivator payloads", () => {
1187
- const modifierOnly = JSON.stringify({
1188
- kind: "modifierOnly",
1189
- modifierFlags: 8388608,
1190
- });
1191
- expect(sanitizePttActivationKey(modifierOnly)).toBe(modifierOnly);
1192
- const keyPayload = JSON.stringify({ kind: "key", keyCode: 49 });
1193
- expect(sanitizePttActivationKey(keyPayload)).toBe(keyPayload);
1194
- const nonePayload = JSON.stringify({ kind: "none" });
1195
- expect(sanitizePttActivationKey(nonePayload)).toBe(nonePayload);
1196
- });
1197
-
1198
- test("returns undefined for invalid keys", () => {
1199
- expect(
1200
- sanitizePttActivationKey("malicious\nprompt injection"),
1201
- ).toBeUndefined();
1202
- expect(sanitizePttActivationKey("arbitrary_value")).toBeUndefined();
1203
- expect(sanitizePttActivationKey("")).toBeUndefined();
1204
- });
1205
- });
1206
-
1207
- // ---------------------------------------------------------------------------
1208
- // resolveChannelCapabilities sanitizes pttActivationKey
1209
- // ---------------------------------------------------------------------------
1210
-
1211
- describe("resolveChannelCapabilities with PTT metadata", () => {
1212
- test("sanitizes valid JSON PTTActivator pttActivationKey", () => {
1213
- const key = JSON.stringify({
1214
- kind: "modifierOnly",
1215
- modifierFlags: 8388608,
1216
- });
1217
- const caps = resolveChannelCapabilities("macos", "macos", {
1218
- pttActivationKey: key,
1219
- });
1220
- expect(caps.pttActivationKey).toBe(key);
1221
- });
1222
-
1223
- test("sanitizes invalid pttActivationKey to undefined", () => {
1224
- const caps = resolveChannelCapabilities("macos", "macos", {
1225
- pttActivationKey: "evil\nprompt",
1226
- });
1227
- expect(caps.pttActivationKey).toBeUndefined();
1228
- });
1229
-
1230
- test("passes through microphonePermissionGranted", () => {
1231
- const key = JSON.stringify({
1232
- kind: "modifierOnly",
1233
- modifierFlags: 8388608,
1234
- });
1235
- const caps = resolveChannelCapabilities("macos", "macos", {
1236
- pttActivationKey: key,
1237
- microphonePermissionGranted: true,
1238
- });
1239
- expect(caps.microphonePermissionGranted).toBe(true);
1240
- });
1241
- });
1242
-
1243
1255
  // ---------------------------------------------------------------------------
1244
1256
  // applyRuntimeInjections — injection mode
1245
1257
  // ---------------------------------------------------------------------------
@@ -1299,8 +1311,8 @@ describe("applyRuntimeInjections — injection mode", () => {
1299
1311
  expect(allText).toContain("<channel_command_context>");
1300
1312
  expect(allText).toContain("<active_workspace>");
1301
1313
  expect(allText).toContain("<channel_capabilities>");
1302
- expect(allText).toContain("<channel_turn_context>");
1303
- expect(allText).toContain("<interface_turn_context>");
1314
+ expect(allText).toContain("<turn_context>");
1315
+ expect(allText).toContain("<turn_context>");
1304
1316
  expect(allText).toContain("<inbound_actor_context>");
1305
1317
  expect(allText).toContain("<non_interactive_context>");
1306
1318
  });
@@ -1349,8 +1361,8 @@ describe("applyRuntimeInjections — injection mode", () => {
1349
1361
  .join("\n");
1350
1362
 
1351
1363
  // Kept in minimal mode
1352
- expect(allText).toContain("<channel_turn_context>");
1353
- expect(allText).toContain("<interface_turn_context>");
1364
+ expect(allText).toContain("<turn_context>");
1365
+ expect(allText).toContain("<turn_context>");
1354
1366
  expect(allText).toContain("<inbound_actor_context>");
1355
1367
  expect(allText).toContain("<non_interactive_context>");
1356
1368
  expect(allText).toContain("<channel_capabilities>");
@@ -27,7 +27,7 @@ function makeSignal(
27
27
  signalId: "sig-seed-001",
28
28
  createdAt: Date.now(),
29
29
  sourceChannel: "scheduler",
30
- sourceSessionId: "sess-1",
30
+ sourceContextId: "sess-1",
31
31
  sourceEventName: "test.event",
32
32
  contextPayload: {},
33
33
  attentionHints: {