stitchkit 0.93.0 → 0.94.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 (791) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +5 -2
  3. package/dist/agent-runtime/children-control.d.ts +6 -0
  4. package/dist/agent-runtime/children-control.d.ts.map +1 -0
  5. package/dist/agent-runtime/children-spawn.d.ts +6 -0
  6. package/dist/agent-runtime/children-spawn.d.ts.map +1 -0
  7. package/dist/agent-runtime/children-state.d.ts +46 -0
  8. package/dist/agent-runtime/children-state.d.ts.map +1 -0
  9. package/dist/agent-runtime/children-usage.d.ts +8 -0
  10. package/dist/agent-runtime/children-usage.d.ts.map +1 -0
  11. package/dist/agent-runtime/children.d.ts +11 -14
  12. package/dist/agent-runtime/children.d.ts.map +1 -1
  13. package/dist/agent-runtime/coding-tool-contract.d.ts +2 -1
  14. package/dist/agent-runtime/coding-tool-contract.d.ts.map +1 -1
  15. package/dist/agent-runtime/deferred-tool-types.d.ts +2 -2
  16. package/dist/agent-runtime/deferred-tool-types.d.ts.map +1 -1
  17. package/dist/agent-runtime/events.d.ts +1 -1
  18. package/dist/agent-runtime/events.d.ts.map +1 -1
  19. package/dist/agent-runtime/managed-tools.d.ts +1 -1
  20. package/dist/agent-runtime/managed-tools.d.ts.map +1 -1
  21. package/dist/agent-runtime/observability.d.ts +1 -1
  22. package/dist/agent-runtime/observability.d.ts.map +1 -1
  23. package/dist/{agent-runtime-openrouter.d.ts → agent-runtime/openrouter.d.ts} +3 -3
  24. package/dist/agent-runtime/openrouter.d.ts.map +1 -0
  25. package/dist/agent-runtime/projections.d.ts +1 -1
  26. package/dist/agent-runtime/projections.d.ts.map +1 -1
  27. package/dist/agent-runtime/run-acquisition.d.ts +17 -0
  28. package/dist/agent-runtime/run-acquisition.d.ts.map +1 -0
  29. package/dist/agent-runtime/run-execution-state.d.ts +133 -0
  30. package/dist/agent-runtime/run-execution-state.d.ts.map +1 -0
  31. package/dist/agent-runtime/run-execution.d.ts +17 -33
  32. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  33. package/dist/agent-runtime/run-failure.d.ts +18 -0
  34. package/dist/agent-runtime/run-failure.d.ts.map +1 -0
  35. package/dist/agent-runtime/run-model-attempts.d.ts +6 -0
  36. package/dist/agent-runtime/run-model-attempts.d.ts.map +1 -0
  37. package/dist/agent-runtime/run-prompt-history.d.ts +58 -0
  38. package/dist/agent-runtime/run-prompt-history.d.ts.map +1 -0
  39. package/dist/agent-runtime/run-provider-ledger.d.ts +26 -0
  40. package/dist/agent-runtime/run-provider-ledger.d.ts.map +1 -0
  41. package/dist/agent-runtime/run-stream-parts.d.ts +17 -0
  42. package/dist/agent-runtime/run-stream-parts.d.ts.map +1 -0
  43. package/dist/agent-runtime/run-terminal.d.ts +11 -0
  44. package/dist/agent-runtime/run-terminal.d.ts.map +1 -0
  45. package/dist/agent-runtime/run-turn-preparation.d.ts +22 -0
  46. package/dist/agent-runtime/run-turn-preparation.d.ts.map +1 -0
  47. package/dist/agent-runtime/runtime-admission-gate.d.ts +29 -0
  48. package/dist/agent-runtime/runtime-admission-gate.d.ts.map +1 -0
  49. package/dist/agent-runtime/runtime-control.d.ts +9 -0
  50. package/dist/agent-runtime/runtime-control.d.ts.map +1 -0
  51. package/dist/agent-runtime/runtime-recovery.d.ts +10 -0
  52. package/dist/agent-runtime/runtime-recovery.d.ts.map +1 -0
  53. package/dist/agent-runtime/runtime-state.d.ts +36 -0
  54. package/dist/agent-runtime/runtime-state.d.ts.map +1 -0
  55. package/dist/agent-runtime/runtime-submit.d.ts +5 -0
  56. package/dist/agent-runtime/runtime-submit.d.ts.map +1 -0
  57. package/dist/agent-runtime/runtime.d.ts +9 -4
  58. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  59. package/dist/{agent-runtime-sqlite-bun.d.ts → agent-runtime/sqlite-bun.d.ts} +5 -4
  60. package/dist/agent-runtime/sqlite-bun.d.ts.map +1 -0
  61. package/dist/agent-runtime/sqlite-events.d.ts +5 -0
  62. package/dist/agent-runtime/sqlite-events.d.ts.map +1 -0
  63. package/dist/agent-runtime/sqlite-history.d.ts +7 -0
  64. package/dist/agent-runtime/sqlite-history.d.ts.map +1 -0
  65. package/dist/agent-runtime/sqlite-ledger.d.ts +7 -0
  66. package/dist/agent-runtime/sqlite-ledger.d.ts.map +1 -0
  67. package/dist/{agent-runtime-sqlite-node.d.ts → agent-runtime/sqlite-node.d.ts} +5 -4
  68. package/dist/agent-runtime/sqlite-node.d.ts.map +1 -0
  69. package/dist/agent-runtime/sqlite-reads.d.ts +14 -0
  70. package/dist/agent-runtime/sqlite-reads.d.ts.map +1 -0
  71. package/dist/agent-runtime/sqlite-rows.d.ts +178 -0
  72. package/dist/agent-runtime/sqlite-rows.d.ts.map +1 -0
  73. package/dist/agent-runtime/sqlite-schema.d.ts +7 -0
  74. package/dist/agent-runtime/sqlite-schema.d.ts.map +1 -0
  75. package/dist/agent-runtime/sqlite-transactions.d.ts +25 -0
  76. package/dist/agent-runtime/sqlite-transactions.d.ts.map +1 -0
  77. package/dist/agent-runtime/sqlite.d.ts +10 -6
  78. package/dist/agent-runtime/sqlite.d.ts.map +1 -1
  79. package/dist/agent-runtime/store-archive.d.ts +16 -0
  80. package/dist/agent-runtime/store-archive.d.ts.map +1 -0
  81. package/dist/{testing/agent-store-conformance.d.ts → agent-runtime/store-conformance.d.ts} +2 -2
  82. package/dist/agent-runtime/store-conformance.d.ts.map +1 -0
  83. package/dist/agent-runtime/store-create.d.ts +11 -0
  84. package/dist/agent-runtime/store-create.d.ts.map +1 -0
  85. package/dist/agent-runtime/{store-driver.d.ts → store-driver-contract.d.ts} +7 -7
  86. package/dist/agent-runtime/store-driver-contract.d.ts.map +1 -0
  87. package/dist/agent-runtime/store-events.d.ts +7 -89
  88. package/dist/agent-runtime/store-events.d.ts.map +1 -1
  89. package/dist/agent-runtime/store-memory.d.ts +4 -0
  90. package/dist/agent-runtime/store-memory.d.ts.map +1 -0
  91. package/dist/agent-runtime/store-mutate.d.ts +13 -0
  92. package/dist/agent-runtime/store-mutate.d.ts.map +1 -0
  93. package/dist/agent-runtime/store-purge.d.ts +1 -1
  94. package/dist/agent-runtime/store-purge.d.ts.map +1 -1
  95. package/dist/agent-runtime/store-reads.d.ts +29 -0
  96. package/dist/agent-runtime/store-reads.d.ts.map +1 -0
  97. package/dist/agent-runtime/store-reduce-history.d.ts +10 -0
  98. package/dist/agent-runtime/store-reduce-history.d.ts.map +1 -0
  99. package/dist/agent-runtime/store-reduce-run.d.ts +14 -0
  100. package/dist/agent-runtime/store-reduce-run.d.ts.map +1 -0
  101. package/dist/agent-runtime/store-reduce-shared.d.ts +76 -0
  102. package/dist/agent-runtime/store-reduce-shared.d.ts.map +1 -0
  103. package/dist/agent-runtime/store-reduce-terminal.d.ts +6 -0
  104. package/dist/agent-runtime/store-reduce-terminal.d.ts.map +1 -0
  105. package/dist/agent-runtime/store-reducer.d.ts +24 -0
  106. package/dist/agent-runtime/store-reducer.d.ts.map +1 -0
  107. package/dist/agent-runtime/store-snapshot.d.ts +19 -0
  108. package/dist/agent-runtime/store-snapshot.d.ts.map +1 -0
  109. package/dist/agent-runtime/store.d.ts +1 -1
  110. package/dist/agent-runtime/store.d.ts.map +1 -1
  111. package/dist/application/admission.d.ts +1 -1
  112. package/dist/application/admission.d.ts.map +1 -1
  113. package/dist/application/events.d.ts +1 -1
  114. package/dist/application/events.d.ts.map +1 -1
  115. package/dist/{server → application}/file-state-store.d.ts +1 -1
  116. package/dist/application/file-state-store.d.ts.map +1 -0
  117. package/dist/application/health.d.ts +1 -1
  118. package/dist/application/health.d.ts.map +1 -1
  119. package/dist/application/kernel-admission.d.ts +8 -0
  120. package/dist/application/kernel-admission.d.ts.map +1 -0
  121. package/dist/application/kernel-contract.d.ts +142 -0
  122. package/dist/application/kernel-contract.d.ts.map +1 -0
  123. package/dist/application/kernel-resource-context.d.ts +24 -0
  124. package/dist/application/kernel-resource-context.d.ts.map +1 -0
  125. package/dist/application/kernel-restart.d.ts +5 -0
  126. package/dist/application/kernel-restart.d.ts.map +1 -0
  127. package/dist/application/kernel-rollback.d.ts +4 -0
  128. package/dist/application/kernel-rollback.d.ts.map +1 -0
  129. package/dist/application/kernel-shutdown.d.ts +6 -0
  130. package/dist/application/kernel-shutdown.d.ts.map +1 -0
  131. package/dist/application/kernel-start.d.ts +20 -0
  132. package/dist/application/kernel-start.d.ts.map +1 -0
  133. package/dist/application/kernel-state.d.ts +116 -0
  134. package/dist/application/kernel-state.d.ts.map +1 -0
  135. package/dist/application/kernel.d.ts +9 -140
  136. package/dist/application/kernel.d.ts.map +1 -1
  137. package/dist/application/latest-sink.d.ts +1 -1
  138. package/dist/application/latest-sink.d.ts.map +1 -1
  139. package/dist/application/opentelemetry.d.ts +1 -1
  140. package/dist/application/opentelemetry.d.ts.map +1 -1
  141. package/dist/application/watch-hub.d.ts +2 -2
  142. package/dist/application/watch-hub.d.ts.map +1 -1
  143. package/dist/browser/client-multipart.d.ts +1 -1
  144. package/dist/browser/client-multipart.d.ts.map +1 -1
  145. package/dist/browser/client-url.d.ts +1 -1
  146. package/dist/browser/client-url.d.ts.map +1 -1
  147. package/dist/browser/client.d.ts +1 -1
  148. package/dist/browser/client.d.ts.map +1 -1
  149. package/dist/browser/contract-stream.d.ts +1 -1
  150. package/dist/browser/contract-stream.d.ts.map +1 -1
  151. package/dist/browser/http.d.ts +1 -1
  152. package/dist/browser/http.d.ts.map +1 -1
  153. package/dist/browser/resumable.d.ts +1 -28
  154. package/dist/browser/resumable.d.ts.map +1 -1
  155. package/dist/browser/retained.d.ts.map +1 -0
  156. package/dist/browser/socket-io.d.ts +23 -1
  157. package/dist/browser/socket-io.d.ts.map +1 -1
  158. package/dist/browser/stream.d.ts +14 -2
  159. package/dist/browser/stream.d.ts.map +1 -1
  160. package/dist/contract/define.d.ts +72 -65
  161. package/dist/contract/define.d.ts.map +1 -1
  162. package/dist/{internal/errors.d.ts → contract/normalize.d.ts} +3 -39
  163. package/dist/contract/normalize.d.ts.map +1 -0
  164. package/dist/contract/tool-view.d.ts +135 -0
  165. package/dist/contract/tool-view.d.ts.map +1 -0
  166. package/dist/declaration/declaration.d.ts.map +1 -0
  167. package/dist/{agent-runtime/durability-contract.d.ts → durability/contract.d.ts} +2 -2
  168. package/dist/durability/contract.d.ts.map +1 -0
  169. package/dist/{agent-runtime/durability.d.ts → durability/engine.d.ts} +4 -4
  170. package/dist/durability/engine.d.ts.map +1 -0
  171. package/dist/durability/events.d.ts +101 -0
  172. package/dist/durability/events.d.ts.map +1 -0
  173. package/dist/{agent-runtime/durability-json.d.ts → durability/json.d.ts} +1 -1
  174. package/dist/durability/json.d.ts.map +1 -0
  175. package/dist/{agent-runtime/durability-ledger.d.ts → durability/ledger.d.ts} +2 -2
  176. package/dist/durability/ledger.d.ts.map +1 -0
  177. package/dist/{agent-runtime/durability-scheduler.d.ts → durability/scheduler.d.ts} +2 -2
  178. package/dist/durability/scheduler.d.ts.map +1 -0
  179. package/dist/entrypoints/agent-runtime/browser.d.ts +5 -0
  180. package/dist/entrypoints/agent-runtime/browser.d.ts.map +1 -0
  181. package/dist/{agent-runtime-browser.js → entrypoints/agent-runtime/browser.js} +3 -3
  182. package/dist/{agent-runtime-coding-tools.d.ts → entrypoints/agent-runtime/coding-tools.d.ts} +2 -2
  183. package/dist/entrypoints/agent-runtime/coding-tools.d.ts.map +1 -0
  184. package/dist/entrypoints/agent-runtime/coding-tools.js +22 -0
  185. package/dist/{agent-runtime-harness.d.ts → entrypoints/agent-runtime/harness.d.ts} +2 -2
  186. package/dist/entrypoints/agent-runtime/harness.d.ts.map +1 -0
  187. package/dist/{agent-runtime-harness.js → entrypoints/agent-runtime/harness.js} +24 -25
  188. package/dist/entrypoints/agent-runtime/openrouter.d.ts +2 -0
  189. package/dist/entrypoints/agent-runtime/openrouter.d.ts.map +1 -0
  190. package/dist/{agent-runtime-openrouter.js → entrypoints/agent-runtime/openrouter.js} +3 -3
  191. package/dist/{agent-runtime-sandbox.d.ts → entrypoints/agent-runtime/sandbox.d.ts} +5 -5
  192. package/dist/entrypoints/agent-runtime/sandbox.d.ts.map +1 -0
  193. package/dist/{agent-runtime-sandbox.js → entrypoints/agent-runtime/sandbox.js} +11 -12
  194. package/dist/entrypoints/agent-runtime/sqlite/bun.d.ts +2 -0
  195. package/dist/entrypoints/agent-runtime/sqlite/bun.d.ts.map +1 -0
  196. package/dist/{agent-runtime-sqlite-bun.js → entrypoints/agent-runtime/sqlite/bun.js} +8 -6
  197. package/dist/entrypoints/agent-runtime/sqlite/node.d.ts +2 -0
  198. package/dist/entrypoints/agent-runtime/sqlite/node.d.ts.map +1 -0
  199. package/dist/{agent-runtime-sqlite-node.js → entrypoints/agent-runtime/sqlite/node.js} +8 -6
  200. package/dist/entrypoints/agent-runtime/testing.d.ts +4 -0
  201. package/dist/entrypoints/agent-runtime/testing.d.ts.map +1 -0
  202. package/dist/entrypoints/agent-runtime/testing.js +1084 -0
  203. package/dist/{agent-runtime.d.ts → entrypoints/agent-runtime.d.ts} +41 -37
  204. package/dist/entrypoints/agent-runtime.d.ts.map +1 -0
  205. package/dist/{agent-runtime.js → entrypoints/agent-runtime.js} +408 -366
  206. package/dist/{application-diagnostic-journal.d.ts → entrypoints/application/diagnostic-journal.d.ts} +2 -2
  207. package/dist/entrypoints/application/diagnostic-journal.d.ts.map +1 -0
  208. package/dist/{application-diagnostic-journal.js → entrypoints/application/diagnostic-journal.js} +3 -3
  209. package/dist/{application-grammy.d.ts → entrypoints/application/grammy.d.ts} +2 -2
  210. package/dist/entrypoints/application/grammy.d.ts.map +1 -0
  211. package/dist/{application-grammy.js → entrypoints/application/grammy.js} +2 -2
  212. package/dist/{application-opentelemetry.d.ts → entrypoints/application/opentelemetry.d.ts} +2 -2
  213. package/dist/entrypoints/application/opentelemetry.d.ts.map +1 -0
  214. package/dist/{application-opentelemetry.js → entrypoints/application/opentelemetry.js} +4 -4
  215. package/dist/{application-schemas.d.ts → entrypoints/application/schemas.d.ts} +2 -2
  216. package/dist/entrypoints/application/schemas.d.ts.map +1 -0
  217. package/dist/{application-schemas.js → entrypoints/application/schemas.js} +2 -2
  218. package/dist/{application.d.ts → entrypoints/application.d.ts} +22 -21
  219. package/dist/entrypoints/application.d.ts.map +1 -0
  220. package/dist/{application.js → entrypoints/application.js} +24 -22
  221. package/dist/entrypoints/bin/upgrade-cli.d.ts.map +1 -0
  222. package/dist/{upgrade-cli.js → entrypoints/bin/upgrade-cli.js} +3 -3
  223. package/dist/{cli.d.ts → entrypoints/cli.d.ts} +15 -15
  224. package/dist/entrypoints/cli.d.ts.map +1 -0
  225. package/dist/{cli.js → entrypoints/cli.js} +30 -32
  226. package/dist/entrypoints/contract.d.ts +9 -0
  227. package/dist/entrypoints/contract.d.ts.map +1 -0
  228. package/dist/{contract/index.js → entrypoints/contract.js} +14 -6
  229. package/dist/entrypoints/declaration.d.ts +2 -0
  230. package/dist/entrypoints/declaration.d.ts.map +1 -0
  231. package/dist/{declaration.js → entrypoints/declaration.js} +1 -1
  232. package/dist/{files.d.ts → entrypoints/files.d.ts} +2 -2
  233. package/dist/entrypoints/files.d.ts.map +1 -0
  234. package/dist/{files.js → entrypoints/files.js} +3 -3
  235. package/dist/{geo.d.ts → entrypoints/geo.d.ts} +1 -1
  236. package/dist/entrypoints/geo.d.ts.map +1 -0
  237. package/dist/{geo.js → entrypoints/geo.js} +4 -4
  238. package/dist/entrypoints/google.d.ts +2 -0
  239. package/dist/entrypoints/google.d.ts.map +1 -0
  240. package/dist/{google.js → entrypoints/google.js} +1 -1
  241. package/dist/{index.d.ts → entrypoints/index.d.ts} +14 -13
  242. package/dist/entrypoints/index.d.ts.map +1 -0
  243. package/dist/{index.js → entrypoints/index.js} +99 -33
  244. package/dist/{live.d.ts → entrypoints/live.d.ts} +4 -4
  245. package/dist/entrypoints/live.d.ts.map +1 -0
  246. package/dist/{live.js → entrypoints/live.js} +6 -4
  247. package/dist/{node.d.ts → entrypoints/node.d.ts} +11 -11
  248. package/dist/entrypoints/node.d.ts.map +1 -0
  249. package/dist/{node.js → entrypoints/node.js} +20 -16
  250. package/dist/entrypoints/oauth.d.ts +2 -0
  251. package/dist/entrypoints/oauth.d.ts.map +1 -0
  252. package/dist/{oauth.js → entrypoints/oauth.js} +3 -3
  253. package/dist/{observability/index.d.ts → entrypoints/observability.d.ts} +11 -11
  254. package/dist/entrypoints/observability.d.ts.map +1 -0
  255. package/dist/{observability/index.js → entrypoints/observability.js} +9 -11
  256. package/dist/entrypoints/primitives.d.ts +3 -0
  257. package/dist/entrypoints/primitives.d.ts.map +1 -0
  258. package/dist/{primitives.js → entrypoints/primitives.js} +1 -6
  259. package/dist/{react.d.ts → entrypoints/react.d.ts} +4 -4
  260. package/dist/entrypoints/react.d.ts.map +1 -0
  261. package/dist/{react.js → entrypoints/react.js} +3 -3
  262. package/dist/{release.d.ts → entrypoints/release.d.ts} +4 -4
  263. package/dist/entrypoints/release.d.ts.map +1 -0
  264. package/dist/{release.js → entrypoints/release.js} +1 -1
  265. package/dist/{remote.d.ts → entrypoints/remote.d.ts} +1 -1
  266. package/dist/entrypoints/remote.d.ts.map +1 -0
  267. package/dist/entrypoints/remote.js +66 -0
  268. package/dist/{server/index.d.ts → entrypoints/server.d.ts} +35 -34
  269. package/dist/entrypoints/server.d.ts.map +1 -0
  270. package/dist/{server/index.js → entrypoints/server.js} +294 -244
  271. package/dist/{telegram.d.ts → entrypoints/telegram.d.ts} +2 -2
  272. package/dist/entrypoints/telegram.d.ts.map +1 -0
  273. package/dist/{telegram.js → entrypoints/telegram.js} +1 -1
  274. package/dist/entrypoints/testing.d.ts +9 -0
  275. package/dist/entrypoints/testing.d.ts.map +1 -0
  276. package/dist/{testing.js → entrypoints/testing.js} +80 -919
  277. package/dist/{tools/connections/index.d.ts → entrypoints/tools/connections.d.ts} +6 -7
  278. package/dist/entrypoints/tools/connections.d.ts.map +1 -0
  279. package/dist/{tools/connections/index.js → entrypoints/tools/connections.js} +7 -4
  280. package/dist/{tools-contract.d.ts → entrypoints/tools/contract.d.ts} +5 -5
  281. package/dist/entrypoints/tools/contract.d.ts.map +1 -0
  282. package/dist/{tools-contract.js → entrypoints/tools/contract.js} +4 -4
  283. package/dist/{tool-invoker.d.ts → entrypoints/tools/invoker.d.ts} +2 -2
  284. package/dist/entrypoints/tools/invoker.d.ts.map +1 -0
  285. package/dist/entrypoints/tools/invoker.js +20 -0
  286. package/dist/entrypoints/tools.d.ts +51 -0
  287. package/dist/entrypoints/tools.d.ts.map +1 -0
  288. package/dist/{tools.js → entrypoints/tools.js} +1787 -1751
  289. package/dist/{tracking-server.d.ts → entrypoints/tracking/server.d.ts} +8 -8
  290. package/dist/entrypoints/tracking/server.d.ts.map +1 -0
  291. package/dist/{tracking.d.ts → entrypoints/tracking.d.ts} +15 -15
  292. package/dist/entrypoints/tracking.d.ts.map +1 -0
  293. package/dist/{tracking.js → entrypoints/tracking.js} +5 -8
  294. package/dist/google/google.d.ts.map +1 -0
  295. package/dist/{index-a916km3r.js → index-0b1ess5e.js} +6 -13
  296. package/dist/{index-zwkgfq1s.js → index-1nd48tm9.js} +113 -118
  297. package/dist/{index-7zbps32p.js → index-1zbbzgfh.js} +2 -2
  298. package/dist/{index-mzvgxp0n.js → index-2cr1cqhn.js} +16 -4
  299. package/dist/{index-f9mb610r.js → index-35mh5bhj.js} +2 -2
  300. package/dist/index-48ffdxgk.js +6 -0
  301. package/dist/index-58k4fp34.js +1023 -0
  302. package/dist/{index-p13mwz16.js → index-83bdg97s.js} +1 -1
  303. package/dist/index-97ac4gph.js +3618 -0
  304. package/dist/{index-kw9t5ayn.js → index-cq4qfycf.js} +5 -5
  305. package/dist/{index-7gnkny4z.js → index-d9pk3td4.js} +1 -1
  306. package/dist/{index-e42wzzz5.js → index-dcpdvjsc.js} +7 -7
  307. package/dist/{index-h9etkkyk.js → index-dydjcp8z.js} +102 -25
  308. package/dist/{index-sr93cwf9.js → index-e2z3ahw9.js} +2 -2
  309. package/dist/{index-yewwh1w4.js → index-e87kakm4.js} +96 -11
  310. package/dist/{index-cby4ar3v.js → index-eqqvgdb3.js} +1 -1
  311. package/dist/index-fdp6hdxn.js +942 -0
  312. package/dist/{index-0a349c33.js → index-gyr48nzg.js} +125 -198
  313. package/dist/{index-wnfk50r0.js → index-h6eeys1v.js} +1 -1
  314. package/dist/{index-77fekveh.js → index-hffjpmq9.js} +1 -15
  315. package/dist/{index-yn8remb1.js → index-j05z5gn4.js} +50 -14
  316. package/dist/index-k5hsfc3j.js +31 -0
  317. package/dist/{index-fq77yfzc.js → index-kj2wbeta.js} +1 -1
  318. package/dist/index-kvj7f4hq.js +39 -0
  319. package/dist/{index-4fpa9dy2.js → index-m9zn6cpq.js} +2 -2
  320. package/dist/{index-2na0qrfn.js → index-mdzh02yc.js} +7 -7
  321. package/dist/{index-ywd0y6m1.js → index-mkm15s49.js} +1 -1
  322. package/dist/index-mp5sn43f.js +1084 -0
  323. package/dist/{index-3bmnp41g.js → index-n6hxg8as.js} +507 -419
  324. package/dist/index-ngfhvfpd.js +30 -0
  325. package/dist/{index-e0znv5gn.js → index-p5r7qpdd.js} +2 -93
  326. package/dist/{index-n0f51dfn.js → index-p81267c2.js} +608 -549
  327. package/dist/{index-wc4x00bn.js → index-qwhynv6x.js} +1 -1
  328. package/dist/index-r7nmqpsg.js +154 -0
  329. package/dist/{index-bx75zwt7.js → index-rewkc2hj.js} +5 -4
  330. package/dist/{index-9f8ztey6.js → index-sees8yf8.js} +2 -2
  331. package/dist/{index-vjpvjqpn.js → index-v38qyerb.js} +16 -2
  332. package/dist/{index-2c2bjw3s.js → index-vcvkq30w.js} +3 -3
  333. package/dist/index-wqgjfstn.js +18 -0
  334. package/dist/{index-f477k430.js → index-x4799c19.js} +1 -1
  335. package/dist/{index-cxqhvq21.js → index-xnj9bgxe.js} +142 -129
  336. package/dist/index-xsqjkha9.js +281 -0
  337. package/dist/{index-6kyksz9y.js → index-yw4fzmdz.js} +1 -1
  338. package/dist/{index-3xwxfj4z.js → index-zeemqhqx.js} +1 -1
  339. package/dist/{index-tmwcrptx.js → index-zfrhfn82.js} +1 -1
  340. package/dist/index-znfs20ss.js +98 -0
  341. package/dist/internal/backoff.d.ts +29 -0
  342. package/dist/internal/backoff.d.ts.map +1 -0
  343. package/dist/internal/canonical-json.d.ts +21 -11
  344. package/dist/internal/canonical-json.d.ts.map +1 -1
  345. package/dist/internal/channel.d.ts.map +1 -0
  346. package/dist/internal/logger.d.ts.map +1 -0
  347. package/dist/internal/request.d.ts.map +1 -0
  348. package/dist/internal/stable-digest.d.ts +0 -43
  349. package/dist/internal/stable-digest.d.ts.map +1 -1
  350. package/dist/internal/typed.d.ts +0 -8
  351. package/dist/internal/typed.d.ts.map +1 -1
  352. package/dist/internal/zod-issues.d.ts +38 -0
  353. package/dist/internal/zod-issues.d.ts.map +1 -0
  354. package/dist/{tools/json-schema-dialect.d.ts → json-schema/dialect.d.ts} +1 -1
  355. package/dist/json-schema/dialect.d.ts.map +1 -0
  356. package/dist/json-schema/json-schema.d.ts.map +1 -0
  357. package/dist/live/watch-delta.d.ts.map +1 -1
  358. package/dist/{oauth.d.ts → oauth/authorization-code.d.ts} +1 -1
  359. package/dist/oauth/authorization-code.d.ts.map +1 -0
  360. package/dist/observability/audit.d.ts +2 -2
  361. package/dist/observability/audit.d.ts.map +1 -1
  362. package/dist/observability/bounded-logger.d.ts +1 -1
  363. package/dist/observability/bounded-logger.d.ts.map +1 -1
  364. package/dist/{internal/observability-sink.d.ts → observability/bounded-sink.d.ts} +2 -2
  365. package/dist/observability/bounded-sink.d.ts.map +1 -0
  366. package/dist/observability/context.d.ts +1 -1
  367. package/dist/observability/context.d.ts.map +1 -1
  368. package/dist/observability/event.d.ts +1 -1
  369. package/dist/observability/event.d.ts.map +1 -1
  370. package/dist/observability/work.d.ts +1 -1
  371. package/dist/observability/work.d.ts.map +1 -1
  372. package/dist/primitives/audit.d.ts +1 -1
  373. package/dist/primitives/audit.d.ts.map +1 -1
  374. package/dist/primitives/export-operation.d.ts +4 -2
  375. package/dist/primitives/export-operation.d.ts.map +1 -1
  376. package/dist/realtime/socket.d.ts +1 -1
  377. package/dist/realtime/socket.d.ts.map +1 -1
  378. package/dist/server/context-contribution.d.ts +1 -1
  379. package/dist/server/context-contribution.d.ts.map +1 -1
  380. package/dist/server/context.d.ts +2 -2
  381. package/dist/server/context.d.ts.map +1 -1
  382. package/dist/server/contract-method.d.ts +15 -0
  383. package/dist/server/contract-method.d.ts.map +1 -0
  384. package/dist/server/contract-stream.d.ts +1 -1
  385. package/dist/server/contract-stream.d.ts.map +1 -1
  386. package/dist/server/create.d.ts +8 -0
  387. package/dist/server/create.d.ts.map +1 -1
  388. package/dist/server/endpoint-dispatch.d.ts +10 -0
  389. package/dist/server/endpoint-dispatch.d.ts.map +1 -0
  390. package/dist/server/endpoint-response.d.ts +18 -0
  391. package/dist/server/endpoint-response.d.ts.map +1 -0
  392. package/dist/server/error-dispatch.d.ts +17 -1
  393. package/dist/server/error-dispatch.d.ts.map +1 -1
  394. package/dist/server/error-hook.d.ts +2 -2
  395. package/dist/server/error-hook.d.ts.map +1 -1
  396. package/dist/server/handler-state.d.ts +44 -0
  397. package/dist/server/handler-state.d.ts.map +1 -0
  398. package/dist/server/implement.d.ts +1 -1
  399. package/dist/server/implement.d.ts.map +1 -1
  400. package/dist/server/middleware/auth.d.ts +1 -1
  401. package/dist/server/middleware/auth.d.ts.map +1 -1
  402. package/dist/server/middleware/cors.d.ts +17 -0
  403. package/dist/server/middleware/cors.d.ts.map +1 -1
  404. package/dist/server/middleware/trust-fence.d.ts +1 -1
  405. package/dist/server/middleware/trust-fence.d.ts.map +1 -1
  406. package/dist/server/multipart.d.ts +1 -1
  407. package/dist/server/multipart.d.ts.map +1 -1
  408. package/dist/{tools/oauth-metadata.d.ts → server/oauth/metadata.d.ts} +2 -2
  409. package/dist/server/oauth/metadata.d.ts.map +1 -0
  410. package/dist/{tools/oauth-provider.d.ts → server/oauth/provider.d.ts} +2 -2
  411. package/dist/server/oauth/provider.d.ts.map +1 -0
  412. package/dist/server/realtime.d.ts +1 -1
  413. package/dist/server/realtime.d.ts.map +1 -1
  414. package/dist/server/request-completion.d.ts +16 -0
  415. package/dist/server/request-completion.d.ts.map +1 -0
  416. package/dist/server/response-metadata.d.ts +1 -1
  417. package/dist/server/response-metadata.d.ts.map +1 -1
  418. package/dist/server/route-table.d.ts +9 -0
  419. package/dist/server/route-table.d.ts.map +1 -0
  420. package/dist/server/stream.d.ts +0 -13
  421. package/dist/server/stream.d.ts.map +1 -1
  422. package/dist/server/types.d.ts +7 -4
  423. package/dist/server/types.d.ts.map +1 -1
  424. package/dist/testing/handler-clients.d.ts +31 -0
  425. package/dist/testing/handler-clients.d.ts.map +1 -0
  426. package/dist/testing/managed-resource-conformance-scenarios.d.ts.map +1 -1
  427. package/dist/testing/surface-conformance.d.ts +2 -1
  428. package/dist/testing/surface-conformance.d.ts.map +1 -1
  429. package/dist/testing/surface-manifest.d.ts +30 -4
  430. package/dist/testing/surface-manifest.d.ts.map +1 -1
  431. package/dist/tools/{cli-args.d.ts → cli/args.d.ts} +1 -1
  432. package/dist/tools/cli/args.d.ts.map +1 -0
  433. package/dist/tools/{cli-checkpoint.d.ts → cli/checkpoint.d.ts} +1 -1
  434. package/dist/tools/cli/checkpoint.d.ts.map +1 -0
  435. package/dist/tools/{cli-command.d.ts → cli/command.d.ts} +4 -4
  436. package/dist/tools/cli/command.d.ts.map +1 -0
  437. package/dist/tools/{cli.d.ts → cli/config.d.ts} +5 -9
  438. package/dist/tools/cli/config.d.ts.map +1 -0
  439. package/dist/tools/cli/create-cli.d.ts +36 -0
  440. package/dist/tools/cli/create-cli.d.ts.map +1 -0
  441. package/dist/tools/cli/download.d.ts +8 -0
  442. package/dist/tools/cli/download.d.ts.map +1 -0
  443. package/dist/tools/{cli-format.d.ts → cli/format.d.ts} +2 -2
  444. package/dist/tools/cli/format.d.ts.map +1 -0
  445. package/dist/tools/cli/help.d.ts +24 -0
  446. package/dist/tools/cli/help.d.ts.map +1 -0
  447. package/dist/tools/{cli-installer.d.ts → cli/installer.d.ts} +2 -2
  448. package/dist/tools/cli/installer.d.ts.map +1 -0
  449. package/dist/tools/{cli-invoke.d.ts → cli/invoke.d.ts} +8 -8
  450. package/dist/tools/cli/invoke.d.ts.map +1 -0
  451. package/dist/tools/cli/io.d.ts +10 -0
  452. package/dist/tools/cli/io.d.ts.map +1 -0
  453. package/dist/tools/cli/managed-surface.d.ts +30 -0
  454. package/dist/tools/cli/managed-surface.d.ts.map +1 -0
  455. package/dist/tools/{cli-manifest.d.ts → cli/manifest.d.ts} +1 -1
  456. package/dist/tools/cli/manifest.d.ts.map +1 -0
  457. package/dist/tools/{cli-policy.d.ts → cli/policy.d.ts} +2 -2
  458. package/dist/tools/cli/policy.d.ts.map +1 -0
  459. package/dist/tools/cli/prepare.d.ts +14 -0
  460. package/dist/tools/cli/prepare.d.ts.map +1 -0
  461. package/dist/tools/{cli-profile.d.ts → cli/profile.d.ts} +2 -2
  462. package/dist/tools/cli/profile.d.ts.map +1 -0
  463. package/dist/tools/cli/run-managed.d.ts +5 -0
  464. package/dist/tools/cli/run-managed.d.ts.map +1 -0
  465. package/dist/tools/cli/run-native.d.ts +9 -0
  466. package/dist/tools/cli/run-native.d.ts.map +1 -0
  467. package/dist/tools/cli/session.d.ts +40 -0
  468. package/dist/tools/cli/session.d.ts.map +1 -0
  469. package/dist/tools/{cli-signature.d.ts → cli/signature.d.ts} +2 -2
  470. package/dist/tools/cli/signature.d.ts.map +1 -0
  471. package/dist/tools/{cli-stream.d.ts → cli/stream.d.ts} +4 -4
  472. package/dist/tools/cli/stream.d.ts.map +1 -0
  473. package/dist/tools/{cli-update.d.ts → cli/update.d.ts} +3 -3
  474. package/dist/tools/cli/update.d.ts.map +1 -0
  475. package/dist/tools/{cli-view.d.ts → cli/view.d.ts} +2 -2
  476. package/dist/tools/cli/view.d.ts.map +1 -0
  477. package/dist/tools/{cli-wait.d.ts → cli/wait.d.ts} +2 -2
  478. package/dist/tools/cli/wait.d.ts.map +1 -0
  479. package/dist/tools/connections/runtime.d.ts +1 -1
  480. package/dist/tools/connections/runtime.d.ts.map +1 -1
  481. package/dist/tools/connections/types.d.ts +3 -3
  482. package/dist/tools/connections/types.d.ts.map +1 -1
  483. package/dist/tools/durability-context.d.ts +1 -1
  484. package/dist/tools/durability-context.d.ts.map +1 -1
  485. package/dist/tools/execute.d.ts +30 -3
  486. package/dist/tools/execute.d.ts.map +1 -1
  487. package/dist/tools/internal/surface-projector.d.ts +8 -6
  488. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  489. package/dist/tools/internal/tool-view.d.ts +41 -0
  490. package/dist/tools/internal/tool-view.d.ts.map +1 -0
  491. package/dist/tools/invoker.d.ts +11 -3
  492. package/dist/tools/invoker.d.ts.map +1 -1
  493. package/dist/tools/list-names.d.ts +3 -3
  494. package/dist/tools/list-names.d.ts.map +1 -1
  495. package/dist/tools/manifest.d.ts +2 -2
  496. package/dist/tools/manifest.d.ts.map +1 -1
  497. package/dist/tools/{mcp-app.d.ts → mcp/app.d.ts} +1 -1
  498. package/dist/tools/mcp/app.d.ts.map +1 -0
  499. package/dist/tools/{mcp-catalog.d.ts → mcp/catalog.d.ts} +2 -2
  500. package/dist/tools/mcp/catalog.d.ts.map +1 -0
  501. package/dist/tools/{mcp-handler.d.ts → mcp/handler.d.ts} +5 -5
  502. package/dist/tools/mcp/handler.d.ts.map +1 -0
  503. package/dist/tools/{mcp.d.ts → mcp/mount.d.ts} +9 -9
  504. package/dist/tools/mcp/mount.d.ts.map +1 -0
  505. package/dist/tools/{mcp-prepare.d.ts → mcp/prepare.d.ts} +9 -9
  506. package/dist/tools/mcp/prepare.d.ts.map +1 -0
  507. package/dist/tools/{mcp-progress.d.ts → mcp/progress.d.ts} +2 -2
  508. package/dist/tools/mcp/progress.d.ts.map +1 -0
  509. package/dist/tools/mcp/register.d.ts +35 -0
  510. package/dist/tools/mcp/register.d.ts.map +1 -0
  511. package/dist/tools/{mcp-round-policy.d.ts → mcp/round-policy.d.ts} +2 -2
  512. package/dist/tools/mcp/round-policy.d.ts.map +1 -0
  513. package/dist/tools/{mcp-round.d.ts → mcp/round.d.ts} +4 -4
  514. package/dist/tools/mcp/round.d.ts.map +1 -0
  515. package/dist/tools/mcp/runtime-tools.d.ts +24 -0
  516. package/dist/tools/mcp/runtime-tools.d.ts.map +1 -0
  517. package/dist/tools/{mcp-stdio-signals.d.ts → mcp/stdio-signals.d.ts} +3 -3
  518. package/dist/tools/mcp/stdio-signals.d.ts.map +1 -0
  519. package/dist/tools/{mcp-stdio.d.ts → mcp/stdio.d.ts} +4 -4
  520. package/dist/tools/mcp/stdio.d.ts.map +1 -0
  521. package/dist/tools/{mcp-trace.d.ts → mcp/trace.d.ts} +1 -1
  522. package/dist/tools/mcp/trace.d.ts.map +1 -0
  523. package/dist/tools/mount.d.ts +12 -7
  524. package/dist/tools/mount.d.ts.map +1 -1
  525. package/dist/tools/native-definition.d.ts +3 -3
  526. package/dist/tools/native-definition.d.ts.map +1 -1
  527. package/dist/tools/operations/async-operation-binding-types.d.ts +127 -0
  528. package/dist/tools/operations/async-operation-binding-types.d.ts.map +1 -0
  529. package/dist/tools/operations/async-operation-binding.d.ts +28 -0
  530. package/dist/tools/operations/async-operation-binding.d.ts.map +1 -0
  531. package/dist/tools/operations/async-operation-canonical-types.d.ts +110 -0
  532. package/dist/tools/operations/async-operation-canonical-types.d.ts.map +1 -0
  533. package/dist/tools/operations/async-operation-canonical.d.ts +7 -0
  534. package/dist/tools/operations/async-operation-canonical.d.ts.map +1 -0
  535. package/dist/tools/operations/async-operation-contract.d.ts.map +1 -0
  536. package/dist/tools/operations/async-operation-id.d.ts +23 -0
  537. package/dist/tools/operations/async-operation-id.d.ts.map +1 -0
  538. package/dist/tools/operations/async-operation-runtime-tools.d.ts +40 -0
  539. package/dist/tools/operations/async-operation-runtime-tools.d.ts.map +1 -0
  540. package/dist/tools/operations/async-operation-runtime-types.d.ts +68 -0
  541. package/dist/tools/operations/async-operation-runtime-types.d.ts.map +1 -0
  542. package/dist/tools/operations/async-operation-runtime.d.ts +5 -0
  543. package/dist/tools/operations/async-operation-runtime.d.ts.map +1 -0
  544. package/dist/tools/{define-wait-tool.d.ts → operations/define-wait-tool.d.ts} +2 -2
  545. package/dist/tools/operations/define-wait-tool.d.ts.map +1 -0
  546. package/dist/tools/operations/mount-wait.d.ts.map +1 -0
  547. package/dist/tools/operations/wait-core.d.ts.map +1 -0
  548. package/dist/tools/remote.d.ts +1 -1
  549. package/dist/tools/remote.d.ts.map +1 -1
  550. package/dist/tools/runtime-tool.d.ts +11 -4
  551. package/dist/tools/runtime-tool.d.ts.map +1 -1
  552. package/dist/tools/schema/coerce.d.ts.map +1 -0
  553. package/dist/tools/schema/flatten-join.d.ts.map +1 -0
  554. package/dist/tools/schema/flatten.d.ts.map +1 -0
  555. package/dist/tools/schema/portable-formats.d.ts.map +1 -0
  556. package/dist/tools/{presentation.d.ts → schema/presentation.d.ts} +2 -0
  557. package/dist/tools/schema/presentation.d.ts.map +1 -0
  558. package/dist/tools/schema/schema.d.ts.map +1 -0
  559. package/dist/tools/schema/untyped-properties.d.ts.map +1 -0
  560. package/dist/tools/surface.d.ts +2 -3
  561. package/dist/tools/surface.d.ts.map +1 -1
  562. package/dist/tools/toolkit.d.ts +4 -4
  563. package/dist/tools/toolkit.d.ts.map +1 -1
  564. package/dist/tools/{define-download-tool.d.ts → transfer/define-download-tool.d.ts} +4 -4
  565. package/dist/tools/transfer/define-download-tool.d.ts.map +1 -0
  566. package/dist/tools/{define-upload-tool.d.ts → transfer/define-upload-tool.d.ts} +3 -3
  567. package/dist/tools/transfer/define-upload-tool.d.ts.map +1 -0
  568. package/dist/tools/{define-view-file-tool.d.ts → transfer/define-view-file-tool.d.ts} +2 -2
  569. package/dist/tools/transfer/define-view-file-tool.d.ts.map +1 -0
  570. package/dist/tools/{download-core.d.ts → transfer/download-core.d.ts} +2 -2
  571. package/dist/tools/transfer/download-core.d.ts.map +1 -0
  572. package/dist/tools/{managed-file-error.d.ts → transfer/managed-file-error.d.ts} +1 -1
  573. package/dist/tools/transfer/managed-file-error.d.ts.map +1 -0
  574. package/dist/tools/{mount-download.d.ts → transfer/mount-download.d.ts} +1 -1
  575. package/dist/tools/transfer/mount-download.d.ts.map +1 -0
  576. package/dist/tools/{mount-upload.d.ts → transfer/mount-upload.d.ts} +1 -1
  577. package/dist/tools/transfer/mount-upload.d.ts.map +1 -0
  578. package/dist/tools/transfer/native-result.d.ts.map +1 -0
  579. package/dist/tools/{upload-core.d.ts → transfer/upload-core.d.ts} +1 -1
  580. package/dist/tools/transfer/upload-core.d.ts.map +1 -0
  581. package/dist/tools/transfer/view-file-contract.d.ts.map +1 -0
  582. package/dist/tools/{view-file.d.ts → transfer/view-file.d.ts} +1 -1
  583. package/dist/tools/transfer/view-file.d.ts.map +1 -0
  584. package/dist/tools/transports.d.ts +1 -1
  585. package/dist/tools/transports.d.ts.map +1 -1
  586. package/dist/tracking/contract.d.ts +1 -1
  587. package/dist/tracking/contract.d.ts.map +1 -1
  588. package/llms/agent-runtime-browser.txt +33 -0
  589. package/llms/agent-runtime-coding-tools.txt +58 -0
  590. package/llms/agent-runtime-harness.txt +45 -0
  591. package/llms/agent-runtime-openrouter.txt +22 -0
  592. package/llms/agent-runtime-sandbox.txt +159 -0
  593. package/llms/agent-runtime-sqlite-bun.txt +29 -0
  594. package/llms/agent-runtime-sqlite-node.txt +26 -0
  595. package/llms/agent-runtime-testing.txt +30 -0
  596. package/llms/agent-runtime.2.txt +550 -0
  597. package/llms/agent-runtime.3.txt +311 -0
  598. package/llms/agent-runtime.txt +893 -0
  599. package/llms/application-diagnostic-journal.txt +30 -0
  600. package/llms/application-grammy.txt +27 -0
  601. package/llms/application-opentelemetry.txt +27 -0
  602. package/llms/application-schemas.txt +42 -0
  603. package/llms/application.2.txt +223 -0
  604. package/llms/application.txt +1144 -0
  605. package/llms/cli.txt +977 -0
  606. package/llms/contract.txt +606 -0
  607. package/llms/declaration.txt +286 -0
  608. package/llms/files.txt +30 -0
  609. package/llms/geo.txt +114 -0
  610. package/llms/google.txt +27 -0
  611. package/llms/live.txt +471 -0
  612. package/llms/node.txt +43 -0
  613. package/llms/oauth.txt +136 -0
  614. package/llms/observability.txt +893 -0
  615. package/llms/primitives.txt +158 -0
  616. package/llms/react.txt +184 -0
  617. package/llms/release.txt +176 -0
  618. package/llms/remote.txt +25 -0
  619. package/llms/server.2.txt +361 -0
  620. package/llms/server.3.txt +1031 -0
  621. package/llms/server.4.txt +905 -0
  622. package/llms/server.5.txt +250 -0
  623. package/llms/server.txt +996 -0
  624. package/llms/stitchkit.2.txt +836 -0
  625. package/llms/stitchkit.3.txt +147 -0
  626. package/llms/stitchkit.txt +260 -0
  627. package/llms/telegram.txt +30 -0
  628. package/llms/testing.txt +676 -0
  629. package/llms/tools-connections.txt +45 -0
  630. package/llms/tools-contract.txt +9 -0
  631. package/llms/tools-invoker.txt +9 -0
  632. package/llms/tools.2.txt +838 -0
  633. package/llms/tools.txt +1004 -0
  634. package/llms/tracking-server.txt +32 -0
  635. package/llms/tracking.txt +263 -0
  636. package/llms/upgrading-0.37.0-0.50.0.txt +947 -0
  637. package/llms/upgrading-0.53.0-0.67.0.txt +1174 -0
  638. package/llms/upgrading-0.68.0-0.94.0.txt +1159 -0
  639. package/llms/upgrading.txt +215 -0
  640. package/llms-full.txt +214 -75
  641. package/llms.txt +54 -5
  642. package/package.json +84 -83
  643. package/dist/agent-runtime/durability-contract.d.ts.map +0 -1
  644. package/dist/agent-runtime/durability-json.d.ts.map +0 -1
  645. package/dist/agent-runtime/durability-ledger.d.ts.map +0 -1
  646. package/dist/agent-runtime/durability-scheduler.d.ts.map +0 -1
  647. package/dist/agent-runtime/durability.d.ts.map +0 -1
  648. package/dist/agent-runtime/store-driver.d.ts.map +0 -1
  649. package/dist/agent-runtime-browser.d.ts +0 -5
  650. package/dist/agent-runtime-browser.d.ts.map +0 -1
  651. package/dist/agent-runtime-coding-tools.d.ts.map +0 -1
  652. package/dist/agent-runtime-coding-tools.js +0 -23
  653. package/dist/agent-runtime-harness.d.ts.map +0 -1
  654. package/dist/agent-runtime-openrouter.d.ts.map +0 -1
  655. package/dist/agent-runtime-sandbox.d.ts.map +0 -1
  656. package/dist/agent-runtime-sqlite-bun.d.ts.map +0 -1
  657. package/dist/agent-runtime-sqlite-node.d.ts.map +0 -1
  658. package/dist/agent-runtime-testing.d.ts +0 -3
  659. package/dist/agent-runtime-testing.d.ts.map +0 -1
  660. package/dist/agent-runtime-testing.js +0 -156
  661. package/dist/agent-runtime.d.ts.map +0 -1
  662. package/dist/application/channel.d.ts.map +0 -1
  663. package/dist/application-diagnostic-journal.d.ts.map +0 -1
  664. package/dist/application-grammy.d.ts.map +0 -1
  665. package/dist/application-opentelemetry.d.ts.map +0 -1
  666. package/dist/application-schemas.d.ts.map +0 -1
  667. package/dist/application.d.ts.map +0 -1
  668. package/dist/cli.d.ts.map +0 -1
  669. package/dist/contract/index.d.ts +0 -8
  670. package/dist/contract/index.d.ts.map +0 -1
  671. package/dist/declaration.d.ts.map +0 -1
  672. package/dist/files.d.ts.map +0 -1
  673. package/dist/geo.d.ts.map +0 -1
  674. package/dist/google.d.ts.map +0 -1
  675. package/dist/index-0j3vcp5m.js +0 -28
  676. package/dist/index-a11g2kry.js +0 -866
  677. package/dist/index-cnyk6te3.js +0 -77
  678. package/dist/index-fadnxwna.js +0 -1046
  679. package/dist/index-hrbajay7.js +0 -1196
  680. package/dist/index-s8cnx02s.js +0 -90
  681. package/dist/index-v5573g1k.js +0 -142
  682. package/dist/index-vk3kq9sb.js +0 -3344
  683. package/dist/index.d.ts.map +0 -1
  684. package/dist/internal/errors.d.ts.map +0 -1
  685. package/dist/internal/observability-sink.d.ts.map +0 -1
  686. package/dist/live.d.ts.map +0 -1
  687. package/dist/logger.d.ts.map +0 -1
  688. package/dist/node.d.ts.map +0 -1
  689. package/dist/oauth.d.ts.map +0 -1
  690. package/dist/observability/index.d.ts.map +0 -1
  691. package/dist/primitives.d.ts +0 -3
  692. package/dist/primitives.d.ts.map +0 -1
  693. package/dist/react.d.ts.map +0 -1
  694. package/dist/release.d.ts.map +0 -1
  695. package/dist/remote.d.ts.map +0 -1
  696. package/dist/remote.js +0 -88
  697. package/dist/retained.d.ts.map +0 -1
  698. package/dist/server/file-state-store.d.ts.map +0 -1
  699. package/dist/server/index.d.ts.map +0 -1
  700. package/dist/server/request.d.ts.map +0 -1
  701. package/dist/telegram.d.ts.map +0 -1
  702. package/dist/testing/agent-store-conformance.d.ts.map +0 -1
  703. package/dist/testing.d.ts +0 -39
  704. package/dist/testing.d.ts.map +0 -1
  705. package/dist/tool-invoker.d.ts.map +0 -1
  706. package/dist/tool-invoker.js +0 -21
  707. package/dist/tools/async-operation-contract.d.ts.map +0 -1
  708. package/dist/tools/async-operation.d.ts +0 -332
  709. package/dist/tools/async-operation.d.ts.map +0 -1
  710. package/dist/tools/cli-args.d.ts.map +0 -1
  711. package/dist/tools/cli-checkpoint.d.ts.map +0 -1
  712. package/dist/tools/cli-command.d.ts.map +0 -1
  713. package/dist/tools/cli-format.d.ts.map +0 -1
  714. package/dist/tools/cli-installer.d.ts.map +0 -1
  715. package/dist/tools/cli-invoke.d.ts.map +0 -1
  716. package/dist/tools/cli-manifest.d.ts.map +0 -1
  717. package/dist/tools/cli-policy.d.ts.map +0 -1
  718. package/dist/tools/cli-profile.d.ts.map +0 -1
  719. package/dist/tools/cli-signature.d.ts.map +0 -1
  720. package/dist/tools/cli-stream.d.ts.map +0 -1
  721. package/dist/tools/cli-update.d.ts.map +0 -1
  722. package/dist/tools/cli-view.d.ts.map +0 -1
  723. package/dist/tools/cli-wait.d.ts.map +0 -1
  724. package/dist/tools/cli.d.ts.map +0 -1
  725. package/dist/tools/coerce.d.ts.map +0 -1
  726. package/dist/tools/connections/index.d.ts.map +0 -1
  727. package/dist/tools/define-download-tool.d.ts.map +0 -1
  728. package/dist/tools/define-upload-tool.d.ts.map +0 -1
  729. package/dist/tools/define-view-file-tool.d.ts.map +0 -1
  730. package/dist/tools/define-wait-tool.d.ts.map +0 -1
  731. package/dist/tools/download-core.d.ts.map +0 -1
  732. package/dist/tools/flatten-join.d.ts.map +0 -1
  733. package/dist/tools/flatten.d.ts.map +0 -1
  734. package/dist/tools/json-schema-dialect.d.ts.map +0 -1
  735. package/dist/tools/json-schema.d.ts.map +0 -1
  736. package/dist/tools/managed-file-error.d.ts.map +0 -1
  737. package/dist/tools/mcp-app.d.ts.map +0 -1
  738. package/dist/tools/mcp-catalog.d.ts.map +0 -1
  739. package/dist/tools/mcp-handler.d.ts.map +0 -1
  740. package/dist/tools/mcp-prepare.d.ts.map +0 -1
  741. package/dist/tools/mcp-progress.d.ts.map +0 -1
  742. package/dist/tools/mcp-round-policy.d.ts.map +0 -1
  743. package/dist/tools/mcp-round.d.ts.map +0 -1
  744. package/dist/tools/mcp-stdio-signals.d.ts.map +0 -1
  745. package/dist/tools/mcp-stdio.d.ts.map +0 -1
  746. package/dist/tools/mcp-trace.d.ts.map +0 -1
  747. package/dist/tools/mcp.d.ts.map +0 -1
  748. package/dist/tools/mount-download.d.ts.map +0 -1
  749. package/dist/tools/mount-upload.d.ts.map +0 -1
  750. package/dist/tools/mount-wait.d.ts.map +0 -1
  751. package/dist/tools/native-mcp.d.ts +0 -20
  752. package/dist/tools/native-mcp.d.ts.map +0 -1
  753. package/dist/tools/native-result.d.ts.map +0 -1
  754. package/dist/tools/oauth-metadata.d.ts.map +0 -1
  755. package/dist/tools/oauth-provider.d.ts.map +0 -1
  756. package/dist/tools/portable-formats.d.ts.map +0 -1
  757. package/dist/tools/presentation.d.ts.map +0 -1
  758. package/dist/tools/schema.d.ts.map +0 -1
  759. package/dist/tools/untyped-properties.d.ts.map +0 -1
  760. package/dist/tools/upload-core.d.ts.map +0 -1
  761. package/dist/tools/view-file-contract.d.ts.map +0 -1
  762. package/dist/tools/view-file.d.ts.map +0 -1
  763. package/dist/tools/wait-core.d.ts.map +0 -1
  764. package/dist/tools-contract.d.ts.map +0 -1
  765. package/dist/tools.d.ts +0 -50
  766. package/dist/tools.d.ts.map +0 -1
  767. package/dist/tracking-server.d.ts.map +0 -1
  768. package/dist/tracking.d.ts.map +0 -1
  769. package/dist/upgrade-cli.d.ts.map +0 -1
  770. package/dist/{retained.d.ts → browser/retained.d.ts} +0 -0
  771. package/dist/{declaration.d.ts → declaration/declaration.d.ts} +0 -0
  772. package/dist/{upgrade-cli.d.ts → entrypoints/bin/upgrade-cli.d.ts} +0 -0
  773. package/dist/{tracking-server.js → entrypoints/tracking/server.js} +0 -0
  774. package/dist/{google.d.ts → google/google.d.ts} +0 -0
  775. package/dist/{index-pq95qkbf.js → index-5jm36zv1.js} +0 -0
  776. package/dist/{index-actkayzn.js → index-70yj2ebz.js} +3 -3
  777. /package/dist/{application → internal}/channel.d.ts +0 -0
  778. /package/dist/{logger.d.ts → internal/logger.d.ts} +0 -0
  779. /package/dist/{server → internal}/request.d.ts +0 -0
  780. /package/dist/{tools → json-schema}/json-schema.d.ts +0 -0
  781. /package/dist/tools/{async-operation-contract.d.ts → operations/async-operation-contract.d.ts} +0 -0
  782. /package/dist/tools/{mount-wait.d.ts → operations/mount-wait.d.ts} +0 -0
  783. /package/dist/tools/{wait-core.d.ts → operations/wait-core.d.ts} +0 -0
  784. /package/dist/tools/{coerce.d.ts → schema/coerce.d.ts} +0 -0
  785. /package/dist/tools/{flatten-join.d.ts → schema/flatten-join.d.ts} +0 -0
  786. /package/dist/tools/{flatten.d.ts → schema/flatten.d.ts} +0 -0
  787. /package/dist/tools/{portable-formats.d.ts → schema/portable-formats.d.ts} +0 -0
  788. /package/dist/tools/{schema.d.ts → schema/schema.d.ts} +0 -0
  789. /package/dist/tools/{untyped-properties.d.ts → schema/untyped-properties.d.ts} +0 -0
  790. /package/dist/tools/{native-result.d.ts → transfer/native-result.d.ts} +0 -0
  791. /package/dist/tools/{view-file-contract.d.ts → transfer/view-file-contract.d.ts} +0 -0
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  ManagedFileError
3
- } from "./index-actkayzn.js";
3
+ } from "../index-70yj2ebz.js";
4
4
  import {
5
5
  signJwt,
6
6
  verifyPkce
7
- } from "./index-sr93cwf9.js";
8
- import"./index-hr4tzbwq.js";
7
+ } from "../index-e2z3ahw9.js";
8
+ import"../index-hr4tzbwq.js";
9
9
  import {
10
10
  DEFAULT_CORS_ALLOW_HEADERS,
11
11
  DEFAULT_PROCESS_SIGNALS,
@@ -14,20 +14,31 @@ import {
14
14
  guardSignalCallback,
15
15
  mediaTypeEssence,
16
16
  reportSignalError
17
- } from "./index-cxqhvq21.js";
17
+ } from "../index-xnj9bgxe.js";
18
+ import"../index-kvj7f4hq.js";
18
19
  import {
19
20
  redact
20
- } from "./index-6kyksz9y.js";
21
+ } from "../index-yw4fzmdz.js";
21
22
  import {
22
23
  buildToolManifest,
23
24
  describeToolCatalog,
24
25
  mountAgent
25
- } from "./index-e42wzzz5.js";
26
+ } from "../index-dcpdvjsc.js";
26
27
  import {
27
28
  AgentToolError,
28
29
  createLocalStepDurability,
29
30
  isAgentToolError
30
- } from "./index-kw9t5ayn.js";
31
+ } from "../index-cq4qfycf.js";
32
+ import"../index-rxfy4cq7.js";
33
+ import {
34
+ ALL_TRANSPORTS,
35
+ TOOL_TRANSPORTS,
36
+ defineContract
37
+ } from "../index-gyr48nzg.js";
38
+ import {
39
+ ManagedFilePathSchema,
40
+ ManagedFileRefSchema
41
+ } from "../index-6k1937bx.js";
31
42
  import {
32
43
  AsyncOperationCancelResultSchema,
33
44
  McpAnnotationsSchema,
@@ -36,69 +47,52 @@ import {
36
47
  ViewFileInputSchema,
37
48
  ViewFileOutputSchema,
38
49
  createAsyncOperationSnapshotSchema
39
- } from "./index-4fpa9dy2.js";
50
+ } from "../index-m9zn6cpq.js";
40
51
  import {
41
52
  createToolInvoker
42
- } from "./index-bx75zwt7.js";
53
+ } from "../index-rewkc2hj.js";
43
54
  import {
44
55
  WaitTimeoutError,
45
56
  createCli,
46
- defineCliCommand,
47
57
  fetchGuarded,
48
58
  fetchPinnedDocument,
49
59
  readCapped,
50
60
  runWaitOperation
51
- } from "./index-3bmnp41g.js";
52
- import {
53
- compareCodepoints,
54
- serializeCanonicalJson
55
- } from "./index-0j3vcp5m.js";
56
- import"./index-sbdmyz75.js";
61
+ } from "../index-n6hxg8as.js";
62
+ import"../index-sbdmyz75.js";
57
63
  import {
58
64
  collectToolSurface
59
- } from "./index-2c2bjw3s.js";
65
+ } from "../index-vcvkq30w.js";
60
66
  import {
61
67
  argumentsDigest
62
- } from "./index-a916km3r.js";
68
+ } from "../index-0b1ess5e.js";
63
69
  import {
64
70
  collectTools,
65
71
  createToolRunner,
66
72
  formatToolError
67
- } from "./index-mzvgxp0n.js";
73
+ } from "../index-2cr1cqhn.js";
68
74
  import {
69
75
  ToolExecutionControlError,
70
- executeToolMethod,
71
76
  isToolExecutionControlError,
72
77
  parseToolCallArguments,
73
78
  toolResultFromError
74
- } from "./index-yn8remb1.js";
79
+ } from "../index-j05z5gn4.js";
75
80
  import {
76
81
  getRequestContext,
77
82
  getTraceId,
78
83
  runWithRequestContext
79
- } from "./index-tmwcrptx.js";
80
- import"./index-f9mb610r.js";
81
- import {
82
- normalizeError
83
- } from "./index-e0znv5gn.js";
84
- import {
85
- defineContract
86
- } from "./index-0a349c33.js";
87
- import"./index-rxfy4cq7.js";
88
- import {
89
- ManagedFilePathSchema,
90
- ManagedFileRefSchema
91
- } from "./index-6k1937bx.js";
84
+ } from "../index-zfrhfn82.js";
85
+ import"../index-35mh5bhj.js";
92
86
  import {
93
87
  resolvePropagationContext
94
- } from "./index-zcgf3gqf.js";
88
+ } from "../index-zcgf3gqf.js";
95
89
  import {
96
90
  coerceJsonArgs
97
- } from "./index-7zbps32p.js";
91
+ } from "../index-1zbbzgfh.js";
98
92
  import {
99
93
  createRuntimeToolFactory,
100
94
  defineRuntimeTool
101
- } from "./index-vjpvjqpn.js";
95
+ } from "../index-v38qyerb.js";
102
96
  import {
103
97
  PORTABLE_JSON_SCHEMA_FORMATS,
104
98
  assertToolName,
@@ -109,778 +103,714 @@ import {
109
103
  prepareProjectedMcpTools,
110
104
  presentationMetadata,
111
105
  staticInputRounds,
106
+ toolSurfaceOutputSchema,
112
107
  validateMcpRoundPolicy,
113
108
  validateResolvedInputRounds
114
- } from "./index-h9etkkyk.js";
109
+ } from "../index-dydjcp8z.js";
115
110
  import {
116
111
  toJsonSchema
117
- } from "./index-cby4ar3v.js";
112
+ } from "../index-eqqvgdb3.js";
113
+ import {
114
+ normalizeError
115
+ } from "../index-znfs20ss.js";
116
+ import"../index-p5r7qpdd.js";
118
117
  import {
119
118
  AppError,
120
119
  STITCH_ERROR_STATUS
121
- } from "./index-2vtcmrj9.js";
122
- import"./index-kzfs85xp.js";
120
+ } from "../index-2vtcmrj9.js";
121
+ import {
122
+ compareCodeUnits,
123
+ serializeCanonicalJson
124
+ } from "../index-k5hsfc3j.js";
125
+ import"../index-kzfs85xp.js";
123
126
  import {
124
127
  isRecord
125
- } from "./index-77fekveh.js";
126
- // src/tools/async-operation.ts
127
- import { z } from "zod";
128
- // src/tools/native-definition.ts
129
- function managedNativeIdentity(identity, method) {
130
- return { ...identity, method };
131
- }
132
-
133
- // src/tools/define-wait-tool.ts
134
- function defineWaitTool(config) {
135
- const renderedMcp = config.render ? {
136
- mcp: (state) => {
137
- const rendered = config.render?.(state, false);
138
- return {
139
- content: [
140
- {
141
- type: "text",
142
- text: rendered?.text ?? JSON.stringify(state, null, 2)
143
- }
144
- ]
145
- };
146
- }
147
- } : undefined;
148
- const present = renderedMcp || config.present ? { ...renderedMcp, ...config.present } : undefined;
149
- return defineRuntimeTool({
150
- name: config.name ?? "wait",
151
- description: config.description,
152
- identity: managedNativeIdentity(config.identity, "GET"),
153
- input: config.input,
154
- output: config.state,
155
- transports: config.transports,
156
- annotations: config.annotations,
157
- present,
158
- handler: async (context) => {
159
- let result;
160
- try {
161
- result = await runWaitOperation({
162
- input: context.input,
163
- poll: async (input, signal) => config.state.parse(await config.poll(input, { ...context, signal })),
164
- done: config.done,
165
- backoff: config.backoff,
166
- timeoutSec: config.timeoutFromInput?.(context.input) ?? config.defaultTimeout,
167
- signal: context.signal
168
- });
169
- } catch (error) {
170
- if (error instanceof WaitTimeoutError) {
171
- throw new AppError("WAIT_TIMEOUT", "Wait timed out before the first snapshot", 408);
172
- }
173
- throw error;
174
- }
175
- const rendered = config.render?.(result.state, result.timedOut);
176
- if (result.timedOut) {
177
- throw new AppError("WAIT_TIMEOUT", rendered?.text ?? "Wait timed out before the operation completed", 408);
178
- }
179
- if (rendered?.isError) {
180
- throw new AppError("WAIT_FAILED", rendered.text, 409);
181
- }
182
- return result.state;
183
- }
184
- });
128
+ } from "../index-hffjpmq9.js";
129
+ // src/server/oauth/metadata.ts
130
+ var PROTECTED_RESOURCE_PATH = "/.well-known/oauth-protected-resource";
131
+ function metadataPath(resource) {
132
+ const { pathname } = new URL(resource);
133
+ return pathname === "/" ? PROTECTED_RESOURCE_PATH : `${PROTECTED_RESOURCE_PATH}${pathname}`;
185
134
  }
