@vellumai/assistant 0.4.56 → 0.5.0

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 (457) 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 +204 -185
  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 +249 -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__/file-read-tool.test.ts +40 -0
  67. package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
  68. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  69. package/src/__tests__/gemini-image-service.test.ts +4 -4
  70. package/src/__tests__/gemini-provider.test.ts +6 -9
  71. package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
  72. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  73. package/src/__tests__/host-file-read-tool.test.ts +87 -0
  74. package/src/__tests__/host-shell-tool.test.ts +6 -6
  75. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  76. package/src/__tests__/identity-intro-cache.test.ts +209 -0
  77. package/src/__tests__/intent-routing.test.ts +51 -99
  78. package/src/__tests__/invite-routes-http.test.ts +5 -0
  79. package/src/__tests__/list-messages-attachments.test.ts +1 -1
  80. package/src/__tests__/managed-proxy-context.test.ts +2 -5
  81. package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
  82. package/src/__tests__/media-generate-image.test.ts +32 -15
  83. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  84. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
  85. package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
  86. package/src/__tests__/memory-recall-quality.test.ts +4 -3
  87. package/src/__tests__/memory-regressions.test.ts +86 -90
  88. package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
  89. package/src/__tests__/migration-export-http.test.ts +26 -27
  90. package/src/__tests__/migration-import-commit-http.test.ts +165 -37
  91. package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
  92. package/src/__tests__/migration-validate-http.test.ts +16 -16
  93. package/src/__tests__/model-intents.test.ts +2 -2
  94. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
  95. package/src/__tests__/non-member-access-request.test.ts +3 -3
  96. package/src/__tests__/notification-broadcaster.test.ts +1 -1
  97. package/src/__tests__/notification-decision-fallback.test.ts +2 -2
  98. package/src/__tests__/notification-decision-identity.test.ts +8 -9
  99. package/src/__tests__/notification-decision-strategy.test.ts +1 -1
  100. package/src/__tests__/notification-deep-link.test.ts +1 -1
  101. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  102. package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
  103. package/src/__tests__/oauth-store.test.ts +1 -3
  104. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
  105. package/src/__tests__/onboarding-template-contract.test.ts +23 -59
  106. package/src/__tests__/provider-error-scenarios.test.ts +154 -0
  107. package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
  108. package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
  109. package/src/__tests__/provider-registry-ollama.test.ts +5 -2
  110. package/src/__tests__/qdrant-manager.test.ts +7 -7
  111. package/src/__tests__/ratelimit.test.ts +0 -74
  112. package/src/__tests__/recording-handler.test.ts +0 -1
  113. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  114. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  115. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  116. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  117. package/src/__tests__/scheduler-recurrence.test.ts +46 -2
  118. package/src/__tests__/schema-transforms.test.ts +114 -54
  119. package/src/__tests__/secret-onetime-send.test.ts +20 -0
  120. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
  121. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  122. package/src/__tests__/send-endpoint-busy.test.ts +63 -4
  123. package/src/__tests__/send-notification-tool.test.ts +2 -2
  124. package/src/__tests__/shell-credential-ref.test.ts +0 -1
  125. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
  126. package/src/__tests__/skill-memory.test.ts +549 -0
  127. package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
  128. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
  129. package/src/__tests__/slack-channel-config.test.ts +109 -94
  130. package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
  131. package/src/__tests__/swarm-recursion.test.ts +2 -2
  132. package/src/__tests__/swarm-tool.test.ts +2 -2
  133. package/src/__tests__/system-prompt.test.ts +19 -66
  134. package/src/__tests__/telegram-config.test.ts +121 -0
  135. package/src/__tests__/terminal-tools.test.ts +1 -1
  136. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
  137. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  138. package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
  139. package/src/__tests__/tool-executor.test.ts +1 -1
  140. package/src/__tests__/trace-emitter.test.ts +8 -1
  141. package/src/__tests__/trust-store.test.ts +7 -8
  142. package/src/__tests__/twilio-routes.test.ts +1 -18
  143. package/src/__tests__/user-reference.test.ts +82 -2
  144. package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
  145. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  146. package/src/approvals/guardian-request-resolvers.ts +3 -3
  147. package/src/avatar/ascii-renderer.ts +2 -2
  148. package/src/avatar/png-renderer.ts +2 -2
  149. package/src/avatar/resvg-lazy.ts +21 -0
  150. package/src/calls/guardian-dispatch.ts +1 -1
  151. package/src/calls/relay-access-wait.ts +2 -2
  152. package/src/calls/twilio-rest.ts +0 -248
  153. package/src/cli/AGENTS.md +5 -8
  154. package/src/cli/__tests__/notifications.test.ts +5 -5
  155. package/src/cli/commands/avatar.ts +64 -2
  156. package/src/cli/commands/conversations.ts +131 -1
  157. package/src/cli/commands/credentials.ts +2 -0
  158. package/src/cli/commands/notifications.ts +3 -3
  159. package/src/cli.ts +10 -0
  160. package/src/config/bundled-skills/acp/SKILL.md +5 -5
  161. package/src/config/bundled-skills/acp/TOOLS.json +6 -6
  162. package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
  163. package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
  164. package/src/config/bundled-skills/browser/SKILL.md +15 -15
  165. package/src/config/bundled-skills/browser/TOOLS.json +14 -14
  166. package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
  167. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
  168. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  169. package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
  170. package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
  171. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
  172. package/src/config/bundled-skills/contacts/SKILL.md +3 -3
  173. package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
  174. package/src/config/bundled-skills/document/SKILL.md +4 -4
  175. package/src/config/bundled-skills/document/TOOLS.json +2 -2
  176. package/src/config/bundled-skills/followups/TOOLS.json +3 -3
  177. package/src/config/bundled-skills/gmail/SKILL.md +32 -32
  178. package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
  179. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
  180. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  181. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
  182. package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
  183. package/src/config/bundled-skills/google-calendar/types.ts +1 -1
  184. package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
  185. package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
  186. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
  187. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
  188. package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
  189. package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
  190. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
  191. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
  192. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
  193. package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
  194. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
  195. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
  196. package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
  197. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  198. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
  199. package/src/config/bundled-skills/messaging/SKILL.md +29 -25
  200. package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
  201. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  202. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  203. package/src/config/bundled-skills/notifications/SKILL.md +3 -3
  204. package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
  205. package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
  206. package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
  207. package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
  208. package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
  209. package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
  210. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
  211. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
  212. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
  213. package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
  214. package/src/config/bundled-skills/schedule/SKILL.md +26 -26
  215. package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
  216. package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
  217. package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
  218. package/src/config/bundled-skills/sequences/SKILL.md +2 -2
  219. package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
  220. package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
  221. package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
  222. package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
  223. package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
  224. package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
  225. package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
  226. package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
  227. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  228. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
  229. package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
  230. package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
  231. package/src/config/bundled-skills/slack/SKILL.md +2 -2
  232. package/src/config/bundled-skills/slack/TOOLS.json +8 -8
  233. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
  234. package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
  235. package/src/config/bundled-skills/tasks/SKILL.md +1 -1
  236. package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
  237. package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
  238. package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
  240. package/src/config/bundled-skills/watcher/SKILL.md +4 -4
  241. package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
  242. package/src/config/feature-flag-registry.json +33 -17
  243. package/src/config/schemas/sandbox.ts +1 -1
  244. package/src/config/schemas/services.ts +13 -3
  245. package/src/config/schemas/timeouts.ts +0 -10
  246. package/src/contacts/contact-store.ts +63 -0
  247. package/src/contacts/contacts-write.ts +1 -1
  248. package/src/daemon/assistant-attachments.ts +2 -2
  249. package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
  250. package/src/daemon/conversation-agent-loop.ts +7 -30
  251. package/src/daemon/conversation-error.ts +24 -0
  252. package/src/daemon/conversation-memory.ts +8 -7
  253. package/src/daemon/conversation-runtime-assembly.ts +141 -275
  254. package/src/daemon/conversation-slash.ts +7 -26
  255. package/src/daemon/conversation-surfaces.ts +14 -0
  256. package/src/daemon/conversation-tool-setup.ts +9 -8
  257. package/src/daemon/conversation.ts +2 -0
  258. package/src/daemon/daemon-control.ts +1 -1
  259. package/src/daemon/date-context.ts +10 -83
  260. package/src/daemon/handlers/config-channels.ts +12 -2
  261. package/src/daemon/handlers/config-slack-channel.ts +7 -1
  262. package/src/daemon/handlers/config-telegram.ts +6 -1
  263. package/src/daemon/handlers/conversations.ts +2 -2
  264. package/src/daemon/handlers/skills.ts +4 -0
  265. package/src/daemon/lifecycle.ts +28 -4
  266. package/src/daemon/providers-setup.ts +1 -1
  267. package/src/daemon/server.ts +1 -5
  268. package/src/daemon/shutdown-handlers.ts +9 -3
  269. package/src/daemon/tool-side-effects.ts +40 -0
  270. package/src/daemon/trace-emitter.ts +26 -2
  271. package/src/events/domain-events.ts +1 -1
  272. package/src/events/tool-permission-telemetry-listener.ts +46 -0
  273. package/src/inbound/platform-callback-registration.ts +0 -18
  274. package/src/media/app-icon-generator.ts +15 -8
  275. package/src/media/avatar-router.ts +15 -8
  276. package/src/media/gemini-image-service.ts +125 -21
  277. package/src/memory/attachments-store.ts +3 -3
  278. package/src/memory/channel-verification-sessions.ts +6 -6
  279. package/src/memory/conversation-crud.ts +196 -1
  280. package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
  281. package/src/memory/conversation-title-service.ts +2 -3
  282. package/src/memory/db-init.ts +25 -1
  283. package/src/memory/invite-store.ts +4 -4
  284. package/src/memory/items-extractor.ts +4 -4
  285. package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
  286. package/src/memory/jobs-store.ts +3 -2
  287. package/src/memory/jobs-worker.ts +7 -5
  288. package/src/memory/lifecycle-events-store.ts +63 -0
  289. package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
  290. package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
  291. package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
  292. package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
  293. package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
  294. package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
  295. package/src/memory/migrations/index.ts +6 -0
  296. package/src/memory/migrations/registry.ts +13 -0
  297. package/src/memory/retriever.test.ts +223 -96
  298. package/src/memory/retriever.ts +115 -138
  299. package/src/memory/schema/calls.ts +1 -1
  300. package/src/memory/schema/contacts.ts +1 -1
  301. package/src/memory/schema/infrastructure.ts +29 -0
  302. package/src/memory/schema/memory-core.ts +7 -17
  303. package/src/memory/schema/notifications.ts +1 -1
  304. package/src/memory/search/formatting.ts +23 -6
  305. package/src/memory/search/lexical.ts +2 -0
  306. package/src/memory/search/semantic.ts +2 -0
  307. package/src/memory/search/staleness.ts +5 -1
  308. package/src/memory/search/types.ts +4 -0
  309. package/src/memory/task-memory-cleanup.ts +96 -6
  310. package/src/memory/trace-event-store.ts +148 -0
  311. package/src/notifications/README.md +1 -1
  312. package/src/notifications/decision-engine.ts +45 -4
  313. package/src/notifications/emit-signal.ts +5 -4
  314. package/src/notifications/events-store.ts +4 -4
  315. package/src/notifications/signal.ts +1 -1
  316. package/src/oauth/manual-token-connection.ts +49 -25
  317. package/src/permissions/checker.ts +6 -5
  318. package/src/permissions/defaults.ts +4 -4
  319. package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
  320. package/src/prompts/cache-boundary.ts +8 -0
  321. package/src/prompts/system-prompt.ts +105 -634
  322. package/src/prompts/templates/BOOTSTRAP.md +172 -33
  323. package/src/prompts/templates/IDENTITY.md +8 -24
  324. package/src/prompts/templates/SOUL.md +20 -41
  325. package/src/prompts/templates/USER.md +3 -19
  326. package/src/prompts/user-reference.ts +14 -16
  327. package/src/providers/anthropic/client.ts +51 -19
  328. package/src/providers/gemini/client.ts +6 -9
  329. package/src/providers/managed-proxy/constants.ts +1 -7
  330. package/src/providers/managed-proxy/context.ts +0 -1
  331. package/src/providers/model-intents.ts +5 -5
  332. package/src/providers/openai/client.ts +10 -1
  333. package/src/providers/openrouter/client.ts +1 -0
  334. package/src/providers/ratelimit.ts +0 -35
  335. package/src/providers/registry.ts +3 -5
  336. package/src/providers/retry.ts +18 -1
  337. package/src/runtime/access-request-helper.ts +16 -2
  338. package/src/runtime/auth/route-policy.ts +7 -0
  339. package/src/runtime/channel-verification-service.ts +1 -1
  340. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  341. package/src/runtime/guardian-vellum-migration.ts +61 -1
  342. package/src/runtime/http-server.ts +8 -4
  343. package/src/runtime/migrations/vbundle-builder.ts +212 -32
  344. package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
  345. package/src/runtime/migrations/vbundle-importer.ts +66 -1
  346. package/src/runtime/migrations/vbundle-validator.ts +17 -3
  347. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
  348. package/src/runtime/routes/attachment-routes.ts +2 -2
  349. package/src/runtime/routes/btw-routes.ts +93 -0
  350. package/src/runtime/routes/channel-verification-routes.ts +19 -2
  351. package/src/runtime/routes/conversation-management-routes.ts +55 -1
  352. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  353. package/src/runtime/routes/conversation-routes.ts +49 -5
  354. package/src/runtime/routes/conversation-starter-routes.ts +207 -0
  355. package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
  356. package/src/runtime/routes/identity-intro-cache.ts +105 -0
  357. package/src/runtime/routes/identity-routes.ts +51 -0
  358. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
  359. package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
  360. package/src/runtime/routes/migration-routes.ts +25 -13
  361. package/src/runtime/routes/secret-routes.ts +18 -0
  362. package/src/runtime/routes/settings-routes.ts +9 -9
  363. package/src/runtime/routes/telemetry-routes.ts +53 -0
  364. package/src/runtime/routes/trace-event-routes.ts +62 -0
  365. package/src/runtime/tool-grant-request-helper.ts +1 -1
  366. package/src/runtime/verification-outbound-actions.ts +47 -31
  367. package/src/security/encrypted-store.ts +262 -78
  368. package/src/skills/catalog-install.ts +10 -0
  369. package/src/skills/managed-store.ts +2 -0
  370. package/src/skills/skill-memory.ts +222 -0
  371. package/src/subagent/manager.ts +1 -4
  372. package/src/telemetry/types.ts +10 -1
  373. package/src/telemetry/usage-telemetry-reporter.test.ts +7 -2
  374. package/src/telemetry/usage-telemetry-reporter.ts +53 -4
  375. package/src/tools/AGENTS.md +11 -11
  376. package/src/tools/acp/spawn.ts +1 -1
  377. package/src/tools/apps/executors.ts +8 -8
  378. package/src/tools/apps/registry.ts +1 -1
  379. package/src/tools/assets/materialize.ts +6 -6
  380. package/src/tools/assets/search.ts +10 -10
  381. package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
  382. package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
  383. package/src/tools/browser/auth-detector.ts +6 -6
  384. package/src/tools/browser/browser-execution.ts +13 -13
  385. package/src/tools/browser/browser-manager.ts +3 -3
  386. package/src/tools/browser/chrome-cdp.ts +5 -5
  387. package/src/tools/browser/jit-auth.ts +2 -2
  388. package/src/tools/browser/network-recorder.test.ts +2 -2
  389. package/src/tools/browser/network-recorder.ts +3 -3
  390. package/src/tools/browser/runtime-check.ts +3 -3
  391. package/src/tools/claude-code/claude-code.ts +2 -2
  392. package/src/tools/computer-use/definitions.ts +18 -18
  393. package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
  394. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
  395. package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
  396. package/src/tools/credentials/broker-types.ts +5 -5
  397. package/src/tools/credentials/broker.ts +15 -15
  398. package/src/tools/credentials/metadata-store.ts +2 -2
  399. package/src/tools/credentials/resolve.ts +1 -1
  400. package/src/tools/credentials/selection.ts +1 -1
  401. package/src/tools/credentials/tool-policy.ts +1 -1
  402. package/src/tools/credentials/vault.ts +115 -25
  403. package/src/tools/execution-target.ts +2 -2
  404. package/src/tools/executor.ts +7 -7
  405. package/src/tools/filesystem/edit.ts +2 -2
  406. package/src/tools/filesystem/read.ts +15 -4
  407. package/src/tools/filesystem/write.ts +1 -1
  408. package/src/tools/host-filesystem/edit.ts +2 -1
  409. package/src/tools/host-filesystem/read.ts +18 -1
  410. package/src/tools/host-filesystem/write.ts +1 -1
  411. package/src/tools/host-terminal/host-shell.ts +9 -8
  412. package/src/tools/mcp/mcp-tool-factory.ts +7 -6
  413. package/src/tools/memory/definitions.ts +6 -5
  414. package/src/tools/memory/handlers.test.ts +1 -1
  415. package/src/tools/network/__tests__/web-search.test.ts +3 -3
  416. package/src/tools/network/domain-normalize.ts +2 -2
  417. package/src/tools/network/script-proxy/session-manager.ts +10 -10
  418. package/src/tools/network/web-fetch.ts +1 -1
  419. package/src/tools/network/web-search.ts +3 -3
  420. package/src/tools/permission-checker.ts +8 -8
  421. package/src/tools/registry.ts +7 -7
  422. package/src/tools/schedule/list.ts +2 -2
  423. package/src/tools/schema-transforms.ts +31 -21
  424. package/src/tools/secret-detection-handler.ts +1 -1
  425. package/src/tools/sensitive-output-placeholders.ts +1 -1
  426. package/src/tools/shared/filesystem/edit-engine.ts +1 -1
  427. package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
  428. package/src/tools/shared/filesystem/image-read.ts +25 -5
  429. package/src/tools/shared/filesystem/path-policy.ts +2 -2
  430. package/src/tools/shared/shell-output.ts +1 -1
  431. package/src/tools/side-effects.ts +1 -1
  432. package/src/tools/skills/execute.ts +1 -1
  433. package/src/tools/skills/load.ts +3 -3
  434. package/src/tools/skills/sandbox-runner.ts +3 -3
  435. package/src/tools/subagent/read.ts +1 -1
  436. package/src/tools/subagent/spawn.ts +2 -2
  437. package/src/tools/swarm/delegate.ts +3 -3
  438. package/src/tools/system/request-permission.ts +5 -4
  439. package/src/tools/terminal/backends/native.ts +4 -4
  440. package/src/tools/terminal/parser.ts +6 -6
  441. package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
  442. package/src/tools/terminal/shell.ts +16 -16
  443. package/src/tools/tool-approval-handler.ts +21 -12
  444. package/src/tools/tool-manifest.ts +4 -4
  445. package/src/tools/types.ts +3 -3
  446. package/src/tools/ui-surface/definitions.ts +9 -37
  447. package/src/tools/watcher/list.ts +1 -1
  448. package/src/util/logger.ts +7 -2
  449. package/src/util/pricing.ts +4 -0
  450. package/src/util/retry.ts +29 -1
  451. package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
  452. package/src/workspace/migrations/registry.ts +2 -0
  453. package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
  454. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
  455. package/src/cli/reference.ts +0 -38
  456. package/src/memory/job-handlers/capability-cards.ts +0 -420
  457. package/src/runtime/routes/thread-starter-routes.ts +0 -294
