
Building an Autonomous Coding Agent on Amazon Bedrock AgentCore
Co-authored with Karim Akhnoukh and Talha Chattha. A ticket goes in, working code comes out. How to build your own autonomous coding backend with durable orchestration, zero-cost suspension, and cross-ticket learning - on Amazon Bedrock AgentCore Runtime.
Co-authored with Karim Akhnoukh and Talha Chattha .
A ticket goes in, working code comes out. How to build your own autonomous coding backend with durable orchestration, zero-cost suspension, and cross-ticket learning — on Amazon Bedrock AgentCore Runtime.
AI coding assistants have changed how developers work. Tools like Kiro reduce the cycle from idea to implementation by combining spec-driven planning with autonomous code generation. Most of these tools are designed for interactive use: a developer describes a task, reviews a plan, approves it, and watches the code appear. This backend takes a different direction: tickets arrive from any event source, an agent implements them autonomously, a separate review agent evaluates the result, and lessons learned feed back into future tickets — all without a human in the loop.
For background on AgentCore Runtime, session storage, and persistent filesystems, see It's safe to close your laptop now . For an introduction to Cedar policies at the Gateway level, see Secure AI agents with Policy and Lambda interceptors in Amazon Bedrock AgentCore gateway . This post extends both concepts: a headless coding backend for automation that applies Cedar enforcement inside the sandbox, adds durable orchestration with async callbacks, and introduces cross-ticket memory.
The complete source code is available in the autonomous-agentcore-coding-agent GitHub repository.
Solution Overview
The system implements a five-stage pipeline orchestrated by an AWS Lambda Durable Function. Each stage is checkpointed — if the Lambda dies mid-run, it resumes exactly where it left off. Between stages, the function suspends at zero compute cost while the coding agent works asynchronously in the background. The following diagram describes the data flow between the components:

