@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2

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 (596) hide show
  1. package/AGENTS.md +9 -3
  2. package/ARCHITECTURE.md +32 -10
  3. package/docs/architecture/memory.md +43 -5
  4. package/docs/architecture/security.md +96 -152
  5. package/docs/architecture/turn-actor.md +70 -0
  6. package/docs/browser-use-architecture-phase2.md +128 -56
  7. package/docs/flux-turn-detection-spike.md +248 -0
  8. package/docs/guardian-request-flow.md +35 -0
  9. package/docs/stt-provider-onboarding.md +3 -1
  10. package/knip.json +3 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  13. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  19. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  23. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
  24. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
  25. package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
  26. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  27. package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  28. package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
  29. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  30. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  31. package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  32. package/openapi.yaml +289 -113
  33. package/package.json +1 -1
  34. package/scripts/voice-ttft-spike.ts +3 -3
  35. package/scripts/write-plugin-api-shim.ts +10 -0
  36. package/src/__tests__/app-compiler.test.ts +38 -3
  37. package/src/__tests__/app-control-flow.test.ts +1 -0
  38. package/src/__tests__/approval-routes-http.test.ts +2 -2
  39. package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
  40. package/src/__tests__/attachments-store.test.ts +21 -12
  41. package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
  42. package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
  43. package/src/__tests__/call-site-routing-provider.test.ts +1 -1
  44. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  45. package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
  46. package/src/__tests__/channel-delivery-store.test.ts +14 -14
  47. package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
  48. package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
  49. package/src/__tests__/checker.test.ts +20 -314
  50. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
  51. package/src/__tests__/compaction-events.test.ts +8 -10
  52. package/src/__tests__/config-loader-backfill.test.ts +3 -3
  53. package/src/__tests__/config-schema.test.ts +25 -10
  54. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  55. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
  56. package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
  57. package/src/__tests__/conversation-agent-loop.test.ts +32 -21
  58. package/src/__tests__/conversation-attachments.test.ts +0 -1
  59. package/src/__tests__/conversation-attention-store.test.ts +63 -0
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
  61. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
  62. package/src/__tests__/conversation-fork-crud.test.ts +69 -0
  63. package/src/__tests__/conversation-fork-referential.test.ts +67 -0
  64. package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
  65. package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
  66. package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
  67. package/src/__tests__/conversation-queue.test.ts +154 -4
  68. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
  69. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
  71. package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
  75. package/src/__tests__/conversation-slash-queue.test.ts +3 -0
  76. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  77. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
  78. package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
  79. package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
  80. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
  81. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
  82. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
  83. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
  84. package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
  85. package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
  86. package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
  87. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
  88. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
  89. package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
  90. package/src/__tests__/credential-prompt-route.test.ts +7 -10
  91. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  92. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  93. package/src/__tests__/custom-profile-ensure.test.ts +5 -1
  94. package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
  95. package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
  96. package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
  97. package/src/__tests__/document-append-idempotency.test.ts +233 -0
  98. package/src/__tests__/document-sync-tags.test.ts +0 -75
  99. package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
  100. package/src/__tests__/edit-propagation.test.ts +107 -11
  101. package/src/__tests__/file-ops-service.test.ts +163 -30
  102. package/src/__tests__/filesystem-tools.test.ts +23 -24
  103. package/src/__tests__/gateway-only-guard.test.ts +2 -5
  104. package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
  105. package/src/__tests__/helpers/mock-actor-context.ts +49 -0
  106. package/src/__tests__/helpers/mock-conversation.ts +13 -1
  107. package/src/__tests__/host-file-read-tool.test.ts +16 -19
  108. package/src/__tests__/http-user-message-parity.test.ts +1 -1
  109. package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
  110. package/src/__tests__/injector-chain.test.ts +63 -41
  111. package/src/__tests__/injector-disk-pressure.test.ts +11 -23
  112. package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
  113. package/src/__tests__/llm-context-resolution.test.ts +73 -1
  114. package/src/__tests__/llm-schema.test.ts +5 -2
  115. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
  116. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  117. package/src/__tests__/media-generate-image.test.ts +131 -21
  118. package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
  119. package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
  120. package/src/__tests__/mtime-cache.test.ts +3 -1
  121. package/src/__tests__/non-member-access-request.test.ts +0 -20
  122. package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
  123. package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
  124. package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
  125. package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
  126. package/src/__tests__/plugin-disabled-state.test.ts +2 -0
  127. package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
  128. package/src/__tests__/plugin-execution-context.test.ts +73 -0
  129. package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
  130. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
  131. package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
  132. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
  133. package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
  134. package/src/__tests__/proxy-approval-callback.test.ts +1 -0
  135. package/src/__tests__/qdrant-manager.test.ts +14 -1
  136. package/src/__tests__/reaction-persistence.test.ts +200 -7
  137. package/src/__tests__/require-fresh-approval.test.ts +0 -4
  138. package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
  139. package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
  140. package/src/__tests__/run-due-schedules.test.ts +21 -0
  141. package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
  142. package/src/__tests__/schedule-routes.test.ts +23 -0
  143. package/src/__tests__/schedule-store.test.ts +17 -0
  144. package/src/__tests__/scoped-approval-grants.test.ts +11 -6
  145. package/src/__tests__/secret-ingress-channel.test.ts +0 -1
  146. package/src/__tests__/secret-ingress-http.test.ts +1 -1
  147. package/src/__tests__/send-endpoint-busy.test.ts +3 -3
  148. package/src/__tests__/skills.test.ts +32 -0
  149. package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
  150. package/src/__tests__/starter-task-flow.test.ts +5 -4
  151. package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
  152. package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
  153. package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
  154. package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
  155. package/src/__tests__/subagent-tools.test.ts +81 -101
  156. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
  157. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  158. package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
  159. package/src/__tests__/tool-executor.test.ts +68 -39
  160. package/src/__tests__/turn-events-store.test.ts +43 -0
  161. package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
  162. package/src/__tests__/ui-shape-teaching.test.ts +33 -0
  163. package/src/__tests__/ui-visual-surface.test.ts +1 -1
  164. package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
  165. package/src/__tests__/ui-work-result-surface.test.ts +1 -1
  166. package/src/__tests__/user-plugin-loader.test.ts +3 -1
  167. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  168. package/src/__tests__/visible-app-context.test.ts +16 -9
  169. package/src/__tests__/voice-config-update.test.ts +40 -0
  170. package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
  171. package/src/__tests__/voice-session-bridge.test.ts +85 -29
  172. package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
  173. package/src/__tests__/worker-plugin-surface.test.ts +77 -0
  174. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
  175. package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
  176. package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
  177. package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
  178. package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
  179. package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
  180. package/src/acp/__tests__/auth-required.test.ts +161 -0
  181. package/src/acp/acp-claude-oauth.ts +19 -2
  182. package/src/acp/agent-process.test.ts +100 -0
  183. package/src/acp/agent-process.ts +29 -26
  184. package/src/acp/auth-required.ts +102 -0
  185. package/src/acp/session-manager.test.ts +119 -0
  186. package/src/acp/session-manager.ts +76 -3
  187. package/src/api/events/acp-auth-required.ts +55 -0
  188. package/src/api/events/host-file.ts +2 -2
  189. package/src/api/index.ts +7 -0
  190. package/src/api/surfaces.ts +12 -3
  191. package/src/apps/app-store.ts +3 -0
  192. package/src/bundler/package-resolver.ts +2 -30
  193. package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
  194. package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
  195. package/src/calls/call-controller.ts +19 -3
  196. package/src/calls/call-setup-flow.ts +0 -1
  197. package/src/calls/media-stream-stt-session.ts +15 -0
  198. package/src/calls/voice-session-bridge.ts +115 -41
  199. package/src/calls/voice-triage-escalate.ts +105 -2
  200. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  201. package/src/channels/config.ts +13 -0
  202. package/src/channels/plugin-channel-declarations.ts +108 -0
  203. package/src/channels/types.ts +30 -0
  204. package/src/cli/AGENTS.md +5 -2
  205. package/src/cli/bundled-modules.ts +29 -0
  206. package/src/cli/commands/credentials.help.ts +2 -2
  207. package/src/cli/commands/db/repair.ts +4 -8
  208. package/src/cli/commands/domain.ts +6 -3
  209. package/src/cli/commands/email.ts +6 -3
  210. package/src/cli/commands/inference-providers.ts +1 -1
  211. package/src/cli/commands/keys.ts +8 -3
  212. package/src/cli/commands/mcp.help.ts +13 -4
  213. package/src/cli/commands/mcp.ts +9 -0
  214. package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
  215. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
  216. package/src/cli/commands/memory/index.help.ts +84 -28
  217. package/src/cli/commands/memory/index.ts +2 -0
  218. package/src/cli/commands/memory/memory-v2.ts +58 -54
  219. package/src/cli/commands/memory/memory-v3.ts +64 -0
  220. package/src/cli/commands/memory/memory-validate.ts +18 -0
  221. package/src/cli/commands/plugins.ts +85 -36
  222. package/src/cli/commands/schedules.ts +35 -1
  223. package/src/cli/commands/stt.help.ts +27 -2
  224. package/src/cli/commands/trust.ts +3 -14
  225. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
  226. package/src/cli/lib/bundled-marketplace.json +14 -1
  227. package/src/cli/lib/upgrade-plugin.ts +42 -0
  228. package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
  229. package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
  230. package/src/config/__tests__/default-provider.test.ts +6 -1
  231. package/src/config/__tests__/profile-materialization.test.ts +75 -19
  232. package/src/config/assistant-feature-flags.ts +36 -15
  233. package/src/config/balanced-model-experiment.ts +35 -0
  234. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  235. package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
  236. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  237. package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
  238. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
  240. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  241. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  242. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
  243. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
  244. package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
  245. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
  246. package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
  247. package/src/config/bundled-skills/subagent/SKILL.md +17 -12
  248. package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
  249. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  250. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  251. package/src/config/call-site-defaults.ts +11 -5
  252. package/src/config/default-profile-catalog.ts +179 -16
  253. package/src/config/default-profile-names.ts +4 -1
  254. package/src/config/default-provider-resolution.ts +4 -0
  255. package/src/config/feature-flag-registry.json +12 -12
  256. package/src/config/llm-context-resolution.ts +11 -3
  257. package/src/config/llm-resolver.ts +28 -1
  258. package/src/config/profile-materialization.ts +70 -22
  259. package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
  260. package/src/config/schemas/call-site-catalog.ts +4 -4
  261. package/src/config/schemas/live-voice.ts +57 -23
  262. package/src/config/schemas/llm.ts +59 -32
  263. package/src/config/schemas/mcp.ts +23 -0
  264. package/src/config/schemas/plugin-updates.ts +6 -2
  265. package/src/config/schemas/stt.ts +1 -0
  266. package/src/config/skills.ts +9 -2
  267. package/src/context/outbound-sanitize.ts +96 -1
  268. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  269. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
  270. package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
  271. package/src/daemon/conversation-agent-loop.ts +31 -19
  272. package/src/daemon/conversation-messaging.ts +5 -1
  273. package/src/daemon/conversation-notifiers.ts +20 -10
  274. package/src/daemon/conversation-process.ts +20 -6
  275. package/src/daemon/conversation-runtime-assembly.ts +12 -6
  276. package/src/daemon/conversation-store.ts +14 -4
  277. package/src/daemon/conversation-surfaces.ts +75 -26
  278. package/src/daemon/conversation-tool-setup.ts +3 -6
  279. package/src/daemon/conversation.ts +154 -51
  280. package/src/daemon/doordash-steps.ts +2 -2
  281. package/src/daemon/lifecycle.ts +14 -1
  282. package/src/daemon/mcp-reload-service.ts +36 -6
  283. package/src/daemon/process-message.ts +13 -26
  284. package/src/daemon/providers-setup.ts +6 -3
  285. package/src/daemon/trust-context-types.ts +29 -0
  286. package/src/daemon/trust-context.ts +12 -7
  287. package/src/daemon/wake-conversation-ops.ts +3 -2
  288. package/src/daemon/windows-compiled-entry.ts +4 -0
  289. package/src/documents/document-store.ts +143 -240
  290. package/src/hooks/hook-loader.ts +3 -3
  291. package/src/hooks/registry.ts +103 -45
  292. package/src/hooks/types.ts +5 -0
  293. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  294. package/src/inbound/oauth-callback-url.ts +61 -0
  295. package/src/ipc/gateway-client.test.ts +1 -1
  296. package/src/ipc/gateway-client.ts +14 -21
  297. package/src/ipc/gateway-flag-listener.ts +17 -3
  298. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  299. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  300. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
  301. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  302. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  303. package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
  304. package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
  305. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
  306. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  307. package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
  308. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  309. package/src/live-voice/live-voice-archive.ts +2 -0
  310. package/src/live-voice/live-voice-manager.ts +16 -3
  311. package/src/live-voice/live-voice-metrics.ts +57 -32
  312. package/src/live-voice/live-voice-photo.ts +1 -2
  313. package/src/live-voice/live-voice-session.ts +598 -323
  314. package/src/live-voice/progress-narration.ts +277 -0
  315. package/src/live-voice/protocol.ts +21 -1
  316. package/src/live-voice/windows-compiled-live-voice.ts +4 -0
  317. package/src/mcp/__tests__/effective-config.test.ts +238 -0
  318. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  319. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
  320. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  321. package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
  322. package/src/mcp/client.ts +16 -11
  323. package/src/mcp/effective-config.ts +113 -0
  324. package/src/mcp/manager.ts +11 -6
  325. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  326. package/src/mcp/mcp-oauth-provider.ts +205 -240
  327. package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
  328. package/src/monitoring/control.ts +1 -0
  329. package/src/monitoring/db-integrity-sample.ts +4 -5
  330. package/src/monitoring/plugin-auto-update.ts +128 -24
  331. package/src/notifications/AGENTS.md +2 -0
  332. package/src/notifications/approval-card-data.ts +33 -0
  333. package/src/notifications/signal.ts +1 -0
  334. package/src/permissions/AGENTS.md +16 -0
  335. package/src/permissions/checker.test.ts +75 -122
  336. package/src/permissions/checker.ts +44 -560
  337. package/src/permissions/confirmation-guardian-request.test.ts +15 -11
  338. package/src/permissions/confirmation-guardian-request.ts +2 -2
  339. package/src/permissions/prompter.ts +1 -5
  340. package/src/permissions/question-guardian-request.test.ts +14 -6
  341. package/src/permissions/question-guardian-request.ts +1 -2
  342. package/src/persistence/attachments-store.ts +8 -1
  343. package/src/persistence/bookmark-crud.ts +3 -7
  344. package/src/persistence/conversation-attention-store.ts +16 -45
  345. package/src/persistence/conversation-crud.ts +33 -4
  346. package/src/persistence/conversation-lineage.ts +9 -0
  347. package/src/persistence/conversation-queries.ts +182 -52
  348. package/src/persistence/delivery-crud.ts +150 -36
  349. package/src/persistence/embeddings/qdrant-manager.ts +84 -49
  350. package/src/persistence/external-conversation-store.ts +32 -4
  351. package/src/persistence/llm-request-log-store.ts +4 -10
  352. package/src/persistence/llm-usage-store.ts +8 -3
  353. package/src/persistence/message-reads.test.ts +197 -0
  354. package/src/persistence/message-reads.ts +211 -0
  355. package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
  356. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
  357. package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
  358. package/src/persistence/real-user-turn-filter.ts +27 -3
  359. package/src/persistence/schema/documents.ts +4 -4
  360. package/src/persistence/steps.ts +9 -0
  361. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  362. package/src/plugin-api/constants.ts +8 -0
  363. package/src/plugin-api/conversation-turn.ts +195 -5
  364. package/src/plugin-api/index.ts +38 -6
  365. package/src/plugin-api/resolve-credential.ts +3 -2
  366. package/src/plugin-api/store-credential.ts +146 -0
  367. package/src/plugin-api/vision-support.test.ts +1 -1
  368. package/src/plugin-api/webhook-url.ts +13 -11
  369. package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
  370. package/src/plugins/defaults/main.ts +21 -7
  371. package/src/plugins/defaults/memory/AGENTS.md +15 -3
  372. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
  373. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
  374. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
  375. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
  376. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
  377. package/src/plugins/defaults/memory/buffer-format.ts +165 -0
  378. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
  379. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
  380. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
  381. package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
  382. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
  383. package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
  384. package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
  385. package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
  386. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
  387. package/src/plugins/defaults/memory/indexer.ts +3 -1
  388. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
  389. package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
  390. package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
  391. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
  392. package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
  393. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
  394. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
  395. package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
  396. package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
  397. package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
  398. package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
  399. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
  400. package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
  401. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
  402. package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
  403. package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
  404. package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
  405. package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
  406. package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
  407. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
  408. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  409. package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
  410. package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
  411. package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
  412. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
  413. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
  414. package/src/plugins/defaults/memory/v3/card.ts +9 -9
  415. package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
  416. package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
  417. package/src/plugins/defaults/memory/v3/edge.ts +13 -57
  418. package/src/plugins/defaults/memory/v3/injector.ts +8 -0
  419. package/src/plugins/defaults/memory/v3/prune.ts +2 -0
  420. package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
  421. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
  422. package/src/plugins/defaults/memory/worker-control.ts +1 -0
  423. package/src/plugins/defaults/memory/worker.ts +6 -3
  424. package/src/plugins/defaults/worker-entrypoints.ts +3 -0
  425. package/src/plugins/external-plugin-loader.ts +47 -0
  426. package/src/plugins/mcp-servers.ts +361 -0
  427. package/src/plugins/mtime-cache.ts +37 -46
  428. package/src/plugins/pipeline.ts +14 -7
  429. package/src/plugins/plugin-execution-context.ts +35 -11
  430. package/src/plugins/worker-plugin-surface.ts +33 -0
  431. package/src/prompts/templates/system-sections.ts +0 -7
  432. package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
  433. package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
  434. package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
  435. package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
  436. package/src/providers/__tests__/retry-callsite.test.ts +25 -2
  437. package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
  438. package/src/providers/anthropic/client.ts +332 -241
  439. package/src/providers/call-site-routing.ts +30 -3
  440. package/src/providers/connection-resolution.ts +194 -11
  441. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
  442. package/src/providers/inference/adapter-factory.ts +3 -0
  443. package/src/providers/inference/auth.ts +6 -0
  444. package/src/providers/inference/connection-availability.ts +24 -2
  445. package/src/providers/inference/connections.ts +2 -0
  446. package/src/providers/model-catalog.ts +3 -3
  447. package/src/providers/model-intents.ts +28 -8
  448. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
  449. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
  450. package/src/providers/openai/chat-completions-provider.ts +133 -36
  451. package/src/providers/openai/codex-models.ts +2 -1
  452. package/src/providers/provider-send-message.ts +32 -3
  453. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  454. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
  455. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  456. package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
  457. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  458. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
  459. package/src/providers/speech-to-text/provider-catalog.ts +99 -8
  460. package/src/providers/speech-to-text/resolve.ts +25 -2
  461. package/src/routes/control.ts +1 -0
  462. package/src/routes/route-host-client.ts +1 -0
  463. package/src/routes/route-host-protocol.ts +7 -0
  464. package/src/routes/worker.ts +48 -15
  465. package/src/runtime/AGENTS.md +16 -17
  466. package/src/runtime/access-request-helper.ts +9 -12
  467. package/src/runtime/agent-wake.ts +18 -15
  468. package/src/runtime/effective-capabilities.test.ts +43 -0
  469. package/src/runtime/effective-capabilities.ts +27 -0
  470. package/src/runtime/guardian-reply-router.ts +6 -2
  471. package/src/runtime/pre-first-message-gate.ts +4 -0
  472. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
  473. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
  474. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
  475. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
  476. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
  477. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
  478. package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
  479. package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
  480. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  481. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
  482. package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
  483. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
  484. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  485. package/src/runtime/routes/channel-route-shared.ts +0 -6
  486. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
  487. package/src/runtime/routes/conversation-list-routes.ts +181 -23
  488. package/src/runtime/routes/conversation-management-routes.ts +17 -12
  489. package/src/runtime/routes/conversation-query-routes.ts +40 -27
  490. package/src/runtime/routes/conversation-routes.ts +43 -29
  491. package/src/runtime/routes/credential-prompt-routes.ts +4 -7
  492. package/src/runtime/routes/credential-routes.ts +26 -82
  493. package/src/runtime/routes/default-provider-routes.ts +15 -0
  494. package/src/runtime/routes/documents-routes.ts +3 -222
  495. package/src/runtime/routes/inbound-message-handler.ts +21 -45
  496. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
  497. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
  498. package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
  499. package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
  500. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
  501. package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
  502. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
  503. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
  504. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
  505. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
  506. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
  507. package/src/runtime/routes/inference-profiles-routes.ts +20 -11
  508. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
  509. package/src/runtime/routes/log-export-routes.ts +3 -0
  510. package/src/runtime/routes/mcp-auth-routes.ts +148 -57
  511. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
  512. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
  513. package/src/runtime/routes/playground/inject-failures.ts +2 -2
  514. package/src/runtime/routes/playground/reset-circuit.ts +1 -1
  515. package/src/runtime/routes/plugins-routes.ts +38 -8
  516. package/src/runtime/routes/schedule-routes.ts +94 -7
  517. package/src/runtime/routes/settings-routes.ts +6 -6
  518. package/src/runtime/routes/stt-routes.ts +31 -25
  519. package/src/runtime/routes/surface-action-routes.ts +20 -19
  520. package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
  521. package/src/runtime/routes/user-route-dispatcher.ts +69 -15
  522. package/src/runtime/routes/user-route-import.ts +108 -0
  523. package/src/runtime/routes/user-route-resolution.ts +18 -1
  524. package/src/runtime/routes/workspace-routes.ts +0 -9
  525. package/src/runtime/routes/workspace-utils.ts +3 -13
  526. package/src/runtime/services/conversation-serializer.ts +7 -2
  527. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
  528. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
  529. package/src/schedule/plugin-schedule-availability.ts +58 -0
  530. package/src/schedule/plugin-schedule-declarations.ts +23 -27
  531. package/src/schedule/plugin-schedule-reconciler.ts +12 -3
  532. package/src/schedule/schedule-store.ts +5 -1
  533. package/src/schedule/scheduler.ts +9 -4
  534. package/src/schedule/worker-control.ts +1 -0
  535. package/src/schedule/worker.ts +6 -0
  536. package/src/security/oauth2.ts +6 -22
  537. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  538. package/src/stt/__tests__/types.test.ts +94 -0
  539. package/src/stt/daemon-batch-transcriber.ts +10 -0
  540. package/src/stt/stt-stream-session.ts +8 -4
  541. package/src/stt/types.ts +103 -0
  542. package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
  543. package/src/subagent/consult-context.ts +11 -11
  544. package/src/subagent/consult-prompt.ts +26 -35
  545. package/src/subagent/manager.ts +21 -40
  546. package/src/subagent/notify.ts +7 -1
  547. package/src/subagent/types.ts +15 -12
  548. package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
  549. package/src/tools/acp/spawn.test.ts +97 -0
  550. package/src/tools/acp/spawn.ts +38 -4
  551. package/src/tools/credentials/ref-parse.ts +35 -0
  552. package/src/tools/credentials/resolve.ts +4 -10
  553. package/src/tools/credentials/store.ts +168 -0
  554. package/src/tools/document/document-tool.ts +12 -3
  555. package/src/tools/executor.ts +53 -15
  556. package/src/tools/filesystem/read.ts +27 -10
  557. package/src/tools/host-filesystem/read.ts +27 -15
  558. package/src/tools/network/url-safety.ts +9 -16
  559. package/src/tools/permission-checker.ts +43 -52
  560. package/src/tools/registry.ts +2 -1
  561. package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
  562. package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
  563. package/src/tools/shared/filesystem/types.ts +5 -5
  564. package/src/tools/skills/scaffold-managed.ts +25 -7
  565. package/src/tools/subagent/spawn.ts +28 -88
  566. package/src/tools/tool-types.ts +26 -13
  567. package/src/tools/types.ts +6 -6
  568. package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
  569. package/src/tools/workflows/run-workflow.ts +1 -2
  570. package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
  571. package/src/tts/reasoning-tag-filter.ts +89 -0
  572. package/src/util/__tests__/worker-process-command.test.ts +37 -0
  573. package/src/util/logger.ts +16 -0
  574. package/src/util/think-tag-stream.ts +95 -0
  575. package/src/util/worker-process.ts +37 -4
  576. package/src/windows-compiled-cli.ts +32 -0
  577. package/src/windows-compiled-entry.ts +4 -0
  578. package/src/windows-compiled-logger.ts +6 -0
  579. package/src/windows-compiled-worker-entry.ts +29 -0
  580. package/src/workspace/byok-default-profile-ensure.ts +76 -24
  581. package/src/workspace/custom-profile-ensure.ts +4 -24
  582. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  583. package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
  584. package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
  585. package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
  586. package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
  587. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
  588. package/src/workspace/migrations/registry.ts +10 -0
  589. package/src/workspace/provider-commit-message-generator.ts +7 -5
  590. package/src/__tests__/document-workspace-file.test.ts +0 -467
  591. package/src/live-voice/__tests__/front-decision.test.ts +0 -645
  592. package/src/live-voice/front-decision.ts +0 -476
  593. package/src/permissions/ipc-risk-types.ts +0 -143
  594. package/src/permissions/risk-types.ts +0 -76
  595. package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
  596. package/src/subagent/consult-transcript.ts +0 -90
