@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
@@ -21,6 +21,7 @@ import { httpError } from "../http-errors.js";
21
21
  import type { RouteDefinition } from "../http-router.js";
22
22
  import {
23
23
  cancelOutbound,
24
+ deliverVerificationSlack,
24
25
  normalizeTelegramDestination,
25
26
  resendOutbound,
26
27
  startOutbound,
@@ -118,12 +119,20 @@ export async function handleCreateVerificationSession(
118
119
  verificationRateLimiter.recordFailure(rateLimitKey);
119
120
  }
120
121
 
122
+ // Dispatch Slack DM delivery from the daemon process (not sandboxed).
123
+ if (result._pendingSlackDm) {
124
+ const { userId, text, assistantId: aid } = result._pendingSlackDm;
125
+ deliverVerificationSlack(userId, text, aid);
126
+ }
127
+
121
128
  const status = result.success
122
129
  ? 200
123
130
  : result.error === "rate_limited"
124
131
  ? 429
125
132
  : 400;
126
- return Response.json(result, { status });
133
+ // Strip internal field from the response
134
+ const { _pendingSlackDm: _, ...publicResult } = result;
135
+ return Response.json(publicResult, { status });
127
136
  }
128
137
 
129
138
  // Inbound challenge path
@@ -167,12 +176,20 @@ export async function handleResendVerificationSession(
167
176
  channel: body.channel,
168
177
  originConversationId: body.originConversationId,
169
178
  });
179
+
180
+ // Dispatch Slack DM delivery from the daemon process (not sandboxed).
181
+ if (result._pendingSlackDm) {
182
+ const { userId, text, assistantId: aid } = result._pendingSlackDm;
183
+ deliverVerificationSlack(userId, text, aid);
184
+ }
185
+
170
186
  const status = result.success
171
187
  ? 200
172
188
  : result.error === "rate_limited"
173
189
  ? 429
174
190
  : 400;
175
- return Response.json(result, { status });
191
+ const { _pendingSlackDm: _, ...publicResult } = result;
192
+ return Response.json(publicResult, { status });
176
193
  }
177
194
 
