Skip to content

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 into
feat/p1-sdk-2-remove-events-sinkfrom
feat/p1-sdk-13-sandbox-images
Oct 28, 2026
Merged

yash-kark merged 14 commits into
feat/p1-sdk-2-remove-events-sinkfrom
feat/p1-sdk-13-sandbox-images

Conversation

@vrajp-lyzr

Copy link
Copy Markdown
Contributor

Stacked on #22. Four commits, each passing the suite on its own.

1. feat(harness)!: verbose COMPUTER_AGENT_* settings; the app server takes the agent and token per session

  • Rename. The settings' environment variables are renamed so each says what it is. Constructor arguments are unchanged, and no old name is read.
  • Per-session values. 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 the first and builds the session's agent from the second, falling back to the image's own COMPUTER_AGENT_ID.
Was Now
COMPUTERAGENT_AGENT_ID COMPUTER_AGENT_ID
COMPUTERAGENT_ENGINE COMPUTER_AGENT_ENGINE
LYZR_API_KEY COMPUTER_AGENT_EGRESS_PROXY_SECRET
COMPUTERAGENT_BASE_URL COMPUTER_AGENT_EGRESS_API_URL
COMPUTERAGENT_{LLM,MCP,GUARDRAILS,TELEMETRY,SESSION_STORE}_URL COMPUTER_AGENT_EGRESS_{…}_URL
COMPUTERAGENT_GAP_DIR COMPUTER_AGENT_GAP_DIR

2. fix(server): keep the SSE stream open across keepalives

  • Bug. asyncio.wait_for(frames.__anext__(), 20 s) cancelled the EventLog generator on a timeout. A cancelled async generator is finished, so the stream ended right after the first :keepalive. On Lambda MicroVM, CAS reported STREAM_UPSTREAM_LOST on any 20 s quiet gap.
  • Fix. EventLog.iterate now owns the keepalive and yields None when it expires.
  • Test. test_a_quiet_gap_keeps_the_stream_open fails on the old code (assert 1 >= 2) and passes now.

3. feat(harness): key the session store by session_id

  • Bug. The store was keyed by the Claude CLI's per-process UUID, so a new sandbox started blank. agentos expects the CAS session id (old-CAS plan 10).
  • Store. With session_id=, PinnedSessionStore puts that id on every append and load; project_key and subpath are kept.
  • CLI id. The CLI runs under 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.
  • Scope. The app server already passes session_id = the CAS sessionId. Without session_id, nothing changes.
  • Checked: claude_agent_sdk's own materialize_resume_session loads the pinned row under the derived UUID.

4. docs(examples)

  • examples/agentcore_runtime/ and examples/lambda_microvm/: the SDK app server, with a baked .env holding 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.
  • ruff: clean.
  • mypy: 18 errors, all already on the base branch.

@vrajp-lyzr
vrajp-lyzr added this pull request to stack #24 October 27, 2026 12:33
@yash-kark yash-kark mentioned this pull request Oct 28, 2026
@yash-kark
yash-kark force-pushed the feat/p1-sdk-13-sandbox-images branch 9 times, most recently from e009727 to 135885d Compare October 28, 2026 17:20
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
yash-kark force-pushed the feat/p1-sdk-13-sandbox-images branch from 135885d to e5f414e Compare October 28, 2026 17:20
@yash-kark
yash-kark merged commit 05d43c7 into feat/p1-sdk-2-remove-events-sink Oct 28, 2026
1 of 5 checks passed
yash-kark added a commit that referenced this pull request Oct 28, 2026
…sink

feat!: SDK phase 1 stack (#11–#23) into main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants