@e2edev/e2e 0.10.0 → 0.11.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 (153) hide show
  1. package/README.md +11 -5
  2. package/dist/agent/act-validation.d.ts +2 -0
  3. package/dist/agent/act-validation.d.ts.map +1 -1
  4. package/dist/agent/act-validation.js +1 -1
  5. package/dist/agent/act-validation.js.map +1 -1
  6. package/dist/agent/act.d.ts.map +1 -1
  7. package/dist/agent/act.js +14 -0
  8. package/dist/agent/act.js.map +1 -1
  9. package/dist/agent/ai-sdk.d.ts +2 -2
  10. package/dist/agent/ai-sdk.d.ts.map +1 -1
  11. package/dist/agent/ai-sdk.js +3 -4
  12. package/dist/agent/ai-sdk.js.map +1 -1
  13. package/dist/agent/default-agent.d.ts +17 -2
  14. package/dist/agent/default-agent.d.ts.map +1 -1
  15. package/dist/agent/default-agent.js +17 -3
  16. package/dist/agent/default-agent.js.map +1 -1
  17. package/dist/agent/error.d.ts.map +1 -1
  18. package/dist/agent/error.js +1 -0
  19. package/dist/agent/error.js.map +1 -1
  20. package/dist/agent/executor.d.ts +13 -0
  21. package/dist/agent/executor.d.ts.map +1 -1
  22. package/dist/agent/executor.js.map +1 -1
  23. package/dist/agent/model/adapter.d.ts +3 -0
  24. package/dist/agent/model/adapter.d.ts.map +1 -1
  25. package/dist/agent/model/adapter.js.map +1 -1
  26. package/dist/agent/model/overflow.d.ts +20 -0
  27. package/dist/agent/model/overflow.d.ts.map +1 -0
  28. package/dist/agent/model/overflow.js +82 -0
  29. package/dist/agent/model/overflow.js.map +1 -0
  30. package/dist/agent/model/prompt-cache.d.ts +47 -0
  31. package/dist/agent/model/prompt-cache.d.ts.map +1 -0
  32. package/dist/agent/model/prompt-cache.js +78 -0
  33. package/dist/agent/model/prompt-cache.js.map +1 -0
  34. package/dist/agent/model/sdk.d.ts +41 -24
  35. package/dist/agent/model/sdk.d.ts.map +1 -1
  36. package/dist/agent/model/sdk.js +64 -91
  37. package/dist/agent/model/sdk.js.map +1 -1
  38. package/dist/agent/primitives.d.ts.map +1 -1
  39. package/dist/agent/primitives.js +2 -1
  40. package/dist/agent/primitives.js.map +1 -1
  41. package/dist/agent/public.d.ts +1 -1
  42. package/dist/agent/public.d.ts.map +1 -1
  43. package/dist/agent/public.js.map +1 -1
  44. package/dist/agent/screen-update.d.ts +8 -3
  45. package/dist/agent/screen-update.d.ts.map +1 -1
  46. package/dist/agent/screen-update.js +18 -5
  47. package/dist/agent/screen-update.js.map +1 -1
  48. package/dist/agent/tool-loop.d.ts.map +1 -1
  49. package/dist/agent/tool-loop.js +149 -33
  50. package/dist/agent/tool-loop.js.map +1 -1
  51. package/dist/agent/tool-output.d.ts +31 -0
  52. package/dist/agent/tool-output.d.ts.map +1 -0
  53. package/dist/agent/tool-output.js +60 -0
  54. package/dist/agent/tool-output.js.map +1 -0
  55. package/dist/agent/tool.d.ts +9 -3
  56. package/dist/agent/tool.d.ts.map +1 -1
  57. package/dist/agent/tool.js +2 -2
  58. package/dist/agent/tool.js.map +1 -1
  59. package/dist/agent/usage.d.ts +7 -0
  60. package/dist/agent/usage.d.ts.map +1 -1
  61. package/dist/agent/usage.js +11 -0
  62. package/dist/agent/usage.js.map +1 -1
  63. package/dist/cli/index.d.ts.map +1 -1
  64. package/dist/cli/index.js +119 -34
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/cli/init/engines.d.ts +2 -2
  67. package/dist/cli/init/engines.js +3 -3
  68. package/dist/cli/init/gateways.d.ts +22 -0
  69. package/dist/cli/init/gateways.d.ts.map +1 -0
  70. package/dist/cli/init/gateways.js +50 -0
  71. package/dist/cli/init/gateways.js.map +1 -0
  72. package/dist/cli/init/scaffold.d.ts +7 -1
  73. package/dist/cli/init/scaffold.d.ts.map +1 -1
  74. package/dist/cli/init/scaffold.js +21 -10
  75. package/dist/cli/init/scaffold.js.map +1 -1
  76. package/dist/cli/init.d.ts.map +1 -1
  77. package/dist/cli/init.js +51 -7
  78. package/dist/cli/init.js.map +1 -1
  79. package/dist/collect/collect.d.ts +7 -0
  80. package/dist/collect/collect.d.ts.map +1 -1
  81. package/dist/collect/collect.js +16 -0
  82. package/dist/collect/collect.js.map +1 -1
  83. package/dist/collect/registry.d.ts.map +1 -1
  84. package/dist/collect/registry.js +10 -4
  85. package/dist/collect/registry.js.map +1 -1
  86. package/dist/config/agent.d.ts +13 -32
  87. package/dist/config/agent.d.ts.map +1 -1
  88. package/dist/config/agent.js +32 -123
  89. package/dist/config/agent.js.map +1 -1
  90. package/dist/config/resolve.js +1 -1
  91. package/dist/explore/body.d.ts +36 -0
  92. package/dist/explore/body.d.ts.map +1 -0
  93. package/dist/explore/body.js +168 -0
  94. package/dist/explore/body.js.map +1 -0
  95. package/dist/explore/executor.d.ts +25 -0
  96. package/dist/explore/executor.d.ts.map +1 -0
  97. package/dist/explore/executor.js +86 -0
  98. package/dist/explore/executor.js.map +1 -0
  99. package/dist/explore/index.d.ts +64 -0
  100. package/dist/explore/index.d.ts.map +1 -0
  101. package/dist/explore/index.js +183 -0
  102. package/dist/explore/index.js.map +1 -0
  103. package/dist/explore/plan.d.ts +74 -0
  104. package/dist/explore/plan.d.ts.map +1 -0
  105. package/dist/explore/plan.js +149 -0
  106. package/dist/explore/plan.js.map +1 -0
  107. package/dist/explore/reporter.d.ts +11 -0
  108. package/dist/explore/reporter.d.ts.map +1 -0
  109. package/dist/explore/reporter.js +54 -0
  110. package/dist/explore/reporter.js.map +1 -0
  111. package/dist/explore/state.d.ts +58 -0
  112. package/dist/explore/state.d.ts.map +1 -0
  113. package/dist/explore/state.js +147 -0
  114. package/dist/explore/state.js.map +1 -0
  115. package/dist/report/build.d.ts +53 -0
  116. package/dist/report/build.d.ts.map +1 -1
  117. package/dist/report/build.js +10 -1
  118. package/dist/report/build.js.map +1 -1
  119. package/dist/report/debug-steps.d.ts.map +1 -1
  120. package/dist/report/debug-steps.js +15 -0
  121. package/dist/report/debug-steps.js.map +1 -1
  122. package/dist/report/format.d.ts +11 -1
  123. package/dist/report/format.d.ts.map +1 -1
  124. package/dist/report/format.js +15 -3
  125. package/dist/report/format.js.map +1 -1
  126. package/dist/run/events.d.ts +2 -2
  127. package/dist/run/events.d.ts.map +1 -1
  128. package/dist/run/in-process.d.ts +8 -0
  129. package/dist/run/in-process.d.ts.map +1 -1
  130. package/dist/run/in-process.js +3 -3
  131. package/dist/run/in-process.js.map +1 -1
  132. package/dist/run/runner.d.ts +20 -1
  133. package/dist/run/runner.d.ts.map +1 -1
  134. package/dist/run/runner.js +19 -14
  135. package/dist/run/runner.js.map +1 -1
  136. package/dist/run/steps.d.ts +7 -0
  137. package/dist/run/steps.d.ts.map +1 -1
  138. package/dist/run/steps.js.map +1 -1
  139. package/dist/run/worker-models.d.ts +4 -4
  140. package/dist/run/worker-models.js +6 -13
  141. package/dist/run/worker-models.js.map +1 -1
  142. package/dist/telemetry/events.d.ts.map +1 -1
  143. package/dist/telemetry/events.js +17 -2
  144. package/dist/telemetry/events.js.map +1 -1
  145. package/dist/types.d.ts +12 -16
  146. package/dist/types.d.ts.map +1 -1
  147. package/package.json +4 -2
  148. package/skills/e2e/SKILL.md +11 -4
  149. package/skills/e2e/references/agent.md +19 -14
  150. package/skills/e2e/references/debugging.md +2 -1
  151. package/skills/e2e/references/explore.md +83 -0
  152. package/skills/e2e/references/running.md +4 -3
  153. package/skills/e2e/references/setup.md +10 -7
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/agent/model/adapter.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAI9B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsEzC;;;;GAIG;AACH,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IAC5C,OAAO,CAAqB;IAErC,YAAY,KAAa,EAAE,OAAO,GAA0C,EAAE;QAC5E,KAAK,CAAC,sBAAsB,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAClG,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC;AACnD,CAAC;AAED,+EAA+E;AAC/E,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAwC;IAC3E,OAAO,CACL,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC;QACnD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC,CACrD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/agent/model/adapter.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAI9B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAyEzC;;;;GAIG;AACH,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IAC5C,OAAO,CAAqB;IAErC,YAAY,KAAa,EAAE,OAAO,GAA0C,EAAE;QAC5E,KAAK,CAAC,sBAAsB,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAClG,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC;AACnD,CAAC;AAED,+EAA+E;AAC/E,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAwC;IAC3E,OAAO,CACL,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC;QACnD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC,CACrD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Recognizes a context-window overflow in a provider failure. Providers say
3
+ * it in as many ways as there are providers, and a gateway forwards their
4
+ * words; the patterns below are the ones seen in the wild (ported from the
5
+ * pi agent harness, which collected them across twenty providers), plus the
6
+ * HTTP 413 some of them answer with instead of a message.
7
+ *
8
+ * The runner reacts to an overflow differently from any other provider
9
+ * failure: the request was too big, so a smaller request can still succeed,
10
+ * where a 5xx or a rate limit needs only time.
11
+ */
12
+ /**
13
+ * Whether a provider failure says the request exceeded the model's context
14
+ * window. Reads the message, the HTTP status, and the raw response body of
15
+ * the error and of everything it wraps (`cause`, or the last attempt of a
16
+ * spent retry chain), so a gateway or retry wrapper does not hide the
17
+ * provider's words.
18
+ */
19
+ export declare function isContextOverflow(error: unknown): boolean;
20
+ //# sourceMappingURL=overflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../../../src/agent/model/overflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAwCH;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CASzD"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Recognizes a context-window overflow in a provider failure. Providers say
3
+ * it in as many ways as there are providers, and a gateway forwards their
4
+ * words; the patterns below are the ones seen in the wild (ported from the
5
+ * pi agent harness, which collected them across twenty providers), plus the
6
+ * HTTP 413 some of them answer with instead of a message.
7
+ *
8
+ * The runner reacts to an overflow differently from any other provider
9
+ * failure: the request was too big, so a smaller request can still succeed,
10
+ * where a 5xx or a rate limit needs only time.
11
+ */
12
+ const OVERFLOW_PATTERNS = [
13
+ /prompt is too long/i, // Anthropic
14
+ /request_too_large/i, // Anthropic (HTTP 413)
15
+ /input is too long for requested model/i, // Amazon Bedrock
16
+ /exceeds the context window/i, // OpenAI
17
+ /exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\d,]+ tokens?|\s*\([\d,]+\))/i, // OpenAI-compatible proxies
18
+ /input token count.*exceeds the maximum/i, // Google
19
+ /maximum prompt length is \d+/i, // xAI
20
+ /reduce the length of the messages/i, // Groq
21
+ /maximum context length is \d+ tokens/i, // OpenRouter
22
+ /exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i, // OpenRouter/Poolside
23
+ /input \(\d+ tokens\) is longer than the model'?s context length \(\d+ tokens\)/i, // Together AI
24
+ /exceeds the limit of \d+/i, // GitHub Copilot
25
+ /exceeds the available context size/i, // llama.cpp
26
+ /greater than the context length/i, // LM Studio
27
+ /context window exceeds limit/i, // MiniMax
28
+ /exceeded model token limit/i, // Kimi
29
+ /too large for model with \d+ maximum context length/i, // Mistral
30
+ /prompt has [\d,]+ tokens?, but the configured context size is [\d,]+ tokens?/i, // DS4
31
+ /model_context_window_exceeded/i, // z.ai
32
+ /prompt too long; exceeded (?:max )?context length/i, // Ollama
33
+ /range of input length should be/i, // DashScope / Qwen
34
+ /context[_ ]length[_ ]exceeded/i, // generic
35
+ /too many tokens/i, // generic
36
+ /token limit exceeded/i, // generic
37
+ ];
38
+ /** Failures that mention tokens without being an overflow: throttling and its cousins. */
39
+ const NON_OVERFLOW_PATTERNS = [
40
+ /throttl/i, // Amazon Bedrock: "ThrottlingException: Too many tokens, please wait…"
41
+ /service unavailable/i,
42
+ /rate limit/i,
43
+ /too many requests/i,
44
+ ];
45
+ /** Longest cause chain walked; a wrapped error rarely nests deeper. */
46
+ const MAX_CAUSE_DEPTH = 8;
47
+ /**
48
+ * Whether a provider failure says the request exceeded the model's context
49
+ * window. Reads the message, the HTTP status, and the raw response body of
50
+ * the error and of everything it wraps (`cause`, or the last attempt of a
51
+ * spent retry chain), so a gateway or retry wrapper does not hide the
52
+ * provider's words.
53
+ */
54
+ export function isContextOverflow(error) {
55
+ let current = error;
56
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && current !== undefined && current !== null; depth += 1) {
57
+ if (describesOverflow(current))
58
+ return true;
59
+ if (typeof current !== 'object')
60
+ return false;
61
+ const wrapper = current;
62
+ current = wrapper.cause ?? wrapper.lastError;
63
+ }
64
+ return false;
65
+ }
66
+ /**
67
+ * One error object, read on its own. HTTP 413 is an overflow whatever the
68
+ * text says. A text field counts when it matches an overflow pattern and no
69
+ * throttling pattern; the veto is per field, so a wrapper's "rate limit"
70
+ * message cannot hide the provider's "prompt is too long" in the body.
71
+ */
72
+ function describesOverflow(error) {
73
+ if (typeof error !== 'object' || error === null)
74
+ return false;
75
+ const record = error;
76
+ if ((record.statusCode ?? record.status) === 413)
77
+ return true;
78
+ return [record.message, record.responseBody].some((text) => typeof text === 'string' &&
79
+ OVERFLOW_PATTERNS.some((pattern) => pattern.test(text)) &&
80
+ !NON_OVERFLOW_PATTERNS.some((pattern) => pattern.test(text)));
81
+ }
82
+ //# sourceMappingURL=overflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../../src/agent/model/overflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,iBAAiB,GAAsB;IAC3C,qBAAqB,EAAE,YAAY;IACnC,oBAAoB,EAAE,uBAAuB;IAC7C,wCAAwC,EAAE,iBAAiB;IAC3D,6BAA6B,EAAE,SAAS;IACxC,4FAA4F,EAAE,4BAA4B;IAC1H,yCAAyC,EAAE,SAAS;IACpD,+BAA+B,EAAE,MAAM;IACvC,oCAAoC,EAAE,OAAO;IAC7C,uCAAuC,EAAE,aAAa;IACtD,kEAAkE,EAAE,sBAAsB;IAC1F,iFAAiF,EAAE,cAAc;IACjG,2BAA2B,EAAE,iBAAiB;IAC9C,qCAAqC,EAAE,YAAY;IACnD,kCAAkC,EAAE,YAAY;IAChD,+BAA+B,EAAE,UAAU;IAC3C,6BAA6B,EAAE,OAAO;IACtC,sDAAsD,EAAE,UAAU;IAClE,+EAA+E,EAAE,MAAM;IACvF,gCAAgC,EAAE,OAAO;IACzC,oDAAoD,EAAE,SAAS;IAC/D,kCAAkC,EAAE,mBAAmB;IACvD,gCAAgC,EAAE,UAAU;IAC5C,kBAAkB,EAAE,UAAU;IAC9B,uBAAuB,EAAE,UAAU;CACpC,CAAC;AAEF,0FAA0F;AAC1F,MAAM,qBAAqB,GAAsB;IAC/C,UAAU,EAAE,uEAAuE;IACnF,sBAAsB;IACtB,aAAa;IACb,oBAAoB;CACrB,CAAC;AAEF,uEAAuE;AACvE,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,OAAO,GAAY,KAAK,CAAC;IAC7B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,eAAe,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrG,IAAI,iBAAiB,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC9C,MAAM,OAAO,GAAG,OAAmD,CAAC;QACpE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,KAAc;IACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,MAAM,GAAG,KAA8F,CAAC;IAC9G,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC9D,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAC/C,CAAC,IAAI,EAAE,EAAE,CACP,OAAO,IAAI,KAAK,QAAQ;QACxB,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAC/D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Prompt-cache hints: what a request carries so the provider can serve the
3
+ * repeated prefix (system prompt, tool definitions, the conversation so far)
4
+ * from its cache instead of reading it again at full price.
5
+ *
6
+ * Anthropic caches only up to an explicit breakpoint, so the system prompt
7
+ * carries one (it covers the tool definitions ahead of it) and the newest
8
+ * message carries the other: the next turn's request then matches everything
9
+ * up to it. OpenAI caches prefixes on its own and takes a routing key; one
10
+ * key per system prompt sends every call of a run to the same cache. Other
11
+ * providers get nothing extra, and the request is exactly what it was.
12
+ */
13
+ import type { ModelMessage, SystemModelMessage } from 'ai';
14
+ import type { ProviderOptions } from '../../types.ts';
15
+ export type CacheFamily = 'anthropic' | 'openai';
16
+ /** What the hints need to know about the model: the provider and model id the SDK reports. */
17
+ export interface CacheModelRef {
18
+ readonly provider?: string | undefined;
19
+ readonly modelId?: string | undefined;
20
+ }
21
+ /**
22
+ * The provider family a model routes to. A gateway model names its upstream
23
+ * in the model id (`anthropic/claude-…`); a direct provider names it in the
24
+ * provider (`anthropic.messages`, `openai.responses`).
25
+ */
26
+ export declare function cacheFamily(model: CacheModelRef | undefined): CacheFamily | undefined;
27
+ export interface PromptCacheHints {
28
+ readonly family: CacheFamily | undefined;
29
+ /** The system prompt as the request's instructions, carrying a breakpoint where the provider needs one. */
30
+ instructions(system: string): string | SystemModelMessage;
31
+ /**
32
+ * Request-level provider options: the caller's own, plus a prompt-cache
33
+ * routing key where the provider takes one. The caller's values win.
34
+ */
35
+ providerOptions(base: ProviderOptions | undefined, system: string): ProviderOptions | undefined;
36
+ /**
37
+ * Moves the conversation breakpoint to the newest message. Every other
38
+ * message loses its breakpoint, so a history that carries forward across
39
+ * turns never accumulates more than the provider allows.
40
+ */
41
+ markLatest(messages: ModelMessage[]): ModelMessage[];
42
+ }
43
+ /** The hints for one model; a no-op for providers without a cache the request can address. */
44
+ export declare function promptCacheHints(model: CacheModelRef | undefined): PromptCacheHints;
45
+ /** A stable key for one system prompt, so every call sharing the prefix routes alike. */
46
+ export declare function promptCacheKey(system: string): string;
47
+ //# sourceMappingURL=prompt-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-cache.d.ts","sourceRoot":"","sources":["../../../src/agent/model/prompt-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,IAAI,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEjD,8FAA8F;AAC9F,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAMrF;AAID,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IACzC,2GAA2G;IAC3G,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,kBAAkB,CAAC;IAC1D;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IAChG;;;;OAIG;IACH,UAAU,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAAC;CACtD;AAED,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,gBAAgB,CAiBnF;AAED,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Prompt-cache hints: what a request carries so the provider can serve the
3
+ * repeated prefix (system prompt, tool definitions, the conversation so far)
4
+ * from its cache instead of reading it again at full price.
5
+ *
6
+ * Anthropic caches only up to an explicit breakpoint, so the system prompt
7
+ * carries one (it covers the tool definitions ahead of it) and the newest
8
+ * message carries the other: the next turn's request then matches everything
9
+ * up to it. OpenAI caches prefixes on its own and takes a routing key; one
10
+ * key per system prompt sends every call of a run to the same cache. Other
11
+ * providers get nothing extra, and the request is exactly what it was.
12
+ */
13
+ import { createHash } from 'node:crypto';
14
+ /**
15
+ * The provider family a model routes to. A gateway model names its upstream
16
+ * in the model id (`anthropic/claude-…`); a direct provider names it in the
17
+ * provider (`anthropic.messages`, `openai.responses`).
18
+ */
19
+ export function cacheFamily(model) {
20
+ const provider = (model?.provider ?? '').toLowerCase();
21
+ const modelId = (model?.modelId ?? '').toLowerCase();
22
+ if (modelId.startsWith('anthropic/') || provider.startsWith('anthropic'))
23
+ return 'anthropic';
24
+ if (modelId.startsWith('openai/') || provider.startsWith('openai'))
25
+ return 'openai';
26
+ return undefined;
27
+ }
28
+ const ANTHROPIC_BREAKPOINT = { cacheControl: { type: 'ephemeral' } };
29
+ /** The hints for one model; a no-op for providers without a cache the request can address. */
30
+ export function promptCacheHints(model) {
31
+ const family = cacheFamily(model);
32
+ return {
33
+ family,
34
+ instructions: (system) => family === 'anthropic'
35
+ ? { role: 'system', content: system, providerOptions: { anthropic: ANTHROPIC_BREAKPOINT } }
36
+ : system,
37
+ providerOptions: (base, system) => {
38
+ if (family !== 'openai')
39
+ return base;
40
+ return {
41
+ ...base,
42
+ openai: { promptCacheKey: promptCacheKey(system), ...base?.['openai'] },
43
+ };
44
+ },
45
+ markLatest: (messages) => (family === 'anthropic' ? moveBreakpoint(messages) : messages),
46
+ };
47
+ }
48
+ /** A stable key for one system prompt, so every call sharing the prefix routes alike. */
49
+ export function promptCacheKey(system) {
50
+ return `e2e-${createHash('sha256').update(system).digest('hex').slice(0, 16)}`;
51
+ }
52
+ function moveBreakpoint(messages) {
53
+ if (messages.length === 0)
54
+ return messages;
55
+ const last = messages.length - 1;
56
+ return messages.map((message, index) => (index === last ? withBreakpoint(message) : withoutBreakpoint(message)));
57
+ }
58
+ function withBreakpoint(message) {
59
+ const anthropic = message.providerOptions?.['anthropic'];
60
+ return {
61
+ ...message,
62
+ providerOptions: { ...message.providerOptions, anthropic: { ...anthropic, ...ANTHROPIC_BREAKPOINT } },
63
+ };
64
+ }
65
+ function withoutBreakpoint(message) {
66
+ const anthropic = message.providerOptions?.['anthropic'];
67
+ if (anthropic === undefined || !('cacheControl' in anthropic))
68
+ return message;
69
+ const { cacheControl: _dropped, ...rest } = anthropic;
70
+ const { anthropic: _anthropic, ...others } = message.providerOptions ?? {};
71
+ const providerOptions = Object.keys(rest).length === 0 ? others : { ...others, anthropic: rest };
72
+ if (Object.keys(providerOptions).length === 0) {
73
+ const { providerOptions: _none, ...bare } = message;
74
+ return bare;
75
+ }
76
+ return { ...message, providerOptions };
77
+ }
78
+ //# sourceMappingURL=prompt-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-cache.js","sourceRoot":"","sources":["../../../src/agent/model/prompt-cache.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  /**
2
- * AI SDK adapter. One implementation serves every
3
- * provider: a `provider/model-id` reference resolves through the AI Gateway,
4
- * and a caller-supplied AI SDK model instance (`openai('gpt-4o')`, a local
5
- * provider, a scripted test model) is used directly. Everything after model
6
- * construction — bounded requests, closed-grammar validation, usage and error
7
- * translation — is provider-independent.
2
+ * AI SDK adapter. One implementation serves every provider: the
3
+ * caller-supplied AI SDK model instance (`gateway('openai/gpt-5.4-mini')`,
4
+ * `openrouter(...)`, `openai('gpt-4o')`, a local provider, a scripted test
5
+ * model) is used as is. Everything after the model — bounded requests,
6
+ * closed-grammar validation, usage and error translation — is
7
+ * provider-independent.
8
8
  */