@@ -147,6 +147,11 @@ const { _resetAdapterInstallCacheForTests } =
147
147
  await import("../../acp/auto-install.js");
148
148
  const { ACP_CLAUDE_OAUTH_MISSING_CODE } =
149
149
  await import("../../acp/prepare-agent-env.js");
150
+ const { ACP_CLAUDE_AUTH_REQUIRED_CODE, AcpAuthRequiredError } =
151
+ await import("../../acp/auth-required.js");
152
+ const { hasAcpConnectCardRaised } =
153
+ await import("../../acp/acp-connect-card-state.js");
154
+ const { AcpAgentProcess } = await import("../../acp/agent-process.js");
150
155
 
151
156
  // ---------------------------------------------------------------------------
152
157
  // Helpers
@@ -547,3 +552,95 @@ describe("executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN injection", () => {
547
552
  expect(result.errorCode).toBe(ACP_CLAUDE_OAUTH_MISSING_CODE);
548
553
  });
549
554
  });
555
+
556
+ // ---------------------------------------------------------------------------
557
+ // Pre-spawn Claude auth rejection
558
+ // ---------------------------------------------------------------------------
559
+
560
+ describe("pre-spawn Claude auth rejection", () => {
561
+ test("raises the recovery surface: errorCode, card guidance, registry mark", async () => {
562
+ // The adapter can reject session creation with auth_required (a stored
563
+ // credential the server refuses). The error thrown here is produced by
564
+ // the REAL conversion seam, not hand-built: a wire-shaped JSON-RPC
565
+ // auth_required rejection driven through AcpAgentProcess.createSession's
566
+ // actual retry path, so this test fails if that seam stops yielding the
567
+ // typed error the tool boundary maps to the recovery surface.
568
+ const proc = new AcpAgentProcess(
569
+ "claude",
570
+ { command: "claude-agent-acp", args: [] },
571
+ (() => ({})) as never,
572
+ );
573
+ (proc as unknown as { connection: unknown }).connection = {
574
+ newSession: () =>
575
+ Promise.reject({ code: -32000, message: "Authentication required" }),
576
+ };
577
+ (proc as unknown as { initializeResponse: unknown }).initializeResponse = {
578
+ authMethods: [
579
+ {
580
+ id: "claude-ai-login",
581
+ name: "Claude Subscription",
582
+ type: "terminal",
583
+ },
584
+ ],
585
+ };
586
+ (proc as unknown as { spawnedEnv: unknown }).spawnedEnv = {};
587
+ const realSeamError: unknown = await proc.createSession("/tmp").then(
588
+ () => null,
589
+ (e: unknown) => e,
590
+ );
591
+ expect(realSeamError).toBeInstanceOf(AcpAuthRequiredError);
592
+
593
+ spawnMock.mockImplementationOnce(async () => {
594
+ throw realSeamError;
595
+ });
596
+
597
+ const context = { ...makeContext(), conversationId: "conv-prespawn-auth" };
598
+ const result = await executeAcpSpawn(
599
+ { agent: "claude", task: "do something" },
600
+ context,
601
+ );
602
+
603
+ expect(result.isError).toBe(true);
604
+ expect(result.errorCode).toBe(ACP_CLAUDE_AUTH_REQUIRED_CODE);
605
+ expect(result.content).toContain("Connect Claude Code");
606
+ expect(hasAcpConnectCardRaised("conv-prespawn-auth")).toBe(true);
607
+ });
608
+
609
+ test("a raw wire auth_required object (unwrapped initialize path) gets the same surface", async () => {
610
+ // initialize() cannot ride withAuthRetry (its response advertises the
611
+ // methods the retry selects from), so its rejection arrives as the plain
612
+ // JSON-RPC object rather than the typed error. The gate must recognize
613
+ // both shapes or this path silently flattens to a generic failure.
614
+ spawnMock.mockImplementationOnce(async () => {
615
+ throw { code: -32000, message: "Authentication required" };
616
+ });
617
+
618
+ const context = { ...makeContext(), conversationId: "conv-prespawn-raw" };
619
+ const result = await executeAcpSpawn(
620
+ { agent: "claude", task: "do something" },
621
+ context,
622
+ );
623
+
624
+ expect(result.isError).toBe(true);
625
+ expect(result.errorCode).toBe(ACP_CLAUDE_AUTH_REQUIRED_CODE);
626
+ expect(result.content).toContain("Connect Claude Code");
627
+ expect(hasAcpConnectCardRaised("conv-prespawn-raw")).toBe(true);
628
+ });
629
+
630
+ test("a non-auth spawn failure keeps the plain flattened result", async () => {
631
+ spawnMock.mockImplementationOnce(async () => {
632
+ throw new Error("adapter exploded");
633
+ });
634
+
635
+ const context = { ...makeContext(), conversationId: "conv-prespawn-boom" };
636
+ const result = await executeAcpSpawn(
637
+ { agent: "claude", task: "do something" },
638
+ context,
639
+ );
640
+
641
+ expect(result.isError).toBe(true);
642
+ expect(result.errorCode).toBeUndefined();
643
+ expect(result.content).toContain("adapter exploded");
644
+ expect(hasAcpConnectCardRaised("conv-prespawn-boom")).toBe(false);
645
+ });
646
+ });
@@ -3,6 +3,12 @@ import { basename } from "node:path";
3
3
  import { z } from "zod";
