View a markdown version of this page

Interactive shells (terminals) - Amazon Bedrock AgentCore
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China (PDF).

Interactive shells (terminals)

The InvokeAgentRuntimeCommandShell operation opens a persistent, interactive terminal inside a running AgentCore harness session over WebSocket. The API identifies this harness session with a runtime session ID. Unlike one-shot command execution, an interactive shell maintains its environment variables, working directory, command history, and running processes across inputs.

Use an interactive shell for debugging, iterative development, or long-running processes when you need to preserve shell state across commands.

For SigV4 and SigV4 pre-signed requests, the caller needs bedrock-agentcore:InvokeAgentRuntimeCommandShell permission on the harness. Bearer-token requests must satisfy the harness inbound authorizer instead.

Important

At launch, AgentCore CLI commands and the high-level AgentCore SDK shell helpers don’t support harness targets. Connect to a harness shell through the InvokeAgentRuntimeCommandShell WebSocket API directly, as shown on this page.

How it works

InvokeAgentRuntimeCommandShell establishes a WebSocket connection to an interactive shell process in the microVM that runs your harness session. The connection uses binary frames to stream terminal input and output in both directions.

If the harness uses the default environment, the shell runs in the managed harness environment. If the harness uses a custom container, the shell runs in that container. Harness interactive terminals currently support Bash only.

The connection supports:

Feature Description

Persistent state

Environment variables, working directory, command history, and running processes persist within the same shell.

Reconnection

Provide the same runtime session ID and shellId to reconnect to a detached shell. The service replays up to 256 KB of buffered output.

Multiple concurrent shells

Open up to 10 active shell sessions on one harness session. Detached shells don’t count toward this limit. Each shell has an independent terminal process and shares the session filesystem.

Prerequisites

  • A harness in READY state

  • Authorization for the selected authentication method: bedrock-agentcore:InvokeAgentRuntimeCommandShell IAM permission for SigV4, or a bearer token accepted by the harness inbound authorizer

  • A WebSocket client that supports binary frames and either request headers or pre-signed URLs

  • For a custom container, Bash installed at /bin/bash. Other shells, including sh and dash, aren’t currently supported.

For more information about the complete harness permissions model, see Required IAM permissions for callers.

Connect through the WebSocket API

Open a WebSocket connection to the following URL:

wss://bedrock-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-harness-arn>/ws/shells

Use the harness ARN, not the ARN of its managed Runtime. URL-encode the complete harness ARN before putting it in the path. A harness ARN has the following format:

arn:aws:bedrock-agentcore:<region>:<accountId>:harness/<harnessId>

Send the following values with the WebSocket upgrade request:

Value Required Description

Authentication

Yes

Sign the request with Signature Version 4 (SigV4), use a SigV4 pre-signed URL, or send the bearer token configured by the harness inbound authorizer in the Authorization header. The SigV4 service name is bedrock-agentcore.

X-Amzn-Bedrock-AgentCore-Runtime-Session-Id header or query parameter

No

Targets an existing harness session or creates one with the supplied ID. The value must contain at least 33 characters. If you omit it, the service creates a runtime session ID and returns it in the response.

Sec-WebSocket-Protocol

No

For SigV4 connections, optionally offer v1.command.agentcore.aws.dev to identify the command-shell binary protocol. Bearer-token connections use the authentication subprotocols described after this table instead.

shellId query parameter

No

Reconnects to an existing shell. Use it with the same runtime session ID that created the shell.

For a browser client that cannot set an Authorization header, base64url-encode the bearer token and offer both base64UrlBearerAuthorization.<encoded-token> and the base64UrlBearerAuthorization sentinel through Sec-WebSocket-Protocol. Do not also offer v1.command.agentcore.aws.dev on a bearer-token connection.

The 101 Switching Protocols response returns the runtime session ID and shell ID in the X-Amzn-Bedrock-AgentCore-Runtime-Session-Id and X-Amzn-Bedrock-AgentCore-Shell-Id headers. Clients that cannot read WebSocket upgrade response headers can read the shell ID from the first STATUS frame. The frame doesn’t contain the runtime session ID, so these clients must generate and retain a runtime session ID before connecting. When you create a pre-signed URL, include the runtime session ID as a query parameter before signing the URL.

