@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
@@ -11,13 +11,14 @@ import {
11
11
  readAudioFile,
12
12
  } from "../shared/filesystem/audio-read.js";
13
13
  import {
14
- DEFAULT_READ_LINE_LIMIT,
15
14
  FileSystemOps,
15
+ READ_CHAR_BUDGET,
16
16
  } from "../shared/filesystem/file-ops-service.js";
17
17
  import {
18
18
  IMAGE_EXTENSIONS,
19
19
  readImageFile,
20
20
  } from "../shared/filesystem/image-read.js";
21
+ import { legacyReadArgsError } from "../shared/filesystem/legacy-read-args.js";
21
22
  import { hostPolicy } from "../shared/filesystem/path-policy.js";
22
23
  import {
23
24
  invalidToolInputResult,
@@ -32,9 +33,9 @@ import type {
32
33
  /**
33
34
  * Model-input schema, the single source for both runtime validation (via
34
35
  * `TOOL_INPUT_SCHEMAS`) and the advertised `input_schema` below — mirrors
35
- * `filesystem/read.ts`. `offset`/`limit` catch to `undefined` so a
36
- * non-numeric value falls back to the default line window instead of failing
37
- * the call; `target_client_id` catches so a non-string (or empty) value means
36
+ * `filesystem/read.ts`. `start_index`/`max_chars` catch to `undefined` so a
37
+ * non-numeric value falls back to the default character window instead of
38
+ * failing the call; `target_client_id` catches so a non-string (or empty) value means
38
39
  * "untargeted".
39
40
  */
40
41
  export const hostFileReadInputSchema = z.looseObject({
@@ -44,14 +45,16 @@ export const hostFileReadInputSchema = z.looseObject({
44
45
  .describe(
45
46
  "Absolute path on the guardian's device, which is a separate filesystem from your workspace, to read.",
46
47
  ),
47
- offset: z
48
+ start_index: z
48
49
  .number()
49
- .describe("Line number to start reading from (1-indexed)")
50
+ .describe("Character to start reading from (0-indexed). Text files only.")
50
51
  .optional()
51
52
  .catch(undefined),
52
- limit: z
53
+ max_chars: z
53
54
  .number()
54
- .describe("Maximum number of lines to read (defaults to 2000)")
55
+ .describe(
56
+ "Maximum number of characters to read. Defaults to 20000, which is also the ceiling. Text files only.",
57
+ )
55
58
  .optional()
56
59
  .catch(undefined),
57
60
  target_client_id: z
@@ -66,7 +69,7 @@ export const hostFileReadInputSchema = z.looseObject({
66
69
  export const hostFileReadTool = {
67
70
  name: "host_file_read",
68
71
  description:
69
- "Read the contents of a file on your guardian's device, including images (JPEG, PNG, GIF, WebP) and audio (MP3, WAV, OGG, FLAC, AAC, M4A). 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 files on your own machine, use file_read instead.",
72
+ "Read the contents of a file on your guardian's device, including images (JPEG, PNG, GIF, WebP) and audio (MP3, WAV, OGG, FLAC, AAC, M4A). 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. For files on your own machine, use file_read instead.",
70
73
  category: "host-filesystem",
71
74
  executionTarget: "host",
72
75
  defaultRiskLevel: RiskLevel.Medium,
@@ -81,12 +84,12 @@ export const hostFileReadTool = {
81
84
  if (!parsed.success) {
82
85
  return invalidToolInputResult("host_file_read", parsed.error);
83
86
  }
84
- const { path: rawPath, offset } = parsed.data;
87
+ const { path: rawPath, start_index: startIndex } = parsed.data;
85
88
  // Resolve the default here rather than leaving it to the read, so the
86
89
  // proxied branch below is bounded by the same window as the local one. A
87
- // proxied read that sent no limit would stream a whole host file across
90
+ // proxied read that sent no budget would stream a whole host file across
88
91
  // the bridge before anything could trim it.
89
- const limit = parsed.data.limit ?? DEFAULT_READ_LINE_LIMIT;
92
+ const maxChars = parsed.data.max_chars ?? READ_CHAR_BUDGET;
90
93
 
91
94
  const targetClientId =
92
95
  parsed.data.target_client_id !== ""
@@ -153,8 +156,8 @@ export const hostFileReadTool = {
153
156
  {
154
157
  operation: "read",
155
158
  path: rawPath,
156
- offset,
157
- limit,
159
+ startIndex,
160
+ maxChars,
158
161
  targetClientId,
159
162
  },
160
163
  context.conversationId,
@@ -181,9 +184,18 @@ export const hostFileReadTool = {
181
184
  return readAudioFile(pathCheck.resolved);
182
185
  }
183
186
 
187
+ const legacyArgs = legacyReadArgsError("host_file_read", input);
188
+ if (legacyArgs !== undefined) {
189
+ return { content: legacyArgs, isError: true };
190
+ }
191
+
184
192
  const ops = new FileSystemOps(hostPolicy);
185
193
 
186
- const result = await ops.readFileSafe({ path: rawPath, offset, limit });
194
+ const result = await ops.readFileSafe({
195
+ path: rawPath,
196
+ startIndex,
197
+ maxChars,
198
+ });
187
199
 
188
200
  if (!result.ok) {
189
201
  const { error } = result;
@@ -1,23 +1,16 @@
1
1
  import { lookup } from "node:dns/promises";
2
2
 
3
- export type ResolveHostAddresses = (hostname: string) => Promise<string[]>;
3
+ import {
4
+ looksLikeHostPortShorthand,
5
+ looksLikePathOnlyInput,
6
+ } from "@vellumai/service-contracts/url-normalization";
4
7
 
5
- export function looksLikeHostPortShorthand(value: string): boolean {
6
- if (/^\[[0-9a-fA-F:.%]+\]:\d+(?:[/?#]|$)/.test(value)) {
7
- return true;
8
- }
9
- return /^[^/?#@\s:]+:\d+(?:[/?#]|$)/.test(value);
10
- }
8
+ export type ResolveHostAddresses = (hostname: string) => Promise<string[]>;
11
9
 
12
- export function looksLikePathOnlyInput(value: string): boolean {
13
- return (
14
- value.startsWith("/") ||
15
- value.startsWith("./") ||
16
- value.startsWith("../") ||
17
- value.startsWith("?") ||
18
- value.startsWith("#")
19
- );
20
- }
10
+ // The URL input-shape predicates are defined once, beside the canonicalizer
11
+ // the gateway builds trust-rule patterns with, so what counts as a `host:port`
12
+ // or a path here is what counts there. Re-exported for existing consumers.
13
+ export { looksLikeHostPortShorthand, looksLikePathOnlyInput };
21
14
 
22
15
  export function parseUrl(input: unknown): URL | null {
23
16
  if (typeof input !== "string") {
@@ -1,13 +1,18 @@
1
+ import type {
2
+ RiskAllowlistOption,
3
+ RiskDirectoryScopeOption,
4
+ RiskPatternScopeOption,
5
+ } from "@vellumai/gateway-client";
6
+
1
7
  import { getIsContainerized } from "../config/env-registry.js";
2
8
  import { mapApprovalProvenance } from "../permissions/approval-provenance.js";
3
9
  import { buildChannelPermissionCellQuery } from "../permissions/channel-permission-query.js";
4
10
  import {
5
11
  check,
6
12
  classifyRisk,
7
- generateAllowlistOptions,
8
13
  generateScopeOptions,
9
- getCachedAssessment,
10
14
  isDynamicSkillLoadInvocation,
15
+ type RiskClassificationWithMeta,
11
16
  } from "../permissions/checker.js";
12
17
  import { getAutoApproveThreshold } from "../permissions/gateway-threshold-reader.js";
13
18
  import type { PermissionPrompter } from "../permissions/prompter.js";
@@ -37,19 +42,15 @@ export type PermissionDecision =
37
42
  decision: string;
38
43
  riskLevel: string;
39
44
  wasPrompted?: boolean;
40
- /** ID of the trust rule that matched this invocation (if any). Always set when a rule matched, even for non-classifier tools where riskMeta is absent. */
45
+ /** ID of the trust rule that matched this invocation (if any). */
41
46
  matchedTrustRuleId?: string;
42
- /** Risk metadata from the classifier assessment cache (when available). */
43
- riskMeta?: {
47
+ /** Risk metadata from the invocation's classification. */
48
+ riskMeta: {
44
49
  riskLevel: string;
45
50
  riskReason: string;
46
- riskScopeOptions: Array<{ pattern: string; label: string }>;
47
- riskAllowlistOptions?: Array<{
48
- label: string;
49
- description: string;
50
- pattern: string;
51
- }>;
52
- riskDirectoryScopeOptions?: Array<{ scope: string; label: string }>;
51
+ riskScopeOptions: RiskPatternScopeOption[];
52
+ riskAllowlistOptions?: RiskAllowlistOption[];
53
+ riskDirectoryScopeOptions?: RiskDirectoryScopeOption[];
53
54
  isContainerized?: boolean;
54
55
  };
55
56
  approvalMode?: ApprovalMode;
@@ -61,19 +62,15 @@ export type PermissionDecision =
61
62
  decision: string;
62
63
  riskLevel: string;
63
64
  content: string;
64
- /** ID of the trust rule that matched this invocation (if any). Always set when a rule matched, even for non-classifier tools where riskMeta is absent. */
65
+ /** ID of the trust rule that matched this invocation (if any). */
65
66
  matchedTrustRuleId?: string;
66
- /** Risk metadata from the classifier assessment cache (when available). */
67
- riskMeta?: {
67
+ /** Risk metadata from the invocation's classification. */
68
+ riskMeta: {
68
69
  riskLevel: string;
69
70
  riskReason: string;
70
- riskScopeOptions: Array<{ pattern: string; label: string }>;
71
- riskAllowlistOptions?: Array<{
72
- label: string;
73
- description: string;
74
- pattern: string;
75
- }>;
76
- riskDirectoryScopeOptions?: Array<{ scope: string; label: string }>;
71
+ riskScopeOptions: RiskPatternScopeOption[];
72
+ riskAllowlistOptions?: RiskAllowlistOption[];
73
+ riskDirectoryScopeOptions?: RiskDirectoryScopeOption[];
77
74
  isContainerized?: boolean;
78
75
  };
79
76
  approvalMode?: ApprovalMode;
@@ -89,10 +86,13 @@ export class PermissionChecker {
89
86
  }
90
87
 
91
88
  /**
92
- * Run risk classification, trust rule evaluation, and (if needed) user
93
- * prompting for a tool invocation. Returns whether the tool is allowed
94
- * to execute, along with the decision string and risk level for lifecycle
95
- * event reporting.
89
+ * Run trust rule evaluation and (if needed) user prompting for a tool
90
+ * invocation. Returns whether the tool is allowed to execute, along with
91
+ * the decision string and risk level for lifecycle event reporting.
92
+ *
93
+ * `classification` is the invocation's gateway classification when the
94
+ * executor already holds it (it classifies once, before its gates); absent
95
+ * that, the same input is classified here.
96
96
  */
97
97
  async checkPermission(
98
98
  name: string,
@@ -113,10 +113,11 @@ export class PermissionChecker {
113
113
  }
114
114
  | undefined
115
115
  >,
116
+ classification?: RiskClassificationWithMeta,
116
117
  ): Promise<PermissionDecision> {
117
118
  // Sandbox/host routing for the prompt comes from the tool's manifest.
118
119
  const executionTarget = resolveExecutionTarget(tool);
119
- const { level: risk, reason: riskReason } = await classifyRisk(
120
+ classification ??= await classifyRisk(
120
121
  name,
121
122
  input,
122
123
  context.workingDir,
@@ -124,26 +125,19 @@ export class PermissionChecker {
124
125
  undefined,
125
126
  context.signal,
126
127
  );
128
+ const { level: risk, reason: riskReason } = classification;
127
129
  const riskLevel: string = risk;
128
130
 
129
- // Look up the cached assessment to extract risk metadata for the tool result.
130
- // This is populated by classifyRisk() for classifier-backed tools (bash, file, web, skill).
131
- // For tools without classifiers (e.g. MCP tools), the cache returns undefined.
132
- const cachedAssessment = getCachedAssessment(name, input);
133
- const riskMeta = cachedAssessment
134
- ? {
135
- riskLevel: cachedAssessment.riskLevel,
136
- riskReason: cachedAssessment.reason,
137
- // Display ladder (regex patterns — internal only, not for save).
138
- riskScopeOptions: cachedAssessment.scopeOptions,
139
- // Save ladder (Minimatch globs — what the gateway matches against).
140
- // Populated for classifiers that produce allowlist options
141
- // (bash, file, skill); undefined otherwise.
142
- riskAllowlistOptions: cachedAssessment.allowlistOptions,
143
- riskDirectoryScopeOptions: cachedAssessment.directoryScopeOptions,
144
- isContainerized: getIsContainerized(),
145
- }
146
- : undefined;
131
+ const riskMeta = {
132
+ riskLevel: classification.level,
133
+ riskReason: classification.reason,
134
+ // Display ladder: bash only, internal, never persisted as a rule.
135
+ riskScopeOptions: classification.scopeOptions,
136
+ // Save ladder: the patterns the gateway matches a rule by, exactly.
137
+ riskAllowlistOptions: classification.allowlistOptions,
138
+ riskDirectoryScopeOptions: classification.directoryScopeOptions,
139
+ isContainerized: getIsContainerized(),
140
+ };
147
141
 
148
142
  // Wrap the rest of permission evaluation so that any exception
149
143
  // carries the classified risk level back to the caller. Without
@@ -158,6 +152,7 @@ export class PermissionChecker {
158
152
  policyContext,
159
153
  undefined,
160
154
  context.signal,
155
+ classification,
161
156
  );
162
157
 
163
158
  // Every threshold read for this invocation must consult the same
@@ -240,7 +235,7 @@ export class PermissionChecker {
240
235
  if (
241
236
  context.isInteractive === false &&
242
237
  isDynamicSkillLoadInvocation(name, input) &&
243
- getCachedAssessment(name, input)?.matchType !== "user_rule"
238
+ classification.matchType !== "user_rule"
244
239
  ) {
245
240
  log.info(
246
241
  { toolName: name, riskLevel },
@@ -385,11 +380,7 @@ export class PermissionChecker {
385
380
  context.workingDir,
386
381
  );
387
382
  const promptOptions = {
388
- allowlistOptions: await generateAllowlistOptions(
389
- name,
390
- input,
391
- context.signal,
392
- ),
383
+ allowlistOptions: classification.allowlistOptions ?? [],
393
384
  scopeOptions: generateScopeOptions(context.workingDir, name),
394
385
  persistentDecisionsAllowed: !context.requireFreshApproval,
395
386
  };
@@ -410,7 +401,7 @@ export class PermissionChecker {
410
401
  context.toolUseId,
411
402
  riskReason,
412
403
  getIsContainerized(),
413
- cachedAssessment?.directoryScopeOptions,
404
+ classification.directoryScopeOptions,
414
405
  );
415
406
 
416
407
  const decision = response.decision;
@@ -431,7 +431,8 @@ export function unregisterPluginTools(pluginName: string): void {
431
431
  * {@link import("../plugins/mtime-cache.js").getActiveUserPluginTools} and diffs
432
432
  * the result into the registry: plugins that vanished are unregistered, new
433
433
  * plugins register, and a plugin whose per-tool source mtimes moved is
434
- * re-registered. Mirrors how hooks resolve through `getUserHookEntriesFor`.
434
+ * re-registered. Mirrors how hooks resolve through `getUserHookEntriesFor`
435
+ * in `hooks/registry.ts`.
435
436
  *
436
437
  * This is a pure read of the plugin cache — it does NOT reconcile the cache
437
438
  * against the source-versions sentinel, so it never activates a plugin or runs
@@ -78,26 +78,55 @@ function pathError(
78
78
  }
79
79
 
80
80
  /**
81
- * Lines returned by a read that names no `limit`. Without a default the read
82
- * returns the whole file, and a file-read result is honored in full for the
83
- * rest of the turn (see `isSpoolEligible` in `context/tool-result-spool.ts`),
84
- * so one unbounded read of a large file rides every subsequent LLM call in
85
- * that turn. The cap bounds that; `offset`/`limit` page past it, and
86
- * {@link truncationNotice} tells the model when it is looking at a window
87
- * rather than the whole file.
81
+ * Characters returned by a read that names no `max_chars`. Stays under
82
+ * `THRESHOLD_CHARS` in `context/post-turn-tool-result-truncation.ts`, which
83
+ * spools any larger tool result to disk and replaces it inline with a short
84
+ * stub, so a default read returns content rather than a stub.
88
85
  */
89
- export const DEFAULT_READ_LINE_LIMIT = 2000;
86
+ export const READ_CHAR_BUDGET = 20_000;
90
87
 
91
88
  /**
92
- * Trailing marker appended when a read stops short of the last line. Silent
93
- * truncation is the failure mode worth avoiding: a model that cannot tell a
94
- * window from a whole file reasons about code it never saw.
89
+ * Trailing marker appended when a read stops short of the end of the file. A
90
+ * model that cannot tell a window from a whole file reasons about code it
91
+ * never saw.
95
92
  */
96
93
  function truncationNotice(
97
- lastLineReturned: number,
98
- totalLines: number,
94
+ start: number,
95
+ end: number,
96
+ totalChars: number,
99
97
  ): string {
100
- return `\n\n[Truncated: showing through line ${lastLineReturned} of ${totalLines}. Read on with offset=${lastLineReturned + 1}, or pass an explicit limit.]`;
98
+ return `\n\n[Truncated: characters ${start}-${end} of ${totalChars}. Read on with start_index=${end}.]`;
99
+ }
100
+
101
+ const isHighSurrogate = (code: number): boolean =>
102
+ code >= 0xd800 && code <= 0xdbff;
103
+ const isLowSurrogate = (code: number): boolean =>
104
+ code >= 0xdc00 && code <= 0xdfff;
105
+
106
+ /**
107
+ * Character window that never splits a surrogate pair. A split leaves a lone
108
+ * half at each edge, and each encodes to U+FFFD, so the character is lost from
109
+ * both this window and the next one paged in after it.
110
+ */
111
+ export function surrogateSafeWindow(
112
+ total: number,
113
+ charCodeAt: (index: number) => number,
114
+ requestedStart: number,
115
+ maxChars: number,
116
+ ): { start: number; end: number } {
117
+ let start = Math.max(0, Math.min(requestedStart, total));
118
+ if (start > 0 && start < total && isLowSurrogate(charCodeAt(start))) {
119
+ start -= 1;
120
+ }
121
+
122
+ let end = Math.min(total, start + maxChars);
123
+ if (end > start && end < total && isHighSurrogate(charCodeAt(end - 1))) {
124
+ // Backing off would empty a one-character window, which stalls paging on
125
+ // the same offset, so take the whole pair instead.
126
+ end = end - 1 > start ? end - 1 : Math.min(total, end + 1);
127
+ }
128
+
129
+ return { start, end };
101
130
  }
102
131
 
103
132
  export class FileSystemOps {
@@ -139,28 +168,27 @@ export class FileSystemOps {
139
168
 
140
169
  try {
141
170
  const raw = await readFile(filePath, "utf-8");
142
- const lines = raw.split("\n");
143
-
144
- const offset = (input.offset ?? 1) - 1;
145
- const start = Math.max(0, offset);
146
- const limit = input.limit ?? DEFAULT_READ_LINE_LIMIT;
147
- const selected = lines.slice(start, offset + limit);
148
-
149
- const numbered = selected
150
- .map((line, i) => {
151
- const lineNum = offset + i + 1;
152
- return `${String(lineNum).padStart(6)} ${line}`;
153
- })
154
- .join("\n");
155
-
156
- // Only when the window stops before the last line. An empty window means
157
- // the caller paged past the end or asked for nothing, which is not a
158
- // truncated read.
159
- const lastLineReturned = start + selected.length;
171
+
172
+ // A ceiling, not just a default: a larger window would be spooled to
173
+ // disk and replaced with a stub, returning less than this.
174
+ const maxChars = Math.min(
175
+ READ_CHAR_BUDGET,
176
+ Math.max(0, input.maxChars ?? READ_CHAR_BUDGET),
177
+ );
178
+ const { start, end } = surrogateSafeWindow(
179
+ raw.length,
180
+ (i) => raw.charCodeAt(i),
181
+ input.startIndex ?? 0,
182
+ maxChars,
183
+ );
184
+ const window = raw.slice(start, end);
185
+
186
+ // An empty window means the caller paged past the end or asked for
187
+ // nothing, which is not a truncated read.
160
188
  const content =
161
- selected.length > 0 && lastLineReturned < lines.length
162
- ? numbered + truncationNotice(lastLineReturned, lines.length)
163
- : numbered;
189
+ window.length > 0 && end < raw.length
190
+ ? window + truncationNotice(start, end, raw.length)
191
+ : window;
164
192
 
165
193
  return { ok: true, value: { content } };
166
194
  } catch (err) {
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Guard for callers still passing the line-based `offset`/`limit` read
3
+ * arguments. The tool schemas are loose, so those keys parse and are then
4
+ * dropped, which would silently serve the start of the file to a caller that
5
+ * asked to page into the middle of it.
6
+ *
7
+ * There is no safe translation: `offset` counted lines and `start_index`
8
+ * counts characters, so mapping one onto the other would read the wrong
9
+ * region rather than fail. Naming the rename lets the caller correct itself.
10
+ */
11
+ export function legacyReadArgsError(
12
+ toolName: string,
13
+ input: Record<string, unknown>,
14
+ ): string | undefined {
15
+ const usesLegacy = input.offset !== undefined || input.limit !== undefined;
16
+ const usesCurrent =
17
+ input.start_index !== undefined || input.max_chars !== undefined;
18
+ if (!usesLegacy || usesCurrent) {
19
+ return undefined;
20
+ }
21
+ return `Error: ${toolName} no longer takes \`offset\`/\`limit\` (lines). Use \`start_index\` (0-indexed characters) and \`max_chars\` instead.`;
22
+ }
@@ -6,14 +6,14 @@ import type { FsError } from "./errors.js";
6
6
 
7
7
  export interface ReadInput {
8
8
  path: string;
9
- /** 1-indexed line number to start reading from. */
10
- offset?: number;
11
- /** Maximum number of lines to read. */
12
- limit?: number;
9
+ /** 0-indexed character to start reading from. */
10
+ startIndex?: number;
11
+ /** Maximum number of characters to read. */
12
+ maxChars?: number;
13
13
  }
14
14
 
15
15
  export interface ReadOutput {
16
- /** The (possibly line-numbered) file content. */
16
+ /** The character window, with a truncation notice when it stops short. */
17
17
  content: string;
18
18
  }
19
19
 
@@ -28,10 +28,20 @@ function sanitizeFrontmatterValue(value: string): string {
28
28
  }
29
29
 
30
30
  /**
31
- * Validate + normalize an optional string-array input (sanitize, drop blanks,
32
- * dedupe). Returns `{ error }` on the first invalid element, or `{ value }`
33
- * holding the normalized array (undefined when empty). Shared by the
34
- * includes / activation_hints / avoid_when inputs so they behave identically.
31
+ * Self-correcting error for a scaffold call with no activation hints. Matches
32
+ * the shape of the schema validator's required-field error, which is what a
33
+ * call through the registered tool hits first; this covers direct callers and
34
+ * an explicit empty list, which the schema's presence check accepts.
35
+ */
36
+ const MISSING_ACTIVATION_HINTS =
37
+ 'activation_hints is required: pass 1-4 short trigger phrases stating the intent this skill serves (for example "user asks to deploy staging") so it can be found later by intent, not just by name.';
38
+
39
+ /**
40
+ * Validate + normalize a string-array input (sanitize, drop blanks, dedupe).
41
+ * Returns `{ error }` on the first invalid element, or `{ value }` holding
42
+ * the normalized array (undefined when absent or empty). Shared by the
43
+ * includes / activation_hints / avoid_when inputs so they behave identically;
44
+ * whether an absent value is acceptable is the caller's call.
35
45
  * Each element goes through sanitizeFrontmatterValue: activation_hints /
36
46
  * avoid_when are concatenated verbatim into capability memory text (see
37
47
  * buildSkillContent), so an embedded newline could otherwise smuggle an extra
@@ -184,9 +194,9 @@ export async function executeScaffoldManagedSkill(
184
194
  };
185
195
  }
186
196
 
187
- // Validate and normalize the optional string-array inputs. `includes` lists
188
- // child skill IDs; activation_hints / avoid_when become the skill's
189
- // "Use when:" / "Avoid when:" retrieval signal in memory.
197
+ // Validate and normalize the string-array inputs. `includes` lists child
198
+ // skill IDs; activation_hints / avoid_when become the skill's "Use when:" /
199
+ // "Avoid when:" retrieval signal in memory.
190
200
  const includesResult = normalizeOptionalStringArray(
191
201
  input.includes,
192
202
  "includes",
@@ -204,6 +214,14 @@ export async function executeScaffoldManagedSkill(
204
214
  return { content: `Error: ${activationHintsResult.error}`, isError: true };
205
215
  }
206
216
  const activationHints = activationHintsResult.value;
217
+ // Hints are the skill's "Use when:" retrieval text, and scaffolding rewrites
218
+ // the whole SKILL.md, so a write without them leaves (or strips) none.
219
+ if (!activationHints) {
220
+ return {
221
+ content: `Error: ${MISSING_ACTIVATION_HINTS}`,
222
+ isError: true,
223
+ };
224
+ }
207
225
 
208
226
  const avoidWhenResult = normalizeOptionalStringArray(
209
227
  input.avoid_when,