// Configuring agents — agents.yaml + bindings.yaml
function AgentsGuide() {
  const shell = useShell();
  const tocItems = [
    { id: 'model', label: 'The agent model' },
    { id: 'files', label: 'Files that define an agent' },
    { id: 'agents-yaml', label: 'agents.yaml' },
    { id: 'identity', label: 'IDENTITY.md' },
    { id: 'bindings', label: 'bindings.yaml' },
    { id: 'adding', label: 'Adding a new agent' },
    { id: 'tools', label: 'Tool allowlists' },
    { id: 'multi', label: 'Multi-agent patterns' },
    { id: 'gotchas', label: 'Gotchas' },
  ];

  return (
    <div className="app">
      <Topbar section="docs" theme={shell.theme} setTheme={shell.setTheme}
              onSearch={() => shell.setSearchOpen(true)}
              onMenuToggle={() => shell.setMobileMenuOpen(true)} />
      <div className="main">
        <Sidebar activeId="agents-guide"
                 mobileOpen={shell.mobileMenuOpen}
                 onMobileClose={() => shell.setMobileMenuOpen(false)} />
        <article className="content">
          <div className="crumbs">
            <a href="index.html">Docs</a>
            <span className="sep">/</span>
            <a href="#">Concepts</a>
            <span className="sep">/</span>
            <span>Configuring agents</span>
          </div>

          <div className="eyebrow">Concepts · ~8 min</div>
          <h1 className="h1">Configuring <em>agents.</em></h1>
          <p className="lede">
            A tenant can run as many agents as they need. Each agent is a YAML entry + an{' '}
            <code>IDENTITY.md</code>. The runtime routes inbound traffic to the right agent based
            on <code>bindings.yaml</code>. No code changes, no redeploy.
          </p>

          <h2 id="model" className="h2">The agent model</h2>
          <p>
            An agent is identified by a short id (<code>admin</code>, <code>cs</code>,{' '}
            <code>research</code>, <code>sales-ops</code>, ...). Each agent has:
          </p>
          <ul>
            <li>An <strong>identity</strong> — who it is, how it speaks, what it knows.</li>
            <li>A <strong>tool allowlist</strong> — which tools from the registry it can invoke.</li>
            <li>A set of <strong>bindings</strong> — which channel/sender combinations route to it.</li>
            <li>Shared access to the tenant's <strong>skills</strong> and <strong>memory</strong> by default.</li>
          </ul>

          <h2 id="files" className="h2">Files that define an agent</h2>
          <CodeBlock tabs={[{ label: 'layout', raw: `/data/tenants/{id}/\n├── agents.yaml            # list of agents for this tenant\n├── bindings.yaml          # (channel, sender) -> agent routing rules\n├── IDENTITY.md            # default identity (fallback)\n├── IDENTITY.research.md   # identity override for the 'research' agent\n├── IDENTITY.sales.md      # identity override for the 'sales-ops' agent\n├── SOUL.md                # shared values / tone across all agents\n└── USER.md                # shared facts about the tenant owner`,
            code: `/data/tenants/{id}/
├── agents.yaml            <span class="tok-com"># list of agents for this tenant</span>
├── bindings.yaml          <span class="tok-com"># (channel, sender) -&gt; agent routing rules</span>
├── IDENTITY.md            <span class="tok-com"># default identity (fallback)</span>
├── IDENTITY.research.md   <span class="tok-com"># identity override for the 'research' agent</span>
├── IDENTITY.sales.md      <span class="tok-com"># identity override for the 'sales-ops' agent</span>
├── SOUL.md                <span class="tok-com"># shared values / tone across all agents</span>
└── USER.md                <span class="tok-com"># shared facts about the tenant owner</span>` }]} />

          <h2 id="agents-yaml" className="h2">agents.yaml</h2>
          <CodeBlock tabs={[{ label: 'agents.yaml', raw: `agents:\n  - id: admin\n    description: Owner-facing agent, broad toolset\n    # inherits default IDENTITY.md\n\n  - id: cs\n    description: Customer-facing agent, read-only + messaging\n    tools:\n      allow: [read, ls, grep, web_search, web_fetch, memory_query, faq_search, pdf, message, load_skill]\n\n  - id: research\n    description: Read-only research assistant for long-form questions\n    identity: IDENTITY.research.md\n    tools:\n      allow: [read, ls, find, grep, web_search, web_fetch, memory_query, load_skill]`,
            code: `<span class="tok-var">agents</span>:
  - <span class="tok-var">id</span>: admin
    <span class="tok-var">description</span>: Owner-facing agent, broad toolset
    <span class="tok-com"># inherits default IDENTITY.md</span>

  - <span class="tok-var">id</span>: cs
    <span class="tok-var">description</span>: Customer-facing agent, read-only + messaging
    <span class="tok-var">tools</span>:
      <span class="tok-var">allow</span>: [read, ls, grep, web_search, web_fetch, memory_query, faq_search, pdf, message, load_skill]

  - <span class="tok-var">id</span>: research
    <span class="tok-var">description</span>: Read-only research assistant for long-form questions
    <span class="tok-var">identity</span>: IDENTITY.research.md
    <span class="tok-var">tools</span>:
      <span class="tok-var">allow</span>: [read, ls, find, grep, web_search, web_fetch, memory_query, load_skill]` }]} />

          <h2 id="identity" className="h2">IDENTITY.md</h2>
          <p>
            Each agent's <code>IDENTITY.md</code> is prepended to the system prompt. Keep it
            short — under 500 tokens is a good target. State the role, voice, and any hard rules:
          </p>
          <CodeBlock tabs={[{ label: 'IDENTITY.research.md', raw: `# Research agent\n\nYou are a research assistant for this tenant. Your job is to help the owner answer\nquestions that need web sources, past memory, or long-form synthesis. You are NOT a\ngeneral-purpose assistant for scheduling, sending messages, or running commands.\n\n## Voice\n\n- Direct. Cite sources.\n- Prefer bullet points for comparisons; prose for explanations.\n\n## Hard rules\n\n- Never fabricate a source. If a citation is uncertain, say so.\n- If the question is outside research (e.g., "send a message to Alex"), hand off to\n  the admin agent via sessions_spawn.`,
            code: `<span class="tok-kw"># Research agent</span>

You are a research assistant for this tenant. Your job is to help the owner answer
questions that need web sources, past memory, or long-form synthesis. You are NOT a
general-purpose assistant for scheduling, sending messages, or running commands.

<span class="tok-kw">## Voice</span>

- Direct. Cite sources.
- Prefer bullet points for comparisons; prose for explanations.

<span class="tok-kw">## Hard rules</span>

- Never fabricate a source. If a citation is uncertain, say so.
- If the question is outside research (e.g., <span class="tok-str">"send a message to Alex"</span>), hand off to
  the admin agent via sessions_spawn.` }]} />

          <h2 id="bindings" className="h2">bindings.yaml</h2>
          <p>
            Routes inbound traffic to an agent based on{' '}
            <code>(channel, sender)</code>. First matching rule wins.
          </p>
          <CodeBlock tabs={[{ label: 'bindings.yaml', raw: `bindings:\n  # The web-admin channel always goes to the admin agent\n  - channel: web-admin\n    agent: admin\n\n  # Research inbox — a specific Telegram handle\n  - channel: telegram\n    sender: tg:+15551230000\n    agent: research\n\n  # Default CS for all other inbound traffic\n  - channel: telegram\n    agent: cs\n  - channel: whatsapp_web\n    agent: cs\n\n  # Anything else\n  - agent: admin`,
            code: `<span class="tok-var">bindings</span>:
  <span class="tok-com"># The web-admin channel always goes to the admin agent</span>
  - <span class="tok-var">channel</span>: web-admin
    <span class="tok-var">agent</span>: admin

  <span class="tok-com"># Research inbox — a specific Telegram handle</span>
  - <span class="tok-var">channel</span>: telegram
    <span class="tok-var">sender</span>: tg:+15551230000
    <span class="tok-var">agent</span>: research

  <span class="tok-com"># Default CS for all other inbound traffic</span>
  - <span class="tok-var">channel</span>: telegram
    <span class="tok-var">agent</span>: cs
  - <span class="tok-var">channel</span>: whatsapp_web
    <span class="tok-var">agent</span>: cs

  <span class="tok-com"># Anything else</span>
  - <span class="tok-var">agent</span>: admin` }]} />

          <h2 id="adding" className="h2">Adding a new agent</h2>
          <ol>
            <li>Append an entry to <code>agents.yaml</code> with <code>id</code>, <code>description</code>, optional <code>identity</code>, optional <code>tools.allow</code>.</li>
            <li>Write the <code>IDENTITY.&lt;agent-id&gt;.md</code> if you set one.</li>
            <li>Add or update <code>bindings.yaml</code> rules so traffic routes to the new agent.</li>
            <li>The next inbound request picks up the change — no restart.</li>
          </ol>
          <Callout type="note" title="No DB migration">
            Adding an agent is pure filesystem. The SQLite <code>sessions</code> index gains a new
            <code>agent</code> value on first use; no schema change.
          </Callout>

          <h2 id="tools" className="h2">Tool allowlists</h2>
          <p>
            Omit <code>tools.allow</code> to inherit the domain-gated defaults (admin → all;
            non-admin → the read-only subset). Set <code>tools.allow</code> explicitly for a
            narrower or custom set. The intersection of (your allowlist) and (each tool's{' '}
            <code>agents</code> declaration) is what the LLM sees.
          </p>

          <h2 id="multi" className="h2">Multi-agent patterns</h2>
          <ul>
            <li><strong>Owner + customer split.</strong> Admin answers the owner on web + Telegram DMs; CS answers external contacts on chat-app webhooks.</li>
            <li><strong>Task specialists.</strong> <code>research</code> (read-heavy, web-capable), <code>ops</code> (scheduling + cron), <code>comms</code> (message + summarization) — the admin agent <code>sessions_spawn</code>s into one of them for focused subtasks.</li>
            <li><strong>Per-senior-user agent.</strong> One staff member, one identity, different voices for different contexts — same memory, different IDENTITY.md overrides.</li>
          </ul>

          <h2 id="gotchas" className="h2">Gotchas</h2>
          <ul>
            <li><strong>Memory is shared by default.</strong> All agents read the same <code>MEMORY.md</code>. If you want per-agent memory isolation, use separate tenants.</li>
            <li><strong>Binding order matters.</strong> Put specific rules (by sender) before general ones (by channel). First match wins.</li>
            <li><strong>The LLM protocol role "assistant" is unrelated.</strong> That's the OpenAI/Anthropic message-format role. The <em>agent concept</em> (admin, cs, research) is a runtime choice — not the LLM's message role.</li>
          </ul>

          <Feedback />
          <PageFoot
            prev={{ label: 'Writing skills', href: 'skills-guide.html' }}
            next={{ label: 'API reference', href: 'api-reference.html' }}
          />
        </article>
        <TOC items={tocItems} />
      </div>
      <SearchOverlay open={shell.searchOpen} onClose={() => shell.setSearchOpen(false)} />
      <TweaksPanel visible={shell.tweaksVisible} theme={shell.theme} setTheme={shell.setTheme} />
    </div>
  );
}
ReactDOM.createRoot(document.getElementById('root')).render(<AgentsGuide />);
