agentfootprint 11.5.0 → 12.0.1
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.
- package/CAPABILITIES.md +1 -0
- package/dist/adapters/code/agentcore.js +295 -214
- package/dist/adapters/code/agentcore.js.map +1 -1
- package/dist/adapters/code/agentcoreClient.js +178 -0
- package/dist/adapters/code/agentcoreClient.js.map +1 -0
- package/dist/adapters/code/agentcoreWire.js +348 -0
- package/dist/adapters/code/agentcoreWire.js.map +1 -0
- package/dist/adapters/code/fileName.js +100 -0
- package/dist/adapters/code/fileName.js.map +1 -0
- package/dist/adapters/code/local.js +529 -183
- package/dist/adapters/code/local.js.map +1 -1
- package/dist/adapters/code/output.js +140 -0
- package/dist/adapters/code/output.js.map +1 -0
- package/dist/answer-account/templates.js +12 -6
- package/dist/answer-account/templates.js.map +1 -1
- package/dist/answer-validation/types.js.map +1 -1
- package/dist/core/agent/assessment/assess.js +94 -32
- package/dist/core/agent/assessment/assess.js.map +1 -1
- package/dist/core/agent/assessment/reasons.js +2 -2
- package/dist/core/agent/assessment/reasons.js.map +1 -1
- package/dist/core/agent/assessment/sources.js +61 -23
- package/dist/core/agent/assessment/sources.js.map +1 -1
- package/dist/core/agent/negativeClaims/delivery.js +10 -1
- package/dist/core/agent/negativeClaims/delivery.js.map +1 -1
- package/dist/core/codeRunnerTool.js +52 -7
- package/dist/core/codeRunnerTool.js.map +1 -1
- package/dist/doors/providers.js.map +1 -1
- package/dist/esm/adapters/code/agentcore.d.ts +65 -68
- package/dist/esm/adapters/code/agentcore.js +293 -212
- package/dist/esm/adapters/code/agentcore.js.map +1 -1
- package/dist/esm/adapters/code/agentcoreClient.d.ts +193 -0
- package/dist/esm/adapters/code/agentcoreClient.js +173 -0
- package/dist/esm/adapters/code/agentcoreClient.js.map +1 -0
- package/dist/esm/adapters/code/agentcoreWire.d.ts +119 -0
- package/dist/esm/adapters/code/agentcoreWire.js +334 -0
- package/dist/esm/adapters/code/agentcoreWire.js.map +1 -0
- package/dist/esm/adapters/code/fileName.d.ts +43 -0
- package/dist/esm/adapters/code/fileName.js +96 -0
- package/dist/esm/adapters/code/fileName.js.map +1 -0
- package/dist/esm/adapters/code/local.d.ts +91 -12
- package/dist/esm/adapters/code/local.js +528 -182
- package/dist/esm/adapters/code/local.js.map +1 -1
- package/dist/esm/adapters/code/output.d.ts +70 -0
- package/dist/esm/adapters/code/output.js +129 -0
- package/dist/esm/adapters/code/output.js.map +1 -0
- package/dist/esm/answer-account/templates.d.ts +1 -1
- package/dist/esm/answer-account/templates.js +12 -6
- package/dist/esm/answer-account/templates.js.map +1 -1
- package/dist/esm/answer-validation/types.d.ts +10 -0
- package/dist/esm/answer-validation/types.js.map +1 -1
- package/dist/esm/core/agent/assessment/assess.d.ts +6 -3
- package/dist/esm/core/agent/assessment/assess.js +95 -33
- package/dist/esm/core/agent/assessment/assess.js.map +1 -1
- package/dist/esm/core/agent/assessment/reasons.js +2 -2
- package/dist/esm/core/agent/assessment/reasons.js.map +1 -1
- package/dist/esm/core/agent/assessment/sources.d.ts +26 -1
- package/dist/esm/core/agent/assessment/sources.js +59 -22
- package/dist/esm/core/agent/assessment/sources.js.map +1 -1
- package/dist/esm/core/agent/assessment/types.d.ts +3 -1
- package/dist/esm/core/agent/negativeClaims/delivery.d.ts +9 -1
- package/dist/esm/core/agent/negativeClaims/delivery.js +10 -1
- package/dist/esm/core/agent/negativeClaims/delivery.js.map +1 -1
- package/dist/esm/core/codeRunnerTool.d.ts +15 -3
- package/dist/esm/core/codeRunnerTool.js +52 -7
- package/dist/esm/core/codeRunnerTool.js.map +1 -1
- package/dist/esm/doors/providers.d.ts +1 -1
- package/dist/esm/doors/providers.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +16 -2
- package/dist/esm/identity/types.d.ts +12 -0
- package/dist/esm/identity/types.js.map +1 -1
- package/dist/esm/identity/withCredentialRetry.d.ts +5 -0
- package/dist/esm/identity/withCredentialRetry.js +16 -3
- package/dist/esm/identity/withCredentialRetry.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/boundedJson.d.ts +27 -0
- package/dist/esm/lib/boundedJson.js +33 -0
- package/dist/esm/lib/boundedJson.js.map +1 -1
- package/dist/esm/mcp/gatewayTransport.d.ts +8 -0
- package/dist/esm/mcp/gatewayTransport.js +35 -2
- package/dist/esm/mcp/gatewayTransport.js.map +1 -1
- package/dist/esm/mcp/replay.d.ts +21 -13
- package/dist/esm/mcp/replay.js +14 -10
- package/dist/esm/mcp/replay.js.map +1 -1
- package/dist/esm/mcp/replayUnavailable.d.ts +21 -8
- package/dist/esm/mcp/replayUnavailable.js +21 -8
- package/dist/esm/mcp/replayUnavailable.js.map +1 -1
- package/dist/esm/mcp/throttleRetry.d.ts +5 -5
- package/dist/esm/mcp/throttleRetry.js +5 -5
- package/dist/esm/mcp/types.d.ts +13 -10
- package/dist/esm/provider-contract/codeSandbox.d.ts +89 -0
- package/dist/esm/provider-contract/codeSandbox.js +119 -0
- package/dist/esm/provider-contract/codeSandbox.js.map +1 -0
- package/dist/esm/provider-contract/sessionExpired.d.ts +31 -0
- package/dist/esm/provider-contract/sessionExpired.js +40 -0
- package/dist/esm/provider-contract/sessionExpired.js.map +1 -0
- package/dist/esm/provider-contract/types.d.ts +166 -17
- package/dist/esm/provider-contract/types.js +9 -0
- package/dist/esm/provider-contract/types.js.map +1 -1
- package/dist/esm/redaction/knownStrings.js +3 -0
- package/dist/esm/redaction/knownStrings.js.map +1 -1
- package/dist/identity/types.js.map +1 -1
- package/dist/identity/withCredentialRetry.js +16 -3
- package/dist/identity/withCredentialRetry.js.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/boundedJson.js +35 -1
- package/dist/lib/boundedJson.js.map +1 -1
- package/dist/mcp/gatewayTransport.js +35 -2
- package/dist/mcp/gatewayTransport.js.map +1 -1
- package/dist/mcp/replay.js +14 -10
- package/dist/mcp/replay.js.map +1 -1
- package/dist/mcp/replayUnavailable.js +21 -8
- package/dist/mcp/replayUnavailable.js.map +1 -1
- package/dist/mcp/throttleRetry.js +5 -5
- package/dist/provider-contract/codeSandbox.js +125 -0
- package/dist/provider-contract/codeSandbox.js.map +1 -0
- package/dist/provider-contract/sessionExpired.js +44 -0
- package/dist/provider-contract/sessionExpired.js.map +1 -0
- package/dist/provider-contract/types.js +12 -1
- package/dist/provider-contract/types.js.map +1 -1
- package/dist/redaction/knownStrings.js +3 -0
- package/dist/redaction/knownStrings.js.map +1 -1
- package/dist/types/adapters/code/agentcore.d.ts +65 -68
- package/dist/types/adapters/code/agentcore.d.ts.map +1 -1
- package/dist/types/adapters/code/agentcoreClient.d.ts +194 -0
- package/dist/types/adapters/code/agentcoreClient.d.ts.map +1 -0
- package/dist/types/adapters/code/agentcoreWire.d.ts +120 -0
- package/dist/types/adapters/code/agentcoreWire.d.ts.map +1 -0
- package/dist/types/adapters/code/fileName.d.ts +44 -0
- package/dist/types/adapters/code/fileName.d.ts.map +1 -0
- package/dist/types/adapters/code/local.d.ts +91 -12
- package/dist/types/adapters/code/local.d.ts.map +1 -1
- package/dist/types/adapters/code/output.d.ts +71 -0
- package/dist/types/adapters/code/output.d.ts.map +1 -0
- package/dist/types/answer-account/templates.d.ts +1 -1
- package/dist/types/answer-account/templates.d.ts.map +1 -1
- package/dist/types/answer-validation/types.d.ts +10 -0
- package/dist/types/answer-validation/types.d.ts.map +1 -1
- package/dist/types/core/agent/assessment/assess.d.ts +6 -3
- package/dist/types/core/agent/assessment/assess.d.ts.map +1 -1
- package/dist/types/core/agent/assessment/reasons.d.ts.map +1 -1
- package/dist/types/core/agent/assessment/sources.d.ts +26 -1
- package/dist/types/core/agent/assessment/sources.d.ts.map +1 -1
- package/dist/types/core/agent/assessment/types.d.ts +3 -1
- package/dist/types/core/agent/assessment/types.d.ts.map +1 -1
- package/dist/types/core/agent/negativeClaims/delivery.d.ts +9 -1
- package/dist/types/core/agent/negativeClaims/delivery.d.ts.map +1 -1
- package/dist/types/core/codeRunnerTool.d.ts +15 -3
- package/dist/types/core/codeRunnerTool.d.ts.map +1 -1
- package/dist/types/doors/providers.d.ts +1 -1
- package/dist/types/doors/providers.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +16 -2
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/identity/types.d.ts +12 -0
- package/dist/types/identity/types.d.ts.map +1 -1
- package/dist/types/identity/withCredentialRetry.d.ts +5 -0
- package/dist/types/identity/withCredentialRetry.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/boundedJson.d.ts +27 -0
- package/dist/types/lib/boundedJson.d.ts.map +1 -1
- package/dist/types/mcp/gatewayTransport.d.ts +8 -0
- package/dist/types/mcp/gatewayTransport.d.ts.map +1 -1
- package/dist/types/mcp/replay.d.ts +21 -13
- package/dist/types/mcp/replay.d.ts.map +1 -1
- package/dist/types/mcp/replayUnavailable.d.ts +21 -8
- package/dist/types/mcp/replayUnavailable.d.ts.map +1 -1
- package/dist/types/mcp/throttleRetry.d.ts +5 -5
- package/dist/types/mcp/types.d.ts +13 -10
- package/dist/types/mcp/types.d.ts.map +1 -1
- package/dist/types/provider-contract/codeSandbox.d.ts +90 -0
- package/dist/types/provider-contract/codeSandbox.d.ts.map +1 -0
- package/dist/types/provider-contract/sessionExpired.d.ts +32 -0
- package/dist/types/provider-contract/sessionExpired.d.ts.map +1 -0
- package/dist/types/provider-contract/types.d.ts +166 -17
- package/dist/types/provider-contract/types.d.ts.map +1 -1
- package/dist/types/redaction/knownStrings.d.ts.map +1 -1
- package/package.json +1 -1
package/CAPABILITIES.md
CHANGED
|
@@ -20,6 +20,7 @@ not in this table, search `src/index.ts` for the nearest noun before writing cod
|
|
|
20
20
|
|
|
21
21
|
| If you are about to build… | It is | Where | Since |
|
|
22
22
|
|---|---|---|---|
|
|
23
|
+
| moving files in and out of a code sandbox exactly and bounded, and running shell commands in it — staging text as text and bytes as bytes (AgentCore: one `writeFiles` call, `blob` for bytes, refused over the 100 MB per-call limit, `AF_STAGED_INPUTS` filled as the local runner fills it); reading ONE file back whole or not at all (paired to its path by the returned URI, a missing or different one refused; text or bytes by the field the backend filled, never the extension; over `maxBytes` refused, never cut); a shell command with bounded output, the cut stated, a checkpoint read before the cut, and an exit status only when the backend reported one — off unless the runner opts in; one path rule for both runners; a gone AgentCore session as one typed error; an outcome nobody reported kept unknown (`CodeResult.ok` absent, never success), and a failed program (`exitCode`) told apart from an unconfirmed run (`serviceError`) and a stopped process (`terminatedBy`) in the result and in the `tools.code_run` record | `CodeSession.readFile` + `CodeSession.runCommand` (runner option `commands: true`) + `canReadCodeFiles` / `canRunCodeCommands` + `CodeSandboxError` + `SessionExpiredError` + `sandboxPath` (`agentfootprint`); `agentCoreCodeRunner` / `localCodeRunner` (`agentfootprint/providers`) | `src/adapters/code/` | 12.0.0 |
|
|
23
24
|
| tuning how an MCP call waits before asking again — the first wait, how it grows, jitter, a cap on the scheduled wait, a deadline for the whole call — and keeping a 429 retry and AgentCore Runtime's session-busy retry from multiplying requests: each call is ONE footprintjs `attempt()` series, the 429 rule JOINS a transport's own series instead of wrapping it (one attempt count, one waiting total, one deadline), a `Retry-After` is waited whole (never cut by the cap), and `onRetry` reports the refusal's `kind` but never arguments or answers | `retryOnThrottle` / `agentCoreRuntimeMcpTransport({ retry })` / `retryingFetch(fetch, options)`: `baseDelayMs`, `multiplier`, `jitter` (none, equal or full), `maxDelayMs`, `maxElapsedMs` beside `maxAttempts`, `maxWaitMs`, `onRetry` (`agentfootprint/providers`); AgentCore default 500 ms doubling, 5 attempts = 3.75–7.5 s of waiting | `src/mcp/throttleRetry.ts` | 11.2.0 |
|
|
24
25
|
| deciding which MCP tool calls are sent again after a 429 or a lost connection — permission declared by the APP per (server, tool) at registration, never by the model, a tool description or server annotations (`readOnlyHint`); a generic 429 is not proof nothing ran, so `'app-authorized'` (opt-in) sends a call again only on the kinds its entry names (`throttle`, `network`) and otherwise returns the refusal (`not-permitted`); today's default stays as the NAMED `'compatibility'` policy, every retry recording its assumption (`permission: { by: 'compatibility', assumes: 'refused-before-execution' }`); the adapter you chose retries its own refusal (AgentCore session-busy) as its POLICY, recording `permission: { by: 'adapter', assumes: 'refused-before-execution' }` — never proof: a busy-shaped reply can come from a server that ran the call, so every attempt sent that did not complete is `unknown` and `none` is only a request never sent; `retryOnThrottle: false` turns off the 429 rule only — a declared `network` repeat keeps its series, budget and record; the side-effect guard — a tool declared `changes: true` is never sent again unless the server is declared to deduplicate by a key the call carries (`changes-without-key`); each attempt records failure, effect (`none`/`unknown`/`applied`, footprintjs's law) and permission apart, plus `durationMs`; the end counts the requests sent and keeps uncertainty (a later refusal or success never resolves an earlier `unknown`; `applied` = the call completed, never proof of a write); under `'app-authorized'` a received refusal or error (429, 503, JSON-RPC or tool error — or a chain of them) keeps the server's error, effect `unknown`, while a call that genuinely lacked an answer (an attempt with no response, or a request accepted over HTTP — 202, a stream — whose JSON-RPC answer never came: the ledger watches it by id) and then fails, fails as "may have run" (footprintjs `UnknownEffectError`, its final error the cause, `mayHaveRun: true`); `'compatibility'` keeps today's errors; `'app-authorized'` never silently weakens: it needs each request matched to its call (a library-private `_meta` marker, stripped before sending, so a copying SDK still matches) and each answer seen (checked when the `initialize` answer passes the observer), or the call is refused BEFORE it is sent with `McpReplayUnavailableError` (`sent: false`, `reason`: `answers-unobserved` for an SDK whose `connect` replaces `onmessage`, as 1.10's does; `request-unmatched` for one that drops `_meta`; `requests-unwatched` for stdio); an answer's watch ends by request id (answer, SDK cancellation, call settled, client closed), never with the HTTP send alone; a repeat re-sends the same arguments, server, session and identity | `mcpClient({ replay: { policy, tools: { [name]: { on, changes } }, deduplicates: { argument } } })` + `McpReplayOptions` + `McpReplayUnavailableError` (`agentfootprint/providers`); `effect` / `permission` / `durationMs` on `agentfootprint.tools.request_attempts` rows; `attempts` / `effect` / `mayHaveRun` on its `end` | `src/mcp/replay.ts` | 11.5.0 |
|
|
25
26
|
| saving what an MCP call's retries did into the run that made the call — every attempt, why the series went on or stopped after it, each wait and any stated `Retry-After`, and how the request ended, a cancellation DURING A WAIT included — so a saved and re-read recording gives it back; concurrent calls and runs on one connection never mix; past 100 attempts the rest are counted (`omitted`), never dropped; a 503 received is `answered` with its status, never success and never retried; allow-listed metadata only (a thrown error is a fixed sentence and its category; the endpoint is the declared `endpointLabel`, else its origin; a filing is read as plain data). Every request sent is a row or counted; one cut in flight is `cut`, `remote: 'unknown'` (never recorded as not run), and only a request never sent has no row. Filed ONCE per request by the call's own footprintjs `attempt()` series (no second loop) through an internal per-dispatch port (`REQUEST_EVIDENCE`, not public API) and a per-connection ledger: the call's private `_meta` marker (taken out before sending) → JSON-RPC id → series. An MCP tool call honours its `ctx.signal` (the SDK request is cancelled, `notifications/cancelled` sent), its series stops with it, a wait included, filed the moment it lands, and a call that settles first cuts its series inside the call — never filed after its run, nothing re-sent | `agentfootprint.tools.request_attempts` / `SeriesEvidence` + `RequestLedger` | `src/mcp/` | 11.3.0 |
|
|
@@ -11,43 +11,70 @@
|
|
|
11
11
|
* network-and-filesystem-isolated environment, and the tool code is identical
|
|
12
12
|
* across the swap.
|
|
13
13
|
*
|
|
14
|
-
* ── The
|
|
15
|
-
* Pinned in `test/adapters/aws/awsCommandPin.ts
|
|
16
|
-
* install of `@aws-sdk/client-bedrock-agentcore`
|
|
14
|
+
* ── The operations, verified against the SDK ────────────────────────────────
|
|
15
|
+
* Pinned in `test/adapters/aws/awsCommandPin.ts`. The three commands were
|
|
16
|
+
* verified against a real install of `@aws-sdk/client-bedrock-agentcore`
|
|
17
|
+
* 3.1108.0, and the four `InvokeCodeInterpreter` tool shapes below against
|
|
18
|
+
* 3.1159.0:
|
|
17
19
|
*
|
|
18
20
|
* • `StartCodeInterpreterSessionCommand`
|
|
19
21
|
* in `{ codeInterpreterIdentifier, name?, sessionTimeoutSeconds? }`
|
|
20
22
|
* out `{ codeInterpreterIdentifier, sessionId, createdAt }`
|
|
21
|
-
* • `InvokeCodeInterpreterCommand`
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* • `InvokeCodeInterpreterCommand` — `{ codeInterpreterIdentifier, sessionId,
|
|
24
|
+
* name, arguments }`, one service tool per call:
|
|
25
|
+
* `executeCode` `{ code, language? }` → `execute`
|
|
26
|
+
* `writeFiles` `{ content: [{ path, text } | { path, blob }] }` → `stageInputs`
|
|
27
|
+
* `readFiles` `{ paths: [path] }` → `readFile`
|
|
28
|
+
* `executeCommand` `{ command }` → `runCommand`
|
|
29
|
+
* out `{ sessionId, stream }` — an **event stream**, not a body
|
|
25
30
|
* • `StopCodeInterpreterSessionCommand`
|
|
26
31
|
* in `{ codeInterpreterIdentifier, sessionId }` ← the session ID, not a URI
|
|
27
|
-
* out `{ codeInterpreterIdentifier, sessionId, lastUpdatedAt }`
|
|
28
32
|
*
|
|
29
|
-
* **`Invoke` answers with an `AsyncIterable
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* four more. An adapter that read a `body` field would find `undefined` and
|
|
34
|
-
* report an empty success on every call, and one that iterated only for
|
|
35
|
-
* `result` would treat an AccessDenied as "the code printed nothing." So this
|
|
36
|
-
* drains the stream, raises the exception members BY NAME, and folds the
|
|
37
|
-
* result members into one `CodeResult`.
|
|
33
|
+
* **`Invoke` answers with an `AsyncIterable`** whose members are `{ result }`
|
|
34
|
+
* or a modelled EXCEPTION, and `./agentcoreClient.ts` drains it: exception
|
|
35
|
+
* members raise BY NAME, result members fold into one answer — and nothing
|
|
36
|
+
* the stream did not carry is filled in.
|
|
38
37
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
38
|
+
* ── Results say only what the service reported ──────────────────────────────
|
|
39
|
+
* `CodeResult.ok` is `true` or `false` only when the service said which: an
|
|
40
|
+
* exit status, or its own `isError` verdict. When it reported neither, `ok`
|
|
41
|
+
* is ABSENT — unknown. (It used to be `true`: success from silence.) A
|
|
42
|
+
* command's `exitCode` is the reported one or absent, never read off `isError`.
|
|
43
|
+
*
|
|
44
|
+
* ── Files in and out, exactly and bounded ───────────────────────────────────
|
|
45
|
+
* `stageInputs` writes TEXT as text and BYTES as a blob — never base64 inside
|
|
46
|
+
* text with a decode step — in ONE `writeFiles` call, refused before it is
|
|
47
|
+
* sent when its body would pass AgentCore's 100 MB per-call limit. AgentCore
|
|
48
|
+
* takes no environment, so `AF_STAGED_INPUTS` reaches code by a setup
|
|
49
|
+
* execution before EVERY run (confirmed by the line it prints; the runtime is
|
|
50
|
+
* mutable by the code it runs, so an earlier setup proves nothing now) and
|
|
51
|
+
* reaches a command as an `export` line ahead of it. `readFile` reads ONE path
|
|
52
|
+
* and pairs the file to it by the URI the service returned — one `file:///`
|
|
53
|
+
* form, read one way, a raw query or fragment refused — failing closed on a
|
|
54
|
+
* missing or different one; text or bytes is whichever field the service
|
|
55
|
+
* filled, and a file over `maxBytes` is refused, never cut.
|
|
56
|
+
*
|
|
57
|
+
* ── Commands are opt-in ─────────────────────────────────────────────────────
|
|
58
|
+
* `runCommand` runs arbitrary shell, so a session has it only when the runner
|
|
59
|
+
* is built with `commands: true`. The sandbox's network is whatever the code
|
|
60
|
+
* interpreter resource was created with — the built-in `aws.codeinterpreter.v1`
|
|
61
|
+
* runs with AWS's most restrictive defaults (Sandbox mode: limited network
|
|
62
|
+
* access, Amazon S3 included); a custom interpreter may be Public (internet)
|
|
63
|
+
* or VPC. This adapter can neither see nor change that.
|
|
64
|
+
*
|
|
65
|
+
* ── A session that is gone says so, once ────────────────────────────────────
|
|
66
|
+
* A service error naming the session as "not active" becomes
|
|
67
|
+
* {@link SessionExpiredError} on every verb, and so does a
|
|
68
|
+
* `ResourceNotFoundException` on a verb that addresses nothing but the session
|
|
69
|
+
* (code, a command, a write). On a READ, "not found" may mean the file, so it
|
|
70
|
+
* is `CodeSandboxError` (`'file-not-returned'`), naming both possibilities —
|
|
71
|
+
* never offered as proof the session is gone.
|
|
43
72
|
*
|
|
44
73
|
* ── How this talks to the SDK (the 9.4.0 law) ───────────────────────────────
|
|
45
74
|
* Through `client.send(new SomeCommand(input))`, never a method on the client.
|
|
46
75
|
* A bare `@aws-sdk/client-*` **Client is command-based**: its prototype carries
|
|
47
|
-
* `send` and `destroy` and nothing else
|
|
48
|
-
*
|
|
49
|
-
* `undefined`; the shortcut exists only on the aggregated `BedrockAgentCore`).
|
|
50
|
-
* Three adapters have shipped that bug in this package.
|
|
76
|
+
* `send` and `destroy` and nothing else. Three adapters have shipped that bug
|
|
77
|
+
* in this package.
|
|
51
78
|
*
|
|
52
79
|
* ── Credentials ─────────────────────────────────────────────────────────────
|
|
53
80
|
* Standard AWS credential resolution (the SDK's own chain). A tool holding a
|
|
@@ -60,17 +87,23 @@
|
|
|
60
87
|
*/
|
|
61
88
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
62
89
|
exports.agentCoreCodeRunner = void 0;
|
|
90
|
+
const codeSandbox_js_1 = require("../../provider-contract/codeSandbox.js");
|
|
91
|
+
const sessionExpired_js_1 = require("../../provider-contract/sessionExpired.js");
|
|
63
92
|
const toolSessions_js_1 = require("../../tools/toolSessions.js");
|
|
64
|
-
const
|
|
93
|
+
const abort_js_1 = require("../../lib/abort.js");
|
|
94
|
+
const agentcoreClient_js_1 = require("./agentcoreClient.js");
|
|
95
|
+
const agentcoreWire_js_1 = require("./agentcoreWire.js");
|
|
96
|
+
const fileName_js_1 = require("./fileName.js");
|
|
97
|
+
const output_js_1 = require("./output.js");
|
|
65
98
|
const DEFAULT_MAX_OUTPUT_CHARS = 8_000;
|
|
66
|
-
/** The
|
|
67
|
-
const
|
|
99
|
+
/** The adapter's own name, on every refusal it raises. */
|
|
100
|
+
const BACKEND = 'agentCoreCodeRunner';
|
|
68
101
|
function agentCoreCodeRunner(options) {
|
|
69
102
|
const id = options.id ?? 'agentcore-code-runner';
|
|
70
103
|
const maxOutputChars = options.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
71
104
|
let client;
|
|
72
105
|
const resolve = () => {
|
|
73
|
-
client ??= options._client ?? createCodeClient(options);
|
|
106
|
+
client ??= options._client ?? (0, agentcoreClient_js_1.createCodeClient)(options);
|
|
74
107
|
return client;
|
|
75
108
|
};
|
|
76
109
|
return {
|
|
@@ -94,66 +127,242 @@ function agentCoreCodeRunner(options) {
|
|
|
94
127
|
'Nothing can be invoked or stopped without one, so this is raised rather than ' +
|
|
95
128
|
'carried forward as an unusable session.');
|
|
96
129
|
}
|
|
97
|
-
|
|
130
|
+
return openSession({
|
|
131
|
+
api,
|
|
132
|
+
identifier: options.identifier,
|
|
133
|
+
sessionId,
|
|
134
|
+
language: req.language ?? options.language,
|
|
135
|
+
maxOutputChars,
|
|
136
|
+
commands: options.commands === true,
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
exports.agentCoreCodeRunner = agentCoreCodeRunner;
|
|
142
|
+
/** The session behind {@link agentCoreCodeRunner}: the verbs, in the order a caller uses them. */
|
|
143
|
+
function openSession(plan) {
|
|
144
|
+
const { api, identifier, sessionId, maxOutputChars } = plan;
|
|
145
|
+
const target = { codeInterpreterIdentifier: identifier, sessionId };
|
|
146
|
+
let stopped = false;
|
|
147
|
+
/** name → path, exactly as the manifest carries it. */
|
|
148
|
+
const staged = new Map();
|
|
149
|
+
const refuseIfStopped = (toDo) => {
|
|
150
|
+
if (stopped) {
|
|
151
|
+
throw new Error(`agentCoreCodeRunner: session ${sessionId} was stopped; open a new one to ${toDo}.`);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
const manifestJson = () => staged.size > 0 ? JSON.stringify(Object.fromEntries(staged)) : undefined;
|
|
155
|
+
/**
|
|
156
|
+
* One deadline and one cancellation for a whole verb. A verb that makes two
|
|
157
|
+
* calls — the manifest setup, then the code — spends ONE `timeoutMs`, not
|
|
158
|
+
* one each. A deadline or the caller's own signal stops the WAIT at once
|
|
159
|
+
* (`lib/abort.ts`); what the service does with a request it already
|
|
160
|
+
* received is its own business, and the timeout error says so.
|
|
161
|
+
*/
|
|
162
|
+
const withinBounds = async (verb, bounds, work) => {
|
|
163
|
+
(0, abort_js_1.throwIfAborted)(bounds.signal);
|
|
164
|
+
const { controller, unlink } = (0, abort_js_1.linkedController)(bounds.signal);
|
|
165
|
+
const disarm = bounds.timeoutMs === undefined
|
|
166
|
+
? undefined
|
|
167
|
+
: (0, abort_js_1.armDeadline)(controller, bounds.timeoutMs, () => new codeSandbox_js_1.CodeSandboxError('timed-out', `agentCoreCodeRunner: ${verb} did not answer within ${bounds.timeoutMs}ms ` +
|
|
168
|
+
'and was abandoned. The session was not stopped, so what was sent may still ' +
|
|
169
|
+
'be running in it — whether it finished is not known.'));
|
|
170
|
+
try {
|
|
171
|
+
return await work(controller.signal);
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
disarm?.();
|
|
175
|
+
unlink();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* One invoke under `signal`. A gone session becomes `SessionExpiredError`;
|
|
180
|
+
* a modelled service error on a file or command call is rewritten without
|
|
181
|
+
* the service's words (they can quote what was sent).
|
|
182
|
+
*/
|
|
183
|
+
const invoke = async (input, signal) => {
|
|
184
|
+
(0, abort_js_1.throwIfAborted)(signal);
|
|
185
|
+
try {
|
|
186
|
+
return await (0, abort_js_1.untilAborted)(api.invoke(input, { abortSignal: signal }), signal);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
if (signal.aborted)
|
|
190
|
+
throw (0, abort_js_1.abortErrorOf)(signal);
|
|
191
|
+
const evidence = (0, agentcoreWire_js_1.sessionExpiryEvidence)(err, input.name);
|
|
192
|
+
if (evidence === 'session') {
|
|
193
|
+
throw new sessionExpired_js_1.SessionExpiredError(BACKEND, sessionId, (0, agentcoreWire_js_1.errorNameOf)(err));
|
|
194
|
+
}
|
|
195
|
+
if (evidence === 'file-or-session' && input.name === 'readFiles') {
|
|
196
|
+
// A read names a FILE as well as the session, so "not found" can mean
|
|
197
|
+
// either. It is not taken as proof the session is gone.
|
|
198
|
+
throw new codeSandbox_js_1.CodeSandboxError('file-not-returned', `agentCoreCodeRunner: reading '${input.arguments.paths.join(', ')}' — the service ` +
|
|
199
|
+
'answered ResourceNotFoundException, which can mean the file is missing or the ' +
|
|
200
|
+
'session is gone; which one is not known here.', { path: input.arguments.paths[0] ?? '' });
|
|
201
|
+
}
|
|
202
|
+
if (input.name === 'executeCode')
|
|
203
|
+
throw err;
|
|
204
|
+
throw (0, agentcoreWire_js_1.serviceRefusal)(err, input.name) ?? err;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Set {@link STAGED_INPUTS_ENV} in the runtime `language` runs in — before
|
|
209
|
+
* EVERY execution once anything is staged, and confirmed each time.
|
|
210
|
+
*
|
|
211
|
+
* Never skipped because this process set it before: the runtime is mutable
|
|
212
|
+
* by the very code it runs, so a previous execution may have changed or
|
|
213
|
+
* deleted the variable, and a record of what was once sent is not an
|
|
214
|
+
* observation of what the runtime holds. Re-applying it costs one call per
|
|
215
|
+
* execution, inside the same deadline. Nothing when nothing was staged — a
|
|
216
|
+
* session that never stages sends exactly what it always sent.
|
|
217
|
+
*/
|
|
218
|
+
const exposeManifest = async (language, signal) => {
|
|
219
|
+
const json = manifestJson();
|
|
220
|
+
if (json === undefined)
|
|
221
|
+
return;
|
|
222
|
+
const runtime = (0, agentcoreWire_js_1.manifestRuntime)(language);
|
|
223
|
+
if (runtime === undefined) {
|
|
224
|
+
throw new codeSandbox_js_1.CodeSandboxError('staged-inputs-unset', `agentCoreCodeRunner: inputs were staged, and this adapter does not know how to set ` +
|
|
225
|
+
`AF_STAGED_INPUTS for language '${String(language)}', so the code is not run against ` +
|
|
226
|
+
'a manifest it could not read. AgentCore runs python, javascript and typescript.');
|
|
227
|
+
}
|
|
228
|
+
const answer = await invoke({
|
|
229
|
+
...target,
|
|
230
|
+
name: 'executeCode',
|
|
231
|
+
arguments: { code: (0, agentcoreWire_js_1.manifestSetupCode)(runtime, json), ...(language ? { language } : {}) },
|
|
232
|
+
}, signal);
|
|
233
|
+
// The setup prints one line once the variable is set — it proves that
|
|
234
|
+
// checkpoint and nothing else, which is all that is asked of it.
|
|
235
|
+
if (!(0, output_js_1.printedLine)(answer.stdout, agentcoreWire_js_1.MANIFEST_SET_MARKER)) {
|
|
236
|
+
throw new codeSandbox_js_1.CodeSandboxError('staged-inputs-unset', `agentCoreCodeRunner: the staged inputs are in the session, but setting AF_STAGED_INPUTS ` +
|
|
237
|
+
`in its ${runtime} runtime was not confirmed, so the code is not run against a ` +
|
|
238
|
+
'manifest it may not be able to read.');
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const session = {
|
|
242
|
+
id: sessionId,
|
|
243
|
+
async execute(exec) {
|
|
244
|
+
refuseIfStopped('run more code');
|
|
245
|
+
const language = exec.language ?? plan.language;
|
|
246
|
+
const bounds = {
|
|
247
|
+
...(exec.timeoutMs !== undefined && { timeoutMs: exec.timeoutMs }),
|
|
248
|
+
...(exec.signal && { signal: exec.signal }),
|
|
249
|
+
};
|
|
250
|
+
const answer = await withinBounds('executeCode', bounds, async (signal) => {
|
|
251
|
+
await exposeManifest(language, signal);
|
|
252
|
+
return invoke({
|
|
253
|
+
...target,
|
|
254
|
+
name: 'executeCode',
|
|
255
|
+
arguments: { code: exec.code, ...(language ? { language } : {}) },
|
|
256
|
+
}, signal);
|
|
257
|
+
});
|
|
258
|
+
const bounded = (0, output_js_1.boundOutput)(answer.stdout, answer.stderr, maxOutputChars);
|
|
259
|
+
const ok = reportedOutcome(answer);
|
|
260
|
+
// An error flag with no exit status is the SERVICE's verdict, not the
|
|
261
|
+
// program's: whether the code ran is unconfirmed, and the result says
|
|
262
|
+
// which kind of `false` it is.
|
|
263
|
+
const serviceError = answer.isError === true && answer.exitCode === undefined;
|
|
98
264
|
return {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
codeInterpreterIdentifier: options.identifier,
|
|
106
|
-
sessionId,
|
|
107
|
-
name: EXECUTE_CODE,
|
|
108
|
-
arguments: {
|
|
109
|
-
code: exec.code,
|
|
110
|
-
...(exec.language ?? req.language ?? options.language
|
|
111
|
-
? { language: exec.language ?? req.language ?? options.language ?? 'python' }
|
|
112
|
-
: {}),
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
const stdout = cut(answer.stdout, maxOutputChars);
|
|
116
|
-
const stderr = cut(answer.stderr, maxOutputChars);
|
|
117
|
-
const truncated = {
|
|
118
|
-
...(stdout.cut && { stdout: true }),
|
|
119
|
-
...(stderr.cut && { stderr: true }),
|
|
120
|
-
...((stdout.cut || stderr.cut) && {
|
|
121
|
-
ofChars: Math.max(answer.stdout.length, answer.stderr.length),
|
|
122
|
-
}),
|
|
123
|
-
};
|
|
124
|
-
return {
|
|
125
|
-
// `isError` is the service's own verdict; a non-zero exit is ours.
|
|
126
|
-
ok: answer.isError !== true && (answer.exitCode ?? 0) === 0,
|
|
127
|
-
stdout: stdout.text,
|
|
128
|
-
stderr: stderr.text,
|
|
129
|
-
...(answer.exitCode !== undefined && { exitCode: answer.exitCode }),
|
|
130
|
-
...(Object.keys(truncated).length > 0 && { truncated }),
|
|
131
|
-
};
|
|
132
|
-
},
|
|
133
|
-
async stop() {
|
|
134
|
-
if (stopped)
|
|
135
|
-
return;
|
|
136
|
-
stopped = true;
|
|
137
|
-
try {
|
|
138
|
-
await api.stopSession({
|
|
139
|
-
codeInterpreterIdentifier: options.identifier,
|
|
140
|
-
sessionId,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
catch (err) {
|
|
144
|
-
// "Already gone" is the EXPECTED shape here, not an incident: the
|
|
145
|
-
// session TTL is enforced by AWS, so a run that idled past it finds
|
|
146
|
-
// the session collected. Anything else is re-raised, and the tier
|
|
147
|
-
// reports it on `tools.session_close_failed`.
|
|
148
|
-
if (!isAlreadyGone(err))
|
|
149
|
-
throw err;
|
|
150
|
-
}
|
|
151
|
-
},
|
|
265
|
+
...(ok !== undefined && { ok }),
|
|
266
|
+
stdout: bounded.stdout,
|
|
267
|
+
stderr: bounded.stderr,
|
|
268
|
+
...(answer.exitCode !== undefined && { exitCode: answer.exitCode }),
|
|
269
|
+
...(serviceError && { serviceError: true }),
|
|
270
|
+
...(bounded.truncated !== undefined && { truncated: bounded.truncated }),
|
|
152
271
|
};
|
|
153
272
|
},
|
|
273
|
+
async stageInputs(inputs) {
|
|
274
|
+
refuseIfStopped('stage inputs');
|
|
275
|
+
if (inputs.length === 0)
|
|
276
|
+
return [];
|
|
277
|
+
// The name is CALLER data landing in a filesystem, so it is ENCODED
|
|
278
|
+
// into one inert segment — the same encoder `localCodeRunner` uses, so
|
|
279
|
+
// a name lands under the same file name on either backend.
|
|
280
|
+
const landing = inputs.map((input) => ({
|
|
281
|
+
input,
|
|
282
|
+
path: `${agentcoreWire_js_1.STAGED_INPUTS_DIR}/${(0, fileName_js_1.safeFileName)(input.fileName ?? input.name)}`,
|
|
283
|
+
}));
|
|
284
|
+
const content = landing.map(({ input, path }) => (0, agentcoreWire_js_1.fileContent)(path, input.data));
|
|
285
|
+
const bytes = (0, agentcoreWire_js_1.writeFilesRequestBytes)(content);
|
|
286
|
+
if (bytes > agentcoreWire_js_1.AGENTCORE_MAX_REQUEST_BYTES) {
|
|
287
|
+
throw new codeSandbox_js_1.CodeSandboxError('request-too-large', `agentCoreCodeRunner: staging these ${inputs.length} input(s) would send a ` +
|
|
288
|
+
`${bytes}-byte request, and AgentCore takes at most ${agentcoreWire_js_1.AGENTCORE_MAX_REQUEST_BYTES} ` +
|
|
289
|
+
'bytes in one call. Nothing was sent. Stage fewer or smaller inputs per call, or ' +
|
|
290
|
+
'have the code fetch large data itself (from Amazon S3, for example).', { bytes, limit: agentcoreWire_js_1.AGENTCORE_MAX_REQUEST_BYTES });
|
|
291
|
+
}
|
|
292
|
+
const answer = await withinBounds('writeFiles', {}, (signal) => invoke({ ...target, name: 'writeFiles', arguments: { content } }, signal));
|
|
293
|
+
if (answer.isError === true) {
|
|
294
|
+
throw new codeSandbox_js_1.CodeSandboxError('write-failed', 'agentCoreCodeRunner: the service flagged the write of the staged inputs as an error, ' +
|
|
295
|
+
'so which of them landed is not known. Its words are not repeated here.');
|
|
296
|
+
}
|
|
297
|
+
for (const { input, path } of landing)
|
|
298
|
+
staged.set(input.name, path);
|
|
299
|
+
return landing.map(({ input, path }) => ({
|
|
300
|
+
name: input.name,
|
|
301
|
+
path,
|
|
302
|
+
bytes: (0, output_js_1.payloadBytes)(input.data),
|
|
303
|
+
}));
|
|
304
|
+
},
|
|
305
|
+
async readFile(path, readOptions) {
|
|
306
|
+
refuseIfStopped('read files');
|
|
307
|
+
const maxBytes = (0, codeSandbox_js_1.readBound)(readOptions);
|
|
308
|
+
const relative = (0, codeSandbox_js_1.sandboxPath)(path);
|
|
309
|
+
const answer = await withinBounds('readFiles', { ...(readOptions.signal && { signal: readOptions.signal }) }, (signal) => invoke({ ...target, name: 'readFiles', arguments: { paths: [relative] } }, signal));
|
|
310
|
+
return (0, agentcoreWire_js_1.pairReadFile)(relative, answer, maxBytes);
|
|
311
|
+
},
|
|
312
|
+
...(plan.commands && {
|
|
313
|
+
async runCommand(command, commandOptions) {
|
|
314
|
+
refuseIfStopped('run commands');
|
|
315
|
+
const bounds = (0, output_js_1.commandBounds)(command, commandOptions, maxOutputChars);
|
|
316
|
+
const json = manifestJson();
|
|
317
|
+
const answer = await withinBounds('executeCommand', {
|
|
318
|
+
...(bounds.timeoutMs !== undefined && { timeoutMs: bounds.timeoutMs }),
|
|
319
|
+
...(bounds.signal && { signal: bounds.signal }),
|
|
320
|
+
}, (signal) => invoke({
|
|
321
|
+
...target,
|
|
322
|
+
name: 'executeCommand',
|
|
323
|
+
arguments: {
|
|
324
|
+
command: json === undefined ? command : (0, agentcoreWire_js_1.withManifestExport)(command, json),
|
|
325
|
+
},
|
|
326
|
+
}, signal));
|
|
327
|
+
return (0, output_js_1.commandResult)(answer, bounds);
|
|
328
|
+
},
|
|
329
|
+
}),
|
|
330
|
+
async stop() {
|
|
331
|
+
if (stopped)
|
|
332
|
+
return;
|
|
333
|
+
stopped = true;
|
|
334
|
+
staged.clear();
|
|
335
|
+
try {
|
|
336
|
+
await api.stopSession({ codeInterpreterIdentifier: identifier, sessionId });
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
// "Already gone" is the EXPECTED shape here, not an incident: the
|
|
340
|
+
// session TTL is enforced by AWS, so a run that idled past it finds
|
|
341
|
+
// the session collected. Anything else is re-raised, and the tier
|
|
342
|
+
// reports it on `tools.session_close_failed`.
|
|
343
|
+
if (!isAlreadyGone(err))
|
|
344
|
+
throw err;
|
|
345
|
+
}
|
|
346
|
+
},
|
|
154
347
|
};
|
|
348
|
+
return session;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Did the code complete — as far as the service SAID?
|
|
352
|
+
*
|
|
353
|
+
* Its own error verdict first, then an exit status, then its explicit
|
|
354
|
+
* "not an error". When it said none of those, the answer is `undefined`:
|
|
355
|
+
* unknown, never success from silence.
|
|
356
|
+
*/
|
|
357
|
+
function reportedOutcome(answer) {
|
|
358
|
+
if (answer.isError === true)
|
|
359
|
+
return false;
|
|
360
|
+
if (answer.exitCode !== undefined)
|
|
361
|
+
return answer.exitCode === 0;
|
|
362
|
+
if (answer.isError === false)
|
|
363
|
+
return true;
|
|
364
|
+
return undefined;
|
|
155
365
|
}
|
|
156
|
-
exports.agentCoreCodeRunner = agentCoreCodeRunner;
|
|
157
366
|
/**
|
|
158
367
|
* A label for the remote session — derived FROM the isolation key, carrying
|
|
159
368
|
* none of it.
|
|
@@ -181,7 +390,8 @@ exports.agentCoreCodeRunner = agentCoreCodeRunner;
|
|
|
181
390
|
* nothing — here or in the SDK — ever looks a session up by it. The address is
|
|
182
391
|
* the service-assigned `sessionId`, which `start()` reads back and passes to
|
|
183
392
|
* every `Invoke` and `Stop` verbatim. So no stored state is keyed on this
|
|
184
|
-
* string and relabelling migrates nothing
|
|
393
|
+
* string and relabelling migrates nothing — and no session is ever reattached
|
|
394
|
+
* by it.
|
|
185
395
|
*/
|
|
186
396
|
function sessionName(key) {
|
|
187
397
|
return `af-${(0, toolSessions_js_1.hashSessionKey)(key)}`;
|
|
@@ -190,133 +400,4 @@ function isAlreadyGone(err) {
|
|
|
190
400
|
const name = err instanceof Error ? err.name : '';
|
|
191
401
|
return name === 'ResourceNotFoundException' || name === 'ValidationException';
|
|
192
402
|
}
|
|
193
|
-
/**
|
|
194
|
-
* Map {@link AgentCoreCodeClientLike} onto the real SDK commands.
|
|
195
|
-
*
|
|
196
|
-
* Two things this function owns and nothing else does: the COMMAND FORM (see
|
|
197
|
-
* the module header), and the DRAIN of `InvokeCodeInterpreterResponse.stream`.
|
|
198
|
-
*/
|
|
199
|
-
function createCodeClient(options) {
|
|
200
|
-
let mod;
|
|
201
|
-
if (options._sdk) {
|
|
202
|
-
mod = options._sdk;
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
try {
|
|
206
|
-
// Lazy peer-dep: only loaded when no _client/_sdk is injected and start() runs.
|
|
207
|
-
mod = (0, lazyRequire_js_1.lazyRequire)('@aws-sdk/client-bedrock-agentcore');
|
|
208
|
-
}
|
|
209
|
-
catch {
|
|
210
|
-
throw new Error('agentCoreCodeRunner requires the `@aws-sdk/client-bedrock-agentcore` peer dependency.\n' +
|
|
211
|
-
' Install: npm install @aws-sdk/client-bedrock-agentcore\n' +
|
|
212
|
-
' Or pass `_client` for a pre-built or mock client.\n' +
|
|
213
|
-
' For a local dev loop with no AWS at all, use `localCodeRunner()`.');
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (!mod.BedrockAgentCoreClient) {
|
|
217
|
-
throw new Error('agentCoreCodeRunner: `@aws-sdk/client-bedrock-agentcore` is installed but ' +
|
|
218
|
-
'`BedrockAgentCoreClient` was not found. Update the SDK.');
|
|
219
|
-
}
|
|
220
|
-
const sdk = new mod.BedrockAgentCoreClient({ ...(options.region && { region: options.region }) });
|
|
221
|
-
const send = async (Ctor, name, input) => {
|
|
222
|
-
if (!Ctor) {
|
|
223
|
-
throw new Error(`agentCoreCodeRunner: \`@aws-sdk/client-bedrock-agentcore\` is missing ${name}. ` +
|
|
224
|
-
'Upgrade the SDK, or pass `_client` with your own mapping.');
|
|
225
|
-
}
|
|
226
|
-
return sdk.send(new Ctor(input));
|
|
227
|
-
};
|
|
228
|
-
return {
|
|
229
|
-
async startSession(input) {
|
|
230
|
-
const r = (await send(mod.StartCodeInterpreterSessionCommand, 'StartCodeInterpreterSessionCommand', input));
|
|
231
|
-
return { ...(r?.sessionId !== undefined && { sessionId: r.sessionId }) };
|
|
232
|
-
},
|
|
233
|
-
async invoke(input) {
|
|
234
|
-
const r = (await send(mod.InvokeCodeInterpreterCommand, 'InvokeCodeInterpreterCommand', input));
|
|
235
|
-
return drainStream(r?.stream);
|
|
236
|
-
},
|
|
237
|
-
async stopSession(input) {
|
|
238
|
-
await send(mod.StopCodeInterpreterSessionCommand, 'StopCodeInterpreterSessionCommand', input);
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
/** Every modelled error member of the stream union, in SDK spelling. */
|
|
243
|
-
const STREAM_ERROR_MEMBERS = [
|
|
244
|
-
'accessDeniedException',
|
|
245
|
-
'conflictException',
|
|
246
|
-
'internalServerException',
|
|
247
|
-
'resourceNotFoundException',
|
|
248
|
-
'serviceQuotaExceededException',
|
|
249
|
-
'throttlingException',
|
|
250
|
-
'validationException',
|
|
251
|
-
];
|
|
252
|
-
/**
|
|
253
|
-
* Drain the event stream into one answer.
|
|
254
|
-
*
|
|
255
|
-
* An ERROR MEMBER RAISES. This is the honesty line for this adapter: the SDK
|
|
256
|
-
* models seven failure shapes as ordinary members of the stream union, so an
|
|
257
|
-
* `AccessDeniedException` arrives as a value, not a rejection. Folding it in
|
|
258
|
-
* as empty output would report a clean run in which the code "printed
|
|
259
|
-
* nothing" — a silent success, at the exact moment an operator most needs the
|
|
260
|
-
* word "AccessDenied".
|
|
261
|
-
*/
|
|
262
|
-
async function drainStream(stream) {
|
|
263
|
-
if (!stream) {
|
|
264
|
-
throw new Error('agentCoreCodeRunner: InvokeCodeInterpreter returned no stream. The response carries ' +
|
|
265
|
-
'its payload as an event stream (`response.stream`), so an absent one is a broken ' +
|
|
266
|
-
'call, not an empty result.');
|
|
267
|
-
}
|
|
268
|
-
let stdout = '';
|
|
269
|
-
let stderr = '';
|
|
270
|
-
let exitCode;
|
|
271
|
-
let isError = false;
|
|
272
|
-
for await (const event of stream) {
|
|
273
|
-
for (const member of STREAM_ERROR_MEMBERS) {
|
|
274
|
-
const raised = event[member];
|
|
275
|
-
if (raised) {
|
|
276
|
-
const error = new Error(`agentCoreCodeRunner: ${member} from InvokeCodeInterpreter — ` +
|
|
277
|
-
`${raised.message ?? 'no message supplied'}`);
|
|
278
|
-
error.name = member.charAt(0).toUpperCase() + member.slice(1);
|
|
279
|
-
throw error;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
const result = event.result;
|
|
283
|
-
if (!result)
|
|
284
|
-
continue;
|
|
285
|
-
if (result.isError === true)
|
|
286
|
-
isError = true;
|
|
287
|
-
const structured = result.structuredContent;
|
|
288
|
-
if (structured) {
|
|
289
|
-
if (structured.stdout !== undefined)
|
|
290
|
-
stdout += structured.stdout;
|
|
291
|
-
if (structured.stderr !== undefined)
|
|
292
|
-
stderr += structured.stderr;
|
|
293
|
-
if (structured.exitCode !== undefined)
|
|
294
|
-
exitCode = structured.exitCode;
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
// Fallback: a response that filled only the typed content blocks. Text
|
|
298
|
-
// blocks are the readable half; a binary/resource block is described by
|
|
299
|
-
// name rather than inlined, because inlining it is the bug this port exists
|
|
300
|
-
// to prevent.
|
|
301
|
-
for (const block of result.content ?? []) {
|
|
302
|
-
if (block.text !== undefined)
|
|
303
|
-
stdout += block.text;
|
|
304
|
-
else if (block.name !== undefined)
|
|
305
|
-
stdout += `[${block.type ?? 'resource'}: ${block.name}]`;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
return {
|
|
310
|
-
stdout,
|
|
311
|
-
stderr,
|
|
312
|
-
...(exitCode !== undefined && { exitCode }),
|
|
313
|
-
...(isError && { isError }),
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
/** Cut to `max` characters, saying whether it cut. */
|
|
317
|
-
function cut(text, max) {
|
|
318
|
-
if (text.length <= max)
|
|
319
|
-
return { text, cut: false };
|
|
320
|
-
return { text: text.slice(0, max), cut: true };
|
|
321
|
-
}
|
|
322
403
|
//# sourceMappingURL=agentcore.js.map
|