channel-agent-builder-system.md

Channel Agent Builder System

This document describes the editable channel-agent system now implemented in bot0.

It covers how bot0 creates local file-backed agents, how those agents become channel targets, and how the builder avoids relying on the bot0 monorepo or existing package examples.

See also: Channel Agent Runtime Boundary, which documents the package-owned system prompt used when these agents run.


Summary

bot0 can now create user-owned channel agents as local packages under the bot0 data directory:

txt
~/.bot0/agents/<agent-id>/ agent.json skills/ <entry-skill>/ SKILL.md sub-skills.json benchmarks.json <optional-sub-skill>/ SKILL.md sub-skills.json benchmarks.json state/ README.md

The daemon discovers these packages, exposes production packages as channel AgentTargets, and routes incoming Telegram/Slack/WhatsApp/SMS messages by agentId.

The core idea is:

  • agent_create is the canonical skeleton generator.
  • channel-agent-builder is the protected system skill that teaches bot0 how to use the tools.
  • existing packages are not examples to copy.
  • channel connection and route state is inspected through tools, not config files.
  • secrets never live in agent packages.

Components

ComponentPurpose
~/.bot0/agentsUser-owned editable agent packages. In dev, this may be ~/.bot0-dev/agents or BOT0_DATA_DIR/agents.
agent.jsonPackage manifest: id, label, description, entry skill, status, allowed tools, channel defaults.
skills/*Skill-backed runtime behavior for the agent. The entry skill orchestrates optional sub-skills.
state/Agent-local durable state files. Skills should refer to runtime-provided state context instead of hardcoded absolute paths.
channel-agent-builderProtected system skill installed under .system; hidden from the editable Skills library.
Agent workspace toolsCreate, read, validate, update, and deploy packages.
Channel route toolsList connections/routes and preflight deployments without reading config directly.

Builder Skill

channel-agent-builder is installed at daemon startup into:

txt
~/.bot0/skills/.system/channel-agent-builder

It is protected:

  • ownershipKind: "system"
  • isProtected: true
  • isVisibleInLibrary: false
  • canEdit: false

The builder skill is used when a user asks to create, modify, validate, preflight, or deploy a channel agent.

Critical builder rules:

  • Do not inspect bot0 source code, repo docs, or daemon config files.
  • Do not inspect existing bundled or user agent package files to copy structure.
  • Use agent_list only for metadata and id collision awareness.
  • Use agent_create as the source of truth for the package skeleton.
  • Use agent_read only when modifying the specific user-owned agent named by the user.
  • Use channel route tools instead of reading config.
  • Do not claim deployment is live until agent_deploy_channel succeeds.

Creation Flow

For a new channel agent, bot0 should follow this flow:

  1. Load channel-agent-builder.
  2. Call agent_list only to check existing IDs and names.
  3. Choose a valid lowercase agentId.
  4. Choose the narrowest practical allowedTools.
  5. Call agent_create.
  6. Edit only files inside the new package returned by agent_create.
  7. Call agent_validate.
  8. If deployment was requested, run route preflight and then deploy only if safe.

Example agent_create call for a multi-skill stateful agent:

json
{ "id": "meeting-miner", "label": "Meeting Miner", "description": "Turns messy meeting notes into concise briefs and tracks open action items.", "entrySkill": "meeting-miner", "status": "production", "allowedTools": ["execute_skill", "read", "write_file"], "subSkills": [ { "id": "meeting-miner-parser", "description": "Parse messy meeting notes into structured decisions, actions, blockers, and questions." }, { "id": "meeting-miner-action-tracker", "description": "Normalize, merge, and update tracked action items." }, { "id": "meeting-miner-response-writer", "description": "Write concise Telegram-friendly replies." } ], "stateFiles": ["meeting-log.json", "action-items.json"] }

agent_create creates:

  • the manifest
  • the entry skill
  • each requested sub-skill
  • sub-skills.json wiring
  • empty benchmark files
  • requested state files under state/
  • README.md

This removes the need for bot0 to browse existing packages for structure.


Workspace Tools

agent_list

Lists package metadata only:

  • id
  • label
  • description
  • entrySkill
  • status
  • source

It intentionally does not expose directory or skillsDirectory. This keeps new-agent creation from drifting into "copy an existing package" behavior.

agent_read

Reads one user-owned package and returns editable locations for that target package.

Use only when modifying an existing user agent.

agent_create

Creates a user-owned package under ${getChannelPaths().bot0Dir}/agents.

Important inputs:

  • id
  • label
  • description
  • entrySkill
  • status
  • allowedTools
  • subSkills
  • stateFiles
  • channelDefaults
  • overwrite

Guardrails:

  • validates ID and skill names
  • rejects path traversal
  • rejects bundled ID collisions by default
  • rejects secret-looking manifest values
  • clears discovery caches after writes

agent_update_manifest

Updates agent.json for a user-owned package.

It preserves the same manifest validation and secret rejection rules as creation.

agent_validate

Validates user packages.

Current checks include:

  • entry skill exists
  • declared sub-skills exist
  • skill files do not hardcode local agent workspace paths such as ~/.bot0-dev/agents/...

Validation returns a structured checks array with valid and errors.

agent_deploy_channel

Creates or replaces a daemon channel route for an agent.

Deployment now runs the same preflight logic used by channel_route_preflight. If an enabled route already owns the target connection, deployment fails unless disableConflictingRoutes=true is explicitly passed.

disableConflictingRoutes=true should only be used after the user explicitly approves disabling an existing active route.


Channel Route Tools

channel_connection_list

Lists configured channel connections using safe metadata only.

It redacts secret values. For example, Telegram bot tokens are shown only as booleans such as:

json
{ "hasBotToken": true }

It does not expose raw tokens, webhook secrets, API keys, passwords, or credentials.

channel_route_list

Lists existing channel routes:

  • route id
  • route name
  • enabled state
  • channel
  • connection id
  • agent id
  • route scope

channel_route_preflight

Checks whether a deployment can safely proceed.

It reports:

  • missing channel connections
  • disabled connections
  • channel/connection mismatch
  • broad any scope warnings
  • active route conflicts
  • available connections for that channel

The builder should call this before agent_deploy_channel.


Runtime Behavior

The channel runtime path is:

  1. channel adapter normalizes the inbound event.
  2. route resolver finds a route and agentId.
  3. the agent registry resolves the file-backed AgentTarget.
  4. the target builds a lean agent-owned system prompt from agent.json and the entry skill SKILL.md.
  5. the target invokes the daemon task runner with that systemPrompt override and the package allowedTools.
  6. the entry skill, embedded in the agent prompt, handles the domain behavior.
  7. optional sub-skills run through execute_skill.
  8. the final response is sent back through the channel adapter.

File-backed agent prompts include:

txt
You are <label>, a file-backed channel agent. Agent id: <agent-id> Agent package directory: <path> Agent skills directory: <path> Agent state directory: <path> Entry skill: <entry-skill> <entry_skill> ...SKILL.md... </entry_skill>

The packaged agent run does not use the default interactive bot0 system prompt. Normal skills still use the regular bot0 prompt plus loaded skill instructions.

Skills should use the runtime-provided state location instead of hardcoding absolute paths.

The channel gateway rebuilds the default agent registry dynamically when a fixed registry is not supplied, so newly created agents can be routed without rebuilding the app.


Tool Scope

agent.json.allowedTools narrows runtime access for the channel agent. It cannot expand daemon permissions.

The runtime does not automatically add skill. The entry skill is already embedded in the package-owned agent prompt. Add skill only when the package explicitly needs the skill tool.

For most stateful orchestrator agents, a narrow tool set is:

json
["execute_skill", "read", "write_file"]

Add tools only when the agent behavior requires them. Avoid edit_file unless the agent truly needs patch-style edits.


Deployment Safety

Routes are effectively exclusive per channel connection.

If one enabled route already owns a Telegram bot connection, deploying another enabled route to the same connection would cause one route to be normalized disabled. The new flow prevents silent failure:

  1. channel_route_preflight reports the conflict.
  2. agent_deploy_channel rejects the deployment by default.
  3. deployment can disable conflicting routes only with explicit disableConflictingRoutes=true.

This keeps bot0 from accidentally replacing a live agent route.


Security Boundaries

Agent packages may contain:

  • prompts
  • skill instructions
  • non-secret metadata
  • state files
  • route references by ID

Agent packages must not contain:

  • bot tokens
  • OAuth secrets
  • API keys
  • webhook secrets
  • passwords
  • credentials

Channel connections and provider credentials remain in existing config, connector, proxy, or secure storage surfaces.


Current Media Limitation

Telegram image/file/audio messages can be normalized with metadata and, when downloads are enabled, local file paths.

The current generic channel-agent path does not automatically pass image bytes into the model as multimodal input. Image-dependent agents should not claim they can inspect pixels unless an explicit OCR/image-analysis tool or parser path is available.

For now, a receipt or image-based agent should either:

  • require pasted text or a useful caption, or
  • use a dedicated OCR/image tool once one exists.

Example User Prompt

A good non-handholding test prompt:

txt
Create me a Telegram channel agent named meeting-miner. It should turn messy meeting notes or transcript chunks into concise meeting briefs, keep track of open action items, and support basic commands for status, completed actions, blockers, due-today items, and recaps. Use a few focused sub-skills where it makes sense. Keep replies short and Telegram-friendly. Validate it, and deploy it to Telegram DM if that can be done safely with the configured connections.

The expected behavior is that bot0:

  • uses agent_create with subSkills and stateFiles
  • does not inspect existing package files
  • validates the package
  • calls channel_connection_list
  • calls channel_route_list
  • calls channel_route_preflight
  • deploys only if preflight passes

Verification

Focused regression coverage includes:

  • protected channel-agent-builder install/discovery
  • metadata-only agent_list
  • agent_create sub-skill and state-file scaffolding
  • hardcoded local path validation
  • route conflict rejection
  • explicit conflict disabling
  • safe channel connection metadata redaction
  • channel route preflight blockers

Useful local checks:

bash
node --import tsx --test \ packages/daemon/src/tools/agent-workspace.test.ts \ packages/daemon/src/tools/channel-workspace.test.ts \ packages/daemon/src/skill/system-skills.test.ts pnpm --filter @bot0/daemon typecheck
Archived product

A chapter of bot0, preserved.

bot0 was a working product by Bytespace Labs. This site preserves its original design and product experience. The hosted service is no longer running; downloads, new accounts and purchases are unavailable.

Product descriptions, documentation and pricing reflect the product when it was active. The interactions preserved here are not connected to its former backend.

Interested in the technology?

We’re open to discussing an acquisition of the technology and codebase behind bot0.

Discuss an acquisition