Repository navigation
feat!: sandbox images for AgentCore and Lambda MicroVM; per-session agent, SSE keepalive fix, session_id-keyed store - #23
Merged
yash-kark merged 14 commits intoOct 28, 2026
Conversation
Merged
yash-kark
force-pushed
the
feat/p1-sdk-13-sandbox-images
branch
9 times, most recently
from
October 28, 2026 17:20
e009727 to
135885d
Compare
Base automatically changed from
feat/p1-sdk-12-agentcore-relay
to
feat/p1-sdk-2-remove-events-sink
October 28, 2026 17:20
…es the agent and token per session
The settings' environment variables are renamed so each says what it is:
COMPUTER_AGENT_ID, COMPUTER_AGENT_ENGINE, COMPUTER_AGENT_EGRESS_PROXY_SECRET
(was LYZR_API_KEY) and COMPUTER_AGENT_EGRESS_{API,LLM,MCP,GUARDRAILS,
TELEMETRY,SESSION_STORE}_URL, plus COMPUTER_AGENT_GAP_DIR. Constructor
arguments are unchanged; no old name is read.
Old CAS now passes COMPUTER_AGENT_EGRESS_PROXY_SECRET and COMPUTER_AGENT_ID
in POST /v1/sessions envs: the app server seeds the token from it and builds
the session's agent from the id, falling back to the image's own.
events() bounded each read with asyncio.wait_for(frames.__anext__(), 20 s). On a timeout, wait_for cancelled the EventLog generator inside its wait for new events; a cancelled async generator is finished, so the next read raised StopAsyncIteration and the response ended right after the first keepalive. On Lambda MicroVM CAS reported STREAM_UPSTREAM_LOST on any 20 s quiet gap. EventLog.iterate now takes the keepalive and yields None when it expires, so no cancellation crosses the generator.
The Claude CLI keys transcripts by its own per-process UUID, so a new sandbox for the same conversation looked under a key nobody wrote and started blank, and agentos had no stable id to file the store under (old-CAS plan 10). With ComputerAgent(session_id=..., session_store=...), PinnedSessionStore puts that id on every append and load; project_key and subpath are kept. The Claude engine runs the CLI under uuid5(session_id): on the first turn of a process it loads the store once and starts a new conversation under that UUID, or resumes the stored one. Every process serving a conversation then uses the same CLI UUID, so the stored entries' ids always match. The app server already passes session_id = the CAS sessionId. Without session_id nothing changes.
Both example images install the otel extra and set COMPUTER_AGENT_EGRESS_TELEMETRY_URL=<egress>/otlp.
policy_config_url and fetch_policy_binding had no caller since 371c815 (0.4.0, one run mode). AgentOS has dropped the ingest/policy-config/{name} route they read (computeragent-enterprise a8843c9) because the agent gateway applies org and agent policies, so a direct caller got a 404 that read as "no policy". bind_policy stays: PUT /agents/{id}/policy is still served.
CAS now passes the six egress routes per session, beside the token and the
agent id: COMPUTER_AGENT_EGRESS_{API,LLM,MCP,GUARDRAILS,SESSION_STORE,
TELEMETRY}_URL in POST /v1/sessions envs. Each is used only when the image's
own environment does not set it, and an agent factory's arguments win over
both, so one image runs against whatever proxy CAS names while an image or
its code can still pin a route.
Rename the examples to examples/sdk_default_lambda_microvm and examples/sdk_default_agentcore: they build the images UAT CAS boots when an agent names no MicroVM image or AgentCore runtime of its own. Their .env now bakes only the engine; CAS passes the agent id, the sandbox token and the six egress routes per session, so one image runs against whichever egress proxy CAS names.
…og the instance Matches CAS's AgentCore relay (computeragent-enterprise #158). - PY1: AgentCore never forwards a caller's disconnect, so CAS names every envelope with x-ca-relay-stream: <uuid v4> and sends DELETE /_relay/streams/<id> when its caller goes away or before it re-attaches. The relay strips the header before dispatch, keeps a map id -> open call, and answers the DELETE itself: it closes that call (the relayed /events stream ends and its subscriber detaches) -> relayed 204; an unknown or non-UUID id -> relayed 404. Logs relay.stream.opened / relay.stream.closed. - PY2: AgentCore cuts any answer at 104,857,600 bytes. A JSON envelope over that is replaced by a relayed 502 AGENTCORE_RESPONSE_TOO_LARGE (source CAS_RUNTIME, retry false, meta.maxBytes) instead of a cut body. Event streams are not capped. - PY3: the per-microVM instance id is logged once, as relay.instance, when it is minted on the first invocation.
yash-kark
force-pushed
the
feat/p1-sdk-13-sandbox-images
branch
from
October 28, 2026 17:20
135885d to
e5f414e
Compare
yash-kark
merged commit Oct 28, 2026
05d43c7
into
feat/p1-sdk-2-remove-events-sink
1 of 5 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #22. Four commits, each passing the suite on its own.
1.
feat(harness)!: verboseCOMPUTER_AGENT_*settings; the app server takes the agent and token per sessionCOMPUTER_AGENT_EGRESS_PROXY_SECRETandCOMPUTER_AGENT_IDinPOST /v1/sessionsenvs. The app server seeds the token from the first and builds the session's agent from the second, falling back to the image's ownCOMPUTER_AGENT_ID.COMPUTERAGENT_AGENT_IDCOMPUTER_AGENT_IDCOMPUTERAGENT_ENGINECOMPUTER_AGENT_ENGINELYZR_API_KEYCOMPUTER_AGENT_EGRESS_PROXY_SECRETCOMPUTERAGENT_BASE_URLCOMPUTER_AGENT_EGRESS_API_URLCOMPUTERAGENT_{LLM,MCP,GUARDRAILS,TELEMETRY,SESSION_STORE}_URLCOMPUTER_AGENT_EGRESS_{…}_URLCOMPUTERAGENT_GAP_DIRCOMPUTER_AGENT_GAP_DIR2.
fix(server): keep the SSE stream open across keepalivesasyncio.wait_for(frames.__anext__(), 20 s)cancelled theEventLoggenerator on a timeout. A cancelled async generator is finished, so the stream ended right after the first:keepalive. On Lambda MicroVM, CAS reportedSTREAM_UPSTREAM_LOSTon any 20 s quiet gap.EventLog.iteratenow owns the keepalive and yieldsNonewhen it expires.test_a_quiet_gap_keeps_the_stream_openfails on the old code (assert 1 >= 2) and passes now.3.
feat(harness): key the session store bysession_idsession_id=,PinnedSessionStoreputs that id on everyappendandload;project_keyandsubpathare kept.uuid5(session_id). On a process's first turn the engine loads the store once: if nothing is stored it starts a new conversation under that UUID, otherwise it resumes. Every sandbox for a conversation then uses the same CLI UUID.session_id= the CASsessionId. Withoutsession_id, nothing changes.claude_agent_sdk's ownmaterialize_resume_sessionloads the pinned row under the derived UUID.4.
docs(examples)examples/agentcore_runtime/andexamples/lambda_microvm/: the SDK app server, with a baked.envholding the egress routes and the engine.docs/DEPLOYING_ON_AGENTCORE_AND_MICROVM.md: integrating the SDK, building the image,create-microvm-image/create-agent-runtime, and pointing an AgentOS agent at it.Testing
pytest -m "not integration": 669 passed.