@vellumai/assistant 0.8.12 → 0.9.0-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/AGENTS.md +0 -14
  2. package/ARCHITECTURE.md +45 -45
  3. package/README.md +1 -1
  4. package/bun.lock +200 -154
  5. package/docs/architecture/integrations.md +3 -3
  6. package/docs/architecture/memory.md +2 -2
  7. package/docs/architecture/security.md +10 -10
  8. package/docs/runbook-trusted-contacts.md +12 -12
  9. package/docs/skills.md +6 -6
  10. package/docs/workflows-testing.md +221 -0
  11. package/docs/workflows.md +510 -0
  12. package/examples/plugins/echo/README.md +5 -5
  13. package/knip.json +2 -0
  14. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
  15. package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
  16. package/openapi.yaml +7197 -5708
  17. package/package.json +8 -4
  18. package/scripts/generate-openapi.ts +66 -114
  19. package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
  20. package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
  21. package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
  22. package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  24. package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
  25. package/src/__tests__/app-compiler.test.ts +7 -1
  26. package/src/__tests__/app-dir-path-guard.test.ts +27 -3
  27. package/src/__tests__/app-executors.test.ts +43 -0
  28. package/src/__tests__/approval-cascade.test.ts +0 -5
  29. package/src/__tests__/approval-routes-http.test.ts +91 -0
  30. package/src/__tests__/assistant-stream-state.test.ts +107 -0
  31. package/src/__tests__/browser-fill-credential.test.ts +3 -3
  32. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
  33. package/src/__tests__/compaction-events.test.ts +63 -7
  34. package/src/__tests__/compaction-trail-store.test.ts +74 -1
  35. package/src/__tests__/compaction.benchmark.test.ts +63 -41
  36. package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
  37. package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
  39. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
  40. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  41. package/src/__tests__/conversation-process-callsite.test.ts +0 -5
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
  43. package/src/__tests__/conversation-queue.test.ts +0 -5
  44. package/src/__tests__/conversation-slash-queue.test.ts +0 -5
  45. package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
  46. package/src/__tests__/conversation-speed-override.test.ts +0 -5
  47. package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
  48. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  49. package/src/__tests__/conversation-usage.test.ts +2 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
  52. package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
  53. package/src/__tests__/credential-broker-server-use.test.ts +2 -2
  54. package/src/__tests__/credential-broker.test.ts +1 -1
  55. package/src/__tests__/credential-prompt-route.test.ts +417 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault.test.ts +37 -0
  58. package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
  59. package/src/__tests__/dynamic-page-surface.test.ts +219 -0
  60. package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
  61. package/src/__tests__/gateway-flag-listener.test.ts +24 -7
  62. package/src/__tests__/guardian-action-sweep.test.ts +56 -219
  63. package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
  64. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  65. package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
  66. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
  67. package/src/__tests__/llm-resolver.test.ts +115 -0
  68. package/src/__tests__/managed-profile-guard.test.ts +6 -5
  69. package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
  70. package/src/__tests__/media-generate-image.test.ts +20 -9
  71. package/src/__tests__/mock-gateway-ipc.ts +23 -0
  72. package/src/__tests__/model-intents.test.ts +1 -1
  73. package/src/__tests__/normalize-onboarding.test.ts +26 -0
  74. package/src/__tests__/notification-decision-strategy.test.ts +4 -2
  75. package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
  76. package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
  77. package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
  78. package/src/__tests__/require-fresh-approval.test.ts +425 -1
  79. package/src/__tests__/resolve-app-id.test.ts +56 -0
  80. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
  81. package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
  82. package/src/__tests__/schedule-routes.test.ts +257 -4
  83. package/src/__tests__/schedule-store.test.ts +60 -0
  84. package/src/__tests__/schedule-tools.test.ts +247 -2
  85. package/src/__tests__/skill-execute-input.test.ts +85 -0
  86. package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
  87. package/src/__tests__/skills.test.ts +3 -3
  88. package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
  89. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  90. package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
  91. package/src/__tests__/system-prompt.test.ts +1 -1
  92. package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
  93. package/src/__tests__/task-scheduler.test.ts +299 -0
  94. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
  95. package/src/__tests__/tool-result-spool.test.ts +3 -1
  96. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
  97. package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
  98. package/src/agent/compaction-circuit.ts +11 -0
  99. package/src/agent/loop.ts +181 -12
  100. package/src/api/constants/call-sites.ts +12 -0
  101. package/src/api/events/assistant-thinking-delta.ts +10 -0
  102. package/src/api/events/usage-update.ts +7 -0
  103. package/src/api/index.ts +4 -1
  104. package/src/api/responses/memory-v3-selection-log.ts +18 -11
  105. package/src/approvals/approval-primitive.ts +2 -2
  106. package/src/background-wake/background-wake-routes.test.ts +5 -2
  107. package/src/bundler/compiler-tools.ts +1 -1
  108. package/src/bundler/package-resolver.ts +0 -1
  109. package/src/calls/call-domain.ts +1 -1
  110. package/src/calls/guardian-action-sweep.ts +16 -93
  111. package/src/cli/AGENTS.md +4 -0
  112. package/src/cli/commands/__tests__/schedules.test.ts +430 -1
  113. package/src/cli/commands/credentials.ts +28 -24
  114. package/src/cli/commands/image-generation.ts +23 -9
  115. package/src/cli/commands/notifications.ts +1 -1
  116. package/src/cli/commands/plugins.ts +89 -46
  117. package/src/cli/commands/schedules.ts +384 -11
  118. package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
  119. package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
  120. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
  121. package/src/cli/lib/inspect-plugin.ts +62 -1
  122. package/src/cli/lib/install-from-github.ts +52 -5
  123. package/src/cli/lib/upgrade-plugin.ts +18 -0
  124. package/src/config/__tests__/workflows-schema.test.ts +60 -0
  125. package/src/config/bundled-skills/acp/SKILL.md +2 -2
  126. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  127. package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
  128. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
  129. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
  130. package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
  131. package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
  132. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
  133. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
  134. package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
  135. package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
  136. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
  137. package/src/config/bundled-skills/schedule/SKILL.md +7 -2
  138. package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
  139. package/src/config/bundled-skills/workflows/SKILL.md +214 -0
  140. package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
  141. package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
  142. package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
  143. package/src/config/bundled-tool-registry.ts +14 -2
  144. package/src/config/call-site-defaults.ts +5 -0
  145. package/src/config/feature-flag-registry.json +12 -4
  146. package/src/config/llm-context-resolution.ts +8 -0
  147. package/src/config/llm-resolver.ts +30 -0
  148. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
  149. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
  150. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
  151. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
  152. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
  153. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
  154. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
  155. package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
  156. package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
  157. package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
  158. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
  159. package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
  160. package/src/config/schema.ts +2 -0
  161. package/src/config/schemas/call-site-catalog.ts +7 -0
  162. package/src/config/schemas/heartbeat.ts +4 -1
  163. package/src/config/schemas/llm.ts +33 -26
  164. package/src/config/schemas/memory-retrospective.ts +19 -0
  165. package/src/config/schemas/platform.ts +8 -0
  166. package/src/config/schemas/services.ts +5 -2
  167. package/src/config/schemas/workflows.ts +42 -0
  168. package/src/config/skills.ts +3 -3
  169. package/src/context/compactor.ts +273 -39
  170. package/src/context/post-turn-tool-result-truncation.ts +23 -6
  171. package/src/context/tool-result-spool.ts +12 -17
  172. package/src/credential-execution/executable-discovery.ts +1 -1
  173. package/src/credential-execution/process-manager.ts +37 -3
  174. package/src/credential-execution/prompted-credential.ts +205 -0
  175. package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
  176. package/src/daemon/conversation-process.ts +11 -2
  177. package/src/daemon/conversation-surfaces.ts +167 -3
  178. package/src/daemon/conversation-tool-setup.ts +103 -26
  179. package/src/daemon/conversation-usage.ts +2 -0
  180. package/src/daemon/conversation.ts +115 -11
  181. package/src/daemon/handlers/shared.ts +26 -14
  182. package/src/daemon/host-cu-proxy.ts +15 -12
  183. package/src/daemon/host-file-proxy.ts +15 -12
  184. package/src/daemon/host-transfer-proxy.ts +30 -24
  185. package/src/daemon/lifecycle.ts +40 -3
  186. package/src/daemon/message-protocol.ts +3 -0
  187. package/src/daemon/message-types/messages.ts +2 -10
  188. package/src/daemon/message-types/workflows.ts +49 -0
  189. package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
  190. package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
  191. package/src/daemon/process-message.ts +6 -0
  192. package/src/daemon/tool-setup-types.ts +57 -0
  193. package/src/daemon/wake-conversation-ops.ts +18 -0
  194. package/src/heartbeat/heartbeat-run-store.ts +8 -2
  195. package/src/home/feed-types.ts +1 -1
  196. package/src/ipc/gateway-flag-listener.ts +28 -6
  197. package/src/mcp/mcp-auth-state.ts +8 -20
  198. package/src/media/__tests__/image-models.test.ts +57 -0
  199. package/src/media/image-models.ts +66 -0
  200. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
  201. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
  202. package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
  203. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
  204. package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
  205. package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
  206. package/src/memory/auto-analysis-enqueue.ts +14 -1
  207. package/src/memory/compaction-log-store-clickhouse.ts +6 -4
  208. package/src/memory/conversation-crud.ts +9 -2
  209. package/src/memory/conversation-disk-view.ts +1 -1
  210. package/src/memory/conversation-queries.ts +22 -7
  211. package/src/memory/db-init.ts +20 -0
  212. package/src/memory/db-maintenance.ts +16 -0
  213. package/src/memory/embedding-runtime-manager.ts +1 -1
  214. package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
  215. package/src/memory/llm-request-log-source-local.ts +19 -1
  216. package/src/memory/llm-request-log-source.ts +35 -6
  217. package/src/memory/llm-request-log-store.ts +90 -2
  218. package/src/memory/memory-retrospective-constants.ts +9 -0
  219. package/src/memory/memory-retrospective-enqueue.ts +3 -6
  220. package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
  221. package/src/memory/memory-retrospective-job.ts +500 -208
  222. package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
  223. package/src/memory/memory-retrospective-state.ts +85 -2
  224. package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
  225. package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
  226. package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
  227. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
  228. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
  229. package/src/memory/migrations/284-workflow-runs.ts +51 -0
  230. package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
  231. package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
  232. package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
  233. package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
  234. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
  235. package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
  236. package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
  237. package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
  238. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
  239. package/src/memory/migrations/index.ts +10 -0
  240. package/src/memory/preloaded-apps.ts +116 -0
  241. package/src/memory/schema/infrastructure.ts +4 -0
  242. package/src/memory/schema/memory-core.ts +4 -0
  243. package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
  244. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
  245. package/src/memory/v2/__tests__/page-store.test.ts +13 -2
  246. package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
  247. package/src/memory/v2/frontmatter-sweep.ts +7 -6
  248. package/src/memory/v2/page-store.ts +4 -3
  249. package/src/memory/v2/qdrant.ts +42 -3
  250. package/src/memory/v2/types.ts +16 -10
  251. package/src/messaging/draft-store.ts +1 -1
  252. package/src/notifications/access-request-copy.ts +200 -113
  253. package/src/notifications/adapters/slack.ts +250 -111
  254. package/src/notifications/adapters/telegram.ts +7 -44
  255. package/src/notifications/approval-card-builder.ts +93 -0
  256. package/src/notifications/broadcaster.ts +74 -0
  257. package/src/notifications/conversation-pairing.ts +8 -6
  258. package/src/notifications/copy-composer.ts +32 -26
  259. package/src/notifications/decision-engine.ts +59 -7
  260. package/src/notifications/guardian-question-mode.ts +145 -155
  261. package/src/notifications/home-feed-side-effect.ts +28 -11
  262. package/src/notifications/notification-utils.ts +66 -0
  263. package/src/notifications/signal.ts +6 -0
  264. package/src/notifications/tool-approval-copy.ts +142 -0
  265. package/src/notifications/types.ts +19 -0
  266. package/src/permissions/threshold.ts +11 -0
  267. package/src/plugin-api/types.ts +16 -4
  268. package/src/plugins/defaults/compaction/window-manager.ts +44 -0
  269. package/src/plugins/defaults/index.ts +46 -0
  270. package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
  271. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
  272. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
  273. package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
  274. package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
  275. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
  276. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
  277. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
  278. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
  282. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
  283. package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
  284. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
  285. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
  286. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
  287. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
  288. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
  289. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
  290. package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
  291. package/src/prompts/__tests__/system-prompt.test.ts +100 -1
  292. package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
  293. package/src/prompts/normalize-onboarding.ts +2 -0
  294. package/src/prompts/persona-resolver.ts +3 -0
  295. package/src/prompts/system-prompt.ts +51 -2
  296. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
  297. package/src/prompts/templates/system-sections.ts +8 -3
  298. package/src/providers/call-site-routing.ts +6 -3
  299. package/src/providers/fireworks/client.ts +3 -0
  300. package/src/providers/inference/auth.ts +52 -46
  301. package/src/providers/model-intents.ts +2 -2
  302. package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
  303. package/src/providers/openai/chat-completions-provider.ts +47 -9
  304. package/src/providers/openai/coerce-object-args.ts +104 -0
  305. package/src/providers/retry.ts +8 -5
  306. package/src/providers/types.ts +10 -0
  307. package/src/runtime/__tests__/agent-wake.test.ts +629 -7
  308. package/src/runtime/access-request-helper.ts +16 -9
  309. package/src/runtime/agent-wake.ts +302 -51
  310. package/src/runtime/assistant-stream-state.ts +141 -8
  311. package/src/runtime/background-job-runner.ts +9 -0
  312. package/src/runtime/channel-approval-types.ts +1 -0
  313. package/src/runtime/channel-invite-transports/telegram.ts +6 -5
  314. package/src/runtime/channel-invite-transports/voice.ts +2 -2
  315. package/src/runtime/channel-invite-types.ts +4 -2
  316. package/src/runtime/channel-retry-sweep.ts +19 -41
  317. package/src/runtime/finalize-event-delivery.ts +72 -0
  318. package/src/runtime/guardian-action-message-composer.ts +0 -54
  319. package/src/runtime/http-server.ts +6 -14
  320. package/src/runtime/http-types.ts +0 -1
  321. package/src/runtime/message-composer-types.ts +0 -9
  322. package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
  323. package/src/runtime/middleware/auth.ts +27 -3
  324. package/src/runtime/middleware/rate-limiter.ts +28 -1
  325. package/src/runtime/migrations/vbundle-builder.ts +6 -5
  326. package/src/runtime/pending-interactions.ts +20 -1
  327. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
  328. package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
  329. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
  330. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
  331. package/src/runtime/routes/approval-routes.ts +35 -8
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
  333. package/src/runtime/routes/btw-routes.ts +37 -1
  334. package/src/runtime/routes/channel-delivery-routes.ts +11 -7
  335. package/src/runtime/routes/channel-route-definitions.ts +3 -0
  336. package/src/runtime/routes/channel-route-shared.ts +3 -1
  337. package/src/runtime/routes/consolidation-routes.ts +17 -13
  338. package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
  339. package/src/runtime/routes/conversation-list-routes.ts +41 -4
  340. package/src/runtime/routes/conversation-query-routes.ts +196 -11
  341. package/src/runtime/routes/conversation-routes.ts +15 -1
  342. package/src/runtime/routes/credential-prompt-routes.ts +39 -17
  343. package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
  344. package/src/runtime/routes/heartbeat-routes.ts +18 -13
  345. package/src/runtime/routes/image-generation-routes.ts +20 -2
  346. package/src/runtime/routes/inbound-message-handler.ts +13 -12
  347. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
  348. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
  349. package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
  350. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  351. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
  352. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
  353. package/src/runtime/routes/index.ts +6 -0
  354. package/src/runtime/routes/log-export/AGENTS.md +1 -1
  355. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  356. package/src/runtime/routes/migration-routes.ts +5 -9
  357. package/src/runtime/routes/plugins-routes.ts +26 -0
  358. package/src/runtime/routes/ps-routes.ts +10 -8
  359. package/src/runtime/routes/retrospective-routes.ts +235 -0
  360. package/src/runtime/routes/runs-pagination.ts +75 -0
  361. package/src/runtime/routes/schedule-routes.ts +367 -59
  362. package/src/runtime/routes/sounds-config-routes.ts +239 -0
  363. package/src/runtime/routes/surface-action-routes.ts +84 -4
  364. package/src/runtime/routes/workflow-routes.test.ts +372 -0
  365. package/src/runtime/routes/workflow-routes.ts +363 -0
  366. package/src/runtime/routes/workspace-routes.test.ts +61 -1
  367. package/src/runtime/routes/workspace-routes.ts +26 -1
  368. package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
  369. package/src/runtime/services/analyze-conversation.ts +26 -13
  370. package/src/schedule/inference-profile.ts +28 -0
  371. package/src/schedule/schedule-store.ts +152 -4
  372. package/src/schedule/scheduler-types.ts +6 -0
  373. package/src/schedule/scheduler.ts +96 -0
  374. package/src/security/secret-allowlist.ts +1 -1
  375. package/src/skills/path-classifier.ts +1 -1
  376. package/src/tools/apps/executors.ts +23 -0
  377. package/src/tools/apps/resolve-app-id.ts +42 -0
  378. package/src/tools/browser/browser-execution.ts +9 -11
  379. package/src/tools/credentials/broker.ts +4 -4
  380. package/src/tools/credentials/vault.ts +26 -137
  381. package/src/tools/executor.ts +69 -0
  382. package/src/tools/flag-gated-tools.test.ts +76 -0
  383. package/src/tools/permission-checker.ts +8 -1
  384. package/src/tools/registry.ts +51 -0
  385. package/src/tools/schedule/create.ts +77 -1
  386. package/src/tools/schedule/list.ts +1 -0
  387. package/src/tools/schedule/update.ts +73 -1
  388. package/src/tools/skills/execute.ts +56 -0
  389. package/src/tools/terminal/shell.ts +1 -1
  390. package/src/tools/ui-surface/definitions.ts +91 -2
  391. package/src/tools/workflows/manage-workflows.ts +183 -0
  392. package/src/tools/workflows/run-workflow.test.ts +442 -0
  393. package/src/tools/workflows/run-workflow.ts +88 -0
  394. package/src/types/onboarding-context.ts +2 -0
  395. package/src/usage/attribution.ts +24 -0
  396. package/src/util/canonicalize-identity.ts +12 -3
  397. package/src/util/platform.ts +17 -17
  398. package/src/watcher/__tests__/engine.test.ts +24 -0
  399. package/src/watcher/__tests__/telemetry.test.ts +135 -0
  400. package/src/watcher/engine.ts +7 -0
  401. package/src/watcher/telemetry.ts +74 -0
  402. package/src/workflows/capabilities.test.ts +365 -0
  403. package/src/workflows/capabilities.ts +359 -0
  404. package/src/workflows/deterministic-stringify.ts +27 -0
  405. package/src/workflows/engine-integration.test.ts +656 -0
  406. package/src/workflows/engine.test.ts +1144 -0
  407. package/src/workflows/engine.ts +1078 -0
  408. package/src/workflows/fanout-load.test.ts +168 -0
  409. package/src/workflows/journal-store.test.ts +369 -0
  410. package/src/workflows/journal-store.ts +470 -0
  411. package/src/workflows/leaf-runner.test.ts +704 -0
  412. package/src/workflows/leaf-runner.ts +589 -0
  413. package/src/workflows/library.test.ts +134 -0
  414. package/src/workflows/library.ts +124 -0
  415. package/src/workflows/run-manager.test.ts +711 -0
  416. package/src/workflows/run-manager.ts +593 -0
  417. package/src/workflows/sandbox-escape.test.ts +339 -0
  418. package/src/workflows/sandbox.test.ts +251 -0
  419. package/src/workflows/sandbox.ts +447 -0
  420. package/src/workspace/adaptive-thinking-repair.ts +33 -11
  421. package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
  422. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
  423. package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
  424. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
  425. package/src/workspace/migrations/031-drop-user-md.ts +1 -4
  426. package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
  427. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
  428. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
  429. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
  430. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
  431. package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
  432. package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
  433. package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
  434. package/src/workspace/migrations/registry.ts +6 -0
  435. package/src/workspace/migrations/runner.ts +1 -1
  436. package/tsconfig.json +1 -1
  437. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
  438. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
  439. package/src/__tests__/guardian-action-store.test.ts +0 -106
  440. package/src/daemon/guardian-action-generators.ts +0 -71
  441. package/src/memory/guardian-action-store.ts +0 -484
  442. package/src/runtime/guardian-action-grant-minter.ts +0 -150
