agentfootprint 9.5.1 → 9.7.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 (185) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/ai-instructions/claude-code/SKILL.md +1 -1
  4. package/dist/adapters/code/agentcore.js +294 -0
  5. package/dist/adapters/code/agentcore.js.map +1 -0
  6. package/dist/adapters/code/local.js +200 -0
  7. package/dist/adapters/code/local.js.map +1 -0
  8. package/dist/adapters/llm/AnthropicProvider.js +6 -0
  9. package/dist/adapters/llm/AnthropicProvider.js.map +1 -1
  10. package/dist/adapters/llm/BedrockProvider.js +7 -0
  11. package/dist/adapters/llm/BedrockProvider.js.map +1 -1
  12. package/dist/adapters/llm/BrowserAnthropicProvider.js +13 -0
  13. package/dist/adapters/llm/BrowserAnthropicProvider.js.map +1 -1
  14. package/dist/adapters/llm/BrowserOpenAIProvider.js +14 -0
  15. package/dist/adapters/llm/BrowserOpenAIProvider.js.map +1 -1
  16. package/dist/adapters/llm/OpenAIProvider.js +7 -0
  17. package/dist/adapters/llm/OpenAIProvider.js.map +1 -1
  18. package/dist/adapters/llm/contextWindow.js +284 -0
  19. package/dist/adapters/llm/contextWindow.js.map +1 -0
  20. package/dist/core/Agent.js +145 -0
  21. package/dist/core/Agent.js.map +1 -1
  22. package/dist/core/RunnerBase.js +67 -0
  23. package/dist/core/RunnerBase.js.map +1 -1
  24. package/dist/core/agent/stages/seed.js +67 -11
  25. package/dist/core/agent/stages/seed.js.map +1 -1
  26. package/dist/core/agent/stages/toolCalls.js +90 -0
  27. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  28. package/dist/core/codeRunnerTool.js +252 -0
  29. package/dist/core/codeRunnerTool.js.map +1 -0
  30. package/dist/core/runCheckpoint.js +102 -2
  31. package/dist/core/runCheckpoint.js.map +1 -1
  32. package/dist/core/toolSessions.js +396 -0
  33. package/dist/core/toolSessions.js.map +1 -0
  34. package/dist/core/tools.js.map +1 -1
  35. package/dist/doors/providers.js +13 -0
  36. package/dist/doors/providers.js.map +1 -1
  37. package/dist/esm/adapters/code/agentcore.d.ts +133 -0
  38. package/dist/esm/adapters/code/agentcore.js +290 -0
  39. package/dist/esm/adapters/code/agentcore.js.map +1 -0
  40. package/dist/esm/adapters/code/local.d.ts +99 -0
  41. package/dist/esm/adapters/code/local.js +196 -0
  42. package/dist/esm/adapters/code/local.js.map +1 -0
  43. package/dist/esm/adapters/llm/AnthropicProvider.js +6 -0
  44. package/dist/esm/adapters/llm/AnthropicProvider.js.map +1 -1
  45. package/dist/esm/adapters/llm/BedrockProvider.js +7 -0
  46. package/dist/esm/adapters/llm/BedrockProvider.js.map +1 -1
  47. package/dist/esm/adapters/llm/BrowserAnthropicProvider.js +13 -0
  48. package/dist/esm/adapters/llm/BrowserAnthropicProvider.js.map +1 -1
  49. package/dist/esm/adapters/llm/BrowserOpenAIProvider.js +14 -0
  50. package/dist/esm/adapters/llm/BrowserOpenAIProvider.js.map +1 -1
  51. package/dist/esm/adapters/llm/OpenAIProvider.js +7 -0
  52. package/dist/esm/adapters/llm/OpenAIProvider.js.map +1 -1
  53. package/dist/esm/adapters/llm/contextWindow.d.ts +111 -0
  54. package/dist/esm/adapters/llm/contextWindow.js +277 -0
  55. package/dist/esm/adapters/llm/contextWindow.js.map +1 -0
  56. package/dist/esm/adapters/types.d.ts +87 -0
  57. package/dist/esm/core/Agent.d.ts +65 -0
  58. package/dist/esm/core/Agent.js +146 -1
  59. package/dist/esm/core/Agent.js.map +1 -1
  60. package/dist/esm/core/RunnerBase.d.ts +51 -0
  61. package/dist/esm/core/RunnerBase.js +67 -0
  62. package/dist/esm/core/RunnerBase.js.map +1 -1
  63. package/dist/esm/core/agent/stages/seed.d.ts +12 -0
  64. package/dist/esm/core/agent/stages/seed.js +67 -11
  65. package/dist/esm/core/agent/stages/seed.js.map +1 -1
  66. package/dist/esm/core/agent/stages/toolCalls.d.ts +31 -0
  67. package/dist/esm/core/agent/stages/toolCalls.js +90 -0
  68. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  69. package/dist/esm/core/agent/types.d.ts +23 -2
  70. package/dist/esm/core/codeRunnerTool.d.ts +120 -0
  71. package/dist/esm/core/codeRunnerTool.js +247 -0
  72. package/dist/esm/core/codeRunnerTool.js.map +1 -0
  73. package/dist/esm/core/runCheckpoint.d.ts +25 -0
  74. package/dist/esm/core/runCheckpoint.js +100 -1
  75. package/dist/esm/core/runCheckpoint.js.map +1 -1
  76. package/dist/esm/core/toolSessions.d.ts +318 -0
  77. package/dist/esm/core/toolSessions.js +389 -0
  78. package/dist/esm/core/toolSessions.js.map +1 -0
  79. package/dist/esm/core/tools.d.ts +60 -0
  80. package/dist/esm/core/tools.js.map +1 -1
  81. package/dist/esm/doors/providers.d.ts +7 -0
  82. package/dist/esm/doors/providers.js +10 -0
  83. package/dist/esm/doors/providers.js.map +1 -1
  84. package/dist/esm/events/payloads.d.ts +50 -0
  85. package/dist/esm/events/registry.d.ts +9 -1
  86. package/dist/esm/events/registry.js +8 -0
  87. package/dist/esm/events/registry.js.map +1 -1
  88. package/dist/esm/index.d.ts +4 -1
  89. package/dist/esm/index.js +11 -1
  90. package/dist/esm/index.js.map +1 -1
  91. package/dist/esm/lib/mcp/mcpServe.js +37 -1
  92. package/dist/esm/lib/mcp/mcpServe.js.map +1 -1
  93. package/dist/esm/lib/trace-toolpack/traceToolpack.js +15 -1
  94. package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
  95. package/dist/esm/memory/causal/writeSnapshot.d.ts +17 -4
  96. package/dist/esm/memory/causal/writeSnapshot.js +31 -27
  97. package/dist/esm/memory/causal/writeSnapshot.js.map +1 -1
  98. package/dist/esm/memory/define.js +4 -0
  99. package/dist/esm/memory/define.js.map +1 -1
  100. package/dist/esm/memory/define.types.d.ts +17 -0
  101. package/dist/esm/memory/define.types.js.map +1 -1
  102. package/dist/esm/memory/index.d.ts +1 -0
  103. package/dist/esm/memory/index.js +1 -0
  104. package/dist/esm/memory/index.js.map +1 -1
  105. package/dist/esm/memory/stages/types.d.ts +13 -3
  106. package/dist/esm/memory/turn/index.d.ts +7 -0
  107. package/dist/esm/memory/turn/index.js +11 -0
  108. package/dist/esm/memory/turn/index.js.map +1 -0
  109. package/dist/esm/memory/turn/resolveTurnNumber.d.ts +94 -0
  110. package/dist/esm/memory/turn/resolveTurnNumber.js +75 -0
  111. package/dist/esm/memory/turn/resolveTurnNumber.js.map +1 -0
  112. package/dist/events/registry.js +8 -0
  113. package/dist/events/registry.js.map +1 -1
  114. package/dist/index.js +23 -1
  115. package/dist/index.js.map +1 -1
  116. package/dist/lib/mcp/mcpServe.js +37 -1
  117. package/dist/lib/mcp/mcpServe.js.map +1 -1
  118. package/dist/lib/trace-toolpack/traceToolpack.js +15 -1
  119. package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
  120. package/dist/memory/causal/writeSnapshot.js +31 -27
  121. package/dist/memory/causal/writeSnapshot.js.map +1 -1
  122. package/dist/memory/define.js +4 -0
  123. package/dist/memory/define.js.map +1 -1
  124. package/dist/memory/define.types.js.map +1 -1
  125. package/dist/memory/index.js +1 -0
  126. package/dist/memory/index.js.map +1 -1
  127. package/dist/memory/turn/index.js +16 -0
  128. package/dist/memory/turn/index.js.map +1 -0
  129. package/dist/memory/turn/resolveTurnNumber.js +81 -0
  130. package/dist/memory/turn/resolveTurnNumber.js.map +1 -0
  131. package/dist/types/adapters/code/agentcore.d.ts +134 -0
  132. package/dist/types/adapters/code/agentcore.d.ts.map +1 -0
  133. package/dist/types/adapters/code/local.d.ts +100 -0
  134. package/dist/types/adapters/code/local.d.ts.map +1 -0
  135. package/dist/types/adapters/llm/AnthropicProvider.d.ts.map +1 -1
  136. package/dist/types/adapters/llm/BedrockProvider.d.ts.map +1 -1
  137. package/dist/types/adapters/llm/BrowserAnthropicProvider.d.ts.map +1 -1
  138. package/dist/types/adapters/llm/BrowserOpenAIProvider.d.ts.map +1 -1
  139. package/dist/types/adapters/llm/OpenAIProvider.d.ts.map +1 -1
  140. package/dist/types/adapters/llm/contextWindow.d.ts +112 -0
  141. package/dist/types/adapters/llm/contextWindow.d.ts.map +1 -0
  142. package/dist/types/adapters/types.d.ts +87 -0
  143. package/dist/types/adapters/types.d.ts.map +1 -1
  144. package/dist/types/core/Agent.d.ts +65 -0
  145. package/dist/types/core/Agent.d.ts.map +1 -1
  146. package/dist/types/core/RunnerBase.d.ts +51 -0
  147. package/dist/types/core/RunnerBase.d.ts.map +1 -1
  148. package/dist/types/core/agent/stages/seed.d.ts +12 -0
  149. package/dist/types/core/agent/stages/seed.d.ts.map +1 -1
  150. package/dist/types/core/agent/stages/toolCalls.d.ts +31 -0
  151. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  152. package/dist/types/core/agent/types.d.ts +23 -2
  153. package/dist/types/core/agent/types.d.ts.map +1 -1
  154. package/dist/types/core/codeRunnerTool.d.ts +121 -0
  155. package/dist/types/core/codeRunnerTool.d.ts.map +1 -0
  156. package/dist/types/core/runCheckpoint.d.ts +25 -0
  157. package/dist/types/core/runCheckpoint.d.ts.map +1 -1
  158. package/dist/types/core/toolSessions.d.ts +319 -0
  159. package/dist/types/core/toolSessions.d.ts.map +1 -0
  160. package/dist/types/core/tools.d.ts +60 -0
  161. package/dist/types/core/tools.d.ts.map +1 -1
  162. package/dist/types/doors/providers.d.ts +7 -0
  163. package/dist/types/doors/providers.d.ts.map +1 -1
  164. package/dist/types/events/payloads.d.ts +50 -0
  165. package/dist/types/events/payloads.d.ts.map +1 -1
  166. package/dist/types/events/registry.d.ts +9 -1
  167. package/dist/types/events/registry.d.ts.map +1 -1
  168. package/dist/types/index.d.ts +4 -1
  169. package/dist/types/index.d.ts.map +1 -1
  170. package/dist/types/lib/mcp/mcpServe.d.ts.map +1 -1
  171. package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
  172. package/dist/types/memory/causal/writeSnapshot.d.ts +17 -4
  173. package/dist/types/memory/causal/writeSnapshot.d.ts.map +1 -1
  174. package/dist/types/memory/define.d.ts.map +1 -1
  175. package/dist/types/memory/define.types.d.ts +17 -0
  176. package/dist/types/memory/define.types.d.ts.map +1 -1
  177. package/dist/types/memory/index.d.ts +1 -0
  178. package/dist/types/memory/index.d.ts.map +1 -1
  179. package/dist/types/memory/stages/types.d.ts +13 -3
  180. package/dist/types/memory/stages/types.d.ts.map +1 -1
  181. package/dist/types/memory/turn/index.d.ts +8 -0
  182. package/dist/types/memory/turn/index.d.ts.map +1 -0
  183. package/dist/types/memory/turn/resolveTurnNumber.d.ts +95 -0
  184. package/dist/types/memory/turn/resolveTurnNumber.d.ts.map +1 -0
  185. package/package.json +1 -1