9
- import { type ResolvedModel, type SdkLanguageModel } from '../../config/agent.ts';
9
+ import type { ResolvedModel, SdkLanguageModel } from '../../config/agent.ts';
10
10
  import { type ModelAdapter } from './adapter.ts';
11
11
  /**
12
12
  * Provider transport retries; distinct from runner-owned model-call budget.
@@ -23,30 +23,47 @@ export declare const TRANSPORT_RETRIES = 5;
23
23
  /** Creates the adapter for one resolved model, or fails with MODEL_UNAVAILABLE. */
24
24
  export declare function createModelAdapter(model: ResolvedModel | undefined): ModelAdapter;
25
25
  /**
26
- * Builds the bare AI SDK language model for one resolved model reference, for
27
- * callers that drive the SDK directly (the default step executor) rather than
28
- * through the adapter. Fails with MODEL_UNAVAILABLE exactly like the adapter.
29
- * Gateway references require the AI SDK to be loaded (loadAiSdk) first; model
30
- * instances never touch it.
26
+ * The bare AI SDK language model of one resolved model, for callers that
27
+ * drive the SDK directly (the default step executor) rather than through the
28
+ * adapter.
31
29
  */
32
30
  export declare function instantiateLanguageModel(model: ResolvedModel): SdkLanguageModel;
