@vellumai/assistant 0.8.10 → 0.8.11-staging.1

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 (400) hide show
  1. package/bun.lock +62 -1
  2. package/docs/workspace-tools.md +196 -0
  3. package/examples/plugins/echo/README.md +3 -3
  4. package/knip.json +1 -0
  5. package/openapi.yaml +460 -128
  6. package/package.json +2 -1
  7. package/scripts/build-plugin-api.ts +299 -0
  8. package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
  9. package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
  10. package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
  12. package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
  13. package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
  14. package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
  15. package/src/__tests__/agent-loop-thinking.test.ts +4 -0
  16. package/src/__tests__/agent-loop.test.ts +578 -5
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  20. package/src/__tests__/btw-routes.test.ts +0 -1
  21. package/src/__tests__/build-persisted-content.test.ts +75 -1
  22. package/src/__tests__/catalog-install-normalize.test.ts +141 -0
  23. package/src/__tests__/ces-startup-timeout.test.ts +60 -0
  24. package/src/__tests__/compaction-events.test.ts +1 -0
  25. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  26. package/src/__tests__/context-overflow-reducer.test.ts +264 -124
  27. package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
  28. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  29. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
  30. package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
  31. package/src/__tests__/conversation-agent-loop.test.ts +131 -551
  32. package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
  33. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  34. package/src/__tests__/conversation-fork-crud.test.ts +259 -0
  35. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  36. package/src/__tests__/conversation-lifecycle.test.ts +257 -1
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
  39. package/src/__tests__/conversation-queue.test.ts +1 -39
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
  41. package/src/__tests__/conversation-skill-tools.test.ts +491 -5
  42. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  43. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  44. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  45. package/src/__tests__/conversation-store.test.ts +74 -0
  46. package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
  47. package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
  48. package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
  49. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
  52. package/src/__tests__/corrected-target.test.ts +93 -0
  53. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
  54. package/src/__tests__/credential-execution-tools.test.ts +23 -11
  55. package/src/__tests__/credential-security-invariants.test.ts +6 -1
  56. package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
  57. package/src/__tests__/device-id.test.ts +70 -1
  58. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  59. package/src/__tests__/empty-response-hook.test.ts +242 -66
  60. package/src/__tests__/external-plugin-loader.test.ts +0 -31
  61. package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
  62. package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
  63. package/src/__tests__/history-repair-hook.test.ts +228 -3
  64. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +254 -9
  66. package/src/__tests__/identity-routes.test.ts +1 -0
  67. package/src/__tests__/image-recovery-hook.test.ts +387 -0
  68. package/src/__tests__/injector-chain.test.ts +5 -4
  69. package/src/__tests__/injector-v3-suppression.test.ts +373 -47
  70. package/src/__tests__/intent-routing.test.ts +7 -0
  71. package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
  72. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  73. package/src/__tests__/oauth-store.test.ts +0 -85
  74. package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
  75. package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
  76. package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
  77. package/src/__tests__/persist-unsendable-image.test.ts +4 -4
  78. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  79. package/src/__tests__/plugin-bootstrap.test.ts +82 -73
  80. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  81. package/src/__tests__/plugin-types.test.ts +0 -8
  82. package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
  83. package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
  84. package/src/__tests__/registry.test.ts +240 -1
  85. package/src/__tests__/require-fresh-approval.test.ts +3 -0
  86. package/src/__tests__/schedule-routes.test.ts +116 -1
  87. package/src/__tests__/schedule-store.test.ts +28 -0
  88. package/src/__tests__/schedule-tools.test.ts +94 -1
  89. package/src/__tests__/server-history-render.test.ts +39 -0
  90. package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
  91. package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
  92. package/src/__tests__/skills.test.ts +202 -0
  93. package/src/__tests__/slim-skill-category.test.ts +195 -0
  94. package/src/__tests__/strip-memory-injections.test.ts +33 -39
  95. package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
  96. package/src/__tests__/title-generate-hook.test.ts +9 -7
  97. package/src/__tests__/tool-audit-listener.test.ts +264 -1
  98. package/src/__tests__/tool-error-hook.test.ts +4 -3
  99. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
  100. package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
  101. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
  102. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  103. package/src/__tests__/tools-get-route.test.ts +202 -0
  104. package/src/__tests__/workspace-tool-loader.test.ts +319 -0
  105. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  106. package/src/agent/loop.ts +569 -319
  107. package/src/api/events/tool-result.ts +9 -0
  108. package/src/api/events/tool-use-start.ts +7 -0
  109. package/src/api/index.ts +10 -0
  110. package/src/api/responses/conversation-message.ts +135 -27
  111. package/src/api/responses/memory-v3-selection-log.ts +4 -4
  112. package/src/approvals/guardian-request-resolvers.ts +26 -0
  113. package/src/browser-session/backends/host-bridge.ts +29 -0
  114. package/src/browser-session/index.ts +1 -0
  115. package/src/browser-session/types.ts +5 -1
  116. package/src/cli/commands/__tests__/schedules.test.ts +62 -4
  117. package/src/cli/commands/__tests__/skills.test.ts +53 -0
  118. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  119. package/src/cli/commands/inference-providers.ts +0 -8
  120. package/src/cli/commands/plugins.ts +2 -2
  121. package/src/cli/commands/schedules.ts +27 -4
  122. package/src/cli/commands/skills.ts +187 -146
  123. package/src/cli/commands/tools.ts +106 -0
  124. package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
  125. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
  126. package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
  127. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
  128. package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
  129. package/src/cli/lib/install-from-github.ts +187 -117
  130. package/src/cli/lib/plugin-catalog-cache.ts +9 -9
  131. package/src/cli/lib/plugin-details.ts +38 -68
  132. package/src/cli/lib/plugin-marketplace.ts +42 -14
  133. package/src/cli/lib/search-plugins.ts +29 -129
  134. package/src/cli/program.ts +2 -0
  135. package/src/config/bundled-skills/acp/SKILL.md +1 -0
  136. package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
  137. package/src/config/bundled-skills/app-control/SKILL.md +1 -0
  138. package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
  139. package/src/config/bundled-skills/contacts/SKILL.md +1 -0
  140. package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
  141. package/src/config/bundled-skills/followups/SKILL.md +1 -0
  142. package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
  143. package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
  144. package/src/config/bundled-skills/messaging/SKILL.md +1 -0
  145. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
  146. package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
  147. package/src/config/bundled-skills/schedule/SKILL.md +1 -0
  148. package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
  149. package/src/config/bundled-skills/sequences/SKILL.md +1 -0
  150. package/src/config/bundled-skills/settings/SKILL.md +1 -0
  151. package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
  152. package/src/config/bundled-skills/subagent/SKILL.md +1 -0
  153. package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
  154. package/src/config/env-registry.ts +23 -0
  155. package/src/config/feature-flag-registry.json +17 -81
  156. package/src/config/loader.ts +5 -22
  157. package/src/config/schema.ts +2 -0
  158. package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
  159. package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
  160. package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
  161. package/src/config/schemas/compaction-logs.ts +79 -0
  162. package/src/config/schemas/memory-v2.ts +0 -8
  163. package/src/config/schemas/memory-v3.ts +104 -33
  164. package/src/config/seed-inference-profiles.ts +1 -1
  165. package/src/config/skills.ts +117 -47
  166. package/src/context/compactor.ts +11 -0
  167. package/src/context/strip-injections.ts +38 -4
  168. package/src/credential-execution/feature-gates.ts +0 -21
  169. package/src/credential-execution/startup-timeout.ts +32 -4
  170. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
  171. package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
  172. package/src/daemon/conversation-agent-loop.ts +123 -658
  173. package/src/daemon/conversation-error.ts +6 -33
  174. package/src/daemon/conversation-lifecycle.ts +1 -1
  175. package/src/daemon/conversation-runtime-assembly.ts +183 -22
  176. package/src/daemon/conversation-skill-tools.ts +137 -8
  177. package/src/daemon/conversation-slash.ts +0 -14
  178. package/src/daemon/conversation-store.ts +2 -19
  179. package/src/daemon/conversation-tool-setup.ts +87 -1
  180. package/src/daemon/conversation.ts +119 -50
  181. package/src/daemon/external-plugins-bootstrap.ts +36 -95
  182. package/src/daemon/handlers/config-channels.ts +11 -2
  183. package/src/daemon/handlers/shared.ts +9 -1
  184. package/src/daemon/handlers/skills.ts +10 -4
  185. package/src/daemon/host-app-control-proxy.ts +72 -57
  186. package/src/daemon/host-browser-proxy.ts +117 -22
  187. package/src/daemon/lifecycle.ts +34 -5
  188. package/src/daemon/message-protocol.ts +0 -7
  189. package/src/daemon/message-types/schedules.ts +1 -0
  190. package/src/daemon/message-types/skills.ts +17 -0
  191. package/src/daemon/providers-setup.ts +3 -0
  192. package/src/daemon/server.ts +3 -3
  193. package/src/daemon/tool-setup-types.ts +9 -3
  194. package/src/daemon/trust-context.ts +23 -0
  195. package/src/daemon/workspace-tools-watcher.ts +324 -0
  196. package/src/events/tool-audit-listener.ts +78 -16
  197. package/src/events/tool-metrics-listener.ts +2 -5
  198. package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
  199. package/src/memory/__tests__/conversation-queries.test.ts +176 -0
  200. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
  201. package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
  202. package/src/memory/conversation-crud.ts +134 -3
  203. package/src/memory/conversation-queries.ts +64 -7
  204. package/src/memory/db-init.ts +14 -0
  205. package/src/memory/embedding-backend.test.ts +130 -1
  206. package/src/memory/embedding-backend.ts +79 -106
  207. package/src/memory/embedding-gemini.ts +5 -0
  208. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
  209. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
  210. package/src/memory/graph/conversation-graph-memory.ts +36 -25
  211. package/src/memory/graph/tool-handlers.ts +3 -0
  212. package/src/memory/job-handlers/cleanup.ts +3 -1
  213. package/src/memory/jobs-store.ts +0 -28
  214. package/src/memory/jobs-worker.ts +10 -22
  215. package/src/memory/memory-marker.ts +29 -0
  216. package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
  217. package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
  218. package/src/memory/migrations/270-schedule-description.ts +36 -0
  219. package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
  220. package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
  221. package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
  222. package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
  223. package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
  224. package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
  225. package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
  226. package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
  227. package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
  228. package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
  229. package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
  230. package/src/memory/migrations/index.ts +10 -0
  231. package/src/memory/migrations/registry.ts +8 -0
  232. package/src/memory/schema/conversations.ts +16 -0
  233. package/src/memory/schema/infrastructure.ts +26 -0
  234. package/src/memory/skill-loaded-events-store.test.ts +160 -0
  235. package/src/memory/skill-loaded-events-store.ts +95 -0
  236. package/src/memory/tool-executed-events-store.test.ts +219 -0
  237. package/src/memory/tool-executed-events-store.ts +102 -0
  238. package/src/memory/tool-usage-store.ts +15 -3
  239. package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
  240. package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
  241. package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
  242. package/src/memory/v2/__tests__/page-store.test.ts +33 -0
  243. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
  244. package/src/memory/v2/activation-store.ts +50 -1
  245. package/src/memory/v2/consolidation-job.ts +113 -29
  246. package/src/memory/v2/injected-block-slugs.ts +79 -0
  247. package/src/memory/v2/injection.ts +6 -1
  248. package/src/memory/v2/prompts/consolidation.ts +414 -13
  249. package/src/memory/v2/router.ts +2 -28
  250. package/src/memory/v2/static-context.ts +1 -1
  251. package/src/memory/v2/types.ts +16 -0
  252. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  253. package/src/notifications/access-request-copy.ts +298 -0
  254. package/src/notifications/adapters/slack.ts +3 -3
  255. package/src/notifications/adapters/telegram.ts +2 -1
  256. package/src/notifications/copy-composer.ts +49 -267
  257. package/src/notifications/decision-engine.ts +16 -35
  258. package/src/notifications/home-feed-side-effect.ts +1 -6
  259. package/src/oauth/oauth-store.ts +0 -9
  260. package/src/permissions/checker.test.ts +83 -1
  261. package/src/permissions/checker.ts +25 -2
  262. package/src/permissions/gateway-threshold-reader.test.ts +182 -0
  263. package/src/permissions/gateway-threshold-reader.ts +80 -0
  264. package/src/platform/client.ts +1 -3
  265. package/src/platform/feature-gate.ts +3 -12
  266. package/src/plugin-api/constants.ts +4 -2
  267. package/src/plugin-api/index.ts +63 -11
  268. package/src/plugin-api/types.ts +236 -71
  269. package/src/plugins/defaults/compaction/compact.ts +66 -2
  270. package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
  271. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  272. package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
  273. package/src/plugins/defaults/compaction/window-manager.ts +303 -1
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
  275. package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
  276. package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
  277. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
  278. package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
  279. package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
  280. package/src/plugins/defaults/history-repair/terminal.ts +39 -2
  281. package/src/plugins/defaults/image-recovery/detect.ts +25 -0
  282. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
  283. package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
  284. package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
  285. package/src/plugins/defaults/image-recovery/package.json +14 -0
  286. package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
  287. package/src/plugins/defaults/index.ts +71 -5
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
  289. package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
  291. package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
  304. package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
  305. package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
  306. package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
  307. package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
  308. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
  309. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
  310. package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
  311. package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
  312. package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
  313. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
  314. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
  315. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
  316. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
  317. package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
  318. package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
  319. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
  320. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
  321. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
  322. package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
  323. package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
  324. package/src/plugins/pipeline.ts +8 -5
  325. package/src/plugins/types.ts +5 -51
  326. package/src/providers/cache-control.ts +26 -0
  327. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
  328. package/src/providers/model-catalog.ts +13 -1
  329. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
  330. package/src/providers/openai/chat-completions-provider.ts +46 -0
  331. package/src/providers/openai/responses-provider.ts +45 -0
  332. package/src/providers/registry.ts +0 -8
  333. package/src/runtime/__tests__/agent-wake.test.ts +0 -1
  334. package/src/runtime/agent-wake.ts +13 -0
  335. package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
  336. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
  337. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
  338. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
  339. package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
  340. package/src/runtime/routes/acp-routes.test.ts +106 -0
  341. package/src/runtime/routes/acp-routes.ts +248 -2
  342. package/src/runtime/routes/browser-tabs-routes.ts +1 -1
  343. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  344. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
  345. package/src/runtime/routes/consolidation-routes.ts +6 -82
  346. package/src/runtime/routes/conversation-list-routes.ts +6 -0
  347. package/src/runtime/routes/conversation-management-routes.ts +71 -0
  348. package/src/runtime/routes/identity-routes.ts +8 -0
  349. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  350. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
  351. package/src/runtime/routes/plugins-routes.ts +34 -45
  352. package/src/runtime/routes/schedule-routes.ts +43 -5
  353. package/src/runtime/routes/settings-routes.ts +140 -15
  354. package/src/runtime/routes/skills-routes.ts +18 -6
  355. package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
  356. package/src/runtime/services/conversation-serializer.ts +38 -1
  357. package/src/runtime/verification-outbound-actions.ts +147 -2
  358. package/src/runtime/verification-templates.ts +29 -3
  359. package/src/schedule/schedule-store.ts +19 -0
  360. package/src/skills/catalog-install.ts +77 -13
  361. package/src/tasks/task-scheduler.ts +1 -0
  362. package/src/telemetry/types.ts +66 -1
  363. package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
  364. package/src/telemetry/usage-telemetry-reporter.ts +213 -20
  365. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
  366. package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
  367. package/src/tools/browser/browser-execution.ts +27 -9
  368. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
  369. package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
  370. package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
  371. package/src/tools/browser/cdp-client/factory.ts +217 -17
  372. package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
  373. package/src/tools/browser/cdp-client/types.ts +22 -2
  374. package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
  375. package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
  376. package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
  377. package/src/tools/executor.ts +39 -7
  378. package/src/tools/registry.ts +387 -5
  379. package/src/tools/schedule/create.ts +16 -0
  380. package/src/tools/schedule/list.ts +12 -4
  381. package/src/tools/schedule/update.ts +12 -0
  382. package/src/tools/skills/load.ts +11 -6
  383. package/src/tools/terminal/safe-env.ts +2 -0
  384. package/src/tools/types.ts +65 -9
  385. package/src/tools/workspace-tools/loader.ts +673 -0
  386. package/src/usage/attribution.ts +28 -0
  387. package/src/util/device-id.ts +17 -3
  388. package/src/util/platform.ts +16 -0
  389. package/tsconfig.plugin-api.json +13 -0
  390. package/src/__tests__/plugin-external-api.test.ts +0 -68
  391. package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
  392. package/src/daemon/message-types/browser.ts +0 -10
  393. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  394. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
  395. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
  396. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
  397. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
  398. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
  399. package/src/plugins/external-api.ts +0 -114
  400. package/src/plugins/plugin-skill-contributions.ts +0 -292