@@ -0,0 +1,417 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import type { SlackChannelConfigResult } from "../daemon/handlers/config-slack-channel.js";
4
+ import type { SecretPromptResult } from "../permissions/secret-prompter.js";
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Mutable mock state (closed over by the mock factories below)
8
+ // ---------------------------------------------------------------------------
9
+
10
+ let capturedSecretParams: Record<string, unknown> | undefined;
11
+ let capturedMetadata: Record<string, unknown> | undefined;
12
+ let secretResult: SecretPromptResult = {
13
+ value: "secret-value",
14
+ delivery: "store",
15
+ };
16
+ let allowOneTimeSend = true;
17
+ let existingMetadata: Record<string, unknown> | undefined;
18
+ let slackConfigResult: SlackChannelConfigResult;
19
+ let slackConfigArgs: Array<(string | undefined)[]>;
20
+ let secureKeyWrites: Array<{ key: string; value: string }>;
21
+ let transientInjections: Array<{
22
+ service: string;
23
+ field: string;
24
+ value: string;
25
+ }>;
26
+ let syncedServices: string[];
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Mocks for the route's collaborators
30
+ // ---------------------------------------------------------------------------
31
+
32
+ mock.module("../util/logger.js", () => ({
33
+ getLogger: () =>
34
+ new Proxy({} as Record<string, unknown>, {
35
+ get: () => () => {},
36
+ }),
37
+ }));
38
+
39
+ mock.module("../daemon/handlers/shared.js", () => ({
40
+ requestSecretStandalone: mock(
41
+ async (params: Record<string, unknown>): Promise<SecretPromptResult> => {
42
+ capturedSecretParams = params;
43
+ return secretResult;
44
+ },
45
+ ),
46
+ }));
47
+
48
+ mock.module("../tools/credentials/metadata-store.js", () => ({
49
+ assertMetadataWritable: () => {},
50
+ getCredentialMetadata: mock(() => existingMetadata),
51
+ upsertCredentialMetadata: mock(
52
+ (service: string, field: string, meta: Record<string, unknown>) => {
53
+ capturedMetadata = { service, field, ...meta };
54
+ },
55
+ ),
56
+ }));
57
+
58
+ mock.module("../security/secure-keys.js", () => ({
59
+ setSecureKeyAsync: mock(async (key: string, value: string) => {
60
+ secureKeyWrites.push({ key, value });
61
+ return true;
62
+ }),
63
+ }));
64
+
65
+ mock.module("../security/credential-key.js", () => ({
66
+ credentialKey: (service: string, field: string) => `${service}:${field}`,
67
+ }));
68
+
69
+ mock.module("../oauth/manual-token-connection.js", () => ({
70
+ syncManualTokenConnection: mock(async (service: string) => {
71
+ syncedServices.push(service);
72
+ }),
73
+ }));
74
+
75
+ mock.module("../config/loader.js", () => ({
76
+ getConfig: () => ({ secretDetection: { allowOneTimeSend } }),
77
+ }));
78
+
79
+ mock.module("../daemon/handlers/config-slack-channel.js", () => ({
80
+ setSlackChannelConfig: mock(
81
+ async (
82
+ botToken?: string,
83
+ appToken?: string,
84
+ userToken?: string,
85
+ ): Promise<SlackChannelConfigResult> => {
86
+ slackConfigArgs.push([botToken, appToken, userToken]);
87
+ return slackConfigResult;
88
+ },
89
+ ),
90
+ }));
91
+
92
+ mock.module("../tools/credentials/broker.js", () => ({
93
+ credentialBroker: {
94
+ injectTransient: mock((service: string, field: string, value: string) => {
95
+ transientInjections.push({ service, field, value });
96
+ }),
97
+ },
98
+ }));
99
+
100
+ import { ROUTES } from "../runtime/routes/credential-prompt-routes.js";
101
+
102
+ const promptRoute = ROUTES.find((r) => r.operationId === "credentials_prompt");
103
+
104
+ function slackResult(
105
+ overrides: Partial<SlackChannelConfigResult>,
106
+ ): SlackChannelConfigResult {
107
+ return {
108
+ success: true,
109
+ hasBotToken: false,
110
+ hasAppToken: false,
111
+ hasUserToken: false,
112
+ connected: false,
113
+ ...overrides,
114
+ };
115
+ }
116
+
117
+ type PromptResponse = {
118
+ ok: boolean;
119
+ error?: string;
120
+ service?: string;
121
+ field?: string;
122
+ message?: string;
123
+ };
124
+
125
+ describe("credentials/prompt route", () => {
126
+ beforeEach(() => {
127
+ capturedSecretParams = undefined;
128
+ capturedMetadata = undefined;
129
+ secretResult = { value: "secret-value", delivery: "store" };
130
+ allowOneTimeSend = true;
131
+ existingMetadata = undefined;
132
+ slackConfigResult = slackResult({});
133
+ slackConfigArgs = [];
134
+ secureKeyWrites = [];
135
+ transientInjections = [];
136
+ syncedServices = [];
137
+ });
138
+
139
+ test("forwards usageDescription as the prompt purpose and to metadata", async () => {
140
+ /**
141
+ * The CLI `credentials prompt` command exposes `--usage-description` to
142
+ * match the credential_store tool's prompt action. The route must thread
143
+ * it to the secure prompt (as `purpose`, shown to the user) and persist it
144
+ * to credential metadata (as `usageDescription`).
145
+ */
146
+ // GIVEN the credentials/prompt route is registered
147
+ expect(promptRoute).toBeDefined();
148
+
149
+ // WHEN a prompt request carries a usageDescription
150
+ const result = (await promptRoute!.handler({
151
+ body: {
152
+ service: "sentry",
153
+ field: "auth_token",
154
+ label: "Sentry Auth Token",
155
+ usageDescription: "Needed to read issues",
156
+ },
157
+ })) as PromptResponse;
158
+
159
+ // THEN the prompt is issued with the usageDescription as its purpose
160
+ expect(result.ok).toBe(true);
161
+ expect(capturedSecretParams?.purpose).toBe("Needed to read issues");
162
+
163
+ // AND the credential metadata records the usageDescription
164
+ expect(capturedMetadata?.usageDescription).toBe("Needed to read issues");
165
+ });
166
+
167
+ test("omits purpose when usageDescription is not provided", async () => {
168
+ /**
169
+ * usageDescription is optional, so a prompt without it must not invent a
170
+ * purpose or a metadata usageDescription.
171
+ */
172
+ // GIVEN a prompt request with no usageDescription
173
+ // WHEN the route handles it
174
+ const result = (await promptRoute!.handler({
175
+ body: {
176
+ service: "github",
177
+ field: "pat",
178
+ label: "GitHub PAT",
179
+ },
180
+ })) as PromptResponse;
181
+
182
+ // THEN no purpose is forwarded to the prompt
183
+ expect(result.ok).toBe(true);
184
+ expect(capturedSecretParams?.purpose).toBeUndefined();
185
+
186
+ // AND no usageDescription is written to metadata
187
+ expect(capturedMetadata?.usageDescription).toBeUndefined();
188
+ });
189
+
190
+ test("stores a non-slack credential to secure storage without a message", async () => {
191
+ /**
192
+ * The default delivery persists the value to secure storage and runs the
193
+ * manual-token connection sync, returning no message so the CLI prints its
194
+ * generic "Stored credential" line.
195
+ */
196
+ // GIVEN a standard store-delivery prompt for a non-slack credential
197
+ // WHEN the route handles it
198
+ const result = (await promptRoute!.handler({
199
+ body: { service: "stripe", field: "api_key", label: "Stripe API Key" },
200
+ })) as PromptResponse;
201
+
202
+ // THEN the value is written to secure storage
203
+ expect(result.ok).toBe(true);
204
+ expect(secureKeyWrites).toEqual([
205
+ { key: "stripe:api_key", value: "secret-value" },
206
+ ]);
207
+
208
+ // AND the manual token connection is synced for the service
209
+ expect(syncedServices).toEqual(["stripe"]);
210
+
211
+ // AND no message is returned (the CLI falls back to its default line)
212
+ expect(result.message).toBeUndefined();
213
+ });
214
+
215
+ test("connects the channel when a slack_channel token is provided", async () => {
216
+ /**
217
+ * Slack channel tokens must route through the Slack channel config so the
218
+ * channel actually connects, and the route surfaces the connection status
219
+ * to the CLI via `message`.
220
+ */
221
+ // GIVEN the Slack channel config reports a successful connection
222
+ slackConfigResult = slackResult({
223
+ connected: true,
224
+ teamName: "Acme",
225
+ botUsername: "acmebot",
226
+ });
227
+
228
+ // WHEN a slack_channel bot_token is submitted
229
+ const result = (await promptRoute!.handler({
230
+ body: {
231
+ service: "slack_channel",
232
+ field: "bot_token",
233
+ label: "Slack Bot Token",
234
+ },
235
+ })) as PromptResponse;
236
+
237
+ // THEN the token is routed through the Slack channel config, not secure storage
238
+ expect(slackConfigArgs).toEqual([["secret-value", undefined, undefined]]);
239
+ expect(secureKeyWrites).toEqual([]);
240
+
241
+ // AND the connection status is surfaced as the response message
242
+ expect(result.ok).toBe(true);
243
+ expect(result.message).toBe("Slack channel connected to Acme (@acmebot).");
244
+ });
245
+
246
+ test("surfaces the warning when a slack_channel connection is incomplete", async () => {
247
+ /**
248
+ * When only one of the required Slack tokens is present the config returns a
249
+ * warning instead of a connection; the route surfaces it so the user knows
250
+ * to provide the remaining token.
251
+ */
252
+ // GIVEN the Slack channel config reports an incomplete connection
253
+ slackConfigResult = slackResult({
254
+ warning: "Slack channel connection incomplete; provide the app token.",
255
+ });
256
+
257
+ // WHEN a slack_channel app_token is submitted
258
+ const result = (await promptRoute!.handler({
259
+ body: {
260
+ service: "slack_channel",
261
+ field: "app_token",
262
+ label: "Slack App Token",
263
+ },
264
+ })) as PromptResponse;
265
+
266
+ // THEN the warning is surfaced as the response message
267
+ expect(result.ok).toBe(true);
268
+ expect(result.message).toBe(
269
+ "Slack channel connection incomplete; provide the app token.",
270
+ );
271
+ });
272
+
273
+ test("fails when the slack_channel config rejects the token", async () => {
274
+ /**
275
+ * A failed Slack configuration (e.g. invalid_auth) must surface as an error
276
+ * rather than silently storing an unusable token.
277
+ */
278
+ // GIVEN the Slack channel config rejects the token
279
+ slackConfigResult = slackResult({ success: false, error: "invalid_auth" });
280
+
281
+ // WHEN a slack_channel bot_token is submitted
282
+ const result = (await promptRoute!.handler({
283
+ body: {
284
+ service: "slack_channel",
285
+ field: "bot_token",
286
+ label: "Slack Bot Token",
287
+ },
288
+ })) as PromptResponse;
289
+
290
+ // THEN the route returns the configuration error
291
+ expect(result.ok).toBe(false);
292
+ expect(result.error).toBe("invalid_auth");
293
+ });
294
+
295
+ test("injects a one-time credential without persisting it", async () => {
296
+ /**
297
+ * One-time-send delivery hands the value to the broker for the next
298
+ * operation and must never write it to secure storage.
299
+ */
300
+ // GIVEN one-time send is enabled and the user chose transient delivery
301
+ allowOneTimeSend = true;
302
+ secretResult = { value: "secret-value", delivery: "transient_send" };
303
+
304
+ // WHEN a non-slack credential is submitted for one-time use
305
+ const result = (await promptRoute!.handler({
306
+ body: { service: "stripe", field: "api_key", label: "Stripe API Key" },
307
+ })) as PromptResponse;
308
+
309
+ // THEN the value is injected into the broker, not secure storage
310
+ expect(transientInjections).toEqual([
311
+ { service: "stripe", field: "api_key", value: "secret-value" },
312
+ ]);
313
+ expect(secureKeyWrites).toEqual([]);
314
+
315
+ // AND the response message notes the value was not saved
316
+ expect(result.ok).toBe(true);
317
+ expect(result.message).toContain("One-time credential provided");
318
+ });
319
+
320
+ test("rejects one-time send when it is disabled in config", async () => {
321
+ /**
322
+ * Transient delivery requires the secretDetection.allowOneTimeSend config
323
+ * flag; otherwise the value is rejected rather than injected.
324
+ */
325
+ // GIVEN one-time send is disabled and the user chose transient delivery
326
+ allowOneTimeSend = false;
327
+ secretResult = { value: "secret-value", delivery: "transient_send" };
328
+
329
+ // WHEN a credential is submitted for one-time use
330
+ const result = (await promptRoute!.handler({
331
+ body: { service: "stripe", field: "api_key", label: "Stripe API Key" },
332
+ })) as PromptResponse;
333
+
334
+ // THEN the request is rejected and nothing is injected
335
+ expect(result.ok).toBe(false);
336
+ expect(result.error).toContain("one-time send is not enabled");
337
+ expect(transientInjections).toEqual([]);
338
+ });
339
+
340
+ test("rejects one-time send for slack_channel credentials", async () => {
341
+ /**
342
+ * Slack channel tokens must be saved so the channel can connect; a one-time
343
+ * send for them is rejected.
344
+ */
345
+ // GIVEN the user chose transient delivery for a slack_channel token
346
+ secretResult = { value: "secret-value", delivery: "transient_send" };
347
+
348
+ // WHEN a slack_channel bot_token is submitted for one-time use
349
+ const result = (await promptRoute!.handler({
350
+ body: {
351
+ service: "slack_channel",
352
+ field: "bot_token",
353
+ label: "Slack Bot Token",
354
+ },
355
+ })) as PromptResponse;
356
+
357
+ // THEN the request is rejected without injecting or connecting
358
+ expect(result.ok).toBe(false);
359
+ expect(result.error).toContain("must be saved to secure storage");
360
+ expect(transientInjections).toEqual([]);
361
+ expect(slackConfigArgs).toEqual([]);
362
+ });
363
+
364
+ test("omitting allowed-tools/-domains forwards undefined so an existing policy is preserved", async () => {
365
+ /**
366
+ * Rotating an existing credential without policy flags must not wipe its
367
+ * allowed tools/domains. The route forwards `undefined` (not `[]`) for
368
+ * omitted flags so the metadata store's partial-update path leaves the
369
+ * existing policy untouched.
370
+ */
371
+ // GIVEN a credential already exists with an allowed-tools policy
372
+ existingMetadata = {
373
+ service: "github",
374
+ field: "pat",
375
+ allowedTools: ["bash"],
376
+ allowedDomains: ["github.com"],
377
+ };
378
+
379
+ // WHEN it is re-prompted without allowed-tools or allowed-domains
380
+ const result = (await promptRoute!.handler({
381
+ body: { service: "github", field: "pat", label: "GitHub PAT" },
382
+ })) as PromptResponse;
383
+
384
+ // THEN the metadata upsert receives undefined for both lists
385
+ expect(result.ok).toBe(true);
386
+ expect(capturedMetadata).toBeDefined();
387
+ expect(capturedMetadata!.allowedTools).toBeUndefined();
388
+ expect(capturedMetadata!.allowedDomains).toBeUndefined();
389
+ });
390
+
391
+ test("forwards provided allowed-tools/-domains to credential metadata", async () => {
392
+ /**
393
+ * When the caller does supply policy flags they must reach the metadata
394
+ * store verbatim, including an explicit empty array used to set a deny-all
395
+ * policy.
396
+ */
397
+ // GIVEN a prompt request that supplies an allowed-tools list and a
398
+ // deny-all (empty) allowed-domains list
399
+ // WHEN the route handles it
400
+ const result = (await promptRoute!.handler({
401
+ body: {
402
+ service: "stripe",
403
+ field: "api_key",
404
+ label: "Stripe API Key",
405
+ allowedTools: ["make_authenticated_request"],
406
+ allowedDomains: [],
407
+ },
408
+ })) as PromptResponse;
409
+
410
+ // THEN the metadata upsert receives the supplied lists verbatim
411
+ expect(result.ok).toBe(true);
412
+ expect(capturedMetadata!.allowedTools).toEqual([
413
+ "make_authenticated_request",
414
+ ]);
415
+ expect(capturedMetadata!.allowedDomains).toEqual([]);
416
+ });
417
+ });
@@ -166,6 +166,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
166
166
  // Any new import must be reviewed for secret-leak risk and added here.