@@ -0,0 +1,277 @@
1
+ /**
2
+ * ContextWindowExceededError — "the request did not fit" as a typed error
3
+ * that names the fixes (9.6.0).
4
+ *
5
+ * Every vendor refuses an over-long request in its own words, and until now
6
+ * every one of them reached the caller as an opaque provider error:
7
+ *
8
+ * [openai] 400 Input tokens exceed the configured limit of 272000 tokens.
9
+ * Your messages resulted in 879073 tokens.
10
+ *
11
+ * That is the message a production field deployment spent a day on. It is a
12
+ * true sentence and a useless one: it does not say which of the framework's
13
+ * dials moves that number, and it looks exactly like a transient 400 — so the
14
+ * natural next move is to retry, which re-sends the same oversized history and
15
+ * fails identically, forever.
16
+ *
17
+ * So the adapters translate it, ONCE, into an error whose message contains the
18
+ * three fixes in the order they are worth trying, and which carries the two
19
+ * numbers (limit, actual) when the vendor stated them.
20
+ *
21
+ * **Detection is deliberately conservative.** Only unmistakable phrases and
22
+ * vendor codes translate; everything else passes through untouched as the
23
+ * provider error it always was. In particular a RATE limit ("rate limit
24
+ * reached … tokens per min") is a different failure with a different fix and
25
+ * must never land here — which is why no pattern matches on "too many tokens".
26
+ *
27
+ * @see ../../core/runCheckpoint.ts where this class makes `resumeOnError`
28
+ * stop advertising a resume that cannot work
29
+ */
30
+ /** Discriminator carried on the error — stable across releases. */
31
+ export const ERR_CONTEXT_WINDOW_EXCEEDED = 'ERR_CONTEXT_WINDOW_EXCEEDED';
32
+ /**
33
+ * Thrown by an LLM adapter when the provider refused the request because the
34
+ * prompt did not fit the model's context window.
35
+ *
36
+ * A refusal, not a failure of the model: nothing was generated and nothing was
37
+ * charged for output. Retrying the identical request is deterministic — it
38
+ * fails the same way — so this is one of the classes
39
+ * {@link canResume} reports as not resumable.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { ContextWindowExceededError } from 'agentfootprint';
44
+ *
45
+ * try {
46
+ * await agent.run({ message: 'summarise the fabric inventory' });
47
+ * } catch (err) {
48
+ * if (err instanceof ContextWindowExceededError) {
49
+ * console.error(`sent ${err.actualTokens} against ${err.limitTokens}`);
50
+ * // …cap the tool result, or add .window(slidingWindow({ keepRecentTurns: 2 }))
51
+ * }
52
+ * }
53
+ * ```
54
+ */
55
+ export class ContextWindowExceededError extends Error {
56
+ code = ERR_CONTEXT_WINDOW_EXCEEDED;
57
+ /** Which adapter refused — `'openai'`, `'anthropic'`, `'bedrock'`, … */
58
+ provider;
59
+ /** The model's (or gateway's) ceiling in tokens, when the vendor said it. */
60
+ limitTokens;
61
+ /** What the request actually came to in tokens, when the vendor said it. */
62
+ actualTokens;
63
+ /** HTTP status, when there was one. Kept so retry policies still see a 4xx. */
64
+ status;
65
+ /** The provider's own error, unchanged. */
66
+ cause;
67
+ constructor(args) {
68
+ super(buildMessage(args));
69
+ this.name = 'ContextWindowExceededError';
70
+ this.provider = args.provider;
71
+ if (args.limitTokens !== undefined)
72
+ this.limitTokens = args.limitTokens;
73
+ if (args.actualTokens !== undefined)
74
+ this.actualTokens = args.actualTokens;
75
+ if (args.status !== undefined)
76
+ this.status = args.status;
77
+ if (args.cause !== undefined)
78
+ this.cause = args.cause;
79
+ }
80
+ }
81
+ /** `true` for the typed error, including across a `structuredClone`-free
82
+ * boundary where `instanceof` still holds. Kept as a function so callers do
83
+ * not have to import the class to ask the question. */
84
+ export function isContextWindowExceeded(err) {
85
+ return (err instanceof ContextWindowExceededError ||
86
+ (err instanceof Error && err.code === ERR_CONTEXT_WINDOW_EXCEEDED));
87
+ }
88
+ // ─── Detection ───────────────────────────────────────────────────────
89
+ /**
90
+ * The phrases that mean "your request was too big for the context window",
91
+ * as the four shipped wire vendors actually word them. Every one of these is
92
+ * unambiguous on its own — which is the bar for being on this list, because a
93
+ * pattern that also matches a rate limit or a `max_tokens` validation error
94
+ * would send callers to the wrong fix.
95
+ *
96
+ * openai This model's maximum context length is 8192 tokens. However,
97
+ * your messages resulted in 9000 tokens.
98
+ * openai (code) context_length_exceeded
99
+ * gateways Input tokens exceed the configured limit of 272000 tokens.
100
+ * Your messages resulted in 879073 tokens.
101
+ * anthropic prompt is too long: 250000 tokens > 200000 maximum
102
+ * anthropic input length and `max_tokens` exceed context limit:
103
+ * 195000 + 8192 > 200000
104
+ * bedrock Input is too long for requested model.
105
+ * bedrock (ValidationException carrying either anthropic phrase)
106
+ */
107
+ const CONTEXT_PHRASES = [
108
+ /context_length_exceeded/i,
109
+ /maximum context length/i,
110
+ /prompt is too long/i,
111
+ /exceed(?:s)? context limit/i,
112
+ /input (?:is )?too long for (?:the )?requested model/i,
113
+ /input tokens exceed the configured limit/i,
114
+ // "input tokens" spelled out on purpose. A gateway saying "exceeds the
115
+ // maximum allowed number of tokens" with no side named could equally be an
116
+ // OUTPUT `max_tokens` cap, which has a different fix — and a pattern that
117
+ // cannot tell those apart sends callers to the wrong one.
118
+ /exceeds? the maximum (?:allowed )?(?:number of )?input tokens/i,
119
+ ];
120
+ /** `"272,000"` → `272000`; anything unparseable → undefined. */
121
+ function num(raw) {
122
+ if (raw === undefined)
123
+ return undefined;
124
+ const value = Number(raw.replace(/[,_\s]/g, ''));
125
+ return Number.isFinite(value) && value > 0 ? value : undefined;
126
+ }
127
+ /** Pull `{ limit, actual }` out of the vendor's sentence when it stated them. */
128
+ function readNumbers(text) {
129
+ // anthropic: "prompt is too long: 250000 tokens > 200000 maximum"
130
+ const tooLong = /prompt is too long:\s*([\d,_]+)\s*tokens?\s*>\s*([\d,_]+)/i.exec(text);
131
+ if (tooLong) {
132
+ return { actualTokens: num(tooLong[1]), limitTokens: num(tooLong[2]) };
133
+ }
134
+ // anthropic / bedrock: "input length and `max_tokens` exceed context limit:
135
+ // 195000 + 8192 > 200000"
136
+ const sum = /exceed(?:s)? context limit:\s*([\d,_]+)\s*\+\s*([\d,_]+)\s*>\s*([\d,_]+)/i.exec(text);
137
+ if (sum) {
138
+ const input = num(sum[1]);
139
+ const output = num(sum[2]);
140
+ return {
141
+ ...(input !== undefined && output !== undefined && { actualTokens: input + output }),
142
+ ...(num(sum[3]) !== undefined && { limitTokens: num(sum[3]) }),
143
+ };
144
+ }
145
+ const limit = num(/maximum context length is\s*([\d,_]+)/i.exec(text)?.[1]) ??
146
+ num(/configured limit of\s*([\d,_]+)/i.exec(text)?.[1]) ??
147
+ num(/limit of\s*([\d,_]+)\s*tokens/i.exec(text)?.[1]);
148
+ const actual = num(/resulted in\s*([\d,_]+)\s*tokens/i.exec(text)?.[1]) ??
149
+ num(/requested\s*([\d,_]+)\s*tokens/i.exec(text)?.[1]) ??
150
+ num(/you (?:sent|submitted|provided)\s*([\d,_]+)\s*tokens/i.exec(text)?.[1]);
151
+ return {
152
+ ...(limit !== undefined && { limitTokens: limit }),
153
+ ...(actual !== undefined && { actualTokens: actual }),
154
+ };
155
+ }
156
+ /** Every place a vendor hides its sentence, flattened into one string. */
157
+ function textOf(err, extraText) {
158
+ const parts = [];
159
+ if (typeof extraText === 'string')
160
+ parts.push(extraText);
161
+ const seen = new Set();
162
+ let node = err;
163
+ for (let depth = 0; depth < 4 && node !== undefined && node !== null; depth++) {
164
+ if (seen.has(node))
165
+ break;
166
+ seen.add(node);
167
+ const record = node;
168
+ for (const value of [record.message, record.code, record.type, record.name]) {
169
+ if (typeof value === 'string')
170
+ parts.push(value);
171
+ }
172
+ node = record.error ?? record.cause;
173
+ }
174
+ return parts.join(' • ');
175
+ }
176
+ /** HTTP status from wherever the SDK put it. */
177
+ function statusOf(err) {
178
+ const record = err;
179
+ for (const value of [
180
+ record?.status,
181
+ record?.statusCode,
182
+ record?.$metadata?.httpStatusCode,
183
+ record?.response?.status,
184
+ ]) {
185
+ if (typeof value === 'number' && Number.isFinite(value))
186
+ return value;
187
+ }
188
+ return undefined;
189
+ }
190
+ /**
191
+ * Does this error SAY it is a context overflow, whoever produced it?
192
+ *
193
+ * The same conservative phrase test the adapters translate on, asked without
194
+ * building anything — for code that has to classify an error it did not
195
+ * catch at an adapter boundary. A custom `LLMProvider`, a gateway wrapper or
196
+ * a proxy in front of a vendor produces the vendor's sentence without ever
197
+ * passing through `wrapError`, and a caller reading that error deserves the
198
+ * same answer an adapter would have given.
199
+ *
200
+ * @see ../../core/runCheckpoint.ts — the caller this exists for
201
+ */
202
+ export function looksLikeContextWindowExceeded(err, bodyText) {
203
+ if (isContextWindowExceeded(err))
204
+ return true;
205
+ const text = textOf(err, bodyText);
206
+ return text.length > 0 && CONTEXT_PHRASES.some((pattern) => pattern.test(text));
207
+ }
208
+ /**
209
+ * Translate a provider error into {@link ContextWindowExceededError} — or
210
+ * return `undefined`, which means "not this failure, leave it alone".
211
+ *
212
+ * Already-translated errors are returned as they are, so an adapter that
213
+ * wraps another adapter cannot double-wrap.
214
+ */
215
+ export function asContextWindowExceeded(err, options) {
216
+ if (isContextWindowExceeded(err))
217
+ return err;
218
+ if (!looksLikeContextWindowExceeded(err, options.bodyText))
219
+ return undefined;
220
+ const text = textOf(err, options.bodyText);
221
+ const numbers = readNumbers(text);
222
+ // The status the SDK attached, else the one the adapter read off the
223
+ // response, else a leading `400` the vendor put in the message itself
224
+ // (several gateways do). Kept because retry policies classify on it, and a
225
+ // context overflow must never look retryable.
226
+ const status = statusOf(err) ?? options.status ?? num(/^\s*(4\d\d)\b/.exec(messageOf(err))?.[1]);
227
+ return new ContextWindowExceededError({
228
+ provider: options.provider,
229
+ providerMessage: messageOf(err) || options.bodyText || text,
230
+ ...numbers,
231
+ ...(status !== undefined && { status }),
232
+ ...(err instanceof Error && { cause: err }),
233
+ });
234
+ }
235
+ function messageOf(err) {
236
+ if (err instanceof Error)
237
+ return err.message;
238
+ if (typeof err === 'string')
239
+ return err;
240
+ return '';
241
+ }
242
+ // ─── The message ─────────────────────────────────────────────────────
243
+ function tokens(value) {
244
+ return value === undefined ? undefined : `${value.toLocaleString('en-US')} tokens`;
245
+ }
246
+ /**
247
+ * The teaching half. Three fixes, cheapest first, and the one thing about
248
+ * `.compaction()` that is not obvious until it fails: folding a span means
249
+ * SENDING that span to the summarizer, so compaction cannot rescue a single
250
+ * round that is already bigger than the window.
251
+ */
252
+ function buildMessage(args) {
253
+ const sent = tokens(args.actualTokens);
254
+ const limit = tokens(args.limitTokens);
255
+ const size = sent && limit
256
+ ? ` — ${sent} sent against a limit of ${limit}`
257
+ : limit
258
+ ? ` — the limit is ${limit}`
259
+ : sent
260
+ ? ` — the request came to ${sent}`
261
+ : '';
262
+ return (`[${args.provider}] the request did not fit the context window${size}. ` +
263
+ `The provider refused it before the model saw it, so nothing was generated ` +
264
+ `and re-sending the same conversation fails the same way.\n` +
265
+ ` Fix 1 — cap oversized TOOL RESULTS where they are produced. One raw ` +
266
+ `database dump returned to the model is the usual whole overrun: return a ` +
267
+ `summary plus an id the model can ask about, not every row.\n` +
268
+ ` Fix 2 — .window(slidingWindow({ keepRecentTurns: 2 })) drops older ` +
269
+ `rounds before the request is assembled, so the conversation stops growing ` +
270
+ `without bound.\n` +
271
+ ` Fix 3 — .compaction() keeps a LONG conversation small, but it cannot ` +
272
+ `fold a span that is already bigger than the window: the summarizer call ` +
273
+ `sends that span, so it fails exactly as this call did. Fix the oversized ` +
274
+ `round first, then compact.\n` +
275
+ ` Provider said: ${args.providerMessage}`);
276
+ }
277
+ //# sourceMappingURL=contextWindow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextWindow.js","sourceRoot":"","sources":["../../../../src/adapters/llm/contextWindow.ts"],"names":[],"mappings":"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"}
@@ -584,3 +584,90 @@ export interface PricingTable {
584
584
  /** USD per ONE token for the given model+kind. */
585
585
  pricePerToken(model: string, kind: TokenKind): number;
586
586
  }