The WebSocket upgrade timeout applies only until the service returns 101 Switching Protocols. Shell and custom-container initialization can continue after the upgrade. Wait for the initial STATUS frame before treating the shell as ready, apply a separate application timeout while waiting, and handle close code 1011 as a shell initialization or server failure.

The following Python example uses a generic WebSocket client and botocore only to sign the upgrade request. It doesn’t use an AgentCore SDK shell helper.

pip install "boto3>=1.34" "websockets>=15"
import asyncio import json import urllib.parse import uuid import botocore.session import websockets from botocore.auth import SigV4Auth from botocore.awsrequest import AWSRequest REGION = "us-west-2" HARNESS_ARN = ( "arn:aws:bedrock-agentcore:us-west-2:123456789012:" "harness/MyHarness-a1b2c3d4e5" ) SESSION_ID = str(uuid.uuid4()) # 36 characters SUBPROTOCOL = "v1.command.agentcore.aws.dev" def signed_headers(url): request = AWSRequest( method="GET", url=url, headers={ "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": SESSION_ID, }, ) credentials = botocore.session.Session().get_credentials() SigV4Auth(credentials, "bedrock-agentcore", REGION).add_auth(request) return dict(request.headers) async def main(): encoded_arn = urllib.parse.quote(HARNESS_ARN, safe="") host = f"bedrock-agentcore.{REGION}.amazonaws.com" path = f"/runtimes/{encoded_arn}/ws/shells" https_url = f"https://{host}{path}" websocket_url = f"wss://{host}{path}" async with websockets.connect( websocket_url, additional_headers=signed_headers(https_url), subprotocols=[SUBPROTOCOL], open_timeout=330, ) as websocket: shell_id = websocket.response.headers.get( "X-Amzn-Bedrock-AgentCore-Shell-Id" ) print(f"Connected to shell {shell_id}") # Channel 0x00 carries terminal input. await websocket.send(b"\x00echo 'Hello from my harness'\n") await websocket.send(b"\x00exit\n") async for frame in websocket: if isinstance(frame, str) or not frame: continue channel, payload = frame[0], frame[1:] if channel == 0x01: # PTY output (command stdout and stderr) print(payload.decode("utf-8", errors="replace"), end="") elif channel == 0x02: # Service or platform diagnostics print(payload.decode("utf-8", errors="replace"), end="") elif channel == 0x03: # STATUS status = json.loads(payload) shell_id = status.get("metadata", {}).get("shellId", shell_id) print(f"\nStatus: {status}") asyncio.run(main())

Binary frame protocol

Every application-level message is a binary WebSocket frame. The first byte identifies the channel, and the remaining bytes are the payload.

Channel Direction Payload

0x00 - STDIN

Client to service

Raw terminal input.

0x01 - STDOUT

Service to client

Raw PTY output. The PTY combines command stdout and stderr on this channel.

0x02 - STDERR

Service to client

Service or platform diagnostics. Command stderr is part of the 0x01 PTY output.

0x03 - STATUS

Service to client

UTF-8 JSON containing connection metadata or the shell exit status.

0x04 - RESIZE

Client to service

UTF-8 JSON in the form {"width":120,"height":40}. Each dimension must be from 1 through 1,000.

0x05 - HEARTBEAT

Both directions

Optional application heartbeat. The service echoes the payload. WebSocket ping frames are also supported.

0xFF - CLOSE

Both directions

Ends the shell session.

Text frames aren’t part of the protocol. The service ignores isolated text frames and closes the connection with code 1003 after five consecutive text frames.

The first STATUS frame confirms the connection and includes metadata similar to the following:

{ "kind": "Status", "apiVersion": "v1", "metadata": { "shellId": "my-shell", "reconnected": false }, "status": "Success" }

When the shell exits with a nonzero status, the final STATUS frame includes an ExitCode cause in details.causes.

Reconnect to a shell

Store both the runtime session ID and the shell ID. To reconnect, use the same runtime session ID header and add the URL-encoded shell ID as a query parameter:

wss://bedrock-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-harness-arn>/ws/shells?shellId=<shell-id>

The shell process continues running while the connection is detached. On reconnection, the service replays up to 256 KB of buffered output. The connection STATUS frame sets metadata.reconnected to true and includes metadata.bytesDropped if the detached shell produced more output than the replay buffer could retain.

Opening another connection with the same shell ID replaces the existing connection. The replaced client receives close code 4000.

Common use cases

Interactive debugging

Inspect installed packages, files, processes, and environment configuration in the same environment as the harness agent.