@@ -354,6 +354,9 @@ function getDetailedHealth() {
354
354
  ces: {
355
355
  connected: cesClient?.isReady() ?? false,
356
356
  },
357
+ capabilities: {
358
+ memoryOptOut: true,
359
+ },
357
360
  ...(profiler ? { profiler } : {}),
358
361
  };
359
362
  }
@@ -645,6 +648,10 @@ const cesHealthSchema = z.object({
645
648
  connected: z.boolean(),
646
649
  });
647
650
 
651
+ const healthCapabilitiesSchema = z.object({
652
+ memoryOptOut: z.boolean(),
653
+ });
654
+
648
655
  const healthDiskSchema = z.object({
649
656
  path: z.string(),
650
657
  totalMb: z.number(),
@@ -677,6 +684,7 @@ const detailedHealthSchema = z.object({
677
684
  cpu: healthCpuSchema,
678
685
  migrations: healthMigrationsSchema,
679
686
  ces: cesHealthSchema,
687
+ capabilities: healthCapabilitiesSchema,
680
688
  profiler: profilerStatusSchema.optional(),
681
689
  });
682
690
 
@@ -23,7 +23,7 @@ import {
23
23
  findByInviteCodeHash,
24
24
  findByInviteCodeHashAnyChannel,
25
25
  } from "../../../memory/invite-store.js";
26
- import { MESSAGE_PREVIEW_MAX_LENGTH } from "../../../notifications/copy-composer.js";
26
+ import { MESSAGE_PREVIEW_MAX_LENGTH } from "../../../notifications/access-request-copy.js";
27
27
  import { resolveGuardianName } from "../../../prompts/user-reference.js";
28
28
  import { getLogger } from "../../../util/logger.js";
29
29
  import { truncate } from "../../../util/truncate.js";
@@ -332,12 +332,12 @@ export async function enforceIngressAcl(
332
332
 
333
333
  return {
334
334
  resolvedMember: null,
335
- earlyResponse: ({
335
+ earlyResponse: {
336
336
  accepted: true,
337
337
  denied: true,
338
338
  reason: "verification_challenge_sent",
339
339
  verificationSessionId: slackVerifyResult.sessionId,
340
- }),
340
+ },
341
341
  };
342
342
  }
343
343
  }
@@ -375,12 +375,12 @@ export async function enforceIngressAcl(
375
375
 
376
376
  return {
377
377
  resolvedMember: null,
378
- earlyResponse: ({
378
+ earlyResponse: {
379
379
  accepted: true,
380
380
  denied: true,
381
381
  reason: "verification_challenge_sent",
382
382
  verificationSessionId: emailVerifyResult.sessionId,
383
- }),
383
+ },
384
384
  };
385
385
  }
386
386
  }
