Building an Agent Client from Claude Code Patterns
What I extracted from the learning-claude-code codebase about sessions, permissions, plans, subagents, and remote transport when building a serious agent client.
I went through the learning-claude-code codebase with one practical question in mind:
How do you move from "a model that can call tools" to a real agent product that people can trust and actually use?
The short answer is that the interesting part is not the model loop by itself. The interesting part is the runtime around it.
The repo makes that obvious. There are explicit subsystems for:
- session identity and resume
- tool registration and capability filtering
- permission decisions with allow / deny / ask
- plan files that live outside the raw transcript
- forked agents with isolated mutable state
- bridge and remote-session transport over HTTP + WebSocket
That is why I think "agent client" is a reasonable term here. This is not just a chat frontend. It is a stateful execution client.
1. The client owns more than rendering
If you only look at a demo, it is easy to assume the client is just text in, text out.
That is not what a serious runtime does.
The client surface ends up owning:
- streaming output
- progress updates
- permission prompts
- interrupts
- local context gathering
- resume and reconnection behavior
Once those features exist, the client is part of the runtime architecture.
2. Sessions are infrastructure
One of the clearest signals in the codebase is how much machinery exists around sessions.
There are dedicated session types, resume helpers, remote session managers, transport code, and plan persistence. That tells you something important: once tasks can span many turns, session identity stops being an implementation detail.
If I were building this from scratch, I would make session state explicit from day one:
session_id- stable session key for resume
- transcript storage
- current permission mode
- current plan artifact
- active child agents
Without that, resumability becomes an afterthought and background work becomes brittle.
3. Permission gating is the real trust boundary
The most important extraction from the repo is not a prompt trick. It is the permission model.
Tool execution is surrounded by structured policy:
- allow
- deny
- ask
That is the right abstraction.
If a model can read files, edit files, run shell commands, or talk to external systems, then safety is not "be careful in the system prompt." Safety is a runtime gate in front of every side effect.
This also explains why approval UX matters so much. Once a tool call is blocked, the product needs a clear round-trip:
- emit a permission request
- let the user inspect it
- optionally edit the input
- continue or reject execution
That approval loop is part of the product, not a minor edge case.
4. Plans should outlive the transcript
Another good pattern in the codebase is persistent plan handling.
That is a stronger design than treating the plan as just another assistant message buried in history.
Why it matters:
- transcripts get long
- context gets compacted
- sessions get resumed
- child work gets forked
A durable plan artifact gives the runtime a stable working memory that is easier to inspect and recover.
For larger engineering tasks, I would rather have:
- a transcript for conversational history
- a plan file for current execution intent
than force both jobs into the same text stream.
5. Subagents are useful, but only with strict boundaries
The forked-agent utilities were the most useful architectural clue for parallel work.
The key lesson is not "spawn lots of agents." The lesson is:
- keep delegated tasks bounded
- isolate mutable state
- preserve cache-safe prompt prefixes where useful
- surface child progress explicitly
That is the difference between a useful helper-agent pattern and a messy swarm.
Subagents make sense for:
- repository exploration
- verification side work
- independent implementation slices
They are a bad fit when the next step is tightly blocked on the answer or when the task is too coupled to specify cleanly.
6. Remote transport becomes product architecture fast
The bridge and remote-session code is a reminder that "transport" is not just plumbing.
As soon as an agent can:
- continue on another machine
- run detached
- reconnect after interruption
- stream output over WebSocket
you need explicit session transport design.
That means:
- session creation APIs
- stream subscription
- control messages for approval / interrupt
- token refresh
- reconnect semantics
A local-only agent is simpler. A remote-capable agent is much more powerful. The cost is that transport failures become first-class runtime failures.
My current mental model
If I were sketching the architecture today, I would break an agent client into six pieces:
- User surface: CLI, desktop app, or web app
- Session runtime: transcript, plan, resume, task state
- Tool registry: typed capabilities and enablement
- Permission gate: allow / deny / ask around side effects
- Execution loop: model + tools + progress events
- Bridge transport: remote session creation, stream, reconnect
Then I would add subagents only after the first five are solid.
What I am turning into site content
I am using this analysis to build a small reference set on the site:
- a Claude Code blog series
- design docs that track source coverage and deep-read notes
- later, API Design and Architecture Lab material once the core series is complete
That is more useful than trapping the learning in one long post, and it also makes the coverage easier to audit over time.
Final thought
The main mistake people make with agents is over-focusing on the reasoning loop and under-focusing on the runtime.
The model is important.
But if you want a tool that survives real usage, the harder design problems are usually:
- what state persists
- what actions are allowed
- how recovery works
- how delegation is bounded
- where trust actually lives
That is the part of Claude Code worth learning from.