@@ -48,13 +48,14 @@ function toArrayBuffer(data: Uint8Array): ArrayBuffer {
48
48
  const testDir = realpathSync(
49
49
  mkdtempSync(join(tmpdir(), "migration-import-commit-http-test-")),
50
50
  );
51
- const testDbDir = join(testDir, "db");
51
+ const testDbDir = join(testDir, "data", "db");
52
52
  const testDbPath = join(testDbDir, "assistant.db");
53
53
  const testConfigPath = join(testDir, "config.json");
54
54
 
55
55
  mock.module("../util/platform.js", () => ({
56
56
  getRootDir: () => testDir,
57
- getDataDir: () => testDir,
57
+ getDataDir: () => join(testDir, "data"),
58
+ getWorkspaceDir: () => testDir,
58
59
  getWorkspaceConfigPath: () => testConfigPath,
59
60
  isMacOS: () => process.platform === "darwin",
60
61
  isLinux: () => process.platform === "linux",
@@ -78,9 +79,8 @@ mock.module("../config/loader.js", () => ({
78
79
  model: "test",
79
80
  provider: "test",
80
81
  memory: { enabled: false },
81
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
82
+ rateLimit: { maxRequestsPerMinute: 0 },
82
83
  secretDetection: { enabled: false },
83
- sandbox: { enabled: false },
84
84
  }),
85
85
  }));
86
86
 
@@ -377,10 +377,13 @@ describe("handleMigrationImport", () => {
377
377
  expect(writtenData).toEqual(newDbData);
378
378
  });
379
379
 
380
- test("creates backup of existing files before overwriting", async () => {
380
+ test("workspace is cleared before restore — files are created fresh", async () => {
381
+ // Only new-format bundles (workspace/ prefix) trigger workspace clearing.
382
+ // With clearing, existing files are removed before writing, so all files
383
+ // in the bundle are "created" (not "overwritten") and no backups are made.
381
384
  const newDbData = new Uint8Array([0x01, 0x02, 0x03]);
382
385
  const vbundle = createValidVBundle([
383
- { path: "data/db/assistant.db", data: newDbData },
386
+ { path: "workspace/data/db/assistant.db", data: newDbData },
384
387
  ]);
385
388
  const req = new Request("http://localhost/v1/migrations/import", {
386
389
  method: "POST",
@@ -392,29 +395,22 @@ describe("handleMigrationImport", () => {
392
395
  const body = (await res.json()) as ImportCommitResponse;
393
396
 
394
397
  expect(body.success).toBe(true);
395
- expect(body.summary.backups_created).toBeGreaterThan(0);
398
+ expect(body.summary.backups_created).toBe(0);
396
399
 
397
- // Verify backup was created
398
- const dbFile = body.files.find((f) => f.path === "data/db/assistant.db");
400
+ const dbFile = body.files.find(
401
+ (f) => f.path === "workspace/data/db/assistant.db",
402
+ );
399
403
  expect(dbFile).toBeDefined();
400
- expect(dbFile!.action).toBe("overwritten");
401
- expect(dbFile!.backup_path).not.toBeNull();
402
- expect(existsSync(dbFile!.backup_path!)).toBe(true);
403
-
404
- // Verify backup contains the original data
405
- const backupData = new Uint8Array(readFileSync(dbFile!.backup_path!));
406
- expect(backupData).toEqual(EXISTING_DB_DATA);
404
+ expect(dbFile!.action).toBe("created");
407
405
  });
408
406
 
409
- test("reports correct actions for created and overwritten files", async () => {
410
- // Remove the config file so it will be "created" instead of "overwritten"
411
- rmSync(testConfigPath, { force: true });
412
-
407
+ test("reports all files as created after workspace clear", async () => {
408
+ // New-format workspace/ paths trigger clearing both files are fresh.
413
409
  const newDbData = new Uint8Array([0xaa, 0xbb]);
414
410
  const newConfigData = new TextEncoder().encode('{"provider":"openai"}');
415
411
  const vbundle = createValidVBundle([
416
- { path: "data/db/assistant.db", data: newDbData },
417
- { path: "config/settings.json", data: newConfigData },
412
+ { path: "workspace/data/db/assistant.db", data: newDbData },
413
+ { path: "workspace/config.json", data: newConfigData },
418
414
  ]);
419
415
  const req = new Request("http://localhost/v1/migrations/import", {
420
416
  method: "POST",
@@ -427,14 +423,16 @@ describe("handleMigrationImport", () => {
427
423
 
428
424
  expect(body.success).toBe(true);
429
425
 
430
- const dbFile = body.files.find((f) => f.path === "data/db/assistant.db");
426
+ const dbFile = body.files.find(
427
+ (f) => f.path === "workspace/data/db/assistant.db",
428
+ );
431
429
  const configFile = body.files.find(
432
- (f) => f.path === "config/settings.json",
430
+ (f) => f.path === "workspace/config.json",
433
431
  );
434
432
 
435
- expect(dbFile!.action).toBe("overwritten");
433
+ // Both are "created" because workspace was cleared before writing
434
+ expect(dbFile!.action).toBe("created");
436
435
  expect(configFile!.action).toBe("created");
437
- expect(configFile!.backup_path).toBeNull();
438
436
  });
439
437
 
440
438
  test("summary counts match file details", async () => {
@@ -641,7 +639,7 @@ describe("commitImport", () => {
641
639
  { path: "data/db/assistant.db", data: newDbData },
642
640
  ]);
643
641
 
644
- const resolver = new DefaultPathResolver(testDbPath, testConfigPath);
642
+ const resolver = new DefaultPathResolver(undefined, testDir);
645
643
  const result = commitImport({
646
644
  archiveData: vbundle,
647
645
  pathResolver: resolver,
@@ -655,7 +653,7 @@ describe("commitImport", () => {
655
653
  });
656
654
 
657
655
  test("returns validation_failed for invalid bundles", () => {
658
- const resolver = new DefaultPathResolver(testDbPath, testConfigPath);
656
+ const resolver = new DefaultPathResolver(undefined, testDir);
659
657
  const result = commitImport({
660
658
  archiveData: new Uint8Array([0xba, 0xad]),
661
659
  pathResolver: resolver,
@@ -671,17 +669,16 @@ describe("commitImport", () => {
671
669
  });
672
670
 
673
671
  test("creates parent directories if they do not exist", () => {
674
- // Use a path that does not exist yet
675
- const nonexistentDir = join(testDir, "new-subdir");
676
- const newDbPath = join(nonexistentDir, "assistant.db");
677
- const newConfigPath = join(testDir, "new-config.json");
672
+ // Use a workspace that does not exist yet
673
+ const nonexistentWorkspace = join(testDir, "new-workspace");
674
+ const expectedDbPath = join(nonexistentWorkspace, "data", "db", "assistant.db");
678
675
 
679
676
  const dbData = new Uint8Array([0x01, 0x02, 0x03]);
680
677
  const vbundle = createValidVBundle([
681
678
  { path: "data/db/assistant.db", data: dbData },
682
679
  ]);
683
680
 
684
- const resolver = new DefaultPathResolver(newDbPath, newConfigPath);
681
+ const resolver = new DefaultPathResolver(undefined, nonexistentWorkspace);
685
682
  const result = commitImport({
686
683
  archiveData: vbundle,
687
684
  pathResolver: resolver,
@@ -690,11 +687,11 @@ describe("commitImport", () => {
690
687
  expect(result.ok).toBe(true);
691
688
  if (result.ok) {
692
689
  expect(result.report.files[0].action).toBe("created");
693
- expect(existsSync(newDbPath)).toBe(true);
690
+ expect(existsSync(expectedDbPath)).toBe(true);
694
691
  }
695
692
 
696
693
  // Clean up
697
- rmSync(nonexistentDir, { recursive: true, force: true });
694
+ rmSync(nonexistentWorkspace, { recursive: true, force: true });
698
695
  });
699
696
 
700
697
  test("post-write integrity: written file SHA-256 matches expected", () => {
@@ -705,7 +702,7 @@ describe("commitImport", () => {
705
702
  { path: "data/db/assistant.db", data: newDbData },
706
703
  ]);
707
704
 
708
- const resolver = new DefaultPathResolver(testDbPath, testConfigPath);
705
+ const resolver = new DefaultPathResolver(undefined, testDir);
709
706
  const result = commitImport({
710
707
  archiveData: vbundle,
711
708
  pathResolver: resolver,
@@ -735,7 +732,7 @@ describe("commitImport", () => {
735
732
  { path: "config/settings.json", data: newConfigData },
736
733
  ]);
737
734
 
738
- const resolver = new DefaultPathResolver(testDbPath, testConfigPath);
735
+ const resolver = new DefaultPathResolver(undefined, testDir);
739
736
  const result = commitImport({
740
737
  archiveData: vbundle,
741
738
  pathResolver: resolver,
@@ -755,6 +752,137 @@ describe("commitImport", () => {
755
752
  });
756
753
  });
757
754
 
755
+ // ---------------------------------------------------------------------------
756
+ // Workspace clearing tests
757
+ // ---------------------------------------------------------------------------
758
+
759
+ describe("commitImport — workspace clearing", () => {
760
+ const skillsDir = join(testDir, "skills");
761
+ const hooksDir = join(testDir, "hooks");
762
+
763
+ afterEach(() => {
764
+ // Restore test fixture files for subsequent tests
765
+ mkdirSync(testDbDir, { recursive: true });
766
+ writeFileSync(testDbPath, EXISTING_DB_DATA);
767
+ writeFileSync(testConfigPath, JSON.stringify(EXISTING_CONFIG, null, 2));
768
+ // Clean up skills/hooks dirs
769
+ for (const dir of [skillsDir, hooksDir]) {
770
+ if (existsSync(dir)) rmSync(dir, { recursive: true, force: true });
771
+ }
772
+ });
773
+
774
+ test("clears stale files via workspace clearing (new-format workspace/ entries)", () => {
775
+ mkdirSync(join(skillsDir, "stale-skill"), { recursive: true });
776
+ writeFileSync(join(skillsDir, "stale-skill", "SKILL.md"), "stale");
777
+
778
+ const skillData = new TextEncoder().encode("# New Skill");
779
+ const vbundle = createValidVBundle([
780
+ { path: "workspace/skills/new-skill/SKILL.md", data: skillData },
781
+ ]);
782
+
783
+ const resolver = new DefaultPathResolver(undefined, testDir);
784
+ const result = commitImport({
785
+ archiveData: vbundle,
786
+ pathResolver: resolver,
787
+ workspaceDir: testDir,
788
+ });
789
+
790
+ expect(result.ok).toBe(true);
791
+ if (!result.ok) return;
792
+
793
+ // New skill written
794
+ expect(existsSync(join(skillsDir, "new-skill", "SKILL.md"))).toBe(true);
795
+ expect(readFileSync(join(skillsDir, "new-skill", "SKILL.md"), "utf8")).toBe(
796
+ "# New Skill",
797
+ );
798
+
799
+ // Stale skill removed by workspace clearing
800
+ expect(existsSync(join(skillsDir, "stale-skill"))).toBe(false);
801
+ });
802
+
803
+ test("old-format skills/ entries do not trigger workspace clearing", () => {
804
+ mkdirSync(join(skillsDir, "stale-skill"), { recursive: true });
805
+ writeFileSync(join(skillsDir, "stale-skill", "SKILL.md"), "stale");
806
+
807
+ const skillData = new TextEncoder().encode("# New Skill");
808
+ const vbundle = createValidVBundle([
809
+ { path: "skills/new-skill/SKILL.md", data: skillData },
810
+ ]);
811
+
812
+ const resolver = new DefaultPathResolver(undefined, testDir);
813
+ const result = commitImport({
814
+ archiveData: vbundle,
815
+ pathResolver: resolver,
816
+ workspaceDir: testDir,
817
+ });
818
+
819
+ expect(result.ok).toBe(true);
820
+ if (!result.ok) return;
821
+
822
+ // New skill written
823
+ expect(existsSync(join(skillsDir, "new-skill", "SKILL.md"))).toBe(true);
824
+
825
+ // Stale skill survives — old-format bundles don't trigger workspace clearing
826
+ expect(existsSync(join(skillsDir, "stale-skill", "SKILL.md"))).toBe(true);
827
+ });
828
+
829
+ test("hooks/ entries import to hooksDir (not workspace/hooks/)", () => {
830
+ // Use a separate hooks dir outside the workspace, like production layout
831
+ const externalHooksDir = join(testDir, ".hooks-external");
832
+ mkdirSync(externalHooksDir, { recursive: true });
833
+
834
+ const hookData = new TextEncoder().encode("#!/bin/sh\necho new");
835
+ const vbundle = createValidVBundle([
836
+ { path: "hooks/new-hook/hook.sh", data: hookData },
837
+ ]);
838
+
839
+ const resolver = new DefaultPathResolver(undefined, testDir, externalHooksDir);
840
+ const result = commitImport({
841
+ archiveData: vbundle,
842
+ pathResolver: resolver,
843
+ workspaceDir: testDir,
844
+ });
845
+
846
+ expect(result.ok).toBe(true);
847
+ if (!result.ok) return;
848
+
849
+ // Hook written to the external hooks dir, not workspace/hooks/
850
+ expect(existsSync(join(externalHooksDir, "new-hook", "hook.sh"))).toBe(true);
851
+ expect(
852
+ readFileSync(join(externalHooksDir, "new-hook", "hook.sh"), "utf8"),
853
+ ).toBe("#!/bin/sh\necho new");
854
+
855
+ // Cleanup
856
+ rmSync(externalHooksDir, { recursive: true, force: true });
857
+ });
858
+
859
+ test("without workspaceDir, no clearing happens", () => {
860
+ mkdirSync(join(skillsDir, "existing-skill"), { recursive: true });
861
+ writeFileSync(
862
+ join(skillsDir, "existing-skill", "SKILL.md"),
863
+ "should survive",
864
+ );
865
+
866
+ const vbundle = createValidVBundle([
867
+ { path: "skills/new-skill/SKILL.md", data: new TextEncoder().encode("new") },
868
+ ]);
869
+
870
+ const resolver = new DefaultPathResolver(undefined, testDir);
871
+ // No workspaceDir — no clearing
872
+ const result = commitImport({
873
+ archiveData: vbundle,
874
+ pathResolver: resolver,
875
+ });
876
+
877
+ expect(result.ok).toBe(true);
878
+
879
+ // Existing skill survives (no workspace clearing without workspaceDir)
880
+ expect(existsSync(join(skillsDir, "existing-skill", "SKILL.md"))).toBe(
881
+ true,
882
+ );
883
+ });
884
+ });
885
+
758
886
  // ---------------------------------------------------------------------------
759
887
  // Auth policy registration tests
760
888
  // ---------------------------------------------------------------------------
@@ -34,13 +34,14 @@ function toArrayBuffer(data: Uint8Array): ArrayBuffer {
34
34
  const testDir = realpathSync(
35
35
  mkdtempSync(join(tmpdir(), "migration-import-preflight-http-test-")),
36
36
  );
37
- const testDbDir = join(testDir, "db");
37
+ const testDbDir = join(testDir, "data", "db");
38
38
  const testDbPath = join(testDbDir, "assistant.db");
39
39
  const testConfigPath = join(testDir, "config.json");
40
40
 
41
41
  mock.module("../util/platform.js", () => ({
42
42
  getRootDir: () => testDir,
43
- getDataDir: () => testDir,
43
+ getDataDir: () => join(testDir, "data"),
44
+ getWorkspaceDir: () => testDir,
44
45
  getWorkspaceConfigPath: () => testConfigPath,
45
46
  isMacOS: () => process.platform === "darwin",
46
47
  isLinux: () => process.platform === "linux",
@@ -64,9 +65,8 @@ mock.module("../config/loader.js", () => ({
64
65
  model: "test",
65
66
  provider: "test",
66
67
  memory: { enabled: false },
67
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
68
+ rateLimit: { maxRequestsPerMinute: 0 },
68
69
  secretDetection: { enabled: false },
69
- sandbox: { enabled: false },
70
70
  }),
71
71
  }));
72
72
 
@@ -525,8 +525,8 @@ describe("handleMigrationImportPreflight — validation failures", () => {
525
525
  describe("analyzeImport", () => {
526
526
  test("detects create when file does not exist on disk", () => {
527
527
  const resolver = new DefaultPathResolver(
528
- join(testDir, "nonexistent.db"),
529
- join(testDir, "nonexistent-config.json"),
528
+ undefined,
529
+ join(testDir, "nonexistent-workspace"),
530
530
  );
531
531
 
532
532
  const report = analyzeImport({
@@ -553,7 +553,7 @@ describe("analyzeImport", () => {
553
553
  });
554
554
 
555
555
  test("detects unchanged when file on disk matches bundle", () => {
556
- const resolver = new DefaultPathResolver(testDbPath, testConfigPath);
556
+ const resolver = new DefaultPathResolver(undefined, testDir);
557
557
 
558
558
  const report = analyzeImport({
559
559
  manifest: {
@@ -577,7 +577,7 @@ describe("analyzeImport", () => {
577
577
  });
578
578
 
579
579
  test("detects overwrite when file on disk differs from bundle", () => {
580
- const resolver = new DefaultPathResolver(testDbPath, testConfigPath);
580
+ const resolver = new DefaultPathResolver(undefined, testDir);
581
581
 
582
582
  const report = analyzeImport({
583
583
  manifest: {
@@ -602,7 +602,7 @@ describe("analyzeImport", () => {
602
602
  });
603
603
 
604
604
  test("flags unknown archive paths as conflicts with skip action", () => {
605
- const resolver = new DefaultPathResolver(testDbPath, testConfigPath);
605
+ const resolver = new DefaultPathResolver(undefined, testDir);
606
606
 
607
607
  const report = analyzeImport({
608
608
  manifest: {
@@ -640,7 +640,7 @@ describe("analyzeImport", () => {
640
640
  });
641
641
 
642
642
  test("includes manifest in report", () => {
643
- const resolver = new DefaultPathResolver(testDbPath, testConfigPath);
643
+ const resolver = new DefaultPathResolver(undefined, testDir);
644
644
  const manifest = {
645
645
  schema_version: "1.0",
646
646
  created_at: "2024-01-01T00:00:00.000Z",
@@ -668,29 +668,90 @@ describe("analyzeImport", () => {
668
668
  // ---------------------------------------------------------------------------
669
669
 
670
670
  describe("DefaultPathResolver", () => {
671
- test("resolves data/db/assistant.db to configured db path", () => {
671
+ test("resolves data/db/assistant.db to workspace db path (backward compat)", () => {
672
672
  const resolver = new DefaultPathResolver(
673
- "/some/db.db",
674
- "/some/config.json",
673
+ undefined,
674
+ "/home/user/.vellum/workspace",
675
+ );
676
+ expect(resolver.resolve("data/db/assistant.db")).toBe(
677
+ "/home/user/.vellum/workspace/data/db/assistant.db",
675
678
  );
676
- expect(resolver.resolve("data/db/assistant.db")).toBe("/some/db.db");
677
679
  });
678
680
 
679
- test("resolves config/settings.json to configured config path", () => {
681
+ test("resolves config/settings.json to workspace config path (backward compat)", () => {
680
682
  const resolver = new DefaultPathResolver(
681
- "/some/db.db",
682
- "/some/config.json",
683
+ undefined,
684
+ "/home/user/.vellum/workspace",
685
+ );
686
+ expect(resolver.resolve("config/settings.json")).toBe(
687
+ "/home/user/.vellum/workspace/config.json",
683
688
  );
684
- expect(resolver.resolve("config/settings.json")).toBe("/some/config.json");
685
689
  });
686
690
 
687
691
  test("returns null for unknown paths", () => {
688
692
  const resolver = new DefaultPathResolver(
689
- "/some/db.db",
690
- "/some/config.json",
693
+ undefined,
694
+ "/home/user/.vellum/workspace",
691
695
  );
692
696
  expect(resolver.resolve("unknown/path.txt")).toBeNull();
693
697
  });
698
+
699
+ test("resolves valid skills path via backward compat", () => {
700
+ const resolver = new DefaultPathResolver(
701
+ undefined,
702
+ "/home/user/.vellum/workspace",
703
+ );
704
+ expect(resolver.resolve("skills/my-skill/SKILL.md")).toBe(
705
+ "/home/user/.vellum/workspace/skills/my-skill/SKILL.md",
706
+ );
707
+ });
708
+
709
+ test("resolves workspace/ prefix paths", () => {
710
+ const resolver = new DefaultPathResolver(
711
+ undefined,
712
+ "/home/user/.vellum/workspace",
713
+ );
714
+ expect(resolver.resolve("workspace/data/db/assistant.db")).toBe(
715
+ "/home/user/.vellum/workspace/data/db/assistant.db",
716
+ );
717
+ expect(resolver.resolve("workspace/config.json")).toBe(
718
+ "/home/user/.vellum/workspace/config.json",
719
+ );
720
+ expect(resolver.resolve("workspace/skills/my-skill/SKILL.md")).toBe(
721
+ "/home/user/.vellum/workspace/skills/my-skill/SKILL.md",
722
+ );
723
+ });
724
+
725
+ test("returns null for workspace/ path traversal attempt", () => {
726
+ const resolver = new DefaultPathResolver(
727
+ undefined,
728
+ "/home/user/.vellum/workspace",
729
+ );
730
+ expect(resolver.resolve("workspace/../../etc/passwd")).toBeNull();
731
+ });
732
+
733
+ test("returns null for skills path traversal attempt (../../etc/passwd)", () => {
734
+ const resolver = new DefaultPathResolver(
735
+ undefined,
736
+ "/home/user/.vellum/workspace",
737
+ );
738
+ expect(resolver.resolve("skills/../../etc/passwd")).toBeNull();
739
+ });
740
+
741
+ test("returns null for skills path traversal attempt (../../../.ssh/authorized_keys)", () => {
742
+ const resolver = new DefaultPathResolver(
743
+ undefined,
744
+ "/home/user/.vellum/workspace",
745
+ );
746
+ expect(
747
+ resolver.resolve("skills/../../../.ssh/authorized_keys"),
748
+ ).toBeNull();
749
+ });
750
+
751
+ test("returns null for skills paths when workspaceDir is not provided", () => {
752
+ const resolver = new DefaultPathResolver();
753
+ expect(resolver.resolve("skills/my-skill/SKILL.md")).toBeNull();
754
+ });
694
755
  });
695
756
 
696
757
  // ---------------------------------------------------------------------------
@@ -53,9 +53,8 @@ mock.module("../config/loader.js", () => ({
53
53
  model: "test",
54
54
  provider: "test",
55
55
  memory: { enabled: false },
56
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
56
+ rateLimit: { maxRequestsPerMinute: 0 },
57
57
  secretDetection: { enabled: false },
58
- sandbox: { enabled: false },
59
58
  }),
60
59
  }));
61
60
 
@@ -307,26 +306,27 @@ describe("validateVBundle", () => {
307
306
  expect(manifestError).toBeDefined();
308
307
  });
309
308
 
310
- test("missing data/db/assistant.db returns MISSING_ENTRY error", () => {
311
- const manifestData = new TextEncoder().encode(
312
- JSON.stringify({
313
- schema_version: "1.0",
314
- created_at: new Date().toISOString(),
315
- files: [],
316
- manifest_sha256: "placeholder",
317
- }),
318
- );
309
+ test("bundle with only manifest (no data files) is structurally valid", () => {
310
+ // After the workspace walk refactor, only manifest.json is required.
311
+ // But the manifest checksum must match for full validity.
312
+ const manifestWithoutChecksum = {
313
+ schema_version: "1.0",
314
+ created_at: new Date().toISOString(),
315
+ files: [],
316
+ };
317
+ const manifestSha256 = sha256Hex(canonicalizeJson(manifestWithoutChecksum));
318
+ const manifest = {
319
+ ...manifestWithoutChecksum,
320
+ manifest_sha256: manifestSha256,
321
+ };
322
+ const manifestData = new TextEncoder().encode(JSON.stringify(manifest));
319
323
  const tar = createTarArchive([
320
324
  { name: "manifest.json", data: manifestData },
321
325
  ]);
322
326
  const vbundle = gzipSync(tar);
323
327
  const result = validateVBundle(vbundle);
324
328
 
325
- expect(result.is_valid).toBe(false);
326
- const dbError = result.errors.find(
327
- (e) => e.code === "MISSING_ENTRY" && e.path === "data/db/assistant.db",
328
- );
329
- expect(dbError).toBeDefined();
329
+ expect(result.is_valid).toBe(true);
330
330
  });
331
331
 
332
332
  test("invalid manifest JSON returns INVALID_MANIFEST_JSON error", () => {
@@ -62,10 +62,10 @@ describe("model intents", () => {
62
62
  "claude-opus-4-6",
63
63
  );
64
64
  expect(resolveModelIntent("anthropic", "vision-optimized")).toBe(
65
- "claude-sonnet-4-6",
65
+ "claude-opus-4-6",
66
66
  );
67
67
  expect(resolveModelIntent("openai", "latency-optimized")).toBe(
68
- "gpt-4o-mini",
68
+ "gpt-5.4-nano",
69
69
  );
70
70
  });
71
71
 
@@ -5,7 +5,7 @@ import { describe, expect, test } from "bun:test";
5
5
  /**
6
6
  * Guard test: domain-specific routing sections must NOT appear in the system
7
7
  * prompt source file. Routing cues now live in skill frontmatter
8
- * (`activation-hints` / `avoid-when`) and are projected into `<available_skills>` XML.
8
+ * (`activation-hints` / `avoid-when`) and are projected into the skills catalog.
9
9
  *
10
10
  * If this test fails, you are re-introducing hardcoded routing into the system
11
11
  * prompt. Instead, add `activation-hints` to the skill's SKILL.md frontmatter.
@@ -623,7 +623,7 @@ describe("access-request-helper unit tests", () => {
623
623
  expect(telegram!.status).toBe("sent");
624
624
  });
625
625
 
626
- test("notifyGuardianOfAccessRequest records failed vellum fallback when pipeline has no vellum delivery", async () => {
626
+ test("notifyGuardianOfAccessRequest skips vellum fallback for same-channel-only routing (telegram)", async () => {
627
627
  mockEmitResult = {
628
628
  signalId: "sig-no-vellum",
629
629
  deduplicated: false,
@@ -657,8 +657,8 @@ describe("access-request-helper unit tests", () => {
657
657
  (d) => d.destinationChannel === "telegram",
658
658
  );
659
659
 
660
- expect(vellum).toBeDefined();
661
- expect(vellum!.status).toBe("failed");
660
+ // Same-channel routing skips vellum delivery entirely — no fallback record
661
+ expect(vellum).toBeUndefined();
662
662
  expect(telegram).toBeDefined();
663
663
  expect(telegram!.destinationChatId).toBe("guardian-chat-456");
664
664
  expect(telegram!.status).toBe("sent");
@@ -115,7 +115,7 @@ function makeSignal(
115
115
  signalId: "sig-broadcast-001",
116
116
  createdAt: Date.now(),
117
117
  sourceChannel: "scheduler",
118
- sourceSessionId: "sess-001",
118
+ sourceContextId: "sess-001",
119
119
  sourceEventName: "test.event",
120
120
  contextPayload: {},
121
121
  attentionHints: {
@@ -65,7 +65,7 @@ function makeSignal(
65
65
  signalId: "sig-fallback-guardian-1",
66
66
  createdAt: Date.now(),
67
67
  sourceChannel: "phone",
68
- sourceSessionId: "call-session-1",
68
+ sourceContextId: "call-session-1",
69
69
  sourceEventName: "guardian.question",
70
70
  contextPayload: {
71
71
  questionText: "What is the gate code?",
@@ -308,7 +308,7 @@ describe("access-request instruction enforcement", () => {
308
308
  signalId: "sig-access-req-1",
309
309
  createdAt: Date.now(),
310
310
  sourceChannel: "telegram",
311
- sourceSessionId: "tg-session-1",
311
+ sourceContextId: "tg-session-1",
312
312
  sourceEventName: "ingress.access_request",
313
313
  contextPayload: {
314
314
  senderIdentifier: "Alice",
@@ -45,7 +45,9 @@ mock.module("../prompts/system-prompt.js", () => ({
45
45
 
46
46
  // ── Provider mock with system prompt capture ──────────────────────────
47
47
 
48
- let configuredProvider: { sendMessage: (...args: unknown[]) => Promise<unknown> } | null = null;
48
+ let configuredProvider: {
49
+ sendMessage: (...args: unknown[]) => Promise<unknown>;
50
+ } | null = null;
49
51
  let extractedToolUse: unknown = null;
50
52
  let capturedSystemPrompt: string | undefined;
51
53
 
@@ -81,7 +83,7 @@ function makeSignal(
81
83
  signalId: "sig-identity-test-1",
82
84
  createdAt: Date.now(),
83
85
  sourceChannel: "phone",
84
- sourceSessionId: "call-session-1",
86
+ sourceContextId: "call-session-1",
85
87
  sourceEventName: "guardian.question",
86
88
  contextPayload: {
87
89
  questionText: "What is the gate code?",
@@ -141,9 +143,7 @@ describe("identity context in notification decision engine", () => {
141
143
 
142
144
  expect(capturedSystemPrompt).toBeDefined();
143
145
  expect(capturedSystemPrompt).toContain("<assistant-identity>");
144
- expect(capturedSystemPrompt).toContain(
145
- "I am Jarvis, a helpful assistant",
146
- );
146
+ expect(capturedSystemPrompt).toContain("I am Jarvis, a helpful assistant");
147
147
  expect(capturedSystemPrompt).toContain("</assistant-identity>");
148
148
  });
149
149
 
@@ -192,10 +192,9 @@ describe("identity context in notification decision engine", () => {
192
192
  configuredProvider = null;
193
193
 
194
194
  const signal = makeSignal();
195
- const decision = await evaluateSignal(
196
- signal,
197
- ["vellum"] as NotificationChannel[],
198
- );
195
+ const decision = await evaluateSignal(signal, [
196
+ "vellum",
197
+ ] as NotificationChannel[]);
199
198
 
200
199
  // Fallback should produce valid copy regardless of identity context
201
200
  expect(decision.fallbackUsed).toBe(true);
@@ -38,7 +38,7 @@ function makeSignal(
38
38
  signalId: "sig-test-001",
39
39
  createdAt: Date.now(),
40
40
  sourceChannel: "scheduler",
41
- sourceSessionId: "sess-001",
41
+ sourceContextId: "sess-001",
42
42
  sourceEventName: "test.event",
43
43
  contextPayload: {},
44
44
  attentionHints: {