@@ -438,14 +438,14 @@ export async function enforceIngressAcl(
438
438
 
439
439
  return {
440
440
  resolvedMember: null,
441
- earlyResponse: ({
441
+ earlyResponse: {
442
442
  accepted: true,
443
443
  denied: true,
444
444
  reason: "not_a_member",
445
445
  // Include reply text so the gateway can deliver directly when
446
446
  // callback delivery failed (e.g. signing-key mismatch → 401).
447
447
  ...(!replyDelivered && { replyText }),
448
- }),
448
+ },
449
449
  };
450
450
  }
451
451
  }
@@ -610,12 +610,12 @@ export async function enforceIngressAcl(
610
610
 
611
611
  return {
612
612
  resolvedMember,
613
- earlyResponse: ({
613
+ earlyResponse: {
614
614
  accepted: true,
615
615
  denied: true,
616
616
  reason: "verification_challenge_sent",
617
617
  verificationSessionId: slackVerifyResult.sessionId,
618
- }),
618
+ },
619
619
  };
620
620
  }
621
621
  }
@@ -682,12 +682,12 @@ export async function enforceIngressAcl(
682
682
  }
683
683
  return {
684
684
  resolvedMember,
685
- earlyResponse: ({
685
+ earlyResponse: {
686
686
  accepted: true,
687
687
  denied: true,
688
688
  reason: `member_${channelStatusToMemberStatus(resolvedMember.channel.status)}`,
689
689
  ...(!inactiveReplyDelivered && { replyText: inactiveReplyText }),
690
- }),
690
+ },
691
691
  };