178
195
  /**
@@ -4,6 +4,7 @@
4
4
  * POST /v1/conversations/switch — switch to an existing conversation
5
5
  * PATCH /v1/conversations/:id/name — rename a conversation
6
6
  * DELETE /v1/conversations — clear all conversations
7
+ * POST /v1/conversations/:id/wipe — wipe conversation and revert memory
7
8
  * DELETE /v1/conversations/:id — delete a single conversation
8
9
  * POST /v1/conversations/:id/cancel — cancel generation
9
10
  * POST /v1/conversations/:id/undo — undo last message
@@ -14,6 +15,7 @@
14
15
  import {
15
16
  batchSetDisplayOrders,
16
17
  deleteConversation,
18
+ wipeConversation,
17
19
  } from "../../memory/conversation-crud.js";
18
20
  import {
19
21
  resolveConversationId,
@@ -121,6 +123,57 @@ export function conversationManagementRouteDefinitions(
121
123
  return new Response(null, { status: 204 });
122
124
  },
123
125
  },
126
+ {
127
+ endpoint: "conversations/:id/wipe",
128
+ method: "POST",
129
+ policyKey: "conversations/wipe",
130
+ handler: async ({ params }) => {
131
+ const resolvedId = resolveConversationId(params.id);
132
+ if (!resolvedId) {
133
+ return httpError(
134
+ "NOT_FOUND",
135
+ `Conversation ${params.id} not found`,
136
+ 404,
137
+ );
138
+ }
139
+ deps.destroyConversation(resolvedId);
140
+ const result = wipeConversation(resolvedId);
141
+ // Enqueue Qdrant vector cleanup jobs
142
+ for (const segId of result.segmentIds) {
143
+ enqueueMemoryJob("delete_qdrant_vectors", {
144
+ targetType: "segment",
145
+ targetId: segId,
146
+ });
147
+ }
148
+ for (const itemId of result.orphanedItemIds) {
149
+ enqueueMemoryJob("delete_qdrant_vectors", {
150
+ targetType: "item",
151
+ targetId: itemId,
152
+ });
153
+ }
154
+ for (const summaryId of result.deletedSummaryIds) {
155
+ enqueueMemoryJob("delete_qdrant_vectors", {
156
+ targetType: "summary",
157
+ targetId: summaryId,
158
+ });
159
+ }
160
+ log.info(
161
+ {
162
+ conversationId: resolvedId,
163
+ unsuperseded: result.unsupersededItemIds.length,
164
+ summariesDeleted: result.deletedSummaryIds.length,
165
+ jobsCancelled: result.cancelledJobCount,
166
+ },
167
+ "Wiped conversation and reverted memory changes",
168
+ );
169
+ return Response.json({
170
+ wiped: true,
171
+ unsupersededItems: result.unsupersededItemIds.length,
172
+ deletedSummaries: result.deletedSummaryIds.length,
173
+ cancelledJobs: result.cancelledJobCount,
174
+ });
175
+ },
176
+ },
124
177
  {
125
178
  endpoint: "conversations/:id",
126
179
  method: "DELETE",
@@ -163,7 +216,8 @@ export function conversationManagementRouteDefinitions(
163
216
  method: "POST",
164
217
  policyKey: "conversations/cancel",
165
218
  handler: ({ params }) => {
166
- deps.cancelGeneration(params.id);
219
+ const resolvedId = resolveConversationId(params.id) ?? params.id;
220
+ deps.cancelGeneration(resolvedId);
167
221
  return new Response(null, { status: 202 });
168
222
  },
169
223
  },
@@ -200,7 +200,7 @@ export function conversationQueryRouteDefinitions(
200
200
  requestId: params.id,
201
201
  });
202
202
  }
203
- if (result.reason === "session_not_found") {
203
+ if (result.reason === "conversation_not_found") {
204
204
  return httpError("NOT_FOUND", "Conversation not found", 404);
205
205
  }
206
206
  return httpError("NOT_FOUND", "Queued message not found", 404);
@@ -35,6 +35,7 @@ import {
35
35
  createCanonicalGuardianRequest,
36
36
  generateCanonicalRequestCode,
37
37
  listPendingRequestsByConversationScope,
38
+ resolveCanonicalGuardianRequest,
38
39
  } from "../../memory/canonical-guardian-store.js";
39
40
  import {
40
41
  addMessage,
@@ -58,6 +59,7 @@ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
58
59
  import type { AuthContext } from "../auth/types.js";
59
60
  import { bridgeConfirmationRequestToGuardian } from "../confirmation-request-guardian-bridge.js";
60
61
  import { routeGuardianReply } from "../guardian-reply-router.js";
62
+ import { healGuardianBindingDrift } from "../guardian-vellum-migration.js";
61
63
  import { httpError } from "../http-errors.js";
62
64
  import type { RouteDefinition } from "../http-router.js";
63
65
  import type {
@@ -492,16 +494,16 @@ function makeHubPublisher(
492
494
  }
493
495
 
494
496
  // ServerMessage is a large union; conversationId exists on most but not all variants.
495
- const msgSessionId =
497
+ const msgConversationId =
496
498
  "conversationId" in msg &&
497
499
  typeof (msg as { conversationId?: unknown }).conversationId === "string"
498
500
  ? (msg as { conversationId: string }).conversationId
499
501
  : undefined;
500
- const resolvedSessionId = msgSessionId ?? conversationId;
502
+ const resolvedConversationId = msgConversationId ?? conversationId;
501
503
  const event = buildAssistantEvent(
502
504
  DAEMON_INTERNAL_ASSISTANT_ID,
503
505
  msg,
504
- resolvedSessionId,
506
+ resolvedConversationId,
505
507
  );
506
508
  hubChain = (async () => {
507
509
  await hubChain;
@@ -646,12 +648,44 @@ export async function handleSendMessage(
646
648
  // the same trust resolution pipeline that channel ingress uses.
647
649
  if (authContext.actorPrincipalId) {
648
650
  const assistantId = DAEMON_INTERNAL_ASSISTANT_ID;
649
- const trustCtx = resolveTrustContext({
651
+ let trustCtx = resolveTrustContext({
650
652
  assistantId,
651
653
  sourceChannel: "vellum",
652
654
  conversationExternalId: "local",
653
655
  actorExternalId: authContext.actorPrincipalId,
654
656
  });
657
+ if (trustCtx.trustClass === "unknown") {
658
+ // Attempt to heal guardian binding drift: after a DB reset the
659
+ // guardian binding gets a new vellum-principal-* UUID while the
660
+ // client still holds a valid JWT with the old one. The signing
661
+ // key survives the reset, so the JWT is authentic — just stale.
662
+ const healed = healGuardianBindingDrift(authContext.actorPrincipalId);
663
+ if (healed) {
664
+ trustCtx = resolveTrustContext({
665
+ assistantId,
666
+ sourceChannel: "vellum",
667
+ conversationExternalId: "local",
668
+ actorExternalId: authContext.actorPrincipalId,
669
+ });
670
+ log.info(
671
+ {
672
+ actorPrincipalId: authContext.actorPrincipalId,
673
+ trustClass: trustCtx.trustClass,
674
+ },
675
+ "Trust re-resolved after guardian binding drift heal",
676
+ );
677
+ } else {
678
+ log.warn(
679
+ {
680
+ actorPrincipalId: authContext.actorPrincipalId,
681
+ sourceChannel,
682
+ trustClass: trustCtx.trustClass,
683
+ principalType: authContext.principalType,
684
+ },
685
+ "JWT-verified actor resolved to unknown trust class — possible guardian binding drift (e.g. DB reset without re-bootstrap)",
686
+ );
687
+ }
688
+ }
655
689
  conversation.setTrustContext(withSourceChannel(sourceChannel, trustCtx));
656
690
  } else {
657
691
  // Service principals (svc_gateway) or tokens without an actor ID
@@ -811,6 +845,11 @@ export async function handleSendMessage(
811
845
  state: "denied" as const,
812
846
  source: "auto_deny" as const,
813
847
  });
848
+ // Sync canonical guardian request status so stale "pending" DB
849
+ // records don't get matched by later guardian reply routing.
850
+ resolveCanonicalGuardianRequest(interaction.requestId, "pending", {
851
+ status: "denied",
852
+ });
814
853
  }
815
854
  }
816
855
  conversation.denyAllPendingConfirmations();
@@ -842,6 +881,11 @@ export async function handleSendMessage(
842
881
  state: "denied" as const,
843
882
  source: "auto_deny" as const,
844
883
  });
884
+ // Sync canonical guardian request status so stale "pending" DB
885
+ // records don't get matched by later guardian reply routing.
886
+ resolveCanonicalGuardianRequest(interaction.requestId, "pending", {
887
+ status: "denied",
888
+ });
845
889
  }
846
890
  }
847
891
  conversation.denyAllPendingConfirmations();
@@ -931,7 +975,7 @@ export async function handleSendMessage(
931
975
  );
932
976
 
933
977
  // Defer event publishing to next tick so the HTTP response reaches the
934
- // client first. This ensures the client's serverToLocalSessionMap is
978
+ // client first. This ensures the client's serverToLocalConversationMap is
935
979
  // populated before SSE events arrive, preventing dropped events in new
936
980
  // desktop conversations.
937
981
  //
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Route handlers for conversation starter endpoints.
3
+ *
4
+ * GET /v1/conversation-starters — list conversation starters (chips)
5
+ */
6
+
7
+ import { and, desc, eq, inArray, like } from "drizzle-orm";
8
+
9
+ import { getDb } from "../../memory/db.js";
10
+ import { enqueueMemoryJob } from "../../memory/jobs-store.js";
11
+ import { rawGet } from "../../memory/raw-query.js";
12
+ import { conversationStarters, memoryJobs } from "../../memory/schema.js";
13
+ import type { RouteDefinition } from "../http-router.js";
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Strongest-first ordering — maximize category diversity so the top four
17
+ // chips form a coherent, non-repetitive row.
18
+ // ---------------------------------------------------------------------------
19
+
20
+ interface StarterItem {
21
+ id: string;
22
+ label: string;
23
+ prompt: string;
24
+ category: string | null;
25
+ batch: number;
26
+ }
27
+
28
+ /**
29
+ * Re-order starters so adjacent items have distinct categories wherever
30
+ * possible. Within each category, preserve the original (batch-desc) order.
31
+ * This is deterministic — same input always produces the same output.
32
+ */
33
+ export function orderStrongestFirst<T extends StarterItem>(items: T[]): T[] {
34
+ if (items.length <= 1) return items;
35
+
36
+ // Group by category, preserving original order within each group
37
+ const byCategory = new Map<string, T[]>();
38
+ for (const item of items) {
39
+ const cat = item.category ?? "other";
40
+ let group = byCategory.get(cat);
41
+ if (!group) {
42
+ group = [];
43
+ byCategory.set(cat, group);
44
+ }
45
+ group.push(item);
46
+ }
47
+
48
+ // Prefer categories with the most remaining items so the row stays varied
49
+ // early without burying the dominant themes entirely.
50
+ const sortedGroups = [...byCategory.entries()]
51
+ .sort((a, b) => b[1].length - a[1].length)
52
+ .map(([, group]) => ({ items: group, idx: 0 }));
53
+
54
+ const result: T[] = [];
55
+ const seenCategories = new Set<string>();
56
+ let lastCategory: string | null = null;
57
+
58
+ while (result.length < items.length) {
59
+ let picked = false;
60
+ const availableGroups = sortedGroups.filter(
61
+ (group) => group.idx < group.items.length,
62
+ );
63
+
64
+ const unseenGroups = availableGroups.filter((group) => {
65
+ const category = group.items[group.idx]?.category ?? "other";
66
+ return category !== lastCategory && !seenCategories.has(category);
67
+ });
68
+
69
+ const nextGroups =
70
+ unseenGroups.length > 0
71
+ ? unseenGroups
72
+ : availableGroups.filter((group) => {
73
+ const category = group.items[group.idx]?.category ?? "other";
74
+ return category !== lastCategory;
75
+ });
76
+
77
+ // First pass: prefer unseen categories, then avoid adjacent duplicates.
78
+ for (const group of nextGroups) {
79
+ if (group.idx >= group.items.length) continue;
80
+ const candidate = group.items[group.idx];
81
+ const cat = candidate.category ?? "other";
82
+ result.push(candidate);
83
+ group.idx++;
84
+ seenCategories.add(cat);
85
+ lastCategory = cat;
86
+ picked = true;
87
+ break;
88
+ }
89
+
90
+ // Fallback: if all remaining items share the same category, just pick next
91
+ if (!picked) {
92
+ for (const group of availableGroups) {
93
+ if (group.idx < group.items.length) {
94
+ const candidate = group.items[group.idx];
95
+ const cat = candidate.category ?? "other";
96
+ result.push(candidate);
97
+ group.idx++;
98
+ seenCategories.add(cat);
99
+ lastCategory = cat;
100
+ picked = true;
101
+ break;
102
+ }
103
+ }
104
+ }
105
+
106
+ if (!picked) break;
107
+ }
108
+
109
+ return result;
110
+ }
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // GET /v1/conversation-starters
114
+ // ---------------------------------------------------------------------------
115
+
116
+ function handleListConversationStarters(url: URL): Response {
117
+ const limitParam = Math.min(
118
+ Math.max(1, Number(url.searchParams.get("limit") ?? 4)),
119
+ 20,
120
+ );
121
+ const offsetParam = Math.max(0, Number(url.searchParams.get("offset") ?? 0));
122
+ const scopeId = url.searchParams.get("scope_id") ?? "default";
123
+
124
+ const db = getDb();
125
+
126
+ // Fetch chips (ranked by model, newest batch first)
127
+ const rawItems = db
128
+ .select({
129
+ id: conversationStarters.id,
130
+ label: conversationStarters.label,
131
+ prompt: conversationStarters.prompt,
132
+ category: conversationStarters.category,
133
+ batch: conversationStarters.generationBatch,
134
+ })
135
+ .from(conversationStarters)
136
+ .where(
137
+ and(
138
+ eq(conversationStarters.scopeId, scopeId),
139
+ eq(conversationStarters.cardType, "chip"),
140
+ ),
141
+ )
142
+ .orderBy(
143
+ desc(conversationStarters.generationBatch),
144
+ desc(conversationStarters.createdAt),
145
+ )
146
+ .limit(limitParam)
147
+ .offset(offsetParam)
148
+ .all();
149
+
150
+ const countRow = rawGet<{ c: number }>(
151
+ `SELECT COUNT(*) AS c FROM conversation_starters WHERE scope_id = ? AND card_type = 'chip'`,
152
+ scopeId,
153
+ );
154
+ const total = countRow?.c ?? 0;
155
+
156
+ // If starters exist, return them immediately.
157
+ if (total > 0) {
158
+ return Response.json({
159
+ starters: orderStrongestFirst(rawItems),
160
+ total,
161
+ status: "ready",
162
+ });
163
+ }
164
+
165
+ // No starters — check whether we have memory items to generate from.
166
+ const memoryCount = rawGet<{ c: number }>(
167
+ `SELECT COUNT(*) AS c FROM memory_items WHERE status = 'active' AND scope_id = ?`,
168
+ scopeId,
169
+ );
170
+
171
+ if (!memoryCount || memoryCount.c === 0) {
172
+ return Response.json({ starters: [], total: 0, status: "empty" });
173
+ }
174
+
175
+ // Memory items exist but no starters yet — ensure a generation job is queued.
176
+ const existing = db
177
+ .select({ id: memoryJobs.id })
178
+ .from(memoryJobs)
179
+ .where(
180
+ and(
181
+ eq(memoryJobs.type, "generate_conversation_starters"),
182
+ inArray(memoryJobs.status, ["pending", "running"]),
183
+ like(memoryJobs.payload, `%"scopeId":"${scopeId}"%`),
184
+ ),
185
+ )
186
+ .get();
187
+
188
+ if (!existing) {
189
+ enqueueMemoryJob("generate_conversation_starters", { scopeId });
190
+ }
191
+
192
+ return Response.json({ starters: [], total: 0, status: "generating" });
193
+ }
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // Route definitions
197
+ // ---------------------------------------------------------------------------
198
+
199
+ export function conversationStarterRouteDefinitions(): RouteDefinition[] {
200
+ return [
201
+ {
202
+ endpoint: "conversation-starters",
203
+ method: "GET",
204
+ handler: (ctx) => handleListConversationStarters(ctx.url),
205
+ },
206
+ ];
207
+ }
@@ -19,6 +19,7 @@ import { getLogger } from "../../util/logger.js";
19
19
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
20
20
  import { mintCredentialPair } from "../auth/credential-service.js";
