@vellumai/assistant 0.8.1 → 0.8.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 (506) hide show
  1. package/ARCHITECTURE.md +2 -7
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +5 -0
  5. package/docker-init-apt-root.sh +94 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +325 -3
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  21. package/src/__tests__/anthropic-provider.test.ts +45 -0
  22. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  23. package/src/__tests__/app-executors.test.ts +220 -4
  24. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  25. package/src/__tests__/bundled-asset.test.ts +6 -6
  26. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  27. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  28. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  29. package/src/__tests__/clawhub.test.ts +75 -16
  30. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  31. package/src/__tests__/config-schema.test.ts +21 -0
  32. package/src/__tests__/config-set-route.test.ts +80 -0
  33. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  34. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  35. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  36. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  37. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  38. package/src/__tests__/context-token-estimator.test.ts +1 -0
  39. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  42. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  43. package/src/__tests__/conversation-error.test.ts +42 -3
  44. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  45. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  46. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  47. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  48. package/src/__tests__/conversation-pairing.test.ts +54 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-queue.test.ts +4 -1
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
  53. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  54. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  55. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  56. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  59. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  60. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  61. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  62. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  63. package/src/__tests__/dm-backfill.test.ts +121 -10
  64. package/src/__tests__/document-tool-security.test.ts +258 -0
  65. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  66. package/src/__tests__/edit-propagation.test.ts +33 -0
  67. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  68. package/src/__tests__/external-plugin-loader.test.ts +60 -36
  69. package/src/__tests__/filing-service.test.ts +140 -0
  70. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  71. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  72. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  73. package/src/__tests__/helpers/wait-for.ts +21 -0
  74. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  75. package/src/__tests__/history-repair.test.ts +73 -0
  76. package/src/__tests__/host-app-control-proxy.test.ts +266 -10
  77. package/src/__tests__/image-credentials.test.ts +1 -1
  78. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  79. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  80. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  81. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  82. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  83. package/src/__tests__/injector-chain.test.ts +10 -8
  84. package/src/__tests__/install-skill-routing.test.ts +155 -37
  85. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
  86. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  87. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  88. package/src/__tests__/llm-catalog-parity.test.ts +55 -13
  89. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
  90. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  91. package/src/__tests__/llm-usage-store.test.ts +114 -0
  92. package/src/__tests__/managed-profile-guard.test.ts +31 -29
  93. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  94. package/src/__tests__/managed-store.test.ts +84 -192
  95. package/src/__tests__/media-generate-image.test.ts +1 -1
  96. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  97. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  98. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  99. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  100. package/src/__tests__/openai-provider.test.ts +24 -0
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  102. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  103. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  104. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  105. package/src/__tests__/platform.test.ts +2 -0
  106. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  107. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  108. package/src/__tests__/plugin-external-api.test.ts +68 -0
  109. package/src/__tests__/plugin-registry.test.ts +0 -77
  110. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  111. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  112. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  113. package/src/__tests__/plugin-types.test.ts +3 -13
  114. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  115. package/src/__tests__/process-message-display-content.test.ts +421 -0
  116. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  117. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  118. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
  119. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  120. package/src/__tests__/schedule-routes.test.ts +50 -3
  121. package/src/__tests__/schedule-store.test.ts +94 -0
  122. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  123. package/src/__tests__/schema-transforms.test.ts +20 -0
  124. package/src/__tests__/search-skills-unified.test.ts +0 -5
  125. package/src/__tests__/server-history-render.test.ts +43 -0
  126. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  127. package/src/__tests__/skill-load-tool.test.ts +27 -89
  128. package/src/__tests__/skill-memory.test.ts +23 -3
  129. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  130. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  131. package/src/__tests__/skills-install-extract.test.ts +49 -38
  132. package/src/__tests__/skills-install-staging.test.ts +159 -0
  133. package/src/__tests__/skills-uninstall.test.ts +9 -41
  134. package/src/__tests__/skills.test.ts +51 -58
  135. package/src/__tests__/slack-channel-config.test.ts +9 -0
  136. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  137. package/src/__tests__/system-prompt.test.ts +737 -63
  138. package/src/__tests__/terminal-tools.test.ts +28 -1
  139. package/src/__tests__/thread-backfill.test.ts +557 -27
  140. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  141. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  142. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  143. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  145. package/src/__tests__/tool-executor.test.ts +16 -4
  146. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  147. package/src/__tests__/turn-events-store.test.ts +256 -0
  148. package/src/__tests__/twilio-routes.test.ts +4 -0
  149. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  150. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  151. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  152. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  153. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  154. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  155. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  156. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  157. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  158. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  159. package/src/acp/resolve-agent.ts +1 -1
  160. package/src/agent/image-optimize.ts +13 -5
  161. package/src/calls/voice-session-bridge.ts +61 -42
  162. package/src/channels/types.ts +108 -0
  163. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  164. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  165. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  166. package/src/cli/commands/changelog.ts +106 -42
  167. package/src/cli/commands/conversations.ts +102 -17
  168. package/src/cli/commands/default-action.ts +10 -53
  169. package/src/cli/commands/notifications.ts +329 -317
  170. package/src/cli/commands/plugins.ts +185 -0
  171. package/src/cli/commands/schedules.ts +391 -0
  172. package/src/cli/commands/telemetry.ts +40 -0
  173. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  174. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  177. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  178. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  179. package/src/cli/lib/cli-colors.ts +12 -0
  180. package/src/cli/lib/confirm-prompt.ts +79 -0
  181. package/src/cli/lib/install-from-github.ts +304 -0
  182. package/src/cli/lib/list-installed-plugins.ts +137 -0
  183. package/src/cli/lib/uninstall-plugin.ts +82 -0
  184. package/src/cli/lib/unknown-command.ts +111 -0
  185. package/src/cli/program.ts +38 -2
  186. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  187. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  188. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  189. package/src/config/bundled-skills/document/SKILL.md +23 -3
  190. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  191. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  192. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  193. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  194. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  195. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  196. package/src/config/bundled-tool-registry.ts +6 -0
  197. package/src/config/feature-flag-registry.json +41 -1
  198. package/src/config/loader.ts +64 -38
  199. package/src/config/schema.ts +7 -10
  200. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  201. package/src/config/schemas/channels.ts +8 -0
  202. package/src/config/schemas/compaction.ts +28 -0
  203. package/src/config/schemas/heartbeat.ts +9 -0
  204. package/src/config/schemas/llm-request-logs.ts +31 -7
  205. package/src/config/schemas/llm.ts +3 -0
  206. package/src/config/schemas/memory-retrieval.ts +18 -0
  207. package/src/config/schemas/tools.ts +14 -0
  208. package/src/config/skills.ts +3 -96
  209. package/src/context/compactor.ts +1047 -0
  210. package/src/context/token-estimator.ts +2 -2
  211. package/src/context/window-manager.ts +197 -1520
  212. package/src/credential-execution/managed-catalog.ts +37 -0
  213. package/src/credential-health/credential-health-service.ts +280 -19
  214. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
  215. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  216. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  217. package/src/daemon/approval-generators.ts +8 -6
  218. package/src/daemon/config-watcher.ts +94 -31
  219. package/src/daemon/conversation-agent-loop.ts +169 -9
  220. package/src/daemon/conversation-error.ts +171 -37
  221. package/src/daemon/conversation-lifecycle.ts +53 -40
  222. package/src/daemon/conversation-messaging.ts +25 -6
  223. package/src/daemon/conversation-process.ts +49 -12
  224. package/src/daemon/conversation-runtime-assembly.ts +16 -1
  225. package/src/daemon/conversation-slash.ts +12 -5
  226. package/src/daemon/conversation-store.ts +11 -4
  227. package/src/daemon/conversation-tool-setup.ts +39 -7
  228. package/src/daemon/conversation.ts +33 -1
  229. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  230. package/src/daemon/first-greeting.ts +22 -2
  231. package/src/daemon/handlers/config-model.ts +6 -5
  232. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  233. package/src/daemon/handlers/shared.ts +14 -5
  234. package/src/daemon/handlers/skills.ts +111 -108
  235. package/src/daemon/history-repair.ts +28 -1
  236. package/src/daemon/host-app-control-proxy.ts +98 -23
  237. package/src/daemon/lifecycle.ts +45 -35
  238. package/src/daemon/meet-host-supervisor.ts +5 -4
  239. package/src/daemon/memory-v2-startup.ts +49 -0
  240. package/src/daemon/message-protocol.ts +1 -0
  241. package/src/daemon/message-types/conversations.ts +25 -0
  242. package/src/daemon/message-types/messages.ts +61 -0
  243. package/src/daemon/message-types/subagents.ts +1 -0
  244. package/src/daemon/message-types/sync.ts +1 -0
  245. package/src/daemon/pkb-reminder-builder.test.ts +1 -1
  246. package/src/daemon/pkb-reminder-builder.ts +1 -1
  247. package/src/daemon/plugin-source-watcher.ts +146 -0
  248. package/src/daemon/process-message.ts +21 -3
  249. package/src/daemon/server.ts +11 -2
  250. package/src/daemon/skill-memory-refresh.ts +29 -0
  251. package/src/documents/document-store.ts +221 -3
  252. package/src/embedded/plugin-api.ts +40 -0
  253. package/src/filing/filing-service.ts +39 -0
  254. package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
  255. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  256. package/src/heartbeat/heartbeat-service.ts +41 -0
  257. package/src/home/__tests__/feed-types.test.ts +40 -0
  258. package/src/home/feed-types.ts +22 -0
  259. package/src/home/post-connect-feed.ts +1 -0
  260. package/src/index.ts +18 -1
  261. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  262. package/src/mcp/client.ts +20 -4
  263. package/src/media/image-credentials.ts +3 -3
  264. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  265. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  266. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  267. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  268. package/src/memory/__tests__/message-content.test.ts +35 -0
  269. package/src/memory/bookmark-crud.ts +42 -10
  270. package/src/memory/context-search/sources/conversations.ts +62 -2
  271. package/src/memory/context-search/sources/workspace.ts +4 -0
  272. package/src/memory/conversation-crud.ts +63 -19
  273. package/src/memory/conversation-queries.ts +110 -10
  274. package/src/memory/db-init.ts +6 -0
  275. package/src/memory/delivery-crud.ts +152 -5
  276. package/src/memory/embedding-backend.ts +4 -4
  277. package/src/memory/external-conversation-store.ts +66 -5
  278. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  279. package/src/memory/graph/conversation-graph-memory.ts +31 -15
  280. package/src/memory/graph/tools.ts +3 -3
  281. package/src/memory/indexer.ts +34 -29
  282. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  283. package/src/memory/jobs/embed-concept-page.ts +20 -11
  284. package/src/memory/jobs-worker.ts +6 -1
  285. package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
  286. package/src/memory/llm-request-log-source.ts +19 -52
  287. package/src/memory/llm-usage-store.ts +125 -5
  288. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  289. package/src/memory/message-content.ts +1 -1
  290. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  291. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  292. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  293. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  294. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  295. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  296. package/src/memory/migrations/index.ts +6 -0
  297. package/src/memory/migrations/registry.ts +8 -0
  298. package/src/memory/onboarding-events-store.ts +106 -0
  299. package/src/memory/schema/bookmarks.ts +0 -2
  300. package/src/memory/schema/calls.ts +1 -0
  301. package/src/memory/schema/inference.ts +1 -3
  302. package/src/memory/schema/infrastructure.ts +12 -0
  303. package/src/memory/turn-events-store.ts +127 -2
  304. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  305. package/src/memory/v2/__tests__/injection.test.ts +98 -8
  306. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  307. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  308. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  309. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  310. package/src/memory/v2/__tests__/router.test.ts +15 -0
  311. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  312. package/src/memory/v2/injection.ts +32 -6
  313. package/src/memory/v2/migration.ts +49 -19
  314. package/src/memory/v2/page-index.ts +35 -5
  315. package/src/memory/v2/prompts/router.ts +11 -8
  316. package/src/memory/v2/prompts/sweep.ts +2 -2
  317. package/src/memory/v2/qdrant.ts +135 -7
  318. package/src/memory/v2/router.ts +9 -8
  319. package/src/memory/v2/skill-store.ts +120 -35
  320. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  321. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  322. package/src/messaging/providers/slack/adapter.ts +43 -5
  323. package/src/messaging/providers/slack/client.ts +27 -0
  324. package/src/messaging/providers/slack/deep-link.ts +65 -0
  325. package/src/messaging/providers/slack/download.ts +104 -0
  326. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  327. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  328. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  329. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  330. package/src/messaging/providers/slack/types.ts +20 -1
  331. package/src/notifications/conversation-pairing.ts +2 -1
  332. package/src/notifications/decision-engine.ts +2 -1
  333. package/src/notifications/emit-signal.ts +20 -1
  334. package/src/notifications/home-feed-side-effect.ts +54 -0
  335. package/src/notifications/signal.ts +3 -1
  336. package/src/oauth/connection-resolver.ts +8 -4
  337. package/src/oauth/platform-connection.ts +6 -2
  338. package/src/oauth/seed-providers.ts +10 -1
  339. package/src/permissions/checker.ts +2 -0
  340. package/src/permissions/ipc-risk-types.ts +1 -0
  341. package/src/permissions/question-prompter.test.ts +416 -0
  342. package/src/permissions/question-prompter.ts +294 -0
  343. package/src/platform/client.test.ts +1 -1
  344. package/src/platform/client.ts +1 -1
  345. package/src/plugin-api/constants.ts +26 -0
  346. package/src/plugin-api/index.ts +34 -1
  347. package/src/plugin-api/types.ts +104 -22
  348. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  349. package/src/plugins/defaults/compaction.ts +0 -4
  350. package/src/plugins/defaults/empty-response.ts +0 -2
  351. package/src/plugins/defaults/history-repair.ts +0 -2
  352. package/src/plugins/defaults/injectors.ts +36 -3
  353. package/src/plugins/defaults/llm-call.ts +0 -2
  354. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  355. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  356. package/src/plugins/defaults/persistence.ts +0 -2
  357. package/src/plugins/defaults/title-generate.ts +0 -5
  358. package/src/plugins/defaults/token-estimate.ts +0 -2
  359. package/src/plugins/defaults/tool-error.ts +0 -7
  360. package/src/plugins/defaults/tool-execute.ts +0 -2
  361. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  362. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  363. package/src/plugins/external-api.ts +104 -0
  364. package/src/plugins/external-plugin-loader.ts +105 -32
  365. package/src/plugins/feature-gate.ts +22 -0
  366. package/src/plugins/pipeline.ts +37 -0
  367. package/src/plugins/registry.ts +48 -80
  368. package/src/plugins/types.ts +31 -26
  369. package/src/plugins/user-loader.ts +21 -2
  370. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  371. package/src/proactive-artifact/job.test.ts +37 -5
  372. package/src/prompts/__tests__/system-prompt.test.ts +12 -0
  373. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  374. package/src/prompts/normalize-onboarding.ts +27 -0
  375. package/src/prompts/sections.ts +302 -0
  376. package/src/prompts/system-prompt.ts +63 -166
  377. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  378. package/src/prompts/templates/system-sections.ts +173 -0
  379. package/src/providers/__tests__/inference.test.ts +22 -7
  380. package/src/providers/anthropic/client.ts +28 -28
  381. package/src/providers/connection-resolution.ts +7 -0
  382. package/src/providers/inference/adapter-factory.ts +41 -4
  383. package/src/providers/inference/connections.ts +74 -29
  384. package/src/providers/inference/resolve-auth.ts +12 -4
  385. package/src/providers/model-catalog.ts +294 -12
  386. package/src/providers/openai/chat-completions-provider.ts +10 -2
  387. package/src/providers/openrouter/client.ts +7 -0
  388. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  389. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  390. package/src/providers/provider-availability.ts +17 -2
  391. package/src/providers/provider-catalog-visibility.ts +36 -0
  392. package/src/providers/registry.ts +22 -14
  393. package/src/providers/retry.ts +47 -1
  394. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  395. package/src/runtime/agent-wake.ts +42 -14
  396. package/src/runtime/auth/route-policy.ts +8 -1
  397. package/src/runtime/btw-sidechain.ts +2 -0
  398. package/src/runtime/http-types.ts +19 -0
  399. package/src/runtime/migrations/origin-mode.ts +1 -1
  400. package/src/runtime/pending-interactions.ts +1 -0
  401. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  402. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  403. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
  404. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  405. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  406. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  407. package/src/runtime/routes/acp-routes.ts +5 -3
  408. package/src/runtime/routes/auth-routes.ts +1 -1
  409. package/src/runtime/routes/bookmark-routes.ts +5 -3
  410. package/src/runtime/routes/btw-routes.ts +5 -1
  411. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  412. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  413. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  414. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  415. package/src/runtime/routes/conversation-query-routes.ts +40 -35
  416. package/src/runtime/routes/conversation-routes.ts +90 -11
  417. package/src/runtime/routes/documents-routes.ts +25 -86
  418. package/src/runtime/routes/group-routes.ts +5 -0
  419. package/src/runtime/routes/inbound-conversation.ts +28 -8
  420. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  421. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  422. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  423. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  424. package/src/runtime/routes/index.ts +6 -0
  425. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  426. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  427. package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
  428. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  429. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  430. package/src/runtime/routes/integrations/twilio.ts +6 -13
  431. package/src/runtime/routes/notification-routes.ts +1 -1
  432. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  433. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  434. package/src/runtime/routes/question-routes.ts +259 -0
  435. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  436. package/src/runtime/routes/schedule-routes.ts +4 -7
  437. package/src/runtime/routes/subagents-routes.ts +57 -18
  438. package/src/runtime/routes/telemetry-routes.ts +27 -0
  439. package/src/runtime/routes/tts-routes.ts +27 -2
  440. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  441. package/src/runtime/routes/workspace-routes.ts +28 -0
  442. package/src/runtime/services/conversation-serializer.ts +39 -7
  443. package/src/runtime/sync/resource-sync-events.ts +93 -1
  444. package/src/schedule/schedule-store.ts +27 -2
  445. package/src/schedule/scheduler.ts +9 -1
  446. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  447. package/src/security/untrusted-content.ts +93 -8
  448. package/src/skills/catalog-files.ts +1 -1
  449. package/src/skills/catalog-install.ts +233 -116
  450. package/src/skills/clawhub.ts +70 -13
  451. package/src/skills/managed-store.ts +4 -119
  452. package/src/skills/skillssh-registry.ts +27 -48
  453. package/src/subagent/manager.ts +15 -7
  454. package/src/telemetry/types.ts +113 -1
  455. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  456. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  457. package/src/tools/apps/executors.ts +58 -7
  458. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  459. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  460. package/src/tools/browser/browser-execution.ts +15 -11
  461. package/src/tools/computer-use/definitions.ts +3 -3
  462. package/src/tools/credentials/vault.ts +1 -1
  463. package/src/tools/document/document-tool.ts +124 -1
  464. package/src/tools/filesystem/edit.ts +1 -1
  465. package/src/tools/filesystem/list.ts +1 -1
  466. package/src/tools/filesystem/read.ts +1 -1
  467. package/src/tools/filesystem/write.ts +5 -2
  468. package/src/tools/host-filesystem/transfer.ts +1 -1
  469. package/src/tools/host-terminal/host-shell.ts +1 -1
  470. package/src/tools/permission-checker.ts +1 -1
  471. package/src/tools/registry.ts +17 -7
  472. package/src/tools/schedule/create.ts +2 -2
  473. package/src/tools/schema-transforms.ts +7 -2
  474. package/src/tools/side-effects.ts +1 -0
  475. package/src/tools/skills/delete-managed.ts +4 -4
  476. package/src/tools/skills/execute.ts +1 -1
  477. package/src/tools/skills/scaffold-managed.ts +3 -2
  478. package/src/tools/subagent/notify-parent.ts +1 -1
  479. package/src/tools/system/request-permission.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +60 -1
  481. package/src/tools/tool-manifest.ts +2 -0
  482. package/src/tools/types.ts +72 -21
  483. package/src/tools/ui-surface/definitions.ts +6 -5
  484. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  485. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  486. package/src/types/onboarding-context.ts +2 -0
  487. package/src/util/errors.ts +17 -0
  488. package/src/util/platform.ts +10 -0
  489. package/src/watcher/__tests__/engine.test.ts +22 -0
  490. package/src/watcher/engine.ts +6 -2
  491. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  492. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  493. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  494. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  495. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  496. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  497. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  498. package/src/workspace/migrations/registry.ts +8 -0
  499. package/src/workspace/migrations/runner.ts +39 -9
  500. package/src/workspace/migrations/types.ts +4 -0
  501. package/examples/plugins/echo/bun.lock +0 -25
  502. package/src/__tests__/context-window-manager.test.ts +0 -2481
  503. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  504. package/src/context/prompts/compact.md +0 -26
  505. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  506. /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
