agentfootprint 9.6.0 → 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 (98) 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/core/Agent.js +132 -0
  9. package/dist/core/Agent.js.map +1 -1
  10. package/dist/core/RunnerBase.js +67 -0
  11. package/dist/core/RunnerBase.js.map +1 -1
  12. package/dist/core/agent/stages/toolCalls.js +90 -0
  13. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  14. package/dist/core/codeRunnerTool.js +252 -0
  15. package/dist/core/codeRunnerTool.js.map +1 -0
  16. package/dist/core/toolSessions.js +396 -0
  17. package/dist/core/toolSessions.js.map +1 -0
  18. package/dist/core/tools.js.map +1 -1
  19. package/dist/doors/providers.js +13 -0
  20. package/dist/doors/providers.js.map +1 -1
  21. package/dist/esm/adapters/code/agentcore.d.ts +133 -0
  22. package/dist/esm/adapters/code/agentcore.js +290 -0
  23. package/dist/esm/adapters/code/agentcore.js.map +1 -0
  24. package/dist/esm/adapters/code/local.d.ts +99 -0
  25. package/dist/esm/adapters/code/local.js +196 -0
  26. package/dist/esm/adapters/code/local.js.map +1 -0
  27. package/dist/esm/adapters/types.d.ts +87 -0
  28. package/dist/esm/core/Agent.d.ts +65 -0
  29. package/dist/esm/core/Agent.js +133 -1
  30. package/dist/esm/core/Agent.js.map +1 -1
  31. package/dist/esm/core/RunnerBase.d.ts +51 -0
  32. package/dist/esm/core/RunnerBase.js +67 -0
  33. package/dist/esm/core/RunnerBase.js.map +1 -1
  34. package/dist/esm/core/agent/stages/toolCalls.d.ts +31 -0
  35. package/dist/esm/core/agent/stages/toolCalls.js +90 -0
  36. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  37. package/dist/esm/core/agent/types.d.ts +23 -2
  38. package/dist/esm/core/codeRunnerTool.d.ts +120 -0
  39. package/dist/esm/core/codeRunnerTool.js +247 -0
  40. package/dist/esm/core/codeRunnerTool.js.map +1 -0
  41. package/dist/esm/core/toolSessions.d.ts +318 -0
  42. package/dist/esm/core/toolSessions.js +389 -0
  43. package/dist/esm/core/toolSessions.js.map +1 -0
  44. package/dist/esm/core/tools.d.ts +60 -0
  45. package/dist/esm/core/tools.js.map +1 -1
  46. package/dist/esm/doors/providers.d.ts +7 -0
  47. package/dist/esm/doors/providers.js +10 -0
  48. package/dist/esm/doors/providers.js.map +1 -1
  49. package/dist/esm/events/payloads.d.ts +50 -0
  50. package/dist/esm/events/registry.d.ts +9 -1
  51. package/dist/esm/events/registry.js +8 -0
  52. package/dist/esm/events/registry.js.map +1 -1
  53. package/dist/esm/index.d.ts +2 -0
  54. package/dist/esm/index.js +5 -0
  55. package/dist/esm/index.js.map +1 -1
  56. package/dist/esm/lib/mcp/mcpServe.js +37 -1
  57. package/dist/esm/lib/mcp/mcpServe.js.map +1 -1
  58. package/dist/esm/lib/trace-toolpack/traceToolpack.js +15 -1
  59. package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
  60. package/dist/events/registry.js +8 -0
  61. package/dist/events/registry.js.map +1 -1
  62. package/dist/index.js +14 -1
  63. package/dist/index.js.map +1 -1
  64. package/dist/lib/mcp/mcpServe.js +37 -1
  65. package/dist/lib/mcp/mcpServe.js.map +1 -1
  66. package/dist/lib/trace-toolpack/traceToolpack.js +15 -1
  67. package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
  68. package/dist/types/adapters/code/agentcore.d.ts +134 -0
  69. package/dist/types/adapters/code/agentcore.d.ts.map +1 -0
  70. package/dist/types/adapters/code/local.d.ts +100 -0
  71. package/dist/types/adapters/code/local.d.ts.map +1 -0
  72. package/dist/types/adapters/types.d.ts +87 -0
  73. package/dist/types/adapters/types.d.ts.map +1 -1
  74. package/dist/types/core/Agent.d.ts +65 -0
  75. package/dist/types/core/Agent.d.ts.map +1 -1
  76. package/dist/types/core/RunnerBase.d.ts +51 -0
  77. package/dist/types/core/RunnerBase.d.ts.map +1 -1
  78. package/dist/types/core/agent/stages/toolCalls.d.ts +31 -0
  79. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  80. package/dist/types/core/agent/types.d.ts +23 -2
  81. package/dist/types/core/agent/types.d.ts.map +1 -1
  82. package/dist/types/core/codeRunnerTool.d.ts +121 -0
  83. package/dist/types/core/codeRunnerTool.d.ts.map +1 -0
  84. package/dist/types/core/toolSessions.d.ts +319 -0
  85. package/dist/types/core/toolSessions.d.ts.map +1 -0
  86. package/dist/types/core/tools.d.ts +60 -0
  87. package/dist/types/core/tools.d.ts.map +1 -1
  88. package/dist/types/doors/providers.d.ts +7 -0
  89. package/dist/types/doors/providers.d.ts.map +1 -1
  90. package/dist/types/events/payloads.d.ts +50 -0
  91. package/dist/types/events/payloads.d.ts.map +1 -1
  92. package/dist/types/events/registry.d.ts +9 -1
  93. package/dist/types/events/registry.d.ts.map +1 -1
  94. package/dist/types/index.d.ts +2 -0
  95. package/dist/types/index.d.ts.map +1 -1
  96. package/dist/types/lib/mcp/mcpServe.d.ts.map +1 -1
  97. package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
  98. package/package.json +1 -1
