@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
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Canonical URL normalization for web tool inputs, shared by the gateway's
3
+ * risk classifiers and the daemon's URL safety checks.
4
+ *
5
+ * A URL a model passes to `web_fetch` / `network_request` reaches more than
6
+ * one consumer, and they must agree on what counts as the same target: the
7
+ * gateway builds the trust-rule ladder from it, and anything that later
8
+ * compares a URL to a saved rule has to fold the same spellings together. Two
9
+ * normalizations would mean two answers, so it lives here rather than in
10
+ * either process.
11
+ *
12
+ * Data-only in spirit: no imports, no config, no I/O.
13
+ */
14
+
15
+ /** Whether a bare `host:port` (or `[v6]:port`) shorthand was written. */
16
+ export function looksLikeHostPortShorthand(value: string): boolean {
17
+ if (/^\[[0-9a-fA-F:.%]+\]:\d+(?:[/?#]|$)/.test(value)) {
18
+ return true;
19
+ }
20
+ return /^[^/?#@\s:]+:\d+(?:[/?#]|$)/.test(value);
21
+ }
22
+
23
+ /** Whether the input is a path, query, or fragment rather than a URL. */
24
+ export function looksLikePathOnlyInput(value: string): boolean {
25
+ return (
26
+ value.startsWith("/") ||
27
+ value.startsWith("./") ||
28
+ value.startsWith("../") ||
29
+ value.startsWith("?") ||
30
+ value.startsWith("#")
31
+ );
32
+ }
33
+
34
+ /**
35
+ * Strip the parts of a URL that must not affect a trust decision, and fold
36
+ * the encodings that would otherwise let one URL wear two spellings.
37
+ *
38
+ * Percent-escaped path segments are decoded (`/%70rivate` and `/private` are
39
+ * one path, so a path-scoped rule cannot be bypassed by escaping), a trailing
40
+ * root dot is dropped from the hostname, and fragment and userinfo are
41
+ * removed: the fragment never reaches the server, and credentials in the
42
+ * authority must never end up in a saved rule.
43
+ */
44
+ export function canonicalizeWebUrl(parsed: URL): URL {
45
+ parsed.hash = "";
46
+ parsed.username = "";
47
+ parsed.password = "";
48
+
49
+ try {
50
+ parsed.pathname = decodeURI(parsed.pathname);
51
+ } catch {
52
+ // Keep the URL parser's canonical form when decoding fails.
53
+ }
54
+
55
+ if (parsed.hostname.endsWith(".")) {
56
+ parsed.hostname = parsed.hostname.replace(/\.+$/, "");
57
+ }
58
+
59
+ return parsed;
60
+ }
61
+
62
+ /**
63
+ * Parse and canonicalize a web tool's `url` input, or `null` when it is not a
64
+ * URL this system will fetch.
65
+ *
66
+ * Accepts `https://host/path`, `http://…`, bare `host/path`, and `host:port`
67
+ * shorthand. Rejects path-only input and any other scheme (`file:`, `data:`,
68
+ * `javascript:`), so a non-http target can never acquire a web trust rule.
69
+ */
70
+ export function normalizeWebUrl(rawUrl: string): URL | null {
71
+ const trimmed = rawUrl.trim();
72
+ if (!trimmed) {
73
+ return null;
74
+ }
75
+
76
+ if (looksLikeHostPortShorthand(trimmed)) {
77
+ try {
78
+ return canonicalizeWebUrl(new URL(`https://${trimmed}`));
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ try {
85
+ const parsed = new URL(trimmed);
86
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
87
+ return canonicalizeWebUrl(parsed);
88
+ }
89
+ return null;
90
+ } catch {
91
+ // Not an absolute URL; fall through to the shorthand forms.
92
+ }
93
+
94
+ if (looksLikePathOnlyInput(trimmed)) {
95
+ return null;
96
+ }
97
+
98
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
99
+ return null;
100
+ }
101
+
102
+ try {
103
+ return canonicalizeWebUrl(new URL(`https://${trimmed}`));
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
@@ -14,6 +14,7 @@
14
14
  "./remote-web-pairing": "./src/remote-web-pairing.ts",
15
15
  "./twilio-ingress": "./src/twilio-ingress.ts",
16
16
  "./trust-rules": "./src/trust-rules.ts",
17
+ "./url-normalization": "./src/url-normalization.ts",
17
18
  "./handles": "./src/handles.ts",
18
19
  "./rpc": "./src/rpc.ts",
19
20
  "./attachment-naming": "./src/attachment-naming.ts",
@@ -0,0 +1,135 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ canonicalizeWebUrl,
5
+ looksLikeHostPortShorthand,
6
+ looksLikePathOnlyInput,
7
+ normalizeWebUrl,
8
+ } from "../url-normalization.js";
9
+
10
+ /**
11
+ * These are the rules a trust rule is saved and matched under. A change here
12
+ * changes which saved rules still match, so each case states the target the
13
+ * two spellings must agree on.
14
+ */
15
+ describe("normalizeWebUrl", () => {
16
+ test("keeps an ordinary https URL as-is", () => {
17
+ expect(normalizeWebUrl("https://example.com/docs/page")?.href).toBe(
18
+ "https://example.com/docs/page",
19
+ );
20
+ });
21
+
22
+ test("drops the fragment: it never reaches the server", () => {
23
+ expect(normalizeWebUrl("https://example.com/docs#section")?.href).toBe(
24
+ "https://example.com/docs",
25
+ );
26
+ });
27
+
28
+ test("drops userinfo so credentials cannot land in a saved rule", () => {
29
+ const credentialed = new URL("https://example.com/docs/page");
30
+ credentialed.username = "demo";
31
+ credentialed.password = ["c", "r", "e", "d", "1", "2", "3"].join("");
32
+
33
+ const normalized = normalizeWebUrl(credentialed.href);
34
+ expect(normalized?.href).toBe("https://example.com/docs/page");
35
+ expect(normalized?.username).toBe("");
36
+ expect(normalized?.password).toBe("");
37
+ });
38
+
39
+ test("strips a trailing root dot from the hostname", () => {
40
+ expect(normalizeWebUrl("https://example.com./docs/page")?.href).toBe(
41
+ "https://example.com/docs/page",
42
+ );
43
+ });
44
+
45
+ test("decodes escaped path segments so one path has one spelling", () => {
46
+ // Without this, a rule scoped to /private is bypassed by /%70rivate.
47
+ expect(normalizeWebUrl("https://example.com/%70rivate")?.href).toBe(
48
+ "https://example.com/private",
49
+ );
50
+ });
51
+
52
+ test("reads scheme-less input as https", () => {
53
+ expect(normalizeWebUrl("example.com/docs")?.href).toBe(
54
+ "https://example.com/docs",
55
+ );
56
+ });
57
+
58
+ test("reads host:port shorthand as an https origin, not a scheme", () => {
59
+ expect(normalizeWebUrl("example.com:8443/status")?.origin).toBe(
60
+ "https://example.com:8443",
61
+ );
62
+ expect(normalizeWebUrl("[2001:db8::1]:8443/status")?.origin).toBe(
63
+ "https://[2001:db8::1]:8443",
64
+ );
65
+ });
66
+
67
+ test("rejects path-only input rather than coercing it to a host", () => {
68
+ for (const input of ["/etc/passwd", "./rel", "../up", "?q=1", "#frag"]) {
69
+ expect(normalizeWebUrl(input)).toBeNull();
70
+ }
71
+ });
72
+
73
+ test("rejects every non-http scheme", () => {
74
+ for (const input of [
75
+ "file:///etc/passwd",
76
+ "data:text/html,<script>",
77
+ "javascript:alert(1)",
78
+ "ftp://example.com/f",
79
+ ]) {
80
+ expect(normalizeWebUrl(input)).toBeNull();
81
+ }
82
+ });
83
+
84
+ test("rejects empty and whitespace-only input", () => {
85
+ expect(normalizeWebUrl("")).toBeNull();
86
+ expect(normalizeWebUrl(" ")).toBeNull();
87
+ });
88
+
89
+ test("trims surrounding whitespace", () => {
90
+ expect(normalizeWebUrl(" https://example.com/a ")?.href).toBe(
91
+ "https://example.com/a",
92
+ );
93
+ });
94
+
95
+ test("returns null rather than throwing on an unparseable authority", () => {
96
+ expect(normalizeWebUrl("https://")).toBeNull();
97
+ });
98
+ });
99
+
100
+ describe("canonicalizeWebUrl", () => {
101
+ test("keeps the parser's form when the path is not decodable, without throwing", () => {
102
+ // `%zz` and a truncated UTF-8 sequence are not valid escapes; the path
103
+ // must survive unchanged rather than the call throwing.
104
+ expect(canonicalizeWebUrl(new URL("https://example.com/100%zz")).href).toBe(
105
+ "https://example.com/100%zz",
106
+ );
107
+ expect(
108
+ canonicalizeWebUrl(new URL("https://example.com/%E0%A4%A")).href,
109
+ ).toBe("https://example.com/%E0%A4%A");
110
+ });
111
+
112
+ test("decodes an escaped percent, so `%25` and `%` are one path", () => {
113
+ expect(canonicalizeWebUrl(new URL("https://example.com/100%25")).href).toBe(
114
+ "https://example.com/100%",
115
+ );
116
+ });
117
+ });
118
+
119
+ describe("input shape predicates", () => {
120
+ test("host:port shorthand is recognized, scheme-prefixed input is not", () => {
121
+ expect(looksLikeHostPortShorthand("example.com:8443/x")).toBe(true);
122
+ expect(looksLikeHostPortShorthand("[2001:db8::1]:443")).toBe(true);
123
+ expect(looksLikeHostPortShorthand("https://example.com/x")).toBe(false);
124
+ expect(looksLikeHostPortShorthand("example.com/x")).toBe(false);
125
+ });
126
+
127
+ test("path-only input is recognized", () => {
128
+ expect(looksLikePathOnlyInput("/abs")).toBe(true);
129
+ expect(looksLikePathOnlyInput("./rel")).toBe(true);
130
+ expect(looksLikePathOnlyInput("../up")).toBe(true);
131
+ expect(looksLikePathOnlyInput("?q=1")).toBe(true);
132
+ expect(looksLikePathOnlyInput("#frag")).toBe(true);
133
+ expect(looksLikePathOnlyInput("example.com/x")).toBe(false);
134
+ });
135
+ });
@@ -6,6 +6,16 @@
6
6
  * assistant through (Slack, Telegram, WhatsApp, phone, …) plus a couple of
7
7
  * internal ids (`vellum` for native app conversations, `platform` for the
8
8
  * internal control plane). This is the single source of truth for that set:
9
+ *
10
+ * One id, `plugin`, does not name a surface: it names *every* surface a plugin
11
+ * brings. A plugin channel's real identity is the plugin, which is workspace
12
+ * state and cannot be a compile-time union member, so the plugin name travels
13
+ * in `sourceMetadata.plugin` and is prefixed onto every external id the gateway
14
+ * forwards (`imessage:+15551234567`). Two plugins therefore share a channel
15
+ * row — one admission floor, one set of channel-wide defaults — while their
16
+ * conversations, contacts, and trust records stay disjoint. See
17
+ * `gateway/src/channels/plugin-inbound.ts` for what that concedes.
18
+ *
9
19
  * the assistant adopts it wholesale as its `ChannelId`, and the gateway
10
20
  * asserts its own (narrower) inbound list is a subset of it so the two sides
11
21
  * cannot silently drift.
@@ -30,6 +40,7 @@ export const CHANNEL_IDS = [
30
40
  "platform",
31
41
  "a2a",
32
42
  "discord",
43
+ "plugin",
33
44
  ] as const;
34
45
 
35
46
  export type ChannelId = (typeof CHANNEL_IDS)[number];
@@ -29,3 +29,4 @@ export * from "./trust-rules.js";
29
29
  export * from "./ingress.js";
30
30
  export * from "./remote-web-pairing.js";
31
31
  export * from "./twilio-ingress.js";
32
+ export * from "./url-normalization.js";
@@ -11,6 +11,12 @@
11
11
  * (`gateway/src/http/routes/remote-web-pairing-verification.ts`)
12
12
  * - `POST /v1/remote-web/pairing-token` poll + exchange device code
13
13
  * (`gateway/src/http/routes/remote-web-pairing-token.ts`)
14
+ * - `GET /v1/remote-web/pairing-requests` list pending challenges
15
+ * (loopback-only)
16
+ * - `POST /v1/remote-web/pairing-requests/approve` approve by request id
17
+ * (loopback-only)
18
+ * - `POST /v1/remote-web/pairing-requests/deny` deny (delete) by request id
19
+ * (loopback-only)
14
20
  *
15
21
  * These shapes mirror those handlers' request/response bodies exactly so the
16
22
  * gateway, the `vellum pair` CLI (`cli/src/commands/pair.ts`), and the web SPA
@@ -68,6 +74,60 @@ export interface RemoteWebPairingVerificationResponse {
68
74
  expiresAt: string;
69
75
  }
70
76
 
77
+ /**
78
+ * One pending challenge as shown on a host approval surface.
79
+ *
80
+ * The requesting device already sees the plaintext `userCode` in its own
81
+ * challenge response ({@link RemoteWebPairingChallengeResponse.userCode});
82
+ * the loopback-gated list route is the only host-side re-exposure. Displaying
83
+ * it there is what lets the approver match the code against the requesting
84
+ * device's screen: the device-flow anti-phishing binding.
85
+ */
86
+ export interface RemoteWebPairingRequestSummary {
87
+ /** Opaque server-side id used to approve or deny this request. */
88
+ requestId: string;
89
+ /** The human-readable code the requesting device is displaying (e.g. "ABCD-EFGH"). */
90
+ userCode: string;
91
+ /** Public base URL the challenge was minted for. */
92
+ publicBaseUrl: string;
93
+ /** ISO-8601 instant the challenge was minted. */
94
+ requestedAt: string;
95
+ /** ISO-8601 instant the challenge expires. */
96
+ expiresAt: string;
97
+ /**
98
+ * Client IP of the mint request: the loopback/host address when minted
99
+ * locally, or the edge-observed client address when the mint arrived
100
+ * through the nginx tunnel edge (which stamps it via `proxy_set_header`,
101
+ * so a remote client cannot smuggle a value).
102
+ */
103
+ requesterIp: string;
104
+ /** User-Agent header of the mint request, or null when absent. */
105
+ requesterUserAgent: string | null;
106
+ /**
107
+ * Whether the mint arrived through the public tunnel edge rather than the
108
+ * host itself.
109
+ */
110
+ viaEdgeProxy: boolean;
111
+ }
112
+
113
+ /** `GET /v1/remote-web/pairing-requests` success response body (200). */
114
+ export interface RemoteWebPairingRequestListResponse {
115
+ requests: RemoteWebPairingRequestSummary[];
116
+ }
117
+
118
+ /**
119
+ * Request body for the pairing-request approve and deny routes. The approve
120
+ * route's success body reuses {@link RemoteWebPairingVerificationResponse}.
121
+ */
122
+ export interface RemoteWebPairingRequestActionRequest {
123
+ requestId: string;
124
+ }
125
+
126
+ /** `POST /v1/remote-web/pairing-requests/deny` success response body (200). */
127
+ export interface RemoteWebPairingRequestDenyResponse {
128
+ status: "denied";
129
+ }
130
+
71
131
  /** `POST /v1/remote-web/pairing-token` request body. */
72
132
  export interface RemoteWebPairingTokenRequest {
73
133
  /** The `deviceCode` from the challenge. */
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Canonical URL normalization for web tool inputs, shared by the gateway's
3
+ * risk classifiers and the daemon's URL safety checks.
4
+ *
5
+ * A URL a model passes to `web_fetch` / `network_request` reaches more than
6
+ * one consumer, and they must agree on what counts as the same target: the
7
+ * gateway builds the trust-rule ladder from it, and anything that later
8
+ * compares a URL to a saved rule has to fold the same spellings together. Two
9
+ * normalizations would mean two answers, so it lives here rather than in
10
+ * either process.
11
+ *
12
+ * Data-only in spirit: no imports, no config, no I/O.
13
+ */
14
+
15
+ /** Whether a bare `host:port` (or `[v6]:port`) shorthand was written. */
16
+ export function looksLikeHostPortShorthand(value: string): boolean {
17
+ if (/^\[[0-9a-fA-F:.%]+\]:\d+(?:[/?#]|$)/.test(value)) {
18
+ return true;
19
+ }
20
+ return /^[^/?#@\s:]+:\d+(?:[/?#]|$)/.test(value);
21
+ }
22
+
23
+ /** Whether the input is a path, query, or fragment rather than a URL. */
24
+ export function looksLikePathOnlyInput(value: string): boolean {
25
+ return (
26
+ value.startsWith("/") ||
27
+ value.startsWith("./") ||
28
+ value.startsWith("../") ||
29
+ value.startsWith("?") ||
30
+ value.startsWith("#")
31
+ );
32
+ }
33
+
34
+ /**
35
+ * Strip the parts of a URL that must not affect a trust decision, and fold
36
+ * the encodings that would otherwise let one URL wear two spellings.
37
+ *
38
+ * Percent-escaped path segments are decoded (`/%70rivate` and `/private` are
39
+ * one path, so a path-scoped rule cannot be bypassed by escaping), a trailing
40
+ * root dot is dropped from the hostname, and fragment and userinfo are
41
+ * removed: the fragment never reaches the server, and credentials in the
42
+ * authority must never end up in a saved rule.
43
+ */
44
+ export function canonicalizeWebUrl(parsed: URL): URL {
45
+ parsed.hash = "";
46
+ parsed.username = "";
47
+ parsed.password = "";
48
+
49
+ try {
50
+ parsed.pathname = decodeURI(parsed.pathname);
51
+ } catch {
52
+ // Keep the URL parser's canonical form when decoding fails.
53
+ }
54
+
55
+ if (parsed.hostname.endsWith(".")) {
56
+ parsed.hostname = parsed.hostname.replace(/\.+$/, "");
57
+ }
58
+
59
+ return parsed;
60
+ }
61
+
62
+ /**
63
+ * Parse and canonicalize a web tool's `url` input, or `null` when it is not a
64
+ * URL this system will fetch.
65
+ *
66
+ * Accepts `https://host/path`, `http://…`, bare `host/path`, and `host:port`
67
+ * shorthand. Rejects path-only input and any other scheme (`file:`, `data:`,
68
+ * `javascript:`), so a non-http target can never acquire a web trust rule.
69
+ */
70
+ export function normalizeWebUrl(rawUrl: string): URL | null {
71
+ const trimmed = rawUrl.trim();
72
+ if (!trimmed) {
73
+ return null;
74
+ }
75
+
76
+ if (looksLikeHostPortShorthand(trimmed)) {
77
+ try {
78
+ return canonicalizeWebUrl(new URL(`https://${trimmed}`));
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ try {
85
+ const parsed = new URL(trimmed);
86
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
87
+ return canonicalizeWebUrl(parsed);
88
+ }
89
+ return null;
90
+ } catch {
91
+ // Not an absolute URL; fall through to the shorthand forms.
92
+ }
93
+
94
+ if (looksLikePathOnlyInput(trimmed)) {
95
+ return null;
96
+ }
97
+
98
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
99
+ return null;
100
+ }
101
+
102
+ try {
103
+ return canonicalizeWebUrl(new URL(`https://${trimmed}`));
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
@@ -10,6 +10,8 @@
10
10
 
11
11
  import { z } from "zod";
12
12
 
13
+ import type { TrustClass } from "./trust-verdict-contract.js";
14
+
13
15
  /**
14
16
  * Per-channel inbound admission policy — ordered from most-restrictive
15
17
  * (`no_one`, hard kill switch) to most-permissive (`strangers`, admits any
@@ -101,3 +103,35 @@ export function isAdmissionPolicy(value: unknown): value is AdmissionPolicy {
101
103
  (ADMISSION_POLICY_VALUES as readonly string[]).includes(value)
102
104
  );
103
105
  }
106
+
107
+ /**
108
+ * Trust-class ordinal compared against {@link ADMISSION_FLOOR} to make the
109
+ * admission decision. Higher rank = more trusted. Blocked and revoked members
110
+ * never reach this comparison, short-circuiting to deny on member status, so
111
+ * they carry no rank.
112
+ */
113
+ export const TRUST_CLASS_RANK: Record<TrustClass, number> = {
114
+ guardian: 4,
115
+ trusted_contact: 3,
116
+ unverified_contact: 2,
117
+ unknown: 1,
118
+ };
119
+
120
+ /**
121
+ * Whether a sender of this trust class clears a channel's admission floor.
122
+ *
123
+ * The two halves of the check live together because they are meaningless
124
+ * apart: this compares a table keyed by {@link TrustClass} against one keyed
125
+ * by {@link AdmissionPolicy}, and a floor added to one without a rank in the
126
+ * other silently admits or denies everyone.
127
+ *
128
+ * Both enforcement points read this. The runtime's admission stage answers for
129
+ * every channel it receives; the gateway answers for a channel it delivers
130
+ * somewhere other than the runtime, where there is no later stage to ask.
131
+ */
132
+ export function meetsAdmissionFloor(
133
+ policy: AdmissionPolicy,
134
+ trustClass: TrustClass,
135
+ ): boolean {
136
+ return TRUST_CLASS_RANK[trustClass] >= ADMISSION_FLOOR[policy];
137
+ }
@@ -63,11 +63,16 @@ export type TrustRulesListIpcParams = z.infer<
63
63
  typeof TrustRulesListIpcParamsSchema
64
64
  >;
65
65
 
66
+ /** The three risk levels a trust rule can set and a registry default can carry. */
67
+ export const RISK_LEVEL_VALUES = ["low", "medium", "high"] as const;
68
+ export type RiskLevelValue = (typeof RISK_LEVEL_VALUES)[number];
69
+ export const RiskLevelValueSchema = z.enum(RISK_LEVEL_VALUES);
70
+
66
71
  export const TrustRuleSchema = z.object({
67
72
  id: z.string(),
68
73
  tool: z.string(),
69
74
  pattern: z.string(),
70
- risk: z.enum(["low", "medium", "high"]),
75
+ risk: RiskLevelValueSchema,
71
76
  description: z.string(),
72
77
  origin: z.enum(["default", "user_defined"]),
73
78
  userModified: z.boolean(),
@@ -341,3 +346,163 @@ export const GetGuardianContactIpcResponseSchema = z.object({
341
346
  export type GetGuardianContactIpcResponse = z.infer<
342
347
  typeof GetGuardianContactIpcResponseSchema
343
348
  >;
349
+
350
+ // ── classify_risk ────────────────────────────────────────────────────────────
351
+ // Risk classification is gateway-owned; the assistant sends one request per
352
+ // tool invocation and reads the whole answer back. The gateway validates the
353
+ // request against `ClassifyRiskIpcParamsSchema`; the assistant validates the
354
+ // response against `ClassifyRiskIpcResponseSchema` and fails closed on a
355
+ // mismatch.
356
+
357
+ /** A classified risk; `unknown` is the classifier's own "not in registry". */
358
+ export const ClassifiedRiskSchema = z.enum([...RISK_LEVEL_VALUES, "unknown"]);
359
+ export type ClassifiedRisk = z.infer<typeof ClassifiedRiskSchema>;
360
+
361
+ /** How a risk was determined: a user trust rule, the registry, or neither. */
362
+ export const RiskMatchTypeSchema = z.enum(["user_rule", "registry", "unknown"]);
363
+ export type RiskMatchType = z.infer<typeof RiskMatchTypeSchema>;
364
+
365
+ /**
366
+ * File classifier context the assistant pre-resolves (it owns the workspace
367
+ * filesystem) so the gateway can classify file tools without assistant path
368
+ * helpers. Directories are canonicalized (symlinks resolved) by the sender.
369
+ */
370
+ export const ClassifyRiskFileContextSchema = z.object({
371
+ protectedDir: z.string(),
372
+ deprecatedDir: z.string(),
373
+ hooksDir: z.string(),
374
+ pluginsDir: z.string().optional(),
375
+ toolsDir: z.string().optional(),
376
+ routesDir: z.string().optional(),
377
+ workflowsDir: z.string().optional(),
378
+ /** Monitoring data dir: the sentinel lives here, writes are code-injection risk. */
379
+ monitoringDir: z.string().optional(),
380
+ actorTokenSigningKeyPath: z.string(),
381
+ skillSourceDirs: z.array(z.string()),
382
+ });
383
+ export type ClassifyRiskFileContext = z.infer<
384
+ typeof ClassifyRiskFileContextSchema
385
+ >;
386
+
387
+ /** Skill metadata the assistant pre-resolves for skill-load classification. */
388
+ export const ClassifyRiskSkillMetadataSchema = z.object({
389
+ skillId: z.string(),
390
+ selector: z.string(),
391
+ versionHash: z.string(),
392
+ transitiveHash: z.string().optional(),
393
+ hasInlineExpansions: z.boolean(),
394
+ isDynamic: z.boolean(),
395
+ });
396
+ export type ClassifyRiskSkillMetadata = z.infer<
397
+ typeof ClassifyRiskSkillMetadataSchema
398
+ >;
399
+
400
+ export const ClassifyRiskIpcParamsSchema = z.object({
401
+ tool: z.string().min(1),
402
+ command: z.string().optional(),
403
+ url: z.string().optional(),
404
+ path: z.string().optional(),
405
+ /**
406
+ * The file tool's target path with symlinks resolved by the assistant. The
407
+ * gateway's security escalation prefix checks use it so a symlink cannot
408
+ * mask a write into a protected directory; falls back to lexical resolution
409
+ * of `path` when absent.
410
+ */
411
+ resolvedPath: z.string().optional(),
412
+ /**
413
+ * The sandbox file tool's working directory with symlinks resolved. Paired
414
+ * with `resolvedPath` for the workspace-boundary check so a symlinked
415
+ * workspace prefix (macOS `/var` → `/private/var`) does not read as an
416
+ * escape. Absent for host tools.
417
+ */
418
+ resolvedWorkingDir: z.string().optional(),
419
+ skill: z.string().optional(),
420
+ mode: z.string().optional(),
421
+ script: z.string().optional(),
422
+ workingDir: z.string().optional(),
423
+ allowPrivateNetwork: z.boolean().optional(),
424
+ networkMode: z.string().optional(),
425
+ isContainerized: z.boolean().optional(),
426
+ workspaceRoot: z.string().optional(),
427
+ fileContext: ClassifyRiskFileContextSchema.optional(),
428
+ skillMetadata: ClassifyRiskSkillMetadataSchema.optional(),
429
+ /**
430
+ * The tool's registry default risk, for tools with no dedicated classifier.
431
+ * The gateway answers with it (`matchType: "registry"`); absent, the tool is
432
+ * unknown and answers `medium`.
433
+ */
434
+ registryDefaultRisk: RiskLevelValueSchema.optional(),
435
+ /** Number of credential references attached to this tool invocation. */
436
+ credentialRefCount: z.number().int().nonnegative().optional(),
437
+ /**
438
+ * For `host_file_transfer` to_sandbox: the workspace-side destination path
439
+ * and the sandbox working directory it resolves against, so a transfer that
440
+ * lands an executable file in a code-injection sink (tools, routes, hooks,
441
+ * plugins, skills) escalates even though `path` carries the host-side source.
442
+ */
443
+ transferSandboxDestPath: z.string().optional(),
444
+ transferSandboxWorkingDir: z.string().optional(),
445
+ /** `transferSandboxDestPath` with symlinks resolved by the assistant. */
446
+ resolvedTransferDestPath: z.string().optional(),
447
+ });
448
+ export type ClassifyRiskIpcParams = z.infer<typeof ClassifyRiskIpcParamsSchema>;
449
+
450
+ /** Regex ladder shown when the user saves a classification (display only). */
451
+ export const RiskPatternScopeOptionSchema = z.object({
452
+ pattern: z.string(),
453
+ label: z.string(),
454
+ });
455
+ export type RiskPatternScopeOption = z.infer<
456
+ typeof RiskPatternScopeOptionSchema
457
+ >;
458
+
459
+ /** Minimatch ladder for the "always allow" prompt option (what a rule matches). */
460
+ export const RiskAllowlistOptionSchema = z.object({
461
+ label: z.string(),
462
+ description: z.string(),
463
+ pattern: z.string(),
464
+ });
465
+ export type RiskAllowlistOption = z.infer<typeof RiskAllowlistOptionSchema>;
466
+
467
+ /** Directory ladder for filesystem operations; `scope` is a path glob or `everywhere`. */
468
+ export const RiskDirectoryScopeOptionSchema = z.object({
469
+ scope: z.string(),
470
+ label: z.string(),
471
+ });
472
+ export type RiskDirectoryScopeOption = z.infer<
473
+ typeof RiskDirectoryScopeOptionSchema
474
+ >;
475
+
476
+ export const RiskDangerousPatternSchema = z.object({
477
+ type: z.string(),
478
+ description: z.string(),
479
+ text: z.string(),
480
+ });
481
+ export type RiskDangerousPattern = z.infer<typeof RiskDangerousPatternSchema>;
482
+
483
+ export const ClassifyRiskIpcResponseSchema = z.object({
484
+ risk: ClassifiedRiskSchema,
485
+ reason: z.string(),
486
+ matchType: RiskMatchTypeSchema,
487
+ scopeOptions: z.array(RiskPatternScopeOptionSchema),
488
+ allowlistOptions: z.array(RiskAllowlistOptionSchema).optional(),
489
+ actionKeys: z.array(z.string()).optional(),
490
+ commandCandidates: z.array(z.string()).optional(),
491
+ dangerousPatterns: z.array(RiskDangerousPatternSchema).optional(),
492
+ opaqueConstructs: z.boolean().optional(),
493
+ isComplexSyntax: z.boolean().optional(),
494
+ sandboxAutoApprove: z.boolean().optional(),
495
+ /**
496
+ * Lexically-resolved path arguments from sandbox-auto-approve-eligible bash
497
+ * segments. The gateway has no filesystem access; the assistant resolves
498
+ * these through symlinks and revokes `sandboxAutoApprove` if any escapes
499
+ * the workspace.
500
+ */
501
+ sandboxPathArgs: z.array(z.string()).optional(),
502
+ directoryScopeOptions: z.array(RiskDirectoryScopeOptionSchema).optional(),
503
+ /** Fully resolved filesystem path arguments, for directory-scoped rule matching. */
504
+ resolvedPaths: z.array(z.string()).optional(),
505
+ });
506
+ export type ClassifyRiskIpcResponse = z.infer<
507
+ typeof ClassifyRiskIpcResponseSchema
508
+ >;