<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Deep Dives on MyBrew</title>
    <link>https://aibrew.ai/categories/deep-dives/</link>
    <description>Recent content in Deep Dives on MyBrew</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 29 Jun 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://aibrew.ai/categories/deep-dives/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>AI-Native Workspace: A Four-Layer Architecture for Turning Coding Agents into a Personal OS</title>
      <link>https://aibrew.ai/2026/06/ai-native-workspace-a-four-layer-architecture-for-turning-coding-agents-into-a-personal-os/</link>
      <pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://aibrew.ai/2026/06/ai-native-workspace-a-four-layer-architecture-for-turning-coding-agents-into-a-personal-os/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — &lt;a href=&#34;https://github.com/RedDragonHQ/ai-native-workspace&#34;&gt;AI-Native Workspace&lt;/a&gt; is an open-source framework for running a real research or engineering workspace with AI coding agents. Its core idea is simple: separate the &lt;strong&gt;Model&lt;/strong&gt;, &lt;strong&gt;Agent&lt;/strong&gt;, &lt;strong&gt;Protocol&lt;/strong&gt;, and &lt;strong&gt;Application&lt;/strong&gt; layers so you can swap Claude, DeepSeek, Codex, Cursor, MCP servers, project docs, and knowledge workflows without rewriting everything.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;Most AI coding workflows start as a pile of useful fragments: a &lt;code&gt;CLAUDE.md&lt;/code&gt;, a few prompts, some shell scripts, maybe a &lt;code&gt;.cursorrules&lt;/code&gt; file, and a note that says “remember to update the TODO.” That works for one project. It starts breaking when the workspace grows to five projects, three model providers, a wiki, an inbox, a handful of local services, and months of accumulated decisions.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong> — <a href="https://github.com/RedDragonHQ/ai-native-workspace">AI-Native Workspace</a> is an open-source framework for running a real research or engineering workspace with AI coding agents. Its core idea is simple: separate the <strong>Model</strong>, <strong>Agent</strong>, <strong>Protocol</strong>, and <strong>Application</strong> layers so you can swap Claude, DeepSeek, Codex, Cursor, MCP servers, project docs, and knowledge workflows without rewriting everything.</p>
