@qvac/harness 0.4.0 → 0.5.0

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 (299) hide show
  1. package/CHANGELOG.md +689 -141
  2. package/README.md +29 -7
  3. package/dist/harness.bundle +71 -50
  4. package/dist-lib/index.d.ts +36 -39
  5. package/dist-lib/index.js +279 -114
  6. package/dist-lib/lib/audio-transcode-bare.d.ts +2 -2
  7. package/dist-lib/lib/audio-transcode-bare.js +28 -18
  8. package/dist-lib/lib/audio-transcode-node.d.ts +2 -2
  9. package/dist-lib/lib/capabilities/audio-resample.d.ts +7 -6
  10. package/dist-lib/lib/capabilities/audio-resample.js +30 -18
  11. package/dist-lib/lib/capabilities/completion.d.ts +20 -19
  12. package/dist-lib/lib/capabilities/completion.js +193 -132
  13. package/dist-lib/lib/capabilities/download.d.ts +6 -6
  14. package/dist-lib/lib/capabilities/download.js +15 -8
  15. package/dist-lib/lib/capabilities/embed.js +2 -1
  16. package/dist-lib/lib/capabilities/enhance.d.ts +6 -1
  17. package/dist-lib/lib/capabilities/enhance.js +7 -4
  18. package/dist-lib/lib/capabilities/invoke.js +7 -4
  19. package/dist-lib/lib/capabilities/kv-cache.js +2 -1
  20. package/dist-lib/lib/capabilities/preview-model-source.d.ts +9 -7
  21. package/dist-lib/lib/capabilities/preview-model-source.js +80 -28
  22. package/dist-lib/lib/capabilities/speech-text.js +5 -4
  23. package/dist-lib/lib/capabilities/tool-selection.js +10 -9
  24. package/dist-lib/lib/capabilities/tool-turns.js +30 -16
  25. package/dist-lib/lib/capabilities/usage-breakdown.js +2 -1
  26. package/dist-lib/lib/capabilities/voice.d.ts +5 -1
  27. package/dist-lib/lib/capabilities/voice.js +22 -12
  28. package/dist-lib/lib/connect.d.ts +11 -32
  29. package/dist-lib/lib/connect.js +152 -112
  30. package/dist-lib/lib/context-window.js +25 -21
  31. package/dist-lib/lib/contract-types.d.ts +22 -15
  32. package/dist-lib/lib/diagram-repair.d.ts +4 -4
  33. package/dist-lib/lib/diagram-repair.js +6 -3
  34. package/dist-lib/lib/docx/index.d.ts +3 -2
  35. package/dist-lib/lib/{error-fields.d.ts → guards.d.ts} +1 -0
  36. package/dist-lib/lib/{error-fields.js → guards.js} +3 -0
  37. package/dist-lib/lib/hardware/capability-probe.js +20 -10
  38. package/dist-lib/lib/hardware/gpu-classify.js +6 -3
  39. package/dist-lib/lib/hardware/gpu-probe-addon.js +8 -4
  40. package/dist-lib/lib/hardware/gpu-probe.js +4 -2
  41. package/dist-lib/lib/hardware/gpu-stats.native.cjs +8 -9
  42. package/dist-lib/lib/hardware/parsers.js +8 -4
  43. package/dist-lib/lib/helpers/buffer-burst.js +4 -2
  44. package/dist-lib/lib/helpers/control-tokens.js +4 -2
  45. package/dist-lib/lib/helpers/join-guard.d.ts +2 -2
  46. package/dist-lib/lib/helpers/join-guard.js +4 -2
  47. package/dist-lib/lib/helpers/load-stall.js +4 -2
  48. package/dist-lib/lib/helpers/markup-stream-filter.d.ts +2 -2
  49. package/dist-lib/lib/helpers/markup-stream-filter.js +6 -3
  50. package/dist-lib/lib/helpers/race-abort.js +2 -1
  51. package/dist-lib/lib/helpers/race-timeout.d.ts +5 -0
  52. package/dist-lib/lib/helpers/race-timeout.js +14 -0
  53. package/dist-lib/lib/helpers/run-outcome.d.ts +6 -8
  54. package/dist-lib/lib/helpers/run-outcome.js +36 -20
  55. package/dist-lib/lib/helpers/spawn-path.js +6 -3
  56. package/dist-lib/lib/helpers/text-coalescer.js +6 -3
  57. package/dist-lib/lib/helpers/thinking.js +38 -45
  58. package/dist-lib/lib/helpers/tool-call-markup.d.ts +9 -7
  59. package/dist-lib/lib/helpers/tool-call-markup.js +36 -20
  60. package/dist-lib/lib/helpers/tool-intent.d.ts +3 -2
  61. package/dist-lib/lib/helpers/tool-intent.js +4 -2
  62. package/dist-lib/lib/helpers/tool-label.js +18 -9
  63. package/dist-lib/lib/hooks/built-in/arg-repair.js +23 -13
  64. package/dist-lib/lib/hooks/built-in/egress.js +2 -1
  65. package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
  66. package/dist-lib/lib/hooks/pipeline.d.ts +5 -4
  67. package/dist-lib/lib/hooks/pipeline.js +6 -3
  68. package/dist-lib/lib/hooks/run.js +12 -8
  69. package/dist-lib/lib/model-download-progress.js +10 -8
  70. package/dist-lib/lib/models/context.d.ts +1 -1
  71. package/dist-lib/lib/models/context.js +5 -1
  72. package/dist-lib/lib/models/custom-model-cache.d.ts +10 -1
  73. package/dist-lib/lib/models/custom-model-cache.js +43 -15
  74. package/dist-lib/lib/models/gguf-header.d.ts +3 -2
  75. package/dist-lib/lib/models/gguf-header.js +44 -22
  76. package/dist-lib/lib/models/idle-unload.d.ts +14 -0
  77. package/dist-lib/lib/models/idle-unload.js +51 -0
  78. package/dist-lib/lib/models/kv-cache.d.ts +3 -2
  79. package/dist-lib/lib/models/kv-cache.js +4 -3
  80. package/dist-lib/lib/models/llm-presets.d.ts +2 -2
  81. package/dist-lib/lib/models/llm-presets.js +4 -2
  82. package/dist-lib/lib/models/model-catalog-constants.js +3 -2
  83. package/dist-lib/lib/models/model-catalog.d.ts +13 -20
  84. package/dist-lib/lib/models/model-catalog.js +59 -31
  85. package/dist-lib/lib/models/model-delete.js +18 -8
  86. package/dist-lib/lib/models/model-handle.js +2 -1
  87. package/dist-lib/lib/models/model-list.d.ts +8 -3
  88. package/dist-lib/lib/models/model-list.js +11 -6
  89. package/dist-lib/lib/models/model-pool.d.ts +44 -10
  90. package/dist-lib/lib/models/model-pool.js +47 -42
  91. package/dist-lib/lib/models/model-source.js +10 -5
  92. package/dist-lib/lib/models/music-catalog.d.ts +2 -2
  93. package/dist-lib/lib/models/nmt-catalog.d.ts +4 -3
  94. package/dist-lib/lib/models/nmt-catalog.js +10 -5
  95. package/dist-lib/lib/models/opencl-cache.d.ts +6 -1
  96. package/dist-lib/lib/models/opencl-cache.js +5 -3
  97. package/dist-lib/lib/models/sdk-faults.d.ts +11 -8
  98. package/dist-lib/lib/models/sdk-faults.js +9 -6
  99. package/dist-lib/lib/models/shards.d.ts +11 -0
  100. package/dist-lib/lib/models/shards.js +15 -0
  101. package/dist-lib/lib/models/tool-dialect.js +10 -5
  102. package/dist-lib/lib/no-redirect-fetch-bare.d.ts +3 -3
  103. package/dist-lib/lib/no-redirect-fetch-bare.js +1 -1
  104. package/dist-lib/lib/no-redirect-fetch-impl.js +6 -3
  105. package/dist-lib/lib/no-redirect-fetch-node.d.ts +1 -1
  106. package/dist-lib/lib/no-redirect-fetch-node.js +9 -9
  107. package/dist-lib/lib/office/index.d.ts +3 -2
  108. package/dist-lib/lib/office/python.d.ts +3 -2
  109. package/dist-lib/lib/office/python.js +2 -1
  110. package/dist-lib/lib/prompt.d.ts +5 -4
  111. package/dist-lib/lib/prompt.js +8 -4
  112. package/dist-lib/lib/queue.js +10 -5
  113. package/dist-lib/lib/resolution-detector.js +21 -11
  114. package/dist-lib/lib/rpc-session.js +2 -1
  115. package/dist-lib/lib/sdk-loader.js +2 -1
  116. package/dist-lib/lib/sdk-storage.js +4 -2
  117. package/dist-lib/lib/serve.js +110 -66
  118. package/dist-lib/lib/spawn.js +7 -4
  119. package/dist-lib/lib/tmp-path.d.ts +7 -6
  120. package/dist-lib/lib/tool-call-display.d.ts +2 -2
  121. package/dist-lib/lib/tools/create-documents.js +18 -10
  122. package/dist-lib/lib/tools/edit-documents.js +23 -13
  123. package/dist-lib/lib/tools/edit-image.d.ts +2 -2
  124. package/dist-lib/lib/tools/edit-image.js +60 -36
  125. package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +10 -10
  126. package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +17 -11
  127. package/dist-lib/lib/tools/exec/adapters/python/attachments.js +53 -40
  128. package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +2 -2
  129. package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +60 -32
  130. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +33 -22
  131. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +58 -29
  132. package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +2 -2
  133. package/dist-lib/lib/tools/exec/adapters/python/loader.js +23 -12
  134. package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +13 -11
  135. package/dist-lib/lib/tools/exec/adapters/python/memfs.js +10 -5
  136. package/dist-lib/lib/tools/exec/adapters/python/net-shim.js +3 -1
  137. package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +6 -4
  138. package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +20 -10
  139. package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +2 -2
  140. package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +14 -7
  141. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +41 -34
  142. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +316 -166
  143. package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +9 -8
  144. package/dist-lib/lib/tools/exec/adapters/python/run-session.js +56 -28
  145. package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +8 -10
  146. package/dist-lib/lib/tools/exec/adapters/python/wheels.js +22 -11
  147. package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +3 -2
  148. package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +41 -26
  149. package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +27 -18
  150. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -2
  151. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +14 -13
  152. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +4 -4
  153. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +10 -5
  154. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +4 -4
  155. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +2 -1
  156. package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +6 -1
  157. package/dist-lib/lib/tools/exec/obsidian/cli-process.js +8 -5
  158. package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +6 -6
  159. package/dist-lib/lib/tools/exec/obsidian/cli.js +4 -4
  160. package/dist-lib/lib/tools/exec/obsidian/command.d.ts +3 -2
  161. package/dist-lib/lib/tools/exec/obsidian/command.js +6 -3
  162. package/dist-lib/lib/tools/exec/obsidian/output.js +2 -1
  163. package/dist-lib/lib/tools/exec/obsidian/route.d.ts +4 -6
  164. package/dist-lib/lib/tools/exec/obsidian/route.js +17 -11
  165. package/dist-lib/lib/tools/exec/obsidian/safe-path.js +2 -1
  166. package/dist-lib/lib/tools/exec/obsidian/vault-create.js +15 -7
  167. package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +2 -2
  168. package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +20 -10
  169. package/dist-lib/lib/tools/exec/obsidian/vault-read.js +20 -13
  170. package/dist-lib/lib/tools/exec/obsidian/vault-write.js +24 -12
  171. package/dist-lib/lib/tools/exec/runner.d.ts +14 -14
  172. package/dist-lib/lib/tools/exec/runner.js +5 -3
  173. package/dist-lib/lib/tools/exec/scope.js +52 -28
  174. package/dist-lib/lib/tools/exec/shell-argv.js +31 -18
  175. package/dist-lib/lib/tools/exec/shell-guidance.d.ts +6 -1
  176. package/dist-lib/lib/tools/exec/shell-guidance.js +35 -20
  177. package/dist-lib/lib/tools/exec/video-tools.js +2 -2
  178. package/dist-lib/lib/tools/exec.d.ts +6 -8
  179. package/dist-lib/lib/tools/exec.js +113 -54
  180. package/dist-lib/lib/tools/file-glob.d.ts +4 -5
  181. package/dist-lib/lib/tools/file-glob.js +16 -11
  182. package/dist-lib/lib/tools/fs/glob.js +13 -18
  183. package/dist-lib/lib/tools/fs/remote-protocol.d.ts +10 -10
  184. package/dist-lib/lib/tools/fs/remote.d.ts +3 -2
  185. package/dist-lib/lib/tools/fs/remote.js +13 -7
  186. package/dist-lib/lib/tools/fs/scope.d.ts +8 -3
  187. package/dist-lib/lib/tools/fs/scope.js +20 -12
  188. package/dist-lib/lib/tools/fs/walk.js +6 -3
  189. package/dist-lib/lib/tools/fs/write.d.ts +3 -2
  190. package/dist-lib/lib/tools/generate-image.d.ts +11 -9
  191. package/dist-lib/lib/tools/generate-image.js +73 -37
  192. package/dist-lib/lib/tools/generate-music.d.ts +22 -11
  193. package/dist-lib/lib/tools/generate-music.js +79 -48
  194. package/dist-lib/lib/tools/grep.js +66 -41
  195. package/dist-lib/lib/tools/http-request/encode-fields.js +18 -9
  196. package/dist-lib/lib/tools/http-request/{utils.js → request.js} +20 -11
  197. package/dist-lib/lib/tools/http-request/truncate-response.js +57 -46
  198. package/dist-lib/lib/tools/http-request.d.ts +1 -1
  199. package/dist-lib/lib/tools/http-request.js +33 -18
  200. package/dist-lib/lib/tools/mcp/protocol.d.ts +53 -0
  201. package/dist-lib/lib/tools/mcp/{utils.js → protocol.js} +45 -23
  202. package/dist-lib/lib/tools/mcp-call.d.ts +1 -1
  203. package/dist-lib/lib/tools/mcp-call.js +37 -16
  204. package/dist-lib/lib/tools/operations/builders.js +10 -6
  205. package/dist-lib/lib/tools/operations/compile.js +20 -10
  206. package/dist-lib/lib/tools/operations/decoders.js +12 -6
  207. package/dist-lib/lib/tools/operations/gmail-message.js +3 -2
  208. package/dist-lib/lib/tools/outbound-guard.d.ts +4 -3
  209. package/dist-lib/lib/tools/outbound-guard.js +14 -8
  210. package/dist-lib/lib/tools/private-ip.js +8 -7
  211. package/dist-lib/lib/tools/rag-search.js +14 -7
  212. package/dist-lib/lib/tools/read-files.js +23 -20
  213. package/dist-lib/lib/tools/skill.js +19 -10
  214. package/dist-lib/lib/tools/skills/auth-resolver.d.ts +11 -18
  215. package/dist-lib/lib/tools/skills/auth-resolver.js +30 -18
  216. package/dist-lib/lib/tools/skills/bin-check.d.ts +3 -2
  217. package/dist-lib/lib/tools/skills/bin-check.js +10 -3
  218. package/dist-lib/lib/tools/skills/bundled-dir.js +20 -10
  219. package/dist-lib/lib/tools/skills/bundled-hash.d.ts +3 -2
  220. package/dist-lib/lib/tools/skills/bundled-hash.js +6 -1
  221. package/dist-lib/lib/tools/skills/catalog.d.ts +3 -2
  222. package/dist-lib/lib/tools/skills/catalog.js +33 -28
  223. package/dist-lib/lib/tools/skills/cli-argv.d.ts +3 -2
  224. package/dist-lib/lib/tools/skills/cli-argv.js +55 -52
  225. package/dist-lib/lib/tools/skills/cli-schema-check.js +21 -10
  226. package/dist-lib/lib/tools/skills/cli-schema-load.js +28 -20
  227. package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +6 -6
  228. package/dist-lib/lib/tools/skills/cli-schema.js +2 -1
  229. package/dist-lib/lib/tools/skills/credential-check.d.ts +6 -3
  230. package/dist-lib/lib/tools/skills/credential-check.js +24 -12
  231. package/dist-lib/lib/tools/skills/credential-grants.js +1 -6
  232. package/dist-lib/lib/tools/skills/credential-health.d.ts +5 -1
  233. package/dist-lib/lib/tools/skills/credential-health.js +5 -3
  234. package/dist-lib/lib/tools/skills/frontmatter-parser.js +10 -5
  235. package/dist-lib/lib/tools/skills/manifest-parse.js +84 -64
  236. package/dist-lib/lib/tools/skills/mention.js +7 -6
  237. package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +2 -2
  238. package/dist-lib/lib/tools/skills/obsidian-readiness.js +11 -3
  239. package/dist-lib/lib/tools/skills/overlay-crud.d.ts +6 -4
  240. package/dist-lib/lib/tools/skills/overlay-crud.js +12 -6
  241. package/dist-lib/lib/tools/skills/overlay-dir.js +4 -2
  242. package/dist-lib/lib/tools/skills/overlay-watch.d.ts +3 -2
  243. package/dist-lib/lib/tools/skills/overlay-watch.js +8 -4
  244. package/dist-lib/lib/tools/skills/setup-routes.js +1 -3
  245. package/dist-lib/lib/tools/skills/skill-settings.js +4 -1
  246. package/dist-lib/lib/tools/skills/skill-state.d.ts +6 -11
  247. package/dist-lib/lib/tools/skills/skill-state.js +48 -29
  248. package/dist-lib/lib/tools/skills/tool-grants.js +6 -3
  249. package/dist-lib/lib/tools/toolsets.js +2 -1
  250. package/dist-lib/lib/tools/translate.d.ts +4 -5
  251. package/dist-lib/lib/tools/translate.js +31 -14
  252. package/dist-lib/lib/tools/url-guard.js +2 -1
  253. package/dist-lib/lib/tools/weather-lookup/place.js +8 -4
  254. package/dist-lib/lib/tools/weather-lookup/report.d.ts +7 -1
  255. package/dist-lib/lib/tools/weather-lookup/report.js +46 -9
  256. package/dist-lib/lib/tools/weather-lookup.js +18 -12
  257. package/dist-lib/lib/tools/web-fetch.js +47 -24
  258. package/dist-lib/lib/tools/web-search.js +8 -3
  259. package/dist-lib/lib/validating-lookup.d.ts +4 -4
  260. package/dist-lib/lib/wav-encode.d.ts +1 -1
  261. package/dist-lib/lib/wav-encode.js +11 -10
  262. package/dist-lib/lib/web-fetch/embedded-state.js +24 -12
  263. package/dist-lib/lib/web-fetch/extract.js +4 -2
  264. package/dist-lib/lib/web-fetch/images.js +8 -4
  265. package/dist-lib/lib/web-fetch/video.d.ts +2 -2
  266. package/dist-lib/lib/web-fetch/video.js +8 -4
  267. package/dist-lib/lib/web-search/engine-registry.d.ts +2 -2
  268. package/dist-lib/lib/web-search/engines/aol.js +1 -1
  269. package/dist-lib/lib/web-search/engines/bing.js +1 -1
  270. package/dist-lib/lib/web-search/engines/brave.js +4 -1
  271. package/dist-lib/lib/web-search/engines/duckduckgo.js +3 -2
  272. package/dist-lib/lib/web-search/engines/google.js +7 -3
  273. package/dist-lib/lib/web-search/engines/wikipedia.js +5 -4
  274. package/dist-lib/lib/web-search/engines/yahoo.js +1 -1
  275. package/dist-lib/lib/web-search/fetch-html.d.ts +5 -4
  276. package/dist-lib/lib/web-search/fetch-html.js +13 -7
  277. package/dist-lib/lib/web-search/orchestrator.d.ts +9 -5
  278. package/dist-lib/lib/web-search/orchestrator.js +3 -3
  279. package/dist-lib/lib/wire.d.ts +3 -2
  280. package/dist-lib/lib/wire.js +23 -15
  281. package/dist-lib/schema/baseline.js +10 -5
  282. package/dist-lib/schema/build.js +2 -1
  283. package/dist-lib/schema/harness.d.ts +15 -2
  284. package/dist-lib/schema/harness.js +14 -2
  285. package/dist-lib/schema/release-tags.d.ts +3 -2
  286. package/dist-lib/schema/release-tags.js +6 -3
  287. package/dist-lib/spec/bind.js +3 -0
  288. package/dist-lib/spec/calls.js +1 -0
  289. package/dist-lib/spec/hrpc/hrpc.json +13 -0
  290. package/dist-lib/spec/hrpc/index.js +15 -3
  291. package/dist-lib/spec/hrpc/messages.js +39 -8
  292. package/dist-lib/spec/hyperschema/index.js +39 -8
  293. package/dist-lib/spec/hyperschema/schema.json +30 -1
  294. package/dist-lib/spec/hyperschema/types.d.ts +9 -1
  295. package/package.json +13 -14
  296. package/worker/harness-sidecar.mjs +15 -22
  297. package/worker/stow-entry.mjs +7 -4
  298. package/dist-lib/lib/tools/mcp/utils.d.ts +0 -47
  299. /package/dist-lib/lib/tools/http-request/{utils.d.ts → request.d.ts} +0 -0