Custom environment validation

For a harness with a custom container, verify installed dependencies and filesystem mounts from inside that container.

Coding agent terminal access

Provide a persistent execution environment for coding agents that need to create files, run tests, inspect output, and iterate.

Long-running processes

Start a process, detach from the WebSocket, and reconnect later to check progress or provide input.

Security considerations

Important

A principal that can open a Harness shell has interactive command access to the harness session. Restrict bedrock-agentcore:InvokeAgentRuntimeCommandShell to trusted principals and specific harness ARNs.

The shell can access customer-provided data, files, and secrets in the selected environment or on mounted filesystems. It can also connect to network destinations that the session can reach. The shell environment uses an allowlist and doesn’t inject AgentCore platform or workload credentials. The shell remains inside the session’s isolated microVM and cannot access another customer’s workload.

AgentCore sends the request ID and connection metadata to the harness Runtime log group in Amazon CloudWatch Logs. The service doesn’t log terminal input or output.

Amazon CloudTrail records InvokeAgentRuntimeCommandShell API calls, including the caller identity, timestamp, source IP address, resolved Runtime resource, and response status. CloudTrail doesn’t log terminal input or output.

For more Harness security guidance, see Security and access controls.

Error handling

The WebSocket upgrade can return the following errors:

ValidationException

The request parameters are invalid. Verify that the runtime session ID contains at least 33 characters, the shell ID is valid, and the feature is available in the target Region. Use the URL-encoded harness ARN in the request path; direct use of the managed Runtime ARN is rejected.

AccessDeniedException

The caller doesn’t have bedrock-agentcore:InvokeAgentRuntimeCommandShell permission on the harness.

ResourceNotFoundException

The specified harness doesn’t exist. Verify that the URL contains the URL-encoded harness ARN.

ServiceQuotaExceededException (429)

The maximum of 10 active shells is already open. End an active shell or detach it, and then retry.

RuntimeClientError (424)

The runtime couldn’t be reached.

ThrottlingException

The request exceeded the API rate limit. Retry with exponential backoff.

RetryableConflictException (409)

The service is provisioning or tearing down the target session. Retry with short exponential backoff.

Once connected, the following close codes indicate why the connection ended:

Code Meaning Client action

1000

The shell exited cleanly.

Treat as normal termination.

1003

The client sent five consecutive text frames to the binary-only protocol.

Switch to binary frames. Don’t automatically reconnect with the same behavior.

1006

The connection ended without a close frame because of a network failure or service interruption.

Reconnect with the stored shell ID.

1008

The one-hour connection duration expired, the frame rate exceeded 250 frames per second, or the write buffer overflowed.

Reconnect for duration expiry. For rate or buffer violations, back off before reconnecting.

1009

A frame payload exceeded 64 KB.

Split the payload into frames smaller than 64 KB, then reconnect.

1011

An unexpected server failure occurred.

Retry with exponential backoff.

4000

Another client connected with the same shell ID.

Don’t automatically reconnect. Inform the user that another client attached to the shell.

Best practices

  • Generate and retain a unique shell ID for each logical terminal.

  • Retain the runtime session ID with the shell ID. Both values are required for reconnection.

  • Read output promptly so that the connection write buffer doesn’t fill.

  • Split large input into binary frames smaller than 64 KB.

  • Reconnect before or after the one-hour connection limit if the shell must remain available longer.

  • End shells explicitly when they are no longer needed. Detached shells don’t count toward the 10-active-shell limit. The service retains at most 20 active and detached shell entries and evicts the oldest detached shell when necessary.

  • Use an upgrade timeout of at least 330 seconds when a connection can start a cold harness session. Apply a separate timeout while waiting for the initial STATUS frame after the upgrade.

Quotas and limits

Limit Value Description

Maximum frame payload

64 KB

Larger frames result in close code 1009.

Frame rate

250 frames per second

Exceeding the limit results in close code 1008.

Maximum connection duration

1 hour

Reconnect with the same runtime session ID and shell ID to continue.

Concurrent active shells per harness session

10

New connections are rejected when 10 shells are active. Detached shells don’t count toward this limit.

Retained active and detached shell entries

20

When the limit is reached, the service evicts the oldest detached shell before creating another shell.

Reconnection buffer

256 KB

Maximum output replayed after reconnection.

For complete service limits, see Quotas for Amazon Bedrock AgentCore.