</blockquote>
<hr>
<p>Most AI coding workflows start as a pile of useful fragments: a <code>CLAUDE.md</code>, a few prompts, some shell scripts, maybe a <code>.cursorrules</code> file, and a note that says “remember to update the TODO.” That works for one project. It starts breaking when the workspace grows to five projects, three model providers, a wiki, an inbox, a handful of local services, and months of accumulated decisions.</p>
<p><strong>AI-Native Workspace</strong> is our attempt to turn that mess into an architecture.</p>
<p>The project is not another dotfiles dump. It is a layered framework for treating the workspace itself as the operating system: the model provides reasoning, the agent acts like a process, protocols are the system calls, and the application layer is where real work lives.</p>
<p>That metaphor sounds abstract, but it solves a very practical problem: <strong>how do you keep an AI-operated workspace portable when models, agents, and tools change every few months?</strong></p>
<h2 id="the-problem-most-agent-workflows-are-too-coupled">The Problem: Most Agent Workflows Are Too Coupled</h2>
<p>A lot of current “AI workflow” setups are tightly bound to one ecosystem:</p>
<ul>
<li>one model provider,</li>
<li>one coding agent,</li>
<li>one config format,</li>
<li>one project layout,</li>
<li>one set of tool integrations.</li>
</ul>
<p>That gives you a fast start, but the cost shows up later. If the model becomes expensive, you rewrite your scripts. If your agent changes its config format, you rewrite your rules. If you add a second project, your root instructions become a dumping ground. If chat history disappears, the agent loses the reasons behind past decisions.</p>
<p>AI-Native Workspace starts from the opposite assumption:</p>
<blockquote>
<p><strong>The workspace should outlive any single model, agent, provider, or tool.</strong></p>
</blockquote>
<p>So the framework separates the system into four layers.</p>
<h2 id="the-four-layer-architecture">The Four-Layer Architecture</h2>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>┌─────────────────────────────────────────────────────────┐
</span></span><span style="display:flex;"><span>│  Application                                            │
</span></span><span style="display:flex;"><span>│  Projects · Inbox · Wiki · workflows                    │
</span></span><span style="display:flex;"><span>├─────────────────────────────────────────────────────────┤
</span></span><span style="display:flex;"><span>│  Protocol                                               │
</span></span><span style="display:flex;"><span>│  CLAUDE.md conventions · CLI tools · MCP servers        │
</span></span><span style="display:flex;"><span>├─────────────────────────────────────────────────────────┤
</span></span><span style="display:flex;"><span>│  Agent                                                  │
</span></span><span style="display:flex;"><span>│  Claude Code · Codex · Cursor · markdown-aware agents   │
</span></span><span style="display:flex;"><span>├─────────────────────────────────────────────────────────┤
</span></span><span style="display:flex;"><span>│  Model                                                  │
</span></span><span style="display:flex;"><span>│  DeepSeek · Claude · Qwen · OpenAI · local models       │
</span></span><span style="display:flex;"><span>└─────────────────────────────────────────────────────────┘
</span></span></code></pre></td></tr></table>
</div>
</div><p>Each layer has one responsibility. More importantly, each layer can change without forcing the others to change.</p>
<h3 id="layer-1-model">Layer 1: Model</h3>
<p>The model layer is the LLM that provides reasoning capability: Claude, DeepSeek, Qwen, OpenAI, Gemini, a local model through Ollama or vLLM, or a third-party relay.</p>
<p>AI-Native Workspace treats the model as the most disposable layer. That is intentional. Model quality and pricing change constantly. A workflow that only works with one provider is fragile.</p>
<p>In practice, the framework favors provider swaps through environment configuration. You might use a cheap daily model for routine editing and a stronger model for deeper architecture work. The workspace does not need to know which one is currently running.</p>
<h3 id="layer-2-agent">Layer 2: Agent</h3>
<p>The agent layer is the software that reads instructions, reasons about the task, and calls tools. Today that might be Claude Code. Tomorrow it might be Codex CLI, Cursor, Aider, Continue, or a custom LangGraph agent.</p>
<p>The key design decision is that the workspace instructions are written in plain markdown. The file may be called <code>CLAUDE.md</code>, but the contents are not inherently Claude-only. Any agent that can read markdown can understand the conventions.</p>
<p>That means <code>CLAUDE.md</code> becomes an agent runtime config, not just a README. It tells the agent what projects exist, where state lives, how to end a session, how to resume work, and which docs are authoritative.</p>
<h3 id="layer-3-protocol">Layer 3: Protocol</h3>
<p>The protocol layer defines how the agent interacts with the world.</p>
<p>There are three levels:</p>
<ol>
<li><strong>File protocol</strong> — markdown conventions such as <code>CLAUDE.md</code>, <code>inbox/TODO.md</code>, and <code>wiki/MANIFEST.md</code>.</li>
<li><strong>CLI tools</strong> — git, curl, docker, Python, Node, Hugo, or any local command-line tool.</li>
<li><strong>MCP servers</strong> — optional integrations for search, databases, APIs, game servers, internal tools, and more.</li>
</ol>
<p>The important part is that MCP is not required. The lowest common denominator is a readable file. A markdown-aware agent can still operate the workspace even if no MCP server is available.</p>
<p>This is one of the most useful constraints in the project: <strong>file protocol is the floor; MCP is the ceiling.</strong></p>
<h3 id="layer-4-application">Layer 4: Application</h3>
<p>The application layer is the actual workspace: projects, inbox, wiki, work logs, and accumulated knowledge.</p>
<p>A minimal workspace looks like this:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>workspace/
</span></span><span style="display:flex;"><span>├── CLAUDE.md              # workspace-level cognitive config
</span></span><span style="display:flex;"><span>├── inbox/                 # low-friction capture buffer
</span></span><span style="display:flex;"><span>│   ├── README.md
</span></span><span style="display:flex;"><span>│   └── TODO.md
</span></span><span style="display:flex;"><span>├── projects/              # concrete work
</span></span><span style="display:flex;"><span>│   ├── project-a/
</span></span><span style="display:flex;"><span>│   │   └── CLAUDE.md
</span></span><span style="display:flex;"><span>│   └── project-b/
</span></span><span style="display:flex;"><span>│       └── CLAUDE.md
</span></span><span style="display:flex;"><span>└── wiki/                  # matured knowledge base
</span></span><span style="display:flex;"><span>    └── MANIFEST.md        # progressive-loading index
</span></span></code></pre></td></tr></table>
</div>
</div><p>This layer is where AI-Native Workspace becomes more than an agent config template. It defines a practical knowledge lifecycle.</p>
<h2 id="highlight-1-inbox-first-wiki-later">Highlight 1: Inbox First, Wiki Later</h2>
<p>The most valuable idea in the project may be the simplest one: <strong>do not force every note to become structured knowledge immediately.</strong></p>
<p>The <code>inbox/</code> folder is a draft buffer. Session summaries, temporary notes, half-formed ideas, TODOs, and meeting scraps can all land there. They do not need perfect taxonomy on day one.</p>
<p>Only when something becomes repeatedly useful does it graduate to the <code>wiki/</code>.</p>
<p>That is a better fit for real AI-agent work than trying to maintain a pristine knowledge base at all times. Agents generate lots of intermediate state. Humans also forget details between sessions. The inbox gives both sides a cheap place to put context before deciding what deserves permanence.</p>
<h2 id="highlight-2-progressive-context-loading">Highlight 2: Progressive Context Loading</h2>
<p>AI agents fail when they load too much irrelevant context. A workspace with hundreds of notes cannot be stuffed into every prompt.</p>
<p>AI-Native Workspace uses <code>wiki/MANIFEST.md</code> as a lightweight index. The agent reads the manifest first, sees one-line summaries, and then loads only the relevant files.</p>
<p>This is the same idea as a database index:</p>
<ul>
<li>scan the cheap index,</li>
<li>choose a few relevant entries,</li>
<li>fetch details only when needed.</li>
</ul>
<p>The result is a workspace that scales without turning every session into a context dump.</p>
<h2 id="highlight-3-layered-claudemd-files">Highlight 3: Layered <code>CLAUDE.md</code> Files</h2>
<p>The framework uses <code>CLAUDE.md</code> at multiple levels:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>~/.claude/CLAUDE.md              # global preferences
</span></span><span style="display:flex;"><span>&lt;workspace&gt;/CLAUDE.md            # workspace map and shared workflows
</span></span><span style="display:flex;"><span>&lt;workspace&gt;/&lt;project&gt;/CLAUDE.md  # project-specific instructions
</span></span></code></pre></td></tr></table>
</div>
</div><p>This prevents the root file from becoming a 500-line junk drawer.</p>
<p>The root <code>CLAUDE.md</code> should be a router: what exists, where to look, and what rules apply across the whole workspace. Project-level details stay inside each project. Stable knowledge moves into project docs or the wiki. Temporary state stays in the inbox.</p>
<p>That separation is boring in the best possible way. It makes the workspace readable after months of use.</p>
<h2 id="highlight-4-human-readable-agent-optimized">Highlight 4: Human-Readable, Agent-Optimized</h2>
<p>AI-Native Workspace is designed for agents, but it does not hide the system from humans.</p>
<p>That matters because humans are the fallback operator. APIs fail. Models regress. Tools break. Sometimes the agent simply gets confused. When that happens, a human should be able to read the same files and continue the work.</p>
<p>This is why the framework avoids magic as much as possible. Plain markdown, plain folders, plain CLI tools, explicit protocols.</p>
<h2 id="highlight-5-anti-patterns-as-first-class-design">Highlight 5: Anti-Patterns as First-Class Design</h2>
<p>The repo is unusually explicit about what not to do. A few examples:</p>
<table>
  <thead>
      <tr>
          <th>Avoid</th>
          <th>Prefer</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Start with multi-agent orchestration</td>
          <td>Use one agent; split only when context truly requires it</td>
      </tr>
      <tr>
          <td>Load every workspace file at boot</td>
          <td>Read an index first, then drill down</td>
      </tr>
      <tr>
          <td>Write directly to wiki for everything</td>
          <td>Capture in inbox first, let knowledge sediment</td>
      </tr>
      <tr>
          <td>Make root <code>CLAUDE.md</code> huge</td>
          <td>Keep root as a router; push details down</td>
      </tr>
      <tr>
          <td>Treat <code>TODO.md</code> as an unquestioned command queue</td>
          <td>Validate stale TODOs before acting</td>
      </tr>
      <tr>
          <td>Require MCP for core workflow</td>
          <td>Make files the core protocol; add MCP on demand</td>
      </tr>
  </tbody>
