A simple yet genuinely durable AI agent. A DevOps / on-call agent built with the AWS Strands Agents SDK, made durable with Temporal, reasoning with Anthropic Claude, and deployed on Amazon Bedrock AgentCore as the agent harness (Memory · Gateway · Identity · Observability).
The agent watches a service over HTTP, diagnoses its health with Claude, and can remediate it. Because the agent's reason→act loop runs inside a Temporal workflow, it survives worker crashes, retries flaky calls automatically, and can wait — durably, holding no compute — for a human to approve a disruptive action.
The story in one line: the same Strands agent runs locally, then becomes durable by wrapping it — not rewriting it — with the official
temporalio.contrib.strandsplugin.
Before — a plain Strands agent. Conceptually just an LLM + tools, run once, no durability (a crash mid-remediation loses everything):
agent = Agent(model=AnthropicModel(...), tools=[...], system_prompt=DEVOPS_SYSTEM_PROMPT)
agent("Is the service healthy? Remediate if needed.")After — the same agent, now durable (durable/workflow.py). The loop runs in a Temporal
workflow; the Claude call and each tool call become Temporal activities:
@workflow.defn
class DevOpsAgentWorkflow:
@workflow.run
async def run(self, prompt: str) -> str:
agent = TemporalAgent( # model call -> activity
model="claude",
start_to_close_timeout=timedelta(seconds=120),
tools=[activity_as_tool(check_service_health, ...), # tool call -> activity
activity_as_tool(get_service_metrics, ...),
self._restart_tool()], # gated by a durable approval signal
system_prompt=DEVOPS_SYSTEM_PROMPT,
)
return str(await agent.invoke_async(prompt))The tool logic itself never changes — it lives once in durable/service_ops.py and is wrapped
either as a Strands @tool (Phase 1) or a Temporal @activity.defn (Phase 2).
Amazon Bedrock AgentCore (harness): Runtime · Memory · Gateway · Identity · Observability
│ starts / signals
▼
Temporal Cloud ◄────► Serverless Worker on AWS Lambda ── runs the Strands agent loop in a
(durable state) (temporalio.contrib.strands) @workflow; Claude call + each tool
│ call become Temporal activities
check_service_health · get_service_metrics · restart_service (HTTP → activities)
│
target-service (a self-contained FastAPI app, no external deps)
- AgentCore = the harness: serverless hosting (Runtime), incident memory (Memory), the service API as MCP tools (Gateway), tool credentials (Identity), traces/metrics (Observability).
- Temporal = durable execution: the agent loop is a workflow; LLM + tool calls are activities, so they retry independently and the workflow resumes exactly where it left off after any crash.
- Human-in-the-loop:
restart_servicewaits on a Temporal signal — durably, for as long as needed, consuming no compute.
| Path | What |
|---|---|
durable/ |
The whole agent: tool logic (service_ops.py) + prompt (prompts.py) + Temporal workflow, workers (worker_local.py / worker_lambda.py), client, config. |
harness/ |
Amazon Bedrock AgentCore wiring: runtime entrypoint, memory (+ backends), observability. |
target-service/ |
A self-contained FastAPI app the agent monitors (/health, /metrics, /restart, /fault); models api/database/cache components for the status board. |
backend/ |
FastAPI backend for the React UI — streams the live pulse + run timeline over SSE. Includes bridge.py (Temporal client) + service.py (target-service wrappers). |
frontend/ |
React (Vite + TS + Tailwind) "DevOps Mission Control" dashboard (see below) — the only UI. |
infra/ |
Deploy: Temporal Cloud, Lambda/Fargate worker, AgentCore (deploy.md), and Terraform for the real-AWS slice (terraform/: service + CloudWatch + IAM). |
tests/ |
Tool unit tests + an end-to-end durable HITL workflow test (no API key needed). |
.mcp.json, .claude/skills/ |
Dev tooling: MCP servers + authored skills for Strands & AgentCore. |
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"
cp .env.example .env # set ANTHROPIC_API_KEY (+ CLAUDE_MODEL_ID)Run the durable agent (needs a Temporal dev server: temporal server start-dev)
python target-service/app.py # terminal 1: the monitored service
python -m durable.worker_local # terminal 2: the durable worker
python -m durable.client start "The service looks down — fix it." # terminal 3
python -m durable.client approve <workflow-id> # approve the restartWatch it in the Temporal Web UI (http://localhost:8233). The durability demo: start a run, kill the worker (Ctrl-C) mid-flight, restart it — the workflow resumes from history with no duplicated LLM or tool calls.
Port 8080 in use? Run the service on another port and set
TARGET_SERVICE_URLaccordingly.
A mission-control dashboard that makes the story legible: the monitored service as a live ECG
pulse that goes green→red on fault, an animated durable-activity timeline with the Approve/Deny
gate, the AgentCore harness shown live (each pillar with a "how it helps" line lit up by the
current run), and an embedded Temporal workflows view. A small FastAPI backend streams the pulse
and timeline over SSE via its bridge.py Temporal client. CloudWatch is simulated locally and
becomes real with USE_AWS=1 (see Terraform below).
uv pip install -e ".[dev,web]"
cd frontend && npm install && cd ..
bash scripts/dev.sh # target service + Temporal + worker + backend + frontend
# open http://localhost:5173Then: Inject fault (pick a component) → Run agent → watch check_service_health →
get_service_metrics → get_cloudwatch_status → restart_service stream in and pause at
awaiting approval → Approve → service returns to healthy. Kill the worker mid-approval and
restart it to prove durability. A 2nd run shows Memory recalling the earlier incident.
Exercise the AgentCore harness locally — scripts/dev.sh also runs the AgentCore Runtime
entrypoint (harness/runtime_entrypoint.py, a BedrockAgentCoreApp) on :8081. Tick "route via
AgentCore Runtime" in the UI and runs flow through the real Runtime front door → Temporal, on your
laptop. Memory is live too via a pluggable backend (MEMORY_BACKEND): a zero-dep local store by
default (so the second run on a similar incident shows "recalled N past incidents"), mem0 if you
install .[memory], or real AgentCore Memory once AGENTCORE_MEMORY_ID is set.
See infra/deploy.md: provision AgentCore Memory/Gateway/Identity, deploy the
worker (Lambda serverless worker, or Fargate fallback), then deploy the Runtime entrypoint and
invoke it. The agent loop is identical across local, Fargate, and Lambda.
python -m pytest # tool cycle + end-to-end durable human-in-the-loop workflowThe durable test spins up a local Temporal server and a scripted stub model (no Anthropic key) to
prove: the agent loop runs in the workflow, the restart tool blocks on the approval signal, and the
workflow completes only after approve.
.mcp.json wires MCP servers for Amazon Bedrock AgentCore, AWS docs, Temporal (ops),
Temporal docs, and Strands docs. Two authored skills live in .claude/skills/:
strands-agent-builder (build Strands agents + make them durable) and agentcore-harness (wire the
AgentCore components). Install the official Temporal developer skill with:
/plugin marketplace add temporalio/agent-skills # then install "temporal-developer"