experimental-a2 0.18.1 → 0.20.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 (143) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/actor.js +1 -1
  3. package/dist/{ai-BMLlI2Ff.d.ts → ai-Cz3hqVhk.d.ts} +33 -16
  4. package/dist/ai-Cz3hqVhk.d.ts.map +1 -0
  5. package/dist/ai-client.d.ts +1 -1
  6. package/dist/ai-client.js +1 -1
  7. package/dist/ai-control-CvLbW10B.js +46 -0
  8. package/dist/ai-control-CvLbW10B.js.map +1 -0
  9. package/dist/ai-fx.d.ts +1 -1
  10. package/dist/ai-fx.d.ts.map +1 -1
  11. package/dist/ai-fx.js +63 -13
  12. package/dist/ai-fx.js.map +1 -1
  13. package/dist/{ai-harness-B2suP2Qh.d.ts → ai-harness-CV6rXbB3.d.ts} +2 -2
  14. package/dist/ai-harness-CV6rXbB3.d.ts.map +1 -0
  15. package/dist/{ai-harness-BuLh_EYi.js → ai-harness-Clbg5ei7.js} +88 -15
  16. package/dist/ai-harness-Clbg5ei7.js.map +1 -0
  17. package/dist/{ai-D6mGlO8j.js → ai-mMiWul1T.js} +116 -77
  18. package/dist/ai-mMiWul1T.js.map +1 -0
  19. package/dist/ai-pi.d.ts +1 -1
  20. package/dist/ai-pi.d.ts.map +1 -1
  21. package/dist/ai-pi.js +56 -11
  22. package/dist/ai-pi.js.map +1 -1
  23. package/dist/ai-react.d.ts +1 -1
  24. package/dist/ai-react.js +1 -1
  25. package/dist/ai-server.d.ts +2 -2
  26. package/dist/ai-server.d.ts.map +1 -1
  27. package/dist/ai-server.js +123 -53
  28. package/dist/ai-server.js.map +1 -1
  29. package/dist/ai.d.ts +2 -2
  30. package/dist/ai.js +1 -1
  31. package/dist/internal-CscSY7K_.js.map +1 -1
  32. package/dist/scheduler-qstash.d.ts.map +1 -1
  33. package/dist/scheduler-qstash.js +5 -5
  34. package/dist/scheduler-qstash.js.map +1 -1
  35. package/dist/scheduler-vercel.d.ts.map +1 -1
  36. package/dist/scheduler-vercel.js +5 -7
  37. package/dist/scheduler-vercel.js.map +1 -1
  38. package/dist/server-DYAATFuT.d.ts.map +1 -1
  39. package/dist/{server-D-Y4pqwu.js → server-qyb5aJJG.js} +39 -25
  40. package/dist/server-qyb5aJJG.js.map +1 -0
  41. package/dist/server.js +1 -1
  42. package/dist/session-clear-DGI4jTQ5.js.map +1 -1
  43. package/dist/store-postgres.d.ts.map +1 -1
  44. package/dist/store-postgres.js +38 -136
  45. package/dist/store-postgres.js.map +1 -1
  46. package/docs/01-quickstart.mdx +7 -4
  47. package/docs/actors/01-introduction.mdx +1 -1
  48. package/docs/actors/meta.ts +1 -1
  49. package/docs/agents/02-tools.mdx +1 -1
  50. package/docs/agents/03-conversations.mdx +26 -14
  51. package/docs/agents/04-harnesses.mdx +51 -5
  52. package/docs/agents/meta.ts +1 -1
  53. package/docs/client/meta.ts +1 -1
  54. package/docs/contract/03-sessions.mdx +2 -2
  55. package/docs/index.mdx +2 -2
  56. package/docs/reference/01-core.mdx +8 -8
  57. package/docs/reference/02-server.mdx +3 -3
  58. package/docs/reference/04-ai.mdx +36 -19
  59. package/docs/reference/05-telemetry.mdx +132 -0
  60. package/docs/reference/06-errors.mdx +1 -1
  61. package/docs/reference/meta.ts +1 -1
  62. package/docs/schedulers/01-overview.mdx +112 -0
  63. package/docs/schedulers/02-vercel-queues.mdx +87 -0
  64. package/docs/schedulers/03-qstash.mdx +123 -0
  65. package/docs/schedulers/meta.ts +1 -0
  66. package/docs/server/01-handlers.mdx +29 -2
  67. package/docs/server/02-timers.mdx +3 -3
  68. package/docs/server/06-guarantees.mdx +6 -3
  69. package/docs/stores/01-overview.mdx +37 -0
  70. package/docs/stores/02-redis.mdx +49 -0
  71. package/docs/stores/03-redis-http.mdx +56 -0
  72. package/docs/stores/04-postgres.mdx +121 -0
  73. package/docs/stores/05-sqlite.mdx +34 -0
  74. package/docs/stores/06-memory.mdx +22 -0
  75. package/docs/stores/meta.ts +1 -0
  76. package/examples/playground/DEPLOY.md +19 -5
  77. package/examples/playground/app/a0/a0.css +244 -5
  78. package/examples/playground/app/a0/app-view.tsx +2 -1
  79. package/examples/playground/app/a0/apps/[appId]/route.ts +6 -10
  80. package/examples/playground/app/a0/attachments.ts +133 -0
  81. package/examples/playground/app/a0/chat.test.tsx +595 -0
  82. package/examples/playground/app/a0/chat.tsx +623 -63
  83. package/examples/playground/app/a0/context-menu.tsx +38 -11
  84. package/examples/playground/app/a0/model.test.ts +28 -0
  85. package/examples/playground/app/a0/model.ts +30 -0
  86. package/examples/playground/app/a0/server.ts +5 -0
  87. package/examples/playground/app/a0/shell.tsx +1 -1
  88. package/examples/playground/app/a0/uploads/route.test.ts +59 -0
  89. package/examples/playground/app/a0/uploads/route.ts +29 -0
  90. package/examples/playground/app/agent/[agentId]/agent-client.tsx +2 -1
  91. package/examples/playground/app/agent/[agentId]/agent-queue.test.tsx +116 -89
  92. package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +107 -76
  93. package/examples/playground/app/agent/ingress.test.ts +41 -17
  94. package/examples/playground/app/agent/model-input.test.tsx +92 -43
  95. package/examples/playground/app/agent/model.test.ts +12 -3
  96. package/examples/playground/app/chat/[chatId]/agent-state.test.ts +1 -5
  97. package/examples/playground/app/chat/[chatId]/agent-stream-drawer.test.tsx +2 -2
  98. package/examples/playground/app/chat/server.test.ts +38 -65
  99. package/examples/playground/app/components/activity-history.test.tsx +60 -18
  100. package/examples/playground/app/components/fast-link.test.tsx +3 -5
  101. package/examples/playground/app/initial-state.test.tsx +86 -45
  102. package/examples/playground/next.config.ts +7 -4
  103. package/examples/playground/package.json +4 -1
  104. package/examples/playground/test/agent.ts +35 -0
  105. package/examples/playground/test/model.ts +149 -0
  106. package/examples/playground/test/serve.ts +184 -0
  107. package/examples/playground/test/server-only.ts +1 -0
  108. package/examples/playground/vitest.config.ts +27 -0
  109. package/package.json +3 -1
  110. package/sql/postgres/notifications.sql +24 -0
  111. package/sql/postgres/schema.sql +137 -0
  112. package/src/ai-agent.ts +9 -4
  113. package/src/ai-client-state.ts +21 -9
  114. package/src/ai-context-schema.ts +28 -4
  115. package/src/ai-context.ts +83 -3
  116. package/src/ai-control-server.ts +70 -43
  117. package/src/ai-control-state.ts +3 -4
  118. package/src/ai-control.ts +59 -10
  119. package/src/ai-coordinator.ts +7 -6
  120. package/src/ai-fx.ts +95 -23
  121. package/src/ai-harness.ts +171 -29
  122. package/src/ai-message-shape.ts +41 -0
  123. package/src/ai-pi.ts +88 -32
  124. package/src/ai-stored-state.ts +1 -3
  125. package/src/ai.ts +116 -77
  126. package/src/errors.ts +1 -1
  127. package/src/internal.ts +2 -1
  128. package/src/libfx.d.ts +8 -1
  129. package/src/postgres-notifications.ts +0 -22
  130. package/src/postgres-resources.ts +6 -9
  131. package/src/postgres-schema.ts +80 -0
  132. package/src/scheduler-qstash.ts +12 -5
  133. package/src/scheduler-vercel.ts +11 -16
  134. package/src/server.ts +66 -32
  135. package/src/store-postgres.ts +5 -123
  136. package/dist/ai-BMLlI2Ff.d.ts.map +0 -1
  137. package/dist/ai-D6mGlO8j.js.map +0 -1
  138. package/dist/ai-harness-B2suP2Qh.d.ts.map +0 -1
  139. package/dist/ai-harness-BuLh_EYi.js.map +0 -1
  140. package/dist/server-D-Y4pqwu.js.map +0 -1
  141. package/docs/reference/05-adapters.mdx +0 -351
  142. package/docs/server/05-production.mdx +0 -323
  143. package/examples/playground/app/a0/no-photon.cjs +0 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.20.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 123c993: The Postgres store no longer creates its tables. Before you deploy this release, apply the SQL it needs, `node_modules/experimental-a2/sql/postgres/schema.sql`, once per database, however you run SQL against it. It creates only what is missing and leaves existing data alone, so it is safe on a database an earlier release set up, and the deployment that is running now keeps working against it, without waiting on its writes. It also records a schema version in a new `a2_schema` table. Apply it as the role your app connects with; if another role applies it, grant your app's role `select` on `a2_schema`. With `listenConnectionString`, also apply `sql/postgres/notifications.sql`, which installs the triggers A2 used to install for you.
