Architecting long-running Claude Fable agent sessions is a different discipline than calling a model and printing the response. Once you let an agent own a chunk of a codebase for hours — running tests, editing files, reviewing pull requests — you’ve built a system that behaves like an unattended production service, not a chat feature. That means it needs the same things any unattended service needs: checkpoints it can resume from, a budget it can’t blow through, a way to stop it fast, and someone watching a dashboard while it works. None of this is about the model’s capability — it’s about the scaffolding you build around it. Here’s how to build that scaffolding properly.
Why long-running agents need real infrastructure, not just a bigger context window
It’s tempting to treat “long-running” as purely a model property — Claude Fable 5.1 can hold context longer, so just let it run. That’s how you end up with a four-hour session that dies at hour three from a transient network error, burns your token budget on a task that went sideways at minute twenty, or makes a destructive file change with nobody watching. A long-running agent is an orchestration problem wrapped around a model call, and the orchestration layer is where almost all of the actual engineering effort belongs. Treat the agent loop the way you’d treat any long-running batch job: it needs state persisted outside the process, a supervisor watching it, and defined boundaries on what it’s allowed to do without a human checking in.
Checkpointing: treat every tool call as a commit point
The single highest-leverage architecture decision is checkpointing session state after every meaningful tool call, not just at the end of the session. If the process dies, your cloud bill spikes an alert, or someone needs to pause the agent overnight, you resume from the last checkpoint instead of restarting the whole task. AWS Step Functions is a natural fit here — model the agent loop as a state machine where each tool execution is a state, and persist the running conversation and tool results to DynamoDB after each step:
import boto3
import json
import time
ddb = boto3.resource("dynamodb").Table("agent-session-checkpoints")
def checkpoint(session_id, messages, step_index):
ddb.put_item(Item={
"session_id": session_id,
"step_index": step_index,
"messages": json.dumps(messages),
"updated_at": int(time.time()),
})
def resume(session_id):
items = ddb.query(
KeyConditionExpression=boto3.dynamodb.conditions.Key("session_id").eq(session_id),
ScanIndexForward=False,
Limit=1,
)["Items"]
return json.loads(items[0]["messages"]) if items else []
Checkpoint the conversation state, the diff of files changed so far, and the test results from the last run — not just the raw message list. When an agent picks back up after a restart, it should be able to tell exactly what it already did without re-deriving it by re-reading the whole repo, which wastes tokens and risks it repeating or undoing earlier work.
Cost control: session-level budgets, not monthly aggregates
A runaway agent loop — stuck retrying a failing test, or spiraling into an unproductive back-and-forth with itself — can burn through tokens fast, and a monthly AWS Budget alert won’t catch it until real damage is done. Put a hard ceiling on the session itself: track cumulative token usage per session ID and kill the loop when it crosses a threshold, independent of any account-level billing alarm.
def enforce_session_budget(session_id, usage, max_tokens=500_000):
total = ddb.get_item(Key={"session_id": session_id}).get("Item", {}).get("total_tokens", 0)
total += usage["totalTokens"]
if total > max_tokens:
raise RuntimeError(f"Session {session_id} exceeded token budget: {total}")
ddb.update_item(
Key={"session_id": session_id},
UpdateExpression="SET total_tokens = :t",
ExpressionAttributeValues={":t": total},
)
Pair that in-session ceiling with an account-level Bedrock service quota check and a CloudWatch alarm on Bedrock’s invocation metrics, so both the individual session and the aggregate account have independent stop conditions:
aws cloudwatch put-metric-alarm \
--alarm-name "bedrock-fable-hourly-spend" \
--namespace "AWS/Bedrock" \
--metric-name "InvocationClientErrors" \
--statistic Sum \
--period 3600 \
--threshold 1000000 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 1 \
--alarm-actions "arn:aws:sns:us-east-1:123456789012:agent-cost-alerts"

Guardrails and kill-switches
An agent that can edit files and run commands for hours needs a stop button that works even if the agent’s own loop is unresponsive — never rely on the agent gracefully honoring a stop request from inside its own execution path. Build the kill-switch outside the agent’s process: an EventBridge rule that a human (or an automated guardrail check) can trigger, invoking a Lambda that flips a status flag the orchestrator checks before every tool call.
def before_tool_call(session_id):
status = ddb.get_item(Key={"session_id": session_id}).get("Item", {}).get("status", "running")
if status == "halted":
raise RuntimeError(f"Session {session_id} was halted externally — stopping before next tool call.")
Layer on scope guardrails too: an allowlist of directories the agent can write to, a denylist of commands it can execute (no rm -rf, no unreviewed git push --force), and a hard requirement that anything touching production credentials or infrastructure-as-code goes through a human approval step rather than autonomous execution. Bedrock’s own Guardrails feature can enforce some of this at the model-call layer, but the file-system and command-execution boundaries need to live in your own tool-execution code — Bedrock can’t see what your tool functions actually do once it hands off a toolUse request.
Monitoring an agent that owns a chunk of your codebase unattended
Once an agent is running unsupervised for hours, “check the logs after it’s done” isn’t monitoring — it’s a post-mortem. Stream structured events for every tool call, file change, and test run to CloudWatch Logs in near real time, and build a dashboard that shows session status, cumulative token spend, files touched, and test pass rate as the session progresses:
aws logs put-log-events \
--log-group-name /agents/fable-5-1/sessions \
--log-stream-name session-4471 \
--log-events timestamp=$(date +%s000),message='{"event":"tool_call","tool":"run_tests","result":"pass","files_changed":3}'
Set CloudWatch alarms on the signals that actually indicate trouble — repeated test failures on the same file, a tool-call error rate above baseline, a session running past its expected duration — rather than just watching for the process to crash outright. An agent that’s technically alive but stuck in an unproductive loop is the failure mode that costs you the most money and the most trust, and it’s invisible unless you’re specifically watching for it.
Putting it together
None of these four pieces — checkpointing, cost ceilings, kill-switches, monitoring — are exotic AWS patterns. They’re the same discipline you’d apply to any long-running batch process or unattended automation, applied to a model call instead of a cron job. The difference with Claude Fable 5.1 is that the “batch job” now makes judgment calls about your codebase for hours at a stretch, which raises the cost of getting the scaffolding wrong. Build it right once, and the model’s extended session capability becomes an asset instead of a liability sitting in your CloudWatch console at 2 AM.
At Enkompass (enkompass.net), this is our home turf. We’ve shipped production Bedrock and SageMaker workloads that actually move the needle, not just demo well. We’ve built the checkpointing, budget-ceiling, and kill-switch scaffolding around long-running Claude Fable 5.1 sessions so clients can let an agent own real codebase work without babysitting it. If you’d rather have seasoned cloud people handle it instead of learning the hard way at 2 AM, let’s talk. Reach out through our contact page or call us directly at +1-828-436-5667 — we’ll help you turn this from a someday-project into a done-deal.