@@ -1,4 +1,5 @@
1
- import { MAX_RESPONSE_CHARS } from './utils.js';
1
+ import { MAX_RESPONSE_CHARS } from './request.js';
2
+ import { isRecord } from '../../guards.js';
2
3
  export function truncateResponseBody(body) {
3
4
  if (body.length <= MAX_RESPONSE_CHARS)
4
5
  return body;
@@ -18,63 +19,73 @@ const MARKER_CHARS = 64;
18
19
  function shrinkable(value) {
19
20
  if (typeof value === 'string')
20
21
  return true;
21
- return typeof value === 'object' && value !== null;
22
+ return isRecord(value);
22
23
  }
23
24
  export const ELISION = '… [truncated]';
24
25
  function fitString(value, budget) {
25
26
  const room = budget - ELISION.length - 2;
26
- if (room <= 0)
27
+ if (room <= 0) {
27
28
  return ELISION;
29
+ }
28
30
  return `${value.slice(0, room)}${ELISION}`;
29
31
  }
30
- function fit(value, budget) {
31
- const room = budget - MARKER_CHARS;
32
- if (Array.isArray(value)) {
33
- const kept = [];
34
- let used = 2;
35
- for (const item of value) {
36
- const size = JSON.stringify(item).length + 1;
37
- if (used + size > room) {
38
- const left = room - used;
39
- if (left > MARKER_CHARS && shrinkable(item))
40
- kept.push(fit(item, left));
41
- break;
32
+ function fitArray(value, room) {
33
+ const kept = [];
34
+ let used = 2;
35
+ for (const item of value) {
36
+ const size = JSON.stringify(item).length + 1;
37
+ if (used + size > room) {
38
+ const left = room - used;
39
+ if (left > MARKER_CHARS && shrinkable(item)) {
40
+ kept.push(fit(item, left));
42
41
  }
43
- kept.push(item);
44
- used += size;
42
+ break;
45
43
  }
46
- const dropped = value.length - kept.length;
47
- if (dropped > 0)
48
- kept.push(`… [${dropped} more items truncated]`);
49
- return kept;
44
+ kept.push(item);
45
+ used += size;
50
46
  }
51
- if (typeof value === 'object' && value !== null) {
52
- const entries = Object.entries(value);
53
- const out = {};
54
- let used = 2;
55
- let kept = 0;
56
- let refitted = false;
57
- for (const [key, item] of entries) {
58
- const keySize = JSON.stringify(key).length + 2;
59
- const size = keySize + JSON.stringify(item).length;
60
- if (used + size > room) {
61
- const left = room - used - keySize;
62
- if (!refitted && left > MARKER_CHARS && shrinkable(item)) {
63
- const shrunk = fit(item, left);
64
- out[key] = shrunk;
65
- used += keySize + JSON.stringify(shrunk).length;
66
- refitted = true;
67
- kept++;
68
- }
69
- continue;
47
+ const dropped = value.length - kept.length;
48
+ if (dropped > 0) {
49
+ kept.push(`… [${dropped} more items truncated]`);
50
+ }
51
+ return kept;
52
+ }
53
+ function fitObject(value, room) {
54
+ const entries = Object.entries(value);
55
+ const out = {};
56
+ let used = 2;
57
+ let kept = 0;
58
+ let refitted = false;
59
+ for (const [key, item] of entries) {
60
+ const keySize = JSON.stringify(key).length + 2;
61
+ const size = keySize + JSON.stringify(item).length;
62
+ if (used + size > room) {
63
+ const left = room - used - keySize;
64
+ if (!refitted && left > MARKER_CHARS && shrinkable(item)) {
65
+ const shrunk = fit(item, left);
66
+ out[key] = shrunk;
67
+ used += keySize + JSON.stringify(shrunk).length;
68
+ refitted = true;
69
+ kept++;
70
70
  }
71
- out[key] = item;
72
- used += size;
73
- kept++;
71
+ continue;
74
72
  }
75
- if (kept < entries.length)
76
- out['…'] = `[${entries.length - kept} more fields truncated]`;
77
- return out;
73
+ out[key] = item;
74
+ used += size;
75
+ kept++;
76
+ }
77
+ if (kept < entries.length) {
78
+ out['…'] = `[${entries.length - kept} more fields truncated]`;
79
+ }
80
+ return out;
81
+ }
82
+ function fit(value, budget) {
83
+ const room = budget - MARKER_CHARS;
84
+ if (Array.isArray(value)) {
85
+ return fitArray(value, room);
86
+ }
87
+ if (isRecord(value)) {
88
+ return fitObject(value, room);
78
89
  }
79
90
  if (typeof value === 'string' && JSON.stringify(value).length > budget) {
80
91
  return fitString(value, budget);
@@ -1,5 +1,5 @@
1
1
  import type { ToolDefinition } from '../capabilities/completion.ts';
2
- import { type FetchInit } from './http-request/utils.ts';
2
+ import { type FetchInit } from './http-request/request.ts';
3
3
  import { type CredentialAllowListFn, type CredentialStoreFn } from './skills/auth-resolver.ts';
4
4
  import { type HostResolver } from './url-guard.ts';
5
5
  export type FetchFn = (url: URL, init: FetchInit) => Promise<{
@@ -1,11 +1,11 @@
1
- import defaultFetch from '#no-redirect-fetch';
1
+ import { noRedirectFetch } from '#no-redirect-fetch';
2
2
  import { AbortController } from 'bare-abort-controller';
3
3
  import { lookupHost } from '../dns-lookup.js';
4
4
  import { READ_METHODS } from './http-methods.js';
5
5
  import { encodeDeclaredFields } from './http-request/encode-fields.js';
6
6
  import { truncateResponseBody } from './http-request/truncate-response.js';
7
7
  import { AbortedError } from '../helpers/race-abort.js';
8
- import { applyQuery, buildHeaders, contentTypeOf, encodeBody, isRedirectStatus, parseJsonBody, parseObjectArg, TIMEOUT_MS } from './http-request/utils.js';
8
+ import { applyQuery, buildHeaders, contentTypeOf, encodeBody, isRedirectStatus, parseJsonBody, parseObjectArg, TIMEOUT_MS } from './http-request/request.js';
9
9
  import { parseAuth, resolveBearerHeader, resolveRedirectBearerHeader } from './skills/auth-resolver.js';
10
10
  import { recordCredentialHealth } from './skills/credential-health.js';
11
11
  import { resolveOutboundUrl } from './url-guard.js';
@@ -47,8 +47,9 @@ export function createHttpRequestTool({ fetch: fetchImpl, credentialStore, crede
47
47
  autoApprove: false,
48
48
  effect(args) {
49
49
  const method = typeof args.method === 'string' ? args.method.toUpperCase() : undefined;
50
- if (method === undefined || !READ_METHODS.has(method))
50
+ if (method === undefined || !READ_METHODS.has(method)) {
51
51
  return 'write';
52
+ }
52
53
  return args.body === undefined && args.encode === undefined ? 'read' : 'write';
53
54
  },
54
55
  resource(args) {
@@ -72,39 +73,47 @@ export function createHttpRequestTool({ fetch: fetchImpl, credentialStore, crede
72
73
  return { error: `unsupported protocol: ${url.protocol}` };
73
74
  }
74
75
  const query = parseObjectArg(args.query, 'query');
75
- if (!query.ok)
76
+ if (!query.ok) {
76
77
  return { error: query.error };
78
+ }
77
79
  const encodeArgs = parseObjectArg(args.encode, 'encode');
78
- if (!encodeArgs.ok)
80
+ if (!encodeArgs.ok) {
79
81
  return { error: encodeArgs.error };
82
+ }
80
83
  const headerArgs = parseObjectArg(args.headers, 'headers');
81
- if (!headerArgs.ok)
84
+ if (!headerArgs.ok) {
82
85
  return { error: headerArgs.error };
86
+ }
83
87
  const headers = headerArgs.value ? buildHeaders(headerArgs.value) : {};
84
88
  const parsedBody = parseJsonBody(args.body, contentTypeOf(headers));
85
- if (!parsedBody.ok)
89
+ if (!parsedBody.ok) {
86
90
  return { error: parsedBody.error };
87
- if (query.value)
91
+ }
92
+ if (query.value) {
88
93
  applyQuery(url, query.value);
94
+ }
89
95
  const auth = parseAuth(args.auth);
90
- if (!auth.ok)
96
+ if (!auth.ok) {
91
97
  return { error: auth.error };
98
+ }
92
99
  const allowList = credentialAllowList?.(ctx);
93
100
  const controller = new AbortController();
94
101
  const timeout = setTimeout(() => controller.abort(undefined), TIMEOUT_MS);
95
102
  try {
96
103
  const initialCheck = await resolveOutboundUrl(url, resolveHost, controller.signal);
97
- if (initialCheck.blocked)
104
+ if (initialCheck.blocked) {
98
105
  return { error: initialCheck.blocked };
106
+ }
99
107
  const bearer = await resolveBearerHeader(auth.value, url, credentialStore, allowList, ctx, controller.signal);
100
- if ('error' in bearer)
108
+ if ('error' in bearer) {
101
109
  return { error: bearer.error };
110
+ }
102
111
  const redirectAuth = bearer.credentialKey
103
112
  ? { type: 'bearer', tokenCredentialKey: bearer.credentialKey }
104
113
  : undefined;
105
114
  const encoded = encodeDeclaredFields(parsedBody.value, encodeArgs.value);
106
115
  const requestBody = encodeBody(encoded, headers);
107
- const fetch = fetchImpl ?? defaultFetch;
116
+ const fetch = fetchImpl ?? noRedirectFetch;
108
117
  let currentUrl = url;
109
118
  let method = args.method ?? 'GET';
110
119
  let body = requestBody;
@@ -115,8 +124,9 @@ export function createHttpRequestTool({ fetch: fetchImpl, credentialStore, crede
115
124
  for (let redirects = 0;; redirects++) {
116
125
  const hopHeaders = { ...headers };
117
126
  delete hopHeaders['Authorization'];
118
- if (header)
127
+ if (header) {
119
128
  hopHeaders['Authorization'] = header;
129
+ }
120
130
  response = await fetch(currentUrl, {
121
131
  method,
122
132
  headers: hopHeaders,
@@ -128,11 +138,13 @@ export function createHttpRequestTool({ fetch: fetchImpl, credentialStore, crede
128
138
  if (credentialKey) {
129
139
  recordCredentialHealth(credentialKey, response.status, ctx?.agentId);
130
140
  }
131
- if (!isRedirectStatus(response.status))
141
+ if (!isRedirectStatus(response.status)) {
132
142
  break;
143
+ }
133
144
  const location = response.headers?.get('location');
134
- if (!location)
145
+ if (!location) {
135
146
  break;
147
+ }
136
148
  let nextUrl;
137
149
  try {
138
150
  nextUrl = new URL(location, currentUrl);
@@ -140,13 +152,16 @@ export function createHttpRequestTool({ fetch: fetchImpl, credentialStore, crede
140
152
  catch {
141
153
  break;
142
154
  }
143
- if (nextUrl.protocol !== 'http:' && nextUrl.protocol !== 'https:')
155
+ if (nextUrl.protocol !== 'http:' && nextUrl.protocol !== 'https:') {
144
156
  break;
145
- if (redirects >= MAX_REDIRECTS)
157
+ }
158
+ if (redirects >= MAX_REDIRECTS) {
146
159
  return { error: 'too many redirects' };
160
+ }
147
161
  const hopCheck = await resolveOutboundUrl(nextUrl, resolveHost, controller.signal);
148
- if (hopCheck.blocked)
162
+ if (hopCheck.blocked) {
149
163
  return { error: hopCheck.blocked };
164
+ }
150
165
  if (response.status === 303 ||
151
166
  ((response.status === 301 || response.status === 302) && method === 'POST')) {
152
167
  method = 'GET';
@@ -0,0 +1,53 @@
1
+ import type { AbortSignal as BareAbortSignal } from 'bare-abort-controller';
2
+ import type { ToolExecuteContext } from '../../capabilities/completion.ts';
3
+ import { type FetchInit } from '../http-request/request.ts';
4
+ export declare const MCP_PROTOCOL_VERSION = "2025-03-26";
5
+ export declare const TIMEOUT_MS = 30000;
6
+ export interface McpHeaders {
7
+ get(name: string): string | null;
8
+ }
9
+ export interface McpResponseBody {
10
+ headers: McpHeaders;
11
+ text(): Promise<string> | string;
12
+ }
13
+ export interface McpResponse extends McpResponseBody {
14
+ status: number;
15
+ statusText?: string;
16
+ }
17
+ export type McpFetchFn = (url: URL, init: FetchInit) => Promise<McpResponse>;
18
+ export declare function isNotification(method: string): boolean;
19
+ export declare function isMetadataMethod(method: unknown): boolean;
20
+ export interface McpRequest {
21
+ method: string;
22
+ params: Record<string, unknown> | undefined;
23
+ }
24
+ export declare function normalizeMethod(method: string, params: Record<string, unknown> | undefined): McpRequest;
25
+ export interface McpError {
26
+ error: string;
27
+ }
28
+ export interface McpBody {
29
+ body: string;
30
+ }
31
+ export declare function flattenMcpResponse(body: string): McpError | McpBody;
32
+ export declare function sessionKey(url: string, ctx: ToolExecuteContext | undefined, credentialKey?: string): string;
33
+ export declare function getSession(key: string): string;
34
+ export declare function deleteSession(key: string): void;
35
+ export declare function cacheSession(key: string, sessionId: string): void;
36
+ export interface McpPostOptions {
37
+ readonly method: string;
38
+ readonly params: Record<string, unknown> | undefined;
39
+ readonly authHeader: string | null;
40
+ readonly sessionId: string | undefined;
41
+ readonly signal: BareAbortSignal;
42
+ }
43
+ export declare function post(fetch: McpFetchFn, url: URL, { method, params, authHeader, sessionId, signal }: McpPostOptions): ReturnType<McpFetchFn>;
44
+ export declare function readBody(response: McpResponseBody): Promise<string>;
45
+ export declare function needsSession(status: number, cached: string): boolean;
46
+ export interface McpSessionOptions {
47
+ readonly authHeader: string | null;
48
+ readonly signal: BareAbortSignal;
49
+ }
50
+ export interface McpSessionError extends McpError {
51
+ stateless?: true;
52
+ }
53
+ export declare function establishSession(fetch: McpFetchFn, url: URL, { authHeader, signal }: McpSessionOptions): Promise<string | McpSessionError>;
@@ -1,4 +1,5 @@
1
- import { MAX_RESPONSE_CHARS } from '../http-request/utils.js';
1
+ import { isRecord } from '../../guards.js';
2
+ import { MAX_RESPONSE_CHARS } from '../http-request/request.js';
2
3
  export const MCP_PROTOCOL_VERSION = '2025-03-26';
3
4
  export const TIMEOUT_MS = 30_000;
4
5
  const METADATA_METHODS = new Set([
@@ -24,17 +25,20 @@ export function normalizeMethod(method, params) {
24
25
  const p = params ?? {};
25
26
  return { method, params: p.arguments === undefined ? { ...p, arguments: {} } : p };
26
27
  }
27
- if (PROTOCOL_METHODS.has(method) || isNotification(method))
28
+ if (PROTOCOL_METHODS.has(method) || isNotification(method)) {
28
29
  return { method, params };
30
+ }
29
31
  return { method: 'tools/call', params: { name: method, arguments: params ?? {} } };
30
32
  }
31
33
  let nextId = 1;
32
34
  function buildEnvelope(method, params) {
33
35
  const envelope = { jsonrpc: '2.0', method };
34
- if (params !== undefined)
36
+ if (params !== undefined) {
35
37
  envelope.params = params;
36
- if (!isNotification(method))
38
+ }
39
+ if (!isNotification(method)) {
37
40
  envelope.id = String(nextId++);
41
+ }
38
42
  return JSON.stringify(envelope);
39
43
  }
40
44
  function headers(authHeader, sessionId) {
@@ -42,10 +46,12 @@ function headers(authHeader, sessionId) {
42
46
  'content-type': 'application/json',
43
47
  accept: 'application/json, text/event-stream'
44
48
  };
45
- if (authHeader)
49
+ if (authHeader) {
46
50
  h.authorization = authHeader;
47
- if (sessionId)
51
+ }
52
+ if (sessionId) {
48
53
  h['mcp-session-id'] = sessionId;
54
+ }
49
55
  return h;
50
56
  }
51
57
  function eventData(event) {
@@ -75,14 +81,13 @@ function decodePayload(body, contentType) {
75
81
  }
76
82
  }
77
83
  function asRecord(value) {
78
- return value && typeof value === 'object' && !Array.isArray(value)
79
- ? value
80
- : null;
84
+ return isRecord(value) && !Array.isArray(value) ? value : null;
81
85
  }
82
86
  function contentText(result) {
83
87
  const content = result?.content;
84
- if (!Array.isArray(content))
88
+ if (!Array.isArray(content)) {
85
89
  return null;
90
+ }
86
91
  const texts = content.flatMap((entry) => {
87
92
  const item = asRecord(entry);
88
93
  return item?.type === 'text' && typeof item.text === 'string' ? [item.text] : [];
@@ -91,8 +96,9 @@ function contentText(result) {
91
96
  }
92
97
  function rpcErrorText(error) {
93
98
  const message = typeof error.message === 'string' ? error.message.trim() : '';
94
- if (error.data === undefined)
99
+ if (error.data === undefined) {
95
100
  return message;
101
+ }
96
102
  return `${message} ${JSON.stringify(error.data)}`.trim();
97
103
  }
98
104
  export function flattenMcpResponse(body) {
@@ -104,15 +110,18 @@ export function flattenMcpResponse(body) {
104
110
  return { body: truncate(body) };
105
111
  }
106
112
  const record = asRecord(parsed);
107
- if (!record)
113
+ if (!record) {
108
114
  return { body: truncate(body) };
115
+ }
109
116
  const rpcError = asRecord(record.error);
110
- if (rpcError)
117
+ if (rpcError) {
111
118
  return { error: truncate(rpcErrorText(rpcError) || body.trim()) };
119
+ }
112
120
  const result = asRecord(record.result);
113
121
  const text = contentText(result)?.trim();
114
- if (result?.isError === true)
122
+ if (result?.isError === true) {
115
123
  return { error: truncate(text || body.trim()) };
124
+ }
116
125
  return { body: truncate(text || body) };
117
126
  }
118
127
  function truncate(text) {
@@ -135,11 +144,12 @@ export function cacheSession(key, sessionId) {
135
144
  SESSION_CACHE.set(key, sessionId);
136
145
  if (SESSION_CACHE.size > SESSION_CACHE_MAX) {
137
146
  const oldest = SESSION_CACHE.keys().next().value;
138
- if (oldest !== undefined)
147
+ if (oldest !== undefined) {
139
148
  SESSION_CACHE.delete(oldest);
149
+ }
140
150
  }
141
151
  }
142
- export function post(fetch, url, method, params, authHeader, sessionId, signal) {
152
+ export function post(fetch, url, { method, params, authHeader, sessionId, signal }) {
143
153
  return fetch(url, {
144
154
  method: 'POST',
145
155
  headers: headers(authHeader, sessionId),
@@ -154,12 +164,18 @@ export async function readBody(response) {
154
164
  export function needsSession(status, cached) {
155
165
  return status === 400 || (status === 404 && cached !== '');
156
166
  }
157
- export async function establishSession(fetch, url, authHeader, signal) {
158
- const init = await post(fetch, url, 'initialize', {
159
- protocolVersion: MCP_PROTOCOL_VERSION,
160
- capabilities: {},
161
- clientInfo: { name: 'QV.AC', version: '0.8.x' }
162
- }, authHeader, undefined, signal);
167
+ export async function establishSession(fetch, url, { authHeader, signal }) {
168
+ const init = await post(fetch, url, {
169
+ method: 'initialize',
170
+ params: {
171
+ protocolVersion: MCP_PROTOCOL_VERSION,
172
+ capabilities: {},
173
+ clientInfo: { name: 'QV.AC', version: '0.8.x' }
174
+ },
175
+ authHeader,
176
+ sessionId: undefined,
177
+ signal
178
+ });
163
179
  const sessionId = init.headers.get('mcp-session-id');
164
180
  if (init.status < 200 || init.status >= 300) {
165
181
  return { error: `initialize failed: ${init.status}\n${await readBody(init)}` };
@@ -167,6 +183,12 @@ export async function establishSession(fetch, url, authHeader, signal) {
167
183
  if (!sessionId) {
168
184
  return { error: 'initialize returned no Mcp-Session-Id header', stateless: true };
169
185
  }
170
- await post(fetch, url, 'notifications/initialized', undefined, authHeader, sessionId, signal);
186
+ await post(fetch, url, {
187
+ method: 'notifications/initialized',
188
+ params: undefined,
189
+ authHeader,
190
+ sessionId,
191
+ signal
192
+ });
171
193
  return sessionId;
172
194
  }
@@ -1,5 +1,5 @@
1
1
  import type { ToolDefinition } from '../capabilities/completion.ts';
2
- import { type McpFetchFn } from './mcp/utils.ts';
2
+ import { type McpFetchFn } from './mcp/protocol.ts';
3
3
  import { type CredentialAllowListFn, type CredentialStoreFn } from './skills/auth-resolver.ts';
4
4
  import { type HostResolver } from './url-guard.ts';
5
5
  export type { McpFetchFn };
@@ -1,10 +1,10 @@
1
- import defaultFetch from '#no-redirect-fetch';
1
+ import { noRedirectFetch } from '#no-redirect-fetch';
2
2
  import { AbortController } from 'bare-abort-controller';
3
3
  import { MCP_CALL_TOOL_NAME } from '../capabilities/tool-names.js';
4
4
  import { lookupHost } from '../dns-lookup.js';
5
5
  import { AbortedError } from '../helpers/race-abort.js';
6
- import { isRedirectStatus, parseObjectArg } from './http-request/utils.js';
7
- import { cacheSession, deleteSession, establishSession, flattenMcpResponse, getSession, isNotification, needsSession, normalizeMethod, post, readBody, sessionKey, TIMEOUT_MS } from './mcp/utils.js';
6
+ import { isRedirectStatus, parseObjectArg } from './http-request/request.js';
7
+ import { cacheSession, deleteSession, establishSession, flattenMcpResponse, getSession, isNotification, needsSession, normalizeMethod, post, readBody, sessionKey, TIMEOUT_MS } from './mcp/protocol.js';
8
8
  import { parseAuth, resolveBearerHeader } from './skills/auth-resolver.js';
9
9
  import { recordCredentialHealth } from './skills/credential-health.js';
10
10
  import { resolveOutboundUrl } from './url-guard.js';
@@ -13,8 +13,9 @@ function nestedUrl(params) {
13
13
  }
14
14
  function parseMcpParams(value) {
15
15
  const parsed = parseObjectArg(value, 'params');
16
- if (parsed.ok || typeof value !== 'string')
16
+ if (parsed.ok || typeof value !== 'string') {
17
17
  return parsed;
18
+ }
18
19
  return parseObjectArg(value.replace(/[“”]/g, '"'), 'params');
19
20
  }
20
21
  function toolParams(params, liftedNestedUrl) {
@@ -74,11 +75,11 @@ export function createMcpCallTool({ fetch: fetchImpl, credentialStore, credentia
74
75
  return null;
75
76
  }
76
77
  },
77
- async execute(rawArgs, ctx) {
78
- const args = rawArgs;
78
+ async execute(args, ctx) {
79
79
  const rawParams = parseMcpParams(args.params);
80
- if (!rawParams.ok)
80
+ if (!rawParams.ok) {
81
81
  return { error: rawParams.error };
82
+ }
82
83
  const topLevelUrl = typeof args.url === 'string' && args.url.length > 0 ? args.url : undefined;
83
84
  const urlValue = topLevelUrl ?? nestedUrl(rawParams.value);
84
85
  let url;
@@ -95,30 +96,48 @@ export function createMcpCallTool({ fetch: fetchImpl, credentialStore, credentia
95
96
  return { error: 'method must be a non-empty string' };
96
97
  }
97
98
  const parsedAuth = parseAuth(args.auth);
98
- if (!parsedAuth.ok)
99
+ if (!parsedAuth.ok) {
99
100
  return { error: parsedAuth.error };
101
+ }
100
102
  const { method, params } = normalizeMethod(args.method, toolParams(rawParams.value, topLevelUrl === undefined && urlValue !== undefined));
101
103
  const controller = new AbortController();
102
104
  const timeout = setTimeout(() => controller.abort(undefined), TIMEOUT_MS);
103
105
  try {
104
106
  const urlCheck = await resolveOutboundUrl(url, resolveHost, controller.signal);
105
- if (urlCheck.blocked)
107
+ if (urlCheck.blocked) {
106
108
  return { error: urlCheck.blocked };
109
+ }
107
110
  const bearer = await resolveBearerHeader(parsedAuth.value, url, credentialStore, credentialAllowList?.(ctx), ctx, controller.signal);
108
- if ('error' in bearer)
111
+ if ('error' in bearer) {
109
112
  return { error: bearer.error };
113
+ }
110
114
  const authHeader = bearer.header;
111
- const fetch = fetchImpl ?? defaultFetch;
115
+ const fetch = fetchImpl ?? noRedirectFetch;
112
116
  const guardedFetch = (target, init) => fetch(target, { ...init, validatedAddresses: urlCheck.addresses });
113
117
  const key = sessionKey(url.href, ctx, bearer.credentialKey);
114
118
  const cached = getSession(key);
115
- let response = await post(guardedFetch, url, method, params, authHeader, cached || undefined, controller.signal);
119
+ let response = await post(guardedFetch, url, {
120
+ method,
121
+ params,
122
+ authHeader,
123
+ sessionId: cached || undefined,
124
+ signal: controller.signal
125
+ });
116
126
  if (needsSession(response.status, cached)) {
117
127
  deleteSession(key);
118
- const session = await establishSession(guardedFetch, url, authHeader, controller.signal);
128
+ const session = await establishSession(guardedFetch, url, {
129
+ authHeader,
130
+ signal: controller.signal
131
+ });
119
132
  if (typeof session === 'string') {
120
133
  cacheSession(key, session);
121
- response = await post(guardedFetch, url, method, params, authHeader, session, controller.signal);
134
+ response = await post(guardedFetch, url, {
135
+ method,
136
+ params,
137
+ authHeader,
138
+ sessionId: session,
139
+ signal: controller.signal
140
+ });
122
141
  }
123
142
  else if (!session.stateless) {
124
143
  return { error: session.error };
@@ -126,8 +145,9 @@ export function createMcpCallTool({ fetch: fetchImpl, credentialStore, credentia
126
145
  }
127
146
  else {
128
147
  const issued = response.headers.get('mcp-session-id');
129
- if (issued)
148
+ if (issued) {
130
149
  cacheSession(key, issued);
150
+ }
131
151
  }
132
152
  if (isRedirectStatus(response.status)) {
133
153
  return { error: `MCP server responded with an unexpected redirect (${response.status})` };
@@ -135,8 +155,9 @@ export function createMcpCallTool({ fetch: fetchImpl, credentialStore, credentia
135
155
  if (bearer.credentialKey) {
136
156
  recordCredentialHealth(bearer.credentialKey, response.status, ctx?.agentId);
137
157
  }
138
- if (isNotification(method))
158
+ if (isNotification(method)) {
139
159
  return { status: response.status, body: '' };
160
+ }
140
161
  return { status: response.status, ...flattenMcpResponse(await readBody(response)) };
141
162
  }
142
163
  catch (err) {
@@ -22,15 +22,16 @@ function builder(schema, build) {
22
22
  return {
23
23
  build(args) {
24
24
  const parsed = schema.safeParse(args);
25
- if (!parsed.success)
25
+ if (!parsed.success) {
26
26
  return { error: parsed.error.issues[0]?.message ?? 'invalid arguments' };
27
+ }
27
28
  return { request: build(parsed.data) };
28
29
  }
29
30
  };
30
31
  }
31
32
  const GmailMessageSchema = z
32
33
  .object({ ...GmailEnvelopeSchema.shape, threadId: z.string().optional() })
33
- .refine((value) => Boolean(value.text?.length) || Boolean(value.html?.length), {
34
+ .refine((value) => !!value.text?.length || !!value.html?.length, {
34
35
  message: 'either text or html is required'
35
36
  });
36
37
  const gmailSend = builder(GmailMessageSchema, (args) => ({
@@ -51,7 +52,7 @@ const gmailGet = builder(GmailGetSchema, (args) => ({
51
52
  function createRequest() {
52
53
  return {
53
54
  createRequest: {
54
- requestId: b4a.toString(randomBytes(16), 'hex'),
55
+ requestId: b4a.toHex(randomBytes(16)),
55
56
  conferenceSolutionKey: { type: 'hangoutsMeet' }
56
57
  }
57
58
  };
@@ -98,8 +99,9 @@ const docsAppendText = builder(DocsAppendSchema, (args) => {
98
99
  const requests = [
99
100
  { insertText: { endOfSegmentLocation: {}, text: args.text } }
100
101
  ];
101
- if (args.pageBreak)
102
+ if (args.pageBreak) {
102
103
  requests.push({ insertPageBreak: { endOfSegmentLocation: {} } });
104
+ }
103
105
  return { body: { requests } };
104
106
  });
105
107
  const sheetsCreate = builder(z.object({ name: z.string().min(1) }), (args) => ({
@@ -145,10 +147,12 @@ const NotionCreatePageSchema = z
145
147
  message: 'pass parentPageId or parentDatabaseId, not both'
146
148
  });
147
149
  function notionParent(args) {
148
- if (args.parentPageId)
150
+ if (args.parentPageId) {
149
151
  return { page_id: args.parentPageId };
150
- if (args.parentDatabaseId)
152
+ }
153
+ if (args.parentDatabaseId) {
151
154
  return { database_id: args.parentDatabaseId };
155
+ }
152
156
  return undefined;
153
157
  }
154
158
  const notionCreatePage = builder(NotionCreatePageSchema, (args) => ({