</table>
<p>This is a sign that the project came from real use rather than a clean-room diagram. The anti-patterns are scars from operating a multi-project AI workspace every day.</p>
<h2 id="who-should-use-it">Who Should Use It?</h2>
<p>AI-Native Workspace is useful if:</p>
<ul>
<li>you work with AI coding agents daily,</li>
<li>you run multiple projects in parallel,</li>
<li>you care about cross-session continuity,</li>
<li>you want to switch between model providers without rebuilding your workflow,</li>
<li>your work accumulates knowledge over months.</li>
</ul>
<p>It is probably overkill if you only have one short-lived project. In that case, a simple <code>CLAUDE.md</code> may be enough.</p>
<h2 id="the-bootstrap-file-is-the-product">The Bootstrap File Is the Product</h2>
<p>One interesting detail: the repo includes <code>BOOTSTRAP.md</code>, a document written for AI agents rather than humans.</p>
<p>The intended workflow is simple:</p>
<ol>
<li>Create an empty workspace directory.</li>
<li>Open Claude Code, Codex, Cursor, or another agent.</li>
<li>Paste <code>BOOTSTRAP.md</code> as the first prompt.</li>
<li>Let the agent create the initial structure.</li>
</ol>
<p>That makes the project self-instantiating: the framework explains itself to the agent that will build it.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>AI-Native Workspace is not trying to be a heavyweight platform. Its bet is almost the opposite: the durable layer of an AI-native workflow should be simple files, explicit conventions, and clear boundaries.</p>
<p>Models will change. Agents will change. MCP servers will come and go. But a well-structured workspace can survive those changes.</p>
<p>That is the core insight:</p>
<blockquote>
<p><strong>Do not build your workflow around a model. Build it around a workspace architecture that can swap models.</strong></p>
</blockquote>
<p>For anyone trying to turn an AI coding agent into a long-running research or engineering partner, that distinction matters.</p>
<p>Project link: <a href="https://github.com/RedDragonHQ/ai-native-workspace">RedDragonHQ/ai-native-workspace</a></p>
<p>If you have any questions, concerns, or want to understand how this works in a real workspace, feel free to contact us at <a href="mailto:contact@aibrew.ai">contact@aibrew.ai</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>How Claude Code&#39;s Agent Architecture Works — and How We Built a Similar System for a Terraria Server</title>
      <link>https://aibrew.ai/2026/05/how-claude-codes-agent-architecture-works-and-how-we-built-a-similar-system-for-a-terraria-server/</link>
      <pubDate>Wed, 27 May 2026 00:00:00 +0000</pubDate>
      <guid>https://aibrew.ai/2026/05/how-claude-codes-agent-architecture-works-and-how-we-built-a-similar-system-for-a-terraria-server/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — We reverse-engineered Claude Code&amp;rsquo;s agent architecture from its TypeScript source to understand how it handles security, complex tasks, and tool permissions. Then we applied those patterns to an open-source Terraria AI bridge that lets players talk to an LLM inside the game. Here&amp;rsquo;s what we found, what we built, and what we learned about practical agent design.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;why-we-cracked-open-claude-codes-source&#34;&gt;Why We Cracked Open Claude Code&amp;rsquo;s Source&lt;/h2&gt;