31
+ /** The AI SDK usage fields the runner reads, structurally. */
32
+ export interface SdkUsage {
33
+ readonly inputTokens?: number | undefined;
34
+ readonly outputTokens?: number | undefined;
35
+ readonly inputTokenDetails?: {
36
+ readonly cacheReadTokens?: number | undefined;
37
+ readonly cacheWriteTokens?: number | undefined;
38
+ } | undefined;
39
+ }
33
40
  /**
34
- * The AI Gateway reports per-request cost in provider metadata when available;
35
- * other providers simply lack the key. `cost` is what the gateway bills; BYOK
36
- * routes bill the provider key directly and report `cost: "0"`, so fall back
37
- * to `marketCost`, the list-price estimate of the same request.
41
+ * The prompt-cache split of one call's input, when the provider reported it.
42
+ * Providers that cache nothing report zeros; providers that do not report the
43
+ * split leave the fields undefined, and so does the report.
44
+ */
45
+ export declare function cacheTokenFields(usage: SdkUsage | undefined): {
46
+ cacheReadTokens?: number;
47
+ cacheWriteTokens?: number;
48
+ };
49
+ /**
50
+ * Per-request cost, where the provider reports one in its metadata. The
51
+ * Vercel AI Gateway's `cost` is what it bills; BYOK routes bill the provider
52
+ * key directly and report `cost: "0"`, so fall back to `marketCost`, the
53
+ * list-price estimate of the same request. OpenRouter reports `usage.cost`
54
+ * when its provider is asked to account usage. Other providers simply lack
55
+ * the key.
38
56
  */
