agentfootprint 11.4.0 → 12.0.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 (198) hide show
  1. package/CAPABILITIES.md +3 -1
  2. package/dist/adapters/code/agentcore.js +295 -214
  3. package/dist/adapters/code/agentcore.js.map +1 -1
  4. package/dist/adapters/code/agentcoreClient.js +178 -0
  5. package/dist/adapters/code/agentcoreClient.js.map +1 -0
  6. package/dist/adapters/code/agentcoreWire.js +348 -0
  7. package/dist/adapters/code/agentcoreWire.js.map +1 -0
  8. package/dist/adapters/code/fileName.js +100 -0
  9. package/dist/adapters/code/fileName.js.map +1 -0
  10. package/dist/adapters/code/local.js +529 -183
  11. package/dist/adapters/code/local.js.map +1 -1
  12. package/dist/adapters/code/output.js +140 -0
  13. package/dist/adapters/code/output.js.map +1 -0
  14. package/dist/adapters/mcp/agentcoreRuntime/invoke.js +7 -6
  15. package/dist/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
  16. package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js +36 -2
  17. package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
  18. package/dist/adapters/mcp/agentcoreRuntime/transport.js +3 -1
  19. package/dist/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
  20. package/dist/core/codeRunnerTool.js +52 -7
  21. package/dist/core/codeRunnerTool.js.map +1 -1
  22. package/dist/doors/providers.js.map +1 -1
  23. package/dist/esm/adapters/code/agentcore.d.ts +65 -68
  24. package/dist/esm/adapters/code/agentcore.js +293 -212
  25. package/dist/esm/adapters/code/agentcore.js.map +1 -1
  26. package/dist/esm/adapters/code/agentcoreClient.d.ts +193 -0
  27. package/dist/esm/adapters/code/agentcoreClient.js +173 -0
  28. package/dist/esm/adapters/code/agentcoreClient.js.map +1 -0
  29. package/dist/esm/adapters/code/agentcoreWire.d.ts +119 -0
  30. package/dist/esm/adapters/code/agentcoreWire.js +334 -0
  31. package/dist/esm/adapters/code/agentcoreWire.js.map +1 -0
  32. package/dist/esm/adapters/code/fileName.d.ts +43 -0
  33. package/dist/esm/adapters/code/fileName.js +96 -0
  34. package/dist/esm/adapters/code/fileName.js.map +1 -0
  35. package/dist/esm/adapters/code/local.d.ts +91 -12
  36. package/dist/esm/adapters/code/local.js +528 -182
  37. package/dist/esm/adapters/code/local.js.map +1 -1
  38. package/dist/esm/adapters/code/output.d.ts +70 -0
  39. package/dist/esm/adapters/code/output.js +129 -0
  40. package/dist/esm/adapters/code/output.js.map +1 -0
  41. package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.d.ts +7 -6
  42. package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js +7 -6
  43. package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
  44. package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +32 -1
  45. package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js +35 -1
  46. package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
  47. package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js +4 -2
  48. package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
  49. package/dist/esm/core/codeRunnerTool.d.ts +15 -3
  50. package/dist/esm/core/codeRunnerTool.js +52 -7
  51. package/dist/esm/core/codeRunnerTool.js.map +1 -1
  52. package/dist/esm/doors/providers.d.ts +1 -1
  53. package/dist/esm/doors/providers.js.map +1 -1
  54. package/dist/esm/events/payloads.d.ts +144 -29
  55. package/dist/esm/index.d.ts +2 -0
  56. package/dist/esm/index.js +5 -0
  57. package/dist/esm/index.js.map +1 -1
  58. package/dist/esm/mcp/callLifetime.d.ts +57 -14
  59. package/dist/esm/mcp/callLifetime.js +67 -19
  60. package/dist/esm/mcp/callLifetime.js.map +1 -1
  61. package/dist/esm/mcp/callMarker.d.ts +43 -0
  62. package/dist/esm/mcp/callMarker.js +69 -0
  63. package/dist/esm/mcp/callMarker.js.map +1 -0
  64. package/dist/esm/mcp/connectionRefusals.js +7 -0
  65. package/dist/esm/mcp/connectionRefusals.js.map +1 -1
  66. package/dist/esm/mcp/index.d.ts +2 -0
  67. package/dist/esm/mcp/index.js +3 -0
  68. package/dist/esm/mcp/index.js.map +1 -1
  69. package/dist/esm/mcp/mcpClient.js +93 -26
  70. package/dist/esm/mcp/mcpClient.js.map +1 -1
  71. package/dist/esm/mcp/replay.d.ts +291 -0
  72. package/dist/esm/mcp/replay.js +405 -0
  73. package/dist/esm/mcp/replay.js.map +1 -0
  74. package/dist/esm/mcp/replayUnavailable.d.ts +68 -0
  75. package/dist/esm/mcp/replayUnavailable.js +87 -0
  76. package/dist/esm/mcp/replayUnavailable.js.map +1 -0
  77. package/dist/esm/mcp/requestLedger.d.ts +115 -32
  78. package/dist/esm/mcp/requestLedger.js +193 -52
  79. package/dist/esm/mcp/requestLedger.js.map +1 -1
  80. package/dist/esm/mcp/seriesEvidence.d.ts +73 -15
  81. package/dist/esm/mcp/seriesEvidence.js +114 -21
  82. package/dist/esm/mcp/seriesEvidence.js.map +1 -1
  83. package/dist/esm/mcp/throttleRetry.d.ts +86 -42
  84. package/dist/esm/mcp/throttleRetry.js +158 -50
  85. package/dist/esm/mcp/throttleRetry.js.map +1 -1
  86. package/dist/esm/mcp/toolResult.d.ts +6 -0
  87. package/dist/esm/mcp/toolResult.js +15 -0
  88. package/dist/esm/mcp/toolResult.js.map +1 -1
  89. package/dist/esm/mcp/types.d.ts +73 -8
  90. package/dist/esm/provider-contract/codeSandbox.d.ts +89 -0
  91. package/dist/esm/provider-contract/codeSandbox.js +119 -0
  92. package/dist/esm/provider-contract/codeSandbox.js.map +1 -0
  93. package/dist/esm/provider-contract/sessionExpired.d.ts +31 -0
  94. package/dist/esm/provider-contract/sessionExpired.js +40 -0
  95. package/dist/esm/provider-contract/sessionExpired.js.map +1 -0
  96. package/dist/esm/provider-contract/types.d.ts +166 -17
  97. package/dist/esm/provider-contract/types.js +9 -0
  98. package/dist/esm/provider-contract/types.js.map +1 -1
  99. package/dist/esm/redaction/knownStrings.js +16 -0
  100. package/dist/esm/redaction/knownStrings.js.map +1 -1
  101. package/dist/esm/tool-providers/index.d.ts +2 -2
  102. package/dist/esm/tool-providers/index.js +4 -0
  103. package/dist/esm/tool-providers/index.js.map +1 -1
  104. package/dist/esm/tools/requestEvidence.js +60 -0
  105. package/dist/esm/tools/requestEvidence.js.map +1 -1
  106. package/dist/index.js +12 -4
  107. package/dist/index.js.map +1 -1
  108. package/dist/mcp/callLifetime.js +66 -18
  109. package/dist/mcp/callLifetime.js.map +1 -1
  110. package/dist/mcp/callMarker.js +74 -0
  111. package/dist/mcp/callMarker.js.map +1 -0
  112. package/dist/mcp/connectionRefusals.js +7 -0
  113. package/dist/mcp/connectionRefusals.js.map +1 -1
  114. package/dist/mcp/index.js +5 -1
  115. package/dist/mcp/index.js.map +1 -1
  116. package/dist/mcp/mcpClient.js +91 -24
  117. package/dist/mcp/mcpClient.js.map +1 -1
  118. package/dist/mcp/replay.js +415 -0
  119. package/dist/mcp/replay.js.map +1 -0
  120. package/dist/mcp/replayUnavailable.js +92 -0
  121. package/dist/mcp/replayUnavailable.js.map +1 -0
  122. package/dist/mcp/requestLedger.js +193 -52
  123. package/dist/mcp/requestLedger.js.map +1 -1
  124. package/dist/mcp/seriesEvidence.js +116 -22
  125. package/dist/mcp/seriesEvidence.js.map +1 -1
  126. package/dist/mcp/throttleRetry.js +160 -51
  127. package/dist/mcp/throttleRetry.js.map +1 -1
  128. package/dist/mcp/toolResult.js +17 -1
  129. package/dist/mcp/toolResult.js.map +1 -1
  130. package/dist/provider-contract/codeSandbox.js +125 -0
  131. package/dist/provider-contract/codeSandbox.js.map +1 -0
  132. package/dist/provider-contract/sessionExpired.js +44 -0
  133. package/dist/provider-contract/sessionExpired.js.map +1 -0
  134. package/dist/provider-contract/types.js +12 -1
  135. package/dist/provider-contract/types.js.map +1 -1
  136. package/dist/redaction/knownStrings.js +16 -0
  137. package/dist/redaction/knownStrings.js.map +1 -1
  138. package/dist/tool-providers/index.js +5 -1
  139. package/dist/tool-providers/index.js.map +1 -1
  140. package/dist/tools/requestEvidence.js +60 -0
  141. package/dist/tools/requestEvidence.js.map +1 -1
  142. package/dist/types/adapters/code/agentcore.d.ts +65 -68
  143. package/dist/types/adapters/code/agentcore.d.ts.map +1 -1
  144. package/dist/types/adapters/code/agentcoreClient.d.ts +194 -0
  145. package/dist/types/adapters/code/agentcoreClient.d.ts.map +1 -0
  146. package/dist/types/adapters/code/agentcoreWire.d.ts +120 -0
  147. package/dist/types/adapters/code/agentcoreWire.d.ts.map +1 -0
  148. package/dist/types/adapters/code/fileName.d.ts +44 -0
  149. package/dist/types/adapters/code/fileName.d.ts.map +1 -0
  150. package/dist/types/adapters/code/local.d.ts +91 -12
  151. package/dist/types/adapters/code/local.d.ts.map +1 -1
  152. package/dist/types/adapters/code/output.d.ts +71 -0
  153. package/dist/types/adapters/code/output.d.ts.map +1 -0
  154. package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts +7 -6
  155. package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts.map +1 -1
  156. package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +32 -1
  157. package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts.map +1 -1
  158. package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts.map +1 -1
  159. package/dist/types/core/codeRunnerTool.d.ts +15 -3
  160. package/dist/types/core/codeRunnerTool.d.ts.map +1 -1
  161. package/dist/types/doors/providers.d.ts +1 -1
  162. package/dist/types/doors/providers.d.ts.map +1 -1
  163. package/dist/types/events/payloads.d.ts +144 -29
  164. package/dist/types/events/payloads.d.ts.map +1 -1
  165. package/dist/types/index.d.ts +2 -0
  166. package/dist/types/index.d.ts.map +1 -1
  167. package/dist/types/mcp/callLifetime.d.ts +57 -14
  168. package/dist/types/mcp/callLifetime.d.ts.map +1 -1
  169. package/dist/types/mcp/callMarker.d.ts +44 -0
  170. package/dist/types/mcp/callMarker.d.ts.map +1 -0
  171. package/dist/types/mcp/index.d.ts +2 -0
  172. package/dist/types/mcp/index.d.ts.map +1 -1
  173. package/dist/types/mcp/mcpClient.d.ts.map +1 -1
  174. package/dist/types/mcp/replay.d.ts +292 -0
  175. package/dist/types/mcp/replay.d.ts.map +1 -0
  176. package/dist/types/mcp/replayUnavailable.d.ts +69 -0
  177. package/dist/types/mcp/replayUnavailable.d.ts.map +1 -0
  178. package/dist/types/mcp/requestLedger.d.ts +115 -32
  179. package/dist/types/mcp/requestLedger.d.ts.map +1 -1
  180. package/dist/types/mcp/seriesEvidence.d.ts +73 -15
  181. package/dist/types/mcp/seriesEvidence.d.ts.map +1 -1
  182. package/dist/types/mcp/throttleRetry.d.ts +86 -42
  183. package/dist/types/mcp/throttleRetry.d.ts.map +1 -1
  184. package/dist/types/mcp/toolResult.d.ts +6 -0
  185. package/dist/types/mcp/toolResult.d.ts.map +1 -1
  186. package/dist/types/mcp/types.d.ts +73 -8
  187. package/dist/types/mcp/types.d.ts.map +1 -1
  188. package/dist/types/provider-contract/codeSandbox.d.ts +90 -0
  189. package/dist/types/provider-contract/codeSandbox.d.ts.map +1 -0
  190. package/dist/types/provider-contract/sessionExpired.d.ts +32 -0
  191. package/dist/types/provider-contract/sessionExpired.d.ts.map +1 -0
  192. package/dist/types/provider-contract/types.d.ts +166 -17
  193. package/dist/types/provider-contract/types.d.ts.map +1 -1
  194. package/dist/types/redaction/knownStrings.d.ts.map +1 -1
  195. package/dist/types/tool-providers/index.d.ts +2 -2
  196. package/dist/types/tool-providers/index.d.ts.map +1 -1
  197. package/dist/types/tools/requestEvidence.d.ts.map +1 -1
  198. package/package.json +1 -1