@@ -0,0 +1,120 @@
1
+ /**
2
+ * codeRunnerTool — the tool that turns a {@link CodeRunner} into something an
3
+ * LLM can call, holding ONE session per isolation key.
4
+ *
5
+ * Pattern: Factory over `defineTool` + the 9.7.0 tool-session contract.
6
+ * Role: the first consumer of `ctx.onTeardown` — and the proof it works.
7
+ * Emits: nothing directly; the teardown tier reports
8
+ * `agentfootprint.tools.session_started` / `_reused` / `_closed` /
9
+ * `_close_failed`.
10
+ *
11
+ * ── The doctrine: summarize prose, compute data ─────────────────────────────
12
+ * A tool that hands the model 40,000 rows has not given it data; it has spent
13
+ * the window. The motivating failure is real and measured: a production request
14
+ * of 879,073 tokens, almost all of it one tool result pasted into the prompt.
15
+ * (Since 9.6.0 that shape at least fails by NAME —
16
+ * `ContextWindowExceededError` — instead of as a vendor 400. This is the other
17
+ * half: not failing better, but not needing to.)
18
+ *
19
+ * With a code runner the model writes an aggregation, the RUNNER holds the
20
+ * rows, and what comes back is the number. Prose gets summarized; data gets
21
+ * computed.
22
+ *
23
+ * ── Why the session must be keyed, and keyed WIDELY ENOUGH ──────────────────
24
+ * The session is the whole value: a code interpreter costs seconds to start and
25
+ * milliseconds to invoke. But a session holds a filesystem, an environment and
26
+ * half-run state, so the thing it is keyed on IS the isolation boundary. A
27
+ * standing agent serving many people from one process, holding one session in a
28
+ * module map, gives person B person A's files.
29
+ *
30
+ * So the key comes from `toolSessionKey(ctx, scope)` — one exported
31
+ * implementation, composing tenant + principal + (session | run). Never a bare
32
+ * `sessionId`: that is caller data, and anyone who can reach the host can put
33
+ * someone else's there.
34
+ *
35
+ * ── Degradation is REFUSED, never silent ────────────────────────────────────
36
+ * Ask for `scope: 'session'` at a door with no session and this throws, naming
37
+ * the door. It does not quietly fall back. Falling back to a WIDER key is the
38
+ * cross-binding bug itself; falling back to a NARROWER one is a silent 30×
39
+ * latency change nobody sees until the bill.
40
+ *
41
+ * @example a session per run, on a local dev machine
42
+ * const agent = Agent.create({ provider })
43
+ * .tool(codeRunnerTool({ runner: localCodeRunner() }))
44
+ * .build();
45
+ *
46
+ * @example a session per hosted conversation, on a real sandbox
47
+ * const runner = agentCoreCodeRunner({ region, identifier: 'aws.codeinterpreter.v1' });
48
+ * const agent = Agent.create({ provider })
49
+ * .tool(codeRunnerTool({ runner, scope: 'session', language: 'python' }))
50
+ * .build();
51
+ * // the composition root says when a session is over:
52
+ * conversation.onClose(() => void agent.closeToolSessions({ sessionId }));
53
+ */
54
+ import type { CodeRunner, CodeSession } from '../adapters/types.js';
55
+ import type { CredentialNeed } from '../identity/types.js';
56
+ import type { CheckInDemand } from './checkin.js';
57
+ import { type Tool } from './tools.js';
58
+ import { type TeardownScope } from './toolSessions.js';
59
+ /** The scopes a code session can be held under. `'shutdown'` is not one: it is
60
+ * when everything goes, not a thing to key a session on. */
61
+ export type CodeRunnerToolScope = Extract<TeardownScope, 'call' | 'run' | 'session'>;
62
+ export interface CodeRunnerToolOptions {
63
+ /** The backend. `localCodeRunner()` for a dev loop, `agentCoreCodeRunner(...)`
64
+ * for a real sandbox — the tool is identical across the swap. */
65
+ readonly runner: CodeRunner;
66
+ /** Tool name the model sees. Default `'run_code'`. */
67
+ readonly name?: string;
68
+ /** Description the model sees. A sensible one is composed from `scope` +
69
+ * `language` when you do not pass one. */
70
+ readonly description?: string;
71
+ /**
72
+ * How long one session lives. Default `'run'` — a turn's worth of work shares
73
+ * one interpreter, and nothing outlives the turn.
74
+ *
75
+ * `'session'` keeps the interpreter across the turns of one hosted
76
+ * conversation (variables persist, files persist) and REQUIRES a
77
+ * session-bound run plus a composition root that calls
78
+ * `agent.closeToolSessions({ sessionId })`.
79
+ *
80
+ * `'call'` starts and stops per invocation — the safest and the slowest.
81
+ */
82
+ readonly scope?: CodeRunnerToolScope;
83
+ /** Default language for the code the model writes. Default `'python'`. */
84
+ readonly language?: string;
85
+ /** Per-stream ceiling for what reaches the model, in characters. Default 4000.
86
+ * Anything cut is STATED in the result, never dropped quietly. */
87
+ readonly maxOutputChars?: number;
88
+ /** Per-execution ceiling handed to the runner. */
89
+ readonly timeoutMs?: number;
90
+ /** Demand a human check-in before code runs — `'always'`, or a predicate over
91
+ * the code string. A pause here does NOT tear the session down. */
92
+ readonly checkIn?: CheckInDemand<{
93
+ code: string;
94
+ }>;
95
+ /** A credential this tool needs (declare-and-push). Resolved before execute.
96
+ * Do NOT cache it past the call: a session outliving a run outlives its token. */
97
+ readonly needs?: CredentialNeed;
98
+ }
99
+ /**
100
+ * The per-tool session map, riding the `Tool` under a REGISTRY symbol.
101
+ *
102
+ * `Symbol.for`, not a unique symbol: this package ships CJS and ESM, and a tool
103
+ * built through one entry point must be readable through the other. The same
104
+ * move `INNER_RUN_RECORDS` makes for `flowchartAsTool({ keepRecord })` — and
105
+ * deliberately a DIFFERENT symbol, so one tool can carry both (spreading a tool
106
+ * preserves symbol keys, which is why `{...tool, [SYM]: store}` composes).
107
+ *
108
+ * Invisible to the LLM, invisible to `Tool`'s shape, reachable by a test and by
109
+ * whatever inspector comes next.
110
+ */
111
+ export declare const TOOL_SESSIONS: unique symbol;
112
+ /** A `Tool` that holds live sessions, keyed by isolation key. */
113
+ export interface HoldsToolSessions {
114
+ readonly [TOOL_SESSIONS]: ReadonlyMap<string, CodeSession>;
115
+ }
116
+ /** Read the live-session map off a candidate, or `undefined` when it holds none. */
117
+ export declare function toolSessionsOf(candidate: unknown): ReadonlyMap<string, CodeSession> | undefined;
118
+ export declare function codeRunnerTool(options: CodeRunnerToolOptions): Tool<{
119
+ code: string;
120
+ }, string> & HoldsToolSessions;
@@ -0,0 +1,247 @@
1
+ /**
2
+ * codeRunnerTool — the tool that turns a {@link CodeRunner} into something an
3
+ * LLM can call, holding ONE session per isolation key.
4
+ *
5
+ * Pattern: Factory over `defineTool` + the 9.7.0 tool-session contract.
6
+ * Role: the first consumer of `ctx.onTeardown` — and the proof it works.
7
+ * Emits: nothing directly; the teardown tier reports
8
+ * `agentfootprint.tools.session_started` / `_reused` / `_closed` /
9
+ * `_close_failed`.
10
+ *
11
+ * ── The doctrine: summarize prose, compute data ─────────────────────────────
12
+ * A tool that hands the model 40,000 rows has not given it data; it has spent
13
+ * the window. The motivating failure is real and measured: a production request
14
+ * of 879,073 tokens, almost all of it one tool result pasted into the prompt.
15
+ * (Since 9.6.0 that shape at least fails by NAME —
16
+ * `ContextWindowExceededError` — instead of as a vendor 400. This is the other
17
+ * half: not failing better, but not needing to.)
18
+ *
19
+ * With a code runner the model writes an aggregation, the RUNNER holds the
20
+ * rows, and what comes back is the number. Prose gets summarized; data gets
21
+ * computed.
22
+ *
23
+ * ── Why the session must be keyed, and keyed WIDELY ENOUGH ──────────────────
24
+ * The session is the whole value: a code interpreter costs seconds to start and
25
+ * milliseconds to invoke. But a session holds a filesystem, an environment and
26
+ * half-run state, so the thing it is keyed on IS the isolation boundary. A
27
+ * standing agent serving many people from one process, holding one session in a
28
+ * module map, gives person B person A's files.
29
+ *
30
+ * So the key comes from `toolSessionKey(ctx, scope)` — one exported
31
+ * implementation, composing tenant + principal + (session | run). Never a bare
32
+ * `sessionId`: that is caller data, and anyone who can reach the host can put
33
+ * someone else's there.
34
+ *
35
+ * ── Degradation is REFUSED, never silent ────────────────────────────────────
36
+ * Ask for `scope: 'session'` at a door with no session and this throws, naming
37
+ * the door. It does not quietly fall back. Falling back to a WIDER key is the
38
+ * cross-binding bug itself; falling back to a NARROWER one is a silent 30×
39
+ * latency change nobody sees until the bill.
40
+ *
41
+ * @example a session per run, on a local dev machine
42
+ * const agent = Agent.create({ provider })
43
+ * .tool(codeRunnerTool({ runner: localCodeRunner() }))
44
+ * .build();
45
+ *
46
+ * @example a session per hosted conversation, on a real sandbox
47
+ * const runner = agentCoreCodeRunner({ region, identifier: 'aws.codeinterpreter.v1' });
48
+ * const agent = Agent.create({ provider })
49
+ * .tool(codeRunnerTool({ runner, scope: 'session', language: 'python' }))
50
+ * .build();
51
+ * // the composition root says when a session is over:
52
+ * conversation.onClose(() => void agent.closeToolSessions({ sessionId }));
53
+ */
54
+ import { defineTool } from './tools.js';
55
+ import { toolSessionKey } from './toolSessions.js';
56
+ const DEFAULT_MAX_OUTPUT_CHARS = 4_000;
57
+ /**
58
+ * The per-tool session map, riding the `Tool` under a REGISTRY symbol.
59
+ *
60
+ * `Symbol.for`, not a unique symbol: this package ships CJS and ESM, and a tool
61
+ * built through one entry point must be readable through the other. The same
62
+ * move `INNER_RUN_RECORDS` makes for `flowchartAsTool({ keepRecord })` — and
63
+ * deliberately a DIFFERENT symbol, so one tool can carry both (spreading a tool
64
+ * preserves symbol keys, which is why `{...tool, [SYM]: store}` composes).
65
+ *
66
+ * Invisible to the LLM, invisible to `Tool`'s shape, reachable by a test and by
67
+ * whatever inspector comes next.
68
+ */
69
+ export const TOOL_SESSIONS = Symbol.for('agentfootprint.tools.sessions');
70
+ /** Read the live-session map off a candidate, or `undefined` when it holds none. */
71
+ export function toolSessionsOf(candidate) {
72
+ if (candidate === null || typeof candidate !== 'object')
73
+ return undefined;
74
+ const held = candidate[TOOL_SESSIONS];
75
+ return held instanceof Map ? held : undefined;
76
+ }
77
+ export function codeRunnerTool(options) {
78
+ const name = options.name ?? 'run_code';
79
+ const scope = options.scope ?? 'run';
80
+ const language = options.language ?? 'python';
81
+ const maxOutputChars = options.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
82
+ /**
83
+ * Live sessions by isolation key.
84
+ *
85
+ * A `Map` and not a closure variable, because two keys are two sandboxes: one
86
+ * variable would be exactly the cross-binding this whole feature exists to
87
+ * prevent. Entries are removed by the teardown the tool registers alongside
88
+ * them, so the map cannot outlive what it points at.
89
+ */
90
+ const sessions = new Map();
91
+ /** In-flight starts, so two parallel tool calls under one key open ONE session. */
92
+ const starting = new Map();
93
+ const tool = defineTool({
94
+ name,
95
+ description: options.description ?? describe(scope, language),
96
+ inputSchema: {
97
+ type: 'object',
98
+ properties: {
99
+ code: {
100
+ type: 'string',
101
+ description: `${language} source to execute. Print what you want back — stdout is the result. ` +
102
+ 'Compute over big data here rather than asking for it to be pasted back to you.',
103
+ },
104
+ },
105
+ required: ['code'],
106
+ },
107
+ ...(options.checkIn !== undefined && { checkIn: options.checkIn }),
108
+ ...(options.needs && { needs: options.needs }),
109
+ execute: async (args, ctx) => {
110
+ const key = requireKey(ctx, scope, name);
111
+ const session = await acquire(key, ctx);
112
+ const result = await session.execute({
113
+ code: args.code,
114
+ language,
115
+ ...(options.timeoutMs !== undefined && { timeoutMs: options.timeoutMs }),
116
+ ...(ctx.signal && { signal: ctx.signal }),
117
+ });
118
+ return render(result, maxOutputChars);
119
+ },
120
+ });
121
+ /** Get the session for `key`, opening one if this is the first call. */
122
+ const acquire = async (key, ctx) => {
123
+ const live = sessions.get(key);
124
+ if (live) {
125
+ // A reuse re-registers under the SAME (tool, scope, key). The tier keeps
126
+ // the FIRST cleanup — it is the one holding this handle — and treats the
127
+ // repeat as a touch, which is how the idle sweep and the LRU bound learn
128
+ // this session is still in use. Registering only once would make a busy
129
+ // session look as cold as an abandoned one.
130
+ register(key, live, ctx);
131
+ return live;
132
+ }
133
+ // Two parallel tool calls in one iteration must not open two sandboxes.
134
+ const inFlight = starting.get(key);
135
+ if (inFlight)
136
+ return inFlight;
137
+ const opening = options.runner
138
+ .start({ key, language, ...(ctx.signal && { signal: ctx.signal }) })
139
+ .then((opened) => {
140
+ sessions.set(key, opened);
141
+ register(key, opened, ctx);
142
+ return opened;
143
+ })
144
+ .finally(() => {
145
+ starting.delete(key);
146
+ });
147
+ starting.set(key, opening);
148
+ return opening;
149
+ };
150
+ const register = (key, session, ctx) => {
151
+ ctx.onTeardown?.(async () => {
152
+ // Drop the entry FIRST: if `stop()` throws, the map must not keep
153
+ // handing out a session the runtime has already given up on.
154
+ sessions.delete(key);
155
+ await session.stop();
156
+ }, { scope, key, runnerId: options.runner.id, label: language });
157
+ };
158
+ // The live map rides the Tool under a registry symbol — see TOOL_SESSIONS.
159
+ return { ...tool, [TOOL_SESSIONS]: sessions };
160
+ }
161
+ /**
162
+ * Derive the isolation key, or REFUSE by name.
163
+ *
164
+ * The three refusals are the security surface of this tool, so each says what
165
+ * is missing, why it matters, and the one-line fix. None of them degrades
166
+ * quietly: a fallback to a wider key is the cross-binding bug, and a fallback
167
+ * to a narrower one silently multiplies latency and cost.
168
+ */
169
+ function requireKey(ctx, scope, name) {
170
+ const supported = ctx.teardownScopes;
171
+ if (supported !== undefined && !supported.includes(scope)) {
172
+ throw new Error(`${name}: this tool holds a '${scope}'-scoped code session, and the door it is running ` +
173
+ `behind honours ${supported.length === 0 ? 'no teardown scopes' : supported.join(', ')}. ` +
174
+ 'A session nothing will ever close is a sandbox left running. Either run this tool ' +
175
+ `inside an Agent, or build it with scope: '${supported[0] ?? 'call'}'.`);
176
+ }
177
+ const key = toolSessionKey(ctx, scope);
178
+ if (key)
179
+ return key;
180
+ if (scope === 'session') {
181
+ throw new Error(`${name}: scope 'session' needs a hosting session, and this run has no sessionId. ` +
182
+ 'Pass one — `agent.run({ message, sessionId })`, which `standingAgent` does from ' +
183
+ "`HostRequest.sessionId` — or build this tool with scope: 'run'. It is not silently " +
184
+ 'narrowed, because a session-scoped interpreter and a run-scoped one differ by about ' +
185
+ '30x in start-up cost, and by everything in what persists between turns.');
186
+ }
187
+ throw new Error(`${name}: scope 'run' needs a run, and this call has no runId — it is being served ` +
188
+ 'outside an Agent (over `mcpServe`, or from a script). A served call is one call, not a ' +
189
+ "turn, so nothing would ever end the session. Build this tool with scope: 'call' for " +
190
+ 'that door.');
191
+ }
192
+ /** The description the model reads when the caller did not write one. */
193
+ function describe(scope, language) {
194
+ const persistence = scope === 'call'
195
+ ? 'Each call runs in a fresh environment — nothing carries over, so include everything you need.'
196
+ : scope === 'run'
197
+ ? 'State persists across calls within this turn: variables and files you create stay available.'
198
+ : 'State persists across the whole conversation: variables and files you create stay available in later turns.';
199
+ return (`Execute ${language} code and return its output. ${persistence} ` +
200
+ 'Use this to COMPUTE over data rather than asking for the data itself — fetch, filter, ' +
201
+ 'aggregate and print the answer, so a large result never has to travel through this ' +
202
+ 'conversation. Print what you want to see; stdout is what you get back.');
203
+ }
204
+ /**
205
+ * Render a `CodeResult` for the model.
206
+ *
207
+ * TRUNCATION IS ALWAYS STATED. A slice the model cannot see is a silent
208
+ * success: it goes on to reason over a fragment of a table believing it has the
209
+ * table. The marker is deliberately plain text in the result body, not
210
+ * metadata, because the result body is the only channel the model reads.
211
+ */
212
+ function render(result, maxOutputChars) {
213
+ const parts = [];
214
+ const stdout = clip(result.stdout, maxOutputChars, result.truncated?.stdout === true);
215
+ const stderr = clip(result.stderr, maxOutputChars, result.truncated?.stderr === true);
216
+ if (stdout.text.length > 0)
217
+ parts.push(stdout.text);
218
+ if (stderr.text.length > 0)
219
+ parts.push(`stderr:\n${stderr.text}`);
220
+ if (!result.ok) {
221
+ parts.push(`[exit ${result.exitCode ?? 'non-zero'}] the code did not complete successfully — read ` +
222
+ 'stderr above and fix it.');
223
+ }
224
+ for (const artifact of result.artifacts ?? []) {
225
+ parts.push(`[artifact: ${artifact.name}, ${artifact.bytes} bytes${artifact.uri ? `, ${artifact.uri}` : ''}]`);
226
+ }
227
+ if (parts.length === 0) {
228
+ // "Nothing was printed" and "this returned nothing" are different facts and
229
+ // only one of them tells the model what to do next.
230
+ parts.push('(the code ran and printed nothing — print the value you want returned)');
231
+ }
232
+ return parts.join('\n');
233
+ }
234
+ /** Clip to the tool's ceiling, and SAY SO — including a cut the runner made. */
235
+ function clip(text, max, alreadyCutUpstream) {
236
+ const cutHere = text.length > max;
237
+ const shown = cutHere ? text.slice(0, max) : text;
238
+ if (!cutHere && !alreadyCutUpstream)
239
+ return { text: shown };
240
+ const of = cutHere ? text.length : undefined;
241
+ return {
242
+ text: `${shown}\n[truncated: showing ${shown.length}${of !== undefined ? ` of ${of}` : ''} ` +
243
+ 'characters. Re-run printing a summary, a slice, or an aggregate instead of the whole ' +
244
+ 'value.]',
245
+ };
246
+ }
247
+ //# sourceMappingURL=codeRunnerTool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codeRunnerTool.js","sourceRoot":"","sources":["../../../src/core/codeRunnerTool.ts"],"names":[],"mappings":"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"}