21
21
  import { httpError } from "../http-errors.js";
22
+ import { isPrivateAddress } from "../middleware/auth.js";
22
23
 
23
24
  /** Bun server shape needed for requestIP -- avoids importing the full Bun type. */
24
25
  type ServerWithRequestIP = {
@@ -71,30 +72,33 @@ function ensureGuardianPrincipal(assistantId: string): {
71
72
  return { guardianPrincipalId, isNew: true };
72
73
  }
73
74
 
74
- /** Loopback addresses — used to gate the bootstrap endpoint to local-only. */
75
- const LOOPBACK_ADDRESSES = new Set(["127.0.0.1", "::1", "::ffff:127.0.0.1"]);
76
-
77
75
  /**
78
76
  * Handle POST /v1/guardian/init
79
77
  *
80
78
  * Body: { platform: 'macos', deviceId: string }
81
79
  * Returns: { guardianPrincipalId, accessToken, isNew }
82
80
  *
83
- * This endpoint is loopback-only (macOS local use only). iOS devices
84
- * obtain actor tokens exclusively through the QR pairing flow.
81
+ * This endpoint is restricted to private-network peers (loopback, RFC 1918,
82
+ * Docker bridge, etc.). iOS devices obtain actor tokens exclusively through
83
+ * the QR pairing flow.
85
84
  */
86
85
  export async function handleGuardianBootstrap(
87
86
  req: Request,
88
87
  server: ServerWithRequestIP,
89
88
  ): Promise<Response> {
90
- // Reject proxied requests bootstrap is local-only
91
- if (req.headers.get("x-forwarded-for") && !isHttpAuthDisabled()) {
89
+ // Reject requests forwarded from public networks. The gateway sets
90
+ // x-forwarded-for to the real client IP; if that IP is on a private
91
+ // network (loopback, Docker bridge, RFC 1918) the request is still
92
+ // considered local. Only reject when the forwarded IP is public.
93
+ const forwarded = req.headers.get("x-forwarded-for");
94
+ const forwardedIp = forwarded ? forwarded.split(",")[0].trim() : null;
95
+ if (forwardedIp && !isPrivateAddress(forwardedIp) && !isHttpAuthDisabled()) {
92
96
  return httpError("FORBIDDEN", "Bootstrap endpoint is local-only", 403);
93
97
  }
94
98
 
95
- // Reject non-loopback peers
99
+ // Reject non-private-network peers (allows loopback, Docker bridge, etc.)
96
100
  const peerIp = server.requestIP(req)?.address;
97
- if ((!peerIp || !LOOPBACK_ADDRESSES.has(peerIp)) && !isHttpAuthDisabled()) {
101
+ if ((!peerIp || !isPrivateAddress(peerIp)) && !isHttpAuthDisabled()) {
98
102
  return httpError("FORBIDDEN", "Bootstrap endpoint is local-only", 403);
99
103
  }
100
104
 
@@ -134,7 +134,7 @@ export function handleEscalationIntercept(
134
134
  void emitNotificationSignal({
135
135
  sourceEventName: "ingress.escalation",
136
136
  sourceChannel: sourceChannel as NotificationSourceChannel,
137
- sourceSessionId: conversationId,
137
+ sourceContextId: conversationId,
138
138
  attentionHints: {
139
139
  requiresAction: true,
140
140
  urgency: "high",
@@ -231,7 +231,7 @@ export async function handleVerificationIntercept(
231
231
  void emitNotificationSignal({
232
232
  sourceEventName: "ingress.trusted_contact.activated",
233
233
  sourceChannel: sourceChannel as NotificationSourceChannel,
234
- sourceSessionId: conversationId,
234
+ sourceContextId: conversationId,
235
235
  attentionHints: {
236
236
  requiresAction: false,
237
237
  urgency: "low",
@@ -11,12 +11,19 @@
11
11
  * results with is_valid flag and detailed error descriptions.
12
12
  */
13
13
 
14
+ import { join } from "node:path";
14
15
  import { Database } from "bun:sqlite";
15
16
 
16
17
  import { invalidateConfigCache } from "../../config/loader.js";
17
18
  import { resetDb } from "../../memory/db-connection.js";
19
+ import { clearCache as clearTrustCache } from "../../permissions/trust-store.js";
18
20
  import { getLogger } from "../../util/logger.js";
19
- import { getDbPath, getWorkspaceConfigPath } from "../../util/platform.js";
21
+ import {
22
+ getDbPath,
23
+ getHooksDir,
24
+ getRootDir,
25
+ getWorkspaceDir,
26
+ } from "../../util/platform.js";
20
27
  import { httpError } from "../http-errors.js";
21
28
  import type { RouteDefinition } from "../http-router.js";
22
29
  import { buildExportVBundle } from "../migrations/vbundle-builder.js";
@@ -135,13 +142,14 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
135
142
 
136
143
  try {
137
144
  const { archive, manifest } = buildExportVBundle({
138
- dbPath: getDbPath(),
139
- configPath: getWorkspaceConfigPath(),
145
+ trustPath: join(getRootDir(), "protected", "trust.json"),
146
+ hooksDir: getHooksDir(),
147
+ workspaceDir: getWorkspaceDir(),
140
148
  source: "runtime-export",
141
149
  description,
142
150
  checkpoint: () => {
151
+ const dbPath = getDbPath();
143
152
  try {
144
- const dbPath = getDbPath();
145
153
  const db = new Database(dbPath);
146
154
  try {
147
155
  db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
@@ -149,10 +157,10 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
149
157
  db.close();
150
158
  }
151
159
  } catch (err) {
152
- log.warn(
153
- { err },
154
- "WAL checkpoint failed exporting without checkpoint",
155
- );
160
+ // Best-effort: if the DB can't be checkpointed (e.g. not a valid
161
+ // SQLite file, missing WAL, etc.) we still proceed with the export
162
+ // using whatever is on disk.
163
+ log.warn({ err }, "WAL checkpoint failed — exporting without checkpoint");
156
164
  }
157
165
  },
158
166
  });
@@ -289,8 +297,9 @@ export async function handleMigrationImportPreflight(
289
297
 
290
298
  // Step 2: Analyze what would change on import
291
299
  const pathResolver = new DefaultPathResolver(
292
- getDbPath(),
293
- getWorkspaceConfigPath(),
300
+ join(getRootDir(), "protected"),
301
+ getWorkspaceDir(),
302
+ getHooksDir(),
294
303
  );
295
304
 
296
305
  const report = analyzeImport({
@@ -371,8 +380,9 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
371
380
  }
372
381
 
373
382
  const pathResolver = new DefaultPathResolver(
374
- getDbPath(),
375
- getWorkspaceConfigPath(),
383
+ join(getRootDir(), "protected"),
384
+ getWorkspaceDir(),
385
+ getHooksDir(),
376
386
  );
377
387
 
378
388
  // Close the live SQLite connection before overwriting assistant.db on disk.
@@ -384,6 +394,7 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
384
394
  pathResolver,
385
395
  preValidatedManifest: validation.manifest,
386
396
  preValidatedEntries: validation.entries,
397
+ workspaceDir: getWorkspaceDir(),
387
398
  });
388
399
 
389
400
  if (!result.ok) {
@@ -420,8 +431,9 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
420
431
  );
421
432
  }
422
433
 
423
- // Invalidate in-process config cache so imported settings.json takes effect
434
+ // Invalidate in-process caches so imported settings.json and trust.json take effect
424
435
  invalidateConfigCache();
436
+ clearTrustCache();
425
437
 
426
438
  return Response.json(result.report);
427
439
  } catch (err) {
@@ -11,6 +11,8 @@ import {
11
11
  } from "../../config/loader.js";
12
12
  import type { CesClient } from "../../credential-execution/client.js";
13
13
  import { setSentryOrganizationId } from "../../instrument.js";
14
+ import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
15
+ import { validateAnthropicApiKey } from "../../providers/anthropic/client.js";
14
16
  import { initializeProviders } from "../../providers/registry.js";
15
17
  import { credentialKey } from "../../security/credential-key.js";
16
18
  import {
@@ -129,6 +131,21 @@ export async function handleAddSecret(
129
131
  400,
130
132
  );
131
133
  }
134
+ // Validate Anthropic API keys before storing
135
+ if (name === "anthropic") {
136
+ const validation = await validateAnthropicApiKey(value);
137
+ if (!validation.valid) {
138
+ log.warn(
139
+ { provider: name, reason: validation.reason },
140
+ "API key validation failed",
141
+ );
142
+ return Response.json(
143
+ { success: false, error: validation.reason },
144
+ { status: 422 },
145
+ );
146
+ }
147
+ }
148
+
132
149
  const stored = await setSecureKeyAsync(name, value);
133
150
  if (!stored) {
134
151
  return httpError(
@@ -201,6 +218,7 @@ export async function handleAddSecret(
201
218
  );
202
219
  }
203
220
  upsertCredentialMetadata(service, field, {});
221
+ await syncManualTokenConnection(service);
204
222
  if (service === "vellum" && field === "platform_base_url") {
205
223
  setPlatformBaseUrl(effectiveValue);
206
224
  }