@@ -0,0 +1,304 @@
1
+ import { z } from "zod";
2
+
3
+ import { QuestionPrompter } from "../../permissions/question-prompter.js";
4
+ import { RiskLevel } from "../../permissions/types.js";
5
+ import type { ToolDefinition } from "../../providers/types.js";
6
+ import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
7
+ import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
8
+
9
+ // ── Input schema ────────────────────────────────────────────────────
10
+ // Runtime validation lives in Zod; the wire-level definition surfaced
11
+ // to the LLM is the hand-written JSON Schema in getDefinition() below.
12
+ // (The codebase does not currently use zod-to-json-schema for tool defs,
13
+ // so the two are kept in sync manually.)
14
+
15
+ const OptionSchema = z.object({
16
+ id: z.string().min(1),
17
+ label: z.string().min(1),
18
+ description: z.string().optional(),
19
+ });
20
+
21
+ // One question in a (possibly single-element) batch. Intentionally has no
22
+ // `id` field — per-question ids are daemon-assigned (`q1`, `q2`, ...) inside
23
+ // the prompter, never supplied by the LLM. This keeps the LLM-facing schema
24
+ // smaller and removes a validation surface (no duplicate-id check, no
25
+ // length cap on ids).
26
+ const SingleQuestionSchema = z.object({
27
+ question: z.string().min(1),
28
+ description: z.string().optional(),
29
+ // 2–4 LLM-supplied options. The client renders a fixed 5th "Type
30
+ // something else" slot for free-text, so the model must keep the
31
+ // structured set to 4 or fewer.
32
+ options: z.array(OptionSchema).min(2).max(4),
33
+ freeTextPlaceholder: z.string().optional(),
34
+ });
35
+
36
+ // Cap at 5 questions per batch. Past that it starts to feel like a form,
37
+ // not a clarification — the model should be implementing, not asking. Any
38
+ // input with ≥6 entries is rejected with a clear Zod error.
39
+ const MAX_QUESTIONS_PER_BATCH = 5;
40
+
41
+ // Both the new batched shape (`questions[]`) and the legacy flat shape are
42
+ // accepted. `execute()` normalizes legacy callers into a one-element
43
+ // `questions` array before forwarding to the prompter.
44
+ const InputSchema = z
45
+ .object({
46
+ questions: z
47
+ .array(SingleQuestionSchema)
48
+ .min(1)
49
+ .max(MAX_QUESTIONS_PER_BATCH, {
50
+ message: `At most ${MAX_QUESTIONS_PER_BATCH} questions per batch; split into multiple turns if you need more.`,
51
+ })
52
+ .optional(),
53
+ // Legacy flat fields. Optional so batched callers can omit them; when
54
+ // present and `questions` is absent, they are normalized into a
55
+ // one-element batch in `execute()`.
56
+ question: z.string().min(1).optional(),
57
+ description: z.string().optional(),
58
+ options: z.array(OptionSchema).min(2).max(4).optional(),
59
+ freeTextPlaceholder: z.string().optional(),
60
+ })
61
+ .refine(
62
+ (v) =>
63
+ v.questions !== undefined ||
64
+ (v.question !== undefined && v.options !== undefined),
65
+ {
66
+ message:
67
+ "Provide `questions` (preferred) or the legacy flat fields (`question` + `options`).",
68
+ },
69
+ );
70
+
71
+ export type SingleQuestion = z.infer<typeof SingleQuestionSchema>;
72
+ export type AskQuestionInput = z.infer<typeof InputSchema>;
73
+
74
+ // ── Tool description ────────────────────────────────────────────────
75
+
76
+ const DESCRIPTION = [
77
+ "Use this tool whenever a request is ambiguous and can be resolved",
78
+ "by 2–4 plausible interpretations or discrete choices. Prefer it over",
79
+ "plain-text clarification — structured options are faster to answer and",
80
+ "remove guessing.",
81
+ "",
82
+ "When in doubt between (a) asking inline and (b) calling ask_question with",
83
+ "structured options: call ask_question. The structured choices are better UX.",
84
+ "",
85
+ 'Example: given a request like "schedule lunch with Alice next week" where there',
86
+ "are two plausible Alice contacts, ask which Alice with options like",
87
+ '`{id: "alice_work", label: "Alice (work)"}` and',
88
+ '`{id: "alice_personal", label: "Alice (personal)"}`.',
89
+ "",
90
+ "Batch related clarifications into one call by passing multiple entries in",
91
+ "`questions` (up to 5). Each question gets its own page with a Skip button.",
92
+ "",
93
+ "When NOT to use this tool:",
94
+ "- The answer is obvious from context or recent conversation.",
95
+ "- The question is genuinely open-ended (more than ~4 plausible answers) —",
96
+ " fall back to plain text.",
97
+ "- You're about to take a low-stakes reversible action and can adjust based",
98
+ " on feedback.",
99
+ "",
100
+ "If a question is skipped, proceed with reasonable defaults for that",
101
+ "question; if every question in the batch is skipped, stop interrupting",
102
+ "and use defaults across the board.",
103
+ "",
104
+ "Provide 2–4 options. A free-text fallback is always added by the UI — do not",
105
+ "include a 'something else' option yourself.",
106
+ "",
107
+ "Each option needs a stable `id` (the value the response carries back) and a",
108
+ "short human-readable `label`. Optional `description` adds one line of",
109
+ "context shown beneath the label.",
110
+ ].join("\n");
111
+
112
+ // ── Tool ────────────────────────────────────────────────────────────
113
+
114
+ export class AskQuestionTool implements Tool {
115
+ name = "ask_question";
116
+ description = DESCRIPTION;
117
+ category = "interaction";
118
+ defaultRiskLevel = RiskLevel.Low;
119
+
120
+ // Override hook for tests: lets a test replace the prompter factory
121
+ // without monkey-patching the module. Default factory wires the real
122
+ // broadcastMessage so the question reaches every connected client.
123
+ private prompterFactory: () => Pick<QuestionPrompter, "prompt">;
124
+
125
+ constructor(
126
+ prompterFactory: () => Pick<QuestionPrompter, "prompt"> = () =>
127
+ new QuestionPrompter({ broadcastMessage }),
128
+ ) {
129
+ this.prompterFactory = prompterFactory;
130
+ }
131
+
132
+ getDefinition(): ToolDefinition {
133
+ // Shared option-schema fragment used by both the batched `questions[]`
134
+ // shape and the legacy flat `options` field.
135
+ const optionItemsSchema = {
136
+ type: "object",
137
+ properties: {
138
+ id: {
139
+ type: "string",
140
+ description:
141
+ "Stable identifier for this option (returned verbatim in the response).",
142
+ },
143
+ label: {
144
+ type: "string",
145
+ description: "Short human-readable label.",
146
+ },
147
+ description: {
148
+ type: "string",
149
+ description: "Optional one-line context shown beneath the label.",
150
+ },
151
+ },
152
+ required: ["id", "label"],
153
+ } as const;
154
+
155
+ return {
156
+ name: this.name,
157
+ description: this.description,
158
+ input_schema: {
159
+ type: "object",
160
+ properties: {
161
+ // ── Recommended shape ─────────────────────────────────────
162
+ questions: {
163
+ type: "array",
164
+ minItems: 1,
165
+ maxItems: MAX_QUESTIONS_PER_BATCH,
166
+ description: `Recommended shape. 1–${MAX_QUESTIONS_PER_BATCH} clarifying questions to ask in a single turn. Use a batch when several independent ambiguities block progress; ask one at a time when they're sequentially dependent. Past ${MAX_QUESTIONS_PER_BATCH} questions you should be implementing, not asking.`,
167
+ items: {
168
+ type: "object",
169
+ properties: {
170
+ question: {
171
+ type: "string",
172
+ description: "The clarifying question to display.",
173
+ },
174
+ description: {
175
+ type: "string",
176
+ description:
177
+ "Optional one-line context shown beneath the question.",
178
+ },
179
+ options: {
180
+ type: "array",
181
+ minItems: 2,
182
+ maxItems: 4,
183
+ description:
184
+ "2–4 structured options. The UI always appends a free-text fallback slot, so do not include a 'something else' option here.",
185
+ items: optionItemsSchema,
186
+ },
187
+ freeTextPlaceholder: {
188
+ type: "string",
189
+ description:
190
+ "Optional placeholder text shown inside the free-text fallback input.",
191
+ },
192
+ },
193
+ required: ["question", "options"],
194
+ },
195
+ },
196
+ // ── Legacy single-question fields ─────────────────────────
197
+ // Kept optional so existing prompt caches and any single-question
198
+ // callers continue to work. New callers should use `questions`.
199
+ question: {
200
+ type: "string",
201
+ description:
202
+ "Legacy: the single clarifying question. Prefer `questions[]` for new code.",
203
+ },
204
+ description: {
205
+ type: "string",
206
+ description:
207
+ "Legacy: optional one-line context shown beneath the question. Prefer `questions[].description`.",
208
+ },
209
+ options: {
210
+ type: "array",
211
+ minItems: 2,
212
+ maxItems: 4,
213
+ description:
214
+ "Legacy: 2–4 structured options. Prefer `questions[].options`. The UI always appends a free-text fallback slot, so do not include a 'something else' option here.",
215
+ items: optionItemsSchema,
216
+ },
217
+ freeTextPlaceholder: {
218
+ type: "string",
219
+ description:
220
+ "Legacy: optional placeholder text for the free-text fallback input. Prefer `questions[].freeTextPlaceholder`.",
221
+ },
222
+ },
223
+ // No top-level `required` — caller must supply either `questions`
224
+ // or the legacy flat trio (`question` + `options`). Enforced in Zod.
225
+ },
226
+ };
227
+ }
228
+
229
+ async execute(
230
+ input: Record<string, unknown>,
231
+ context: ToolContext,
232
+ ): Promise<ToolExecutionResult> {
233
+ const parsed = InputSchema.safeParse(input);
234
+ if (!parsed.success) {
235
+ return {
236
+ content: `Invalid input: ${parsed.error.message}`,
237
+ isError: true,
238
+ };
239
+ }
240
+
241
+ // Normalize legacy flat input into a one-element `questions` batch so
242
+ // downstream code only has to deal with the batched shape. The refine
243
+ // above guarantees `question` and `options` are present whenever
244
+ // `questions` is absent.
245
+ const questions: SingleQuestion[] = parsed.data.questions ?? [
246
+ {
247
+ question: parsed.data.question!,
248
+ description: parsed.data.description,
249
+ options: parsed.data.options!,
250
+ freeTextPlaceholder: parsed.data.freeTextPlaceholder,
251
+ },
252
+ ];
253
+
254
+ const prompter = this.prompterFactory();
255
+ const result = await prompter.prompt({
256
+ conversationId: context.conversationId,
257
+ questions,
258
+ toolUseId: context.toolUseId,
259
+ signal: context.signal,
260
+ });
261
+
262
+ // Format the aggregated transcript. Each line is keyed by the original
263
+ // question text (not the daemon-assigned id) — the LLM never sees those
264
+ // ids, and human-readable labels read better in the result content.
265
+ const lines = result.entries.map((entry, i) => {
266
+ const q = questions[i]!;
267
+ const prefix = `Question "${q.question}" →`;
268
+ if (entry.decision === "option") {
269
+ const chosen = q.options.find((o) => o.id === entry.optionId);
270
+ const label = chosen?.label ?? "(unknown)";
271
+ return `${prefix} Option: ${entry.optionId} (${label})`;
272
+ }
273
+ if (entry.decision === "free_text") {
274
+ return `${prefix} Free text: ${entry.text ?? ""}`;
275
+ }
276
+ return `${prefix} Skipped`;
277
+ });
278
+
279
+ switch (result.overall) {
280
+ case "completed":
281
+ return { content: lines.join("\n"), isError: false };
282
+ case "closed": {
283
+ const summary =
284
+ "User closed the question card without answering. All questions skipped.";
285
+ return {
286
+ content: [summary, ...lines].join("\n"),
287
+ isError: false,
288
+ };
289
+ }
290
+ case "timed_out":
291
+ return {
292
+ content: "User did not respond within timeout",
293
+ isError: true,
294
+ };
295
+ case "aborted":
296
+ return {
297
+ content: "Question aborted",
298
+ isError: true,
299
+ };
300
+ }
301
+ }
302
+ }
303
+
304
+ export const askQuestionTool = new AskQuestionTool();
@@ -90,7 +90,7 @@ const MODE_TRADEOFFS: Record<StatusCheckMode, string[]> = {
90
90
  [BROWSER_STATUS_MODE.EXTENSION]: [
91
91
  "This is the preferred approach for all things browser-use.",
92
92
  "On macOS, the host browser proxy is provisioned automatically via the desktop client's SSE bridge — no extension install required.",
93
- "When the Chrome extension is also installed, it takes priority for direct WebSocket routing to the user's Chrome session.",
93
+ "When the Chrome extension is also installed, it takes priority for direct WebSocket routing to the active Chrome session.",
94
94
  "More secure than relying on Chrome's native remote debugging functionality.",
95
95
  ],
96
96
  [BROWSER_STATUS_MODE.CDP_INSPECT]: [
@@ -101,8 +101,8 @@ const MODE_TRADEOFFS: Record<StatusCheckMode, string[]> = {
101
101
  ],
102
102
  [BROWSER_STATUS_MODE.LOCAL]: [
103
103
  "The least-preferred approach for all things browser-use.",
104
- "Considered a last-resort fallback if the user has not installed the Chrome Extension or enabled remote debugging in Chrome, and has indicated that they do not want to.",
105
- "Does not use the user's existing browser profile, so sessions/cookies may differ.",
104
+ "Considered a last-resort fallback when the Chrome Extension is not installed, remote debugging in Chrome is not enabled, and neither will be enabled.",
105
+ "Does not use the existing browser profile, so sessions/cookies may differ.",
106
106
  "Requires that Playwright and Chromium are installed on the host machine,",
107
107
  ],
108
108
  };
@@ -392,8 +392,8 @@ function acquireCdpClientWithMode(
392
392
  targetClientId != null
393
393
  ? "extension"
394
394
  : browserMode === "auto" && rememberedKind !== null
395
- ? rememberedKind
396
- : browserMode;
395
+ ? rememberedKind
396
+ : browserMode;
397
397
 
398
398
  try {
399
399
  const raw = getCdpClient(context, { mode: effectiveMode, targetClientId });
@@ -406,7 +406,11 @@ function acquireCdpClientWithMode(
406
406
  // sticky preference doesn't surface as a hard failure.
407
407
  // Do not apply this fallback when target_client_id is set — a targeting
408
408
  // failure must surface as an error, not silently route elsewhere.
409
- if (browserMode === "auto" && effectiveMode !== "auto" && targetClientId == null) {
409
+ if (
410
+ browserMode === "auto" &&
411
+ effectiveMode !== "auto" &&
412
+ targetClientId == null
413
+ ) {
410
414
  browserManager.clearPreferredBackendKind(context.conversationId);
411
415
  try {
412
416
  const raw = getCdpClient(context, { mode: "auto", targetClientId });
@@ -952,10 +956,10 @@ export async function executeBrowserNavigate(
952
956
  "2. Use credential fill to enter email/password from credential_store",
953
957
  );
954
958
  lines.push(
955
- "3. For email verification codes, use ui_show with a form to ask the user for the code mid-turn",
959
+ "3. For email verification codes, use ui_show with a form to request the code mid-turn",
956
960
  );
957
961
  lines.push(
958
- "4. Do NOT give up or tell the user to sign in manually - handle the login flow yourself",
962
+ "4. Do NOT give up or suggest manual sign-in - handle the login flow yourself",
959
963
  );
960
964
  }
961
965
  } else {
@@ -964,7 +968,7 @@ export async function executeBrowserNavigate(
964
968
  "⚠️ CAPTCHA/Cloudflare verification detected on this page.",
965
969
  );
966
970
  lines.push(
967
- "The user needs to solve this challenge manually. Please inform the user that the page requires human verification before the content can be accessed.",
971
+ "This challenge requires human verification. Surface this clearly: the page cannot be accessed until the verification is solved manually.",
968
972
  );
969
973
  }
970
974
  } else {
@@ -979,10 +983,10 @@ export async function executeBrowserNavigate(
979
983
  "2. Use credential fill to enter email/password from credential_store",
980
984
  );
981
985
  lines.push(
982
- "3. For email verification codes, use ui_show with a form to ask the user for the code mid-turn",
986
+ "3. For email verification codes, use ui_show with a form to request the code mid-turn",
983
987
  );
984
988
  lines.push(
985
- "4. Do NOT give up or tell the user to sign in manually - handle the login flow yourself",
989
+ "4. Do NOT give up or suggest manual sign-in - handle the login flow yourself",
986
990
  );
987
991
  }
988
992
  }
@@ -374,7 +374,7 @@ export const computerUseOpenAppTool: Tool = {
374
374
  export const computerUseRunAppleScriptTool: Tool = {
375
375
  name: "computer_use_run_applescript",
376
376
  description:
377
- "Run an AppleScript command. Prefer this over click/type when possible - it doesn't move the cursor or interrupt the user. Never use 'do shell script' inside AppleScript (blocked for security).",
377
+ "Run an AppleScript command. Prefer this over click/type when possible - it doesn't move the cursor or interrupt foreground activity. Never use 'do shell script' inside AppleScript (blocked for security).",
378
378
  category: "computer-use",
379
379
  defaultRiskLevel: RiskLevel.Low,
380
380
  executionMode: "proxy",
@@ -448,7 +448,7 @@ export const computerUseDoneTool: Tool = {
448
448
  export const computerUseRespondTool: Tool = {
449
449
  name: "computer_use_respond",
450
450
  description:
451
- "Respond to the user with a text answer instead of performing computer actions. Use this when you can answer directly without interacting with the screen.",
451
+ "Reply with a text answer instead of performing computer actions. Use this when you can answer directly without interacting with the screen.",
452
452
  category: "computer-use",
453
453
  defaultRiskLevel: RiskLevel.Low,
454
454
  executionMode: "proxy",
@@ -462,7 +462,7 @@ export const computerUseRespondTool: Tool = {
462
462
  properties: {
463
463
  answer: {
464
464
  type: "string",
465
- description: "The text answer to display to the user",
465
+ description: "The text answer to display",
466
466
  },
467
467
  reasoning: {
468
468
  type: "string",
@@ -88,7 +88,7 @@ class CredentialStoreTool implements Tool {
88
88
  type: "string",
89
89
  enum: ["store", "list", "delete", "prompt"],
90
90
  description:
91
- 'The operation to perform. Use "prompt" to ask the user for a secret via secure UI - the value never enters the conversation.',
91
+ 'The operation to perform. Use "prompt" to request a secret via secure UI - the value never enters the conversation.',
92
92
  },
93
93
  service: {
94
94
  type: "string",
@@ -1,11 +1,40 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
 
3
3
  import {
4
+ deleteDocument,
5
+ getDocumentById,
6
+ getDocumentsForConversation,
7
+ isDocumentAssociatedWithConversation,
4
8
  saveDocument,
9
+ searchDocumentsByTitle,
5
10
  updateDocumentContent,
6
11
  } from "../../documents/document-store.js";
7
12
  import type { ToolContext, ToolExecutionResult } from "../types.js";
8
13
 
14
+ function isPrivilegedDocumentActor(context: ToolContext): boolean {
15
+ return (
16
+ context.trustClass === "guardian" || context.executionChannel === "vellum"
17
+ );
18
+ }
19
+
20
+ function documentNotFound(surfaceId: string): ToolExecutionResult {
21
+ return {
22
+ content: JSON.stringify({
23
+ success: false,
24
+ surface_id: surfaceId,
25
+ error: "Document not found",
26
+ }),
27
+ isError: true,
28
+ };
29
+ }
30
+
31
+ function canAccessDocument(surfaceId: string, context: ToolContext): boolean {
32
+ return (
33
+ isPrivilegedDocumentActor(context) ||
34
+ isDocumentAssociatedWithConversation(surfaceId, context.conversationId)
35
+ );
36
+ }
37
+
9
38
  // ── Exported execute functions ──────────────────────────────────────
10
39
 
11
40
  export function executeDocumentCreate(
@@ -85,7 +114,21 @@ export function executeDocumentUpdate(
85
114
  const content = input.content as string;
86
115
  const mode = (input.mode as string | undefined) || "append";
87
116
 
88
- updateDocumentContent(surfaceId, content, mode);
117
+ if (!canAccessDocument(surfaceId, context)) {
118
+ return documentNotFound(surfaceId);
119
+ }
120
+
121
+ const result = updateDocumentContent(surfaceId, content, mode);
122
+ if (!result.success) {
123
+ return {
124
+ content: JSON.stringify({
125
+ success: false,
126
+ surface_id: surfaceId,
127
+ error: result.error,
128
+ }),
129
+ isError: true,
130
+ };
131
+ }
89
132
 
90
133
  // Send document_editor_update message to update the built-in RTE
91
134
  if (context.sendToClient) {
@@ -117,3 +160,83 @@ export function executeDocumentUpdate(
117
160
  isError: true,
118
161
  };
119
162
  }
163
+
164
+ export function executeDocumentRead(
165
+ input: Record<string, unknown>,
166
+ context: ToolContext,
167
+ ): ToolExecutionResult {
168
+ const surfaceId = input.surface_id as string;
169
+ if (!canAccessDocument(surfaceId, context)) {
170
+ return documentNotFound(surfaceId);
171
+ }
172
+
173
+ const doc = getDocumentById(surfaceId);
174
+ if (!doc) {
175
+ return documentNotFound(surfaceId);
176
+ }
177
+ return {
178
+ content: JSON.stringify({
179
+ success: true,
180
+ surface_id: doc.surfaceId,
181
+ title: doc.title,
182
+ content: doc.content,
183
+ word_count: doc.wordCount,
184
+ updated_at: doc.updatedAt,
185
+ }),
186
+ isError: false,
187
+ };
188
+ }
189
+
190
+ export function executeDocumentList(
191
+ input: Record<string, unknown>,
192
+ context: ToolContext,
193
+ ): ToolExecutionResult {
194
+ const query =
195
+ typeof input.query === "string" && input.query.trim().length > 0
196
+ ? input.query.trim()
197
+ : undefined;
198
+ const docs = query
199
+ ? searchDocumentsByTitle(
200
+ query,
201
+ isPrivilegedDocumentActor(context)
202
+ ? {}
203
+ : { conversationId: context.conversationId },
204
+ )
205
+ : getDocumentsForConversation(context.conversationId);
206
+ return {
207
+ content: JSON.stringify({
208
+ success: true,
209
+ documents: docs.map((d) => ({
210
+ surface_id: d.surfaceId,
211
+ title: d.title,
212
+ word_count: d.wordCount,
213
+ created_at: d.createdAt,
214
+ updated_at: d.updatedAt,
215
+ })),
216
+ }),
217
+ isError: false,
218
+ };
219
+ }
220
+
221
+ export function executeDocumentDelete(
222
+ input: Record<string, unknown>,
223
+ context: ToolContext,
224
+ ): ToolExecutionResult {
225
+ const surfaceId = input.surface_id as string;
226
+ if (!canAccessDocument(surfaceId, context)) {
227
+ return documentNotFound(surfaceId);
228
+ }
229
+
230
+ const deleted = deleteDocument(surfaceId);
231
+ if (!deleted) {
232
+ return documentNotFound(surfaceId);
233
+ }
234
+ return {
235
+ content: JSON.stringify({
236
+ success: true,
237
+ surface_id: surfaceId,
238
+ message: "Document deleted",
239
+ }),
240
+ isError: false,
241
+ };
242
+ }
@@ -41,7 +41,7 @@ class FileEditTool implements Tool {
41
41
  activity: {
42
42
  type: "string",
43
43
  description:
44
- "Brief non-technical explanation of what you are doing and why, shown to the user as a status update.",
44
+ "Brief non-technical explanation of what you are doing and why, shown as a status update.",
45
45
  },
46
46
  },
47
47
  required: ["path", "old_string", "new_string", "activity"],
@@ -30,7 +30,7 @@ class FileListTool implements Tool {
30
30
  activity: {
31
31
  type: "string",
32
32
  description:
33
- "Brief non-technical explanation of what you are doing and why, shown to the user as a status update.",
33
+ "Brief non-technical explanation of what you are doing and why, shown as a status update.",
34
34
  },
35
35
  },
36
36
  required: ["path", "activity"],
@@ -41,7 +41,7 @@ class FileReadTool implements Tool {
41
41
  activity: {
42
42
  type: "string",
43
43
  description:
44
- "Brief non-technical explanation of what you are doing and why, shown to the user as a status update.",
44
+ "Brief non-technical explanation of what you are doing and why, shown as a status update.",
45
45
  },
46
46
  },
47
47
  required: ["path", "activity"],
@@ -56,7 +56,7 @@ class FileWriteTool implements Tool {
56
56
  activity: {
57
57
  type: "string",
58
58
  description:
59
- "Brief non-technical explanation of what you are doing and why, shown to the user as a status update.",
59
+ "Brief non-technical explanation of what you are doing and why, shown as a status update.",
60
60
  },
61
61
  },
62
62
  required: ["path", "content", "activity"],
@@ -121,7 +121,10 @@ class FileWriteTool implements Tool {
121
121
  // Indexing `pkb/*.json` (or any other extension) here would produce
122
122
  // chunks the reconciler can't see, leading to orphaned vectors and
123
123
  // pointless embedding work.
124
- if (filePath.toLowerCase().endsWith(".md") && isInsidePkbRoot(filePath, pkbRoot)) {
124
+ if (
125
+ filePath.toLowerCase().endsWith(".md") &&
126
+ isInsidePkbRoot(filePath, pkbRoot)
127
+ ) {
125
128
  enqueuePkbIndexJob({
126
129
  pkbRoot,
127
130
  absPath: filePath,
@@ -13,7 +13,7 @@ import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
13
13
  class HostFileTransferTool implements Tool {
14
14
  name = "host_file_transfer";
15
15
  description =
16
- "Copy a file between the assistant's workspace and the user's host machine. Set direction to 'to_host' to send a workspace file to the host, or 'to_sandbox' to pull a host file into the workspace. When multiple clients support host_file, specify which one to use with target_client_id.";
16
+ "Copy a file between the assistant's workspace and the host machine. Set direction to 'to_host' to send a workspace file to the host, or 'to_sandbox' to pull a host file into the workspace. When multiple clients support host_file, specify which one to use with target_client_id.";
17
17
  category = "host-filesystem";
18
18
  defaultRiskLevel = RiskLevel.Medium;
19
19
 
@@ -95,7 +95,7 @@ function buildHostBashProxyEnv(
95
95
  class HostShellTool implements Tool {
96
96
  name = "host_bash";
97
97
  description =
98
- "LAST RESORT — Execute a shell command directly on the user's host machine. You MUST strongly prefer the regular `bash` tool for all commands. Only use `host_bash` when you are absolutely certain the command MUST run on the user's host machine and CANNOT run in the workspace (e.g., managing host-level system services, accessing host-only peripherals, or interacting with host paths outside the workspace). If in doubt, use `bash` instead. Approval-gated: your user must allow each invocation. Do not use for commands that require injected credentials or secrets.";
98
+ "LAST RESORT — Execute a shell command directly on the host machine. You MUST strongly prefer the regular `bash` tool for all commands. Only use `host_bash` when you are absolutely certain the command MUST run on the host machine and CANNOT run in the workspace (e.g., managing host-level system services, accessing host-only peripherals, or interacting with host paths outside the workspace). If in doubt, use `bash` instead. Approval-gated: each invocation must be explicitly approved. Do not use for commands that require injected credentials or secrets.";
99
99
  category = "host-terminal";
100
100
  // host_bash is a weaker-tier escape hatch under CES lockdown. It remains
101
101
  // Medium risk by default but persistent approvals are disabled for
@@ -402,7 +402,7 @@ export class PermissionChecker {
402
402
  const denialMessage =
403
403
  contextualDenial.length > 0
404
404
  ? contextualDenial
405
- : `Permission denied by user. The user chose not to allow the "${name}" tool. Do NOT retry this tool call immediately. Instead, tell the user that the action was not performed because they denied permission, and ask if they would like you to try again or take a different approach. Wait for the user to explicitly respond before retrying.`;
405
+ : `Permission denied. The "${name}" tool was not allowed. Do NOT retry this tool call immediately. Instead, explain that the action was not performed because permission was denied, and ask whether to try again or take a different approach. Wait for an explicit response before retrying.`;
406
406
  const denialReason =
407
407
  contextualDenial.length > 0
408
408
  ? `Permission denied (${name}): contextual policy`