@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
@@ -0,0 +1,547 @@
1
+ import { mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import {
5
+ afterAll,
6
+ beforeEach,
7
+ describe,
8
+ expect,
9
+ mock,
10
+ test,
11
+ } from "bun:test";
12
+
13
+ import { eq } from "drizzle-orm";
14
+
15
+ const testDir = mkdtempSync(join(tmpdir(), "skill-memory-"));
16
+
17
+ mock.module("../util/platform.js", () => ({
18
+ getDataDir: () => testDir,
19
+ isMacOS: () => process.platform === "darwin",
20
+ isLinux: () => process.platform === "linux",
21
+ isWindows: () => process.platform === "win32",
22
+ getPidPath: () => join(testDir, "test.pid"),
23
+ getDbPath: () => join(testDir, "test.db"),
24
+ getLogPath: () => join(testDir, "test.log"),
25
+ ensureDataDir: () => {},
26
+ getWorkspaceSkillsDir: () => join(testDir, "skills"),
27
+ getWorkspaceConfigPath: () => join(testDir, "config.json"),
28
+ readPlatformToken: () => undefined,
29
+ }));
30
+
31
+ mock.module("../util/logger.js", () => ({
32
+ getLogger: () =>
33
+ new Proxy({} as Record<string, unknown>, {
34
+ get: () => () => {},
35
+ }),
36
+ }));
37
+
38
+ mock.module("../memory/qdrant-client.js", () => ({
39
+ getQdrantClient: () => ({
40
+ searchWithFilter: async () => [],
41
+ hybridSearch: async () => [],
42
+ upsertPoints: async () => {},
43
+ deletePoints: async () => {},
44
+ }),
45
+ initQdrantClient: () => {},
46
+ }));
47
+
48
+ // Controllable mock for resolveCatalog used by seedCatalogSkillMemories
49
+ let mockResolveCatalog: () => Promise<import("../skills/catalog-install.js").CatalogSkill[]> =
50
+ async () => [];
51
+
52
+ mock.module("../skills/catalog-install.js", () => ({
53
+ resolveCatalog: (..._args: unknown[]) => mockResolveCatalog(),
54
+ }));
55
+
56
+ // Controllable mock for isAssistantFeatureFlagEnabled used by seedCatalogSkillMemories
57
+ let mockIsFeatureFlagEnabled: (key: string) => boolean = () => true;
58
+
59
+ mock.module("../config/assistant-feature-flags.js", () => ({
60
+ isAssistantFeatureFlagEnabled: (key: string, _config: unknown) =>
61
+ mockIsFeatureFlagEnabled(key),
62
+ getAssistantFeatureFlagDefaults: () => ({}),
63
+ }));
64
+
65
+ import { DEFAULT_CONFIG } from "../config/defaults.js";
66
+
67
+ const TEST_CONFIG = {
68
+ ...DEFAULT_CONFIG,
69
+ memory: {
70
+ ...DEFAULT_CONFIG.memory,
71
+ enabled: true,
72
+ extraction: {
73
+ ...DEFAULT_CONFIG.memory.extraction,
74
+ useLLM: false,
75
+ },
76
+ },
77
+ };
78
+
79
+ mock.module("../config/loader.js", () => ({
80
+ loadConfig: () => TEST_CONFIG,
81
+ getConfig: () => TEST_CONFIG,
82
+ loadRawConfig: () => ({}),
83
+ saveRawConfig: () => {},
84
+ invalidateConfigCache: () => {},
85
+ }));
86
+
87
+ import { getDb, initializeDb, resetDb } from "../memory/db.js";
88
+ import { memoryItems, memoryJobs } from "../memory/schema.js";
89
+ import type { CatalogSkill } from "../skills/catalog-install.js";
90
+ import {
91
+ buildCapabilityStatement,
92
+ deleteSkillCapabilityMemory,
93
+ seedCatalogSkillMemories,
94
+ upsertSkillCapabilityMemory,
95
+ } from "../skills/skill-memory.js";
96
+
97
+ initializeDb();
98
+
99
+ afterAll(() => {
100
+ resetDb();
101
+ try {
102
+ rmSync(testDir, { recursive: true });
103
+ } catch {
104
+ // best effort cleanup
105
+ }
106
+ });
107
+
108
+ function resetTables() {
109
+ const db = getDb();
110
+ db.run("DELETE FROM memory_item_sources");
111
+ db.run("DELETE FROM memory_embeddings");
112
+ db.run("DELETE FROM memory_items");
113
+ db.run("DELETE FROM memory_jobs");
114
+ }
115
+
116
+ function makeSkill(overrides: Partial<CatalogSkill> = {}): CatalogSkill {
117
+ return {
118
+ id: "test-skill",
119
+ name: "Test Skill",
120
+ description: "A skill for testing",
121
+ ...overrides,
122
+ };
123
+ }
124
+
125
+ // ─── buildCapabilityStatement ────────────────────────────────────────────────
126
+
127
+ describe("buildCapabilityStatement", () => {
128
+ test("includes display name, id, and description", () => {
129
+ const entry = makeSkill({
130
+ metadata: { vellum: { "display-name": "My Skill" } },
131
+ });
132
+ const result = buildCapabilityStatement(entry);
133
+ expect(result).toContain('"My Skill"');
134
+ expect(result).toContain("(test-skill)");
135
+ expect(result).toContain("A skill for testing");
136
+ });
137
+
138
+ test("includes activation hints when present", () => {
139
+ const entry = makeSkill({
140
+ metadata: {
141
+ vellum: {
142
+ "display-name": "My Skill",
143
+ "activation-hints": ["user asks to search", "needs web data"],
144
+ },
145
+ },
146
+ });
147
+ const result = buildCapabilityStatement(entry);
148
+ expect(result).toContain("Use when:");
149
+ expect(result).toContain("user asks to search");
150
+ expect(result).toContain("needs web data");
151
+ });
152
+
153
+ test("works without metadata (falls back to name)", () => {
154
+ const entry = makeSkill({ metadata: undefined });
155
+ const result = buildCapabilityStatement(entry);
156
+ expect(result).toContain('"Test Skill"');
157
+ expect(result).toContain("(test-skill)");
158
+ expect(result).toContain("A skill for testing");
159
+ });
160
+
161
+ test("truncates long statements to 500 chars", () => {
162
+ const longDesc = "x".repeat(600);
163
+ const entry = makeSkill({ description: longDesc });
164
+ const result = buildCapabilityStatement(entry);
165
+ expect(result.length).toBe(500);
166
+ });
167
+ });
168
+
169
+ // ─── upsertSkillCapabilityMemory ─────────────────────────────────────────────
170
+
171
+ describe("upsertSkillCapabilityMemory", () => {
172
+ beforeEach(resetTables);
173
+
174
+ test("inserts with correct kind, subject, confidence, importance", () => {
175
+ const entry = makeSkill();
176
+ upsertSkillCapabilityMemory("test-skill", entry);
177
+
178
+ const db = getDb();
179
+ const items = db.select().from(memoryItems).all();
180
+ expect(items).toHaveLength(1);
181
+ expect(items[0].kind).toBe("capability");
182
+ expect(items[0].subject).toBe("skill:test-skill");
183
+ expect(items[0].confidence).toBe(1.0);
184
+ expect(items[0].importance).toBe(0.7);
185
+ expect(items[0].status).toBe("active");
186
+ expect(items[0].scopeId).toBe("default");
187
+
188
+ // Should also enqueue an embed_item job
189
+ const jobs = db.select().from(memoryJobs).all();
190
+ expect(jobs).toHaveLength(1);
191
+ expect(jobs[0].type).toBe("embed_item");
192
+ });
193
+
194
+ test("is idempotent (same entry only touches lastSeenAt)", () => {
195
+ const entry = makeSkill();
196
+ upsertSkillCapabilityMemory("test-skill", entry);
197
+
198
+ const db = getDb();
199
+ const before = db.select().from(memoryItems).all();
200
+ expect(before).toHaveLength(1);
201
+ const originalLastSeen = before[0].lastSeenAt;
202
+
203
+ // Upsert again
204
+ upsertSkillCapabilityMemory("test-skill", entry);
205
+
206
+ const after = db.select().from(memoryItems).all();
207
+ expect(after).toHaveLength(1);
208
+ // Fingerprint should be the same, so only lastSeenAt changes
209
+ expect(after[0].fingerprint).toBe(before[0].fingerprint);
210
+ expect(after[0].lastSeenAt).toBeGreaterThanOrEqual(originalLastSeen);
211
+
212
+ // Should NOT enqueue a second embed job (only 1 from initial insert)
213
+ const jobs = db.select().from(memoryJobs).all();
214
+ expect(jobs).toHaveLength(1);
215
+ });
216
+
217
+ test("updates statement when description changes", () => {
218
+ const entry = makeSkill({ description: "Original description" });
219
+ upsertSkillCapabilityMemory("test-skill", entry);
220
+
221
+ const db = getDb();
222
+ const before = db.select().from(memoryItems).all();
223
+ expect(before).toHaveLength(1);
224
+ expect(before[0].statement).toContain("Original description");
225
+
226
+ // Change description
227
+ const updatedEntry = makeSkill({ description: "Updated description" });
228
+ upsertSkillCapabilityMemory("test-skill", updatedEntry);
229
+
230
+ const after = db.select().from(memoryItems).all();
231
+ expect(after).toHaveLength(1);
232
+ expect(after[0].statement).toContain("Updated description");
233
+ expect(after[0].fingerprint).not.toBe(before[0].fingerprint);
234
+
235
+ // Should enqueue a second embed job
236
+ const jobs = db.select().from(memoryJobs).all();
237
+ expect(jobs).toHaveLength(2);
238
+ });
239
+
240
+ test("reactivates soft-deleted items", () => {
241
+ const entry = makeSkill();
242
+ upsertSkillCapabilityMemory("test-skill", entry);
243
+
244
+ const db = getDb();
245
+ // Soft-delete the item
246
+ db.update(memoryItems)
247
+ .set({ status: "deleted" })
248
+ .where(eq(memoryItems.subject, "skill:test-skill"))
249
+ .run();
250
+
251
+ const deleted = db.select().from(memoryItems).all();
252
+ expect(deleted[0].status).toBe("deleted");
253
+
254
+ // Clear jobs from initial insert
255
+ db.run("DELETE FROM memory_jobs");
256
+
257
+ // Upsert again — should reactivate
258
+ upsertSkillCapabilityMemory("test-skill", entry);
259
+
260
+ const reactivated = db.select().from(memoryItems).all();
261
+ expect(reactivated).toHaveLength(1);
262
+ expect(reactivated[0].status).toBe("active");
263
+
264
+ // Should enqueue embed job for reactivated item
265
+ const jobs = db.select().from(memoryJobs).all();
266
+ expect(jobs).toHaveLength(1);
267
+ expect(jobs[0].type).toBe("embed_item");
268
+ });
269
+
270
+ test("does not throw on DB error", () => {
271
+ // Close the DB connection to force errors, then reinitialize
272
+ resetDb();
273
+ // getDb() will create a new connection, but we can force a DB error by
274
+ // dropping the table it reads from. Use a fresh DB without initialization.
275
+ // Instead, verify the try/catch by closing and reopening:
276
+ // resetDb closes the connection; getDb lazily reconnects.
277
+ // We drop the memory_items table to force an error on the next query.
278
+ const db = getDb();
279
+ db.run("DROP TABLE IF EXISTS memory_items");
280
+
281
+ expect(() => {
282
+ upsertSkillCapabilityMemory("test-skill", makeSkill());
283
+ }).not.toThrow();
284
+
285
+ // Restore DB state for subsequent tests
286
+ resetDb();
287
+ initializeDb();
288
+ });
289
+ });
290
+
291
+ // ─── deleteSkillCapabilityMemory ─────────────────────────────────────────────
292
+
293
+ describe("deleteSkillCapabilityMemory", () => {
294
+ beforeEach(resetTables);
295
+
296
+ test("soft-deletes matching item", () => {
297
+ const entry = makeSkill();
298
+ upsertSkillCapabilityMemory("test-skill", entry);
299
+
300
+ const db = getDb();
301
+ const before = db.select().from(memoryItems).all();
302
+ expect(before).toHaveLength(1);
303
+ expect(before[0].status).toBe("active");
304
+
305
+ deleteSkillCapabilityMemory("test-skill");
306
+
307
+ const after = db.select().from(memoryItems).all();
308
+ expect(after).toHaveLength(1);
309
+ expect(after[0].status).toBe("deleted");
310
+ });
311
+
312
+ test("is no-op for missing item", () => {
313
+ // Should not throw when no matching item exists
314
+ expect(() => {
315
+ deleteSkillCapabilityMemory("nonexistent-skill");
316
+ }).not.toThrow();
317
+
318
+ const db = getDb();
319
+ const items = db.select().from(memoryItems).all();
320
+ expect(items).toHaveLength(0);
321
+ });
322
+
323
+ test("does not throw on DB error", () => {
324
+ // Close and reopen DB, then drop the table to force a query error
325
+ resetDb();
326
+ const db = getDb();
327
+ db.run("DROP TABLE IF EXISTS memory_items");
328
+
329
+ expect(() => {
330
+ deleteSkillCapabilityMemory("test-skill");
331
+ }).not.toThrow();
332
+
333
+ // Restore DB state for subsequent tests
334
+ resetDb();
335
+ initializeDb();
336
+ });
337
+ });
338
+
339
+ // ─── seedCatalogSkillMemories ─────────────────────────────────────────────
340
+
341
+ describe("seedCatalogSkillMemories", () => {
342
+ beforeEach(() => {
343
+ resetTables();
344
+ // Reset mocks to defaults
345
+ mockResolveCatalog = async () => [];
346
+ mockIsFeatureFlagEnabled = () => true;
347
+ });
348
+
349
+ test("upserts capability memories for all catalog entries", async () => {
350
+ const skills: CatalogSkill[] = [
351
+ makeSkill({ id: "skill-a", name: "Skill A", description: "Does A" }),
352
+ makeSkill({ id: "skill-b", name: "Skill B", description: "Does B" }),
353
+ makeSkill({ id: "skill-c", name: "Skill C", description: "Does C" }),
354
+ ];
355
+ mockResolveCatalog = async () => skills;
356
+
357
+ await seedCatalogSkillMemories();
358
+
359
+ const db = getDb();
360
+ const items = db
361
+ .select()
362
+ .from(memoryItems)
363
+ .where(eq(memoryItems.kind, "capability"))
364
+ .all();
365
+ expect(items).toHaveLength(3);
366
+
367
+ const subjects = items.map((i) => i.subject).sort();
368
+ expect(subjects).toEqual([
369
+ "skill:skill-a",
370
+ "skill:skill-b",
371
+ "skill:skill-c",
372
+ ]);
373
+
374
+ // All should be active
375
+ for (const item of items) {
376
+ expect(item.status).toBe("active");
377
+ }
378
+ });
379
+
380
+ test("prunes stale capabilities for skills no longer in catalog", async () => {
381
+ // First seed with three skills
382
+ const initialSkills: CatalogSkill[] = [
383
+ makeSkill({ id: "skill-a", name: "Skill A", description: "Does A" }),
384
+ makeSkill({ id: "skill-b", name: "Skill B", description: "Does B" }),
385
+ makeSkill({ id: "skill-c", name: "Skill C", description: "Does C" }),
386
+ ];
387
+ mockResolveCatalog = async () => initialSkills;
388
+ await seedCatalogSkillMemories();
389
+
390
+ const db = getDb();
391
+ const beforeItems = db
392
+ .select()
393
+ .from(memoryItems)
394
+ .where(eq(memoryItems.kind, "capability"))
395
+ .all();
396
+ expect(beforeItems).toHaveLength(3);
397
+ expect(beforeItems.every((i) => i.status === "active")).toBe(true);
398
+
399
+ // Now seed with only skill-a — skill-b and skill-c should be pruned
400
+ mockResolveCatalog = async () => [
401
+ makeSkill({ id: "skill-a", name: "Skill A", description: "Does A" }),
402
+ ];
403
+ await seedCatalogSkillMemories();
404
+
405
+ const afterItems = db
406
+ .select()
407
+ .from(memoryItems)
408
+ .where(eq(memoryItems.kind, "capability"))
409
+ .all();
410
+ expect(afterItems).toHaveLength(3); // still 3 rows, but 2 are soft-deleted
411
+
412
+ const active = afterItems.filter((i) => i.status === "active");
413
+ const deleted = afterItems.filter((i) => i.status === "deleted");
414
+
415
+ expect(active).toHaveLength(1);
416
+ expect(active[0].subject).toBe("skill:skill-a");
417
+
418
+ expect(deleted).toHaveLength(2);
419
+ const deletedSubjects = deleted.map((i) => i.subject).sort();
420
+ expect(deletedSubjects).toEqual(["skill:skill-b", "skill:skill-c"]);
421
+ });
422
+
423
+ test("handles empty catalog without errors", async () => {
424
+ // Pre-populate a skill so we can verify it gets pruned
425
+ upsertSkillCapabilityMemory(
426
+ "existing-skill",
427
+ makeSkill({ id: "existing-skill" }),
428
+ );
429
+
430
+ const db = getDb();
431
+ const beforeItems = db.select().from(memoryItems).all();
432
+ expect(beforeItems).toHaveLength(1);
433
+ expect(beforeItems[0].status).toBe("active");
434
+
435
+ // Seed with empty catalog
436
+ mockResolveCatalog = async () => [];
437
+ await seedCatalogSkillMemories();
438
+
439
+ // The existing skill should be pruned (soft-deleted)
440
+ const afterItems = db.select().from(memoryItems).all();
441
+ expect(afterItems).toHaveLength(1);
442
+ expect(afterItems[0].status).toBe("deleted");
443
+ });
444
+
445
+ test("does not throw when resolveCatalog rejects", async () => {
446
+ mockResolveCatalog = async () => {
447
+ throw new Error("Network failure");
448
+ };
449
+
450
+ // Best-effort: should not propagate the error
451
+ await expect(seedCatalogSkillMemories()).resolves.toBeUndefined();
452
+ });
453
+
454
+ test("skips skills whose feature flag is disabled", async () => {
455
+ const skills: CatalogSkill[] = [
456
+ makeSkill({ id: "unflagged-skill", name: "Unflagged", description: "No flag" }),
457
+ makeSkill({
458
+ id: "flagged-skill",
459
+ name: "Flagged",
460
+ description: "Has flag",
461
+ metadata: { vellum: { "feature-flag": "my_gated_feature" } },
462
+ }),
463
+ ];
464
+ mockResolveCatalog = async () => skills;
465
+
466
+ // Disable the feature flag for the flagged skill
467
+ mockIsFeatureFlagEnabled = (key: string) =>
468
+ key !== "feature_flags.my_gated_feature.enabled";
469
+
470
+ await seedCatalogSkillMemories();
471
+
472
+ const db = getDb();
473
+ const items = db
474
+ .select()
475
+ .from(memoryItems)
476
+ .where(eq(memoryItems.kind, "capability"))
477
+ .all();
478
+
479
+ // Only the unflagged skill should have a capability row
480
+ expect(items).toHaveLength(1);
481
+ expect(items[0].subject).toBe("skill:unflagged-skill");
482
+ expect(items[0].status).toBe("active");
483
+ });
484
+
485
+ test("prunes pre-existing capability for a skill whose flag becomes disabled", async () => {
486
+ // First seed with both skills, all flags enabled
487
+ const skills: CatalogSkill[] = [
488
+ makeSkill({ id: "unflagged-skill", name: "Unflagged", description: "No flag" }),
489
+ makeSkill({
490
+ id: "flagged-skill",
491
+ name: "Flagged",
492
+ description: "Has flag",
493
+ metadata: { vellum: { "feature-flag": "my_gated_feature" } },
494
+ }),
495
+ ];
496
+ mockResolveCatalog = async () => skills;
497
+ mockIsFeatureFlagEnabled = () => true;
498
+ await seedCatalogSkillMemories();
499
+
500
+ const db = getDb();
501
+ const beforeItems = db
502
+ .select()
503
+ .from(memoryItems)
504
+ .where(eq(memoryItems.kind, "capability"))
505
+ .all();
506
+ expect(beforeItems).toHaveLength(2);
507
+ expect(beforeItems.every((i) => i.status === "active")).toBe(true);
508
+
509
+ // Now disable the flag — the flagged skill should be pruned
510
+ mockIsFeatureFlagEnabled = (key: string) =>
511
+ key !== "feature_flags.my_gated_feature.enabled";
512
+ await seedCatalogSkillMemories();
513
+
514
+ const afterItems = db
515
+ .select()
516
+ .from(memoryItems)
517
+ .where(eq(memoryItems.kind, "capability"))
518
+ .all();
519
+ expect(afterItems).toHaveLength(2); // still 2 rows, but one soft-deleted
520
+
521
+ const active = afterItems.filter((i) => i.status === "active");
522
+ const deleted = afterItems.filter((i) => i.status === "deleted");
523
+
524
+ expect(active).toHaveLength(1);
525
+ expect(active[0].subject).toBe("skill:unflagged-skill");
526
+
527
+ expect(deleted).toHaveLength(1);
528
+ expect(deleted[0].subject).toBe("skill:flagged-skill");
529
+ });
530
+
531
+ test("does not throw on DB error during pruning", async () => {
532
+ mockResolveCatalog = async () => [
533
+ makeSkill({ id: "skill-a", name: "Skill A", description: "Does A" }),
534
+ ];
535
+
536
+ // Drop memory_items to force a DB error during the prune phase
537
+ resetDb();
538
+ const db = getDb();
539
+ db.run("DROP TABLE IF EXISTS memory_items");
540
+
541
+ await expect(seedCatalogSkillMemories()).resolves.toBeUndefined();
542
+
543
+ // Restore DB state for subsequent tests
544
+ resetDb();
545
+ initializeDb();
546
+ });
547
+ });
@@ -17,8 +17,7 @@ const mockConfig = {
17
17
  shellMaxTimeoutSec: 600,
18
18
  permissionTimeoutSec: 300,
19
19
  },
20
- sandbox: { enabled: false },
21
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
20
+ rateLimit: { maxRequestsPerMinute: 0 },
22
21
  secretDetection: {
23
22
  enabled: true,
24
23
  action: "warn" as const,
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { describe, expect, test } from "bun:test";
4
+
5
+ const REPO_ROOT = resolve(import.meta.dirname ?? __dirname, "..", "..", "..");
6
+ const SKILL_PATH = resolve(REPO_ROOT, "skills", "slack-app-setup", "SKILL.md");
7
+
8
+ const skillContent = readFileSync(SKILL_PATH, "utf-8");
9
+
10
+ describe("slack-app-setup skill regression", () => {
11
+ test("keeps Slack token collection on the secure credential prompt path", () => {
12
+ expect(skillContent).toContain('`credential_store` with `action: "prompt"`');
13
+ expect(skillContent).toContain(
14
+ "same Slack settings handler used by Settings",
15
+ );
16
+ });
17
+
18
+ test("forbids plaintext forms and chat-pasted secrets", () => {
19
+ expect(skillContent).toContain("Do NOT use `ui_show`");
20
+ expect(skillContent).toContain("Do NOT ask the user to paste them in chat");
21
+ });
22
+
23
+ test("does not instruct the agent to reimplement Slack validation in shell", () => {
24
+ expect(skillContent).not.toContain(
25
+ "assistant credentials reveal --service slack_channel",
26
+ );
27
+ expect(skillContent).not.toContain(
28
+ 'curl -sf -X POST "https://slack.com/api/auth.test"',
29
+ );
30
+ expect(skillContent).not.toContain("assistant config set slack.teamId");
31
+ expect(skillContent).not.toContain("assistant config set slack.teamName");
32
+ expect(skillContent).not.toContain("assistant config set slack.botUserId");
33
+ expect(skillContent).not.toContain(
34
+ "assistant config set slack.botUsername",
35
+ );
36
+ });
37
+ });