agents 0.28.0 → 0.29.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 (121) hide show
  1. package/dist/{agent-routing-QDcchcQP.d.ts → agent-routing-CDbpVjYq.d.ts} +18 -8
  2. package/dist/agent-routing.d.ts +1 -1
  3. package/dist/agent-tool-types.d.ts +1 -1
  4. package/dist/{agent-tools-DHL2XI7u.d.ts → agent-tools-C8DH8Yii.d.ts} +2 -2
  5. package/dist/agent-tools.d.ts +1 -1
  6. package/dist/{ai-ahruGw7e.d.ts → ai-iXw0uOGj.d.ts} +3 -3
  7. package/dist/{ai-sdk-Bdsf6mSA.js → ai-sdk-DRPh2rRa.js} +218 -132
  8. package/dist/ai-sdk-DRPh2rRa.js.map +1 -0
  9. package/dist/browser/ai.d.ts +1 -1
  10. package/dist/browser/ai.js +2 -2
  11. package/dist/browser/index.d.ts +8 -8
  12. package/dist/browser/index.js +3 -3
  13. package/dist/browser/tools/ai-sdk.d.ts +2 -2
  14. package/dist/browser/tools/ai-sdk.js +1 -1
  15. package/dist/browser/tools/pi.d.ts +1 -1
  16. package/dist/browser/tools/pi.js +3 -3
  17. package/dist/browser/tools/pi.js.map +1 -1
  18. package/dist/browser/tools/tanstack-ai.d.ts +2 -2
  19. package/dist/browser/tools/tanstack-ai.js +1 -1
  20. package/dist/{browser-CEdNlsaD.js → browser-DccPw_wW.js} +130 -79
  21. package/dist/browser-DccPw_wW.js.map +1 -0
  22. package/dist/{browser-D30VlpcA.d.ts → browser-DlXhSAdf.d.ts} +41 -37
  23. package/dist/{browser-tool-4iomavs_.js → browser-tool-B1mhifin.js} +3 -3
  24. package/dist/{browser-tool-4iomavs_.js.map → browser-tool-B1mhifin.js.map} +1 -1
  25. package/dist/{browser-tool-vtrPOV8e.d.ts → browser-tool-Cxjk5sgG.d.ts} +2 -2
  26. package/dist/chat/index.d.ts +143 -71
  27. package/dist/chat/index.js +269 -110
  28. package/dist/chat/index.js.map +1 -1
  29. package/dist/chat-sdk/index.d.ts +1 -1
  30. package/dist/chat-sdk/index.js +1 -1
  31. package/dist/cli.js +33 -33
  32. package/dist/{client-BGIQKJhX.d.ts → client-C27CtRZr.d.ts} +2 -2
  33. package/dist/client.d.ts +1 -1
  34. package/dist/{connector-DEXzVoaY.d.ts → connector-20DJ6p4k.d.ts} +2 -2
  35. package/dist/{connector-CtZylXdk.js → connector-DAXBOIIi.js} +2 -2
  36. package/dist/{connector-CtZylXdk.js.map → connector-DAXBOIIi.js.map} +1 -1
  37. package/dist/experimental/channels/web/index.js +1 -1
  38. package/dist/{handler-stateless-VvrWSAVA.js → handler-stateless-Bly_nSH3.js} +12 -3
  39. package/dist/handler-stateless-Bly_nSH3.js.map +1 -0
  40. package/dist/harness/container/index.js +1 -1
  41. package/dist/harness/container/index.js.map +1 -1
  42. package/dist/harness/opencode/index.d.ts +182 -6
  43. package/dist/harness/opencode/index.js +354 -23
  44. package/dist/harness/opencode/index.js.map +1 -1
  45. package/dist/harness/pi/index.d.ts +13 -5
  46. package/dist/harness/pi/index.js +8 -214
  47. package/dist/harness/pi/index.js.map +1 -1
  48. package/dist/{images-Ce9SCu3X.js → images-xaDv2Fqe.js} +662 -152
  49. package/dist/images-xaDv2Fqe.js.map +1 -0
  50. package/dist/index-DCxk3OSJ.d.ts +378 -0
  51. package/dist/{index-kbSUIer3.d.ts → index-iv9FTXX9.d.ts} +80 -112
  52. package/dist/index.d.ts +1 -1
  53. package/dist/index.js +1 -1
  54. package/dist/{input-schema-D2QAtHZH.js → input-schema-B0Etuo-o.js} +2 -2
  55. package/dist/{input-schema-D2QAtHZH.js.map → input-schema-B0Etuo-o.js.map} +1 -1
  56. package/dist/lifecycle/index.js +1 -1
  57. package/dist/{lifecycle-DGorpAmA.js → lifecycle-ClT4DmF2.js} +11 -3
  58. package/dist/lifecycle-ClT4DmF2.js.map +1 -0
  59. package/dist/mcp/client/index.d.ts +1 -1
  60. package/dist/mcp/index.d.ts +1 -1
  61. package/dist/mcp/index.js +17 -3
  62. package/dist/mcp/index.js.map +1 -1
  63. package/dist/mcp/server/index.js +1 -1
  64. package/dist/models/ai-sdk/index.d.ts +31 -326
  65. package/dist/models/ai-sdk/index.js +1 -1
  66. package/dist/models/opencode/index.d.ts +25 -13
  67. package/dist/models/opencode/index.js +16 -11
  68. package/dist/models/opencode/index.js.map +1 -1
  69. package/dist/models/pi-ai/index.d.ts +17 -13
  70. package/dist/models/pi-ai/index.js +89 -36
  71. package/dist/models/pi-ai/index.js.map +1 -1
  72. package/dist/react.d.ts +2 -2
  73. package/dist/routing/index.d.ts +1 -1
  74. package/dist/serializable.d.ts +1 -1
  75. package/dist/{session-store-DoPY12xM.d.ts → session-store-_IK6dV-w.d.ts} +14 -2
  76. package/dist/{session-store-Do4xkDcf.js → session-store-yvwq1f9x.js} +55 -2
  77. package/dist/session-store-yvwq1f9x.js.map +1 -0
  78. package/dist/{settings-C45hDliX.js → settings-Dk2il2Hf.js} +10 -4
  79. package/dist/settings-Dk2il2Hf.js.map +1 -0
  80. package/dist/skills/index.d.ts +22 -28
  81. package/dist/skills/index.js +183 -143
  82. package/dist/skills/index.js.map +1 -1
  83. package/dist/{src-CQxJFv4l.js → src-6lr7MtM_.js} +3 -3
  84. package/dist/src-6lr7MtM_.js.map +1 -0
  85. package/dist/sub-routing.d.ts +1 -1
  86. package/dist/{tool-cQRkwSZZ.js → tool-DkT_DJBm.js} +2 -2
  87. package/dist/tool-DkT_DJBm.js.map +1 -0
  88. package/dist/transport-CPh7YGiw.d.ts +623 -0
  89. package/dist/voice/index.d.ts +1 -1
  90. package/dist/web/fetch/tools/pi.js +2 -2
  91. package/dist/web/fetch/tools/pi.js.map +1 -1
  92. package/dist/web/search/index.js +1 -1
  93. package/dist/web/search/tools/ai-sdk.js +2 -2
  94. package/dist/web/search/tools/pi.js +3 -3
  95. package/dist/web/search/tools/pi.js.map +1 -1
  96. package/dist/web/search/tools/tanstack-ai.js +2 -2
  97. package/dist/websockets/index.js +1 -1
  98. package/dist/{websockets-C3zy8YR0.js → websockets-CqxnntmE.js} +2 -2
  99. package/dist/{websockets-C3zy8YR0.js.map → websockets-CqxnntmE.js.map} +1 -1
  100. package/dist/workflow-types.d.ts +1 -1
  101. package/dist/workflows.d.ts +1 -1
  102. package/dist/workflows.js +1 -1
  103. package/docs/browse-the-web.md +1 -1
  104. package/docs/chat-agents.md +19 -18
  105. package/docs/harnesses/pi.md +47 -18
  106. package/docs/mcp-client.md +1 -1
  107. package/docs/mcp-servers.md +1 -1
  108. package/docs/models-pi-ai.md +70 -17
  109. package/docs/models.md +219 -152
  110. package/package.json +14 -14
  111. package/dist/ai-sdk-Bdsf6mSA.js.map +0 -1
  112. package/dist/browser-CEdNlsaD.js.map +0 -1
  113. package/dist/handler-stateless-VvrWSAVA.js.map +0 -1
  114. package/dist/images-Ce9SCu3X.js.map +0 -1
  115. package/dist/lifecycle-DGorpAmA.js.map +0 -1
  116. package/dist/session-store-Do4xkDcf.js.map +0 -1
  117. package/dist/settings-C45hDliX.js.map +0 -1
  118. package/dist/settings-NgGwF7Qt.d.ts +0 -331
  119. package/dist/src-CQxJFv4l.js.map +0 -1
  120. package/dist/tool-cQRkwSZZ.js.map +0 -1
  121. package/dist/transport-k2mFrtL8.d.ts +0 -200