&lt;p&gt;Claude Code isn&amp;rsquo;t just a coding assistant. Under the hood it&amp;rsquo;s an agent runtime — it spawns sub-agents, manages file permissions, runs bash commands, and decides when to ask the user vs. just doing the thing. We wanted to understand how it works so we could apply the same ideas to a completely different domain: a Terraria game server.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong> — We reverse-engineered Claude Code&rsquo;s agent architecture from its TypeScript source to understand how it handles security, complex tasks, and tool permissions. Then we applied those patterns to an open-source Terraria AI bridge that lets players talk to an LLM inside the game. Here&rsquo;s what we found, what we built, and what we learned about practical agent design.</p>
</blockquote>
<hr>
<h2 id="why-we-cracked-open-claude-codes-source">Why We Cracked Open Claude Code&rsquo;s Source</h2>
<p>Claude Code isn&rsquo;t just a coding assistant. Under the hood it&rsquo;s an agent runtime — it spawns sub-agents, manages file permissions, runs bash commands, and decides when to ask the user vs. just doing the thing. We wanted to understand how it works so we could apply the same ideas to a completely different domain: a Terraria game server.</p>
<p>Our project, <a href="https://github.com/d99sfrmdbz-debug/terra_llm_bridge">terra_llm_bridge</a>, connects a Terraria TShock server to an LLM. Players type <code>@ai</code> in chat and get responses — but the LLM can also <em>act</em>: give items, change weather, teleport players, even toggle hardmode. That last one is where we learned our lesson.</p>
<p>The first time a player asked the AI to set the weather to rain, the LLM autonomously decided to call <code>terra_world_hardmode(confirm=True)</code> — toggling <em>irreversible</em> hardmode for the entire server. No player had asked for it. The model just&hellip; did it.</p>
<p>We needed a real permission system. So we went looking at how Claude Code does it.</p>
<hr>
<h2 id="claude-codes-7-layer-permission-architecture">Claude Code&rsquo;s 7-Layer Permission Architecture</h2>
<p>Reading through ~1,500 lines of <code>src/utils/permissions/permissions.ts</code> plus the Agent tool infrastructure (~3,800 lines), a clear architecture emerged. Claude Code doesn&rsquo;t have one security check — it has <strong>seven</strong>:</p>
<pre tabindex="0"><code>Layer 1a: Deny rules   →  &#34;Never allow Bash(git push --force)&#34;
Layer 1b: Ask rules    →  &#34;Always prompt for Bash(curl *)&#34;
Layer 1c: Tool self-check  →  Each tool&#39;s checkPermissions() method
Layer 1d: Tool self-deny   →  Read tool whitelists specific paths
Layer 1f: Content-specific rules  →  &#34;Even in bypass mode, ask for npm publish&#34;
Layer 1g: Safety checks  →  &#34;.git/, .claude/ are ALWAYS bypass-immune&#34;
Layer 2:  Mode-based bypass  →  bypassPermissions / auto / acceptEdits / dontAsk
Layer 3:  YOLO classifier →  AI reads the transcript, decides if safe
</code></pre><p>The most interesting layer is the <strong>YOLO classifier</strong> — a separate small model that reads the full conversation transcript and classifies each tool call as safe or dangerous. It&rsquo;s a two-stage system: a fast classifier for obvious cases, and a deeper thinking classifier for edge cases.</p>
<p>But the layer that matters most for our use case isn&rsquo;t the AI classifier. It&rsquo;s how Claude Code <strong>structurally prevents certain tools from being called in the wrong context</strong> — through tool allowlists, denylists, and sub-agent specialization.</p>
<hr>
<h2 id="the-agent-pattern-not-multi-agent-but-specialized-workers">The Agent Pattern: Not Multi-Agent, but Specialized Workers</h2>
<p>Claude Code doesn&rsquo;t use multi-agent &ldquo;collaboration&rdquo; in the negotiation sense. It uses a <strong>single coordinator that spawns specialized workers</strong>:</p>
<pre tabindex="0"><code>Main Agent (Tool Calling, all tools)
  │
  ├─ Simple: &#34;read file X&#34; → Read tool
  │
  └─ Complex: &#34;audit this branch&#34; → Agent(&#34;Explore&#34;)
                                       │
                                       ├─ Tools: [Read, Grep, Glob]  ← whitelist
                                       ├─ Disallowed: [Edit, Write]   ← denylist
                                       ├─ System prompt: &#34;You are a file search specialist&#34;
                                       └─ Returns findings → Main agent acts on them