167
167
  const ALLOWED_IMPORTERS = new Set([
168
168
  "tools/credentials/vault.ts", // credential store tool
169
+ "credential-execution/prompted-credential.ts", // shared prompt-action persistence (stores secret via setSecureKeyAsync)
169
170
  "tools/credentials/broker.ts", // brokered credential access
170
171
  "tools/network/web-search.ts", // web search API key lookup
171
172
  "daemon/handlers/config-telegram.ts", // Telegram bot token management
@@ -842,6 +842,43 @@ describe("credential_store tool", () => {
842
842
  expect(metadata!.allowedDomains).toEqual([]);
843
843
  });
844
844
 
845
+ test("store rotation without policy fields preserves the existing policy", async () => {
846
+ /**
847
+ * Re-storing (rotating) a credential without policy fields must keep the
848
+ * policy it was originally stored with, rather than resetting it to a
849
+ * deny-all empty policy.
850
+ */
851
+ // GIVEN a credential stored with an allowed-tools/-domains policy
852
+ await credentialStoreTool.execute(
853
+ {
854
+ action: "store",
855
+ service: "rotation-svc",
856
+ field: "token",
857
+ value: "v1",
858
+ allowed_tools: ["browser_fill_credential"],
859
+ allowed_domains: ["example.com"],
860
+ },
861
+ _ctx,
862
+ );
863
+
864
+ // WHEN it is re-stored with a new value but no policy fields
865
+ const result = await credentialStoreTool.execute(
866
+ {
867
+ action: "store",
868
+ service: "rotation-svc",
869
+ field: "token",
870
+ value: "v2",
871
+ },
872
+ _ctx,
873
+ );
874
+
875
+ // THEN the original policy is preserved
876
+ expect(result.isError).toBe(false);
877
+ const metadata = getCredentialMetadata("rotation-svc", "token");
878
+ expect(metadata!.allowedTools).toEqual(["browser_fill_credential"]);
879
+ expect(metadata!.allowedDomains).toEqual(["example.com"]);
880
+ });
881
+
845
882
  test("store rejects invalid policy input", async () => {
846
883
  const result = await credentialStoreTool.execute(
847
884
  {
@@ -50,7 +50,11 @@ describe("schedule_syntax column migration", () => {
50
50
  reuse_conversation INTEGER NOT NULL DEFAULT 0,
51
51
  script TEXT,
52
52
  wake_conversation_id TEXT,
53
+ workflow_name TEXT,
54
+ workflow_args_json TEXT,
53
55
  timeout_ms INTEGER,
56
+ inference_profile TEXT,
57
+ capabilities_json TEXT,
54
58
  description TEXT NOT NULL DEFAULT '',
55
59
  created_at INTEGER NOT NULL,
56
60
  updated_at INTEGER NOT NULL
@@ -138,6 +142,16 @@ describe("schedule_syntax column migration", () => {
138
142
  } catch {
139
143
  /* already exists */
140
144
  }
145
+ try {
146
+ raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_name TEXT`);
147
+ } catch {
148
+ /* already exists */
149
+ }
150
+ try {
151
+ raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_args_json TEXT`);
152
+ } catch {
153
+ /* already exists */
154
+ }
141
155
  migrateScheduleSourceConversation(db);
142
156
  migrateScheduleDescription(db);
143
157
 
@@ -203,6 +217,16 @@ describe("schedule_syntax column migration", () => {
203
217
  } catch {
204
218
  /* ok */
205
219
  }
220
+ try {
221
+ raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_name TEXT`);
222
+ } catch {
223
+ /* ok */
224
+ }
225
+ try {
226
+ raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_args_json TEXT`);
227
+ } catch {
228
+ /* ok */
229
+ }
206
230
  migrateScheduleSourceConversation(db);
207
231
  migrateScheduleSourceConversation(db);
208
232
  migrateScheduleDescription(db);