39
57
  export declare function readCost(metadata: Readonly<Record<string, Readonly<Record<string, unknown>>>> | undefined): number | undefined;
40
58
  /**
41
- * A rejected credential names the variable the runner read it from, whether
42
- * that was `E2E_MODEL_API_KEY`, the gateway's own `AI_GATEWAY_API_KEY`, or
43
- * the one `apiKeyEnv` chose. The gateway's own text only ever says
44
- * `AI_GATEWAY_API_KEY`. Takes the resolved model (the adapter path) or the SDK
45
- * model object (the tool loop path); a model that brought its own credential
46
- * has no variable to name. Requires the SDK to be loaded, which every caller
47
- * has done by the time a provider call has failed.
59
+ * A rejected credential is named for what it is. The model instance owns its
60
+ * credential, read from the provider package's own variable
61
+ * (`AI_GATEWAY_API_KEY`, `OPENROUTER_API_KEY`, ...) or passed at
62
+ * construction, so the runner can only say that the provider refused it.
63
+ * Requires the SDK to be loaded, which every caller has done by the time a
64
+ * provider call has failed.
48
65
  */
49
- export declare function credentialHint(cause: unknown, model?: ResolvedModel | SdkLanguageModel | string): string;
66
+ export declare function credentialHint(cause: unknown): string;
50
67
  /**
51
68
  * True when a provider call ended by abort or timeout rather than by a
52
69
  * provider answer, including a retry chain the deadline cut short. Requires
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/agent/model/sdk.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAuB,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIvG,OAAO,EAIL,KAAK,YAAY,EAKlB,MAAM,cAAc,CAAC;AAOtB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,YAAY,CAuFjF;AAsBD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,aAAa,GAAG,gBAAgB,CAE/E;AAuFD;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,GAChF,MAAM,GAAG,SAAS,CAIpB;AAqDD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,gBAAgB,GAAG,MAAM,GAAG,MAAM,CAcxG;AAoBD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI/C"}
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/agent/model/sdk.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAM7E,OAAO,EAIL,KAAK,YAAY,EAKlB,MAAM,cAAc,CAAC;AAEtB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,YAAY,CAuFjF;AAsBD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,aAAa,GAAG,gBAAgB,CAE/E;AAUD,8DAA8D;AAC9D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,iBAAiB,CAAC,EACvB;QACE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChD,GACD,SAAS,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,QAAQ,GAAG,SAAS,GAC1B;IAAE,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,CAMzD;AAqCD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,GAChF,MAAM,GAAG,SAAS,CAOpB;AA6DD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAYrD;AAaD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI/C"}
@@ -1,20 +1,18 @@
1
1
  /**
2
- * AI SDK adapter. One implementation serves every
3
- * provider: a `provider/model-id` reference resolves through the AI Gateway,
4
- * and a caller-supplied AI SDK model instance (`openai('gpt-4o')`, a local
5
- * provider, a scripted test model) is used directly. Everything after model
6
- * construction — bounded requests, closed-grammar validation, usage and error
7
- * translation — is provider-independent.
2
+ * AI SDK adapter. One implementation serves every provider: the
3
+ * caller-supplied AI SDK model instance (`gateway('openai/gpt-5.4-mini')`,
4
+ * `openrouter(...)`, `openai('gpt-4o')`, a local provider, a scripted test
5
+ * model) is used as is. Everything after the model — bounded requests,
6
+ * closed-grammar validation, usage and error translation — is
7
+ * provider-independent.
8
8
  */