587
+ /**
588
+ * A service that runs code in an isolated session — a managed code
589
+ * interpreter, a container pool, a subprocess.
590
+ *
591
+ * **The shape is Start → Execute ×N → Stop**, because that is what every real
592
+ * one is, and the middle is the part a framework has to make possible. Paying
593
+ * session start-up on every call is the honest cheap version; holding the
594
+ * session in a module-level map is the fast version that hands one live sandbox
595
+ * to whoever calls next. `ctx.onTeardown` + {@link ToolExecutionContext.runId}
596
+ * are what let a tool do neither.
597
+ *
598
+ * ── Why this port exists at all: "summarize prose, compute data" ────────────
599
+ * A tool that returns 40MB of rows does not need a bigger context window; it
600
+ * needs to not put the rows in one. The motivating failure is a real production
601
+ * request of 879,073 tokens — a tool result pasted straight into the prompt.
602
+ * With a code runner, the model writes the aggregation, the RUNNER holds the
603
+ * data, and what comes back is the answer. Prose gets summarized; data gets
604
+ * computed. `CodeResult.truncated` exists so the second half of that promise
605
+ * cannot quietly break.
606
+ *
607
+ * Implement it for your own backend; ship it to `codeRunnerTool({ runner })`.
608
+ */
609
+ export interface CodeRunner {
610
+ /** Stable id — reported on every `agentfootprint.tools.session_*` event so a
611
+ * row names its backend, not just its tool. */
612
+ readonly id: string;
613
+ /**
614
+ * Open a session.
615
+ *
616
+ * `key` is the ISOLATION key the caller derived (see `toolSessionKey`). An
617
+ * adapter may use it to name the remote session; it must never widen it.
618
+ */
619
+ start(req: {
620
+ readonly key: string;
621
+ readonly language?: string;
622
+ readonly signal?: AbortSignal;
623
+ }): Promise<CodeSession>;
624
+ }
625
+ /** One live session. `stop()` is idempotent and tolerates "already gone". */
626
+ export interface CodeSession {
627
+ /** The backend's own id for this session, when it has one. */
628
+ readonly id: string;
629
+ execute(req: {
630
+ readonly code: string;
631
+ readonly language?: string;
632
+ readonly timeoutMs?: number;
633
+ readonly signal?: AbortSignal;
634
+ }): Promise<CodeResult>;
635
+ /**
636
+ * Release the session.
637
+ *
638
+ * Must tolerate a session the far side already reaped — an idle timeout is
639
+ * the reality on every managed backend, and a `Stop` on a dead session is a
640
+ * no-op, not an error.
641
+ */
642
+ stop(): Promise<void>;
643
+ }
644
+ /** What one execution produced. */
645
+ export interface CodeResult {
646
+ /** Did the code run to completion without an error exit? */
647
+ readonly ok: boolean;
648
+ readonly stdout: string;
649
+ readonly stderr: string;
650
+ readonly exitCode?: number;
651
+ /** Files the run produced, described rather than inlined — the whole point is
652
+ * that big data does not enter the window. */
653
+ readonly artifacts?: readonly {
654
+ readonly name: string;
655
+ readonly bytes: number;
656
+ readonly uri?: string;
657
+ }[];
658
+ /**
659
+ * Present IFF output was cut, and then it says by how much.
660
+ *
661
+ * Load-bearing, not politeness. A runner exists so big data is computed
662
+ * outside the context window instead of pasted into it; a runner that
663
+ * quietly slices its own output to fit is the same bug wearing a different
664
+ * hat, and the model would go on to reason over a truncated table it was
665
+ * never told was truncated. An unstated slice is a silent success.
666
+ */
667
+ readonly truncated?: {
668
+ readonly stdout?: boolean;
669
+ readonly stderr?: boolean;
670
+ /** The pre-truncation length, in characters, of whichever stream was cut. */
671
+ readonly ofChars?: number;
672
+ };
673
+ }
@@ -286,6 +286,9 @@ export declare class Agent extends RunnerBase<AgentInput, AgentOutput> {
286
286
  * default is derived from a runId, and storing that would pin a whole
287
287
  * conversation to the id of the one run that started it. */
288
288
  private lastRunIdentity?;
289
+ /** How long ONE tool teardown may take before the runner stops waiting.
290
+ * See `AgentOptions.toolTeardownTimeoutMs`. */
291
+ private readonly toolTeardownTimeoutMs;
289
292
  /** The run in flight, by id — the whole of the one-turn-at-a-time guard.
290
293
  * Set before the executor is built and cleared in `finally`, so a run that
291
294
  * throws does not leave the agent permanently refusing. */
@@ -611,6 +614,29 @@ export declare class Agent extends RunnerBase<AgentInput, AgentOutput> {
611
614
  */
612
615
  private installCheckpointTracker;
613
616
  resume(checkpoint: FlowchartCheckpoint, input?: unknown, options?: AgentRunOptions): Promise<AgentOutput | RunnerPauseOutcome>;
617
+ /**
618
+ * Fire `'run'`-scoped tool teardown — IF this run really ended.
619
+ *
620
+ * **Not on `finally`, and that is the whole point.** `finally` runs on every
621
+ * exit including a pause, and a pause exits TWO ways: a returned
622
+ * `RunnerPauseOutcome` and a thrown `PauseSignal`. A check-in on a
623
+ * code-interpreter call pauses the run so a person can approve the code —
624
+ * tearing the sandbox down there destroys the exact state the resume needs,
625
+ * and it fails QUIETLY, as a resumed run that "just re-ran everything".
626
+ * Both shapes are discriminated here and both are skipped.
627
+ *
628
+ * An error IS a terminal: the run is over, nobody is coming back, and a
629
+ * sandbox held by a run that crashed is the clearest kind of leak. Only a
630
+ * pause survives.
631
+ *
632
+ * Fired for the TURN, not for `currentRunContext.runId` — `resume()` mints a
633
+ * fresh run id, so a pause and its resume are one turn across two runs, and
634
+ * filtering on the id would leave everything a paused turn opened alive
635
+ * forever. See `ToolSessionTier.fireRun`.
636
+ *
637
+ * @param outcome what `run()`/`resume()` is about to return, or about to throw.
638
+ */
639
+ private endRunToolSessions;
614
640
  /**
615
641
  * The conversation this agent's LAST completed run leaves behind, packed as
616
642
  * the same `AgentRunCheckpoint` that `resumeOnError(...)` accepts. Store it,
@@ -761,6 +787,45 @@ export declare class Agent extends RunnerBase<AgentInput, AgentOutput> {
761
787
  * An open pick ACTIVATES but never moves the cursor — see the tool-calls gate.
762
788
  * Computed once per chart build; the injection list is fixed at construction.
763
789
  */
790
+ /**
791
+ * The identity facts this run hands `tool.execute` (9.7.0).
792
+ *
793
+ * Read through an ACCESSOR from the chart (see `ToolCallsHandlerDeps.currentRun`)
794
+ * because the chart is built once and this changes every run.
795
+ *
796
+ * `identity` is `lastRunIdentity` — what the CALLER passed — and deliberately
797
+ * NOT `scope.runIdentity`, which is always populated and defaults to
798
+ * `{ conversationId: '<runId>' }`. Handing a tool a synthesized conversation
799
+ * as "the identity" would let it key an isolated session on a fiction, and
800
+ * would make "absent" unrepresentable at exactly the layer that most needs to
801
+ * see it.
802
+ */
803
+ private toolRunFacts;
804
+ /**
805
+ * The teardown tier, built on FIRST registration.
806
+ *
807
+ * An agent whose tools never hold a session never allocates one, and its
808
+ * terminals stay a single `undefined` check.
809
+ */
810
+ private toolSessions;
811
+ /**
812
+ * Turn one TEARDOWN report into a typed `agentfootprint.tools.session_*` event.
813
+ *
814
+ * Only the two closing events come through here. A start and a reuse happen
815
+ * inside `tool.execute`, where the dispatch loop still holds the scope, so
816
+ * those ride the ordinary emit channel and carry the stage they really
817
+ * happened in. These two fire after the run's last stage committed, and this
818
+ * is the one place that has to answer "from where?" without a stage to point
819
+ * at.
820
+ *
821
+ * **Built with `buildEventMeta`, never `minimalMeta()`.** `minimalMeta()`
822
+ * hardcodes `runId: 'consumer-scope'`, and a teardown event stamped that way
823
+ * cannot be joined to the run that OPENED the session — the exact
824
+ * unjoinability 9.4.0 spent a release fixing for credential events. So the
825
+ * meta comes from `currentRunContext`, with a STATED pseudo-stage, the same
826
+ * move as the `'<stageId>#paused'` stamp at the pause boundary.
827
+ */
828
+ private emitToolSessionReport;
764
829
  private openSkillIds;
765
830
  /**
766
831
  * The per-iteration `read_skill` offer builder — or `undefined` to leave the tool