186
-
187
- // src/tools/async-operation.ts
188
- function terminal(snapshot) {
189
- if (typeof snapshot !== "object" || snapshot === null || !("phase" in snapshot)) {
190
- return false;
191
- }
192
- return snapshot.phase === "succeeded" || snapshot.phase === "failed" || snapshot.phase === "cancelled";
135
+ function protectedResourceMetadataUrl(resource) {
136
+ return `${new URL(resource).origin}${metadataPath(resource)}`;
193
137
  }
194
- function succeeded(snapshot) {
195
- return typeof snapshot === "object" && snapshot !== null && "phase" in snapshot && snapshot.phase === "succeeded";
138
+ function wwwAuthenticateHeader(resource) {
139
+ return `Bearer resource_metadata="${protectedResourceMetadataUrl(resource)}"`;
196
140
  }
197
- function defineAsyncOperation(config) {
198
- const capabilityName = (capability) => config.names?.[capability] ?? `${config.name}_${capability}`;
199
- const description = (capability) => config.descriptions?.[capability] ?? `${config.description}: ${capability}`;
200
- const identity = (capability, method) => ({
201
- serviceName: config.identity.serviceName,
202
- action: `${config.identity.action}.${capability}`,
203
- scope: config.scopes?.[capability] ?? config.identity.scope,
204
- meta: config.identity.meta,
205
- method
206
- });
207
- const common = (capability) => ({
208
- name: capabilityName(capability),
209
- description: description(capability),
210
- transports: config.transports?.[capability],
211
- annotations: config.annotations?.[capability] ?? (capability === "start" || capability === "cancel" ? { destructiveHint: true } : undefined)
212
- });
213
- const inspect = async (capability, context) => {
214
- await config.authorize(context.input, capability, context);
215
- const state = config.state.parse(await config.inspect(context.input, context));
216
- const snapshot = config.snapshot.parse(await config.classify(state, context));
217
- return { state, snapshot };
218
- };
219
- const start = defineRuntimeTool({
220
- ...common("start"),
221
- identity: identity("start", "POST"),
222
- input: config.startInput,
223
- output: config.id,
224
- handler: (context) => config.start(context.input, context)
225
- });
226
- const status = defineRuntimeTool({
227
- ...common("status"),
228
- identity: identity("status", "GET"),
229
- input: config.id,
230
- output: config.snapshot,
231
- handler: async (context) => (await inspect("status", context)).snapshot
232
- });
233
- const wait = defineWaitTool({
234
- ...common("wait"),
235
- identity: identity("wait", "GET"),
236
- input: config.id,
237
- state: config.snapshot,
238
- poll: async (_id, context) => (await inspect("wait", context)).snapshot,
239
- done: terminal,
240
- backoff: config.backoff,
241
- defaultTimeout: config.defaultTimeout,
242
- timeoutFromInput: config.timeoutFromId
141
+ var PUBLIC_CORS = {
142
+ "Access-Control-Allow-Origin": "*",
143
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
144
+ "Access-Control-Allow-Headers": DEFAULT_CORS_ALLOW_HEADERS
145
+ };
146
+ function oauthProtectedResourceRoute(config) {
147
+ const body = JSON.stringify({
148
+ resource: config.resource,
149
+ authorization_servers: config.authorizationServers,
150
+ ...config.scopesSupported && { scopes_supported: config.scopesSupported },
151
+ bearer_methods_supported: ["header"]
243
152
  });
244
- const definitions = { start, status, wait };
245
- const names = new Set;
246
- if (config.cancel) {
247
- const capability = config.cancel;
248
- definitions.cancel = defineRuntimeTool({
249
- ...common("cancel"),
250
- identity: identity("cancel", "DELETE"),
251
- input: config.id,
252
- output: AsyncOperationCancelResultSchema,
253
- handler: async (context) => {
254
- const inspected = await inspect("cancel", context);
255
- return capability.handler(inspected.state, context);
256
- }
257
- });
258
- }
259
- if (config.result) {
260
- const capability = config.result;
261
- definitions.result = defineRuntimeTool({
262
- ...common("result"),
263
- identity: identity("result", "GET"),
264
- input: config.id,
265
- output: capability.output,
266
- handler: async (context) => {
267
- const inspected = await inspect("result", context);
268
- if (!succeeded(inspected.snapshot)) {
269
- throw new AppError("OPERATION_NOT_SUCCEEDED", "Operation result is not available", 409);
270
- }
271
- return capability.handler(inspected.state, context);
272
- }
273
- });
274
- }
275
- if (config.artifacts) {
276
- const capability = config.artifacts;
277
- definitions.artifacts = defineRuntimeTool({
278
- ...common("artifacts"),
279
- identity: identity("artifacts", "GET"),
280
- input: config.id,
281
- output: capability.output,
282
- handler: async (context) => {
283
- const inspected = await inspect("artifacts", context);
284
- if (!succeeded(inspected.snapshot)) {
285
- throw new AppError("OPERATION_NOT_SUCCEEDED", "Operation artifacts are not available", 409);
286
- }
287
- return capability.handler(inspected.state, context);
288
- }
289
- });
290
- }
291
- for (const definition of Object.values(definitions)) {
292
- assertUniqueToolName(definition.name, names.has(definition.name), "in-process tool name");
293
- names.add(definition.name);
294
- }
295
153
  return {
296
- ...definitions,
297
- runtimeTools: Object.values(definitions),
298
- schemas: {
299
- id: config.id,
300
- snapshot: config.snapshot,
301
- cancelResult: AsyncOperationCancelResultSchema
154
+ method: "ALL",
155
+ path: metadataPath(config.resource),
156
+ handler: (req) => {
157
+ if (req.method === "OPTIONS")
158
+ return new Response(null, { status: 204, headers: PUBLIC_CORS });
159
+ if (req.method !== "GET")
160
+ return new Response("Method Not Allowed", { status: 405 });
161
+ return new Response(body, {
162
+ status: 200,
163
+ headers: { "Content-Type": "application/json", ...PUBLIC_CORS }
164
+ });
302
165
  }
303
166
  };
304
167
  }
305
- function adapterResult(capability, target, schema, value) {
306
- const parsed = schema.safeParse(value);
307
- if (!parsed.success) {
308
- throw new Error(`Async operation adapter for capability "${capability}" returned invalid ${target}`, { cause: parsed.error });
309
- }
310
- return parsed.data;
168
+ // src/server/oauth/provider.ts
169
+ import { randomUUID } from "node:crypto";
170
+ import { z } from "zod";
171
+ function isPlainDisplayCharacter(character) {
172
+ const codePoint = character.codePointAt(0);
173
+ return codePoint !== undefined && codePoint >= 32 && codePoint !== 127 && character !== "<" && character !== ">";
311
174
  }
312
- var NON_WIRE_STABLE_ZOD_TYPES = new Set([
313
- "catch",
314
- "default",
315
- "lazy",
316
- "pipe",
317
- "prefault",
318
- "readonly",
319
- "success",
320
- "transform"
321
- ]);
322
- function hasOverwriteCheck(value) {
323
- return isRecord(value) && isRecord(value._zod) && isRecord(value._zod.def) && value._zod.def.check === "overwrite";
175
+ var CimdClientMetadataSchema = z.object({
176
+ client_id: z.url(),
177
+ redirect_uris: z.array(z.url()).min(1),
178
+ client_name: z.string().trim().min(1).max(200).refine((value) => Array.from(value).every(isPlainDisplayCharacter), {
179
+ message: "client_name must be plain display text"
180
+ }),
181
+ token_endpoint_auth_method: z.literal("none"),
182
+ application_type: z.enum(["native", "web"]).optional()
183
+ });
184
+ function createSecureClientMetadataFetcher(policy = {}) {
185
+ assertPositiveInteger("cimd.maxBytes", policy.maxBytes ?? DEFAULT_CIMD_MAX_BYTES);
186
+ assertPositiveNumber("cimd.timeoutMs", policy.timeoutMs ?? DEFAULT_CIMD_TIMEOUT_MS);
187
+ assertNonNegativeInteger("cimd.maxRedirects", policy.maxRedirects ?? 3);
188
+ return {
189
+ fetch: (url, headers) => fetchPinnedDocument(url, {
190
+ maxBytes: policy.maxBytes ?? DEFAULT_CIMD_MAX_BYTES,
191
+ timeoutMs: policy.timeoutMs ?? DEFAULT_CIMD_TIMEOUT_MS,
192
+ maxRedirects: policy.maxRedirects ?? 3,
193
+ headers,
194
+ requireHttps: true
195
+ })
196
+ };
324
197
  }
325
- function assertWireStableIdSchema(schema, boundary) {
326
- const visited = new Set;
327
- const pending = [schema];
328
- while (pending.length > 0) {
329
- const value = pending.pop();
330
- if (typeof value !== "object" || value === null || visited.has(value))
331
- continue;
332
- visited.add(value);
333
- if (value instanceof z.ZodType) {
334
- const definition = value._zod.def;
335
- if (NON_WIRE_STABLE_ZOD_TYPES.has(definition.type) || "coerce" in definition && definition.coerce === true || "checks" in definition && Array.isArray(definition.checks) && definition.checks.some(hasOverwriteCheck)) {
336
- throw new Error(`${boundary} must use a wire-stable ID schema without transforms, coercion, defaults or overwrites; use binding: "adapted" with explicit ID adapters`);
337
- }
338
- pending.push(...Object.values(definition));
339
- continue;
340
- }
341
- if (Array.isArray(value)) {
342
- pending.push(...value);
343
- continue;
344
- }
345
- if (isRecord(value))
346
- pending.push(...Object.values(value));
198
+ var PUBLIC_CORS2 = {
199
+ "Access-Control-Allow-Origin": "*",
200
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
201
+ "Access-Control-Allow-Headers": DEFAULT_CORS_ALLOW_HEADERS
202
+ };
203
+ var AS_METADATA_PATH = "/.well-known/oauth-authorization-server";
204
+ var AUTH_CODE_TTL_MS = 60000;
205
+ var DEFAULT_CIMD_MAX_BYTES = 64 * 1024;
206
+ var DEFAULT_CIMD_TIMEOUT_MS = 5000;
207
+ function assertPositiveInteger(name, value) {
208
+ if (!Number.isInteger(value) || value < 1)
209
+ throw new Error(`${name} must be a positive integer`);
210
+ }
211
+ function assertNonNegativeInteger(name, value) {
212
+ if (!Number.isInteger(value) || value < 0) {
213
+ throw new Error(`${name} must be a non-negative integer`);
347
214
  }
348
215
  }
349
- function defineAsyncOperationContract(config) {
350
- assertWireStableIdSchema(config.id, "Async operation contract");
351
- const hasStartOutput = config.startOutput !== undefined;
352
- const hasIdFromStart = config.idFromStart !== undefined;
353
- if (hasStartOutput !== hasIdFromStart) {
354
- throw new Error("Async operation contract startOutput and idFromStart must be configured together");
216
+ function assertPositiveNumber(name, value) {
217
+ if (!Number.isFinite(value) || value <= 0)
218
+ throw new Error(`${name} must be positive`);
219
+ }
220
+ function absoluteClientUrl(clientId) {
221
+ try {
222
+ return new URL(clientId);
223
+ } catch {
224
+ return null;
355
225
  }
356
- const description = (capability) => config.descriptions?.[capability] ?? `${config.description}: ${capability}`;
357
- const endpoint = (capability, method, path, input, output) => {
358
- const scope = config.scopes?.[capability];
359
- const expose = config.expose?.[capability];
360
- return {
361
- method,
362
- path,
363
- desc: description(capability),
364
- input,
365
- output,
366
- ...scope !== undefined && { scope },
367
- ...expose !== undefined && { expose }
368
- };
369
- };
370
- const startOutput = config.startOutput ?? config.id;
371
- const endpoints = {
372
- start: endpoint("start", "POST", "/", config.startInput, startOutput),
373
- status: endpoint("status", "POST", "/status", config.id, config.snapshot),
374
- wait: endpoint("wait", "POST", "/wait", config.id, config.snapshot)
375
- };
376
- const capabilities = {
377
- start: "start",
378
- status: "status",
379
- wait: "wait"
380
- };
381
- const schemas = {
382
- startInput: config.startInput,
383
- startOutput,
384
- id: config.id,
385
- snapshot: config.snapshot,
386
- cancelResult: AsyncOperationCancelResultSchema
387
- };
388
- const inputFor = {
389
- status: (id) => adapterResult("status", "input", config.id, id),
390
- wait: (id) => adapterResult("wait", "input", config.id, id)
391
- };
392
- if (config.cancel) {
393
- endpoints.cancel = endpoint("cancel", "POST", "/cancel", config.id, AsyncOperationCancelResultSchema);
394
- capabilities.cancel = "cancel";
395
- schemas.cancel = AsyncOperationCancelResultSchema;
396
- inputFor.cancel = (id) => adapterResult("cancel", "input", config.id, id);
397
- }
398
- if (config.result) {
399
- endpoints.result = endpoint("result", "POST", "/result", config.id, config.result);
400
- capabilities.result = "result";
401
- schemas.result = config.result;
402
- inputFor.result = (id) => adapterResult("result", "input", config.id, id);
403
- }
404
- if (config.artifacts) {
405
- endpoints.artifacts = endpoint("artifacts", "POST", "/artifacts", config.id, config.artifacts);
406
- capabilities.artifacts = "artifacts";
407
- schemas.artifacts = config.artifacts;
408
- inputFor.artifacts = (id) => adapterResult("artifacts", "input", config.id, id);
226
+ }
227
+ function assertCimdClientId(clientId) {
228
+ const url = new URL(clientId);
229
+ if (url.protocol !== "https:")
230
+ throw new Error("CIMD client_id must use https");
231
+ if (url.username || url.password)
232
+ throw new Error("CIMD client_id cannot contain credentials");
233
+ if (url.hash)
234
+ throw new Error("CIMD client_id cannot contain a fragment");
235
+ if (url.search)
236
+ throw new Error("CIMD client_id cannot contain a query");
237
+ if (url.toString() !== clientId) {
238
+ throw new Error("CIMD client_id must be an exact canonical URL");
409
239
  }
410
- const contract = config.scope ? defineContract({ prefix: config.prefix, scope: config.scope }, endpoints) : defineContract({ prefix: config.prefix }, endpoints);
411
- const applicationStartOutput = config.startOutput;
412
- const applicationIdFromStart = config.idFromStart;
413
- const idFromStart = applicationStartOutput && applicationIdFromStart ? (output) => adapterResult("start", "id", config.id, applicationIdFromStart(output)) : (output) => adapterResult("start", "id", config.id, output);
414
- return {
415
- contract,
416
- capabilities,
417
- schemas,
418
- adapters: { idFromStart, inputFor }
419
- };
240
+ return url;
420
241
  }
421
- function bindContractAsyncOperation(config) {
422
- const endpoint = (key) => {
423
- const found = config.contract.endpoints[key];
424
- if (!found)
425
- throw new Error(`Contract async operation method "${key}" not found`);
426
- return found;
427
- };
428
- const start = endpoint(config.capabilities.start);
429
- const status = endpoint(config.capabilities.status);
430
- const wait = endpoint(config.capabilities.wait);
431
- if (!start.output || !status.output || !wait.output) {
432
- throw new Error("Contract async operation requires outputs for start, status and wait");
242
+ function parseAgeSeconds(headers) {
243
+ const raw = headers.get("age");
244
+ if (raw === null)
245
+ return 0;
246
+ const first = raw.split(",")[0]?.trim() ?? "";
247
+ return /^\d+$/.test(first) ? Number(first) : 0;
248
+ }
249
+ function responseFreshness(headers, policy, now) {
250
+ const maxTtl = policy.maxTtlMs ?? 3600000;
251
+ const cacheControl = headers.get("cache-control") ?? "";
252
+ if (/(?:^|,)\s*no-store\s*(?:,|$)/i.test(cacheControl)) {
253
+ return { freshnessMs: 0, store: false };
433
254
  }
434
- const startOutput = start.output;
435
- const statusOutput = status.output;
436
- if (wait.output !== statusOutput) {
437
- throw new Error('Contract async operation capability "wait" output must reuse the same schema instance as the status output');
255
+ if (/(?:^|,)\s*no-cache\s*(?:,|$)/i.test(cacheControl)) {
256
+ return { freshnessMs: 0, store: true };
438
257
  }
439
- const optionalCapabilities = [
440
- "cancel",
441
- "result",
442
- "artifacts"
443
- ];
444
- if (config.binding !== "adapted") {
445
- assertWireStableIdSchema(startOutput, "Direct contract async operation");
446
- if (status.input !== startOutput || wait.input !== startOutput) {
447
- const capability = status.input !== startOutput ? "status" : "wait";
448
- throw new Error(`Contract async operation capability "${capability}" input must reuse the same schema instance as the start output`);
449
- }
450
- const inputFor2 = {
451
- status: (id) => adapterResult("status", "input", startOutput, id),
452
- wait: (id) => adapterResult("wait", "input", startOutput, id)
258
+ const maxAge = cacheControl.match(/(?:^|,)\s*max-age=([^,\s]*)/i)?.[1];
259
+ if (maxAge !== undefined) {
260
+ if (!/^\d+$/.test(maxAge))
261
+ return { freshnessMs: 0, store: true };
262
+ const age = parseAgeSeconds(headers);
263
+ return {
264
+ freshnessMs: Math.max(0, Math.min(maxTtl, (Number(maxAge) - age) * 1000)),
265
+ store: true
453
266
  };
454
- for (const capability of optionalCapabilities) {
455
- const key = config.capabilities[capability];
456
- if (!key)
457
- continue;
458
- if (endpoint(key).input !== startOutput) {
459
- throw new Error(`Contract async operation capability "${capability}" input must reuse the same schema instance as the start output`);
460
- }
461
- inputFor2[capability] = (id) => adapterResult(capability, "input", startOutput, id);
462
- }
267
+ }
268
+ const expires = headers.get("expires");
269
+ if (expires !== null) {
270
+ const expiresAt = Date.parse(expires);
271
+ if (!Number.isFinite(expiresAt))
272
+ return { freshnessMs: 0, store: true };
273
+ const dateAt = Date.parse(headers.get("date") ?? "");
274
+ const ageMs = parseAgeSeconds(headers) * 1000;
275
+ const lifetime = Number.isFinite(dateAt) ? expiresAt - dateAt : expiresAt - now;
463
276
  return {
464
- contract: config.contract,
465
- handlers: config.handlers,
466
- capabilities: config.capabilities,
467
- schemas: { id: startOutput, snapshot: statusOutput },
468
- adapters: {
469
- idFromStart: (output) => adapterResult("start", "id", startOutput, output),
470
- inputFor: inputFor2
471
- }
277
+ freshnessMs: Math.max(0, Math.min(maxTtl, lifetime - ageMs)),
278
+ store: true
472
279
  };
473
280
  }
474
- const adapters = config.adapters;
475
- const idSchema = config.id;
476
- const adaptedInput = (capability, key, builder) => {
477
- const input = endpoint(key).input;
478
- if (!input) {
479
- throw new Error(`Contract async operation capability "${capability}" requires an input schema`);
281
+ return { freshnessMs: Math.min(maxTtl, policy.defaultTtlMs ?? 300000), store: true };
282
+ }
283
+ function safeClientName(value) {
284
+ if (value === undefined)
285
+ return;
286
+ const safe = Array.from(value).filter(isPlainDisplayCharacter).join("").trim().slice(0, 200);
287
+ return safe || undefined;
288
+ }
289
+ function parseScopes(value) {
290
+ if (!value)
291
+ return [];
292
+ return [...new Set(value.split(/\s+/u).filter(Boolean))];
293
+ }
294
+ function toRegisteredClient(metadata) {
295
+ return {
296
+ clientId: metadata.client_id,
297
+ redirectUris: metadata.redirect_uris,
298
+ ...metadata.client_name !== undefined && { clientName: metadata.client_name },
299
+ ...metadata.application_type !== undefined && {
300
+ applicationType: metadata.application_type
480
301
  }
481
- return (id) => adapterResult(capability, "input", input, builder(id));
482
302
  };
483
- const inputFor = {
484
- status: adaptedInput("status", config.capabilities.status, adapters.inputFor.status),
485
- wait: adaptedInput("wait", config.capabilities.wait, adapters.inputFor.wait)
303
+ }
304
+ function isJsonContentType(value) {
305
+ const mediaType = value.split(";", 1)[0]?.trim().toLowerCase();
306
+ return mediaType === "application/json" || mediaType?.endsWith("+json") === true;
307
+ }
308
+ function createCimdResolver(options) {
309
+ const policy = options.cache ?? {};
310
+ const now = options.now ?? Date.now;
311
+ const positives = new Map;
312
+ const negatives = new Map;
313
+ const inflight = new Map;
314
+ const maxEntries = policy.maxEntries ?? 256;
315
+ const maxResolutions = policy.maxResolutions ?? 120;
316
+ const maxResolutionsPerClient = policy.maxResolutionsPerClient ?? 10;
317
+ const resolutionWindowMs = policy.resolutionWindowMs ?? 60000;
318
+ let resolutionWindowStartedAt = now();
319
+ let resolutionsInWindow = 0;
320
+ const clientWindows = new Map;
321
+ const CLIENT_WINDOW_CAP = 4096;
322
+ assertPositiveInteger("cimd.cache.maxEntries", maxEntries);
323
+ assertPositiveInteger("cimd.cache.maxResolutions", maxResolutions);
324
+ assertPositiveInteger("cimd.cache.maxResolutionsPerClient", maxResolutionsPerClient);
325
+ assertPositiveInteger("cimd.cache.resolutionWindowMs", resolutionWindowMs);
326
+ assertNonNegativeInteger("cimd.cache.defaultTtlMs", policy.defaultTtlMs ?? 300000);
327
+ assertNonNegativeInteger("cimd.cache.maxTtlMs", policy.maxTtlMs ?? 3600000);
328
+ assertNonNegativeInteger("cimd.cache.negativeTtlMs", policy.negativeTtlMs ?? 1e4);
329
+ const emit = (event) => {
330
+ try {
331
+ options.onCacheEvent?.(event);
332
+ } catch {}
486
333
  };
487
- const optionalInput = (capability, key, builder) => {
488
- if (!key)
489
- return;
490
- if (!builder) {
491
- throw new Error(`Contract async operation capability "${capability}" requires an input adapter`);
334
+ const readCache = (key) => positives.get(key) ?? negatives.get(key);
335
+ const trimOldest = (pool) => {
336
+ while (pool.size > maxEntries) {
337
+ const oldest = pool.keys().next().value;
338
+ if (oldest === undefined)
339
+ break;
340
+ pool.delete(oldest);
492
341
  }
493
- inputFor[capability] = adaptedInput(capability, key, builder);
494
342
  };
495
- optionalInput("cancel", config.capabilities.cancel, "cancel" in adapters.inputFor ? adapters.inputFor.cancel : undefined);
496
- optionalInput("result", config.capabilities.result, "result" in adapters.inputFor ? adapters.inputFor.result : undefined);
497
- optionalInput("artifacts", config.capabilities.artifacts, "artifacts" in adapters.inputFor ? adapters.inputFor.artifacts : undefined);
498
- if (!idSchema) {
499
- throw new Error("Contract adapted async operation requires an id schema");
500
- }
501
- return {
502
- contract: config.contract,
503
- handlers: config.handlers,
504
- capabilities: config.capabilities,
505
- schemas: { id: idSchema, snapshot: statusOutput },
506
- adapters: {
507
- idFromStart: (output) => adapterResult("start", "id", idSchema, adapters.idFromStart(output)),
508
- inputFor
343
+ const touch = (key, value) => {
344
+ if (value.ok) {
345
+ negatives.delete(key);
346
+ positives.delete(key);
347
+ positives.set(key, value);
348
+ trimOldest(positives);
349
+ } else {
350
+ positives.delete(key);
351
+ negatives.delete(key);
352
+ negatives.set(key, value);
353
+ trimOldest(negatives);
509
354
  }
510
355
  };
356
+ const dropCache = (key) => {
357
+ positives.delete(key);
358
+ negatives.delete(key);
359
+ };
360
+ const resolveOne = async (clientId) => {
361
+ const url = assertCimdClientId(clientId);
362
+ const cached = readCache(clientId);
363
+ if (cached && cached.expiresAt > now()) {
364
+ touch(clientId, cached);
365
+ if (cached.ok) {
366
+ emit({ clientId, status: "hit", freshnessMs: cached.expiresAt - now() });
367
+ return cached.client;
368
+ }
369
+ emit({ clientId, status: "negative", freshnessMs: cached.expiresAt - now() });
370
+ throw new Error(cached.message);
371
+ }
372
+ emit({ clientId, status: "miss" });
373
+ const currentTime = now();
374
+ if (currentTime - resolutionWindowStartedAt >= resolutionWindowMs) {
375
+ resolutionWindowStartedAt = currentTime;
376
+ resolutionsInWindow = 0;
377
+ }
378
+ let clientWindow = clientWindows.get(clientId);
379
+ if (!clientWindow || currentTime - clientWindow.startedAt >= resolutionWindowMs) {
380
+ clientWindow = { startedAt: currentTime, count: 0 };
381
+ clientWindows.delete(clientId);
382
+ clientWindows.set(clientId, clientWindow);
383
+ while (clientWindows.size > CLIENT_WINDOW_CAP) {
384
+ const oldest = clientWindows.keys().next().value;
385
+ if (oldest === undefined)
386
+ break;
387
+ clientWindows.delete(oldest);
388
+ }
389
+ }
390
+ if (clientWindow.count >= maxResolutionsPerClient) {
391
+ throw new Error("CIMD metadata resolution rate limit exceeded for this client_id");
392
+ }
393
+ if (resolutionsInWindow >= maxResolutions) {
394
+ throw new Error("CIMD metadata resolution rate limit exceeded");
395
+ }
396
+ clientWindow.count += 1;
397
+ resolutionsInWindow += 1;
398
+ const conditionalHeaders = {};
399
+ if (cached?.ok && cached.etag)
400
+ conditionalHeaders["if-none-match"] = cached.etag;
401
+ if (cached?.ok && cached.lastModified) {
402
+ conditionalHeaders["if-modified-since"] = cached.lastModified;
403
+ }
404
+ try {
405
+ const response = await options.fetcher.fetch(url, conditionalHeaders);
406
+ const freshness = responseFreshness(response.headers, policy, now());
407
+ if (response.status === 304 && cached?.ok) {
408
+ const refreshed = {
409
+ ...cached,
410
+ expiresAt: now() + freshness.freshnessMs
411
+ };
412
+ if (freshness.store)
413
+ touch(clientId, refreshed);
414
+ else
415
+ dropCache(clientId);
416
+ emit({ clientId, status: "revalidated", freshnessMs: freshness.freshnessMs });
417
+ return refreshed.client;
418
+ }
419
+ if (response.status !== 200) {
420
+ throw new Error(`CIMD endpoint returned HTTP ${response.status}`);
421
+ }
422
+ const contentType = response.headers.get("content-type") ?? "";
423
+ if (!isJsonContentType(contentType)) {
424
+ throw new Error("CIMD endpoint must return a JSON media type");
425
+ }
426
+ const decoded = JSON.parse(new TextDecoder().decode(response.body));
427
+ const metadata = CimdClientMetadataSchema.parse(decoded);
428
+ if (metadata.client_id !== clientId) {
429
+ throw new Error("CIMD client_id does not exactly match the requested URL");
430
+ }
431
+ if (!metadata.redirect_uris.every((uri) => isRegistrableRedirectUri(uri, metadata.application_type))) {
432
+ throw new Error("CIMD redirect_uris contains a URI forbidden for the application type");
433
+ }
434
+ const client = toRegisteredClient(metadata);
435
+ if (freshness.store && Number.isFinite(freshness.freshnessMs)) {
436
+ touch(clientId, {
437
+ ok: true,
438
+ client,
439
+ expiresAt: now() + freshness.freshnessMs,
440
+ ...response.headers.get("etag") !== null && {
441
+ etag: response.headers.get("etag") ?? undefined
442
+ },
443
+ ...response.headers.get("last-modified") !== null && {
444
+ lastModified: response.headers.get("last-modified") ?? undefined
445
+ }
446
+ });
447
+ } else {
448
+ dropCache(clientId);
449
+ }
450
+ return client;
451
+ } catch (error) {
452
+ const message = error instanceof Error ? error.message : "CIMD metadata resolution failed";
453
+ touch(clientId, {
454
+ ok: false,
455
+ message,
456
+ expiresAt: now() + (policy.negativeTtlMs ?? 1e4)
457
+ });
458
+ throw error;
459
+ }
460
+ };
461
+ return async (clientId) => {
462
+ const pending = inflight.get(clientId);
463
+ if (pending)
464
+ return pending;
465
+ const created = resolveOne(clientId).finally(() => inflight.delete(clientId));
466
+ inflight.set(clientId, created);
467
+ return created;
468
+ };
511
469
  }
512
- // src/tools/download-core.ts
513
- import { basename, extname } from "node:path";
514
- var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
515
- var MIME_EXT = {
516
- "image/png": ".png",
517
- "image/jpeg": ".jpg",
518
- "image/webp": ".webp",
519
- "image/gif": ".gif",
520
- "video/mp4": ".mp4",
521
- "video/webm": ".webm",
522
- "audio/mpeg": ".mp3",
523
- "audio/wav": ".wav",
524
- "audio/ogg": ".ogg"
525
- };
526
-
527
- class DownloadOperationError extends Error {
528
- code;
529
- status;
530
- constructor(code, message, status) {
531
- super(message);
532
- this.code = code;
533
- this.status = status;
534
- this.name = "DownloadOperationError";
535
- }
536
- }
537
- function extensionFor(url, mime) {
538
- if (MIME_EXT[mime])
539
- return MIME_EXT[mime];
540
- try {
541
- const ext = extname(new URL(url).pathname).toLowerCase();
542
- if (ext)
543
- return ext;
544
- } catch {}
545
- return ".bin";
546
- }
547
- function baseNameFor(url) {
470
+ function json(body, status = 200, extraHeaders) {
471
+ return new Response(JSON.stringify(body), {
472
+ status,
473
+ headers: { "Content-Type": "application/json", ...PUBLIC_CORS2, ...extraHeaders }
474
+ });
475
+ }
476
+ function oauthError(error, description, status = 400) {
477
+ return json({ error, error_description: description }, status);
478
+ }
479
+ function isRegistrableRedirectUri(value, applicationType) {
548
480
  try {
549
- const path = new URL(url).pathname;
550
- return (basename(path, extname(path)) || "download").slice(0, 60);
481
+ const url = new URL(value);
482
+ if (url.username || url.password || url.hash)
483
+ return false;
484
+ if (url.protocol === "https:")
485
+ return true;
486
+ if (url.protocol !== "http:")
487
+ return false;
488
+ if (applicationType === "web")
489
+ return false;
490
+ const host = url.hostname.replace(/^\[|\]$/g, "");
491
+ return host === "127.0.0.1" || host === "::1" || host === "localhost";
551
492
  } catch {
552
- return "download";
553
- }
554
- }
555
- async function runDownloadOperation(config) {
556
- const res = await fetchGuarded(new URL(config.url), config.allowPrivateHosts ?? false, {
557
- timeoutMs: config.timeoutMs,
558
- signal: config.signal
559
- });
560
- if (!res.ok) {
561
- throw new DownloadOperationError("DOWNLOAD_HTTP_ERROR", `HTTP ${res.status}`, 502);
562
- }
563
- const mimeType = mediaTypeEssence(res.headers.get("content-type")) || "application/octet-stream";
564
- const max = config.maxBytes ?? DEFAULT_MAX_BYTES;
565
- const declared = Number(res.headers.get("content-length") ?? 0);
566
- if (declared > max) {
567
- await res.body?.cancel();
568
- throw new DownloadOperationError("DOWNLOAD_TOO_LARGE", `file exceeds the ${max}-byte cap`, 413);
493
+ return false;
569
494
  }
570
- const path = config.path ?? `${baseNameFor(config.url)}${extensionFor(config.url, mimeType)}`;
571
- return config.files.write(path, res.body ?? new Uint8Array, {
572
- maxBytes: max,
573
- mediaType: mimeType,
574
- signal: config.signal
575
- });
576
495
  }
577
-
578
- // src/tools/managed-file-error.ts
579
- var SAFE_MANAGED_FILE_MESSAGES = {
580
- FILE_INVALID_PATH: "Invalid managed-file path",
581
- FILE_OUTSIDE_ROOT: "Managed-file path escapes its boundary",
582
- FILE_NOT_FOUND: "Managed file not found",
583
- FILE_NOT_REGULAR: "Managed path is not a regular file",
584
- FILE_INSPECTION_REJECTED: "Managed file rejected by inspection",
585
- FILE_TOO_LARGE: "Managed file exceeds the configured size limit",
586
- FILE_EXISTS: "Managed file already exists"
587
- };
588
- function managedFileAppError(error) {
589
- if (!(error instanceof ManagedFileError))
590
- return null;
591
- switch (error.code) {
592
- case "FILE_INVALID_PATH":
593
- case "FILE_OUTSIDE_ROOT":
594
- case "FILE_NOT_FOUND":
595
- case "FILE_NOT_REGULAR":
596
- case "FILE_INSPECTION_REJECTED":
597
- case "FILE_TOO_LARGE":
598
- case "FILE_EXISTS":
599
- return new AppError(error.code, SAFE_MANAGED_FILE_MESSAGES[error.code], STITCH_ERROR_STATUS[error.code]);
600
- case "FILE_IO_ERROR":
601
- return null;
496
+ function isLoopbackRedirectUri(value) {
497
+ try {
498
+ const url = new URL(value);
499
+ if (url.protocol !== "http:")
500
+ return false;
501
+ const host = url.hostname.replace(/^\[|\]$/g, "");
502
+ return host === "127.0.0.1" || host === "::1" || host === "localhost";
503
+ } catch {
504
+ return false;
602
505
  }
603
506
  }
604
- function normalizeFileToolError(error) {
605
- return managedFileAppError(error) ?? normalizeError(error);
507
+ function redirectWith(uri, params) {
508
+ const url = new URL(uri);
509
+ for (const [key, value] of Object.entries(params))
510
+ url.searchParams.set(key, value);
511
+ return new Response(null, { status: 302, headers: { Location: url.toString() } });
606
512
  }
607
-
608
- // src/tools/define-download-tool.ts
609
- function defineDownloadTool(config) {
610
- return defineRuntimeTool({
611
- name: config.name ?? "download",
612
- description: config.description,
613
- identity: managedNativeIdentity(config.identity, "POST"),
614
- input: config.input,
615
- output: ManagedFileRefSchema,
616
- transports: config.transports,
617
- annotations: config.annotations,
618
- present: config.present,
619
- handler: async (context) => {
620
- const url = await config.resolveUrl(context.input, context);
621
- if (!url) {
622
- throw new AppError("DOWNLOAD_NOT_FOUND", "Nothing to download", 404);
623
- }
624
- try {
625
- return await runDownloadOperation({
626
- url,
627
- files: config.files,
628
- path: config.pathFromInput?.(context.input),
629
- allowPrivateHosts: config.allowPrivateHosts,
630
- maxBytes: config.maxBytes,
631
- timeoutMs: config.timeoutMs,
632
- signal: context.signal
633
- });
634
- } catch (error) {
635
- if (error instanceof DownloadOperationError) {
636
- throw new AppError(error.code, `Download failed: ${error.message}`, error.status);
637
- }
638
- const managedError = managedFileAppError(error);
639
- if (managedError)
640
- throw managedError;
641
- throw error;
513
+ async function readForm(req) {
514
+ const contentType = req.headers.get("content-type") ?? "";
515
+ if (contentType.includes("application/json")) {
516
+ const body = await req.json().catch(() => null);
517
+ const params = new URLSearchParams;
518
+ if (isRecord(body)) {
519
+ for (const [key, value] of Object.entries(body)) {
520
+ if (typeof value === "string")
521
+ params.set(key, value);
642
522
  }
643
523
  }
644
- });
524
+ return params;
525
+ }
526
+ return new URLSearchParams(await req.text());
645
527
  }
646
- // src/tools/define-upload-tool.ts
647
- import { z as z2 } from "zod";
648
-
649
- // src/tools/upload-core.ts
650
- function runUploadOperation(files, path, upload, signal) {
651
- signal?.throwIfAborted();
652
- return files.read(path, { signal }).then((source) => upload(source, signal));
653
- }
654
-
655
- // src/tools/define-upload-tool.ts
656
- var UploadToolInputSchema = z2.object({
657
- path: ManagedFilePathSchema.describe("Path relative to the configured file boundary")
658
- });
659
- function defineUploadTool(config) {
660
- return defineRuntimeTool({
661
- name: config.name ?? "upload",
662
- description: config.description,
663
- identity: managedNativeIdentity(config.identity, "POST"),
664
- input: UploadToolInputSchema,
665
- output: config.output,
666
- transports: config.transports,
667
- annotations: config.annotations,
668
- present: config.present,
669
- handler: async (context) => {
528
+ function mountOAuthProvider(config) {
529
+ const base = config.basePath ?? "/oauth";
530
+ const ttl = config.accessTokenTtlSec ?? 3600;
531
+ const registerPath = `${base}/register`;
532
+ const authorizePath = `${base}/authorize`;
533
+ const tokenPath = `${base}/token`;
534
+ const clientRegistration = config.clientRegistration ?? {};
535
+ const dcrRegistry = clientRegistration.dcr || undefined;
536
+ const dcrEnabled = dcrRegistry !== undefined;
537
+ const cimdConfig = clientRegistration.cimd === false ? undefined : clientRegistration.cimd ?? {};
538
+ const cimdEnabled = cimdConfig !== undefined;
539
+ const resolveCimd = cimdEnabled ? createCimdResolver({
540
+ cache: cimdConfig.cache,
541
+ fetcher: cimdConfig.fetcher ?? createSecureClientMetadataFetcher(),
542
+ onCacheEvent: cimdConfig.onCacheEvent
543
+ }) : undefined;
544
+ const resolveClient = async (clientId) => {
545
+ const preRegistered = await clientRegistration.preRegistered?.get(clientId);
546
+ if (preRegistered)
547
+ return preRegistered.clientId === clientId ? preRegistered : null;
548
+ const clientUrl = absoluteClientUrl(clientId);
549
+ if (clientUrl) {
550
+ if (!resolveCimd || clientUrl.protocol !== "https:")
551
+ return null;
670
552
  try {
671
- return await runUploadOperation(config.files, context.input.path, (source) => config.upload(source, context), context.signal);
672
- } catch (error) {
673
- const managedError = managedFileAppError(error);
674
- if (managedError)
675
- throw managedError;
676
- throw error;
553
+ return await resolveCimd(clientId);
554
+ } catch {
555
+ return null;
677
556
  }
678
557
  }
679
- });
680
- }
681
- // src/tools/view-file.ts
682
- import { extname as extname2 } from "node:path";
683
- var MAX_INLINE_BYTES = 20 * 1024 * 1024;
684
- var EXT_MIME = {
685
- ".png": "image/png",
686
- ".jpg": "image/jpeg",
687
- ".jpeg": "image/jpeg",
688
- ".gif": "image/gif",
689
- ".webp": "image/webp",
690
- ".mp3": "audio/mpeg",
691
- ".wav": "audio/wav",
692
- ".ogg": "audio/ogg",
693
- ".m4a": "audio/mp4",
694
- ".mp4": "video/mp4",
695
- ".webm": "video/webm",
696
- ".mov": "video/quicktime"
697
- };
698
- async function fetchSource(pathOrUrl, options, maxBytes) {
699
- const extMime = EXT_MIME[extname2(pathOrUrl).toLowerCase()];
700
- if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
701
- const url = new URL(pathOrUrl);
702
- const res = await fetchGuarded(url, options.allowPrivateHosts ?? false, {
703
- timeoutMs: options.timeoutMs,
704
- signal: options.signal
705
- });
706
- if (!res.ok)
707
- throw new AppError("VIEW_HTTP_ERROR", `HTTP ${res.status}`, 502);
708
- const headerMime = mediaTypeEssence(res.headers.get("content-type"));
709
- const mimeType = headerMime || extMime || "application/octet-stream";
710
- if (mimeType.startsWith("video/")) {
711
- await res.body?.cancel();
712
- return { video: true, mimeType, bytesRead: 0 };
713
- }
714
- const declared = Number(res.headers.get("content-length") ?? 0);
715
- if (declared > maxBytes) {
716
- await res.body?.cancel();
717
- return { tooLarge: true, mimeType, bytesRead: 0 };
558
+ const dcrClient = await dcrRegistry?.get(clientId);
559
+ return dcrClient?.clientId === clientId ? dcrClient : null;
560
+ };
561
+ const redirectToClient = (uri, params) => redirectWith(uri, { ...params, iss: config.issuer });
562
+ const metadataRoute = {
563
+ method: "ALL",
564
+ path: AS_METADATA_PATH,
565
+ handler: (req) => {
566
+ if (req.method === "OPTIONS")
567
+ return new Response(null, { status: 204, headers: PUBLIC_CORS2 });
568
+ return json({
569
+ issuer: config.issuer,
570
+ authorization_endpoint: `${config.issuer}${authorizePath}`,
571
+ token_endpoint: `${config.issuer}${tokenPath}`,
572
+ ...dcrEnabled && { registration_endpoint: `${config.issuer}${registerPath}` },
573
+ response_types_supported: ["code"],
574
+ grant_types_supported: config.refreshTokens ? ["authorization_code", "refresh_token"] : ["authorization_code"],
575
+ code_challenge_methods_supported: ["S256"],
576
+ token_endpoint_auth_methods_supported: ["none"],
577
+ ...cimdEnabled && { client_id_metadata_document_supported: true },
578
+ authorization_response_iss_parameter_supported: true,
579
+ ...config.scopesSupported && { scopes_supported: config.scopesSupported }
580
+ });
718
581
  }
719
- const buffer = await readCapped(res, maxBytes);
720
- return buffer ? { buffer, mimeType, bytesRead: buffer.length } : { tooLarge: true, mimeType, bytesRead: maxBytes };
721
- }
722
- if (!options.files) {
723
- throw new AppError("FILE_INVALID_PATH", "local file paths are disabled — set files to allow them", 400);
724
- }
725
- options.signal?.throwIfAborted();
726
- if (!extMime) {
727
- throw new AppError("FILE_INSPECTION_REJECTED", "refusing to read a non-media file", 422);
728
- }
729
- try {
730
- const source = await options.files.read(pathOrUrl, {
731
- maxBytes,
732
- signal: options.signal
733
- });
734
- const buffer = Buffer.from(source.bytes);
735
- return {
736
- buffer,
737
- mimeType: source.ref.mediaType ?? extMime,
738
- bytesRead: buffer.length
739
- };
740
- } catch (error) {
741
- if (error instanceof ManagedFileError && error.code === "FILE_TOO_LARGE") {
742
- return { tooLarge: true, mimeType: extMime, bytesRead: maxBytes };
582
+ };
583
+ const registerRoute = {
584
+ method: "ALL",
585
+ path: registerPath,
586
+ handler: async (req) => {
587
+ if (req.method === "OPTIONS")
588
+ return new Response(null, { status: 204, headers: PUBLIC_CORS2 });
589
+ if (req.method !== "POST")
590
+ return oauthError("invalid_request", "POST required", 405);
591
+ const meta = await req.json().catch(() => null);
592
+ if (!isRecord(meta)) {
593
+ return oauthError("invalid_client_metadata", "Body must be a JSON object");
594
+ }
595
+ const rawAppType = meta.application_type;
596
+ if (rawAppType !== undefined && rawAppType !== "native" && rawAppType !== "web") {
597
+ return oauthError("invalid_client_metadata", "application_type must be 'native' or 'web'");
598
+ }
599
+ const applicationType = rawAppType;
600
+ const redirectUris = meta.redirect_uris;
601
+ const tokenEndpointAuthMethod = meta.token_endpoint_auth_method ?? "none";
602
+ if (tokenEndpointAuthMethod !== "none") {
603
+ return oauthError("invalid_client_metadata", 'Only public clients with token_endpoint_auth_method "none" are supported');
604
+ }
605
+ if (!Array.isArray(redirectUris) || redirectUris.length === 0 || !redirectUris.every((u) => typeof u === "string" && isRegistrableRedirectUri(u, applicationType))) {
606
+ return oauthError("invalid_redirect_uri", applicationType === "web" ? "redirect_uris must be a non-empty array of absolute https URLs (a web client cannot register an http loopback URI)" : "redirect_uris must be a non-empty array of absolute https URLs (http is allowed only on a loopback host)");
607
+ }
608
+ if (!dcrRegistry)
609
+ throw new Error("DCR route mounted without a DCR registry");
610
+ const client = await dcrRegistry.register({
611
+ redirectUris,
612
+ clientName: typeof meta.client_name === "string" ? meta.client_name : undefined,
613
+ tokenEndpointAuthMethod,
614
+ ...applicationType && { applicationType }
615
+ });
616
+ return json({
617
+ client_id: client.clientId,
618
+ redirect_uris: client.redirectUris,
619
+ token_endpoint_auth_method: "none",
620
+ grant_types: config.refreshTokens ? ["authorization_code", "refresh_token"] : ["authorization_code"],
621
+ response_types: ["code"],
622
+ ...client.clientName && { client_name: client.clientName },
623
+ ...client.applicationType && { application_type: client.applicationType }
624
+ }, 201);
743
625
  }
744
- throw error;
745
- }
746
- }
747
- async function resolveMediaWithinBudget(pathOrUrl, options, maxBytes) {
748
- if (maxBytes <= 0) {
749
- return {
750
- content: [
751
- { type: "text", text: `[media] total inline budget exhausted — ${pathOrUrl}` }
752
- ],
753
- bytes: 0
754
- };
755
- }
756
- const extMime = EXT_MIME[extname2(pathOrUrl).toLowerCase()];
757
- if (extMime?.startsWith("video/")) {
758
- return {
759
- content: [{ type: "text", text: `[video] ${extMime} — ${pathOrUrl}` }],
760
- bytes: 0
761
- };
762
- }
763
- const result = await fetchSource(pathOrUrl, options, maxBytes);
764
- if ("video" in result) {
765
- return {
766
- content: [{ type: "text", text: `[video] ${result.mimeType} — ${pathOrUrl}` }],
767
- bytes: result.bytesRead
768
- };
769
- }
770
- if ("tooLarge" in result) {
771
- return {
772
- content: [
773
- { type: "text", text: `[${result.mimeType}] too large to inline — ${pathOrUrl}` }
774
- ],
775
- bytes: result.bytesRead
776
- };
777
- }
778
- const { buffer, mimeType } = result;
779
- const sizeKb = (buffer.length / 1024).toFixed(0);
780
- if (mimeType.startsWith("image/")) {
781
- return {
782
- content: [
783
- {
784
- type: "image",
785
- data: buffer.toString("base64"),
786
- mimeType,
787
- annotations: { audience: ["user", "assistant"], priority: 0.9 }
626
+ };
627
+ const authorizeRoute = {
628
+ method: "ALL",
629
+ path: authorizePath,
630
+ handler: async (req) => {
631
+ if (req.method === "OPTIONS")
632
+ return new Response(null, { status: 204, headers: PUBLIC_CORS2 });
633
+ const url = new URL(req.url);
634
+ const p = url.searchParams;
635
+ const clientId = p.get("client_id");
636
+ const redirectUri = p.get("redirect_uri");
637
+ const responseType = p.get("response_type");
638
+ const codeChallenge = p.get("code_challenge");
639
+ const codeChallengeMethod = p.get("code_challenge_method") ?? "S256";
640
+ const resource = p.get("resource");
641
+ const scope = p.get("scope") ?? undefined;
642
+ const state = p.get("state") ?? undefined;
643
+ if (!clientId || !redirectUri) {
644
+ return oauthError("invalid_request", "client_id and redirect_uri are required");
645
+ }
646
+ const client = await resolveClient(clientId);
647
+ if (!client)
648
+ return oauthError("invalid_client", "Unknown client_id", 401);
649
+ if (!client.redirectUris.includes(redirectUri)) {
650
+ return oauthError("invalid_request", "redirect_uri does not match a registered URI");
651
+ }
652
+ if (responseType !== "code") {
653
+ return redirectToClient(redirectUri, {
654
+ error: "unsupported_response_type",
655
+ ...state && { state }
656
+ });
657
+ }
658
+ if (!codeChallenge || codeChallengeMethod !== "S256") {
659
+ return redirectToClient(redirectUri, {
660
+ error: "invalid_request",
661
+ error_description: "PKCE S256 code_challenge is required",
662
+ ...state && { state }
663
+ });
664
+ }
665
+ if (!resource) {
666
+ return redirectToClient(redirectUri, {
667
+ error: "invalid_target",
668
+ error_description: "resource parameter is required",
669
+ ...state && { state }
670
+ });
671
+ }
672
+ if (resource !== config.resource) {
673
+ return redirectToClient(redirectUri, {
674
+ error: "invalid_target",
675
+ error_description: "resource is not served by this authorization server",
676
+ ...state && { state }
677
+ });
678
+ }
679
+ const requestedScopes = parseScopes(scope);
680
+ const supportedScopes = new Set(config.scopesSupported ?? []);
681
+ if (config.scopesSupported && requestedScopes.some((requestedScope2) => !supportedScopes.has(requestedScope2))) {
682
+ return redirectToClient(redirectUri, {
683
+ error: "invalid_scope",
684
+ error_description: "One or more requested scopes are not supported",
685
+ ...state && { state }
686
+ });
687
+ }
688
+ const requestedScope = requestedScopes.length > 0 ? requestedScopes.join(" ") : undefined;
689
+ const authRequest = {
690
+ clientId,
691
+ redirectUri,
692
+ scope: requestedScope,
693
+ resource,
694
+ state,
695
+ isLoopbackRedirect: isLoopbackRedirectUri(redirectUri),
696
+ ...safeClientName(client.clientName) !== undefined && {
697
+ clientName: safeClientName(client.clientName)
788
698
  },
789
- { type: "text", text: `[image] ${mimeType}, ${sizeKb}KB` }
790
- ],
791
- bytes: buffer.length
792
- };
793
- }
794
- if (mimeType.startsWith("audio/")) {
795
- return {
796
- content: [
797
- {
798
- type: "audio",
799
- data: buffer.toString("base64"),
800
- mimeType,
801
- annotations: { audience: ["user", "assistant"], priority: 0.9 }
699
+ ...client.applicationType !== undefined && {
700
+ applicationType: client.applicationType
802
701
  },
803
- { type: "text", text: `[audio] ${mimeType}, ${sizeKb}KB` }
804
- ],
805
- bytes: buffer.length
806
- };
807
- }
808
- return {
809
- content: [{ type: "text", text: `[${mimeType}] ${sizeKb}KB — ${pathOrUrl}` }],
810
- bytes: buffer.length
811
- };
812
- }
813
- async function resolveMedia(pathOrUrl, options = {}) {
814
- return (await resolveMediaWithinBudget(pathOrUrl, options, MAX_INLINE_BYTES)).content;
815
- }
816
- async function runViewFileOperation(paths, options = {}, signal) {
817
- const list = Array.isArray(paths) ? paths : [paths];
818
- const content = [];
819
- const errors = [];
820
- let remainingBytes = MAX_INLINE_BYTES;
821
- for (const pathOrUrl of list) {
822
- signal?.throwIfAborted();
823
- try {
824
- const resolved = await resolveMediaWithinBudget(pathOrUrl, { ...options, signal }, remainingBytes);
825
- remainingBytes -= resolved.bytes;
826
- content.push(...resolved.content);
827
- } catch (error) {
828
- if (signal?.aborted)
829
- throw signal.reason ?? error;
830
- const message = normalizeFileToolError(error).message;
831
- errors.push({ path: pathOrUrl, message });
832
- content.push({ type: "text", text: `[${pathOrUrl}] Error: ${message}` });
702
+ ...absoluteClientUrl(clientId)?.protocol === "https:" && {
703
+ clientOrigin: new URL(clientId).origin
704
+ }
705
+ };
706
+ const result = await config.authorizeUser(req, authRequest);
707
+ if (result instanceof Response)
708
+ return result;
709
+ if (!Array.isArray(result.approvedScopes) || !result.approvedScopes.every((scope2) => typeof scope2 === "string")) {
710
+ throw new Error("authorizeUser must return approvedScopes as an array of strings");
711
+ }
712
+ const approvedScopes = [...new Set(result.approvedScopes)];
713
+ if (approvedScopes.some((approvedScope2) => !requestedScopes.includes(approvedScope2) || config.scopesSupported !== undefined && !supportedScopes.has(approvedScope2))) {
714
+ throw new Error("authorizeUser approvedScopes must be a subset of requested scopes");
715
+ }
716
+ const approvedScope = approvedScopes.length > 0 ? approvedScopes.join(" ") : undefined;
717
+ const code = randomUUID();
718
+ await config.codes.save(code, {
719
+ clientId,
720
+ redirectUri,
721
+ codeChallenge,
722
+ codeChallengeMethod: "S256",
723
+ resource,
724
+ scope: approvedScope,
725
+ userId: result.userId,
726
+ expiresAt: Date.now() + AUTH_CODE_TTL_MS
727
+ });
728
+ return redirectToClient(redirectUri, { code, ...state && { state } });
833
729
  }
834
- }
835
- return ViewFileOutputSchema.parse({ content, errors });
836
- }
837
- function mountViewFile(server, options = {}) {
838
- server.registerTool("view_file", {
839
- description: "View media (image, audio, video) by URL or local path — returns it as content you can SEE / HEAR. Pass several paths to view multiple files at once. Use it on a generation `output` url to inspect the result.",
840
- inputSchema: ViewFileInputSchema.shape,
841
- annotations: { title: "View Media", readOnlyHint: true, idempotentHint: true }
842
- }, async (args) => {
843
- const result = await runViewFileOperation(args.paths, options);
844
- return { content: result.content };
845
- });
846
- }
847
-
848
- // src/tools/define-view-file-tool.ts
849
- function defineViewFileTool(config) {
850
- const defaults = {
851
- mcp: (output) => ({ content: output.content }),
852
- agent: (output) => ({
853
- type: "content",
854
- value: output.content.map((part) => {
855
- if (part.type === "text")
856
- return part;
857
- return {
858
- type: "file",
859
- data: { type: "data", data: part.data },
860
- mediaType: part.mimeType
861
- };
862
- })
863
- })
864
730
  };
865
- return defineRuntimeTool({
866
- name: config.name ?? "view_file",
867
- description: config.description,
868
- identity: managedNativeIdentity(config.identity, "GET"),
869
- input: ViewFileInputSchema,
870
- output: ViewFileOutputSchema,
871
- transports: config.transports,
872
- annotations: config.annotations ?? {
873
- title: "View Media",
874
- readOnlyHint: true,
875
- idempotentHint: true
876
- },
877
- present: { ...defaults, ...config.present },
878
- handler: (context) => runViewFileOperation(context.input.paths, {
879
- files: config.files,
880
- allowPrivateHosts: config.allowPrivateHosts,
881
- timeoutMs: config.timeoutMs
882
- }, context.signal)
731
+ const issueAccessToken = (userId, audience, clientId, scope) => signJwt({ scope, client_id: clientId }, config.signingSecret, {
732
+ issuer: config.issuer,
733
+ audience,
734
+ subject: userId,
735
+ expiresInSec: ttl
883
736
  });
737
+ const tokenRoute = {
738
+ method: "ALL",
739
+ path: tokenPath,
740
+ handler: async (req) => {
741
+ if (req.method === "OPTIONS")
742
+ return new Response(null, { status: 204, headers: PUBLIC_CORS2 });
743
+ if (req.method !== "POST")
744
+ return oauthError("invalid_request", "POST required", 405);
745
+ const form = await readForm(req);
746
+ const grantType = form.get("grant_type");
747
+ if (grantType === "authorization_code") {
748
+ const code = form.get("code");
749
+ const verifier = form.get("code_verifier");
750
+ const redirectUri = form.get("redirect_uri");
751
+ const clientId = form.get("client_id");
752
+ if (!code || !verifier || !redirectUri || !clientId) {
753
+ return oauthError("invalid_request", "code, code_verifier, redirect_uri, client_id required");
754
+ }
755
+ const data = await config.codes.take(code);
756
+ if (!data)
757
+ return oauthError("invalid_grant", "Unknown or used authorization code");
758
+ if (data.expiresAt < Date.now())
759
+ return oauthError("invalid_grant", "Authorization code expired");
760
+ if (data.clientId !== clientId)
761
+ return oauthError("invalid_grant", "client_id mismatch");
762
+ if (data.redirectUri !== redirectUri)
763
+ return oauthError("invalid_grant", "redirect_uri mismatch");
764
+ if (!await verifyPkce(verifier, data.codeChallenge)) {
765
+ return oauthError("invalid_grant", "PKCE verification failed");
766
+ }
767
+ const accessToken = await issueAccessToken(data.userId, data.resource, data.clientId, data.scope);
768
+ let refreshToken;
769
+ if (config.refreshTokens) {
770
+ refreshToken = randomUUID();
771
+ await config.refreshTokens.save(refreshToken, {
772
+ clientId: data.clientId,
773
+ resource: data.resource,
774
+ scope: data.scope,
775
+ userId: data.userId
776
+ });
777
+ }
778
+ return json({
779
+ access_token: accessToken,
780
+ token_type: "Bearer",
781
+ expires_in: ttl,
782
+ ...data.scope && { scope: data.scope },
783
+ ...refreshToken && { refresh_token: refreshToken }
784
+ });
785
+ }
786
+ if (grantType === "refresh_token") {
787
+ if (!config.refreshTokens) {
788
+ return oauthError("unsupported_grant_type", "refresh_token grant is not enabled");
789
+ }
790
+ const token = form.get("refresh_token");
791
+ const clientId = form.get("client_id");
792
+ if (!token || !clientId)
793
+ return oauthError("invalid_request", "refresh_token and client_id required");
794
+ const data = await config.refreshTokens.take(token);
795
+ if (!data)
796
+ return oauthError("invalid_grant", "Unknown or used refresh token");
797
+ if (data.clientId !== clientId)
798
+ return oauthError("invalid_grant", "client_id mismatch");
799
+ const accessToken = await issueAccessToken(data.userId, data.resource, data.clientId, data.scope);
800
+ const newRefresh = randomUUID();
801
+ await config.refreshTokens.save(newRefresh, data);
802
+ return json({
803
+ access_token: accessToken,
804
+ token_type: "Bearer",
805
+ expires_in: ttl,
806
+ ...data.scope && { scope: data.scope },
807
+ refresh_token: newRefresh
808
+ });
809
+ }
810
+ return oauthError("unsupported_grant_type", `Unsupported grant_type: ${grantType ?? "none"}`);
811
+ }
812
+ };
813
+ return dcrEnabled ? [metadataRoute, registerRoute, authorizeRoute, tokenRoute] : [metadataRoute, authorizeRoute, tokenRoute];
884
814
  }
885
815
  // src/tools/lifecycle.ts
886
816
  function composeToolLifecycle(...hooks) {
@@ -901,7 +831,6 @@ function composeToolLifecycle(...hooks) {
901
831
  };
902
832
  }
903
833
  // src/tools/list-names.ts
904
- var TOOL_TRANSPORTS = ["MCP", "AGENT", "CLI"];
905
834
  function listToolNames(surface) {
906
835
  const entries = new Map;
907
836
  for (const transport of TOOL_TRANSPORTS) {
@@ -931,14 +860,7 @@ function listToolNames(surface) {
931
860
  function listContractToolNames(contracts) {
932
861
  return listToolNames({ services: contracts.map(contractOnlyService) });
933
862
  }
934
- // src/tools/mcp.ts
935
- import {
936
- createRequestStateCodec,
937
- McpServer
938
- } from "@modelcontextprotocol/server";
939
- import { z as z5 } from "zod";
940
-
941
- // src/tools/mcp-app.ts
863
+ // src/tools/mcp/app.ts
942
864
  import { readFileSync } from "node:fs";
943
865
  import { fileURLToPath } from "node:url";
944
866
  var RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
@@ -958,8 +880,7 @@ function inlineMcpAppBundle(html) {
958
880
  }).join(",")}};`);
959
881
  return html.replace(EXT_APPS_BUNDLE_PLACEHOLDER, () => bundle);
960
882
  }
961
-
962
- // src/tools/mcp-catalog.ts
883
+ // src/tools/mcp/catalog.ts
963
884
  import { createHash } from "node:crypto";
964
885
  var MCP_CATALOG_META_KEY = "stitchkit/catalog";
965
886
  var stampCache = new WeakMap;
@@ -980,7 +901,7 @@ function mcpCatalogStamp(surface) {
980
901
  const descriptors = [
981
902
  ...surface.contractTools,
982
903
  ...surface.runtimeTools.map((entry) => entry.descriptor)
983
- ].sort((left, right) => compareCodepoints(left.mountable.name, right.mountable.name));
904
+ ].sort((left, right) => compareCodeUnits(left.mountable.name, right.mountable.name));
984
905
  const bytes = serializeCanonicalJson(descriptors.map(toolFingerprint));
985
906
  const stamp = {
986
907
  digest: createHash("sha256").update(bytes).digest("hex").slice(0, 16),
@@ -1015,8 +936,22 @@ function stampToolResult(result, stamp) {
1015
936
  return result;
1016
937
  return { ...result, _meta: { ...result._meta, ...mcpCatalogMeta(stamp) } };
1017
938
  }
939
+ // src/tools/mcp/handler.ts
940
+ import {
941
+ createMcpHandler as createSdkMcpHandler,
942
+ hostHeaderValidationResponse,
943
+ localhostAllowedHostnames,
944
+ localhostAllowedOrigins,
945
+ originValidationResponse
946
+ } from "@modelcontextprotocol/server";
947
+
948
+ // src/tools/mcp/mount.ts
949
+ import {
950
+ createRequestStateCodec,
951
+ McpServer
952
+ } from "@modelcontextprotocol/server";
1018
953
 
1019
- // src/tools/mcp-prepare.ts
954
+ // src/tools/mcp/prepare.ts
1020
955
  function textBlock(text) {
1021
956
  return [{ type: "text", text }];
1022
957
  }
@@ -1047,21 +982,25 @@ function prepareMcpSurface(services, config = {}) {
1047
982
  return prepareMcpTools(tools, config);
1048
983
  }
1049
984
  function prepareMcpTools(tools, config = {}) {
1050
- const prepared = prepareProjectedMcpTools(tools.map((mountable) => ({
1051
- tool: mountable,
1052
- name: mountable.name,
1053
- ...mountable.method.paramsSchema !== undefined && {
1054
- paramsSchema: mountable.method.paramsSchema
1055
- },
1056
- ...mountable.method.inputSchema !== undefined && {
1057
- inputSchema: mountable.method.inputSchema
1058
- },
1059
- ...mountable.method.outputSchema !== undefined && {
1060
- outputSchema: mountable.method.outputSchema
1061
- },
1062
- shouldExtend: mountable.shouldExtend,
1063
- ...mountable.method.mcp !== undefined && { mcp: mountable.method.mcp }
1064
- })), config).map((entry) => {
985
+ const prepared = prepareProjectedMcpTools(tools.map((mountable) => {
986
+ const outputSchema = toolSurfaceOutputSchema(mountable.method);
987
+ return {
988
+ tool: mountable,
989
+ name: mountable.name,
990
+ ...mountable.method.paramsSchema !== undefined && {
991
+ paramsSchema: mountable.method.paramsSchema
992
+ },
993
+ ...mountable.method.inputSchema !== undefined && {
994
+ inputSchema: mountable.method.inputSchema
995
+ },
996
+ ...mountable.method.toolView?.defaults !== undefined && {
997
+ inputDefaults: mountable.method.toolView.defaults
998
+ },
999
+ ...outputSchema !== undefined && { outputSchema },
1000
+ shouldExtend: mountable.shouldExtend,
1001
+ ...mountable.method.mcp !== undefined && { mcp: mountable.method.mcp }
1002
+ };
1003
+ }), config).map((entry) => {
1065
1004
  Object.freeze(entry.tool);
1066
1005
  return Object.freeze({
1067
1006
  mountable: entry.tool,
@@ -1105,7 +1044,13 @@ function prepareMcpServerSurface(surface, config = {}) {
1105
1044
  return Object.freeze({ contractTools, runtimeTools: Object.freeze(runtimeTools) });
1106
1045
  }
1107
1046
 
1108
- // src/tools/mcp-round.ts
1047
+ // src/tools/mcp/register.ts
1048
+ import {
1049
+ isCallToolResult
1050
+ } from "@modelcontextprotocol/server";
1051
+ import { z as z3 } from "zod";
1052
+
1053
+ // src/tools/mcp/round.ts
1109
1054
  import {
1110
1055
  acceptedContent,
1111
1056
  CLIENT_INFO_META_KEY,
@@ -1113,9 +1058,9 @@ import {
1113
1058
  inputResponse,
1114
1059
  PROTOCOL_VERSION_META_KEY
1115
1060
  } from "@modelcontextprotocol/server";
1116
- import { z as z3 } from "zod";
1061
+ import { z as z2 } from "zod";
1117
1062
 
1118
- // src/tools/mcp-progress.ts
1063
+ // src/tools/mcp/progress.ts
1119
1064
  function mcpProgressToken(context) {
1120
1065
  const token = context.mcpReq._meta?.progressToken;
1121
1066
  return typeof token === "string" || typeof token === "number" ? token : undefined;
@@ -1146,7 +1091,7 @@ function createMcpProgressReporter(context) {
1146
1091
  };
1147
1092
  }
1148
1093
 
1149
- // src/tools/mcp-round.ts
1094
+ // src/tools/mcp/round.ts
1150
1095
  function transportContext(context, toolName, outcome, round) {
1151
1096
  const protocolVersionValue = isRecord(context.mcpReq.envelope) ? Reflect.get(context.mcpReq.envelope, PROTOCOL_VERSION_META_KEY) : undefined;
1152
1097
  const protocolVersion = typeof protocolVersionValue === "string" ? protocolVersionValue : undefined;
@@ -1213,7 +1158,7 @@ function roundPlanDigest(requests) {
1213
1158
  plan: requests.map((request) => ({
1214
1159
  key: request.key,
1215
1160
  message: request.message,
1216
- schema: z3.toJSONSchema(request.schema, { io: "input", unrepresentable: "any" })
1161
+ schema: z2.toJSONSchema(request.schema, { io: "input", unrepresentable: "any" })
1217
1162
  }))
1218
1163
  });
1219
1164
  }
@@ -1221,7 +1166,10 @@ async function resolveRequests(options, policy) {
1221
1166
  const declared = staticInputRounds(policy);
1222
1167
  if (declared)
1223
1168
  return { requests: declared, guarded: false };
1224
- const parsed = parseToolCallArguments(options.tool.method, options.rawArgs, options.coerceJsonArgs ?? true);
1169
+ const parsed = parseToolCallArguments(options.tool.method, options.rawArgs, {
1170
+ coerceJson: options.coerceJsonArgs ?? true,
1171
+ toolSurface: true
1172
+ });
1225
1173
  if (!parsed.ok) {
1226
1174
  return { requests: [], guarded: false };
1227
1175
  }
@@ -1391,7 +1339,7 @@ async function resolveMcpRound(options) {
1391
1339
  };
1392
1340
  }
1393
1341
 
1394
- // src/tools/mcp-trace.ts
1342
+ // src/tools/mcp/trace.ts
1395
1343
  import {
1396
1344
  BAGGAGE_META_KEY,
1397
1345
  TRACEPARENT_META_KEY,
@@ -1423,73 +1371,102 @@ function runInMcpRequestContext(context, toolName, body) {
1423
1371
  return runWithRequestContext(request, body);
1424
1372
  }
1425
1373
 
1426
- // src/tools/native-mcp.ts
1427
- import {
1428
- isCallToolResult
1429
- } from "@modelcontextprotocol/server";
1430
- import { z as z4 } from "zod";
1431
- function mountPreparedRuntimeMcp(server, tools, config) {
1432
- for (const { definition, descriptor } of tools) {
1433
- const { mountable } = descriptor;
1434
- const toolConfig = {
1435
- description: definition.description,
1436
- inputSchema: z4.looseObject({}).meta(presentationMetadata(descriptor.inputSchema))
1374
+ // src/tools/mcp/register.ts
1375
+ function registerMcpTool(server, presentation, descriptor, config) {
1376
+ const { mountable } = descriptor;
1377
+ const toolConfig = {
1378
+ description: presentation.description,
1379
+ inputSchema: z3.looseObject({}).meta(presentationMetadata(descriptor.inputSchema))
1380
+ };
1381
+ if (descriptor.outputSchema)
1382
+ toolConfig.outputSchema = descriptor.outputSchema;
1383
+ if (presentation.annotations)
1384
+ toolConfig.annotations = presentation.annotations;
1385
+ if (presentation.ui) {
1386
+ toolConfig._meta = {
1387
+ ui: presentation.ui,
1388
+ "ui/resourceUri": presentation.ui.resourceUri
1437
1389
  };
1438
- if (descriptor.outputSchema)
1439
- toolConfig.outputSchema = descriptor.outputSchema;
1440
- if (definition.annotations)
1441
- toolConfig.annotations = definition.annotations;
1442
- if (definition.ui) {
1443
- toolConfig._meta = {
1444
- ui: definition.ui,
1445
- "ui/resourceUri": definition.ui.resourceUri
1446
- };
1447
- }
1448
- server.registerTool(definition.name, stampToolRegistration(toolConfig, config.catalog), async (rawArgs, mcpContext) => stampToolResult(await runInMcpRequestContext(mcpContext, definition.name, async () => {
1449
- const args = isRecord(rawArgs) ? rawArgs : {};
1450
- const runTool = (tool, toolArgs, roundContext) => executeToolMethod(tool.method, definition.name, toolArgs, { ...config.context, ...roundContext, source: "mcp" }, config.hooks, config.lifecycle, config.coerceJsonArgs ?? true, config.onOutputStrip ? (paths) => config.onOutputStrip?.(definition.name, paths) : undefined);
1390
+ }
1391
+ const { name } = presentation;
1392
+ const format = config.formatResult;
1393
+ server.registerTool(name, stampToolRegistration(toolConfig, config.catalog), async (rawArgs, mcpContext) => stampToolResult(await runInMcpRequestContext(mcpContext, name, async () => {
1394
+ const args = isRecord(rawArgs) ? rawArgs : {};
1395
+ try {
1451
1396
  const round = await resolveMcpRound({
1452
1397
  tool: mountable,
1453
1398
  rawArgs: args,
1454
1399
  context: mcpContext,
1455
1400
  policy: mountable.method.mcp,
1456
1401
  runtime: config.multiRoundRuntime,
1457
- runTool,
1458
- formatFailure: (result) => config.formatResult(result, "none", definition.name)
1402
+ runTool: config.runTool,
1403
+ formatFailure: (result2) => format(result2, "none", name)
1459
1404
  });
1460
1405
  if (round.kind === "response")
1461
1406
  return round.response;
1462
- if (!definition.present?.mcp) {
1463
- const result = await runTool(mountable, args, round.context);
1464
- return config.formatResult(result, descriptor.outputMode, definition.name);
1407
+ const { present } = presentation;
1408
+ if (!present) {
1409
+ const result2 = await config.runTool(mountable, args, round.context);
1410
+ return format(result2, descriptor.outputMode, name);
1465
1411
  }
1466
- const presentedResult = await executeToolMethod(mountable.method, definition.name, args, { ...config.context, ...round.context, source: "mcp" }, config.hooks, config.lifecycle, config.coerceJsonArgs ?? true, config.onOutputStrip ? (paths) => config.onOutputStrip?.(definition.name, paths) : undefined, undefined, async (data) => {
1467
- const presented = await definition.present?.mcp?.(data);
1468
- if (!presented) {
1469
- throw new AppError("INTERNAL_SERVER_ERROR", "Runtime MCP presenter did not return a result", 500);
1470
- }
1471
- if ("structuredContent" in presented || "isError" in presented) {
1472
- throw new AppError("INTERNAL_SERVER_ERROR", "Runtime MCP presenter cannot set framework-owned structuredContent or isError", 500);
1473
- }
1474
- const structured = config.formatResult({ ok: true, data }, descriptor.outputMode, definition.name).structuredContent;
1475
- const response = structured === undefined ? presented : { ...presented, structuredContent: structured };
1476
- if (!isCallToolResult(response)) {
1477
- throw new AppError("INTERNAL_SERVER_ERROR", "Runtime MCP presenter returned an invalid CallToolResult", 500);
1412
+ const result = await config.runTool(mountable, args, round.context, {
1413
+ finalizeOutput: async (data) => {
1414
+ const presented = await present(data);
1415
+ if (!isRecord(presented)) {
1416
+ throw new AppError("INTERNAL_SERVER_ERROR", "Runtime MCP presenter did not return a result", 500);
1417
+ }
1418
+ if ("structuredContent" in presented || "isError" in presented) {
1419
+ throw new AppError("INTERNAL_SERVER_ERROR", "Runtime MCP presenter cannot set framework-owned structuredContent or isError", 500);
1420
+ }
1421
+ const structured = format({ ok: true, data }, descriptor.outputMode, name).structuredContent;
1422
+ const response = structured === undefined ? presented : { ...presented, structuredContent: structured };
1423
+ if (!isCallToolResult(response)) {
1424
+ throw new AppError("INTERNAL_SERVER_ERROR", "Runtime MCP presenter returned an invalid CallToolResult", 500);
1425
+ }
1426
+ return response;
1478
1427
  }
1479
- return response;
1480
1428
  });
1481
- if (!presentedResult.ok) {
1482
- return config.formatResult(presentedResult, "none", definition.name);
1483
- }
1484
- if (!isCallToolResult(presentedResult.data)) {
1429
+ if (!result.ok)
1430
+ return format(result, "none", name);
1431
+ if (!isCallToolResult(result.data)) {
1485
1432
  throw new Error("[stitchkit] Runtime MCP presenter invariant failed");
1486
1433
  }
1487
- return presentedResult.data;
1488
- }), config.catalog));
1434
+ return result.data;
1435
+ } catch (err) {
1436
+ return format(toolResultFromError(err), "none", name);
1437
+ }
1438
+ }), config.catalog));
1439
+ }
1440
+
1441
+ // src/tools/mcp/runtime-tools.ts
1442
+ function mountPreparedRuntimeMcp(server, tools, config) {
1443
+ const runTool = createToolRunner({
1444
+ source: "mcp",
1445
+ toolSurface: true,
1446
+ context: config.context,
1447
+ hooks: config.hooks,
1448
+ lifecycle: config.lifecycle,
1449
+ coerceJsonArgs: config.coerceJsonArgs,
1450
+ onOutputStrip: config.onOutputStrip
1451
+ });
1452
+ for (const { definition, descriptor } of tools) {
1453
+ const present = definition.present?.mcp;
1454
+ registerMcpTool(server, {
1455
+ name: definition.name,
1456
+ description: definition.description,
1457
+ annotations: definition.annotations,
1458
+ ui: definition.ui,
1459
+ ...present && { present: (data) => present(data) }
1460
+ }, descriptor, {
1461
+ runTool,
1462
+ formatResult: config.formatResult,
1463
+ catalog: config.catalog,
1464
+ multiRoundRuntime: config.multiRoundRuntime
1465
+ });
1489
1466
  }
1490
1467
  }
1491
1468
 
1492
- // src/tools/mcp.ts
1469
+ // src/tools/mcp/mount.ts
1493
1470
  function validateMcpSchemas(config) {
1494
1471
  prepareMcpSurface(config.services, {
1495
1472
  extend: config.extend,
@@ -1513,51 +1490,27 @@ function mountMcp(mcpServer, services, config = {}) {
1513
1490
  function mountPreparedMcp(mcpServer, prepared, config = {}) {
1514
1491
  const runTool = createToolRunner({
1515
1492
  source: "mcp",
1493
+ toolSurface: true,
1516
1494
  extend: config.extend,
1517
1495
  context: config.context,
1518
1496
  hooks: config.hooks,
1519
1497
  lifecycle: config.lifecycle,
1520
- errorHint: config.errorHint,
1521
1498
  coerceJsonArgs: config.coerceJsonArgs,
1522
1499
  onOutputStrip: config.onOutputStrip
1523
1500
  });
1524
1501
  for (const descriptor of prepared) {
1525
- const { mountable } = descriptor;
1526
- const toolConfig = {
1527
- description: mountable.method.desc,
1528
- inputSchema: z5.looseObject({}).meta(presentationMetadata(descriptor.inputSchema))
1529
- };
1530
- if (descriptor.outputSchema)
1531
- toolConfig.outputSchema = descriptor.outputSchema;
1532
- if (mountable.method.annotations) {
1533
- toolConfig.annotations = mountable.method.annotations;
1534
- }
1535
- if (mountable.method.ui) {
1536
- toolConfig._meta = {
1537
- ui: mountable.method.ui,
1538
- "ui/resourceUri": mountable.method.ui.resourceUri
1539
- };
1540
- }
1541
- mcpServer.registerTool(mountable.name, stampToolRegistration(toolConfig, config.catalog), async (rawArgs, mcpContext) => stampToolResult(await runInMcpRequestContext(mcpContext, mountable.name, async () => {
1542
- const args = isRecord(rawArgs) ? rawArgs : {};
1543
- try {
1544
- const round = await resolveMcpRound({
1545
- tool: mountable,
1546
- rawArgs: args,
1547
- context: mcpContext,
1548
- policy: mountable.method.mcp,
1549
- runtime: config.multiRoundRuntime,
1550
- runTool,
1551
- formatFailure: (result2) => formatMcpResult(result2, "none", mountable.name, config.errorHint)
1552
- });
1553
- if (round.kind === "response")
1554
- return round.response;
1555
- const result = await runTool(mountable, args, round.context);
1556
- return formatMcpResult(result, descriptor.outputMode, mountable.name, config.errorHint);
1557
- } catch (err) {
1558
- return formatMcpResult(toolResultFromError(err), "none", mountable.name, config.errorHint);
1559
- }
1560
- }), config.catalog));
1502
+ const { method, name } = descriptor.mountable;
1503
+ registerMcpTool(mcpServer, {
1504
+ name,
1505
+ description: method.desc,
1506
+ annotations: method.annotations,
1507
+ ui: method.ui
1508
+ }, descriptor, {
1509
+ runTool,
1510
+ formatResult: (result, mode, toolName) => formatMcpResult(result, mode, toolName, config.errorHint),
1511
+ catalog: config.catalog,
1512
+ multiRoundRuntime: config.multiRoundRuntime
1513
+ });
1561
1514
  }
1562
1515
  }
1563
1516
  function buildMcpServer(config, auth) {
@@ -1675,56 +1628,8 @@ function mountMcpResource(server, resource) {
1675
1628
  ]
1676
1629
  }));
1677
1630
  }
1678
- // src/tools/mcp-handler.ts
1679
- import {
1680
- createMcpHandler as createSdkMcpHandler,
1681
- hostHeaderValidationResponse,
1682
- localhostAllowedHostnames,
1683
- localhostAllowedOrigins,
1684
- originValidationResponse
1685
- } from "@modelcontextprotocol/server";
1686
-
1687
- // src/tools/oauth-metadata.ts
1688
- var PROTECTED_RESOURCE_PATH = "/.well-known/oauth-protected-resource";
1689
- function metadataPath(resource) {
1690
- const { pathname } = new URL(resource);
1691
- return pathname === "/" ? PROTECTED_RESOURCE_PATH : `${PROTECTED_RESOURCE_PATH}${pathname}`;
1692
- }
1693
- function protectedResourceMetadataUrl(resource) {
1694
- return `${new URL(resource).origin}${metadataPath(resource)}`;
1695
- }
1696
- function wwwAuthenticateHeader(resource) {
1697
- return `Bearer resource_metadata="${protectedResourceMetadataUrl(resource)}"`;
1698
- }
1699
- var PUBLIC_CORS = {
1700
- "Access-Control-Allow-Origin": "*",
1701
- "Access-Control-Allow-Methods": "GET, OPTIONS",
1702
- "Access-Control-Allow-Headers": DEFAULT_CORS_ALLOW_HEADERS
1703
- };
1704
- function oauthProtectedResourceRoute(config) {
1705
- const body = JSON.stringify({
1706
- resource: config.resource,
1707
- authorization_servers: config.authorizationServers,
1708
- ...config.scopesSupported && { scopes_supported: config.scopesSupported },
1709
- bearer_methods_supported: ["header"]
1710
- });
1711
- return {
1712
- method: "ALL",
1713
- path: metadataPath(config.resource),
1714
- handler: (req) => {
1715
- if (req.method === "OPTIONS")
1716
- return new Response(null, { status: 204, headers: PUBLIC_CORS });
1717
- if (req.method !== "GET")
1718
- return new Response("Method Not Allowed", { status: 405 });
1719
- return new Response(body, {
1720
- status: 200,
1721
- headers: { "Content-Type": "application/json", ...PUBLIC_CORS }
1722
- });
1723
- }
1724
- };
1725
- }
1726
1631
 
1727
- // src/tools/mcp-handler.ts
1632
+ // src/tools/mcp/handler.ts
1728
1633
  function jsonRpcError(code, message, status, headers) {
1729
1634
  return Response.json({ jsonrpc: "2.0", error: { code, message }, id: null }, { status, headers });
1730
1635
  }
@@ -1858,7 +1763,7 @@ function createMcpHttpRoute(config) {
1858
1763
  handler: (request) => config.handler.fetch(request)
1859
1764
  };
1860
1765
  }
1861
- // src/tools/mcp-stdio.ts
1766
+ // src/tools/mcp/stdio.ts
1862
1767
  import { serveStdio } from "@modelcontextprotocol/server/stdio";
1863
1768
  async function createStdioMcpServer(config) {
1864
1769
  const auth = await config.auth;
@@ -1867,7 +1772,7 @@ async function createStdioMcpServer(config) {
1867
1772
  maxSubscriptions: 0
1868
1773
  });
1869
1774
  }
1870
- // src/tools/mcp-stdio-signals.ts
1775
+ // src/tools/mcp/stdio-signals.ts
1871
1776
  var bound = new WeakSet;
1872
1777
  function bindStdioProcessSignals(handle, options = {}) {
1873
1778
  if (bound.has(handle)) {
@@ -1902,900 +1807,1033 @@ function bindStdioProcessSignals(handle, options = {}) {
1902
1807
  if (!started)
1903
1808
  bound.delete(handle);
1904
1809
  };
1905
- const close = () => {
1906
- const hadStarted = started;
1907
- removeListeners();
1908
- if (!hadStarted)
1909
- resolveChain();
1810
+ const close = () => {
1811
+ const hadStarted = started;
1812
+ removeListeners();
1813
+ if (!hadStarted)
1814
+ resolveChain();
1815
+ };
1816
+ const run = async (signal) => {
1817
+ try {
1818
+ await options.onClose?.(signal);
1819
+ } catch (error) {
1820
+ reportSignalError("prepare", error, options.onError);
1821
+ }
1822
+ try {
1823
+ await handle.close();
1824
+ } catch (error) {
1825
+ rejectChain(error);
1826
+ reportSignalError("close", error, options.onError);
1827
+ removeListeners();
1828
+ return;
1829
+ }
1830
+ resolveChain();
1831
+ try {
1832
+ await options.onComplete?.();
1833
+ } catch (error) {
1834
+ reportSignalError("complete", error, options.onError);
1835
+ } finally {
1836
+ removeListeners();
1837
+ }
1838
+ };
1839
+ const onSignal = (signal) => {
1840
+ if (!started) {
1841
+ started = true;
1842
+ sameTurnAsStart = true;
1843
+ queueMicrotask(() => {
1844
+ sameTurnAsStart = false;
1845
+ });
1846
+ run(signal).catch((error) => reportSignalError("close", error, options.onError));
1847
+ return;
1848
+ }
1849
+ if (sameTurnAsStart)
1850
+ return;
1851
+ removeListeners();
1852
+ guardSignalCallback(() => options.onRepeatedSignal?.(signal), "close", options.onError);
1853
+ let restored = false;
1854
+ try {
1855
+ restored = source.raiseDefault(signal);
1856
+ } catch (error) {
1857
+ reportSignalError("close", error, options.onError);
1858
+ }
1859
+ if (!restored) {
1860
+ guardSignalCallback(() => options.onEscalationBlocked?.(signal), "close", options.onError);
1861
+ }
1862
+ };
1863
+ for (const signal of signals) {
1864
+ const handler = () => onSignal(signal);
1865
+ handlers.push([signal, handler]);
1866
+ source.on(signal, handler);
1867
+ }
1868
+ return { promise, close };
1869
+ }
1870
+ // src/tools/operations/async-operation-id.ts
1871
+ import { z as z4 } from "zod";
1872
+ function adapterResult(capability, target, schema, value) {
1873
+ const parsed = schema.safeParse(value);
1874
+ if (!parsed.success) {
1875
+ throw new Error(`Async operation adapter for capability "${capability}" returned invalid ${target}`, { cause: parsed.error });
1876
+ }
1877
+ return parsed.data;
1878
+ }
1879
+ var NON_WIRE_STABLE_ZOD_TYPES = new Set([
1880
+ "catch",
1881
+ "default",
1882
+ "lazy",
1883
+ "pipe",
1884
+ "prefault",
1885
+ "readonly",
1886
+ "success",
1887
+ "transform"
1888
+ ]);
1889
+ function hasOverwriteCheck(value) {
1890
+ return isRecord(value) && isRecord(value._zod) && isRecord(value._zod.def) && value._zod.def.check === "overwrite";
1891
+ }
1892
+ function assertWireStableIdSchema(schema, boundary) {
1893
+ const visited = new Set;
1894
+ const pending = [schema];
1895
+ while (pending.length > 0) {
1896
+ const value = pending.pop();
1897
+ if (typeof value !== "object" || value === null || visited.has(value))
1898
+ continue;
1899
+ visited.add(value);
1900
+ if (value instanceof z4.ZodType) {
1901
+ const definition = value._zod.def;
1902
+ if (NON_WIRE_STABLE_ZOD_TYPES.has(definition.type) || "coerce" in definition && definition.coerce === true || "checks" in definition && Array.isArray(definition.checks) && definition.checks.some(hasOverwriteCheck)) {
1903
+ throw new Error(`${boundary} must use a wire-stable ID schema without transforms, coercion, defaults or overwrites; use binding: "adapted" with explicit ID adapters`);
1904
+ }
1905
+ pending.push(...Object.values(definition));
1906
+ continue;
1907
+ }
1908
+ if (Array.isArray(value)) {
1909
+ pending.push(...value);
1910
+ continue;
1911
+ }
1912
+ if (isRecord(value))
1913
+ pending.push(...Object.values(value));
1914
+ }
1915
+ }
1916
+
1917
+ // src/tools/operations/async-operation-binding.ts
1918
+ function bindContractAsyncOperation(config) {
1919
+ const endpoint = (key) => {
1920
+ const found = config.contract.endpoints[key];
1921
+ if (!found)
1922
+ throw new Error(`Contract async operation method "${key}" not found`);
1923
+ return found;
1924
+ };
1925
+ const start = endpoint(config.capabilities.start);
1926
+ const status = endpoint(config.capabilities.status);
1927
+ const wait = endpoint(config.capabilities.wait);
1928
+ if (!start.output || !status.output || !wait.output) {
1929
+ throw new Error("Contract async operation requires outputs for start, status and wait");
1930
+ }
1931
+ const startOutput = start.output;
1932
+ const statusOutput = status.output;
1933
+ if (wait.output !== statusOutput) {
1934
+ throw new Error('Contract async operation capability "wait" output must reuse the same schema instance as the status output');
1935
+ }
1936
+ const resolved = {
1937
+ endpoint,
1938
+ status,
1939
+ wait,
1940
+ startOutput,
1941
+ statusOutput
1942
+ };
1943
+ if (config.binding !== "adapted")
1944
+ return bindDirect(config, resolved);
1945
+ return bindAdapted(config, resolved);
1946
+ }
1947
+ var OPTIONAL_CAPABILITIES = [
1948
+ "cancel",
1949
+ "result",
1950
+ "artifacts"
1951
+ ];
1952
+ function bindDirect(config, { endpoint, status, wait, startOutput, statusOutput }) {
1953
+ assertWireStableIdSchema(startOutput, "Direct contract async operation");
1954
+ if (status.input !== startOutput || wait.input !== startOutput) {
1955
+ const capability = status.input !== startOutput ? "status" : "wait";
1956
+ throw new Error(`Contract async operation capability "${capability}" input must reuse the same schema instance as the start output`);
1957
+ }
1958
+ const inputFor = {
1959
+ status: (id) => adapterResult("status", "input", startOutput, id),
1960
+ wait: (id) => adapterResult("wait", "input", startOutput, id)
1910
1961
  };
1911
- const run = async (signal) => {
1912
- try {
1913
- await options.onClose?.(signal);
1914
- } catch (error) {
1915
- reportSignalError("prepare", error, options.onError);
1916
- }
1917
- try {
1918
- await handle.close();
1919
- } catch (error) {
1920
- rejectChain(error);
1921
- reportSignalError("close", error, options.onError);
1922
- removeListeners();
1923
- return;
1962
+ for (const capability of OPTIONAL_CAPABILITIES) {
1963
+ const key = config.capabilities[capability];
1964
+ if (!key)
1965
+ continue;
1966
+ if (endpoint(key).input !== startOutput) {
1967
+ throw new Error(`Contract async operation capability "${capability}" input must reuse the same schema instance as the start output`);
1924
1968
  }
1925
- resolveChain();
1926
- try {
1927
- await options.onComplete?.();
1928
- } catch (error) {
1929
- reportSignalError("complete", error, options.onError);
1930
- } finally {
1931
- removeListeners();
1969
+ inputFor[capability] = (id) => adapterResult(capability, "input", startOutput, id);
1970
+ }
1971
+ return {
1972
+ contract: config.contract,
1973
+ handlers: config.handlers,
1974
+ capabilities: config.capabilities,
1975
+ schemas: { id: startOutput, snapshot: statusOutput },
1976
+ adapters: {
1977
+ idFromStart: (output) => adapterResult("start", "id", startOutput, output),
1978
+ inputFor
1932
1979
  }
1933
1980
  };
1934
- const onSignal = (signal) => {
1935
- if (!started) {
1936
- started = true;
1937
- sameTurnAsStart = true;
1938
- queueMicrotask(() => {
1939
- sameTurnAsStart = false;
1940
- });
1941
- run(signal).catch((error) => reportSignalError("close", error, options.onError));
1942
- return;
1981
+ }
1982
+ function bindAdapted(config, { endpoint, statusOutput }) {
1983
+ const adapters = config.adapters;
1984
+ const idSchema = config.id;
1985
+ const adaptedInput = (capability, key, builder) => {
1986
+ const input = endpoint(key).input;
1987
+ if (!input) {
1988
+ throw new Error(`Contract async operation capability "${capability}" requires an input schema`);
1943
1989
  }
1944
- if (sameTurnAsStart)
1990
+ return (id) => adapterResult(capability, "input", input, builder(id));
1991
+ };
1992
+ const inputFor = {
1993
+ status: adaptedInput("status", config.capabilities.status, adapters.inputFor.status),
1994
+ wait: adaptedInput("wait", config.capabilities.wait, adapters.inputFor.wait)
1995
+ };
1996
+ const optionalInput = (capability, key, builder) => {
1997
+ if (!key)
1945
1998
  return;
1946
- removeListeners();
1947
- guardSignalCallback(() => options.onRepeatedSignal?.(signal), "close", options.onError);
1948
- let restored = false;
1949
- try {
1950
- restored = source.raiseDefault(signal);
1951
- } catch (error) {
1952
- reportSignalError("close", error, options.onError);
1953
- }
1954
- if (!restored) {
1955
- guardSignalCallback(() => options.onEscalationBlocked?.(signal), "close", options.onError);
1999
+ if (!builder) {
2000
+ throw new Error(`Contract async operation capability "${capability}" requires an input adapter`);
1956
2001
  }
2002
+ inputFor[capability] = adaptedInput(capability, key, builder);
1957
2003
  };
1958
- for (const signal of signals) {
1959
- const handler = () => onSignal(signal);
1960
- handlers.push([signal, handler]);
1961
- source.on(signal, handler);
2004
+ optionalInput("cancel", config.capabilities.cancel, "cancel" in adapters.inputFor ? adapters.inputFor.cancel : undefined);
2005
+ optionalInput("result", config.capabilities.result, "result" in adapters.inputFor ? adapters.inputFor.result : undefined);
2006
+ optionalInput("artifacts", config.capabilities.artifacts, "artifacts" in adapters.inputFor ? adapters.inputFor.artifacts : undefined);
2007
+ if (!idSchema) {
2008
+ throw new Error("Contract adapted async operation requires an id schema");
1962
2009
  }
1963
- return { promise, close };
1964
- }
1965
- // src/tools/mount-download.ts
1966
- import { z as z6 } from "zod";
1967
-
1968
- // src/tools/native-result.ts
1969
- function textResult(text, isError = false) {
1970
- const content = [{ type: "text", text }];
1971
- return isError ? { content, isError: true } : { content };
1972
- }
1973
-
1974
- // src/tools/mount-download.ts
1975
- function mountDownload(server, config) {
1976
- const name = config.name ?? "download";
1977
- assertToolName(name, "<native>", "download");
1978
- server.registerTool(name, { description: config.description, inputSchema: z6.object(config.inputSchema) }, async (rawArgs) => {
1979
- const args = isRecord(rawArgs) ? rawArgs : {};
1980
- try {
1981
- const url = await config.resolveUrl(args);
1982
- if (!url)
1983
- return textResult("Nothing to download.", true);
1984
- const result = await runDownloadOperation({
1985
- url,
1986
- files: config.files,
1987
- path: config.pathFromArgs?.(args),
1988
- allowPrivateHosts: config.allowPrivateHosts,
1989
- maxBytes: config.maxBytes,
1990
- timeoutMs: config.timeoutMs
1991
- });
1992
- return textResult(JSON.stringify(result, null, 2));
1993
- } catch (error) {
1994
- if (error instanceof DownloadOperationError) {
1995
- return textResult(`Download failed [${error.code}]: ${error.message}`, true);
1996
- }
1997
- const normalized = normalizeFileToolError(error);
1998
- return textResult(`Download failed [${normalized.code}]: ${normalized.message}`, true);
1999
- }
2000
- });
2001
- }
2002
- // src/tools/mount-upload.ts
2003
- function mountUpload(server, config) {
2004
- const name = config.name ?? "upload";
2005
- assertToolName(name, "<native>", "upload");
2006
- server.registerTool(name, {
2007
- description: config.description,
2008
- inputSchema: {
2009
- path: ManagedFilePathSchema.describe("Path relative to the configured file boundary")
2010
- }
2011
- }, async (rawArgs) => {
2012
- const args = isRecord(rawArgs) ? rawArgs : {};
2013
- const path = typeof args.path === "string" ? args.path : "";
2014
- if (!path)
2015
- return textResult("Provide `path`.", true);
2016
- try {
2017
- const uploaded = await runUploadOperation(config.files, path, config.upload);
2018
- return textResult(JSON.stringify(uploaded ?? null, null, 2));
2019
- } catch (error) {
2020
- const normalized = normalizeFileToolError(error);
2021
- return textResult(`Upload failed [${normalized.code}]: ${normalized.message}`, true);
2022
- }
2023
- });
2024
- }
2025
- // src/tools/mount-wait.ts
2026
- import { z as z7 } from "zod";
2027
- function mountWait(server, config) {
2028
- const name = config.name ?? "wait";
2029
- assertToolName(name, "<native>", "wait");
2030
- server.registerTool(name, { description: config.description, inputSchema: z7.object(config.inputSchema) }, async (rawArgs, mcpContext) => {
2031
- const args = isRecord(rawArgs) ? rawArgs : {};
2032
- const report = createMcpProgressReporter(mcpContext);
2033
- let last;
2034
- try {
2035
- const { state, timedOut } = await runWaitOperation({
2036
- input: args,
2037
- poll: async (input) => {
2038
- last = await config.poll(input);
2039
- return last;
2040
- },
2041
- done: config.done,
2042
- backoff: config.backoff,
2043
- timeoutSec: config.timeoutFromArgs?.(args) ?? config.defaultTimeout,
2044
- onTick: (attempt, elapsedSec) => {
2045
- const snapshot = isRecord(last) ? last : undefined;
2046
- const phase = typeof snapshot?.phase === "string" ? snapshot.phase : undefined;
2047
- const measured = typeof snapshot?.progress === "number" && Number.isFinite(snapshot.progress) ? snapshot.progress : undefined;
2048
- report({
2049
- progress: measured ?? attempt,
2050
- message: phase ? `${phase} — poll ${attempt}, ${Math.round(elapsedSec)}s elapsed` : `poll ${attempt}, ${Math.round(elapsedSec)}s elapsed`
2051
- });
2052
- }
2053
- });
2054
- const rendered = config.render ? config.render(state, timedOut) : { text: JSON.stringify(state, null, 2), isError: timedOut };
2055
- return textResult(rendered.text, rendered.isError);
2056
- } catch (err) {
2057
- return textResult(`Wait failed: ${err instanceof Error ? err.message : String(err)}`, true);
2058
- }
2059
- });
2060
- }
2061
- // src/tools/oauth-provider.ts
2062
- import { randomUUID } from "node:crypto";
2063
- import { z as z8 } from "zod";
2064
- function isPlainDisplayCharacter(character) {
2065
- const codePoint = character.codePointAt(0);
2066
- return codePoint !== undefined && codePoint >= 32 && codePoint !== 127 && character !== "<" && character !== ">";
2067
- }
2068
- var CimdClientMetadataSchema = z8.object({
2069
- client_id: z8.url(),
2070
- redirect_uris: z8.array(z8.url()).min(1),
2071
- client_name: z8.string().trim().min(1).max(200).refine((value) => Array.from(value).every(isPlainDisplayCharacter), {
2072
- message: "client_name must be plain display text"
2073
- }),
2074
- token_endpoint_auth_method: z8.literal("none"),
2075
- application_type: z8.enum(["native", "web"]).optional()
2076
- });
2077
- function createSecureClientMetadataFetcher(policy = {}) {
2078
- assertPositiveInteger("cimd.maxBytes", policy.maxBytes ?? DEFAULT_CIMD_MAX_BYTES);
2079
- assertPositiveNumber("cimd.timeoutMs", policy.timeoutMs ?? DEFAULT_CIMD_TIMEOUT_MS);
2080
- assertNonNegativeInteger("cimd.maxRedirects", policy.maxRedirects ?? 3);
2081
2010
  return {
2082
- fetch: (url, headers) => fetchPinnedDocument(url, {
2083
- maxBytes: policy.maxBytes ?? DEFAULT_CIMD_MAX_BYTES,
2084
- timeoutMs: policy.timeoutMs ?? DEFAULT_CIMD_TIMEOUT_MS,
2085
- maxRedirects: policy.maxRedirects ?? 3,
2086
- headers,
2087
- requireHttps: true
2088
- })
2011
+ contract: config.contract,
2012
+ handlers: config.handlers,
2013
+ capabilities: config.capabilities,
2014
+ schemas: { id: idSchema, snapshot: statusOutput },
2015
+ adapters: {
2016
+ idFromStart: (output) => adapterResult("start", "id", idSchema, adapters.idFromStart(output)),
2017
+ inputFor
2018
+ }
2089
2019
  };
2090
2020
  }
2091
- var PUBLIC_CORS2 = {
2092
- "Access-Control-Allow-Origin": "*",
2093
- "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
2094
- "Access-Control-Allow-Headers": DEFAULT_CORS_ALLOW_HEADERS
2095
- };
2096
- var AS_METADATA_PATH = "/.well-known/oauth-authorization-server";
2097
- var AUTH_CODE_TTL_MS = 60000;
2098
- var DEFAULT_CIMD_MAX_BYTES = 64 * 1024;
2099
- var DEFAULT_CIMD_TIMEOUT_MS = 5000;
2100
- function assertPositiveInteger(name, value) {
2101
- if (!Number.isInteger(value) || value < 1)
2102
- throw new Error(`${name} must be a positive integer`);
2103
- }
2104
- function assertNonNegativeInteger(name, value) {
2105
- if (!Number.isInteger(value) || value < 0) {
2106
- throw new Error(`${name} must be a non-negative integer`);
2021
+ // src/tools/operations/async-operation-canonical.ts
2022
+ function buildCanonicalEndpointTable(config, startOutput) {
2023
+ const description = (capability) => config.descriptions?.[capability] ?? `${config.description}: ${capability}`;
2024
+ const endpoint = (capability, method, path, input, output) => {
2025
+ const scope = config.scopes?.[capability];
2026
+ const expose = config.expose?.[capability];
2027
+ return {
2028
+ method,
2029
+ path,
2030
+ desc: description(capability),
2031
+ input,
2032
+ output,
2033
+ ...scope !== undefined && { scope },
2034
+ ...expose !== undefined && { expose }
2035
+ };
2036
+ };
2037
+ const endpoints = {
2038
+ start: endpoint("start", "POST", "/", config.startInput, startOutput),
2039
+ status: endpoint("status", "POST", "/status", config.id, config.snapshot),
2040
+ wait: endpoint("wait", "POST", "/wait", config.id, config.snapshot)
2041
+ };
2042
+ const capabilities = {
2043
+ start: "start",
2044
+ status: "status",
2045
+ wait: "wait"
2046
+ };
2047
+ const schemas = {
2048
+ startInput: config.startInput,
2049
+ startOutput,
2050
+ id: config.id,
2051
+ snapshot: config.snapshot,
2052
+ cancelResult: AsyncOperationCancelResultSchema
2053
+ };
2054
+ const inputFor = {
2055
+ status: (id) => adapterResult("status", "input", config.id, id),
2056
+ wait: (id) => adapterResult("wait", "input", config.id, id)
2057
+ };
2058
+ if (config.cancel) {
2059
+ endpoints.cancel = endpoint("cancel", "POST", "/cancel", config.id, AsyncOperationCancelResultSchema);
2060
+ capabilities.cancel = "cancel";
2061
+ schemas.cancel = AsyncOperationCancelResultSchema;
2062
+ inputFor.cancel = (id) => adapterResult("cancel", "input", config.id, id);
2107
2063
  }
2108
- }
2109
- function assertPositiveNumber(name, value) {
2110
- if (!Number.isFinite(value) || value <= 0)
2111
- throw new Error(`${name} must be positive`);
2112
- }
2113
- function absoluteClientUrl(clientId) {
2114
- try {
2115
- return new URL(clientId);
2116
- } catch {
2117
- return null;
2064
+ if (config.result) {
2065
+ endpoints.result = endpoint("result", "POST", "/result", config.id, config.result);
2066
+ capabilities.result = "result";
2067
+ schemas.result = config.result;
2068
+ inputFor.result = (id) => adapterResult("result", "input", config.id, id);
2069
+ }
2070
+ if (config.artifacts) {
2071
+ endpoints.artifacts = endpoint("artifacts", "POST", "/artifacts", config.id, config.artifacts);
2072
+ capabilities.artifacts = "artifacts";
2073
+ schemas.artifacts = config.artifacts;
2074
+ inputFor.artifacts = (id) => adapterResult("artifacts", "input", config.id, id);
2118
2075
  }
2076
+ return { endpoints, capabilities, schemas, inputFor };
2119
2077
  }
2120
- function assertCimdClientId(clientId) {
2121
- const url = new URL(clientId);
2122
- if (url.protocol !== "https:")
2123
- throw new Error("CIMD client_id must use https");
2124
- if (url.username || url.password)
2125
- throw new Error("CIMD client_id cannot contain credentials");
2126
- if (url.hash)
2127
- throw new Error("CIMD client_id cannot contain a fragment");
2128
- if (url.search)
2129
- throw new Error("CIMD client_id cannot contain a query");
2130
- if (url.toString() !== clientId) {
2131
- throw new Error("CIMD client_id must be an exact canonical URL");
2078
+ function defineAsyncOperationContract(config) {
2079
+ assertWireStableIdSchema(config.id, "Async operation contract");
2080
+ const hasStartOutput = config.startOutput !== undefined;
2081
+ const hasIdFromStart = config.idFromStart !== undefined;
2082
+ if (hasStartOutput !== hasIdFromStart) {
2083
+ throw new Error("Async operation contract startOutput and idFromStart must be configured together");
2132
2084
  }
2133
- return url;
2085
+ const startOutput = config.startOutput ?? config.id;
2086
+ const { endpoints, capabilities, schemas, inputFor } = buildCanonicalEndpointTable(config, startOutput);
2087
+ const contract = config.scope ? defineContract({ prefix: config.prefix, scope: config.scope }, endpoints) : defineContract({ prefix: config.prefix }, endpoints);
2088
+ const applicationStartOutput = config.startOutput;
2089
+ const applicationIdFromStart = config.idFromStart;
2090
+ const idFromStart = applicationStartOutput && applicationIdFromStart ? (output) => adapterResult("start", "id", config.id, applicationIdFromStart(output)) : (output) => adapterResult("start", "id", config.id, output);
2091
+ return {
2092
+ contract,
2093
+ capabilities,
2094
+ schemas,
2095
+ adapters: { idFromStart, inputFor }
2096
+ };
2134
2097
  }
2135
- function parseAgeSeconds(headers) {
2136
- const raw = headers.get("age");
2137
- if (raw === null)
2138
- return 0;
2139
- const first = raw.split(",")[0]?.trim() ?? "";
2140
- return /^\d+$/.test(first) ? Number(first) : 0;
2098
+ // src/tools/native-definition.ts
2099
+ function managedNativeIdentity(identity, method) {
2100
+ return { ...identity, method };
2141
2101
  }
2142
- function responseFreshness(headers, policy, now) {
2143
- const maxTtl = policy.maxTtlMs ?? 3600000;
2144
- const cacheControl = headers.get("cache-control") ?? "";
2145
- if (/(?:^|,)\s*no-store\s*(?:,|$)/i.test(cacheControl)) {
2146
- return { freshnessMs: 0, store: false };
2147
- }
2148
- if (/(?:^|,)\s*no-cache\s*(?:,|$)/i.test(cacheControl)) {
2149
- return { freshnessMs: 0, store: true };
2150
- }
2151
- const maxAge = cacheControl.match(/(?:^|,)\s*max-age=([^,\s]*)/i)?.[1];
2152
- if (maxAge !== undefined) {
2153
- if (!/^\d+$/.test(maxAge))
2154
- return { freshnessMs: 0, store: true };
2155
- const age = parseAgeSeconds(headers);
2156
- return {
2157
- freshnessMs: Math.max(0, Math.min(maxTtl, (Number(maxAge) - age) * 1000)),
2158
- store: true
2159
- };
2160
- }
2161
- const expires = headers.get("expires");
2162
- if (expires !== null) {
2163
- const expiresAt = Date.parse(expires);
2164
- if (!Number.isFinite(expiresAt))
2165
- return { freshnessMs: 0, store: true };
2166
- const dateAt = Date.parse(headers.get("date") ?? "");
2167
- const ageMs = parseAgeSeconds(headers) * 1000;
2168
- const lifetime = Number.isFinite(dateAt) ? expiresAt - dateAt : expiresAt - now;
2169
- return {
2170
- freshnessMs: Math.max(0, Math.min(maxTtl, lifetime - ageMs)),
2171
- store: true
2172
- };
2173
- }
2174
- return { freshnessMs: Math.min(maxTtl, policy.defaultTtlMs ?? 300000), store: true };
2102
+
2103
+ // src/tools/operations/define-wait-tool.ts
2104
+ function defineWaitTool(config) {
2105
+ const renderedMcp = config.render ? {
2106
+ mcp: (state) => {
2107
+ const rendered = config.render?.(state, false);
2108
+ return {
2109
+ content: [
2110
+ {
2111
+ type: "text",
2112
+ text: rendered?.text ?? JSON.stringify(state, null, 2)
2113
+ }
2114
+ ]
2115
+ };
2116
+ }
2117
+ } : undefined;
2118
+ const present = renderedMcp || config.present ? { ...renderedMcp, ...config.present } : undefined;
2119
+ return defineRuntimeTool({
2120
+ name: config.name ?? "wait",
2121
+ description: config.description,
2122
+ identity: managedNativeIdentity(config.identity, "GET"),
2123
+ input: config.input,
2124
+ output: config.state,
2125
+ transports: config.transports,
2126
+ annotations: config.annotations,
2127
+ present,
2128
+ handler: async (context) => {
2129
+ let result;
2130
+ try {
2131
+ result = await runWaitOperation({
2132
+ input: context.input,
2133
+ poll: async (input, signal) => config.state.parse(await config.poll(input, { ...context, signal })),
2134
+ done: config.done,
2135
+ backoff: config.backoff,
2136
+ timeoutSec: config.timeoutFromInput?.(context.input) ?? config.defaultTimeout,
2137
+ signal: context.signal
2138
+ });
2139
+ } catch (error) {
2140
+ if (error instanceof WaitTimeoutError) {
2141
+ throw new AppError("WAIT_TIMEOUT", "Wait timed out before the first snapshot", 408);
2142
+ }
2143
+ throw error;
2144
+ }
2145
+ const rendered = config.render?.(result.state, result.timedOut);
2146
+ if (result.timedOut) {
2147
+ throw new AppError("WAIT_TIMEOUT", rendered?.text ?? "Wait timed out before the operation completed", 408);
2148
+ }
2149
+ if (rendered?.isError) {
2150
+ throw new AppError("WAIT_FAILED", rendered.text, 409);
2151
+ }
2152
+ return result.state;
2153
+ }
2154
+ });
2175
2155
  }
2176
- function safeClientName(value) {
2177
- if (value === undefined)
2178
- return;
2179
- const safe = Array.from(value).filter(isPlainDisplayCharacter).join("").trim().slice(0, 200);
2180
- return safe || undefined;
2156
+
2157
+ // src/tools/operations/async-operation-runtime-tools.ts
2158
+ function terminal(snapshot) {
2159
+ if (typeof snapshot !== "object" || snapshot === null || !("phase" in snapshot)) {
2160
+ return false;
2161
+ }
2162
+ return snapshot.phase === "succeeded" || snapshot.phase === "failed" || snapshot.phase === "cancelled";
2181
2163
  }
2182
- function parseScopes(value) {
2183
- if (!value)
2184
- return [];
2185
- return [...new Set(value.split(/\s+/u).filter(Boolean))];
2164
+ function succeeded(snapshot) {
2165
+ return typeof snapshot === "object" && snapshot !== null && "phase" in snapshot && snapshot.phase === "succeeded";
2186
2166
  }
2187
- function toRegisteredClient(metadata) {
2167
+ function createRuntimeOperationSurface(config) {
2168
+ const capabilityName = (capability) => config.names?.[capability] ?? `${config.name}_${capability}`;
2169
+ const description = (capability) => config.descriptions?.[capability] ?? `${config.description}: ${capability}`;
2188
2170
  return {
2189
- clientId: metadata.client_id,
2190
- redirectUris: metadata.redirect_uris,
2191
- ...metadata.client_name !== undefined && { clientName: metadata.client_name },
2192
- ...metadata.application_type !== undefined && {
2193
- applicationType: metadata.application_type
2171
+ config,
2172
+ identity: (capability, method) => ({
2173
+ serviceName: config.identity.serviceName,
2174
+ action: `${config.identity.action}.${capability}`,
2175
+ scope: config.scopes?.[capability] ?? config.identity.scope,
2176
+ meta: config.identity.meta,
2177
+ method
2178
+ }),
2179
+ common: (capability) => ({
2180
+ name: capabilityName(capability),
2181
+ description: description(capability),
2182
+ transports: config.transports?.[capability],
2183
+ annotations: config.annotations?.[capability] ?? (capability === "start" || capability === "cancel" ? { destructiveHint: true } : undefined)
2184
+ }),
2185
+ inspect: async (capability, context) => {
2186
+ await config.authorize(context.input, capability, context);
2187
+ const state = config.state.parse(await config.inspect(context.input, context));
2188
+ const snapshot = config.snapshot.parse(await config.classify(state, context));
2189
+ return { state, snapshot };
2194
2190
  }
2195
2191
  };
2196
2192
  }
2197
- function isJsonContentType(value) {
2198
- const mediaType = value.split(";", 1)[0]?.trim().toLowerCase();
2199
- return mediaType === "application/json" || mediaType?.endsWith("+json") === true;
2193
+ function defineObservationTools(surface) {
2194
+ const { config } = surface;
2195
+ const status = defineRuntimeTool({
2196
+ ...surface.common("status"),
2197
+ identity: surface.identity("status", "GET"),
2198
+ input: config.id,
2199
+ output: config.snapshot,
2200
+ handler: async (context) => (await surface.inspect("status", context)).snapshot
2201
+ });
2202
+ const wait = defineWaitTool({
2203
+ ...surface.common("wait"),
2204
+ identity: surface.identity("wait", "GET"),
2205
+ input: config.id,
2206
+ state: config.snapshot,
2207
+ poll: async (_id, context) => (await surface.inspect("wait", context)).snapshot,
2208
+ done: terminal,
2209
+ backoff: config.backoff,
2210
+ defaultTimeout: config.defaultTimeout,
2211
+ timeoutFromInput: config.timeoutFromId
2212
+ });
2213
+ return { status, wait };
2200
2214
  }
2201
- function createCimdResolver(options) {
2202
- const policy = options.cache ?? {};
2203
- const now = options.now ?? Date.now;
2204
- const positives = new Map;
2205
- const negatives = new Map;
2206
- const inflight = new Map;
2207
- const maxEntries = policy.maxEntries ?? 256;
2208
- const maxResolutions = policy.maxResolutions ?? 120;
2209
- const maxResolutionsPerClient = policy.maxResolutionsPerClient ?? 10;
2210
- const resolutionWindowMs = policy.resolutionWindowMs ?? 60000;
2211
- let resolutionWindowStartedAt = now();
2212
- let resolutionsInWindow = 0;
2213
- const clientWindows = new Map;
2214
- const CLIENT_WINDOW_CAP = 4096;
2215
- assertPositiveInteger("cimd.cache.maxEntries", maxEntries);
2216
- assertPositiveInteger("cimd.cache.maxResolutions", maxResolutions);
2217
- assertPositiveInteger("cimd.cache.maxResolutionsPerClient", maxResolutionsPerClient);
2218
- assertPositiveInteger("cimd.cache.resolutionWindowMs", resolutionWindowMs);
2219
- assertNonNegativeInteger("cimd.cache.defaultTtlMs", policy.defaultTtlMs ?? 300000);
2220
- assertNonNegativeInteger("cimd.cache.maxTtlMs", policy.maxTtlMs ?? 3600000);
2221
- assertNonNegativeInteger("cimd.cache.negativeTtlMs", policy.negativeTtlMs ?? 1e4);
2222
- const emit = (event) => {
2223
- try {
2224
- options.onCacheEvent?.(event);
2225
- } catch {}
2226
- };
2227
- const readCache = (key) => positives.get(key) ?? negatives.get(key);
2228
- const trimOldest = (pool) => {
2229
- while (pool.size > maxEntries) {
2230
- const oldest = pool.keys().next().value;
2231
- if (oldest === undefined)
2232
- break;
2233
- pool.delete(oldest);
2234
- }
2235
- };
2236
- const touch = (key, value) => {
2237
- if (value.ok) {
2238
- negatives.delete(key);
2239
- positives.delete(key);
2240
- positives.set(key, value);
2241
- trimOldest(positives);
2242
- } else {
2243
- positives.delete(key);
2244
- negatives.delete(key);
2245
- negatives.set(key, value);
2246
- trimOldest(negatives);
2247
- }
2248
- };
2249
- const dropCache = (key) => {
2250
- positives.delete(key);
2251
- negatives.delete(key);
2252
- };
2253
- const resolveOne = async (clientId) => {
2254
- const url = assertCimdClientId(clientId);
2255
- const cached = readCache(clientId);
2256
- if (cached && cached.expiresAt > now()) {
2257
- touch(clientId, cached);
2258
- if (cached.ok) {
2259
- emit({ clientId, status: "hit", freshnessMs: cached.expiresAt - now() });
2260
- return cached.client;
2261
- }
2262
- emit({ clientId, status: "negative", freshnessMs: cached.expiresAt - now() });
2263
- throw new Error(cached.message);
2264
- }
2265
- emit({ clientId, status: "miss" });
2266
- const currentTime = now();
2267
- if (currentTime - resolutionWindowStartedAt >= resolutionWindowMs) {
2268
- resolutionWindowStartedAt = currentTime;
2269
- resolutionsInWindow = 0;
2215
+ function defineCancelTool(surface, capability) {
2216
+ return defineRuntimeTool({
2217
+ ...surface.common("cancel"),
2218
+ identity: surface.identity("cancel", "DELETE"),
2219
+ input: surface.config.id,
2220
+ output: AsyncOperationCancelResultSchema,
2221
+ handler: async (context) => {
2222
+ const inspected = await surface.inspect("cancel", context);
2223
+ return capability.handler(inspected.state, context);
2270
2224
  }
2271
- let clientWindow = clientWindows.get(clientId);
2272
- if (!clientWindow || currentTime - clientWindow.startedAt >= resolutionWindowMs) {
2273
- clientWindow = { startedAt: currentTime, count: 0 };
2274
- clientWindows.delete(clientId);
2275
- clientWindows.set(clientId, clientWindow);
2276
- while (clientWindows.size > CLIENT_WINDOW_CAP) {
2277
- const oldest = clientWindows.keys().next().value;
2278
- if (oldest === undefined)
2279
- break;
2280
- clientWindows.delete(oldest);
2225
+ });
2226
+ }
2227
+ function defineSucceededOutputTool(surface, kind, capability, unavailableMessage) {
2228
+ return defineRuntimeTool({
2229
+ ...surface.common(kind),
2230
+ identity: surface.identity(kind, "GET"),
2231
+ input: surface.config.id,
2232
+ output: capability.output,
2233
+ handler: async (context) => {
2234
+ const inspected = await surface.inspect(kind, context);
2235
+ if (!succeeded(inspected.snapshot)) {
2236
+ throw new AppError("OPERATION_NOT_SUCCEEDED", unavailableMessage, 409);
2281
2237
  }
2238
+ return capability.handler(inspected.state, context);
2282
2239
  }
2283
- if (clientWindow.count >= maxResolutionsPerClient) {
2284
- throw new Error("CIMD metadata resolution rate limit exceeded for this client_id");
2240
+ });
2241
+ }
2242
+
2243
+ // src/tools/operations/async-operation-runtime.ts
2244
+ function defineAsyncOperation(config) {
2245
+ const surface = createRuntimeOperationSurface(config);
2246
+ const start = defineRuntimeTool({
2247
+ ...surface.common("start"),
2248
+ identity: surface.identity("start", "POST"),
2249
+ input: config.startInput,
2250
+ output: config.id,
2251
+ handler: (context) => config.start(context.input, context)
2252
+ });
2253
+ const { status, wait } = defineObservationTools(surface);
2254
+ const definitions = { start, status, wait };
2255
+ const names = new Set;
2256
+ if (config.cancel) {
2257
+ definitions.cancel = defineCancelTool(surface, config.cancel);
2258
+ }
2259
+ if (config.result) {
2260
+ definitions.result = defineSucceededOutputTool(surface, "result", config.result, "Operation result is not available");
2261
+ }
2262
+ if (config.artifacts) {
2263
+ definitions.artifacts = defineSucceededOutputTool(surface, "artifacts", config.artifacts, "Operation artifacts are not available");
2264
+ }
2265
+ for (const definition of Object.values(definitions)) {
2266
+ assertUniqueToolName(definition.name, names.has(definition.name), "in-process tool name");
2267
+ names.add(definition.name);
2268
+ }
2269
+ return {
2270
+ ...definitions,
2271
+ runtimeTools: Object.values(definitions),
2272
+ schemas: {
2273
+ id: config.id,
2274
+ snapshot: config.snapshot,
2275
+ cancelResult: AsyncOperationCancelResultSchema
2285
2276
  }
2286
- if (resolutionsInWindow >= maxResolutions) {
2287
- throw new Error("CIMD metadata resolution rate limit exceeded");
2277
+ };
2278
+ }
2279
+ // src/tools/operations/mount-wait.ts
2280
+ import { z as z5 } from "zod";
2281
+
2282
+ // src/tools/transfer/native-result.ts
2283
+ function textResult(text, isError = false) {
2284
+ const content = [{ type: "text", text }];
2285
+ return isError ? { content, isError: true } : { content };
2286
+ }
2287
+
2288
+ // src/tools/operations/mount-wait.ts
2289
+ function mountWait(server, config) {
2290
+ const name = config.name ?? "wait";
2291
+ assertToolName(name, "<native>", "wait");
2292
+ server.registerTool(name, { description: config.description, inputSchema: z5.object(config.inputSchema) }, async (rawArgs, mcpContext) => {
2293
+ const args = isRecord(rawArgs) ? rawArgs : {};
2294
+ const report = createMcpProgressReporter(mcpContext);
2295
+ let last;
2296
+ try {
2297
+ const { state, timedOut } = await runWaitOperation({
2298
+ input: args,
2299
+ poll: async (input) => {
2300
+ last = await config.poll(input);
2301
+ return last;
2302
+ },
2303
+ done: config.done,
2304
+ backoff: config.backoff,
2305
+ timeoutSec: config.timeoutFromArgs?.(args) ?? config.defaultTimeout,
2306
+ onTick: (attempt, elapsedSec) => {
2307
+ const snapshot = isRecord(last) ? last : undefined;
2308
+ const phase = typeof snapshot?.phase === "string" ? snapshot.phase : undefined;
2309
+ const measured = typeof snapshot?.progress === "number" && Number.isFinite(snapshot.progress) ? snapshot.progress : undefined;
2310
+ report({
2311
+ progress: measured ?? attempt,
2312
+ message: phase ? `${phase} — poll ${attempt}, ${Math.round(elapsedSec)}s elapsed` : `poll ${attempt}, ${Math.round(elapsedSec)}s elapsed`
2313
+ });
2314
+ }
2315
+ });
2316
+ const rendered = config.render ? config.render(state, timedOut) : { text: JSON.stringify(state, null, 2), isError: timedOut };
2317
+ return textResult(rendered.text, rendered.isError);
2318
+ } catch (err) {
2319
+ return textResult(`Wait failed: ${err instanceof Error ? err.message : String(err)}`, true);
2288
2320
  }
2289
- clientWindow.count += 1;
2290
- resolutionsInWindow += 1;
2291
- const conditionalHeaders = {};
2292
- if (cached?.ok && cached.etag)
2293
- conditionalHeaders["if-none-match"] = cached.etag;
2294
- if (cached?.ok && cached.lastModified) {
2295
- conditionalHeaders["if-modified-since"] = cached.lastModified;
2321
+ });
2322
+ }
2323
+ // src/tools/registry.ts
2324
+ function defineToolRegistry(input) {
2325
+ const defaults = [...input.defaults];
2326
+ const slots = new Set;
2327
+ for (const tool of defaults) {
2328
+ if (!tool.name)
2329
+ throw new Error("A default tool must carry a non-empty name");
2330
+ if (slots.has(tool.name))
2331
+ throw new Error(`Duplicate default tool name: ${tool.name}`);
2332
+ slots.add(tool.name);
2333
+ }
2334
+ const disabled = new Set;
2335
+ const replacements = new Map;
2336
+ const assertKnown = (name, verb) => {
2337
+ if (!slots.has(name)) {
2338
+ throw new Error(`Tool registry ${verb}("${name}") names no default; declared: ${[...slots].sort().join(", ") || "(none)"}`);
2296
2339
  }
2297
- try {
2298
- const response = await options.fetcher.fetch(url, conditionalHeaders);
2299
- const freshness = responseFreshness(response.headers, policy, now());
2300
- if (response.status === 304 && cached?.ok) {
2301
- const refreshed = {
2302
- ...cached,
2303
- expiresAt: now() + freshness.freshnessMs
2304
- };
2305
- if (freshness.store)
2306
- touch(clientId, refreshed);
2307
- else
2308
- dropCache(clientId);
2309
- emit({ clientId, status: "revalidated", freshnessMs: freshness.freshnessMs });
2310
- return refreshed.client;
2311
- }
2312
- if (response.status !== 200) {
2313
- throw new Error(`CIMD endpoint returned HTTP ${response.status}`);
2314
- }
2315
- const contentType = response.headers.get("content-type") ?? "";
2316
- if (!isJsonContentType(contentType)) {
2317
- throw new Error("CIMD endpoint must return a JSON media type");
2340
+ };
2341
+ const builder = {
2342
+ replace(name, tool) {
2343
+ assertKnown(name, "replace");
2344
+ if (disabled.has(name)) {
2345
+ throw new Error(`Tool registry cannot replace "${name}": it is disabled`);
2318
2346
  }
2319
- const decoded = JSON.parse(new TextDecoder().decode(response.body));
2320
- const metadata = CimdClientMetadataSchema.parse(decoded);
2321
- if (metadata.client_id !== clientId) {
2322
- throw new Error("CIMD client_id does not exactly match the requested URL");
2347
+ if (replacements.has(name)) {
2348
+ throw new Error(`Tool registry already replaced "${name}"`);
2323
2349
  }
2324
- if (!metadata.redirect_uris.every((uri) => isRegistrableRedirectUri(uri, metadata.application_type))) {
2325
- throw new Error("CIMD redirect_uris contains a URI forbidden for the application type");
2350
+ if (tool.name !== name) {
2351
+ throw new Error(`A replacement for "${name}" must carry that name, not "${tool.name}"`);
2326
2352
  }
2327
- const client = toRegisteredClient(metadata);
2328
- if (freshness.store && Number.isFinite(freshness.freshnessMs)) {
2329
- touch(clientId, {
2330
- ok: true,
2331
- client,
2332
- expiresAt: now() + freshness.freshnessMs,
2333
- ...response.headers.get("etag") !== null && {
2334
- etag: response.headers.get("etag") ?? undefined
2335
- },
2336
- ...response.headers.get("last-modified") !== null && {
2337
- lastModified: response.headers.get("last-modified") ?? undefined
2338
- }
2339
- });
2340
- } else {
2341
- dropCache(clientId);
2353
+ replacements.set(name, tool);
2354
+ return builder;
2355
+ },
2356
+ disable(name) {
2357
+ assertKnown(name, "disable");
2358
+ disabled.add(name);
2359
+ return builder;
2360
+ },
2361
+ build() {
2362
+ const tools = [];
2363
+ const seen = new Set;
2364
+ for (const tool of defaults) {
2365
+ if (disabled.has(tool.name))
2366
+ continue;
2367
+ const effective = replacements.get(tool.name) ?? tool;
2368
+ if (seen.has(effective.name)) {
2369
+ throw new Error(`Duplicate composed tool name: ${effective.name}`);
2370
+ }
2371
+ seen.add(effective.name);
2372
+ tools.push(effective);
2342
2373
  }
2343
- return client;
2344
- } catch (error) {
2345
- const message = error instanceof Error ? error.message : "CIMD metadata resolution failed";
2346
- touch(clientId, {
2347
- ok: false,
2348
- message,
2349
- expiresAt: now() + (policy.negativeTtlMs ?? 1e4)
2350
- });
2351
- throw error;
2374
+ return { tools, names: tools.map((tool) => tool.name) };
2352
2375
  }
2353
2376
  };
2354
- return async (clientId) => {
2355
- const pending = inflight.get(clientId);
2356
- if (pending)
2357
- return pending;
2358
- const created = resolveOne(clientId).finally(() => inflight.delete(clientId));
2359
- inflight.set(clientId, created);
2360
- return created;
2377
+ return builder;
2378
+ }
2379
+ // src/tools/tool-logger.ts
2380
+ function createToolLogger(config = {}) {
2381
+ const log = config.log ?? ((line) => console.error(line));
2382
+ return {
2383
+ afterToolCall: ({ toolName, result, durationMs, context, endpoint }) => {
2384
+ const record = {
2385
+ tool: toolName,
2386
+ service: endpoint.serviceName,
2387
+ action: endpoint.key,
2388
+ ok: result.ok,
2389
+ code: result.ok ? undefined : result.code,
2390
+ durationMs: Math.round(durationMs),
2391
+ source: String(context.source),
2392
+ traceId: getTraceId()
2393
+ };
2394
+ const codePart = result.ok ? "" : ` ${result.code}`;
2395
+ log(`[tool] ${result.ok ? "ok" : "warn"} ${toolName} (${endpoint.serviceName}.${endpoint.key})${codePart} ${record.durationMs}ms`);
2396
+ config.onRecord?.(record);
2397
+ }
2361
2398
  };
2362
2399
  }
2363
- function json(body, status = 200, extraHeaders) {
2364
- return new Response(JSON.stringify(body), {
2365
- status,
2366
- headers: { "Content-Type": "application/json", ...PUBLIC_CORS2, ...extraHeaders }
2367
- });
2400
+ // src/tools/toolkit.ts
2401
+ function createToolkit() {
2402
+ return {
2403
+ mountMcp: (server, services, config) => mountMcp(server, services, config),
2404
+ mountAgent: (services, config) => mountAgent(services, config),
2405
+ buildMcpServer: (config, auth) => buildMcpServer(config, auth),
2406
+ createMcpHandler: (config) => createMcpHandler(config),
2407
+ createStdioMcpServer: (config) => createStdioMcpServer(config),
2408
+ createCli: (config) => createCli(config)
2409
+ };
2368
2410
  }
2369
- function oauthError(error, description, status = 400) {
2370
- return json({ error, error_description: description }, status);
2411
+ // src/tools/transfer/download-core.ts
2412
+ import { basename, extname } from "node:path";
2413
+ var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
2414
+ var MIME_EXT = {
2415
+ "image/png": ".png",
2416
+ "image/jpeg": ".jpg",
2417
+ "image/webp": ".webp",
2418
+ "image/gif": ".gif",
2419
+ "video/mp4": ".mp4",
2420
+ "video/webm": ".webm",
2421
+ "audio/mpeg": ".mp3",
2422
+ "audio/wav": ".wav",
2423
+ "audio/ogg": ".ogg"
2424
+ };
2425
+
2426
+ class DownloadOperationError extends Error {
2427
+ code;
2428
+ status;
2429
+ constructor(code, message, status) {
2430
+ super(message);
2431
+ this.code = code;
2432
+ this.status = status;
2433
+ this.name = "DownloadOperationError";
2434
+ }
2371
2435
  }
2372
- function isRegistrableRedirectUri(value, applicationType) {
2436
+ function extensionFor(url, mime) {
2437
+ if (MIME_EXT[mime])
2438
+ return MIME_EXT[mime];
2373
2439
  try {
2374
- const url = new URL(value);
2375
- if (url.username || url.password || url.hash)
2376
- return false;
2377
- if (url.protocol === "https:")
2378
- return true;
2379
- if (url.protocol !== "http:")
2380
- return false;
2381
- if (applicationType === "web")
2382
- return false;
2383
- const host = url.hostname.replace(/^\[|\]$/g, "");
2384
- return host === "127.0.0.1" || host === "::1" || host === "localhost";
2385
- } catch {
2386
- return false;
2387
- }
2440
+ const ext = extname(new URL(url).pathname).toLowerCase();
2441
+ if (ext)
2442
+ return ext;
2443
+ } catch {}
2444
+ return ".bin";
2388
2445
  }
2389
- function isLoopbackRedirectUri(value) {
2446
+ function baseNameFor(url) {
2390
2447
  try {
2391
- const url = new URL(value);
2392
- if (url.protocol !== "http:")
2393
- return false;
2394
- const host = url.hostname.replace(/^\[|\]$/g, "");
2395
- return host === "127.0.0.1" || host === "::1" || host === "localhost";
2448
+ const path = new URL(url).pathname;
2449
+ return (basename(path, extname(path)) || "download").slice(0, 60);
2396
2450
  } catch {
2397
- return false;
2451
+ return "download";
2398
2452
  }
2399
2453
  }
2400
- function redirectWith(uri, params) {
2401
- const url = new URL(uri);
2402
- for (const [key, value] of Object.entries(params))
2403
- url.searchParams.set(key, value);
2404
- return new Response(null, { status: 302, headers: { Location: url.toString() } });
2454
+ async function runDownloadOperation(config) {
2455
+ const res = await fetchGuarded(new URL(config.url), config.allowPrivateHosts ?? false, {
2456
+ timeoutMs: config.timeoutMs,
2457
+ signal: config.signal
2458
+ });
2459
+ if (!res.ok) {
2460
+ throw new DownloadOperationError("DOWNLOAD_HTTP_ERROR", `HTTP ${res.status}`, 502);
2461
+ }
2462
+ const mimeType = mediaTypeEssence(res.headers.get("content-type")) || "application/octet-stream";
2463
+ const max = config.maxBytes ?? DEFAULT_MAX_BYTES;
2464
+ const declared = Number(res.headers.get("content-length") ?? 0);
2465
+ if (declared > max) {
2466
+ await res.body?.cancel();
2467
+ throw new DownloadOperationError("DOWNLOAD_TOO_LARGE", `file exceeds the ${max}-byte cap`, 413);
2468
+ }
2469
+ const path = config.path ?? `${baseNameFor(config.url)}${extensionFor(config.url, mimeType)}`;
2470
+ return config.files.write(path, res.body ?? new Uint8Array, {
2471
+ maxBytes: max,
2472
+ mediaType: mimeType,
2473
+ signal: config.signal
2474
+ });
2405
2475
  }
2406
- async function readForm(req) {
2407
- const contentType = req.headers.get("content-type") ?? "";
2408
- if (contentType.includes("application/json")) {
2409
- const body = await req.json().catch(() => null);
2410
- const params = new URLSearchParams;
2411
- if (isRecord(body)) {
2412
- for (const [key, value] of Object.entries(body)) {
2413
- if (typeof value === "string")
2414
- params.set(key, value);
2415
- }
2416
- }
2417
- return params;
2476
+
2477
+ // src/tools/transfer/managed-file-error.ts
2478
+ var SAFE_MANAGED_FILE_MESSAGES = {
2479
+ FILE_INVALID_PATH: "Invalid managed-file path",
2480
+ FILE_OUTSIDE_ROOT: "Managed-file path escapes its boundary",
2481
+ FILE_NOT_FOUND: "Managed file not found",
2482
+ FILE_NOT_REGULAR: "Managed path is not a regular file",
2483
+ FILE_INSPECTION_REJECTED: "Managed file rejected by inspection",
2484
+ FILE_TOO_LARGE: "Managed file exceeds the configured size limit",
2485
+ FILE_EXISTS: "Managed file already exists"
2486
+ };
2487
+ function managedFileAppError(error) {
2488
+ if (!(error instanceof ManagedFileError))
2489
+ return null;
2490
+ switch (error.code) {
2491
+ case "FILE_INVALID_PATH":
2492
+ case "FILE_OUTSIDE_ROOT":
2493
+ case "FILE_NOT_FOUND":
2494
+ case "FILE_NOT_REGULAR":
2495
+ case "FILE_INSPECTION_REJECTED":
2496
+ case "FILE_TOO_LARGE":
2497
+ case "FILE_EXISTS":
2498
+ return new AppError(error.code, SAFE_MANAGED_FILE_MESSAGES[error.code], STITCH_ERROR_STATUS[error.code]);
2499
+ case "FILE_IO_ERROR":
2500
+ return null;
2418
2501
  }
2419
- return new URLSearchParams(await req.text());
2420
2502
  }
2421
- function mountOAuthProvider(config) {
2422
- const base = config.basePath ?? "/oauth";
2423
- const ttl = config.accessTokenTtlSec ?? 3600;
2424
- const registerPath = `${base}/register`;
2425
- const authorizePath = `${base}/authorize`;
2426
- const tokenPath = `${base}/token`;
2427
- const clientRegistration = config.clientRegistration ?? {};
2428
- const dcrRegistry = clientRegistration.dcr || undefined;
2429
- const dcrEnabled = dcrRegistry !== undefined;
2430
- const cimdConfig = clientRegistration.cimd === false ? undefined : clientRegistration.cimd ?? {};
2431
- const cimdEnabled = cimdConfig !== undefined;
2432
- const resolveCimd = cimdEnabled ? createCimdResolver({
2433
- cache: cimdConfig.cache,
2434
- fetcher: cimdConfig.fetcher ?? createSecureClientMetadataFetcher(),
2435
- onCacheEvent: cimdConfig.onCacheEvent
2436
- }) : undefined;
2437
- const resolveClient = async (clientId) => {
2438
- const preRegistered = await clientRegistration.preRegistered?.get(clientId);
2439
- if (preRegistered)
2440
- return preRegistered.clientId === clientId ? preRegistered : null;
2441
- const clientUrl = absoluteClientUrl(clientId);
2442
- if (clientUrl) {
2443
- if (!resolveCimd || clientUrl.protocol !== "https:")
2444
- return null;
2445
- try {
2446
- return await resolveCimd(clientId);
2447
- } catch {
2448
- return null;
2449
- }
2450
- }
2451
- const dcrClient = await dcrRegistry?.get(clientId);
2452
- return dcrClient?.clientId === clientId ? dcrClient : null;
2453
- };
2454
- const redirectToClient = (uri, params) => redirectWith(uri, { ...params, iss: config.issuer });
2455
- const metadataRoute = {
2456
- method: "ALL",
2457
- path: AS_METADATA_PATH,
2458
- handler: (req) => {
2459
- if (req.method === "OPTIONS")
2460
- return new Response(null, { status: 204, headers: PUBLIC_CORS2 });
2461
- return json({
2462
- issuer: config.issuer,
2463
- authorization_endpoint: `${config.issuer}${authorizePath}`,
2464
- token_endpoint: `${config.issuer}${tokenPath}`,
2465
- ...dcrEnabled && { registration_endpoint: `${config.issuer}${registerPath}` },
2466
- response_types_supported: ["code"],
2467
- grant_types_supported: config.refreshTokens ? ["authorization_code", "refresh_token"] : ["authorization_code"],
2468
- code_challenge_methods_supported: ["S256"],
2469
- token_endpoint_auth_methods_supported: ["none"],
2470
- ...cimdEnabled && { client_id_metadata_document_supported: true },
2471
- authorization_response_iss_parameter_supported: true,
2472
- ...config.scopesSupported && { scopes_supported: config.scopesSupported }
2473
- });
2474
- }
2475
- };
2476
- const registerRoute = {
2477
- method: "ALL",
2478
- path: registerPath,
2479
- handler: async (req) => {
2480
- if (req.method === "OPTIONS")
2481
- return new Response(null, { status: 204, headers: PUBLIC_CORS2 });
2482
- if (req.method !== "POST")
2483
- return oauthError("invalid_request", "POST required", 405);
2484
- const meta = await req.json().catch(() => null);
2485
- if (!isRecord(meta)) {
2486
- return oauthError("invalid_client_metadata", "Body must be a JSON object");
2487
- }
2488
- const rawAppType = meta.application_type;
2489
- if (rawAppType !== undefined && rawAppType !== "native" && rawAppType !== "web") {
2490
- return oauthError("invalid_client_metadata", "application_type must be 'native' or 'web'");
2491
- }
2492
- const applicationType = rawAppType;
2493
- const redirectUris = meta.redirect_uris;
2494
- const tokenEndpointAuthMethod = meta.token_endpoint_auth_method ?? "none";
2495
- if (tokenEndpointAuthMethod !== "none") {
2496
- return oauthError("invalid_client_metadata", 'Only public clients with token_endpoint_auth_method "none" are supported');
2497
- }
2498
- if (!Array.isArray(redirectUris) || redirectUris.length === 0 || !redirectUris.every((u) => typeof u === "string" && isRegistrableRedirectUri(u, applicationType))) {
2499
- return oauthError("invalid_redirect_uri", applicationType === "web" ? "redirect_uris must be a non-empty array of absolute https URLs (a web client cannot register an http loopback URI)" : "redirect_uris must be a non-empty array of absolute https URLs (http is allowed only on a loopback host)");
2500
- }
2501
- if (!dcrRegistry)
2502
- throw new Error("DCR route mounted without a DCR registry");
2503
- const client = await dcrRegistry.register({
2504
- redirectUris,
2505
- clientName: typeof meta.client_name === "string" ? meta.client_name : undefined,
2506
- tokenEndpointAuthMethod,
2507
- ...applicationType && { applicationType }
2508
- });
2509
- return json({
2510
- client_id: client.clientId,
2511
- redirect_uris: client.redirectUris,
2512
- token_endpoint_auth_method: "none",
2513
- grant_types: config.refreshTokens ? ["authorization_code", "refresh_token"] : ["authorization_code"],
2514
- response_types: ["code"],
2515
- ...client.clientName && { client_name: client.clientName },
2516
- ...client.applicationType && { application_type: client.applicationType }
2517
- }, 201);
2518
- }
2519
- };
2520
- const authorizeRoute = {
2521
- method: "ALL",
2522
- path: authorizePath,
2523
- handler: async (req) => {
2524
- if (req.method === "OPTIONS")
2525
- return new Response(null, { status: 204, headers: PUBLIC_CORS2 });
2526
- const url = new URL(req.url);
2527
- const p = url.searchParams;
2528
- const clientId = p.get("client_id");
2529
- const redirectUri = p.get("redirect_uri");
2530
- const responseType = p.get("response_type");
2531
- const codeChallenge = p.get("code_challenge");
2532
- const codeChallengeMethod = p.get("code_challenge_method") ?? "S256";
2533
- const resource = p.get("resource");
2534
- const scope = p.get("scope") ?? undefined;
2535
- const state = p.get("state") ?? undefined;
2536
- if (!clientId || !redirectUri) {
2537
- return oauthError("invalid_request", "client_id and redirect_uri are required");
2538
- }
2539
- const client = await resolveClient(clientId);
2540
- if (!client)
2541
- return oauthError("invalid_client", "Unknown client_id", 401);
2542
- if (!client.redirectUris.includes(redirectUri)) {
2543
- return oauthError("invalid_request", "redirect_uri does not match a registered URI");
2544
- }
2545
- if (responseType !== "code") {
2546
- return redirectToClient(redirectUri, {
2547
- error: "unsupported_response_type",
2548
- ...state && { state }
2549
- });
2550
- }
2551
- if (!codeChallenge || codeChallengeMethod !== "S256") {
2552
- return redirectToClient(redirectUri, {
2553
- error: "invalid_request",
2554
- error_description: "PKCE S256 code_challenge is required",
2555
- ...state && { state }
2556
- });
2557
- }
2558
- if (!resource) {
2559
- return redirectToClient(redirectUri, {
2560
- error: "invalid_target",
2561
- error_description: "resource parameter is required",
2562
- ...state && { state }
2563
- });
2564
- }
2565
- if (resource !== config.resource) {
2566
- return redirectToClient(redirectUri, {
2567
- error: "invalid_target",
2568
- error_description: "resource is not served by this authorization server",
2569
- ...state && { state }
2570
- });
2503
+ function normalizeFileToolError(error) {
2504
+ return managedFileAppError(error) ?? normalizeError(error);
2505
+ }
2506
+
2507
+ // src/tools/transfer/define-download-tool.ts
2508
+ function defineDownloadTool(config) {
2509
+ return defineRuntimeTool({
2510
+ name: config.name ?? "download",
2511
+ description: config.description,
2512
+ identity: managedNativeIdentity(config.identity, "POST"),
2513
+ input: config.input,
2514
+ output: ManagedFileRefSchema,
2515
+ transports: config.transports,
2516
+ annotations: config.annotations,
2517
+ present: config.present,
2518
+ handler: async (context) => {
2519
+ const url = await config.resolveUrl(context.input, context);
2520
+ if (!url) {
2521
+ throw new AppError("DOWNLOAD_NOT_FOUND", "Nothing to download", 404);
2571
2522
  }
2572
- const requestedScopes = parseScopes(scope);
2573
- const supportedScopes = new Set(config.scopesSupported ?? []);
2574
- if (config.scopesSupported && requestedScopes.some((requestedScope2) => !supportedScopes.has(requestedScope2))) {
2575
- return redirectToClient(redirectUri, {
2576
- error: "invalid_scope",
2577
- error_description: "One or more requested scopes are not supported",
2578
- ...state && { state }
2523
+ try {
2524
+ return await runDownloadOperation({
2525
+ url,
2526
+ files: config.files,
2527
+ path: config.pathFromInput?.(context.input),
2528
+ allowPrivateHosts: config.allowPrivateHosts,
2529
+ maxBytes: config.maxBytes,
2530
+ timeoutMs: config.timeoutMs,
2531
+ signal: context.signal
2579
2532
  });
2580
- }
2581
- const requestedScope = requestedScopes.length > 0 ? requestedScopes.join(" ") : undefined;
2582
- const authRequest = {
2583
- clientId,
2584
- redirectUri,
2585
- scope: requestedScope,
2586
- resource,
2587
- state,
2588
- isLoopbackRedirect: isLoopbackRedirectUri(redirectUri),
2589
- ...safeClientName(client.clientName) !== undefined && {
2590
- clientName: safeClientName(client.clientName)
2591
- },
2592
- ...client.applicationType !== undefined && {
2593
- applicationType: client.applicationType
2594
- },
2595
- ...absoluteClientUrl(clientId)?.protocol === "https:" && {
2596
- clientOrigin: new URL(clientId).origin
2533
+ } catch (error) {
2534
+ if (error instanceof DownloadOperationError) {
2535
+ throw new AppError(error.code, `Download failed: ${error.message}`, error.status);
2597
2536
  }
2598
- };
2599
- const result = await config.authorizeUser(req, authRequest);
2600
- if (result instanceof Response)
2601
- return result;
2602
- if (!Array.isArray(result.approvedScopes) || !result.approvedScopes.every((scope2) => typeof scope2 === "string")) {
2603
- throw new Error("authorizeUser must return approvedScopes as an array of strings");
2604
- }
2605
- const approvedScopes = [...new Set(result.approvedScopes)];
2606
- if (approvedScopes.some((approvedScope2) => !requestedScopes.includes(approvedScope2) || config.scopesSupported !== undefined && !supportedScopes.has(approvedScope2))) {
2607
- throw new Error("authorizeUser approvedScopes must be a subset of requested scopes");
2537
+ const managedError = managedFileAppError(error);
2538
+ if (managedError)
2539
+ throw managedError;
2540
+ throw error;
2608
2541
  }
2609
- const approvedScope = approvedScopes.length > 0 ? approvedScopes.join(" ") : undefined;
2610
- const code = randomUUID();
2611
- await config.codes.save(code, {
2612
- clientId,
2613
- redirectUri,
2614
- codeChallenge,
2615
- codeChallengeMethod: "S256",
2616
- resource,
2617
- scope: approvedScope,
2618
- userId: result.userId,
2619
- expiresAt: Date.now() + AUTH_CODE_TTL_MS
2620
- });
2621
- return redirectToClient(redirectUri, { code, ...state && { state } });
2622
2542
  }
2623
- };
2624
- const issueAccessToken = (userId, audience, clientId, scope) => signJwt({ scope, client_id: clientId }, config.signingSecret, {
2625
- issuer: config.issuer,
2626
- audience,
2627
- subject: userId,
2628
- expiresInSec: ttl
2629
2543
  });
2630
- const tokenRoute = {
2631
- method: "ALL",
2632
- path: tokenPath,
2633
- handler: async (req) => {
2634
- if (req.method === "OPTIONS")
2635
- return new Response(null, { status: 204, headers: PUBLIC_CORS2 });
2636
- if (req.method !== "POST")
2637
- return oauthError("invalid_request", "POST required", 405);
2638
- const form = await readForm(req);
2639
- const grantType = form.get("grant_type");
2640
- if (grantType === "authorization_code") {
2641
- const code = form.get("code");
2642
- const verifier = form.get("code_verifier");
2643
- const redirectUri = form.get("redirect_uri");
2644
- const clientId = form.get("client_id");
2645
- if (!code || !verifier || !redirectUri || !clientId) {
2646
- return oauthError("invalid_request", "code, code_verifier, redirect_uri, client_id required");
2647
- }
2648
- const data = await config.codes.take(code);
2649
- if (!data)
2650
- return oauthError("invalid_grant", "Unknown or used authorization code");
2651
- if (data.expiresAt < Date.now())
2652
- return oauthError("invalid_grant", "Authorization code expired");
2653
- if (data.clientId !== clientId)
2654
- return oauthError("invalid_grant", "client_id mismatch");
2655
- if (data.redirectUri !== redirectUri)
2656
- return oauthError("invalid_grant", "redirect_uri mismatch");
2657
- if (!await verifyPkce(verifier, data.codeChallenge)) {
2658
- return oauthError("invalid_grant", "PKCE verification failed");
2659
- }
2660
- const accessToken = await issueAccessToken(data.userId, data.resource, data.clientId, data.scope);
2661
- let refreshToken;
2662
- if (config.refreshTokens) {
2663
- refreshToken = randomUUID();
2664
- await config.refreshTokens.save(refreshToken, {
2665
- clientId: data.clientId,
2666
- resource: data.resource,
2667
- scope: data.scope,
2668
- userId: data.userId
2669
- });
2670
- }
2671
- return json({
2672
- access_token: accessToken,
2673
- token_type: "Bearer",
2674
- expires_in: ttl,
2675
- ...data.scope && { scope: data.scope },
2676
- ...refreshToken && { refresh_token: refreshToken }
2677
- });
2678
- }
2679
- if (grantType === "refresh_token") {
2680
- if (!config.refreshTokens) {
2681
- return oauthError("unsupported_grant_type", "refresh_token grant is not enabled");
2682
- }
2683
- const token = form.get("refresh_token");
2684
- const clientId = form.get("client_id");
2685
- if (!token || !clientId)
2686
- return oauthError("invalid_request", "refresh_token and client_id required");
2687
- const data = await config.refreshTokens.take(token);
2688
- if (!data)
2689
- return oauthError("invalid_grant", "Unknown or used refresh token");
2690
- if (data.clientId !== clientId)
2691
- return oauthError("invalid_grant", "client_id mismatch");
2692
- const accessToken = await issueAccessToken(data.userId, data.resource, data.clientId, data.scope);
2693
- const newRefresh = randomUUID();
2694
- await config.refreshTokens.save(newRefresh, data);
2695
- return json({
2696
- access_token: accessToken,
2697
- token_type: "Bearer",
2698
- expires_in: ttl,
2699
- ...data.scope && { scope: data.scope },
2700
- refresh_token: newRefresh
2701
- });
2544
+ }
2545
+ // src/tools/transfer/define-upload-tool.ts
2546
+ import { z as z6 } from "zod";
2547
+
2548
+ // src/tools/transfer/upload-core.ts
2549
+ function runUploadOperation(files, path, upload, signal) {
2550
+ signal?.throwIfAborted();
2551
+ return files.read(path, { signal }).then((source) => upload(source, signal));
2552
+ }
2553
+
2554
+ // src/tools/transfer/define-upload-tool.ts
2555
+ var UploadToolInputSchema = z6.object({
2556
+ path: ManagedFilePathSchema.describe("Path relative to the configured file boundary")
2557
+ });
2558
+ function defineUploadTool(config) {
2559
+ return defineRuntimeTool({
2560
+ name: config.name ?? "upload",
2561
+ description: config.description,
2562
+ identity: managedNativeIdentity(config.identity, "POST"),
2563
+ input: UploadToolInputSchema,
2564
+ output: config.output,
2565
+ transports: config.transports,
2566
+ annotations: config.annotations,
2567
+ present: config.present,
2568
+ handler: async (context) => {
2569
+ try {
2570
+ return await runUploadOperation(config.files, context.input.path, (source) => config.upload(source, context), context.signal);
2571
+ } catch (error) {
2572
+ const managedError = managedFileAppError(error);
2573
+ if (managedError)
2574
+ throw managedError;
2575
+ throw error;
2702
2576
  }
2703
- return oauthError("unsupported_grant_type", `Unsupported grant_type: ${grantType ?? "none"}`);
2704
2577
  }
2578
+ });
2579
+ }
2580
+ // src/tools/transfer/view-file.ts
2581
+ import { extname as extname2 } from "node:path";
2582
+ var MAX_INLINE_BYTES = 20 * 1024 * 1024;
2583
+ var EXT_MIME = {
2584
+ ".png": "image/png",
2585
+ ".jpg": "image/jpeg",
2586
+ ".jpeg": "image/jpeg",
2587
+ ".gif": "image/gif",
2588
+ ".webp": "image/webp",
2589
+ ".mp3": "audio/mpeg",
2590
+ ".wav": "audio/wav",
2591
+ ".ogg": "audio/ogg",
2592
+ ".m4a": "audio/mp4",
2593
+ ".mp4": "video/mp4",
2594
+ ".webm": "video/webm",
2595
+ ".mov": "video/quicktime"
2596
+ };
2597
+ async function fetchSource(pathOrUrl, options, maxBytes) {
2598
+ const extMime = EXT_MIME[extname2(pathOrUrl).toLowerCase()];
2599
+ if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
2600
+ const url = new URL(pathOrUrl);
2601
+ const res = await fetchGuarded(url, options.allowPrivateHosts ?? false, {
2602
+ timeoutMs: options.timeoutMs,
2603
+ signal: options.signal
2604
+ });
2605
+ if (!res.ok)
2606
+ throw new AppError("VIEW_HTTP_ERROR", `HTTP ${res.status}`, 502);
2607
+ const headerMime = mediaTypeEssence(res.headers.get("content-type"));
2608
+ const mimeType = headerMime || extMime || "application/octet-stream";
2609
+ if (mimeType.startsWith("video/")) {
2610
+ await res.body?.cancel();
2611
+ return { video: true, mimeType, bytesRead: 0 };
2612
+ }
2613
+ const declared = Number(res.headers.get("content-length") ?? 0);
2614
+ if (declared > maxBytes) {
2615
+ await res.body?.cancel();
2616
+ return { tooLarge: true, mimeType, bytesRead: 0 };
2617
+ }
2618
+ const buffer = await readCapped(res, maxBytes);
2619
+ return buffer ? { buffer, mimeType, bytesRead: buffer.length } : { tooLarge: true, mimeType, bytesRead: maxBytes };
2620
+ }
2621
+ if (!options.files) {
2622
+ throw new AppError("FILE_INVALID_PATH", "local file paths are disabled — set files to allow them", 400);
2623
+ }
2624
+ options.signal?.throwIfAborted();
2625
+ if (!extMime) {
2626
+ throw new AppError("FILE_INSPECTION_REJECTED", "refusing to read a non-media file", 422);
2627
+ }
2628
+ try {
2629
+ const source = await options.files.read(pathOrUrl, {
2630
+ maxBytes,
2631
+ signal: options.signal
2632
+ });
2633
+ const buffer = Buffer.from(source.bytes);
2634
+ return {
2635
+ buffer,
2636
+ mimeType: source.ref.mediaType ?? extMime,
2637
+ bytesRead: buffer.length
2638
+ };
2639
+ } catch (error) {
2640
+ if (error instanceof ManagedFileError && error.code === "FILE_TOO_LARGE") {
2641
+ return { tooLarge: true, mimeType: extMime, bytesRead: maxBytes };
2642
+ }
2643
+ throw error;
2644
+ }
2645
+ }
2646
+ async function resolveMediaWithinBudget(pathOrUrl, options, maxBytes) {
2647
+ if (maxBytes <= 0) {
2648
+ return {
2649
+ content: [
2650
+ { type: "text", text: `[media] total inline budget exhausted — ${pathOrUrl}` }
2651
+ ],
2652
+ bytes: 0
2653
+ };
2654
+ }
2655
+ const extMime = EXT_MIME[extname2(pathOrUrl).toLowerCase()];
2656
+ if (extMime?.startsWith("video/")) {
2657
+ return {
2658
+ content: [{ type: "text", text: `[video] ${extMime} — ${pathOrUrl}` }],
2659
+ bytes: 0
2660
+ };
2661
+ }
2662
+ const result = await fetchSource(pathOrUrl, options, maxBytes);
2663
+ if ("video" in result) {
2664
+ return {
2665
+ content: [{ type: "text", text: `[video] ${result.mimeType} — ${pathOrUrl}` }],
2666
+ bytes: result.bytesRead
2667
+ };
2668
+ }
2669
+ if ("tooLarge" in result) {
2670
+ return {
2671
+ content: [
2672
+ { type: "text", text: `[${result.mimeType}] too large to inline — ${pathOrUrl}` }
2673
+ ],
2674
+ bytes: result.bytesRead
2675
+ };
2676
+ }
2677
+ const { buffer, mimeType } = result;
2678
+ const sizeKb = (buffer.length / 1024).toFixed(0);
2679
+ if (mimeType.startsWith("image/")) {
2680
+ return {
2681
+ content: [
2682
+ {
2683
+ type: "image",
2684
+ data: buffer.toString("base64"),
2685
+ mimeType,
2686
+ annotations: { audience: ["user", "assistant"], priority: 0.9 }
2687
+ },
2688
+ { type: "text", text: `[image] ${mimeType}, ${sizeKb}KB` }
2689
+ ],
2690
+ bytes: buffer.length
2691
+ };
2692
+ }
2693
+ if (mimeType.startsWith("audio/")) {
2694
+ return {
2695
+ content: [
2696
+ {
2697
+ type: "audio",
2698
+ data: buffer.toString("base64"),
2699
+ mimeType,
2700
+ annotations: { audience: ["user", "assistant"], priority: 0.9 }
2701
+ },
2702
+ { type: "text", text: `[audio] ${mimeType}, ${sizeKb}KB` }
2703
+ ],
2704
+ bytes: buffer.length
2705
+ };
2706
+ }
2707
+ return {
2708
+ content: [{ type: "text", text: `[${mimeType}] ${sizeKb}KB — ${pathOrUrl}` }],
2709
+ bytes: buffer.length
2705
2710
  };
2706
- return dcrEnabled ? [metadataRoute, registerRoute, authorizeRoute, tokenRoute] : [metadataRoute, authorizeRoute, tokenRoute];
2707
2711
  }
2708
- // src/tools/registry.ts
2709
- function defineToolRegistry(input) {
2710
- const defaults = [...input.defaults];
2711
- const slots = new Set;
2712
- for (const tool of defaults) {
2713
- if (!tool.name)
2714
- throw new Error("A default tool must carry a non-empty name");
2715
- if (slots.has(tool.name))
2716
- throw new Error(`Duplicate default tool name: ${tool.name}`);
2717
- slots.add(tool.name);
2718
- }
2719
- const disabled = new Set;
2720
- const replacements = new Map;
2721
- const assertKnown = (name, verb) => {
2722
- if (!slots.has(name)) {
2723
- throw new Error(`Tool registry ${verb}("${name}") names no default; declared: ${[...slots].sort().join(", ") || "(none)"}`);
2712
+ async function resolveMedia(pathOrUrl, options = {}) {
2713
+ return (await resolveMediaWithinBudget(pathOrUrl, options, MAX_INLINE_BYTES)).content;
2714
+ }
2715
+ async function runViewFileOperation(paths, options = {}, signal) {
2716
+ const list = Array.isArray(paths) ? paths : [paths];
2717
+ const content = [];
2718
+ const errors = [];
2719
+ let remainingBytes = MAX_INLINE_BYTES;
2720
+ for (const pathOrUrl of list) {
2721
+ signal?.throwIfAborted();
2722
+ try {
2723
+ const resolved = await resolveMediaWithinBudget(pathOrUrl, { ...options, signal }, remainingBytes);
2724
+ remainingBytes -= resolved.bytes;
2725
+ content.push(...resolved.content);
2726
+ } catch (error) {
2727
+ if (signal?.aborted)
2728
+ throw signal.reason ?? error;
2729
+ const message = normalizeFileToolError(error).message;
2730
+ errors.push({ path: pathOrUrl, message });
2731
+ content.push({ type: "text", text: `[${pathOrUrl}] Error: ${message}` });
2724
2732
  }
2733
+ }
2734
+ return ViewFileOutputSchema.parse({ content, errors });
2735
+ }
2736
+ function mountViewFile(server, options = {}) {
2737
+ server.registerTool("view_file", {
2738
+ description: "View media (image, audio, video) by URL or local path — returns it as content you can SEE / HEAR. Pass several paths to view multiple files at once. Use it on a generation `output` url to inspect the result.",
2739
+ inputSchema: ViewFileInputSchema.shape,
2740
+ annotations: { title: "View Media", readOnlyHint: true, idempotentHint: true }
2741
+ }, async (args) => {
2742
+ const result = await runViewFileOperation(args.paths, options);
2743
+ return { content: result.content };
2744
+ });
2745
+ }
2746
+
2747
+ // src/tools/transfer/define-view-file-tool.ts
2748
+ function defineViewFileTool(config) {
2749
+ const defaults = {
2750
+ mcp: (output) => ({ content: output.content }),
2751
+ agent: (output) => ({
2752
+ type: "content",
2753
+ value: output.content.map((part) => {
2754
+ if (part.type === "text")
2755
+ return part;
2756
+ return {
2757
+ type: "file",
2758
+ data: { type: "data", data: part.data },
2759
+ mediaType: part.mimeType
2760
+ };
2761
+ })
2762
+ })
2725
2763
  };
2726
- const builder = {
2727
- replace(name, tool) {
2728
- assertKnown(name, "replace");
2729
- if (disabled.has(name)) {
2730
- throw new Error(`Tool registry cannot replace "${name}": it is disabled`);
2731
- }
2732
- if (replacements.has(name)) {
2733
- throw new Error(`Tool registry already replaced "${name}"`);
2734
- }
2735
- if (tool.name !== name) {
2736
- throw new Error(`A replacement for "${name}" must carry that name, not "${tool.name}"`);
2737
- }
2738
- replacements.set(name, tool);
2739
- return builder;
2740
- },
2741
- disable(name) {
2742
- assertKnown(name, "disable");
2743
- disabled.add(name);
2744
- return builder;
2764
+ return defineRuntimeTool({
2765
+ name: config.name ?? "view_file",
2766
+ description: config.description,
2767
+ identity: managedNativeIdentity(config.identity, "GET"),
2768
+ input: ViewFileInputSchema,
2769
+ output: ViewFileOutputSchema,
2770
+ transports: config.transports,
2771
+ annotations: config.annotations ?? {
2772
+ title: "View Media",
2773
+ readOnlyHint: true,
2774
+ idempotentHint: true
2745
2775
  },
2746
- build() {
2747
- const tools = [];
2748
- const seen = new Set;
2749
- for (const tool of defaults) {
2750
- if (disabled.has(tool.name))
2751
- continue;
2752
- const effective = replacements.get(tool.name) ?? tool;
2753
- if (seen.has(effective.name)) {
2754
- throw new Error(`Duplicate composed tool name: ${effective.name}`);
2755
- }
2756
- seen.add(effective.name);
2757
- tools.push(effective);
2776
+ present: { ...defaults, ...config.present },
2777
+ handler: (context) => runViewFileOperation(context.input.paths, {
2778
+ files: config.files,
2779
+ allowPrivateHosts: config.allowPrivateHosts,
2780
+ timeoutMs: config.timeoutMs
2781
+ }, context.signal)
2782
+ });
2783
+ }
2784
+ // src/tools/transfer/mount-download.ts
2785
+ import { z as z7 } from "zod";
2786
+ function mountDownload(server, config) {
2787
+ const name = config.name ?? "download";
2788
+ assertToolName(name, "<native>", "download");
2789
+ server.registerTool(name, { description: config.description, inputSchema: z7.object(config.inputSchema) }, async (rawArgs) => {
2790
+ const args = isRecord(rawArgs) ? rawArgs : {};
2791
+ try {
2792
+ const url = await config.resolveUrl(args);
2793
+ if (!url)
2794
+ return textResult("Nothing to download.", true);
2795
+ const result = await runDownloadOperation({
2796
+ url,
2797
+ files: config.files,
2798
+ path: config.pathFromArgs?.(args),
2799
+ allowPrivateHosts: config.allowPrivateHosts,
2800
+ maxBytes: config.maxBytes,
2801
+ timeoutMs: config.timeoutMs
2802
+ });
2803
+ return textResult(JSON.stringify(result, null, 2));
2804
+ } catch (error) {
2805
+ if (error instanceof DownloadOperationError) {
2806
+ return textResult(`Download failed [${error.code}]: ${error.message}`, true);
2758
2807
  }
2759
- return { tools, names: tools.map((tool) => tool.name) };
2808
+ const normalized = normalizeFileToolError(error);
2809
+ return textResult(`Download failed [${normalized.code}]: ${normalized.message}`, true);
2760
2810
  }
2761
- };
2762
- return builder;
2811
+ });
2763
2812
  }
2764
- // src/tools/tool-logger.ts
2765
- function createToolLogger(config = {}) {
2766
- const log = config.log ?? ((line) => console.error(line));
2767
- return {
2768
- afterToolCall: ({ toolName, result, durationMs, context, endpoint }) => {
2769
- const record = {
2770
- tool: toolName,
2771
- service: endpoint.serviceName,
2772
- action: endpoint.key,
2773
- ok: result.ok,
2774
- code: result.ok ? undefined : result.code,
2775
- durationMs: Math.round(durationMs),
2776
- source: String(context.source),
2777
- traceId: getTraceId()
2778
- };
2779
- const codePart = result.ok ? "" : ` ${result.code}`;
2780
- log(`[tool] ${result.ok ? "ok" : "warn"} ${toolName} (${endpoint.serviceName}.${endpoint.key})${codePart} ${record.durationMs}ms`);
2781
- config.onRecord?.(record);
2813
+ // src/tools/transfer/mount-upload.ts
2814
+ function mountUpload(server, config) {
2815
+ const name = config.name ?? "upload";
2816
+ assertToolName(name, "<native>", "upload");
2817
+ server.registerTool(name, {
2818
+ description: config.description,
2819
+ inputSchema: {
2820
+ path: ManagedFilePathSchema.describe("Path relative to the configured file boundary")
2782
2821
  }
2783
- };
2784
- }
2785
- // src/tools/toolkit.ts
2786
- function createToolkit() {
2787
- return {
2788
- mountMcp: (server, services, config) => mountMcp(server, services, config),
2789
- mountAgent: (services, config) => mountAgent(services, config),
2790
- buildMcpServer: (config, auth) => buildMcpServer(config, auth),
2791
- createMcpHandler: (config) => createMcpHandler(config),
2792
- createStdioMcpServer: (config) => createStdioMcpServer(config),
2793
- createCli: (config) => createCli(config)
2794
- };
2822
+ }, async (rawArgs) => {
2823
+ const args = isRecord(rawArgs) ? rawArgs : {};
2824
+ const path = typeof args.path === "string" ? args.path : "";
2825
+ if (!path)
2826
+ return textResult("Provide `path`.", true);
2827
+ try {
2828
+ const uploaded = await runUploadOperation(config.files, path, config.upload);
2829
+ return textResult(JSON.stringify(uploaded ?? null, null, 2));
2830
+ } catch (error) {
2831
+ const normalized = normalizeFileToolError(error);
2832
+ return textResult(`Upload failed [${normalized.code}]: ${normalized.message}`, true);
2833
+ }
2834
+ });
2795
2835
  }
2796
2836
  // src/tools/transports.ts
2797
- var ALL_TRANSPORTS = ["HTTP", "MCP", "AGENT", "CLI"];
2798
- var TOOL_TRANSPORTS2 = ["MCP", "AGENT", "CLI"];
2799
2837
  function emptyCounts() {
2800
2838
  return { HTTP: 0, MCP: 0, AGENT: 0, CLI: 0 };
2801
2839
  }
@@ -2805,7 +2843,7 @@ function contractCounts(service) {
2805
2843
  if (!method.expose || method.expose.includes("HTTP"))
2806
2844
  counts.HTTP += 1;
2807
2845
  }
2808
- for (const transport of TOOL_TRANSPORTS2) {
2846
+ for (const transport of TOOL_TRANSPORTS) {
2809
2847
  counts[transport] = collectToolSurface({
2810
2848
  surface: { services: [service] },
2811
2849
  transport,
@@ -2817,7 +2855,7 @@ function contractCounts(service) {
2817
2855
  }
2818
2856
  function runtimeCounts(runtimeTools) {
2819
2857
  const counts = emptyCounts();
2820
- for (const transport of TOOL_TRANSPORTS2) {
2858
+ for (const transport of TOOL_TRANSPORTS) {
2821
2859
  counts[transport] = collectToolSurface({
2822
2860
  surface: { runtimeTools },
2823
2861
  transport,
@@ -2892,7 +2930,6 @@ export {
2892
2930
  defineToolRegistry,
2893
2931
  defineRuntimeTool,
2894
2932
  defineDownloadTool,
2895
- defineCliCommand,
2896
2933
  defineAsyncOperationContract,
2897
2934
  defineAsyncOperation,
2898
2935
  createToolkit,
@@ -2904,7 +2941,6 @@ export {
2904
2941
  createMcpHttpRoute,
2905
2942
  createMcpHandler,
2906
2943
  createLocalStepDurability,
2907
- createCli,
2908
2944
  createAsyncOperationSnapshotSchema,
2909
2945
  composeToolLifecycle,
2910
2946
  collectTools,