</code></pre><p>Each sub-agent type is defined by three things:</p>
<ol>
<li><strong>Tool permissions</strong> (allowlist + denylist) — what it can touch</li>
<li><strong>System prompt</strong> — specialized instructions for its role</li>
<li><strong>Model</strong> — Explore agents use Haiku ($) for speed; Plan agents use Sonnet for reasoning</li>
</ol>
<p>The key insight: <strong>the main agent doesn&rsquo;t get more complex</strong>. It stays simple but has ONE tool (<code>Agent</code>) that lets it offload complex work. The sub-agent is just another Tool Calling loop with restricted tools and a different prompt.</p>
<p>This architecture is elegant because it composes: each piece is simple, but the combination handles complexity that would overwhelm a single prompt.</p>
<hr>
<h2 id="how-we-applied-this-to-terra_llm_bridge">How We Applied This to terra_llm_bridge</h2>
<p>Our Terraria bridge has a simpler job than Claude Code — 46 tools instead of hundreds, and the &ldquo;security&rdquo; problem is &ldquo;don&rsquo;t let the AI toggle hardmode when the player asked about weather&rdquo; rather than &ldquo;don&rsquo;t let the AI rm -rf /&rdquo;. But the patterns transfer directly.</p>
<h3 id="the-problem">The Problem</h3>
<p>Before: our LLM saw all 46 tools at once. When a player asked &ldquo;give me the strongest armor set,&rdquo; the LLM would fire <code>wiki_search</code> AND <code>give_item</code> in parallel — researching while also pre-committing to Solar Flare Armor before reading the wiki results. Sometimes it guessed right. Sometimes it gave a summoner player melee gear.</p>
<h3 id="our-solution-two-phase-tool-access">Our Solution: Two-Phase Tool Access</h3>
<p>We didn&rsquo;t add sub-agents — that would be overkill for 46 tools. Instead, we applied the <strong>tool restriction pattern</strong> at the graph level:</p>
<pre tabindex="0"><code>route → llm(research)  ⇄  tool      →  escalate  →  llm(action)  ⇄  authorize  ⇄  tool  →  output
         17 read tools                            46 full tools     keyword gate
         wiki, lookup, status                     give, kick, spawn