@@ -0,0 +1,334 @@
1
+ /**
2
+ * What `agentCoreCodeRunner` has to know about AgentCore's wire, as pure
3
+ * functions: how big a request is, how a returned file is paired with the
4
+ * path asked for, how the staged-input manifest reaches a runtime that has no
5
+ * environment parameter, and which service errors mean the session is gone.
6
+ *
7
+ * Kept apart from the session so each rule is one small function with its
8
+ * own test, and so the session reads as the sequence it is.
9
+ */
10
+ import { STAGED_INPUTS_ENV } from '../../provider-contract/types.js';
11
+ import { CodeSandboxError } from '../../provider-contract/codeSandbox.js';
12
+ import { isHighSurrogate, isLowSurrogate, payloadBytes } from './output.js';
13
+ // ── Request size ─────────────────────────────────────────────────────────
14
+ /**
15
+ * AgentCore's per-call limit for `InvokeCodeInterpreter`: "Max payload size —
16
+ * 100 MB — Maximum size for request/response payloads", not adjustable
17
+ * (AgentCore Code Interpreter service quotas).
18
+ *
19
+ * The service does not say whether that MB is 10^6 or 2^20 bytes, so the
20
+ * adapter holds a request to the SMALLER reading: a request it sends is never
21
+ * one the limit could refuse on size, and one between the two readings is
22
+ * refused here by name rather than by the service in words nobody can act on.
23
+ */
24
+ export const AGENTCORE_MAX_REQUEST_BYTES = 100_000_000;
25
+ /** `{"arguments":{"content":[` … `]},"name":"writeFiles"}` around the entries. */
26
+ const WRITE_FILES_ENVELOPE_BYTES = JSON.stringify({
27
+ arguments: { content: [] },
28
+ name: 'writeFiles',
29
+ }).length;
30
+ /**
31
+ * The size, in bytes, of the JSON body one `writeFiles` request sends —
32
+ * computed, not built: text as its JSON-escaped UTF-8, bytes as base64 (how
33
+ * the JSON protocol carries a blob), plus the envelope. Building the body to
34
+ * measure it would hold a second copy of up to a hundred megabytes.
35
+ */
36
+ export function writeFilesRequestBytes(content) {
37
+ let bytes = WRITE_FILES_ENVELOPE_BYTES + Math.max(content.length - 1, 0); // the commas
38
+ for (const entry of content) {
39
+ bytes += '{"path":'.length + jsonStringBytes(entry.path) + '}'.length;
40
+ if (entry.text !== undefined)
41
+ bytes += ',"text":'.length + jsonStringBytes(entry.text);
42
+ else
43
+ bytes += ',"blob":'.length + 2 + base64Length(entry.blob.byteLength);
44
+ }
45
+ return bytes;
46
+ }
47
+ /** Bytes of `JSON.stringify(text)` encoded as UTF-8, quotes included, without building it. */
48
+ export function jsonStringBytes(text) {
49
+ let bytes = 2;
50
+ for (let i = 0; i < text.length; i++) {
51
+ const unit = text.charCodeAt(i);
52
+ if (unit === 0x22 || unit === 0x5c)
53
+ bytes += 2; // \" and \\
54
+ else if (unit < 0x20)
55
+ bytes += SHORT_ESCAPES.has(unit) ? 2 : 6; // \n … or \u00XX
56
+ else if (unit < 0x80)
57
+ bytes += 1;
58
+ else if (unit < 0x800)
59
+ bytes += 2;
60
+ else if (isHighSurrogate(unit) && isLowSurrogate(text.charCodeAt(i + 1))) {
61
+ bytes += 4;
62
+ i++;
63
+ }
64
+ else if (isHighSurrogate(unit) || isLowSurrogate(unit))
65
+ bytes += 6; // a lone half: \udXXX
66
+ else
67
+ bytes += 3;
68
+ }
69
+ return bytes;
70
+ }
71
+ /** Control characters JSON writes as a two-character escape: \b \t \n \f \r. */
72
+ const SHORT_ESCAPES = new Set([0x08, 0x09, 0x0a, 0x0c, 0x0d]);
73
+ function base64Length(bytes) {
74
+ return 4 * Math.ceil(bytes / 3);
75
+ }
76
+ /** One `writeFiles` entry for a payload: TEXT for a string, BYTES for bytes. */
77
+ export function fileContent(path, data) {
78
+ return typeof data === 'string' ? { path, text: data } : { path, blob: data };
79
+ }
80
+ // ── Reading a file back ──────────────────────────────────────────────────
81
+ /** How AgentCore names a file it returns: `file:///` + the path asked for,
82
+ * percent-encoded (read from a published real `readFiles` answer:
83
+ * `report.csv` → `file:///report.csv`; the shape is not in AWS's docs). */
84
+ const FILE_URI_PREFIX = 'file:///';
85
+ /**
86
+ * The one file a `readFiles` answer returned for `path` — or a refusal.
87
+ *
88
+ * Paired by the URI the SERVICE put on the file, never by position: a file
89
+ * with no URI, or a URI naming another path, fails closed, and so does an
90
+ * answer carrying several files for one path. Text or bytes is read off which
91
+ * field the service filled — never off the file's name — and an entry with
92
+ * both, or neither, is refused. A file over `maxBytes` is refused whole.
93
+ */
94
+ export function pairReadFile(path, answer, maxBytes) {
95
+ const refuse = (reason, why) => {
96
+ throw new CodeSandboxError(reason, `agentCoreCodeRunner: reading '${path}' — ${why}`, { path });
97
+ };
98
+ if (answer.isError === true) {
99
+ refuse('file-not-returned', 'the service flagged its answer as an error, so no file came back. Its words are not ' +
100
+ 'repeated here.');
101
+ }
102
+ const resources = answer.resources ?? [];
103
+ if (resources.length === 0)
104
+ refuse('file-not-returned', 'the answer held no file.');
105
+ for (const resource of resources) {
106
+ if (typeof resource.uri !== 'string' || resource.uri.length === 0) {
107
+ refuse('uri-missing', 'a file came back without a URI, so it cannot be paired with the path asked for.');
108
+ }
109
+ if (hasQueryOrFragment(resource.uri)) {
110
+ refuse('uri-mismatch', 'a file came back under a URI with a query or fragment, which is not part of any file ' +
111
+ 'name (a literal ? or # in a name arrives as %3F or %23).');
112
+ }
113
+ if (!uriNames(resource.uri, path)) {
114
+ refuse('uri-mismatch', 'a file came back under a URI that names another path.');
115
+ }
116
+ }
117
+ if (resources.length > 1)
118
+ refuse('content-ambiguous', `${resources.length} files came back for one path.`);
119
+ const resource = resources[0];
120
+ const hasText = resource.text !== undefined && resource.text !== null;
121
+ const hasBlob = resource.blob !== undefined && resource.blob !== null;
122
+ if (hasText === hasBlob) {
123
+ refuse('content-ambiguous', hasText
124
+ ? 'the file came back as both text and bytes.'
125
+ : 'the file came back with neither text nor bytes.');
126
+ }
127
+ if (hasText && typeof resource.text !== 'string')
128
+ refuse('content-ambiguous', 'its text is not a string.');
129
+ if (hasBlob && !isBytes(resource.blob))
130
+ refuse('content-ambiguous', 'its bytes are not bytes.');
131
+ const data = hasText ? resource.text : resource.blob;
132
+ const bytes = payloadBytes(data);
133
+ if (bytes > maxBytes) {
134
+ throw new CodeSandboxError('file-too-large', `agentCoreCodeRunner: '${path}' is ${bytes} bytes and this read allows ${maxBytes}. ` +
135
+ 'Nothing was returned — a file is never handed back cut.', { path, bytes, limit: maxBytes });
136
+ }
137
+ return {
138
+ path,
139
+ data,
140
+ bytes,
141
+ ...(typeof resource.mimeType === 'string' &&
142
+ resource.mimeType.length > 0 && {
143
+ mediaType: resource.mimeType,
144
+ }),
145
+ };
146
+ }
147
+ /**
148
+ * Does `uri` name `path`? ONE declared form, read ONE way (RFC 8089's file
149
+ * URI, RFC 3986's path syntax): `file:///` followed by a path whose segments
150
+ * are percent-encoded.
151
+ *
152
+ * - A raw `?` or `#` starts a query or a fragment, which is never part of a
153
+ * file name, so a URI carrying one is refused outright. A name that really
154
+ * contains `?` or `#` arrives as `%3F` / `%23`.
155
+ * - Each segment is decoded on its own. An invalid escape refuses the URI, and
156
+ * a decoded `/` (an encoded `%2F`) is a character inside one name, so it can
157
+ * never stand in for the separator.
158
+ * - Only the decoded path is compared. There is no second, raw reading to fall
159
+ * back on when the first does not match.
160
+ */
161
+ export function uriNames(uri, path) {
162
+ if (!uri.startsWith(FILE_URI_PREFIX))
163
+ return false;
164
+ const named = uri.slice(FILE_URI_PREFIX.length);
165
+ if (hasQueryOrFragment(named))
166
+ return false;
167
+ const segments = [];
168
+ for (const encoded of named.split('/')) {
169
+ let segment;
170
+ try {
171
+ segment = decodeURIComponent(encoded);
172
+ }
173
+ catch {
174
+ return false;
175
+ }
176
+ if (segment.includes('/'))
177
+ return false;
178
+ segments.push(segment);
179
+ }
180
+ return segments.join('/') === path;
181
+ }
182
+ /** A raw `?` or `#` in a URI: a query or a fragment component. */
183
+ function hasQueryOrFragment(uriTail) {
184
+ return uriTail.includes('?') || uriTail.includes('#');
185
+ }
186
+ function isBytes(value) {
187
+ return (value instanceof Uint8Array || Object.prototype.toString.call(value) === '[object Uint8Array]');
188
+ }
189
+ // ── The staged-input manifest ────────────────────────────────────────────
190
+ /** Where staged inputs land, relative to the session's working directory. */
191
+ export const STAGED_INPUTS_DIR = 'af-staged-inputs';
192
+ /** The line the manifest setup prints once the variable is set. */
193
+ export const MANIFEST_SET_MARKER = 'af-staged-inputs-set';
194
+ /**
195
+ * Which runtime a language names, for the manifest setup. `undefined` (no
196
+ * language sent) is the service's default runtime, Python; a language this
197
+ * adapter does not know is `undefined` — and refused, never guessed.
198
+ */
199
+ export function manifestRuntime(language) {
200
+ if (language === undefined || language === '')
201
+ return 'python';
202
+ const lower = language.toLowerCase();
203
+ if (lower === 'python' || lower === 'python3' || lower === 'py')
204
+ return 'python';
205
+ if (lower === 'javascript' || lower === 'js' || lower === 'node')
206
+ return 'javascript';
207
+ if (lower === 'typescript' || lower === 'ts')
208
+ return 'typescript';
209
+ return undefined;
210
+ }
211
+ /**
212
+ * The code that sets {@link STAGED_INPUTS_ENV} inside one runtime of the
213
+ * session, then prints {@link MANIFEST_SET_MARKER}.
214
+ *
215
+ * WHY A SEPARATE CALL. `executeCode` takes no environment, and prepending a
216
+ * line to the caller's code would shift every line number its traceback
217
+ * reports and break a Python `from __future__` import. The session's runtime
218
+ * keeps its state between executions, so the variable is set once per
219
+ * runtime and manifest, by this code, before the caller's code runs.
220
+ *
221
+ * The value is the manifest JSON as a JSON string literal, which is a valid
222
+ * string literal in Python and in JavaScript alike.
223
+ */
224
+ export function manifestSetupCode(runtime, manifestJson) {
225
+ const value = JSON.stringify(manifestJson);
226
+ const name = JSON.stringify(STAGED_INPUTS_ENV);
227
+ const marker = JSON.stringify(MANIFEST_SET_MARKER);
228
+ if (runtime === 'python') {
229
+ return [
230
+ 'import os as _af_os',
231
+ `_af_os.environ[${name}] = ${value}`,
232
+ 'del _af_os',
233
+ `print(${marker})`,
234
+ ].join('\n');
235
+ }
236
+ return [
237
+ '{',
238
+ runtime === 'typescript'
239
+ ? ' const _afHost: any = globalThis;'
240
+ : ' const _afHost = globalThis;',
241
+ ` const _afValue = ${value};`,
242
+ ' if (_afHost.process && _afHost.process.env) {',
243
+ ` _afHost.process.env[${name}] = _afValue;`,
244
+ ' } else {',
245
+ ` _afHost.Deno.env.set(${name}, _afValue);`,
246
+ ' }',
247
+ ` console.log(${marker});`,
248
+ '}',
249
+ ].join('\n');
250
+ }
251
+ /**
252
+ * `command`, with {@link STAGED_INPUTS_ENV} exported ahead of it.
253
+ *
254
+ * A command is its own shell process, so a variable set in a code runtime
255
+ * never reaches it; this line does, every time, and needs no state. The value
256
+ * is single-quoted, the one shell quoting with no expansions inside it.
257
+ */
258
+ export function withManifestExport(command, manifestJson) {
259
+ return `export ${STAGED_INPUTS_ENV}=${shellQuoted(manifestJson)}\n${command}`;
260
+ }
261
+ function shellQuoted(text) {
262
+ return `'${text.split("'").join(`'\\''`)}'`;
263
+ }
264
+ // ── Service errors ───────────────────────────────────────────────────────
265
+ /** The exceptions AgentCore models for `InvokeCodeInterpreter`, by class name. */
266
+ const MODELLED_EXCEPTIONS = new Set([
267
+ 'AccessDeniedException',
268
+ 'ConflictException',
269
+ 'InternalServerException',
270
+ 'ResourceNotFoundException',
271
+ 'ServiceQuotaExceededException',
272
+ 'ThrottlingException',
273
+ 'ValidationException',
274
+ ]);
275
+ /** Words a service uses for a session that is gone — both halves required, so
276
+ * a validation message that merely echoes a request containing "inactive"
277
+ * is not read as a dead session. */
278
+ const SAYS_INACTIVE = /\bnot active\b|\binactive\b/i;
279
+ const NAMES_A_SESSION = /\bsession\b/i;
280
+ /**
281
+ * What a service error says about the session: `'session'` when it means the
282
+ * session itself is gone, `'file-or-session'` when it could mean that or a
283
+ * missing file, `undefined` otherwise.
284
+ *
285
+ * - A modelled exception — a `ValidationException` above all — whose words
286
+ * name a session and say it is "not active" or "inactive": `'session'`.
287
+ * - A `ResourceNotFoundException`, thrown by the SDK or carried on the stream:
288
+ * on a call that addresses nothing but the session (code, a command, a
289
+ * write) the missing resource is the session, so `'session'`. A READ also
290
+ * names a file, and AgentCore does not say which resource was missing, so
291
+ * `'file-or-session'` — never taken as proof the session is gone.
292
+ */
293
+ export function sessionExpiryEvidence(err, tool) {
294
+ const name = errorNameOf(err);
295
+ const message = errorMessage(err);
296
+ if (MODELLED_EXCEPTIONS.has(name) &&
297
+ SAYS_INACTIVE.test(message) &&
298
+ NAMES_A_SESSION.test(message)) {
299
+ return 'session';
300
+ }
301
+ if (name === 'ResourceNotFoundException') {
302
+ return tool === 'readFiles' ? 'file-or-session' : 'session';
303
+ }
304
+ return undefined;
305
+ }
306
+ /**
307
+ * A modelled service exception from a file or command call, rewritten in
308
+ * fixed words that name the exception and the call — or `undefined` for any
309
+ * other error, which passes through as it is.
310
+ *
311
+ * The service's own message is NOT repeated: a validation message can quote
312
+ * what was sent, and what was sent here is a path, a command or a file's
313
+ * bytes. The exception's name is kept as the error's `name`, so a caller
314
+ * branches on it exactly as on the SDK's own error.
315
+ */
316
+ export function serviceRefusal(err, tool) {
317
+ const name = errorNameOf(err);
318
+ if (!MODELLED_EXCEPTIONS.has(name))
319
+ return undefined;
320
+ const refusal = new Error(`agentCoreCodeRunner: the service refused ${tool} (${name}). Its message is not repeated ` +
321
+ 'here, because it can quote what was sent.');
322
+ refusal.name = name;
323
+ return refusal;
324
+ }
325
+ /** The `name` an error carries — an SDK exception's class name — or `''`. */
326
+ export function errorNameOf(err) {
327
+ const name = err?.name;
328
+ return typeof name === 'string' ? name : '';
329
+ }
330
+ function errorMessage(err) {
331
+ const message = err?.message;
332
+ return typeof message === 'string' ? message : '';
333
+ }
334
+ //# sourceMappingURL=agentcoreWire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentcoreWire.js","sourceRoot":"","sources":["../../../../src/adapters/code/agentcoreWire.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The one file-name encoder both code adapters stage inputs under.
3
+ *
4
+ * A staged input's name is caller data landing in a filesystem — a local temp
5
+ * directory for `localCodeRunner`, the sandbox's own disk for
6
+ * `agentCoreCodeRunner`. Both turn it into ONE inert segment through this
7
+ * function, so the same name lands under the same file name on either backend
8
+ * and two names never become one file on either.
9
+ */
10
+ /**
11
+ * One caller-supplied name → one inert file-name segment, **without ever
12
+ * folding two names into one file**.
13
+ *
14
+ * Separators cannot survive as a parent hop and `a/b` cannot become two
15
+ * segments — the same law `artifacts/scopePath` states for scope tuples,
16
+ * applied to the one other place caller strings become file names in this
17
+ * package. But the old version got there by REPLACING every illegal character
18
+ * with `_`, which meant `a/b.csv` and `a_b.csv` became the same file: the
19
+ * second write clobbered the first's bytes, `stageInputs` answered two manifest
20
+ * entries pointing at one path, and the model's code opened `a/b.csv` and read
21
+ * whatever `a_b.csv` contained. Nothing failed. The computation was simply run
22
+ * against the wrong dataset — which is the worst shape a data bug can take,
23
+ * because the answer still looks like an answer. `.slice(0, 120)` folded the
24
+ * same way for any two names agreeing on their first 120 characters.
25
+ *
26
+ * So this ENCODES instead of sanitising, in two arms whose outputs cannot meet
27
+ * (the shape `safeSessionId` uses in `adapters/hosting/agentcore.ts`):
28
+ *
29
+ * A. already one legal segment, not claiming to be an encoded name, and not
30
+ * dots-only → returned BYTE FOR BYTE. This is what keeps the fix from
31
+ * renaming anything that works today: `dataset.json`, `a_b.json`, every
32
+ * name `codeRunnerTool` derives from a declared argument, all land at
33
+ * exactly the path they landed at before.
34
+ * B. anything else → `_enc_` + an escaped form, where `_` is the escape
35
+ * character (`_` → `__`, any other illegal UTF-16 unit → `_uXXXX`). A
36
+ * decoder is a left inverse of that, which is what makes it injective.
37
+ *
38
+ * Arm A never starts with `_enc_` (excluded by construction) and every arm B
39
+ * output does, so no name from one arm can collide with a name from the other.
40
+ * Both arms produce ASCII with no `/`, no `\` and no NUL, so the path-hop law
41
+ * the old version enforced is unchanged.
42
+ */
43
+ export declare function safeFileName(raw: string): string;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The one file-name encoder both code adapters stage inputs under.
3
+ *
4
+ * A staged input's name is caller data landing in a filesystem — a local temp
5
+ * directory for `localCodeRunner`, the sandbox's own disk for
6
+ * `agentCoreCodeRunner`. Both turn it into ONE inert segment through this
7
+ * function, so the same name lands under the same file name on either backend
8
+ * and two names never become one file on either.
9
+ */
10
+ import { sha256Hex } from '../../lib/sha256.js';
11
+ /** Longest file name this adapter will write. Well inside every POSIX
12
+ * `NAME_MAX`, and the cap this adapter has always used. */
13
+ const FILE_NAME_MAX = 120;
14
+ /** Marks a name this adapter had to rewrite. Nothing arm A returns may start
15
+ * with it. */
16
+ const ENCODED_FILE_PREFIX = '_enc_';
17
+ /** Already one inert segment: no separator, no escape-looking bytes. */
18
+ const FILE_NAME_ALREADY_LEGAL = /^[A-Za-z0-9._-]+$/;
19
+ /** `.`, `..`, `...` — a name that is nothing but dots. Not a hop once the
20
+ * separators are gone, but not a name either, so it goes to arm B. */
21
+ const DOTS_ONLY = /^\.+$/;
22
+ /** Legal AND not the escape character — the set {@link encodeFileName} passes
23
+ * through untouched. */
24
+ const FILE_CHAR_PASSES_THROUGH = /[A-Za-z0-9.-]/;
25
+ /**
26
+ * One caller-supplied name → one inert file-name segment, **without ever
27
+ * folding two names into one file**.
28
+ *
29
+ * Separators cannot survive as a parent hop and `a/b` cannot become two
30
+ * segments — the same law `artifacts/scopePath` states for scope tuples,
31
+ * applied to the one other place caller strings become file names in this
32
+ * package. But the old version got there by REPLACING every illegal character
33
+ * with `_`, which meant `a/b.csv` and `a_b.csv` became the same file: the
34
+ * second write clobbered the first's bytes, `stageInputs` answered two manifest
35
+ * entries pointing at one path, and the model's code opened `a/b.csv` and read
36
+ * whatever `a_b.csv` contained. Nothing failed. The computation was simply run
37
+ * against the wrong dataset — which is the worst shape a data bug can take,
38
+ * because the answer still looks like an answer. `.slice(0, 120)` folded the
39
+ * same way for any two names agreeing on their first 120 characters.
40
+ *
41
+ * So this ENCODES instead of sanitising, in two arms whose outputs cannot meet
42
+ * (the shape `safeSessionId` uses in `adapters/hosting/agentcore.ts`):
43
+ *
44
+ * A. already one legal segment, not claiming to be an encoded name, and not
45
+ * dots-only → returned BYTE FOR BYTE. This is what keeps the fix from
46
+ * renaming anything that works today: `dataset.json`, `a_b.json`, every
47
+ * name `codeRunnerTool` derives from a declared argument, all land at
48
+ * exactly the path they landed at before.
49
+ * B. anything else → `_enc_` + an escaped form, where `_` is the escape
50
+ * character (`_` → `__`, any other illegal UTF-16 unit → `_uXXXX`). A
51
+ * decoder is a left inverse of that, which is what makes it injective.
52
+ *
53
+ * Arm A never starts with `_enc_` (excluded by construction) and every arm B
54
+ * output does, so no name from one arm can collide with a name from the other.
55
+ * Both arms produce ASCII with no `/`, no `\` and no NUL, so the path-hop law
56
+ * the old version enforced is unchanged.
57
+ */
58
+ export function safeFileName(raw) {
59
+ if (raw.length <= FILE_NAME_MAX &&
60
+ FILE_NAME_ALREADY_LEGAL.test(raw) &&
61
+ !raw.startsWith(ENCODED_FILE_PREFIX) &&
62
+ !DOTS_ONLY.test(raw)) {
63
+ return raw;
64
+ }
65
+ const encoded = ENCODED_FILE_PREFIX + encodeFileName(raw);
66
+ // Strictly less, so the two shapes below are told apart by LENGTH alone: a
67
+ // digested name is always exactly FILE_NAME_MAX and an escaped one never is.
68
+ if (encoded.length < FILE_NAME_MAX)
69
+ return encoded;
70
+ // Past the cap no mapping can stay injective — there are more names than
71
+ // there are file names — so the tail becomes a digest of the WHOLE raw name.
72
+ // SHA-256 rather than the package's 32-bit FNV-1a: a name reaching this
73
+ // adapter is caller data, and a 32-bit digest is a collision somebody can go
74
+ // and find, which for a file the model then reads as data is the same defect
75
+ // this function exists to close. `lib/sha256.ts` — the library's own,
76
+ // checked against `node:crypto` — so `agentCoreCodeRunner`, which may run
77
+ // where there is no `node:crypto`, encodes a name exactly as the local runner
78
+ // does.
79
+ const digest = sha256Hex(raw).slice(0, 32);
80
+ return `${encoded.slice(0, FILE_NAME_MAX - digest.length - 2)}_z${digest}`;
81
+ }
82
+ /** `_` → `__`, anything else illegal → `_uXXXX`. Injective by construction. */
83
+ function encodeFileName(raw) {
84
+ let out = '';
85
+ for (let i = 0; i < raw.length; i++) {
86
+ const ch = raw[i];
87
+ if (ch === '_')
88
+ out += '__';
89
+ else if (FILE_CHAR_PASSES_THROUGH.test(ch))
90
+ out += ch;
91
+ else
92
+ out += `_u${raw.charCodeAt(i).toString(16).padStart(4, '0')}`;
93
+ }
94
+ return out;
95
+ }
96
+ //# sourceMappingURL=fileName.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileName.js","sourceRoot":"","sources":["../../../../src/adapters/code/fileName.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD;4DAC4D;AAC5D,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B;eACe;AACf,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAEpC,wEAAwE;AACxE,MAAM,uBAAuB,GAAG,mBAAmB,CAAC;AAEpD;uEACuE;AACvE,MAAM,SAAS,GAAG,OAAO,CAAC;AAE1B;yBACyB;AACzB,MAAM,wBAAwB,GAAG,eAAe,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,IACE,GAAG,CAAC,MAAM,IAAI,aAAa;QAC3B,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC;QACjC,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,CAAC;QACpC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EACpB,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,OAAO,GAAG,mBAAmB,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAC1D,2EAA2E;IAC3E,6EAA6E;IAC7E,IAAI,OAAO,CAAC,MAAM,GAAG,aAAa;QAAE,OAAO,OAAO,CAAC;IACnD,yEAAyE;IACzE,6EAA6E;IAC7E,wEAAwE;IACxE,6EAA6E;IAC7E,6EAA6E;IAC7E,sEAAsE;IACtE,0EAA0E;IAC1E,8EAA8E;IAC9E,QAAQ;IACR,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3C,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;AAC7E,CAAC;AAED,+EAA+E;AAC/E,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAW,CAAC;QAC5B,IAAI,EAAE,KAAK,GAAG;YAAE,GAAG,IAAI,IAAI,CAAC;aACvB,IAAI,wBAAwB,CAAC,IAAI,CAAC,EAAE,CAAC;YAAE,GAAG,IAAI,EAAE,CAAC;;YACjD,GAAG,IAAI,KAAK,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IACrE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -12,7 +12,13 @@
12
12
  *
13
13
  * What a child process DOES give you:
14
14
  * • a separate process and heap — a crash or an OOM takes the child, not you;
15
- * • kill on timeout, so runaway code has a ceiling;
15
+ * • kill on timeout, so runaway code has a ceiling — on POSIX the child
16
+ * runs in a process group of its own, and at the timeout, on an abort or
17
+ * at the output ceiling the runner stops that OWNED group (SIGKILL). That
18
+ * is cleanup, not containment: a descendant that starts its own session
19
+ * or group escapes it, and a host killed before its exit handlers run
20
+ * stops nothing. On Windows only the child itself can be stopped, and the
21
+ * timeout error says so;
16
22
  * • no inherited stdin, so nothing can block waiting for a terminal;
17
23
  * • an environment ALLOWLIST — `process.env` is not inherited (only `PATH`,
18
24
  * so the interpreter can be found), so an `AWS_SECRET_ACCESS_KEY` in your
@@ -34,11 +40,21 @@
34
40
  * is not a set this adapter can identify without guessing which of a developer's
35
41
  * files were meant, and a dev-loop runner that quietly uploaded whatever
36
42
  * appeared beside your source would be the worse failure. Producing outputs
37
- * needs three things this adapter does not have yet: a declared output
38
- * directory the model is told about, a bounded read-back of what landed in it,
39
- * and a size policy for what is too big to carry in-band. A runner that has
40
- * them (a managed sandbox that returns file contents) fills the field, and
41
- * `codeRunnerTool` mints every data-carrying entry with no further wiring.
43
+ * needs a declared output directory the model is told about and a size policy
44
+ * for what is too big to carry in-band; `readFile` below is the bounded
45
+ * read-back, for a caller that names the file. A runner that has all three
46
+ * fills the field, and `codeRunnerTool` mints every data-carrying entry with
47
+ * no further wiring.
48
+ *
49
+ * ── Files and commands ─────────────────────────────────────────────────────
50
+ * `readFile(path, { maxBytes })` reads ONE file back from the working
51
+ * directory — the path checked by the port's one rule (`sandboxPath`), then
52
+ * by its REAL location, so a symbolic link cannot lead it outside; a file over
53
+ * `maxBytes` is refused, never cut; a file on disk is bytes, so bytes come
54
+ * back and no encoding is guessed. `runCommand(command)` runs ONE `sh -c`
55
+ * command with the same environment allowlist as the code, and exists only on
56
+ * a runner built with `commands: true`. Neither is a network or filesystem
57
+ * jail: a command reaches whatever this machine reaches.
42
58
  *
43
59
  * So: a development loop, a trusted-input pipeline, a machine you would be
44
60
  * relaxed about a shell script running on. NOT arbitrary model-written code
@@ -93,19 +109,30 @@ export interface LocalCodeRunnerOptions {
93
109
  * reason over is a fragment.
94
110
  */
95
111
  readonly maxOutputChars?: number;
112
+ /**
113
+ * Give each session `runCommand` — one `sh -c` command in the working
114
+ * directory, with the same environment allowlist as the code. Default
115
+ * `false`, and then the member is ABSENT (`canRunCodeCommands` says no):
116
+ * it runs arbitrary shell, so a runner offers it only when built to. Build
117
+ * one runner per agent that should have it.
118
+ *
119
+ * Not a jail: the command reaches this machine's filesystem and network, as
120
+ * the code already does.
121
+ */
122
+ readonly commands?: boolean;
96
123
  /** Stable id (default `'local-code-runner'`). Rides the session events. */
97
124
  readonly id?: string;
98
125
  /** @internal Test seam — the `node:child_process` module. */
99
126
  readonly _childProcess?: ChildProcessModuleLike;
100
- /** @internal Test seam — the `node:fs` slice staging uses. */
127
+ /** @internal Test seam — the `node:fs` slice staging and reading use. */
101
128
  readonly _fs?: FsModuleLike;
102
129
  /** @internal Test seam — where staging directories are created. Defaults to
103
130
  * the OS temp directory. */
104
131
  readonly _stagingRoot?: string;
105
132
  }
106
- /** The slice of `node:fs` staging touches. Loaded lazily, and ONLY when a
107
- * caller actually stages something — a session that never stages never asks
108
- * this runtime for a filesystem. */
133
+ /** The slice of `node:fs` staging and reading touch. Loaded lazily, and ONLY
134
+ * when a caller actually stages or reads something — a session that does
135
+ * neither never asks this runtime for a filesystem. */
109
136
  export interface FsModuleLike {
110
137
  readonly mkdtempSync?: (prefix: string) => string;
111
138
  readonly writeFileSync?: (path: string, data: string | Uint8Array) => void;
@@ -113,18 +140,70 @@ export interface FsModuleLike {
113
140
  recursive?: boolean;
114
141
  force?: boolean;
115
142
  }) => void;
143
+ readonly realpathSync?: (path: string) => string;
144
+ readonly openSync?: (path: string, flags: string | number) => number;
145
+ /** The open flags a read uses where the platform has them. */
146
+ readonly constants?: {
147
+ readonly O_RDONLY?: number;
148
+ readonly O_NONBLOCK?: number;
149
+ readonly O_NOFOLLOW?: number;
150
+ };
151
+ readonly fstatSync?: (fd: number) => {
152
+ isFile(): boolean;
153
+ readonly size: number;
154
+ };
155
+ readonly readSync?: (fd: number, buffer: Uint8Array, offset: number, length: number, position: number | null) => number;
156
+ readonly closeSync?: (fd: number) => void;
116
157
  }
117
158
  /** The slice of `node:child_process` this adapter touches. */
118
159
  export interface ChildProcessModuleLike {
160
+ /** Used when present (the real module always has it): only `spawn` can give
161
+ * a child a process group of its own. */
162
+ readonly spawn?: SpawnLike;
119
163
  readonly execFile?: (file: string, args: readonly string[], options: {
120
164
  cwd?: string;
121
165
  env?: Record<string, string>;
122
- timeout?: number;
123
166
  maxBuffer?: number;
124
- signal?: AbortSignal;
167
+ killSignal?: string;
168
+ detached?: boolean;
125
169
  }, callback: (error: (Error & {
126
170
  code?: number | string;
127
171
  killed?: boolean;
172
+ signal?: string | null;
128
173
  }) | null, stdout: string, stderr: string) => void) => unknown;
129
174
  }
175
+ /** The part of the child `execFile` / `spawn` hands back that ending it needs. */
176
+ interface ChildLike {
177
+ readonly pid?: number;
178
+ kill?(signal?: string): boolean;
179
+ readonly stdout?: {
180
+ destroy?(): void;
181
+ } | null;
182
+ readonly stderr?: {
183
+ destroy?(): void;
184
+ } | null;
185
+ }
186
+ /** A readable end of a spawned child's pipe, as far as collecting it goes. */
187
+ interface PipeLike {
188
+ on(event: 'data', listener: (chunk: Uint8Array) => void): unknown;
189
+ destroy?(): void;
190
+ }
191
+ /** The child `spawn` hands back, as far as this adapter uses it. */
192
+ interface SpawnedLike extends ChildLike {
193
+ readonly stdout?: PipeLike | null;
194
+ readonly stderr?: PipeLike | null;
195
+ on(event: 'error', listener: (error: Error & {
196
+ code?: string;
197
+ }) => void): unknown;
198
+ on(event: 'close', listener: (code: number | null, signal: string | null) => void): unknown;
199
+ }
200
+ /** The slice of `node:child_process` · `spawn` the runner uses — preferred
201
+ * over `execFile`, which does not pass `detached` on to the child. */
202
+ type SpawnLike = (file: string, args: readonly string[], options: {
203
+ cwd?: string;
204
+ env?: Record<string, string>;
205
+ detached?: boolean;
206
+ stdio?: readonly ['ignore', 'pipe', 'pipe'];
207
+ }) => SpawnedLike;
130
208
  export declare function localCodeRunner(options?: LocalCodeRunnerOptions): CodeRunner;
209
+ export {};