Channel Agent Runtime Boundary
This document defines the runtime boundary between editable channel agents and normal bot0 skills.
It exists because user-created channel agents should be able to run from their own package prompt without carrying the full interactive bot0 system prompt.
See also: Editable Agent Workspace and Channel Agent Builder System.
Summary
bot0 now has two related but different execution paths:
| Runtime | System prompt source | Typical use |
|---|---|---|
| Agent | The agent package itself | Long-lived channel agents that receive Telegram/Slack/WhatsApp/SMS messages |
| Skill | The normal bot0 system prompt plus loaded skill instructions | Specialized actions inside a bot0 session or sub-workflow |
For file-backed channel agents, the daemon builds a lean agent-owned system prompt from:
agent.json- the package source and directories
- the package state directory
- the entry skill's
SKILL.md - a small channel-agent runtime contract
The daemon passes that prompt as a trusted systemPrompt override into the task runner. Because the override is present, Bot0Agent.run() does not call the default interactive bot0 system prompt builder for that channel-agent turn.
Why This Exists
The default bot0 prompt is useful for the desktop coding assistant. It includes local project context, artifact rules, daemon-network behavior, broad tool guidance, and other instructions that a channel agent usually does not need.
A channel agent should instead behave like a small deployed worker:
- it has one domain-specific job
- it responds directly to the external channel user
- it uses only the tools declared by its package
- it stores state only in its own package state directory
- it does not inspect the bot0 monorepo or copy existing agent packages
This saves tokens and reduces accidental behavior where a generated channel agent starts acting like the full bot0 coding assistant.
Runtime Flow
For a channel message routed to a file-backed agent:
- The channel adapter normalizes the provider event.
- Route resolution selects
agentId. - The agent registry resolves that ID to a packaged
AgentTarget. - The packaged target builds an agent-owned system prompt from the package.
- The target invokes the task runner with:
- normalized prompt
- channel context
- agent-owned
systemPrompt - package
allowedTools
- The IPC task runner sends
systemPromptthroughrunTask. - The daemon passes
systemPromptdirectly toBot0Agent.run(). - The final assistant text is sent back through the channel adapter.
The important invariant is:
file-backed channel agent run => package-owned system prompt normal bot0 or skill run => default bot0 system prompt path
Package-Owned Prompt
The generated channel-agent prompt includes:
You are <label>, a file-backed channel agent. Agent id: <id> Agent package directory: <path> Agent skills directory: <path> Agent state directory: <path>/state Entry skill: <entrySkill> Runtime tool allowlist: <allowedTools> Runtime contract: - respond directly to the external channel user - keep transport/tool/workflow details invisible - stay inside the package boundary - do not inspect bot0 source or other agent packages - do not store secrets - use execute_skill for sub-workflows when needed Entry skill: <contents of skills/<entrySkill>/SKILL.md>
This makes the entry skill the agent's runtime behavior without requiring the agent to first load the skill through the full bot0 prompt.
Tool Boundary
agent.json.allowedTools narrows the runtime tool registry for the channel agent.
Example:
{ "allowedTools": ["execute_skill", "read", "write_file"] }
Rules:
allowedToolscan only narrow what the daemon already permits.skillis no longer automatically injected just so the agent can load its entry skill.- Add
skillonly if the package explicitly needs theskilltool. - Use
execute_skillfor sub-skills. - Prefer the smallest practical tool set.
This gives generated agents a clearer deployment profile and keeps accidental source-code exploration less likely.
Skill Boundary
Normal skills keep their existing behavior.
When bot0 loads or executes a skill from an interactive session, that run still uses the normal bot0 system prompt plus the selected skill instructions. This is intentional: skills are specialized capabilities inside bot0, not standalone channel workers.
Sub-skills called by a channel agent through execute_skill also execute as skill runs. The parent channel agent remains lean, while the sub-skill gets its own focused skill instructions.
Marketplace Implications
The package is the installable unit for an agent marketplace.
A marketplace agent should contain:
agent.jsonskills/<entrySkill>/SKILL.md- optional sub-skills
- optional benchmark files
- optional state schema or empty state files
- documentation
A marketplace agent must not contain:
- API keys
- bot tokens
- OAuth secrets
- webhook secrets
- user credentials
- machine-specific absolute paths
Installing an agent should copy the package into ${getChannelPaths().bot0Dir}/agents/<agent-id>, validate it, then deploy it only through route preflight.
Verification
Focused checks:
node --import tsx --test \ packages/daemon/src/agents/package-target.test.ts \ packages/daemon/src/integrations/channel/ipc-task-runner.test.ts pnpm --filter @bot0/daemon typecheck
The regression tests assert that:
- the packaged agent prompt embeds the entry skill
- the prompt does not contain default bot0 prompt markers such as
Current working directory: - the packaged target passes
systemPromptto the task runner - the IPC channel task runner preserves that
systemPromptthroughrunTask