| Stage | What happens | Compute cost while waiting |
|---|---|---|
| 1. Admission | Validate event, fetch ticket from Amazon S3, derive session ID | - |
| 2. Hydrate | Clone the ticket's repository into the shared filesystem via sandbox; recall per-repo lessons from AgentCore Memory | - |
| 3. Code loop | Dispatch the coding agent asynchronously; suspend at $0. Agent works in the background (minutes to hours). On callback: run deterministic test gate. Retry up to MAX_ATTEMPTS on failure. | $0 (suspended) |
| 4. Review | Invoke the evaluator agent (read-only, separate runtime). Returns structured verdict + lessons. Optional: one repair loop on request_changes. | - |
| 5. Finalize | Write lessons to AgentCore Memory; send Amazon SNS notification (pass/fail + summary) | - |
Why a Durable Function?
A standard AWS Lambda function has a 15-minute timeout — too short for an agent that may work for 30 minutes on a complex ticket. The durable execution model solves this: the orchestrator dispatches the coding agent, then suspends (no compute cost). When the agent finishes, it calls
SendDurableExecutionCallbackSuccess, which resumes the orchestrator exactly where it left off. The orchestrator can survive for hours or days, but only pays for the seconds it actually runs.The Four Runtimes
The system uses four AgentCore runtimes, each with a dedicated IAM role and container image:
| Runtime | LLM | Purpose | Tools |
|---|---|---|---|
| Coding Agent | Claude Opus 4.8 | Plan, write code, drive sandbox | run_command, write_file, read_file, get_details (MCP → sandbox) |
| Sandbox | None | Run commands, install packages, run tests | subprocess with Cedar policy enforcement |
| Evaluator Agent | Claude Opus 4.8 | Read-only code review after tests pass | Read, Glob, Grep (no write, no execute) |
| Sandbox (Swift) | None | Swift-specific sandbox with SwiftPM scratch persistence | Same as sandbox, .build redirected to session storage |
The coding agent and the sandbox share a session ID and a filesystem mount — but the coding agent cannot run commands in its own process. All command invocation is mediated by the sandbox. The evaluator agent is completely isolated: read-only access, separate runtime, separate IAM role.
Prerequisites
To deploy this solution, you need:
- An AWS account with permissions to create AWS Lambda functions, Amazon EventBridge rules, Amazon Simple Notification Service (Amazon SNS) topics, AWS Identity and Access Management (IAM) roles, and Amazon Bedrock AgentCore runtimes
- AWS CDK v2 installed and bootstrapped in your target region
- Docker with BuildKit support (for ARM64 container image builds)
- Access to Claude Opus 4.8 on Amazon Bedrock in your region
- Python 3.11+
Walkthrough
The durable orchestrator
The orchestrator is an AWS Lambda Durable Function. Each stage is a checkpointed step — if the function restarts mid-flow, completed steps are replayed without re-running their side effects.
The following snippet shows the admission and hydrate stages:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from aws_durable_execution_sdk_python.context import DurableContext
from aws_durable_execution_sdk_python.execution import durable_execution
async def handler(event, context: DurableContext):
# Stage 1: Admission
tid, ticket, sid = await context.step("admission", lambda: _admit(event))
# Stage 2: Hydrate (clone repo + recall memory)
lessons = await context.step("hydrate", lambda: _hydrate(tid, ticket, sid))
# Stage 3: Code loop (async callback — suspends at $0)
for attempt in range(MAX_ATTEMPTS):
callback_id = await context.step(f"dispatch_{attempt}", lambda: _dispatch_coder(sid, tid, ticket, lessons))
coder_result = await context.wait_for_callback(callback_id) # SUSPENDS HERE
test_passed = await context.step(f"gate_{attempt}", lambda: _run_test_gate(sid, tid))
if test_passed:
break
# Stage 4: Review (evaluator agent, read-only)
review = await context.step("review", lambda: _invoke_evaluator(sid, tid))
# Stage 5: Finalize (write lessons + notify)
await context.step("finalize", lambda: _finalize(tid, test_passed, review))The
wait_for_callback call is the key innovation: it suspends the Lambda at zero compute cost. The coding agent works for as long as needed (minutes, even hours), and when it finishes, it calls SendDurableExecutionCallbackSuccess to resume the orchestrator.Session coupling and per-ticket isolation
The orchestrator derives a deterministic session ID from the ticket ID:
1
2
3
4
5
6
7
import hashlib
PROJECT = "cagent"
def session_id_for(tid: str) -> str:
h = hashlib.sha256(f"{PROJECT}:{tid}".encode()).hexdigest()[:32]
return f"{PROJECT}-{h}"All runtimes that work on the same ticket receive this same session ID. AgentCore Runtime assigns each session ID to a dedicated microVM. Different tickets run on different microVMs — state does not leak between them.
The shared workspace is
/mnt/shared/<ticketId>/. Both the coding agent and the sandbox mount the same Amazon S3-backed filesystem. The orchestrator passes the ticket_prefix in the payload, and each component confines itself to that subdirectory.The coding agent
The coding agent runs the Claude Agent SDK on AgentCore Runtime. It has four MCP tools that bridge to the sandbox:
run_command, write_file, read_file, and get_details. It is explicitly prohibited from using Bash, Monitor, WebFetch, or WebSearch in its own process.1
2
3
4
5
6
7
8
async def run_command(args):
if args.get("cwd"):
path_security.check_path(args["cwd"])
out = invoke_sandbox("run_command", _session_id.get(), _ticket_prefix.get(),
cmd=args["cmd"], cwd=args.get("cwd"), timeout=args.get("timeout", 600))
return {"content": [{"type": "text", "text": _fmt(out)}]}Path validation happens in the coding agent before the call reaches the sandbox, and again in the sandbox independently — both use a shared validation module (
shared/validation.py). Neither layer trusts the other.When dispatched in async-callback mode, the agent accepts the work, runs it in a background thread (keeping its session alive via
/ping), and returns to the orchestrator in approximately one second. The actual implementation work happens asynchronously — the orchestrator is suspended the entire time.The sandbox
The sandbox is an AgentCore Runtime with no LLM — a plain command runner. It validates paths, blocks dangerous commands via Cedar policy evaluation, tracks state for crash recovery, and maintains a persistent virtual environment in session storage.
Installed packages live in
/mnt/workspace (session storage), which survives microVM restarts. A persistent venv is created on first use and reused across all subsequent invocations within the same session — zero reinstall cost after a crash.The evaluator agent
After the deterministic test gate passes, the orchestrator invokes a separate evaluator agent. This is a first-class component with its own runtime, container image, IAM role, and cost line. It is strictly read-only: it reads the code in the shared mount, reasons about correctness and code quality, and returns a structured verdict.
1
2
3
4
5
6
7
8
9
10
11
12
SYSTEM_PROMPT = (
"You are a code review / evaluation agent. You run AFTER a coding agent has implemented "
"a ticket and its tests have passed. You are READ-ONLY — you cannot write, edit, or "
"execute code; you only read files in your working directory and reason about them.\n\n"
"Judge whether the implementation correctly and cleanly satisfies the ticket. Focus on: "
"correctness vs. the ticket, obvious bugs, missing tests/edge cases, and clear "
"code-quality problems. Be concise and specific (cite file:line).\n\n"
"Also capture LESSONS: durable, repo-LEVEL takeaways that would help a future coding "
"agent working on THIS repository on a DIFFERENT ticket.\n\n"
"End your response with a JSON object:\n"
'{"verdict": "approve" | "request_changes", "issues": [...], "lessons": [...]}'
)The evaluator's allowed tools are
Read, Glob, and Grep — nothing else. If it returns request_changes, the orchestrator can optionally re-invoke the coding agent with the issues (controlled by the REVIEW_REPAIR flag).AgentCore Memory: cross-ticket learning
Each ticket generates lessons. The next ticket benefits from them. AgentCore Memory provides this capability as a standalone managed resource — no runtime dependency, purely semantic retrieval.
1
2
3
4
5
6
7
# Before coding: recall relevant lessons for this repo
lessons = memory.recall(repo="example-org/my-app", query=ticket_description, top_k=3)
prompt += memory.format_for_prompt(lessons)
# After review: persist new lessons
new_lessons = review_result.get("lessons", [])
memory.remember(repo="example-org/my-app", lessons=new_lessons)Lessons are namespaced per repository. A recall query scoped to
lessons/my-app returns only lessons from previous work on that specific repo. This means the coding agent skips known pitfalls (saving tokens and avoiding repeated mistakes) without lessons from unrelated repos leaking in.Security: Cedar policy enforcement
Running an agent unattended — triggered by external ticket content — means prompt injection is a real attack surface. The system applies deterministic controls at the sandbox layer via Cedar policies:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// HARD DENY: Network exfiltration
forbid(principal, action == Action::"run_command", resource)
when { context.cmd like "*curl *" || context.cmd like "*wget *" };
// HARD DENY: Privilege escalation
forbid(principal, action == Action::"run_command", resource)
when { context.cmd like "*sudo *" || context.cmd like "*su -*" };
// HARD DENY: Path traversal
forbid(principal, action == Action::"write_file", resource)
when { context.path like "../*" || context.path like "*/../*" };
// PERMIT: Normal development operations
permit(principal, action == Action::"run_command", resource);
permit(principal, action == Action::"write_file", resource);The system uses two Cedar layers: AgentCore Gateway (API boundary) and sandbox-local evaluation via
cedarpy. The implementation supports AUDIT mode (log denials, don't block) and ENFORCE mode (hard block). The recommended approach is to run in AUDIT mode against representative ticket workloads, review the denial logs, and iterate on the policy before switching to ENFORCE.All policy decisions are recorded in a structured audit trail (
shared/audit.py) as JSON Lines, one entry per action per ticket.Deployment
Important: The deployment creates billable resources including an Amazon Virtual Private Cloud (Amazon VPC) with NAT gateway, AWS Lambda functions, Amazon S3 storage, Amazon EventBridge rules, Amazon SNS topics, Amazon CloudWatch dashboards, and AgentCore runtimes. Review the AWS pricing pages for these services before proceeding.
- Navigate to the CDK directory:
1
cd cdk- Install the required Python dependencies:
1
pip install -r requirements.txt- Deploy all stacks:
1
cdk deploy --all --require-approval neverTo verify the deployment succeeded:
1
2
3
aws cloudformation list-stacks \
--stack-status-filter CREATE_COMPLETE \
--query "StackSummaries[?starts_with(StackName,'cagent')].StackName"After verifying, submit a test ticket:
1
2
3
4
aws lambda invoke \
--function-name cagent-orchestrator \
--payload '{"ticketId": "TICKET-001"}' \
response.jsonThe durable orchestrator will admit the ticket, hydrate the repo, dispatch the coding agent (then suspend at $0), run the test gate on callback, invoke the evaluator, persist lessons to memory, and post a completion notification.
Extending to your workflow
The orchestrator is event-driven. Amazon EventBridge delivers events, and the
fetch_ticket() function is the integration point:| Event Source | Integration | Result Destination |
|---|---|---|
GitHub Issue (labeled agent-ready) | Amazon EventBridge + GitHub webhook | PR opened on the repo |
| JIRA ticket (status change) | Amazon EventBridge + JIRA webhook | Comment with results and artifacts |
| Slack command | API Gateway + AWS Lambda | Thread reply with summary |
| CI pipeline (on-demand) | AWS Step Functions | Artifacts in Amazon S3, pipeline continues |
Cleaning up
To avoid incurring ongoing charges, destroy all stacks:
1
2
cd cdk
cdk destroy --allThis removes the AWS Lambda function, Amazon EventBridge rule, Amazon SNS topic, AgentCore runtimes, Gateway policies, Memory resources, the shared Amazon S3-backed filesystem, AWS CodeBuild projects, Amazon Elastic Container Registry (Amazon ECR) repositories, IAM roles, Amazon Virtual Private Cloud (Amazon VPC) including NAT gateway, and Amazon CloudWatch resources created by the stacks.
Warning: The Amazon S3 bucket is retained by default to preserve ticket artifacts. If you delete the bucket manually, all stored artifacts are permanently removed and cannot be recovered. Back up any data you need before deleting the bucket.
Conclusion
This post demonstrated one approach to building a headless coding backend on Amazon Bedrock AgentCore Runtime. The five-stage durable pipeline — admission, hydrate, code loop, review, finalize — orchestrates multiple specialized runtimes while suspending at zero cost between stages.
The control-plane / data-plane separation contains the blast radius from prompt injection: the coding agent plans and writes, the sandbox runs commands under Cedar policy enforcement, and the evaluator provides an independent read-only assessment. Deterministic session IDs give per-ticket microVM isolation and shared filesystem access. AgentCore Memory provides cross-ticket learning so that each ticket benefits from the lessons of previous work on the same repository.
From here, the natural extensions are human-in-the-loop approval gates via Cedar soft-deny policies, a GitHub integration that opens PRs with the agent's output, and a multi-agent pipeline where specialized coding agents handle different languages or frameworks.
References
- It's safe to close your laptop now: Hosting coding agents on Amazon Bedrock AgentCore
- Secure AI agents with Policy and Lambda interceptors in Amazon Bedrock AgentCore gateway
- AgentCore Runtime documentation
- AWS Lambda Durable Execution
- Cedar policy language
- autonomous-agentcore-coding-agent — GitHub
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article