692
692
  }
693
693
  }
@@ -722,15 +722,14 @@ export async function enforceIngressAcl(
722
722
  }
723
723
  return {
724
724
  resolvedMember,
725
- earlyResponse: ({
725
+ earlyResponse: {
726
726
  accepted: true,
727
727
  denied: true,
728
728
  reason: "policy_deny",
729
729
  ...(!denyReplyDelivered && { replyText: denyReplyText }),
730
- }),
730
+ },
731
731
  };
732
732
  }
733
-
734
733
  }
735
734
  }
736
735
 
@@ -786,11 +785,11 @@ async function handleInviteTokenIntercept(params: {
786
785
  );
787
786
 
788
787
  if (dedupResult.duplicate) {
789
- return ({
788
+ return {
790
789
  accepted: true,
791
790
  duplicate: true,
792
791
  eventId: dedupResult.eventId,
793
- });
792
+ };
794
793
  }
795
794
 
796
795
  const outcome = redeemInvite({
@@ -835,11 +834,11 @@ async function handleInviteTokenIntercept(params: {
835
834
  }
836
835
  }
837
836
  markProcessed(dedupResult.eventId);
838
- return ({
837
+ return {
839
838
  accepted: true,
840
839
  eventId: dedupResult.eventId,
841
840
  inviteRedemption: "already_member",
842
- });
841
+ };
843
842
  }
844
843
 
845
844
  const replyText = getInviteRedemptionReply(outcome);
@@ -861,22 +860,22 @@ async function handleInviteTokenIntercept(params: {
861
860
 
862
861
  if (outcome.ok && outcome.type === "redeemed") {
863
862
  markProcessed(dedupResult.eventId);
864
- return ({
863
+ return {
865
864
  accepted: true,
866
865
  eventId: dedupResult.eventId,
867
866
  inviteRedemption: "redeemed",
868
867
  memberId: outcome.memberId,
869
- });
868
+ };
870
869
  }
871
870
 
872
871
  // Failed redemption — inform the user and deny
873
872
  markProcessed(dedupResult.eventId);
874
- return ({
873
+ return {
875
874
  accepted: true,
876
875
  eventId: dedupResult.eventId,
877
876
  denied: true,
878
877
  inviteRedemption: outcome.reason,
879
- });
878
+ };
880
879
  }
881
880
 
882
881
  // ---------------------------------------------------------------------------
@@ -942,11 +941,11 @@ async function handleInviteCodeIntercept(params: {
942
941
  );
943
942
 
944
943
  if (dedupResult.duplicate) {
945
- return ({
944
+ return {
946
945
  accepted: true,
947
946
  duplicate: true,
948
947
  eventId: dedupResult.eventId,
949
- });
948
+ };
950
949
  }
951
950
 
952
951
  const mismatchReply = "This invite is not valid for this channel.";
@@ -965,12 +964,12 @@ async function handleInviteCodeIntercept(params: {
965
964
  }
966
965
  }
967
966
  markProcessed(dedupResult.eventId);
968
- return ({
967
+ return {
969
968
  accepted: true,
970
969
  eventId: dedupResult.eventId,
971
970
  denied: true,
972
971
  inviteRedemption: "channel_mismatch",
973
- });
972
+ };
974
973
  }
975
974
  return null;
976
975
  }
@@ -988,11 +987,11 @@ async function handleInviteCodeIntercept(params: {
988
987
  );
989
988
 
990
989
  if (dedupResult.duplicate) {
991
- return ({
990
+ return {
992
991
  accepted: true,
993
992
  duplicate: true,
994
993
  eventId: dedupResult.eventId,
995
- });
994
+ };
996
995
  }
997
996
 
998
997
  let outcome: ReturnType<typeof redeemInviteByCode>;
@@ -1046,11 +1045,11 @@ async function handleInviteCodeIntercept(params: {
1046
1045
  }
1047
1046
  }
1048
1047
  markProcessed(dedupResult.eventId);
1049
- return ({
1048
+ return {
1050
1049
  accepted: true,
1051
1050
  eventId: dedupResult.eventId,
1052
1051
  inviteRedemption: "already_member",
1053
- });
1052
+ };
1054
1053
  }
1055
1054
 
1056
1055
  const replyText = getInviteRedemptionReply(outcome);
@@ -1072,22 +1071,22 @@ async function handleInviteCodeIntercept(params: {
1072
1071
 
1073
1072
  if (outcome.ok && outcome.type === "redeemed") {
1074
1073
  markProcessed(dedupResult.eventId);
1075
- return ({
1074
+ return {
1076
1075
  accepted: true,
1077
1076
  eventId: dedupResult.eventId,
1078
1077
  inviteRedemption: "redeemed",
1079
1078
  memberId: outcome.memberId,
1080
- });
1079
+ };
1081
1080
  }
1082
1081
 
1083
1082
  // Failed redemption (expired, revoked, etc.) — inform and deny
1084
1083
  markProcessed(dedupResult.eventId);
1085
- return ({
1084
+ return {
1086
1085
  accepted: true,
1087
1086
  eventId: dedupResult.eventId,
1088
1087
  denied: true,
1089
1088
  inviteRedemption: !outcome.ok ? outcome.reason : undefined,
1090
- });
1089
+ };
1091
1090
  }
1092
1091
 
1093
1092
  // ---------------------------------------------------------------------------
@@ -10,7 +10,6 @@
10
10
 
11
11
  import { z } from "zod";
12
12
 
13
- import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
14
13
  import { getConfigReadOnly } from "../../config/loader.js";
15
14
  import { getDb } from "../../memory/db-connection.js";
16
15
  import {
@@ -44,22 +43,6 @@ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
44
43
  // ---------------------------------------------------------------------------
45
44
 
46
45
  const providerConnectionResponseSchema = ProviderConnectionSchema;
47
- const OPENAI_COMPATIBLE_ENDPOINTS_FLAG = "openai-compatible-endpoints";
48
-
49
- function openAICompatibleEndpointsEnabled(): boolean {
50
- return isAssistantFeatureFlagEnabled(
51
- OPENAI_COMPATIBLE_ENDPOINTS_FLAG,
52
- getConfigReadOnly(),
53
- );
54
- }
55
-
56
- function rejectDisabledOpenAICompatibleProvider(provider: string): void {
57
- if (provider !== "openai-compatible") return;
58
- if (openAICompatibleEndpointsEnabled()) return;
59
- throw new BadRequestError(
60
- "OpenAI-compatible endpoints are disabled. Enable the openai-compatible-endpoints feature flag to configure this provider.",
61
- );
62
- }
63
46
 
64
47
  // ---------------------------------------------------------------------------
65
48
  // Custom provider field parsing (openai-compatible base_url + models)
@@ -170,18 +153,10 @@ async function parseCustomProviderFields(
170
153
 
171
154
  function handleListConnections({ queryParams = {} }: RouteHandlerArgs) {
172
155
  const provider = queryParams.provider;
173
- if (provider) rejectDisabledOpenAICompatibleProvider(provider);
174
156
  const connections = listConnections(
175
157
  getDb(),
176
158
  provider ? { provider } : undefined,
177
159
  );
178
- if (!openAICompatibleEndpointsEnabled()) {
179
- return {
180
- connections: connections.filter(
181
- (conn) => conn.provider !== "openai-compatible",
182
- ),
183
- };
184
- }
185
160
  return { connections };
186
161
  }
187
162
 
@@ -191,12 +166,6 @@ function handleGetConnection({ pathParams = {} }: RouteHandlerArgs) {
191
166
 
192
167
  const conn = getConnection(getDb(), name);
193
168
  if (!conn) throw new NotFoundError(`Connection "${name}" not found.`);
194
- if (
195
- conn.provider === "openai-compatible" &&
196
- !openAICompatibleEndpointsEnabled()
197
- ) {
198
- throw new NotFoundError(`Connection "${name}" not found.`);
199
- }
200
169
 
201
170
  return conn;
202
171
  }
@@ -216,8 +185,6 @@ async function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
216
185
  `Invalid provider "${String(provider)}". Valid: ${VALID_CONNECTION_PROVIDERS.join(", ")}`,
217
186
  );
218
187
  }
219
- rejectDisabledOpenAICompatibleProvider(providerResult.data);
220
-
221
188
  const authResult = AuthSchema.safeParse(auth);
222
189
  if (!authResult.success) {
223
190
  throw new BadRequestError(`Invalid auth: ${authResult.error.message}`);
@@ -280,12 +247,6 @@ async function handleUpdateConnection({
280
247
 
281
248
  const existing = getConnection(getDb(), name);
282
249
  if (!existing) throw new NotFoundError(`Connection "${name}" not found.`);
283
- if (
284
- existing.provider === "openai-compatible" &&
285
- !openAICompatibleEndpointsEnabled()
286
- ) {
287
- throw new NotFoundError(`Connection "${name}" not found.`);
288
- }
289
250
 
290
251
  const auth = body.auth;
291
252
  const authResult = AuthSchema.safeParse(auth);
@@ -355,12 +316,6 @@ function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
355
316
  if (!existing) {
356
317
  throw new NotFoundError(`Connection "${name}" not found.`);
357
318
  }
358
- if (
359
- existing.provider === "openai-compatible" &&
360
- !openAICompatibleEndpointsEnabled()
361
- ) {
362
- throw new NotFoundError(`Connection "${name}" not found.`);
363
- }
364
319
 
365
320
  // Managed connections are write-protected: `seedCanonicalConnections` would
366
321
  // re-upsert them on the next daemon boot anyway, so a successful delete here
@@ -99,27 +99,20 @@ const pluginsListResponseSchema = z.object({
99
99
  });
100
100
 
101
101
  const pluginMatchSourceSchema = z
102
- .discriminatedUnion("kind", [
103
- z.object({
104
- kind: z.literal("first-party"),
105
- }),
106
- z.object({
107
- kind: z.literal("github"),
108
- repo: z
109
- .string()
110
- .describe("`owner/repo` of the external plugin repository."),
111
- path: z
112
- .string()
113
- .optional()
114
- .describe(
115
- "Directory within the repo, when the plugin is not at the root.",
116
- ),
117
- ref: z.string().describe("Pinned git ref the plugin is fetched from."),
118
- }),
119
- ])
120
- .describe(
121
- "Origin of the match: a first-party in-repo plugin or a whitelisted external one.",
122
- );
102
+ .object({
103
+ kind: z.literal("github"),
104
+ repo: z
105
+ .string()
106
+ .describe("`owner/repo` of the external plugin repository."),
107
+ path: z
108
+ .string()
109
+ .optional()
110
+ .describe(
111
+ "Directory within the repo, when the plugin is not at the root.",
112
+ ),
113
+ ref: z.string().describe("Pinned git ref the plugin is fetched from."),
114
+ })
115
+ .describe("Origin of the match: a whitelisted external plugin repository.");
123
116
 
124
117
  const pluginSearchMatchSchema = z.object({
125
118
  name: z
@@ -128,7 +121,7 @@ const pluginSearchMatchSchema = z.object({
128
121
  path: z
129
122
  .string()
130
123
  .describe(
131
- "Human-readable origin: a repo-relative path (`experimental/plugins/<name>`) for first-party plugins or a `github:owner/repo@ref` locator for external ones.",
124
+ "Human-readable origin: a `github:owner/repo@ref` locator for the external plugin.",
132
125
  ),
133
126
  description: z
134
127
  .string()
@@ -186,7 +179,11 @@ const pluginDetailsResponseSchema = z.object({
186
179
  .describe(
187
180
  "Resolved version (installed copy first, then repo `package.json`).",
188
181
  ),
189
- source: pluginMatchSourceSchema,
182
+ source: pluginMatchSourceSchema
183
+ .nullable()
184
+ .describe(
185
+ "Pinned origin from the marketplace entry, or null when an installed copy has no catalog entry.",
186
+ ),
190
187
  readme: z
191
188
  .string()
192
189
  .nullable()
@@ -199,9 +196,7 @@ const pluginDetailsResponseSchema = z.object({
199
196
  const pluginInstallRequestSchema = z.object({
200
197
  name: z
201
198
  .string()
202
- .describe(
203
- "Install name to resolve against the catalog (first-party directory or marketplace entry).",
204
- ),
199
+ .describe("Install name to resolve against the marketplace catalog."),
205
200
  force: z
206
201
  .boolean()
207
202
  .optional()
@@ -255,9 +250,7 @@ interface PluginMatchView {
255
250
  name: string;
256
251
  path: string;
257
252
  description?: string;
258
- source:
259
- | { kind: "first-party" }
260
- | { kind: "github"; repo: string; path?: string; ref: string };
253
+ source: { kind: "github"; repo: string; path?: string; ref: string };
261
254
  }
262
255
 
263
256
  /**
@@ -269,15 +262,12 @@ function projectMatch(m: PluginSearchMatch): PluginMatchView {
269
262
  const view: PluginMatchView = {
270
263
  name: m.name,
271
264
  path: m.path,
272
- source:
273
- m.source.kind === "github"
274
- ? {
275
- kind: "github",
276
- repo: m.source.repo,
277
- ref: m.source.ref,
278
- ...(m.source.path !== undefined ? { path: m.source.path } : {}),
279
- }
280
- : { kind: "first-party" },
265
+ source: {
266
+ kind: "github",
267
+ repo: m.source.repo,
268
+ ref: m.source.ref,
269
+ ...(m.source.path !== undefined ? { path: m.source.path } : {}),
270
+ },
281
271
  };
282
272
  if (m.description !== undefined) {
283
273
  view.description = m.description;
@@ -441,11 +431,10 @@ async function handleInstallPlugin({ body = {} }: RouteHandlerArgs) {
441
431
  // The ref is pinned to the curated `DEFAULT_PLUGIN_REF` rather than taken
442
432
  // from the request: a caller-supplied ref would let any `settings.write`
443
433
  // principal install from an unreviewed revision (a PR branch, fork ref,
444
- // ...) whose marketplace manifest or first-party plugin directory could
445
- // carry attacker code that the loader then dynamically imports. Installs
446
- // over HTTP therefore only ever resolve against the reviewed catalog on
447
- // the default ref. Operators who need another ref use the local CLI's
448
- // `assistant plugins install --ref`.
434
+ // ...) whose marketplace manifest could carry attacker code that the loader
435
+ // then dynamically imports. Installs over HTTP therefore only ever resolve
436
+ // against the reviewed catalog on the default ref. Operators who need
437
+ // another ref use the local CLI's `assistant plugins install --ref`.
449
438
  try {
450
439
  const result = await installPlugin(
451
440
  { name, ref: DEFAULT_PLUGIN_REF, force },
@@ -517,7 +506,7 @@ export const ROUTES: RouteDefinition[] = [
517
506
  },
518
507
  summary: "Search the plugin catalog",
519
508
  description:
520
- "List installable plugins from the canonical `vellum-ai/vellum-assistant` catalog at `experimental/plugins/`. The query is an ECMAScript regex matched case-insensitively against the directory name (e.g. `memory`, `^simple`). Empty query returns every entry. Mirrors the CLI's `assistant plugins search`.",
509
+ "List installable plugins from the curated `plugins/marketplace.json` catalog. The query is an ECMAScript regex matched case-insensitively against the plugin name (e.g. `memory`, `^simple`). Empty query returns every entry. Mirrors the CLI's `assistant plugins search`.",
521
510
  tags: ["plugins"],
522
511
  queryParams: [
523
512
  {
@@ -546,7 +535,7 @@ export const ROUTES: RouteDefinition[] = [
546
535
  },
547
536
  summary: "Install a plugin",
548
537
  description:
549
- "Install a plugin by name from the canonical source — a whitelisted `experimental/plugins/marketplace.json` entry, else the first-party `experimental/plugins/<name>/` convention. Always resolves against the curated default git ref (no caller-supplied ref): installing from an unreviewed revision would bypass the marketplace/first-party curation boundary and let attacker-controlled code be loaded. Materializes the plugin under `<workspaceDir>/plugins/<name>/`; the assistant must be restarted to load it. Mirrors the CLI's `assistant plugins install <name>`. An already-installed name without `force` returns 409; a name that resolves to nothing returns 404. Sibling to `POST /v1/skills/install`.",
538
+ "Install a plugin by name from the canonical source — a whitelisted `plugins/marketplace.json` entry. Always resolves against the curated default git ref (no caller-supplied ref): installing from an unreviewed revision would bypass the marketplace curation boundary and let attacker-controlled code be loaded. Materializes the plugin under `<workspaceDir>/plugins/<name>/`; the assistant must be restarted to load it. Mirrors the CLI's `assistant plugins install <name>`. An already-installed name without `force` returns 409; a name that resolves to nothing returns 404. Sibling to `POST /v1/skills/install`.",
550
539
  tags: ["plugins"],
551
540
  requestBody: pluginInstallRequestSchema,
552
541
  responseBody: pluginInstallResponseSchema,
@@ -28,6 +28,7 @@ import {
28
28
  getSchedule,
29
29
  getScheduleRuns,
30
30
  listSchedules,
31
+ type ScheduleJob,
31
32
  setScheduleRunConversationId,
32
33
  updateSchedule,
33
34
  } from "../../schedule/schedule-store.js";
@@ -64,7 +65,8 @@ const scheduleSchema = z.object({
64
65
  createdFromConversationId: z.string().nullable(),
65
66
  createdFromConversationExists: z.boolean(),
66
67
  createdFromConversationArchivedAt: z.number().nullable(),
67
- description: z.string().nullable(),
68
+ description: z.string(),
69
+ cadenceDescription: z.string(),
68
70
  mode: z.enum(["notify", "execute", "script", "wake"]),
69
71
  status: z.enum(["active", "firing", "fired", "cancelled"]),
70
72
  routingIntent: z.enum(["single_channel", "multi_channel", "all_channels"]),
@@ -125,6 +127,18 @@ function getCreatedFromConversationState(
125
127
  return state;
126
128
  }
127
129
 
130
+ function getCadenceDescription(
131
+ job: Pick<ScheduleJob, "syntax" | "cronExpression" | "expression">,
132
+ ): string {
133
+ if (job.cronExpression === null) {
134
+ return describeCronExpression(job.cronExpression);
135
+ }
136
+ if (job.syntax === "cron") {
137
+ return describeCronExpression(job.cronExpression);
138
+ }
139
+ return job.expression ?? "";
140
+ }
141
+
128
142
  function handleListSchedules(queryParams: Record<string, string>) {
129
143
  const includeAll = queryParams.include_all === "true";
130
144
  const jobs = listSchedules();
@@ -161,10 +175,8 @@ function handleListSchedules(queryParams: Record<string, string>) {
161
175
  createdFromConversationId: j.createdFromConversationId,
162
176
  createdFromConversationExists: sourceConversation.exists,
163
177
  createdFromConversationArchivedAt: sourceConversation.archivedAt,
164
- description:
165
- j.syntax === "cron"
166
- ? describeCronExpression(j.cronExpression)
167
- : j.expression,
178
+ description: j.description,
179
+ cadenceDescription: getCadenceDescription(j),
168
180
  mode: j.mode,
169
181
  status: j.status,
170
182
  routingIntent: j.routingIntent,
@@ -180,6 +192,12 @@ function handleCreateSchedule(body: Record<string, unknown>) {
180
192
  const name = typeof body.name === "string" ? body.name.trim() : "";
181
193
  const expression =
182
194
  typeof body.expression === "string" ? body.expression.trim() : "";
195
+ const description =
196
+ body.description === undefined
197
+ ? undefined
198
+ : typeof body.description === "string"
199
+ ? body.description.trim()
200
+ : "";
183
201
  const message = typeof body.message === "string" ? body.message : "";
184
202
  const timezoneRaw =
185
203
  typeof body.timezone === "string" ? body.timezone.trim() : "";
@@ -190,6 +208,9 @@ function handleCreateSchedule(body: Record<string, unknown>) {
190
208
  if (!name) throw new BadRequestError("name is required");
191
209
  if (!expression) throw new BadRequestError("expression is required");
192
210
  if (!message) throw new BadRequestError("message is required");
211
+ if (description === "") {
212
+ throw new BadRequestError("description is required");
213
+ }
193
214
 
194
215
  // The settings UI only exposes execute mode for now. Other modes
195
216
  // remain reachable via the schedule_create LLM tool.
@@ -209,6 +230,7 @@ function handleCreateSchedule(body: Record<string, unknown>) {
209
230
  try {
210
231
  const job = createSchedule({
211
232
  name,
233
+ description,
212
234
  message,
213
235
  mode: "execute",
214
236
  enabled,
@@ -304,6 +326,15 @@ function handleUpdateSchedule(id: string, body: Record<string, unknown>) {
304
326
  }
305
327
  }
306
328
 
329
+ if ("description" in body) {
330
+ const description =
331
+ typeof body.description === "string" ? body.description.trim() : "";
332
+ if (!description) {
333
+ throw new BadRequestError("description is required");
334
+ }
335
+ updates.description = description;
336
+ }
337
+
307
338
  if (updates.timeoutMs != null) {
308
339
  if (typeof updates.timeoutMs !== "number") {
309
340
  throw new BadRequestError("timeoutMs must be a number or null");
@@ -460,6 +491,12 @@ export const ROUTES: RouteDefinition[] = [
460
491
  tags: ["schedules"],
461
492
  requestBody: z.object({
462
493
  name: z.string().describe("Display name"),
494
+ description: z
495
+ .string()
496
+ .describe(
497
+ "Authored schedule description. Defaults to the schedule name when omitted for backward compatibility.",
498
+ )
499
+ .optional(),
463
500
  expression: z.string().describe("Cron or RRULE expression"),
464
501
  message: z.string().describe("Message body to execute on each fire"),
465
502
  timezone: z
@@ -552,6 +589,7 @@ export const ROUTES: RouteDefinition[] = [
552
589
  tags: ["schedules"],
553
590
  requestBody: z.object({
554
591
  name: z.string().optional(),
592
+ description: z.string().optional(),
555
593
  expression: z.string().optional(),
556
594
  timezone: z.string().optional(),
557
595
  message: z.string().optional(),