@@ -1,17 +1,16 @@
1
1
  import {
2
- n as GatewayOptions,
3
- o as CloudflareEmbeddingModelId,
4
- p as WorkersAIModelId,
5
- r as ModelOptions,
6
- t as AISettings
7
- } from "../../settings-NgGwF7Qt.js";
8
- import {
2
+ _ as DynamicRouteId,
9
3
  a as CloudflareAIError,
4
+ b as WorkersAIModelId,
5
+ c as GatewayOptions,
6
+ f as CloudflareEmbeddingModelId,
10
7
  i as CloudflareAIAttempt,
8
+ l as ModelOptions,
11
9
  n as GATEWAY_PROVIDER_NAMES,
12
10
  o as CloudflareAIErrorCode,
13
- r as GatewayProviderName
14
- } from "../../transport-k2mFrtL8.js";
11
+ r as GatewayProviderName,
12
+ s as AISettings
13
+ } from "../../transport-CPh7YGiw.js";
15
14
  import {
16
15
  Api,
17
16
  AssistantImages,
@@ -107,7 +106,7 @@ declare const CLOUDFLARE_AI_API = "cloudflare-ai";
107
106
  type CloudflareAIApi = typeof CLOUDFLARE_AI_API;
108
107
  /**
109
108
  * The pi-ai provider id. A `Models` registry resolves specifiers as
110
- * `cloudflare/<id>`, e.g. `cloudflare/anthropic/claude-opus-4-8`.
109
+ * `cloudflare/<id>`, e.g. `cloudflare/anthropic/claude-opus-4.8`.
111
110
  *
112
111
  * @beta This surface is in beta and may change.
113
112
  */
@@ -201,6 +200,8 @@ type StreamFn = (
201
200
  interface AI {
202
201
  /** A Workers AI model: Cloudflare's own catalog, on the run path. */
203
202
  (modelId: WorkersAIModelId, options?: PiModelOptions): CloudflareModel;
203
+ /** An AI Gateway dynamic route, `dynamic/<route>`, on the Workers AI wire. */
204
+ (route: DynamicRouteId, options?: PiModelOptions): CloudflareModel;
204
205
  /** Any pi-ai model, routed through AI Gateway with its metadata intact. */
205
206
  <TApi extends Api>(model: Model<TApi>, options?: PiModelOptions): Model<TApi>;
206
207
  /**
@@ -210,6 +211,7 @@ interface AI {
210
211
  (target: string | Model<Api>, options?: PiModelOptions): RoutedModel;
211
212
  /** Same as calling the provider directly. */
212
213
  model(modelId: WorkersAIModelId, options?: PiModelOptions): CloudflareModel;
214
+ model(route: DynamicRouteId, options?: PiModelOptions): CloudflareModel;
213
215
  model<TApi extends Api>(
214
216
  model: Model<TApi>,
215
217
  options?: PiModelOptions
@@ -263,9 +265,10 @@ interface AI {
263
265
  /**
264
266
  * Creates a pi-ai provider over Cloudflare's AI.
265
267
  *
266
- * The Workers AI binding is the only way in — Workers AI models go through
267
- * `env.AI.run`, vendor models through `env.AI.gateway(id).run`, and there is
268
- * no HTTP transport and no API token. Gateway options may be given flat
268
+ * Workers AI and catalog models go through `env.AI.run`, other model objects
269
+ * through the binding's `fetch` to AI Gateway's provider-native path. With
270
+ * `{ accountId, apiToken }` instead of `binding`, the same requests go to the
271
+ * REST API and to `gateway.ai.cloudflare.com`. Gateway options may be given flat
269
272
  * (`{ binding, id: "prod", cacheTtl: 60 }`) or nested under `gateway`;
270
273
  * per-model options and per-call pi-ai options (`headers`, `metadata`,
271
274
  * `sessionId`) override them in that order.
@@ -297,6 +300,7 @@ export {
297
300
  type CloudflareEmbeddingModelId,
298
301
  type CloudflareImagesModel,
299
302
  type CloudflareModel,
303
+ type DynamicRouteId,
300
304
  FALLBACK_DIAGNOSTIC,
301
305
  type FallbackAttempt,
302
306
  GATEWAY_PROVIDER_NAMES,
@@ -1,5 +1,5 @@
1
- import { _ as isGatewayErrorEnvelope, a as normalizeChatCompletion, c as gatewaySlugForBaseUrl, d as resolveGatewayProvider, f as createTransport, g as headersToObject, h as errorFromResponse, i as normalizeChatCompletionsStream, l as isGatewayShapedUrl, m as errorFromGatewayEnvelope, n as imageMediaTypeOf, o as prepareChatCompletionsRequest, r as sseDataStream, s as GATEWAY_PROVIDER_NAMES, t as buildImageInput, v as unwrapEnvelope, x as isAbortError, y as CloudflareAIError } from "../../images-Ce9SCu3X.js";
2
- import { i as mergeModelOptions, o as resolveOptions, r as gatewayLayerOf } from "../../settings-C45hDliX.js";
1
+ import { C as isCatalogModelId, T as isRunPathLanguageModelId, _ as isGatewayErrorEnvelope, a as normalizeChatCompletion, c as gatewaySlugForBaseUrl, d as resolveGatewayProvider, f as createTransport, g as headersToObject, h as errorFromResponse, i as normalizeChatCompletionsStream, l as isGatewayShapedUrl, m as errorFromGatewayEnvelope, n as imageMediaTypeOf, o as prepareChatCompletionsRequest, r as sseDataStream, s as GATEWAY_PROVIDER_NAMES, t as buildImageInput, v as unwrapEnvelope, x as isAbortError, y as CloudflareAIError } from "../../images-xaDv2Fqe.js";
2
+ import { i as mergeModelOptions, o as resolveOptions, r as gatewayLayerOf } from "../../settings-Dk2il2Hf.js";
3
3
  import { calculateCost, clampThinkingLevel, createAssistantMessageEventStream, createProvider, getCurrentTools, normalizeContext, parseStreamingJson } from "@earendil-works/pi-ai";
4
4
  import { CLOUDFLARE_AI_GATEWAY_MODELS } from "@earendil-works/pi-ai/providers/cloudflare-ai-gateway.models";
5
5
  import { CLOUDFLARE_WORKERS_AI_MODELS } from "@earendil-works/pi-ai/providers/cloudflare-workers-ai.models";
@@ -19,7 +19,7 @@ import { clampOpenAIPromptCacheKey } from "@earendil-works/pi-ai/api/openai-prom
19
19
  const CLOUDFLARE_AI_API = "cloudflare-ai";
20
20
  /**
21
21
  * The pi-ai provider id. A `Models` registry resolves specifiers as
22
- * `cloudflare/<id>`, e.g. `cloudflare/anthropic/claude-opus-4-8`.
22
+ * `cloudflare/<id>`, e.g. `cloudflare/anthropic/claude-opus-4.8`.
23
23
  *
24
24
  * @beta This surface is in beta and may change.
25
25
  */
@@ -125,9 +125,22 @@ function buildWorkersAIModel(modelId, options) {
125
125
  function slugOf(entry) {
126
126
  return entry.baseUrl.split("/").filter((part) => part !== "").at(-1);
127
127
  }
128
+ /**
129
+ * The id AI Gateway's model catalog — `env.AI.run` — knows a registry entry
130
+ * by. pi-ai spells Claude ids as Anthropic does (`claude-opus-4-8`), because pi
131
+ * sends them through the gateway's Anthropic passthrough, while the catalog
132
+ * puts a dot between version numbers (`anthropic/claude-opus-4.8`) and answers
133
+ * `Model not found` to the dashed spelling. Checked against the live catalog
134
+ * for every entry in pi-ai 1.2: that dot is the only difference, and only
135
+ * Anthropic ids have it.
136
+ */
137
+ function gatewayCatalogId(slug, id) {
138
+ return `${slug}/${slug === "anthropic" ? id.replace(/(\d)-(\d)(?!\d)/g, "$1.$2") : id}`;
139
+ }
128
140
  let gatewayRegistry;
129
141
  /**
130
- * pi-ai's generated Cloudflare AI Gateway registry, keyed by `<slug>/<id>`.
142
+ * pi-ai's generated Cloudflare AI Gateway registry, keyed by the id AI
143
+ * Gateway's catalog runs each entry under (see {@link gatewayCatalogId}).
131
144
  * `/compat` entries are skipped: they alias `@cf/` ids the Workers AI registry
132
145
  * already declares, and those belong on the run path.
133
146
  */
@@ -136,7 +149,7 @@ function buildGatewayRegistry() {
136
149
  for (const entry of Object.values(CLOUDFLARE_AI_GATEWAY_MODELS)) {
137
150
  const slug = slugOf(entry);
138
151
  if (slug === void 0 || slug === "compat") continue;
139
- map.set(`${slug}/${entry.id}`, entry);
152
+ map.set(gatewayCatalogId(slug, entry.id), entry);
140
153
  }
141
154
  return map;
142
155
  }
@@ -205,7 +218,7 @@ const API_PATHS = {
205
218
  "openai-responses": "responses"
206
219
  };
207
220
  /**
208
- * The universal request's `endpoint` for a gateway-shaped base URL, whose path
221
+ * The provider-native `endpoint` for a gateway-shaped base URL, whose path
209
222
  * already ends at the provider slug, so what follows is the vendor's own path.
210
223
  */
211
224
  const GATEWAY_ENDPOINTS = {
@@ -482,26 +495,61 @@ function openAIVendorHeaders() {
482
495
  };
483
496
  }
484
497
  /**
485
- * Sends a vendor's own request body through AI Gateway's universal endpoint.
498
+ * The AI Gateway catalog id a model runs under on `/run`, when it has one: the
499
+ * `<author>/<model>` specifier it was resolved from, spelled as the gateway's
500
+ * catalog spells it (`anthropic/claude-opus-4.8`, not pi's `claude-opus-4-8`).
501
+ */
502
+ function catalogIdOf(model) {
503
+ const specifier = specifierOf(model);
504
+ return specifier !== void 0 && isCatalogModelId(specifier) ? specifier : void 0;
505
+ }
506
+ /**
507
+ * Sends a vendor-format request body through AI Gateway, by the binding.
508
+ *
509
+ * A model from pi's Cloudflare AI Gateway catalog runs on `env.AI.run` under
510
+ * its catalog id, in its native `format`: `/run` converts nothing, so the
511
+ * answer — streams included — is the shape this wire parses. Any other model
512
+ * goes to the gateway's provider-native path with the request unchanged; the
513
+ * provider slug comes from the model's base URL and the endpoint from its
514
+ * `api`.
486
515
  *
487
- * The provider slug comes from the model's base URL and the endpoint from its
488
- * `api`; the body is what pi-ai's converter built, `model` included, because
489
- * that is what the universal request carries. The gateway's *own* failures
490
- * (no such gateway, unified billing unpaid) are lifted into a
491
- * `CloudflareAIError` so fallback and retry classification behave as they do on
492
- * the Workers AI run path; a vendor's own error response is left alone for the
493
- * wire's parser to read.
516
+ * The gateway's *own* failures (no such gateway, unified billing unpaid, an
517
+ * unknown catalog model) become a `CloudflareAIError`, so fallback and retry
518
+ * behave as they do on the Workers AI run path; a vendor's own error response
519
+ * on the provider-native path is left for the wire's parser to read.
494
520
  */
495
- async function sendUniversal(request, transport, body, vendorHeaders, onResponse) {
521
+ async function sendGateway(request, transport, body, vendorHeaders, format, onResponse) {
496
522
  const { model, options, resolved } = request;
523
+ const catalogId = catalogIdOf(model);
524
+ if (catalogId !== void 0) {
525
+ const { model: _vendorId, ...input } = body;
526
+ const response = await transport.run({
527
+ format,
528
+ gateway: resolved.gateway,
529
+ headers: request.headers,
530
+ input,
531
+ model: catalogId,
532
+ signal: options.signal
533
+ });
534
+ onResponse?.(response);
535
+ if (!response.ok) throw await errorFromResponse(response, {
536
+ model: catalogId,
537
+ requestBodyValues: input,
538
+ url: transport.url
539
+ });
540
+ return response;
541
+ }
497
542
  const route = routeForModel(model);
498
- const response = await transport.universal({
543
+ const response = await transport.native({
544
+ body: JSON.stringify(body),
499
545
  endpoint: route.endpoint,
500
- extraHeaders: Object.keys(request.headers).length > 0 ? request.headers : void 0,
501
546
  gateway: resolved.gateway,
502
- headers: vendorHeaders,
547
+ headers: {
548
+ ...vendorHeaders,
549
+ ...request.headers
550
+ },
551
+ method: "POST",
503
552
  provider: route.provider,
504
- query: body,
505
553
  signal: options.signal
506
554
  });
507
555
  onResponse?.(response);
@@ -599,7 +647,7 @@ async function assertOk(response, context) {
599
647
  //#region src/models/pi-ai/wires/chat-completions.ts
600
648
  /**
601
649
  * Chat-completions wire: Workers AI on the run path, and any pi-ai model whose
602
- * `api` is `openai-completions` on AI Gateway's universal endpoint.
650
+ * `api` is `openai-completions`, through AI Gateway.
603
651
  *
604
652
  * The request is shaped with pi-ai's own message converter so replay rules
605
653
  * (tool-call id normalization, reasoning replay, role bridging) match pi's
@@ -836,7 +884,7 @@ var CompletionAssembler = class {
836
884
  const choice = chunk.choices?.[0];
837
885
  if (chunk.id !== void 0) this.output.responseId ??= chunk.id;
838
886
  if (typeof chunk.model === "string" && chunk.model.length > 0 && chunk.model !== this.model.id) this.output.responseModel ??= chunk.model;
839
- if (chunk.usage !== void 0) this.output.usage = usageFromOpenAI(this.model, chunk.usage);
887
+ if (chunk.usage !== void 0 && chunk.usage !== null) this.output.usage = usageFromOpenAI(this.model, chunk.usage);
840
888
  const finished = this.applyFinishReason(choice?.finish_reason);
841
889
  const delta = choice?.delta ?? choice?.message;
842
890
  if (delta !== void 0) {
@@ -960,12 +1008,13 @@ function streamCompletions(request, transport) {
960
1008
  const overridden = await options.onPayload?.(prepared.body, model);
961
1009
  const payload = overridden === void 0 ? prepared.body : overridden;
962
1010
  response = workersAI ? await transport.run({
1011
+ format: "chat-completions",
963
1012
  gateway: resolved.gateway,
964
1013
  headers: request.headers,
965
1014
  input: payload,
966
1015
  model: model.id,
967
1016
  signal: options.signal
968
- }) : await sendUniversal(request, transport, payload, openAIVendorHeaders());
1017
+ }) : await sendGateway(request, transport, payload, openAIVendorHeaders(), "chat-completions");
969
1018
  await options.onResponse?.(responseInfo(response), model);
970
1019
  attachCorrelation(output, correlationDetails(request, response, transport));
971
1020
  await assertOk(response, {
@@ -1257,7 +1306,7 @@ function buildRequest(model, context, options, simple, config) {
1257
1306
  //#region src/models/pi-ai/wires/anthropic.ts
1258
1307
  /**
1259
1308
  * Anthropic Messages wire: pi-ai's own Anthropic implementation, over AI
1260
- * Gateway's universal endpoint.
1309
+ * Gateway — `/run` for a catalog model, the provider-native path otherwise.
1261
1310
  *
1262
1311
  * Everything about the wire — request shaping, thinking, tool use, stream
1263
1312
  * parsing, thinking-signature replay — is pi-ai's. It accepts a `client` whose
@@ -1317,7 +1366,7 @@ function streamAnthropic(request, transport) {
1317
1366
  model: wireModelId(model)
1318
1367
  };
1319
1368
  try {
1320
- const response = await sendUniversal(request, transport, body, anthropicVendorHeaders(Array.isArray(betas) ? betas : []), (raw) => {
1369
+ const response = await sendGateway(request, transport, body, anthropicVendorHeaders(Array.isArray(betas) ? betas : []), "messages", (raw) => {
1321
1370
  correlation = correlationDetails(request, raw, transport);
1322
1371
  });
1323
1372
  await assertOk(response, {
@@ -1380,7 +1429,8 @@ function streamAnthropic(request, transport) {
1380
1429
  //#region src/models/pi-ai/wires/responses.ts
1381
1430
  /**
1382
1431
  * OpenAI Responses wire: pi-ai's own Responses converters and stream
1383
- * processor, over AI Gateway's universal endpoint.
1432
+ * processor, over AI Gateway: `/run` for a catalog model, the provider-native
1433
+ * path otherwise.
1384
1434
  *
1385
1435
  * Only the transport differs from talking to OpenAI directly: the body goes to
1386
1436
  * the gateway, which forwards it to `api.openai.com/v1/responses` with the key
@@ -1445,7 +1495,7 @@ function streamResponses(request, transport) {
1445
1495
  if (sampling !== void 0) Object.assign(body, sampling);
1446
1496
  const overridden = await options.onPayload?.(body, model);
1447
1497
  const payload = overridden === void 0 ? body : overridden;
1448
- response = await sendUniversal(request, transport, payload, openAIVendorHeaders());
1498
+ response = await sendGateway(request, transport, payload, openAIVendorHeaders(), "responses");
1449
1499
  await options.onResponse?.(responseInfo(response), model);
1450
1500
  attachCorrelation(output, correlationDetails(request, response, transport));
1451
1501
  recordWarnings(output, ignoredWorkersAIKnobs(request));
@@ -1612,7 +1662,8 @@ function streamWithFallback(legs, signal) {
1612
1662
  * The `api` is the model author's declaration, not a guess of ours: Workers AI
1613
1663
  * models carry {@link CLOUDFLARE_AI_API} and run on `env.AI.run`; every other
1614
1664
  * model carries whatever protocol its registry says it speaks, and goes to AI
1615
- * Gateway's universal endpoint.
1665
+ * Gateway in that protocol: `/run` for a catalog model, the provider-native
1666
+ * path otherwise.
1616
1667
  */
1617
1668
  /** A stream that fails immediately, for a model this provider cannot route. */
1618
1669
  function failedStream(request, error) {
@@ -1646,7 +1697,7 @@ function legOptions(options) {
1646
1697
  }
1647
1698
  /** A leg may be given as a Workers AI id or as a model of any kind. */
1648
1699
  function legModel(leg, options) {
1649
- if (typeof leg === "string") return leg.startsWith("@cf/") ? buildWorkersAIModel(leg, options) : buildGatewayModel(leg, options);
1700
+ if (typeof leg === "string") return isRunPathLanguageModelId(leg) ? buildWorkersAIModel(leg, options) : buildGatewayModel(leg, options);
1650
1701
  const own = optionsOf(leg);
1651
1702
  const inherited = gatewayLayerOf(options);
1652
1703
  return withOptions(leg, mergeModelOptions(own, Object.keys(inherited).length > 0 ? inherited : void 0));
@@ -1700,8 +1751,9 @@ function createStreams(config) {
1700
1751
  * ```
1701
1752
  *
1702
1753
  * Workers AI runs on `env.AI.run` with Cloudflare's own compat layer. Every
1703
- * other model goes to AI Gateway's universal endpoint, which forwards the
1704
- * vendor's own request to the vendor with the key the gateway holds — so no
1754
+ * other model goes to AI Gateway in its own protocol — `/run` for a model
1755
+ * from the gateway's catalog, the provider-native path otherwise — and the
1756
+ * gateway calls the vendor with the key it holds, so no
1705
1757
  * provider key, and no vendor wire format, lives in your Worker. The model's
1706
1758
  * metadata (`api`, `compat`, `thinkingLevelMap`, cost, limits) is whatever its
1707
1759
  * own registry says, and pi-ai's converters and stream processors do the rest.
@@ -1724,25 +1776,26 @@ const VENDOR_HINTS = {
1724
1776
  */
1725
1777
  function unroutableString(specifier) {
1726
1778
  const hint = VENDOR_HINTS[specifier.split("/")[0] ?? ""];
1727
- return /* @__PURE__ */ new TypeError(`ai("${specifier}") is not a model this provider knows. Workers AI ids start with "@cf/". A third-party model comes from its own pi-ai registry: ai(getBuiltinModel(${hint === void 0 ? "\"<provider>\"" : `"${hint}"`}, "<model id>")) from "@earendil-works/pi-ai/providers/all", or any Model object you built yourself.`);
1779
+ return /* @__PURE__ */ new TypeError(`ai("${specifier}") is not a model this provider knows. Workers AI ids start with "@cf/", AI Gateway dynamic routes with "dynamic/". A third-party model comes from its own pi-ai registry: ai(getBuiltinModel(${hint === void 0 ? "\"<provider>\"" : `"${hint}"`}, "<model id>")) from "@earendil-works/pi-ai/providers/all", or any Model object you built yourself.`);
1728
1780
  }
1729
1781
  function buildModel(target, options) {
1730
1782
  for (const leg of options?.fallback ?? []) {
1731
- if (typeof leg !== "string" || leg.startsWith("@cf/")) continue;
1783
+ if (typeof leg !== "string" || isRunPathLanguageModelId(leg)) continue;
1732
1784
  if (!leg.includes("/")) throw unroutableString(leg);
1733
1785
  buildGatewayModel(leg, void 0);
1734
1786
  }
1735
1787
  if (typeof target !== "string") return withOptions(target, options);
1736
- if (target.startsWith("@cf/")) return buildWorkersAIModel(target, options);
1788
+ if (isRunPathLanguageModelId(target)) return buildWorkersAIModel(target, options);
1737
1789
  if (!target.includes("/")) throw unroutableString(target);
1738
1790
  return buildGatewayModel(target, options);
1739
1791
  }
1740
1792
  /**
1741
1793
  * Creates a pi-ai provider over Cloudflare's AI.
1742
1794
  *
1743
- * The Workers AI binding is the only way in — Workers AI models go through
1744
- * `env.AI.run`, vendor models through `env.AI.gateway(id).run`, and there is
1745
- * no HTTP transport and no API token. Gateway options may be given flat
1795
+ * Workers AI and catalog models go through `env.AI.run`, other model objects
1796
+ * through the binding's `fetch` to AI Gateway's provider-native path. With
1797
+ * `{ accountId, apiToken }` instead of `binding`, the same requests go to the
1798
+ * REST API and to `gateway.ai.cloudflare.com`. Gateway options may be given flat
1746
1799
  * (`{ binding, id: "prod", cacheTtl: 60 }`) or nested under `gateway`;
1747
1800
  * per-model options and per-call pi-ai options (`headers`, `metadata`,
1748
1801
  * `sessionId`) override them in that order.