9
9
  import { withHint } from '../../internal/errors.js';
10
- import { GATEWAY_API_KEY_ENV } from '../../config/agent.js';
11
10
  import { aiSdk, loadAiSdk } from '../ai-sdk.js';
12
11
  import { packageVersion } from '../../internal/package-version.js';
13
12
  import { AgentError } from '../error.js';
13
+ import { isContextOverflow } from './overflow.js';
14
+ import { promptCacheHints } from './prompt-cache.js';
14
15
  import { imageTokenUpperBound, ModelOutputInvalidError, tokenUpperBound, } from './adapter.js';
15
- /** Default AI Gateway base URL used when no endpoint override is configured. */
16
- const DEFAULT_GATEWAY_ENDPOINT = 'https://ai-gateway.vercel.sh/v4/ai';
17
- const PROVIDER_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
18
16
  /**
19
17
  * Provider transport retries; distinct from runner-owned model-call budget.
20
18
  *
@@ -30,23 +28,22 @@ export const TRANSPORT_RETRIES = 5;
30
28
  /** Creates the adapter for one resolved model, or fails with MODEL_UNAVAILABLE. */
31
29
  export function createModelAdapter(model) {
32
30
  if (model === undefined) {
33
- throw new AgentError('MODEL_UNAVAILABLE', 'the agent fixture requires a model: pass one to createAgent({ model }), set agent.model, or set E2E_MODEL');
31
+ throw new AgentError('MODEL_UNAVAILABLE', 'the agent fixture requires a model: pass an AI SDK model instance to createAgent({ model }) or set agent.model');
34
32
  }
35
- const { endpoint, flavor } = validateModel(model);
36
33
  const adapterVersion = packageVersion(import.meta.url, '../../../package.json', '0.0.0');
37
- // The AI SDK is an optional peer: the language model is constructed on the
38
- // first call, after the loader has resolved (or clearly refused) it.
39
- let languageModel;
34
+ const languageModel = model.model;
40
35
  return {
41
36
  provenance: {
42
37
  provider: model.provider,
43
38
  model: model.id,
44
- endpoint,
45
- adapterVersion: `${flavor}/${adapterVersion}`,
39
+ // The instance owns its transport; the report records that the endpoint
40
+ // is whatever the provider package was configured with.
41
+ endpoint: 'provider-default',
42
+ adapterVersion: `ai-sdk/${adapterVersion}`,
46
43
  },
47
44
  async generate(call) {
48
45
  const { generateText, jsonSchema, Output } = await loadAiSdk();
49
- languageModel ??= instantiate(model).languageModel;
46
+ const cache = promptCacheHints(languageModel);
50
47
  const images = call.images ?? [];
51
48
  const inputBound = tokenUpperBound(call.system) +
52
49
  tokenUpperBound(call.prompt) +
@@ -56,9 +53,12 @@ export function createModelAdapter(model) {
56
53
  }
57
54
  let issue;
58
55
  const schema = call.schema;
56
+ // The policy prefix is what every judgment call of a run shares, so it
57
+ // is where the prompt cache is addressed; the prompt itself is one-off.
58
+ const providerOptions = cache.providerOptions(call.providerOptions, call.system);
59
59
  const settings = {
60
60
  model: languageModel,
61
- system: call.system,
61
+ instructions: cache.instructions(call.system),
62
62
  // Text-only calls keep the plain prompt form; images require the
63
63
  // multi-part message form, and both must carry the same instruction
64
64
  // text in the same position relative to the system policy.
@@ -66,9 +66,7 @@ export function createModelAdapter(model) {
66
66
  ? { prompt: call.prompt }
67
67
  : { messages: [userMessage(call.prompt, images)] }),
68
68
  maxOutputTokens: call.maxOutputTokens,
69
- ...(call.providerOptions === undefined
70
- ? {}
71
- : { providerOptions: call.providerOptions }),
69
+ ...(providerOptions === undefined ? {} : { providerOptions: providerOptions }),
72
70
  maxRetries: TRANSPORT_RETRIES,
73
71
  abortSignal: call.signal,
74
72
  timeout: call.timeoutMs,
@@ -105,7 +103,7 @@ export function createModelAdapter(model) {
105
103
  return { value: output, usage: readUsage(result, inputBound) };
106
104
  }
107
105
  catch (cause) {
108
- throw translateModelError(cause, issue, call.signal, model);
106
+ throw translateModelError(cause, issue, call.signal);
109
107
  }
110
108
  },
111
109
  };
@@ -130,61 +128,37 @@ function userMessage(prompt, images) {
130
128
  };
131
129
  }
132
130
  /**
133
- * Builds the bare AI SDK language model for one resolved model reference, for
134
- * callers that drive the SDK directly (the default step executor) rather than
135
- * through the adapter. Fails with MODEL_UNAVAILABLE exactly like the adapter.
136
- * Gateway references require the AI SDK to be loaded (loadAiSdk) first; model
137
- * instances never touch it.
131
+ * The bare AI SDK language model of one resolved model, for callers that
132
+ * drive the SDK directly (the default step executor) rather than through the
133
+ * adapter.
138
134
  */
139
135
  export function instantiateLanguageModel(model) {
140
- return instantiate(model).languageModel;
136
+ return model.model;
141
137
  }
142
138
  /**
143
- * Validates one resolved model and derives its report provenance without
144
- * touching the AI SDK, so an adapter can be constructed and refuse clearly —
145
- * before the optional peer dependency is ever loaded.
139
+ * The prompt-cache split of one call's input, when the provider reported it.
140
+ * Providers that cache nothing report zeros; providers that do not report the
141
+ * split leave the fields undefined, and so does the report.
146
142
  */
147
- function validateModel(model) {
148
- if (model.kind === 'instance') {
149
- // The instance owns its transport; the report records that the endpoint is
150
- // whatever the provider package defaults to.
151
- return { endpoint: 'provider-default', flavor: 'ai-sdk' };
152
- }
153
- if (!PROVIDER_PATTERN.test(model.provider)) {
154
- throw new AgentError('MODEL_UNAVAILABLE', `unknown model provider "${model.provider}"; expected a gateway provider ID or an AI SDK model instance`);
155
- }
156
- if (model.apiKey === undefined) {
157
- throw new AgentError('MODEL_UNAVAILABLE', `no model credential: set ${model.apiKeyEnv} or ${GATEWAY_API_KEY_ENV}`);
158
- }
159
- return { endpoint: model.endpoint ?? DEFAULT_GATEWAY_ENDPOINT, flavor: 'ai-gateway' };
160
- }
161
- /**
162
- * The variable each gateway language model's credential was read from, keyed
163
- * by the model object the SDK is handed. The tool loop sees only that object,
164
- * and this is how a rejected credential is still reported by its source. A
165
- * name, never a value.
166
- */
167
- const credentialSources = new WeakMap();
168
- /** Builds the AI SDK language model for one validated resolved model. */
169
- function instantiate(model) {
170
- if (model.kind === 'instance')
171
- return { languageModel: model.model };
172
- const { endpoint } = validateModel(model);
173
- const gateway = aiSdk().createGateway({ apiKey: model.apiKey, baseURL: endpoint });
174
- const languageModel = gateway.languageModel(`${model.provider}/${model.id}`);
175
- credentialSources.set(languageModel, model.apiKeySource ?? model.apiKeyEnv);
176
- return { languageModel };
143
+ export function cacheTokenFields(usage) {
144
+ const details = usage?.inputTokenDetails;
145
+ return {
146
+ ...(typeof details?.cacheReadTokens === 'number' ? { cacheReadTokens: details.cacheReadTokens } : {}),
147
+ ...(typeof details?.cacheWriteTokens === 'number' ? { cacheWriteTokens: details.cacheWriteTokens } : {}),
148
+ };
177
149
  }
178
150
  function readUsage(result, inputBound) {
179
151
  const inputTokens = result.usage?.inputTokens;
180
152
  const outputTokens = result.usage?.outputTokens;
181
153
  const estimatedCostUsd = readCost(result.providerMetadata);
154
+ const cache = cacheTokenFields(result.usage);
182
155
  if (typeof inputTokens === 'number' && typeof outputTokens === 'number') {
183
- return { inputTokens, outputTokens, accounting: 'provider', estimatedCostUsd };
156
+ return { inputTokens, outputTokens, ...cache, accounting: 'provider', estimatedCostUsd };
184
157
  }
185
158
  return {
186
159
  inputTokens: inputBound,
187
160
  outputTokens: tokenUpperBound(result.text ?? ''),
161
+ ...cache,
188
162
  accounting: 'adapter-upper-bound',
189
163
  estimatedCostUsd,
190
164
  };
@@ -208,16 +182,22 @@ function parseJsonObject(text) {
208
182
  }
209
183
  }
210
184
  /**
211
- * The AI Gateway reports per-request cost in provider metadata when available;
212
- * other providers simply lack the key. `cost` is what the gateway bills; BYOK
213
- * routes bill the provider key directly and report `cost: "0"`, so fall back
214
- * to `marketCost`, the list-price estimate of the same request.
185
+ * Per-request cost, where the provider reports one in its metadata. The
186
+ * Vercel AI Gateway's `cost` is what it bills; BYOK routes bill the provider
187
+ * key directly and report `cost: "0"`, so fall back to `marketCost`, the
188
+ * list-price estimate of the same request. OpenRouter reports `usage.cost`
189
+ * when its provider is asked to account usage. Other providers simply lack
190
+ * the key.
215
191
  */
216
192
  export function readCost(metadata) {
217
193
  const billed = parseCost(metadata?.['gateway']?.['cost']);
218
194
  if (billed !== undefined && billed > 0)
219
195
  return billed;
220
- return parseCost(metadata?.['gateway']?.['marketCost']) ?? billed;
196
+ const market = parseCost(metadata?.['gateway']?.['marketCost']) ?? billed;
197
+ if (market !== undefined)
198
+ return market;
199
+ const openrouter = metadata?.['openrouter']?.['usage'];
200
+ return typeof openrouter === 'object' && openrouter !== null ? parseCost(openrouter['cost']) : undefined;
221
201
  }
222
202
  function parseCost(raw) {
223
203
  const cost = typeof raw === 'string' ? Number(raw) : raw;
@@ -228,7 +208,7 @@ function parseCost(raw) {
228
208
  * only after `generate` loaded the SDK, so the cached module is available for
229
209
  * the error-class checks.
230
210
  */
231
- function translateModelError(rawCause, issue, signal, model) {
211
+ function translateModelError(rawCause, issue, signal) {
232
212
  const { APICallError, NoObjectGeneratedError } = aiSdk();
233
213
  const cause = unwrapRetry(rawCause);
234
214
  if (cause instanceof AgentError)
@@ -244,25 +224,29 @@ function translateModelError(rawCause, issue, signal, model) {
244
224
  if (NoObjectGeneratedError.isInstance(cause)) {
245
225
  return new ModelOutputInvalidError(issue ?? 'provider response did not match the closed response grammar', { ...(cause.text !== undefined ? { rawText: cause.text } : {}), cause });
246
226
  }
227
+ // Too big a request is not a provider outage: the observation budget or
228
+ // the step is what needs to shrink, and the code says so.
229
+ if (isContextOverflow(cause)) {
230
+ return new AgentError('CONTEXT_OVERFLOW', `the model request exceeded the context window: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
231
+ }
247
232
  if (APICallError.isInstance(cause)) {
248
- return new AgentError('MODEL_PROVIDER_FAILED', withHint(`model provider failed: ${cause.message}`, credentialHint(cause, model)), { cause });
233
+ return new AgentError('MODEL_PROVIDER_FAILED', withHint(`model provider failed: ${cause.message}`, credentialHint(cause)), { cause });
249
234
  }
250
235
  return new AgentError('MODEL_PROVIDER_FAILED', `model provider failed: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
251
236
  }
252
237
  /**
253
- * A rejected credential names the variable the runner read it from, whether
254
- * that was `E2E_MODEL_API_KEY`, the gateway's own `AI_GATEWAY_API_KEY`, or
255
- * the one `apiKeyEnv` chose. The gateway's own text only ever says
256
- * `AI_GATEWAY_API_KEY`. Takes the resolved model (the adapter path) or the SDK
257
- * model object (the tool loop path); a model that brought its own credential
258
- * has no variable to name. Requires the SDK to be loaded, which every caller
259
- * has done by the time a provider call has failed.
238
+ * A rejected credential is named for what it is. The model instance owns its
239
+ * credential, read from the provider package's own variable
240
+ * (`AI_GATEWAY_API_KEY`, `OPENROUTER_API_KEY`, ...) or passed at
241
+ * construction, so the runner can only say that the provider refused it.
242
+ * Requires the SDK to be loaded, which every caller has done by the time a
243
+ * provider call has failed.
260
244
  */
261
- export function credentialHint(cause, model) {
245
+ export function credentialHint(cause) {
262
246
  const failure = unwrapRetry(cause);
263
247
  if (!(failure instanceof Error))
264
248
  return '';
265
- // The gateway raises its own authentication error class and a raw provider
249
+ // Gateways raise their own authentication error classes and a raw provider
266
250
  // an APICallError; both carry the HTTP status, so that is what is read.
267
251
  const statusCode = failure.statusCode;
268
252
  const rejected = statusCode === 401 ||
@@ -270,18 +254,7 @@ export function credentialHint(cause, model) {
270
254
  /unauthenticated|unauthorized|authentication/i.test(`${failure.name} ${failure.message}`);
271
255
  if (!rejected)
272
256
  return '';
273
- const source = credentialSource(model);
274
- if (source === undefined)
275
- return 'the provider rejected the credential the model instance was created with';
276
- return `the gateway rejected the credential read from ${source}: check that the key is complete and belongs to the AI Gateway (https://vercel.com/docs/ai-gateway)`;
277
- }
278
- /** The variable a gateway model's credential came from; undefined for a model that owns its own. */
279
- function credentialSource(model) {
280
- if (model === undefined || typeof model !== 'object')
281
- return undefined;
282
- if ('kind' in model)
283
- return model.kind === 'gateway' ? (model.apiKeySource ?? model.apiKeyEnv) : undefined;
284
- return credentialSources.get(model);
257
+ return 'the provider rejected the credential the model instance was created with: check the variable the provider package reads, or the key passed at construction';
285
258
  }
286
259
  /**
287
260
  * Unwraps a spent transport retry chain to the attempt that actually failed,