4
4
 
5
5
  import { markAcpConnectCardRaised } from "../../acp/acp-connect-card-state.js";
6
+ import {
7
+ ACP_AUTH_RECOVERY_GUIDANCE,
8
+ ACP_CLAUDE_AUTH_REQUIRED_CODE,
9
+ CLAUDE_ACP_COMMAND,
10
+ isAcpAuthRequired,
11
+ } from "../../acp/auth-required.js";
6
12
  import { resolveAgentWithAutoInstall } from "../../acp/auto-install.js";
7
13
  import { getAcpSessionManager } from "../../acp/index.js";
8
14
  import {
@@ -64,10 +70,12 @@ export async function executeAcpSpawn(
64
70
  return { content: '"task" is required.', isError: true };
65
71
  }
66
72
 
67
- // Pure precondition: check for a connected client BEFORE any side effects
68
- // (auto-install mutates the host via a `bun` global install and can block
69
- // for up to the install timeout). Without a client the spawn cannot
70
- // succeed anyway.
73
+ // Pure precondition: the session streams its results through the
74
+ // conversation's event sink, so a context with no sink at all (a tool run
75
+ // outside any conversation, e.g. the standalone CLI runner) cannot host a
76
+ // spawn. Checked BEFORE any side effects (auto-install mutates the host via
77
+ // a `bun` global install and can block for up to the install timeout).
78
+ // Inside a conversation the sink always exists and is always live.
71
79
  const sendToClient = getSendToClient(context);
72
80
  if (!sendToClient) {
73
81
  return {
@@ -147,6 +155,32 @@ export async function executeAcpSpawn(
147
155
 
148
156
  return { content: payload, isError: false };
149
157
  } catch (err) {
158
+ // A pre-spawn rejection of the stored Claude credential (the adapter
159
+ // raises auth_required during session creation) gets the same recovery
160
+ // surface as the missing-token preflight: the errorCode raises the inline
161
+ // Connect card off this failed tool call, the registry mark dedups a
162
+ // redundant secure prompt, and the guidance keeps the model pointed at
163
+ // the card. Adapter-gated: the card repairs Claude credentials only.
164
+ // `isAcpAuthRequired` rather than an instanceof: it also recognizes the
165
+ // raw JSON-RPC auth_required object, which is how a rejection reaches
166
+ // this catch from the one protocol call `withAuthRetry` cannot wrap
167
+ // (`initialize`, whose response is what advertises the auth methods the
168
+ // retry would need).
169
+ if (
170
+ isAcpAuthRequired(err) &&
171
+ basename(agentConfig.command) === CLAUDE_ACP_COMMAND
172
+ ) {
173
+ const message =
174
+ err instanceof Error
175
+ ? err.message
176
+ : String((err as { message?: unknown }).message ?? err);
177
+ markAcpConnectCardRaised(context.conversationId);
178
+ return {
179
+ content: `${message}\n\n${ACP_AUTH_RECOVERY_GUIDANCE}`,
180
+ isError: true,
181
+ errorCode: ACP_CLAUDE_AUTH_REQUIRED_CODE,
182
+ };
183
+ }
150
184
  const msg =
151
185
  err instanceof Error
152
186
  ? err.message
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Credential reference parsing. Pure string work, no store access.
3
+ *
4
+ * `"service/field"` is the shared vocabulary of every credential entry point:
5
+ * the CLI, the reveal and set routes, and the plugin-facing read and write
6
+ * APIs all name credentials this way. The rules for what counts as a valid
7
+ * reference live here so each path applies the same ones.
8
+ *
9
+ * Deliberately separate from {@link ./resolve}, which reads the metadata store:
10
+ * a caller that only needs to split a reference should not import a module
11
+ * whose behavior depends on what is stored.
12
+ */
13
+
14
+ /**
15
+ * Parse a `"service/field"` credential reference into its parts.
16
+ *
17
+ * Returns undefined for anything that is not exactly one non-empty segment on
18
+ * each side of a single slash: no slash, a leading or trailing slash, or more
19
+ * than one slash as in `"fal/api/key"`.
20
+ */
21
+ export function parseServiceFieldRef(
22
+ ref: string,
23
+ ): { service: string; field: string } | undefined {
24
+ const slashIndex = ref.indexOf("/");
25
+ if (slashIndex <= 0 || slashIndex >= ref.length - 1) {
26
+ return undefined;
27
+ }
28
+ if (ref.indexOf("/", slashIndex + 1) !== -1) {
29
+ return undefined;
30
+ }
31
+ return {
32
+ service: ref.slice(0, slashIndex),
33
+ field: ref.slice(slashIndex + 1),
34
+ };
35
+ }
@@ -16,6 +16,7 @@ import {
16
16
  listCredentialMetadata,
17
17
  } from "./metadata-store.js";
18
18
  import type { CredentialInjectionTemplate } from "./policy-types.js";
19
+ import { parseServiceFieldRef } from "./ref-parse.js";
19
20
 
20
21
  export interface ResolvedCredential {
21
22
  credentialId: string;
@@ -95,18 +96,11 @@ export function resolveCredentialRef(
95
96
  }
96
97
 
97
98
  // Try as service/field
98
- const slashIndex = ref.indexOf("/");
99
- if (slashIndex <= 0 || slashIndex >= ref.length - 1) {
99
+ const parsed = parseServiceFieldRef(ref);
100
+ if (!parsed) {
100
101
  return undefined;
101
102
  }
102
- // Reject refs with more than one slash (e.g. "fal/api/key")
103
- if (ref.indexOf("/", slashIndex + 1) !== -1) {
104
- return undefined;
105
- }
106
-
107
- const service = ref.slice(0, slashIndex);
108
- const field = ref.slice(slashIndex + 1);
109
- return resolveByServiceField(service, field);
103
+ return resolveByServiceField(parsed.service, parsed.field);
110
104
  }
111
105
 
112
106
  /**
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Credential write path. The single sequence every "store a credential"
3
+ * caller runs.
4
+ *
5
+ * Storing a credential is not one write: the plaintext goes to the secure
6
+ * backend, the value is scrubbed from recent transcripts, the metadata record
7
+ * is upserted, and manual-token providers reconcile their connection row.
8
+ * Order matters (see {@link storeCredentialValue}), so the sequence lives here
9
+ * rather than being re-typed per entry point. The `credentials/set` route and
10
+ * the plugin-facing {@link ../../plugin-api/store-credential.storeCredential}
11
+ * both compose it.
12
+ *
13
+ * The transcript scrub and the connection sync are imported lazily, at the
14
+ * point of use. Both pull heavy graphs behind them (the scrub reaches the
15
+ * conversation database and registry; the sync reaches the OAuth store), and
16
+ * this module sits under `@vellumai/plugin-api`, so a static edge would drag
17
+ * that machinery into every plugin-api consumer for a write most of them never
18
+ * perform.
19
+ *
20
+ * Callers own their own transport-level argument validation and error mapping;
21
+ * this module throws {@link InvalidCredentialInputError} for a value it will
22
+ * not store and {@link CredentialStorageError} when the secure backend write
23
+ * fails.
24
+ */
25
+
26
+ import {
27
+ ACP_SERVICE,
28
+ assertAcpCredentialFormat,
29
+ } from "../../acp/acp-credentials.js";
30
+ import { credentialKey } from "../../security/credential-key.js";
31
+ import { normalizeSecretValue } from "../../security/secret-normalize.js";
32
+ import {
33
+ getActiveBackendName,
34
+ setSecureKeyAsync,
35
+ } from "../../security/secure-keys.js";
36
+ import { getLogger } from "../../util/logger.js";
37
+ import {
38
+ assertMetadataWritable,
39
+ upsertCredentialMetadata,
40
+ } from "./metadata-store.js";
41
+ import type { CredentialInjectionTemplate } from "./policy-types.js";
42
+
43
+ const log = getLogger("credential-store");
44
+
45
+ /** Raised for a value the store refuses before writing anything. */
46
+ export class InvalidCredentialInputError extends Error {
47
+ constructor(message: string) {
48
+ super(message);
49
+ this.name = "InvalidCredentialInputError";
50
+ }
51
+ }
52
+
53
+ /** Raised when the secure backend write itself fails. */
54
+ export class CredentialStorageError extends Error {
55
+ constructor(message: string) {
56
+ super(message);
57
+ this.name = "CredentialStorageError";
58
+ }
59
+ }
60
+
61
+ export interface StoreCredentialValueInput {
62
+ service: string;
63
+ field: string;
64
+ /** Plaintext value. Edge whitespace is trimmed before storage. */
65
+ value: string;
66
+ /** Human-friendly alias shown in credential listings. */
67
+ alias?: string;
68
+ /** What the credential is for, surfaced to the agent. */
69
+ usageDescription?: string;
70
+ allowedTools?: string[];
71
+ allowedDomains?: string[];
72
+ injectionTemplates?: CredentialInjectionTemplate[];
73
+ /**
74
+ * Skip the retroactive transcript scrub. Set it only when the plaintext
75
+ * provably never transited a conversation (a token minted by an OAuth
76
+ * refresh, say), since the scrub is a bounded but real sweep of recent
77
+ * history.
78
+ */
79
+ skipTranscriptScrub?: boolean;
80
+ }
81
+
82
+ export interface StoredCredential {
83
+ credentialId: string;
84
+ service: string;
85
+ field: string;
86
+ }
87
+
88
+ /**
89
+ * Store a credential's plaintext and reconcile everything that hangs off it.
90
+ *
91
+ * The order is load-bearing:
92
+ * 1. Validate the value (and the ACP format guard) so a rejected write leaves
93
+ * no side effects.
94
+ * 2. Fail early when credential metadata is not writable.
95
+ * 3. Write the plaintext to the secure backend.
96
+ * 4. Scrub the value from recent transcripts. This runs BEFORE the metadata
97
+ * upsert and the connection sync, because those can throw and a
98
+ * stored-but-unscrubbed secret must not depend on them succeeding. The
99
+ * credential IS stored at this point, so the scrub is best-effort hygiene
100
+ * and stays invisible to the caller.
101
+ * 5. Upsert metadata and reconcile the manual-token connection row.
102
+ *
103
+ * @throws {InvalidCredentialInputError} when the value is empty or malformed
104
+ * for its service.
105
+ * @throws {CredentialStorageError} when the secure backend rejects the write.
106
+ */
107
+ export async function storeCredentialValue(
108
+ input: StoreCredentialValueInput,
109
+ ): Promise<StoredCredential> {
110
+ const { service, field } = input;
111
+
112
+ const value = normalizeSecretValue(input.value);
113
+ if (value.length === 0) {
114
+ throw new InvalidCredentialInputError("value is required");
115
+ }
116
+
117
+ // Reject an Anthropic API key pasted into the ACP OAuth-token field (a 401
118
+ // footgun) before it reaches storage.
119
+ if (service === ACP_SERVICE) {
120
+ assertAcpCredentialFormat(field, value);
121
+ }
122
+
123
+ assertMetadataWritable();
124
+
125
+ const stored = await setSecureKeyAsync(credentialKey(service, field), value);
126
+ if (!stored) {
127
+ throw new CredentialStorageError(
128
+ `Failed to store credential in secure storage (backend: ${getActiveBackendName()})`,
129
+ );
130
+ }
131
+
132
+ // The stored plaintext may already sit in recent transcripts: the user
133
+ // message that pasted it, the persisted tool_use input, the tool result
134
+ // echoing the command. This is the scrub seam, not setSecureKeyAsync, which
135
+ // also fires on OAuth refresh rotations and MCP header writes whose values
136
+ // never transited a transcript.
137
+ if (!input.skipTranscriptScrub) {
138
+ const { isNonSecretPlatformField, scrubStoredCredentialFromTranscripts } =
139
+ await import("../../daemon/credential-transcript-scrub.js");
140
+ if (!isNonSecretPlatformField(service, field)) {
141
+ try {
142
+ const scrubbed = await scrubStoredCredentialFromTranscripts(value);
143
+ log.info(
144
+ { service, field, ...scrubbed },
145
+ "Credential stored; scrubbed value from recent transcripts",
146
+ );
147
+ } catch (err) {
148
+ log.warn(
149
+ { err, service, field },
150
+ "Credential stored, but transcript scrub failed",
151
+ );
152
+ }
153
+ }
154
+ }
155
+
156
+ const metadata = upsertCredentialMetadata(service, field, {
157
+ alias: input.alias,
158
+ usageDescription: input.usageDescription,
159
+ allowedTools: input.allowedTools,
160
+ allowedDomains: input.allowedDomains,
161
+ injectionTemplates: input.injectionTemplates,
162
+ });
163
+ const { syncManualTokenConnection } =
164
+ await import("../../oauth/manual-token-connection.js");
165
+ await syncManualTokenConnection(service);
166
+
167
+ return { credentialId: metadata.credentialId, service, field };
168
+ }
@@ -426,13 +426,22 @@ export function executeDocumentUpdate(
426
426
  };
427
427
  }
428
428
 
429
+ // The store drops append content that merely restates the document's tail,
430
+ // so the client renders what actually landed rather than the submission.
431
+ const applied = result.appliedMarkdown;
432
+ const message = result.duplicateLeadingContentSkipped
433
+ ? applied.length === 0
434
+ ? "Nothing appended: the content is already at the end of the document"
435
+ : "Document content updated (leading content already at the end of the document was skipped)"
436
+ : "Document content updated";
437
+
429
438
  // Send document_editor_update message to update the built-in RTE
430
439
  if (context.sendToClient) {
431
440
  context.sendToClient({
432
441
  type: "document_editor_update",
433
442
  conversationId: context.conversationId,
434
443
  surfaceId,
435
- markdown: content,
444
+ markdown: applied,
436
445
  mode,
437
446
  });
438
447
 
@@ -441,7 +450,7 @@ export function executeDocumentUpdate(
441
450
  success: true,
442
451
  surface_id: surfaceId,
443
452
  mode,
444
- message: "Document content updated",
453
+ message,
445
454
  }),
446
455
  isError: false,
447
456
  };
@@ -458,7 +467,7 @@ export function executeDocumentUpdate(
458
467
  success: true,
459
468
  surface_id: surfaceId,
460
469
  mode,
461
- message: "Document content updated (no client connected to render it)",
470
+ message: `${message} (no client connected to render it)`,
462
471
  }),
463
472
  isError: false,
464
473
  };
@@ -1,9 +1,21 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
 
3
+ import type {
4
+ RiskAllowlistOption,
5
+ RiskDirectoryScopeOption,
6
+ RiskPatternScopeOption,
7
+ } from "@vellumai/gateway-client";
8
+
3
9
  import { getConfig } from "../config/loader.js";
10
+ import {
11
+ classifyRisk,
12
+ type RiskClassificationWithMeta,
13
+ } from "../permissions/checker.js";
4
14
  import { PermissionPrompter } from "../permissions/prompter.js";
5
- import { RiskLevel } from "../permissions/types.js";
6
- import { runInPluginContext } from "../plugins/plugin-execution-context.js";
15
+ import {
16
+ runInPluginContext,
17
+ runOutsidePluginContext,
18
+ } from "../plugins/plugin-execution-context.js";
7
19
  import { TokenExpiredError } from "../security/token-manager.js";
8
20
  import {
9
21
  recordToolError,
@@ -30,6 +42,7 @@ import { MAX_FILE_SIZE_BYTES } from "./shared/filesystem/size-guard.js";
30
42
  import { ToolApprovalHandler } from "./tool-approval-handler.js";
31
43
  import { resolveToolInvocationAlias } from "./tool-name-aliases.js";
32
44
  import { recordToolCompletion } from "./tool-profiler.js";
45
+ import { RISK_LEVEL_UNCLASSIFIED } from "./tool-types.js";
33
46
  import { type ToolContext, type ToolExecutionResult } from "./types.js";
34
47
 
35
48
  export class ToolExecutor {
@@ -60,18 +73,34 @@ export class ToolExecutor {
60
73
  ): Promise<ToolExecutionResult> {
61
74
  const startTime = Date.now();
62
75
  let decision = "allow";
63
- let riskLevel: string = RiskLevel.Low;
76
+ // The invocation's one classification, taken before the gates so every
77
+ // audit row this call can produce (gate denial, gate error, grant-consumed
78
+ // execution, permission decision) records the risk of what the model asked
79
+ // for, then handed down to `checkPermission`. A classification that does
80
+ // not complete (aborted, gateway unreachable) is recorded as such rather
81
+ // than as a level; the permission check still requires one and fails
82
+ // closed on its own.
83
+ let classification: RiskClassificationWithMeta | undefined;
84
+ try {
85
+ classification = await classifyRisk(
86
+ name,
87
+ input,
88
+ context.workingDir,
89
+ undefined,
90
+ undefined,
91
+ context.signal,
92
+ );
93
+ } catch {
94
+ // Stays undefined; the audit row records RISK_LEVEL_UNCLASSIFIED.
95
+ }
96
+ let riskLevel: string = classification?.level ?? RISK_LEVEL_UNCLASSIFIED;
64
97
  let permRiskMeta:
65
98
  | {
66
99
  riskLevel: string;
67
100
  riskReason: string;
68
- riskScopeOptions: Array<{ pattern: string; label: string }>;
69
- riskAllowlistOptions?: Array<{
70
- label: string;
71
- description: string;
72
- pattern: string;
73
- }>;
74
- riskDirectoryScopeOptions?: Array<{ scope: string; label: string }>;
101
+ riskScopeOptions: RiskPatternScopeOption[];
102
+ riskAllowlistOptions?: RiskAllowlistOption[];
103
+ riskDirectoryScopeOptions?: RiskDirectoryScopeOption[];
75
104
  isContainerized?: boolean;
76
105
  }
77
106
  | undefined;
@@ -106,6 +135,11 @@ export class ToolExecutor {
106
135
  // consumed; substitute the parsed value (with `.catch()` recoveries
107
136
  // applied) so validation and execution see the same input.
108
137
  if (gateResult.parsedInput) {
138
+ // The permission decision is about the input that runs, so a parse that
139
+ // reshaped it (a `.catch()` recovery) gets its own classification.
140
+ if (!Bun.deepEquals(input, gateResult.parsedInput)) {
141
+ classification = undefined;
142
+ }
109
143
  input = gateResult.parsedInput;
110
144
  }
111
145
 
@@ -189,6 +223,7 @@ export class ToolExecutor {
189
223
  context,
190
224
  startTime,
191
225
  computePreviewDiff,
226
+ classification,
192
227
  );
193
228
 
194
229
  riskLevel = permResult.riskLevel;
@@ -235,15 +270,18 @@ export class ToolExecutor {
235
270
  const execContext = context;
236
271
 
237
272
  // Mark the owning plugin as in context (via AsyncLocalStorage) so host
238
- // APIs the tool reaches — e.g. resolveCredential — can scope to it. The
273
+ // APIs the tool reaches, e.g. resolveCredential, can scope to it. The
239
274
  // context must be established around the `execute()` call itself so the
240
- // returned promise carries the binding across its awaits. Non-plugin
241
- // tools (default/skill/mcp/workspace) establish no context.
275
+ // returned promise carries the binding across its awaits. A non-plugin
276
+ // tool (default/skill/mcp/workspace) runs with the context explicitly
277
+ // cleared rather than merely unset: the turn may have been started by a
278
+ // plugin (a route handler calling `runConversationTurn`), and this tool
279
+ // is host code that must not inherit that plugin's identity.
242
280
  const owner = getToolOwner(name);
243
281
  const execPromise =
244
282
  owner?.kind === "plugin"
245
283
  ? runInPluginContext(owner.id, () => tool.execute(input, execContext))
246
- : tool.execute(input, execContext);
284
+ : runOutsidePluginContext(() => tool.execute(input, execContext));
247
285
 
248
286
  let execResult: ToolExecutionResult = await executeWithTimeout(
249
287
  execPromise,
@@ -295,7 +333,7 @@ export class ToolExecutor {
295
333
  safeResult.isError,
296
334
  );
297
335
 
298
- // Merge risk metadata from the classifier assessment cache onto the
336
+ // Merge the classification's risk metadata onto the
299
337
  // tool result so downstream consumers (AgentEvent → handleToolResult →
300
338
  // ToolResult SSE message) can forward it to the client.
301
339
  if (permRiskMeta) {
@@ -12,6 +12,7 @@ import {
12
12
  IMAGE_EXTENSIONS,
13
13
  readImageFile,
14
14
  } from "../shared/filesystem/image-read.js";
15
+ import { legacyReadArgsError } from "../shared/filesystem/legacy-read-args.js";
15
16
  import { sandboxReadPolicy } from "../shared/filesystem/path-policy.js";
16
17
  import {
17
18
  invalidToolInputResult,
@@ -25,8 +26,8 @@ import type {
25
26
 
26
27
  /**
27
28
  * Model-input schema, the single source for both runtime validation (via
28
- * `TOOL_INPUT_SCHEMAS`) and the advertised `input_schema` below. `offset` and
29
- * `limit` catch to `undefined` because the tool has always ignored non-numeric
29
+ * `TOOL_INPUT_SCHEMAS`) and the advertised `input_schema` below. `start_index`
30
+ * and `max_chars` catch to `undefined` because the tool ignores non-numeric
30
31
  * values rather than failing the read.
31
32
  */
32
33
  export const fileReadInputSchema = z.looseObject({
@@ -36,14 +37,16 @@ export const fileReadInputSchema = z.looseObject({
36
37
  .describe(
37
38
  "The path to the file to read (absolute or relative to working directory)",
38
39
  ),
39
- offset: z
40
+ start_index: z
40
41
  .number()
41
- .describe("Line number to start reading from (1-indexed)")
42
+ .describe("Character to start reading from (0-indexed). Text files only.")
42
43
  .optional()
43
44
  .catch(undefined),
44
- limit: z
45
+ max_chars: z
45
46
  .number()
46
- .describe("Maximum number of lines to read (defaults to 2000)")
47
+ .describe(
48
+ "Maximum number of characters to read. Defaults to 20000, which is also the ceiling. Text files only.",
49
+ )
47
50
  .optional()
48
51
  .catch(undefined),
49
52
  activity: z
@@ -58,7 +61,7 @@ export const fileReadInputSchema = z.looseObject({
58
61
  export const fileReadTool = {
59
62
  name: "file_read",
60
63
  description:
61
- "Read the contents of a file on your own machine. Text reads return the first 2000 lines unless you pass `limit`; when a read stops short the result says so, and `offset` pages on from there. For image files (JPEG, PNG, GIF, WebP), returns the image for visual analysis. For audio files (MP3, WAV, OGG, FLAC, AAC, M4A), returns the audio for listening. Use host_file_read for files on your guardian's device instead.",
64
+ "Read the contents of a file on your own machine. Text reads return the first 20000 characters unless you pass `max_chars`; when a read stops short the result says so, and `start_index` pages on from there. To find where something is in a large file, code_search is cheaper than paging through it. For image files (JPEG, PNG, GIF, WebP), returns the image for visual analysis. For audio files (MP3, WAV, OGG, FLAC, AAC, M4A), returns the audio for listening. Use host_file_read for files on your guardian's device instead.",
62
65
  category: "filesystem",
63
66
  executionTarget: "sandbox",
64
67
  defaultRiskLevel: RiskLevel.Low,
@@ -75,9 +78,14 @@ export const fileReadTool = {
75
78
  if (!parsed.success) {
76
79
  return invalidToolInputResult("file_read", parsed.error);
77
80
  }
78
- const { path: rawPath, offset, limit } = parsed.data;
81
+ const {
82
+ path: rawPath,
83
+ start_index: startIndex,
84
+ max_chars: maxChars,
85
+ } = parsed.data;
79
86
 
80
- // For image files, delegate to the shared image reader.
87
+ // For image files, delegate to the shared image reader. Media reads carry
88
+ // no window, so the legacy-argument guard below does not apply to them.
81
89
  const ext = extname(rawPath).toLowerCase();
82
90
  if (IMAGE_EXTENSIONS.has(ext)) {
83
91
  const pathCheck = sandboxReadPolicy(rawPath, context.workingDir);
@@ -102,11 +110,20 @@ export const fileReadTool = {
102
110
  return readAudioFile(pathCheck.resolved);
103
111
  }
104
112
 
113
+ const legacyArgs = legacyReadArgsError("file_read", input);
114
+ if (legacyArgs !== undefined) {
115
+ return { content: legacyArgs, isError: true };
116
+ }
117
+
105
118
  const ops = new FileSystemOps((path, opts) =>
106
119
  sandboxReadPolicy(path, context.workingDir, opts),
107
120
  );
108
121
 
109
- const result = await ops.readFileSafe({ path: rawPath, offset, limit });
122
+ const result = await ops.readFileSafe({
123
+ path: rawPath,
124
+ startIndex,
125
+ maxChars,
126
+ });
110
127
 
111
128
  if (!result.ok) {
112
129
  const { error } = result;