</code></pre><p>The graph has two phases:</p>
<p><strong>Research phase</strong> — the LLM gets only 17 read-only tools (wiki_search, item_lookup, player_list, world_info, etc.). It <em>cannot</em> call give_item, kick, spawn, or any destructive tool. It researches first.</p>
<p><strong>Escalate</strong> — when the LLM produces text (no more tool calls needed), the graph automatically flips to action mode and injects a hint: &ldquo;You now have access to ALL tools.&rdquo;</p>
<p><strong>Action phase</strong> — the LLM gets the full 46-tool set and can act on what it found.</p>
<p>This is structurally enforced. Not a prompt suggestion. The LLM physically cannot call <code>give_item</code> during research because the tool isn&rsquo;t bound.</p>
<h3 id="the-permission-gate">The Permission Gate</h3>
<p>Before the two-phase split, we also added <code>authorize_node</code> — a hard gate between the LLM and ToolNode that checks whether the player&rsquo;s recent chat messages contain keywords for the tool&rsquo;s domain:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>GATED_TOOLS <span style="color:#ff79c6">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#34;terra_world_hardmode&#34;</span>: {<span style="color:#f1fa8c">&#34;hardmode&#34;</span>, <span style="color:#f1fa8c">&#34;hard mode&#34;</span>, <span style="color:#f1fa8c">&#34;肉山&#34;</span>, <span style="color:#f1fa8c">&#34;困难模式&#34;</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#34;terra_player_kick&#34;</span>:    {<span style="color:#f1fa8c">&#34;kick&#34;</span>, <span style="color:#f1fa8c">&#34;踢出&#34;</span>, <span style="color:#f1fa8c">&#34;踢了&#34;</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#34;terra_server_stop&#34;</span>:    {<span style="color:#f1fa8c">&#34;stop server&#34;</span>, <span style="color:#f1fa8c">&#34;关服&#34;</span>, <span style="color:#f1fa8c">&#34;停服&#34;</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4"># ... 8 more</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></td></tr></table>
</div>
</div><p>If the player says &ldquo;set weather to rain&rdquo; and the LLM tries to call <code>world_hardmode</code>, authorize_node checks: do any of the hardmode keywords appear in the player&rsquo;s recent messages? No? <strong>Blocked.</strong> The tool call is replaced with a BLOCKED message before ToolNode ever sees it.</p>
<p>This is a coarse filter — it checks what the player <em>mentioned</em>, not what they <em>requested</em>. &ldquo;上次打肉山的时候&rdquo; (last time when I fought Wall of Flesh) would pass the keyword check even though the player didn&rsquo;t ask for hardmode. But coarse is fine here: the goal is blocking catastrophic mismatches (weather → hardmode), not perfect intent understanding.</p>
<hr>
<h2 id="what-we-chose-not-to-build">What We Chose NOT to Build</h2>
<h3 id="no-yolo-classifier">No YOLO Classifier</h3>
<p>Claude Code&rsquo;s AI classifier reads the full transcript and classifies tool calls as safe/dangerous. We didn&rsquo;t build this because:</p>
<ul>
<li>It adds latency — an extra LLM call before every gated tool execution</li>
<li>Terraria chat is low-stakes — a false positive (giving the wrong armor) is fixable</li>
<li>Keyword matching catches the catastrophic cases</li>
</ul>
<h3 id="no-sub-agent-spawning">No Sub-Agent Spawning</h3>
<p>Claude Code spawns sub-agent processes for complex tasks. We didn&rsquo;t need this because:</p>
<ul>
<li>Terraria tool surface is small (46 tools)</li>
<li>Multi-turn tool calling handles the complexity we actually face</li>
<li>Spawning sub-processes for a game chat bot is over-engineering</li>
</ul>
<h3 id="no-react-pattern">No ReAct Pattern</h3>
<p>The classic Thought → Action → Observation loop would add token overhead without changing our core capability. DeepSeek&rsquo;s thinking tokens already handle the reasoning, and the two-phase tool access enforces &ldquo;research before action&rdquo; more reliably than prompt-based ReAct would.</p>
<hr>
<h2 id="the-architecture-in-one-diagram">The Architecture in One Diagram</h2>
<pre tabindex="0"><code>┌──────────────────────────────────────────────────────────┐
│  Terraria Server (TShock + C# plugin, 24 game hooks)      │
│  Player types &#34;@ai give me the best armor&#34;                │
└──────────────────────┬───────────────────────────────────┘
                       │ JSON webhook
┌──────────────────────▼───────────────────────────────────┐
│  Python aiohttp listener (:9876)                          │
└──────────────────────┬───────────────────────────────────┘
                       │
┌──────────────────────▼───────────────────────────────────┐
│  LangGraph StateGraph                                     │
│                                                           │
│  route  →  llm(research)  ⇄  tool    17 read tools      │
│               │                                           │
│          escalate  →  llm(action)  ⇄  authorize  ⇄  tool │
│                          46 full tools    keyword gate    │
│               │                                           │
│             output  →  broadcast to game chat             │
│                                                           │
│  Memory: AsyncSqliteSaver per player (thread_id)          │
└──────────────────────────────────────────────────────────┘
                       │
         ┌─────────────┴──────────────┐
         ▼                            ▼
   TShock REST API              Terraria Wiki API
   (give / kick / spawn)        (terraria.wiki.gg)
</code></pre><hr>
<h2 id="source-diving-lessons">Source Diving Lessons</h2>
<p>Reading Claude Code&rsquo;s source taught us three things that apply to any agent project:</p>
<p><strong>1. Security is layered, not binary.</strong> A single <code>confirm</code> parameter is a soft suggestion to the LLM. Real security needs structural enforcement — the LLM shouldn&rsquo;t be able to call a tool it isn&rsquo;t authorized to use, same way a web server shouldn&rsquo;t let you access endpoints without authentication, no matter how nicely you ask.</p>
<p><strong>2. Tool restrictions are the cheapest and most reliable form of safety.</strong> Claude Code&rsquo;s Explore agent is &ldquo;read-only&rdquo; not because of a prompt — because Edit and Write aren&rsquo;t in its tool list. Our research phase isn&rsquo;t &ldquo;research-first&rdquo; because of a prompt — because give_item literally isn&rsquo;t bound. You can&rsquo;t prompt-inject your way past a tool that doesn&rsquo;t exist.</p>
<p><strong>3. Specialization beats complexity.</strong> Claude Code&rsquo;s sub-agents aren&rsquo;t smarter than the main agent — they&rsquo;re more constrained. Fewer tools + focused prompt = more reliable behavior. Our two-phase system does the same: constrain first, expand only when ready.</p>
<hr>
<h2 id="the-project">The Project</h2>
<p><code>terra_llm_bridge</code> is an open-source project connecting Terraria game servers to LLMs. It features:</p>
<ul>
<li><strong>24 game hooks</strong> — custom C# TShock plugin captures chat, boss kills, deaths, logins, and 20 more events</li>
<li><strong>46 admin tools</strong> — give items, manage players, control weather, spawn NPCs, manage regions and permissions</li>
<li><strong>Two-phase agent</strong> — research (17 tools) → action (46 tools)</li>
<li><strong>Hard permission gate</strong> — keyword-based authorize_node blocks unauthorized tool calls</li>
<li><strong>MCP server</strong> — same 46 tools exposed to Claude Code for server administration</li>
<li><strong>Persistent memory</strong> — per-player conversation history via LangGraph&rsquo;s AsyncSqliteSaver</li>
</ul>
<p>The project is currently in <strong>active testing</strong> and not yet published on GitHub. We&rsquo;re running it on a private Terraria server, iterating on the agent architecture before open-sourcing. If you&rsquo;re interested in the code or want early access, reach out.</p>
<hr>
<p><em>Built with: Python 3.14, LangGraph 1.x, DeepSeek (Anthropic-compatible API), C# .NET 9, TShock v6.1.0, aiohttp, httpx.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