8
+
9
+ If you deploy first, the store throws `STORE_NOT_CONFIGURED` on its first query, naming the file to apply, and the next call after you apply it succeeds without a redeploy. Until then, nothing is written: appends fail, and scheduled work waits. Vercel Queues keeps redelivering it, and the QStash scheduler now treats `STORE_NOT_CONFIGURED` like a store outage, handing each task to a later message (30 seconds, backing off to one day) instead of letting QStash's retries run out into its DLQ. That also covers a missing driver package, which used to dead-letter.
10
+
11
+ Each cold start now reads the schema version instead of running DDL. The DDL took a share lock on `a2_events` even when every table existed, so a cold start waited behind any open write to it, and writes waited behind the cold start. The store also no longer needs permission to create tables, so a runtime role with only read and write access works. SQLite still creates its tables on open.
12
+
13
+ ### Patch Changes
14
+
15
+ - 123c993: Docs: stores and schedulers each get their own section, with one page per adapter, such as `docs/stores/04-postgres.mdx` and `docs/schedulers/03-qstash.mdx`. `docs/server/05-production.mdx` is gone: resolving dead letters moved to `docs/server/01-handlers.mdx`, and traces to `docs/reference/05-telemetry.mdx`, which replaces `docs/reference/05-adapters.mdx`.
16
+ - 5228ecb: A returned batch the store refuses, such as one that reuses an event id, now dead-letters the event on that first attempt, like a `NonRetriableError`, with the store's error as its `lastError`: a retry would return the same ids. Before, the refused completion left the claim to lapse, so the event was retried on every watchdog wake and dead-lettered with no error. A scheduler delivery whose drain throws after arming its successor watchdog now acknowledges once that successor is durable, with the error on its `a2.drain` span (`a2.drain.outcome: failed`). Before, it threw: Vercel Queues redelivered the message alongside the successor, each redelivery arming another, and QStash retried it or, for a store outage, published a second, outage successor. `server.drain()` still throws.
17
+ - 272e603: `fx()` model calls now carry the same AI Gateway defaults as the AI SDK harness: `caching: 'auto'` in the request's Gateway options, under any Gateway options FX sets itself such as its fast tier, and the session's stable `x-session-affinity` header. Before, FX asked for no caching and sent its own affinity, which changed every time A2 rebuilt the kernel, so a response's model calls rarely reused the provider's prompt cache. The rest of each request goes as FX wrote it.
18
+
19
+ ## 0.19.0
20
+
21
+ ### Minor Changes
22
+
23
+ - 8f373b5: Under `fx()` and `pi()`, a user message's images now reach the model. A2 fetches each `image/*` file part from its `https:` or `data:` URL through the AI SDK's download, which refuses private addresses and checks redirects, up to 32 MiB of one message's files, and keeps what it fetched for the process. FX gets them as image prompt blocks and Pi as prompt and steering images. A file that isn't an image, an image A2 can't fetch, or a steer with files under FX fails the turn with an error that names the file. Every step sends the conversation so far with all its files, as under `{ type: 'ai-sdk' }`.
24
+
25
+ Before, both harnesses read only a message's text and dropped its files without a word.
26
+
27
+ - b83ba8b: `agent()` now throws a `TypeError` when a provider-defined tool (`type: 'provider'`, such as a provider's web search) is passed with `fx()` or `pi()`, as it does for a tool that sets `needsApproval`. Before, both harnesses dropped such a tool without a word, so the model never saw it.
28
+ - 88ee923: A2's AI log stores each message's content once, in the `ai.control.requested` command that brought it in. Before, a message sent with no turn running was stored three times, and a queued or steering one four: in its command, in `ai.control.committed`, and in `ai.message.created`.
29
+
30
+ - `ai.message.created` now records an admission as `{ messageId, role, generate? }`. `MessageCreatedPayload` is no longer generic. Only A2's control lane writes the event, after the command that brought its input in; `reduceAIState()` and `deriveUIMessages()` throw on one that no command explains.
31
+ - `ai.control.committed` never carries message content: queued inputs and `AIState.active.input` are the new `InputRef` type, `{ id, role, generate, afterStepOf? }`. `ActiveTurn` is no longer generic. `AIState.inbox.items` keep their `message`, now with `role`.
32
+ - `AIMessageOf` reads the message type from the contract's input command schema.
33
+ - The AI state, model context, and control reducers advance to `a2.ai.state.v27`, `a2.ai.context.v7`, and `a2.ai.control.v6`, so their checkpoints rebuild from the log. Logs written before this release don't fold.
34
+
35
+ Reading the log costs nothing more: the transcript's readers keep a queued input's content in their state from its command until its admission.
36
+
37
+ - da1e0e5: Steering hands off in the order it was sent, as one turn. `steer()` now queues new input right after the turn's earlier steering instead of ahead of it, and `queue.sendNow()` lands after that steering too. At the handoff, every input steering the turn enters together: the last one starts the new turn and the earlier ones enter as its context (`generate: false`), so the model answers them once. Under `fx()` and `pi()`, they reach the harness as one steering message. A turn paused with `pause({ when: 'now' })` keeps its steering until `resume()`, so every steer sent while it's paused targets it and they all enter on Resume.
38
+
39
+ Before, each steer jumped to the front, so several steers ran last-first, each as its own turn, and the first steer ended a paused turn at once, leaving later steers no turn to target.
40
+
41
+ ### Patch Changes
42
+
43
+ - 9b80f3e: Under `pi()`, `speed: 'fast'` now asks AI Gateway for the model's fast tier on each of Pi's model calls, as it already did under `{ type: 'ai-sdk' }` and `fx()`. Before, `pi()` ignored `speed`, so every step ran at standard speed.
44
+ - b83ba8b: The interruption reason `'steered after step'` is reserved: A2 writes it when steering ends a response, and `fx()` and `pi()` replay the next response as that steering. A client interruption that carries it is now rejected. Before, one would make the harness replay a stopped response as steered, so the replay diverged and the session failed.
45
+ - c7afea6: `vercelQueues()` no longer refuses delays over six days itself. A task's delay goes to Vercel Queues as asked, with the seven-day retention it already set, and Vercel Queues refuses one longer than that.
46
+
3
47
  ## 0.18.1
4
48
 
5
49
  ### Patch Changes
package/dist/actor.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { a as NonRetriableError, i as A2Error, n as isSessionCleared, r as sessionClearedError } from "./session-clear-DGI4jTQ5.js";
2
2
  import { n as internalContract } from "./contract-BYKASlCV.js";
3
3
  import { v as serverInternals } from "./internal-CscSY7K_.js";
4
- import { c as parsePresenceSibling, o as setServerFetchHooks, s as sseResponse, t as createServer } from "./server-D-Y4pqwu.js";
4
+ import { c as parsePresenceSibling, o as setServerFetchHooks, s as sseResponse, t as createServer } from "./server-qyb5aJJG.js";
5
5
  import { a as actorFailedSchema, c as isJsonTree, i as ActorRefusedError, l as openPresenceDefs, n as ACTOR_LANE, o as actorReducer, r as ACTOR_STATE_EVENT, s as actorStateSchema, t as ACTOR_FAILED_EVENT } from "./actor-shared-t7w8xIKP.js";
6
6
  //#region src/actor.ts
7
7
  /**
@@ -22,12 +22,20 @@ type MessageBase = {
22
22
  };
23
23
  //#endregion
24
24
  //#region src/ai-control.d.ts
25
- type InboxItem<M extends UIMessage = UIMessage> = {
25
+ /**
26
+ * An input in the queue or the active turn. Its content stays in the
27
+ * `ai.control.requested` command that brought it, and in no other event.
28
+ */
29
+ type InputRef = {
26
30
  id: string;
27
- message: M;
31
+ role: UIMessage["role"];
28
32
  generate: boolean;
29
33
  afterStepOf?: string;
30
34
  };
35
+ /** A queued input with its content, as `AIState` shows it. */
36
+ type InboxItem<M extends UIMessage = UIMessage> = InputRef & {
37
+ message: M;
38
+ };
31
39
  type ControlCommand<M extends UIMessage = UIMessage> = {
32
40
  action: "send";
33
41
  message: M;
@@ -86,9 +94,9 @@ type ControlReceipt = {
86
94
  outcome: "applied" | "rejected";
87
95
  reason?: "not-pending" | "role-mismatch" | "stale-turn" | "not-found" | "closed" | "stale-work" | "not-user-input";
88
96
  };
89
- type ActiveTurn<M extends UIMessage = UIMessage> = {
97
+ type ActiveTurn = {
90
98
  turnId: string;
91
- input: InboxItem<M>;
99
+ input: InputRef;
92
100
  phase: "starting" | "generating" | "tools" | "waiting" | "failed" | "pausing" | "paused";
93
101
  };
94
102
  type WorkFact = {
@@ -216,10 +224,15 @@ type SessionCreatedPayload = {
216
224
  type SessionClosedPayload = {
217
225
  reason?: string;
218
226
  };
219
- type MessageCreatedPayload<M extends UIMessage$1> = {
220
- message: M;
221
- /** For user messages, false records passive context. Omission or true schedules. */
222
- generate?: boolean;
227
+ /**
228
+ * A message entering the transcript. Its content is the command's that brought
229
+ * it in, which every reader of the transcript has already folded.
230
+ */
231
+ type MessageCreatedPayload = {
232
+ messageId: string;
233
+ role: UIMessage$1["role"];
234
+ /** For user messages, false records passive context. Omission schedules. */
235
+ generate?: false;
223
236
  };
224
237
  type MessageCompletedPayload = {
225
238
  messageId: string;
@@ -400,7 +413,7 @@ type AIEventDefs<M extends UIMessage$1 = UIMessage$1> = {
400
413
  }>;
401
414
  "ai.session.created": StandardSchemaV1<SessionCreatedPayload>;
402
415
  "ai.session.closed": StandardSchemaV1<SessionClosedPayload>;
403
- "ai.message.created": StandardSchemaV1<MessageCreatedPayload<M>>;
416
+ "ai.message.created": StandardSchemaV1<MessageCreatedPayload>;
404
417
  "ai.message.completed": StandardSchemaV1<MessageCompletedPayload>;
405
418
  "ai.message.interrupted": StandardSchemaV1<MessageInterruptedPayload>;
406
419
  "ai.generation.requested": StandardSchemaV1<GenerationRequestedPayload>;
@@ -491,7 +504,7 @@ type AIState<M extends UIMessage$1 = UIMessage$1> = {
491
504
  items: InboxItem<M>[];
492
505
  };
493
506
  pendingQueueCommands?: PendingQueueCommand<M>[];
494
- active: ActiveTurn<M> | null;
507
+ active: ActiveTurn | null;
495
508
  starting: boolean;
496
509
  receipt: ControlReceipt | null;
497
510
  pausedGenerationIds?: Record<string, true>;
@@ -518,10 +531,14 @@ type AIState<M extends UIMessage$1 = UIMessage$1> = {
518
531
  usage: GenerationUsage[];
519
532
  error: string | null;
520
533
  };
521
- /** The `UIMessage` type a contract's `ai.message.created` schema carries. */
534
+ /** The `UIMessage` type a contract's input commands carry. */
522
535
  type AIMessageOf<D> = D extends {
523
- readonly "ai.message.created": infer S extends StandardSchemaV1;
524
- } ? StandardSchemaV1.InferOutput<S> extends MessageCreatedPayload<infer M extends UIMessage$1> ? M : UIMessage$1 : UIMessage$1;
536
+ readonly "ai.control.requested": infer S extends StandardSchemaV1;
537
+ } ? Extract<StandardSchemaV1.InferOutput<S>, {
538
+ action: "send";
539
+ }> extends {
540
+ message: infer M extends UIMessage$1;
541
+ } ? M : UIMessage$1 : UIMessage$1;
525
542
  /** What `agentContract()` returns: the AI protocol, with `X` as application state. */
526
543
  type AgentContract<M extends UIMessage$1 = UIMessage$1, D extends EventDefs = AIEventDefs<M>, X extends object = Record<never, never>> = Contract<D, Record<never, never>, AgentReducer<M, D, X>>;
527
544
  type AgentToolEventDefs = EventDefs & Pick<AIEventDefs, "ai.tool.execution.requested">;
@@ -640,7 +657,7 @@ declare const inputs: AIInputs;
640
657
  */
641
658
  declare function handlerContext<D extends AgentToolEventDefs = AIEventDefs>(contract?: Contract<D>): AgentToolContext<D>;
642
659
  /** The built-in AI reducer's name, and so its checkpoint identity. */
643
- declare const AI_STATE_REDUCER = "a2.ai.state.v26";
660
+ declare const AI_STATE_REDUCER = "a2.ai.state.v27";
644
661
  /**
645
662
  * The built-in AI protocol plus application events, as a core contract whose
646
663
  * reducer folds `AIState`. Isomorphic: share it when a client needs
@@ -649,5 +666,5 @@ declare const AI_STATE_REDUCER = "a2.ai.state.v26";
649
666
  */
650
667
  declare function agentContract<M extends UIMessage$1 = UIMessage$1, E extends EventDefs = Record<never, never>, X extends object = Record<never, never>>(options: AgentContractOptions<M, E, X>): AgentContract<M, AIEventDefs<M> & E, X>;
651
668
  //#endregion
652
- export { agentContract as $, InputRespondedPayload as A, PendingApproval as B, GenerationFailedPayload as C, GenerationStartedPayload as D, GenerationRequestedPayload as E, MessageInterruptedPayload as F, SessionClosedPayload as G, ReasoningLevel as H, ModelLimits as I, ToolCalledPayload as J, SessionCreatedPayload as K, ModelMetadataRequestedPayload as L, LanguageModelUsage as M, MessageCompletedPayload as N, GenerationUsage as O, MessageCreatedPayload as P, UIMessageChunk$1 as Q, ModelMetadataResolvedPayload as R, GenerationCompletedPayload as S, GenerationReason as T, RetryGenerationOptions as U, PendingInput as V, RetryRequestedPayload as W, TranscriptMessage as X, ToolResultPayload as Y, UIMessage$1 as Z, CompactionPart as _, AI_STATE_REDUCER as a, reasoningLevels as at, CompactionState as b, AgentContract as c, PendingQueueCommand as ct, AgentStatus as d, ControlReceipt as dt, createEvents as et, AgentToolContext as f, InboxItem as ft, CompactionCompletedPayload as g, ApprovalRespondedPayload as h, AIState as i, inputs as it, JSONValue$1 as j, InputRequestedPayload as k, AgentContractOptions as l, ActiveTurn as lt, ApprovalRequestedPayload as m, AIInputs as n, events as nt, ActiveGeneration as o, reduceAIState as ot, AgentToolEventDefs as p, ToolActivity as q, AIMessageOf as r, handlerContext as rt, ActiveGenerationProjection as s, isCompactionPart as st, AIEventDefs as t, deriveUIMessages as tt, AgentReducer as u, ControlCommand as ut, CompactionPartData as v, GenerationProgressPayload as w, FinishReason as x, CompactionRequestedPayload as y, ModelMetadataState as z };
653
- //# sourceMappingURL=ai-BMLlI2Ff.d.ts.map
669
+ export { agentContract as $, InputRespondedPayload as A, PendingApproval as B, GenerationFailedPayload as C, GenerationStartedPayload as D, GenerationRequestedPayload as E, MessageInterruptedPayload as F, SessionClosedPayload as G, ReasoningLevel as H, ModelLimits as I, ToolCalledPayload as J, SessionCreatedPayload as K, ModelMetadataRequestedPayload as L, LanguageModelUsage as M, MessageCompletedPayload as N, GenerationUsage as O, MessageCreatedPayload as P, UIMessageChunk$1 as Q, ModelMetadataResolvedPayload as R, GenerationCompletedPayload as S, GenerationReason as T, RetryGenerationOptions as U, PendingInput as V, RetryRequestedPayload as W, TranscriptMessage as X, ToolResultPayload as Y, UIMessage$1 as Z, CompactionPart as _, AI_STATE_REDUCER as a, reasoningLevels as at, CompactionState as b, AgentContract as c, PendingQueueCommand as ct, AgentStatus as d, ControlReceipt as dt, createEvents as et, AgentToolContext as f, InboxItem as ft, CompactionCompletedPayload as g, ApprovalRespondedPayload as h, AIState as i, inputs as it, JSONValue$1 as j, InputRequestedPayload as k, AgentContractOptions as l, ActiveTurn as lt, ApprovalRequestedPayload as m, AIInputs as n, events as nt, ActiveGeneration as o, reduceAIState as ot, AgentToolEventDefs as p, InputRef as pt, ToolActivity as q, AIMessageOf as r, handlerContext as rt, ActiveGenerationProjection as s, isCompactionPart as st, AIEventDefs as t, deriveUIMessages as tt, AgentReducer as u, ControlCommand as ut, CompactionPartData as v, GenerationProgressPayload as w, FinishReason as x, CompactionRequestedPayload as y, ModelMetadataState as z };
670
+ //# sourceMappingURL=ai-Cz3hqVhk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-Cz3hqVhk.d.ts","names":[],"sources":["../src/ai-message-base.ts","../src/ai-control.ts","../src/ai-client-state.ts","../src/ai-projector.ts","../src/ai-progress-batches.ts","../src/ai-message-projection.ts","../src/ai.ts"],"mappings":";;;;KAGK;EAAc;;EAAsB;;EAAmB;;KACvD,aAAa,eAAe;KAC5B;EAAa,OAAO;;EAAiC,QAAQ;;KAEtD;EACV;EACA,SAAS;EACT,QAAQ,eAAe;;;;;;;;KCQb;EACV;EACA,MAAM;EACN;EACA;;;KAIU,UAAU,UAAU,YAAY,aAAa;EACvD,SAAS;;KAGC,eAAe,UAAU,YAAY;EAC3C;EAAgB,SAAS;EAAG;;EAC5B;EAAgB,SAAS;;EACzB;EAAgB;EAAiB;;EACjC;EAAkB;;EAElB;EACA;EACA;EACA;;EAEA;EAAgB;EAAgB;;EAChC;EAAiB;EAAgB,SAAS;EAAG;;EAC7C;EAAiB;;EACjB;;EACA;EAAuB,QAAQ;;EAC/B;EAAoB,UAAU;;EAC9B;EAAiB,UAAU;;EAC3B;EAAyB,SAAS;;EAClC;EAAiB,SAAS;;EAC1B;EAAqB,SAAS;;KAExB;EACV;EACA;EACA;EACA;;KAUU;EACV;EACA,OAAO;EACP;;KA+BU;EAAa;EAAc;EAAY;;KACvC;EACV;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;KAGE;EACV;EACA;EACA,MAAM;EACN,YAAY;;KAoDF;EACV;EACA;EACA;EACA;IAAW;IAAe;IAAqB;;EAC/C;IAAS;IAAc;;;KAGb;EACV,SAAS;EACT;IACE;IACA;IACA,OAAO;IACP;IACA;IACA;IACA,eAAe;IACf;IACA;;;;;KC7KQ,aAAa,UAAU,aAAa,QAC9C,eAAe;EAEb;;KAYQ,oBAAoB,UAAU;EACxC;EACA,SAAS,aAAa;;;;KCpBnB;EACH;EACA;EACA;EACA;EACA;EACA;EACA;;KAwJU;EACV,MAAM;EACN,WAAW;EACX,OAAO,eAAe;;;;KChJZ;EAAkB;EAAe,QAAQ;;KAChD,aAAa;EAAoB,MAAM;EAAY,OAAO;;KAEnD;EACV;EACA,MAAM;EACN,QAAQ,MAAM;;;;cCmgBH,mBAAoB;EAC/B;MACE,QAAQ;;;KCncA;KAGA;KAEA,iBAAiB,YAAY;cAE5B,0BAA0B;KAU3B;EACV;EACA;EACA;EACA;EACA,QAAQ;;;KAIE;EACV;EACA;EACA,MAAM;;;;;;KASI,kBAAkB,UAAU,eAAa,KAAK;EACxD,OAAO,MAAM,qBAAqB;;KAGxB;EAA0B,WAAW;;KACrC;EAAyB;;;;;;KAKzB;EACV;EACA,MAAM;;EAEN;;KAEU;EAA4B;;KACnC;EACH;EACA;;EAEA;;KAEU,4BAA4B;EAEhC;EAAsB;;EACtB;EAAmB;;KAEf;EACV;IAAY;IAAgB;;EAC5B;EACA,QAAQ;EACR;EACA;;KAEU;EACV;EACA;EACA;;KAEU,wBAAwB;KACxB;EACV;EACA;EACA;EACA;EACA;;EAEA;EACA;;KAEU;;EAEV;EACA,QAAQ;;KAEE;EACV;EACA;EACA;EACA;EACA,eAAe;EACf,QAAQ;EACR;;KAEU;EACV;EACA;EACA;EACA;EACA;;EAEA;EACA;;KAEU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;KAEU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;KAEU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;;KAEU;EACV;EACA;EACA;EACA;EACA;EACA;;KAEU;EACV;EACA;EACA;EACA;EACA;EACA,WAAW;;KAED;EACV;EACA;EACA;EACA;EACA;EACA,OAAO;;KAEG;EACV;EACA;;KAEU;EAAkC;;KAClC;EACV;EACA,QAAQ;;KAEE;EACN;;EACA;EAAoB,QAAQ;;EAC5B;;KAEM;EACV;EACA;EACA;EACA;EACA;EACA;;KAEU,2BAA2B,UAAU;EAC/C;EACA;EACA;EACA,UAAU;EACV;EACA;EACA,QAAQ;;KAGE,YAAY,UAAU,cAAY;EAC5C,wBAAwB,iBAAiB,eAAe;EACxD,wBAAwB,iBAAiB;EACzC,sBAAsB,iBAAiB;EACvC,oBAAoB,iBAAiB;EACrC,+BAA+B,iBAAiB;EAChD,wBAAwB;IACtB;IACA;;EAEF,sBAAsB,iBAAiB;EACvC,qBAAqB,iBAAiB;EACtC,sBAAsB,iBAAiB;EACvC,wBAAwB,iBAAiB;EACzC,0BAA0B,iBAAiB;EAC3C,2BAA2B,iBAAiB;EAC5C,sBAAsB,iBAAiB;EACvC,yBAAyB,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"}
@@ -1,5 +1,5 @@
1
1
  import { s as CreateClientOptions, t as A2Client } from "./client-h_DKFAjj.js";
2
- import { a as AI_STATE_REDUCER, i as AIState, r as AIMessageOf, t as AIEventDefs } from "./ai-BMLlI2Ff.js";
2
+ import { a as AI_STATE_REDUCER, i as AIState, r as AIMessageOf, t as AIEventDefs } from "./ai-Cz3hqVhk.js";
3
3
  //#region src/ai-client.d.ts
4
4
  /**
5
5
  * The structural shape of `typeof` an `agent({ name })` server: one whose
package/dist/ai-client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as createClient } from "./client-BhHLoNKF.js";
2
- import { n as agentContract } from "./ai-D6mGlO8j.js";
2
+ import { n as agentContract } from "./ai-mMiWul1T.js";
3
3
  //#region src/ai-client.ts
4
4
  /**
5
5
  * experimental-a2/ai/client — a client for an `agent()` route that needs no
@@ -0,0 +1,46 @@
1
+ //#region src/ai-control.ts
2
+ /**
3
+ * The `ai.message.interrupted` reason A2 writes when steering ends a response
4
+ * after a step; a harness's replay continues the turn on it. Reserved, so a
5
+ * client's interruption can't carry it.
6
+ */
7
+ const STEERED_REASON = "steered after step";
8
+ /**
9
+ * A fold met an admission no command explains. Only A2's control lane writes
10
+ * `ai.message.created`, after the command that brought its input in.
11
+ */
12
+ const unexplainedAdmission = (messageId) => /* @__PURE__ */ new TypeError(`ai.message.created for ${messageId} follows no command that brought it in; only A2's control lane writes it`);
13
+ /**
14
+ * Where new steering for a turn goes in the inbox: right after the turn's
15
+ * earlier steering, or first, so it hands off in the order it was sent.
16
+ */
17
+ const steeringEnd = (items, turnId) => items.findLastIndex((item) => item.afterStepOf === turnId) + 1;
18
+ /**
19
+ * Whether an input steering a turn enters as context for later steering: the
20
+ * run of inputs steering one turn is admitted together, and only its last
21
+ * generating input starts the next turn.
22
+ */
23
+ const steersOn = (item, rest) => {
24
+ if (item.afterStepOf === void 0) return false;
25
+ for (const next of rest) {
26
+ if (next.afterStepOf !== item.afterStepOf) return false;
27
+ if (next.generate) return true;
28
+ }
29
+ return false;
30
+ };
31
+ const initialControlState = () => ({
32
+ pendingStops: {},
33
+ inbox: {
34
+ paused: false,
35
+ items: []
36
+ },
37
+ active: null,
38
+ coordinator: {
39
+ closed: false,
40
+ queued: []
41
+ }
42
+ });
43
+ //#endregion
44
+ export { unexplainedAdmission as a, steersOn as i, initialControlState as n, steeringEnd as r, STEERED_REASON as t };
45
+
46
+ //# sourceMappingURL=ai-control-CvLbW10B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-control-CvLbW10B.js","names":[],"sources":["../src/ai-control.ts"],"sourcesContent":["import type { UIMessage } from 'ai'\nimport type { AICoordinatorState } from './ai-coordinator.ts'\nimport type {\n ApprovalRespondedPayload,\n GenerationStartedPayload,\n GenerationReason,\n InputRequestedPayload,\n InputRespondedPayload,\n MessageInterruptedPayload,\n RetryRequestedPayload,\n ToolCalledPayload,\n ToolResultPayload,\n} from './ai.ts'\n\n/**\n * An input in the queue or the active turn. Its content stays in the\n * `ai.control.requested` command that brought it, and in no other event.\n */\nexport type InputRef = {\n id: string\n role: UIMessage['role']\n generate: boolean\n afterStepOf?: string\n}\n\n/** A queued input with its content, as `AIState` shows it. */\nexport type InboxItem<M extends UIMessage = UIMessage> = InputRef & {\n message: M\n}\n\nexport type ControlCommand<M extends UIMessage = UIMessage> =\n | { action: 'send'; message: M; generate?: boolean }\n | { action: 'edit'; message: M }\n | { action: 'move'; inputId: string; beforeId: string | null }\n | { action: 'remove'; inputId: string }\n | {\n action: 'send-now'\n inputId: string\n turnId: string | null\n lastSeenIndex?: number\n }\n | { action: 'stop'; turnId: string; lastSeenIndex?: number }\n | { action: 'steer'; turnId: string; message: M; lastSeenIndex?: number }\n | { action: 'pause'; when: 'now' | 'after-turn' }\n | { action: 'resume' }\n | { action: 'tool-result'; result: ToolResultPayload }\n | { action: 'approval'; response: ApprovalRespondedPayload }\n | { action: 'input'; response: InputRespondedPayload }\n | { action: 'request-input'; request: InputRequestedPayload }\n | { action: 'retry'; request: RetryRequestedPayload }\n | { action: 'interrupt'; request: MessageInterruptedPayload }\n\nexport type ControlReceipt = {\n commandId: string\n work?: true\n outcome: 'applied' | 'rejected'\n reason?:\n | 'not-pending'\n | 'role-mismatch'\n | 'stale-turn'\n | 'not-found'\n | 'closed'\n | 'stale-work'\n | 'not-user-input'\n}\n\nexport type ActiveTurn = {\n turnId: string\n input: InputRef\n phase:\n | 'starting'\n | 'generating'\n | 'tools'\n | 'waiting'\n | 'failed'\n | 'pausing'\n | 'paused'\n}\n\nexport type ControlWork = {\n id: string\n version: number\n settled: boolean\n attempt?: number\n facts?: string[]\n}\n\nexport type AIControlState = {\n pendingStops: Record<string, string>\n inbox: { paused: boolean; items: InputRef[] }\n active:\n | (ActiveTurn & {\n version: number\n suspended: boolean\n model: ControlWork | null\n })\n | null\n coordinator: AICoordinatorState\n}\n\nexport type WorkFact = { type: string; id: string; payload: unknown }\nexport type WorkReport = {\n attempt: number\n workId: string\n turnId: string\n version: number\n kind: 'model' | 'tool'\n settled: boolean\n events: WorkFact[]\n}\n\nexport type ToolExecutionRequest = {\n turnId: string\n version: number\n call: ToolCalledPayload\n generation: GenerationStartedPayload\n}\n\n/**\n * The `ai.message.interrupted` reason A2 writes when steering ends a response\n * after a step; a harness's replay continues the turn on it. Reserved, so a\n * client's interruption can't carry it.\n */\nexport const STEERED_REASON = 'steered after step'\n\n/**\n * A fold met an admission no command explains. Only A2's control lane writes\n * `ai.message.created`, after the command that brought its input in.\n */\nexport const unexplainedAdmission = (messageId: string): TypeError =>\n new TypeError(\n `ai.message.created for ${messageId} follows no command that brought it in; only A2's control lane writes it`,\n )\n\n/**\n * Where new steering for a turn goes in the inbox: right after the turn's\n * earlier steering, or first, so it hands off in the order it was sent.\n */\nexport const steeringEnd = (\n items: readonly InputRef[],\n turnId: string,\n): number => items.findLastIndex((item) => item.afterStepOf === turnId) + 1\n\n/**\n * Whether an input steering a turn enters as context for later steering: the\n * run of inputs steering one turn is admitted together, and only its last\n * generating input starts the next turn.\n */\nexport const steersOn = (\n item: InputRef,\n rest: readonly InputRef[],\n): boolean => {\n if (item.afterStepOf === undefined) return false\n for (const next of rest) {\n if (next.afterStepOf !== item.afterStepOf) return false\n if (next.generate) return true\n }\n return false\n}\n\nexport const initialControlState = (): AIControlState => ({\n pendingStops: {},\n inbox: { paused: false, items: [] },\n active: null,\n coordinator: { closed: false, queued: [] },\n})\n\nexport type ControlChange = {\n path: (string | number)[]\n value?: unknown\n remove?: true\n splice?: { index: number; deleteCount: number; items: unknown[] }\n move?: { from: number; to: number }\n}\n\nexport type ControlCommit = {\n changes: ControlChange[]\n view: {\n turnId: string | null\n version: number | null\n phase: ActiveTurn['phase'] | null\n paused: boolean\n closed: boolean\n requestId: string | null\n requestReason: GenerationReason | null\n sourceGenerationId: string | null\n responseMessageId: string | null\n }\n}\n"],"mappings":";;;;;;AA2HA,MAAa,iBAAiB;;;;;AAM9B,MAAa,wBAAwB,8BACnC,IAAI,UACF,0BAA0B,UAAU,yEACtC;;;;;AAMF,MAAa,eACX,OACA,WACW,MAAM,eAAe,SAAS,KAAK,gBAAgB,MAAM,IAAI;;;;;;AAO1E,MAAa,YACX,MACA,SACY;CACZ,IAAI,KAAK,gBAAgB,KAAA,GAAW,OAAO;CAC3C,KAAK,MAAM,QAAQ,MAAM;EACvB,IAAI,KAAK,gBAAgB,KAAK,aAAa,OAAO;EAClD,IAAI,KAAK,UAAU,OAAO;CAC5B;CACA,OAAO;AACT;AAEA,MAAa,6BAA6C;CACxD,cAAc,CAAC;CACf,OAAO;EAAE,QAAQ;EAAO,OAAO,CAAC;CAAE;CAClC,QAAQ;CACR,aAAa;EAAE,QAAQ;EAAO,QAAQ,CAAC;CAAE;AAC3C"}
package/dist/ai-fx.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Harness } from "./ai-harness-B2suP2Qh.js";
1
+ import { t as Harness } from "./ai-harness-CV6rXbB3.js";
2
2
  //#region src/ai-fx.d.ts
3
3
  /**
4
4
  * The FX harness. Every model call FX makes is one durable step; A2 rebuilds
@@ -1 +1 @@
1
- {"version":3,"file":"ai-fx.d.ts","names":[],"sources":["../src/ai-fx.ts"],"mappings":";;;;;;iBAyCgB,MAAM"}
1
+ {"version":3,"file":"ai-fx.d.ts","names":[],"sources":["../src/ai-fx.ts"],"mappings":";;;;;;iBA4CgB,MAAM"}
package/dist/ai-fx.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as boundedCache } from "./bounded-cache-CPHWF-8h.js";
2
- import { c as pending, d as saveHarness, i as gatewayCredential, l as readHarness, n as deferred, r as defineHarness, t as Divergence } from "./ai-harness-BuLh_EYi.js";
2
+ import { a as gatewaySessionAffinity, d as readHarness, i as gatewayCredential, n as deferred, p as saveHarness, r as defineHarness, s as harnessImages, t as Divergence, u as pending } from "./ai-harness-Clbg5ei7.js";
3
3
  import { t as schemaInput } from "./ai-tool-input-DmSqMUQi.js";
4
4
  import { asSchema } from "ai";
5
5
  //#region src/ai-fx.ts
@@ -19,6 +19,8 @@ function fx() {
19
19
  }
20
20
  const SNAPSHOT_VERSION = 1;
21
21
  const CATALOG_TTL_MS = 36e5;
22
+ /** The Gateway model catalog libfx reads for each new kernel. */
23
+ const CATALOG_PATH = "/coding-agent/v1/models";
22
24
  function fail(state, error) {
23
25
  state.failure ??= error;
24
26
  state.fxTurn.cancel();
@@ -111,10 +113,13 @@ async function runStep(step) {
111
113
  };
112
114
  const hostFetch = async (input, init = {}) => {
113
115
  const url = input instanceof Request ? input.url : String(input);
114
- if ((init.method ?? "GET").toUpperCase() === "GET") return catalog(url, credential, withAuth(init.headers, credential));
116
+ const method = (init.method ?? "GET").toUpperCase();
117
+ if (method === "GET" && new URL(url).pathname === CATALOG_PATH) return catalog(url, credential, withAuth(init.headers, credential));
118
+ if (method === "GET") return fetch(input, init);
115
119
  const state = turn;
116
120
  if (state.failure) throw state.failure;
117
- const compaction = isCompaction(decodeBody(init.body));
121
+ const body = decodeBody(init.body);
122
+ const compaction = isCompaction(body);
118
123
  const recorded = state.plan.calls[state.next];
119
124
  if (recorded !== void 0) {
120
125
  if (recorded.kind === "compaction" !== compaction) fail(state, diverged(`request ${state.next} is not a ${recorded.kind}`));
@@ -138,13 +143,23 @@ async function runStep(step) {
138
143
  return pending(init.signal);
139
144
  }
140
145
  state.live = true;
141
- return live(state, url, init, compaction);
146
+ return live(state, url, {
147
+ ...init,
148
+ body
149
+ }, compaction);
142
150
  };
151
+ const affinity = gatewaySessionAffinity({
152
+ agentName: step.agentName,
153
+ sessionId: step.sessionId
154
+ });
143
155
  let compactions = 0;
144
156
  const live = async (state, url, init, compaction) => {
157
+ const headers = withAuth(init.headers, credential);
158
+ headers.set("x-session-affinity", affinity);
145
159
  const response = await fetch(url, {
146
160
  ...init,
147
- headers: withAuth(init.headers, credential),
161
+ body: withGatewayCaching(init.body),
162
+ headers,
148
163
  signal: network
149
164
  });
150
165
  if (init.signal?.aborted) {
@@ -208,7 +223,7 @@ async function runStep(step) {
208
223
  }
209
224
  return fail(state, diverged(`no recorded result for ${toolName}`));
210
225
  });
211
- let turns = read.turns.map(turnPlan);
226
+ const plans = (turns) => Promise.all(turns.map((recorded) => turnPlan(recorded, step.signal)));
212
227
  let results = read.results;
213
228
  let controller;
214
229
  let closed = false;
@@ -239,11 +254,10 @@ async function runStep(step) {
239
254
  } catch (error) {
240
255
  if (read.saved === void 0) throw error;
241
256
  read = await replayAll();
242
- turns = read.turns.map(turnPlan);
243
257
  results = read.results;
244
258
  agent = await create(void 0);
245
259
  }
246
- await run(streamController, turns);
260
+ await run(streamController, await plans(read.turns));
247
261
  closed = true;
248
262
  streamController.close();
249
263
  } catch (error) {
@@ -266,24 +280,41 @@ async function runStep(step) {
266
280
  completion: () => completion
267
281
  };
268
282
  }
269
- function turnPlan(turn) {
283
+ async function turnPlan(turn, signal) {
270
284
  const calls = [];
271
285
  const steers = /* @__PURE__ */ new Map();
272
- const steer = (at, texts) => {
273
- if (texts !== void 0) steers.set(Math.max(0, at), texts);
286
+ const steer = (at, inputs) => {
287
+ if (inputs !== void 0) steers.set(Math.max(0, at), inputs.map(steerText));
274
288
  };
275
289
  steer(0, turn.generations.length === 0 ? turn.steers.get(0) : void 0);
276
290
  turn.generations.forEach((generation, index) => {
277
291
  calls.push(...recordedCalls(generation.chunks, generation.completed));
278
292
  steer(calls.length - 1, turn.steers.get(index));
279
293
  });
294
+ const images = await harnessImages({
295
+ harness: "fx()",
296
+ input: turn.prompt,
297
+ signal
298
+ });
280
299
  return {
281
- prompt: turn.prompt,
300
+ prompt: images.length === 0 ? turn.prompt.text : [...images.map((image) => ({
301
+ type: "image",
302
+ data: image.data,
303
+ mimeType: image.mediaType
304
+ })), ...turn.prompt.text === "" ? [] : [{
305
+ type: "text",
306
+ text: turn.prompt.text
307
+ }]],
282
308
  calls,
283
309
  steers,
284
310
  end: turn.end
285
311
  };
286
312
  }
313
+ /** libfx steers with text alone, so a steer's files fail the step. */
314
+ function steerText(input) {
315
+ if (input.files.length > 0) throw new Error("fx() steers with text only: send files in a message of their own");
316
+ return input.text;
317
+ }
287
318
  /** A completed generation's model calls: its compactions, then its step. */
288
319
  function recordedCalls(chunks, completed) {
289
320
  const calls = [];
@@ -569,6 +600,25 @@ async function loadLibfx() {
569
600
  throw new Error("fx() needs the libfx package: install libfx", { cause: error });
570
601
  }
571
602
  }
603
+ /**
604
+ * A model request with Gateway's automatic caching, as the AI SDK harness
605
+ * asks for it; options the request already has take precedence. Every other
606
+ * byte stays as the kernel wrote it, a tool input's spacing included.
607
+ */
608
+ function withGatewayCaching(body) {
609
+ const options = JSON.parse(body).providerOptions;
610
+ if (options === void 0) return body.replace(/^\{/, "{\"providerOptions\":{\"gateway\":{\"caching\":\"auto\"}},");
611
+ const own = `"providerOptions":${JSON.stringify(options)}}`;
612
+ if (!body.endsWith(own)) return body;
613
+ const merged = {
614
+ ...options,
615
+ gateway: {
616
+ caching: "auto",
617
+ ...options["gateway"]
618
+ }
619
+ };
620
+ return `${body.slice(0, -own.length)}"providerOptions":${JSON.stringify(merged)}}`;
621
+ }
572
622
  function withAuth(headers, credential) {
573
623
  const result = new Headers(headers);
574
624
  result.set("ai-gateway-auth-method", credential.method);
@@ -585,7 +635,7 @@ function kernelModel(step) {
585
635
  };
586
636
  }
587
637
  async function kernelTools(tools, execute) {
588
- return Promise.all(Object.entries(tools).filter(([, tool]) => tool.type !== "provider").map(async ([toolName, tool]) => ({
638
+ return Promise.all(Object.entries(tools).map(async ([toolName, tool]) => ({
589
639
  name: toolName,
590
640
  description: typeof tool.description === "string" ? tool.description : toolName,
591
641
  inputSchema: await asSchema(tool.inputSchema).jsonSchema,