editable-agent-workspace-spec.md

Editable Agent Workspace Spec

This document specifies the local file-backed agent workspace that lets bot0 create, inspect, edit, run, and deploy user agents from files under the bot0 data directory.

The workspace is the foundation for user-created channel agents.

See also: Channel Agent Builder System, which documents the implemented builder skill, agent tools, channel route tools, and deployment guardrails.

See also: Channel Agent Runtime Boundary, which documents why file-backed agents use package-owned system prompts instead of the default interactive bot0 prompt.


Summary

bot0 agents should be editable local packages:

txt
~/.bot0/agents/ my-agent/ agent.json skills/ my-agent/ SKILL.md sub-skills.json benchmarks.json executables/ state/ README.md

The daemon discovers these folders, exposes them as channel AgentTargets, and routes channel messages to the package's entry skill.

The important boundary is:

  • agent behavior lives in editable files.
  • daemon TypeScript provides generic runtime, tools, validation, storage, permissions, and channel transport.
  • secrets never live in the agent workspace.

Implemented Runtime Hooks

The current runtime has these pieces in place:

  • ~/.bot0 is resolved through getChannelPaths().bot0Dir in packages/core/src/runtime.ts.
  • channel routes support agentId in packages/daemon/src/config.ts.
  • ChannelGateway resolves route.agentId and invokes an AgentTarget.
  • bundled agents are discovered from packages/daemon/src/agents/catalog/*/agent.json.
  • user agents are discovered from ${getChannelPaths().bot0Dir}/agents/*/agent.json.
  • bundled packages win on ID collisions.
  • packaged agent targets load behavior through entrySkill.
  • skill discovery scans both bundled and user agent skill roots.
  • user agent skills are classified as editable bot0 user skills.
  • channel gateway registry resolution can pick up newly created user agents without an app rebuild.
  • channel-agent-builder gives bot0 the workflow for creating and modifying agents.
  • packaged channel agents run with an agent-owned system prompt built from agent.json and the entry skill, not the default interactive bot0 system prompt.

Design Goals

  1. bot0 can create an agent from a prompt by writing local files.
  2. bot0 can iterate on that agent by editing the same files later.
  3. channel routing can target user-created agents by agentId.
  4. user agents use skills as their executable workflow layer.
  5. deterministic safety remains in daemon tools.
  6. secrets never live in the agent workspace.
  7. built-in agents cannot be silently overridden by user files.
  8. edits are picked up without requiring an app rebuild.

Non-Goals

  • Do not dynamically import arbitrary user TypeScript as daemon code in V1.
  • Do not store API tokens, bot tokens, or OAuth secrets in ~/.bot0/agents.
  • Do not require cloud sync for local agent creation.
  • Do not require all built-in agents to migrate at once.
  • Do not make agent files the source of truth for provider credentials.
  • Do not support project-local .bot0/agents discovery in V1.

Workspace Root

The agent workspace root is channel-aware:

ts
path.join(getChannelPaths().bot0Dir, "agents")

Examples:

txt
~/.bot0/agents ~/.bot0-staging/agents ~/.bot0-dev/agents

V1 supports only the global channel-aware workspace under getChannelPaths().bot0Dir. Project-local .bot0/agents can be added later for development and tests, but should require explicit opt-in because channel agents are long-running daemon behavior and project-local discovery could create surprising routing or security behavior.

BOT0_DATA_DIR continues to override the data root in development and tests.


Agent Package Layout

Each child directory is an agent package:

txt
<agent-id>/ agent.json skills/ <entry-skill>/ SKILL.md sub-skills.json benchmarks.json executables/ state/ README.md

Only agent.json is required for discovery. skills/ is required for a runnable skill-backed agent.

Recommended generated files:

txt
agent.json skills/<entrySkill>/SKILL.md skills/<entrySkill>/benchmarks.json skills/<entrySkill>/sub-skills.json skills/<subSkill>/SKILL.md skills/<subSkill>/benchmarks.json skills/<subSkill>/sub-skills.json README.md state/.gitkeep state/<stateFile>

agent_create can scaffold sub-skills and state files directly through subSkills and stateFiles. This is the preferred path for multi-step channel agents; bot0 should not inspect or copy existing packages to infer structure.


agent.json

V1 schema:

json
{ "id": "my-agent", "label": "My Agent", "description": "Handles X and Y from external channels.", "entrySkill": "my-agent", "status": "production", "visibility": "local", "allowedTools": [ "execute_skill", "channel_identity_resolve", "workspace_create" ], "channelDefaults": { "replyStyle": "plain_text", "requiresWorkspace": false } }

Required fields:

  • id: lowercase agent identifier. Same naming rules as skill names.
  • entrySkill: skill name to load for channel invocations.

Optional fields:

  • label: display name.
  • description: one-line agent description.
  • status: draft or production. Channel routing should only use production agents unless explicitly testing.
  • visibility: local for V1.
  • allowedTools: default tool allowlist for the entry skill. If omitted, the skill manifest controls tool access.
  • channelDefaults: channel response hints.

Discovery Rules

Agent discovery scans in this order:

  1. bundled daemon catalog: packages/daemon/src/agents/catalog
  2. local user workspace: ${getChannelPaths().bot0Dir}/agents

Built-ins win on ID collision. If a user package has the same ID as a built-in package, the daemon should log a collision and keep the built-in target unless an explicit migration flag is set.

Implementation shape in packages/daemon/src/agents/packages.ts:

ts
export interface AgentPackage { manifest: AgentPackageManifest; directory: string; skillsDirectory: string; source: "bundled" | "user"; } export function getUserAgentDirectory(): string { return path.join(getChannelPaths().bot0Dir, "agents"); } function discoverAgentPackagesFromDirectory( root: string, options: { source: "bundled" | "user" } ): AgentPackage[] { // read child directories, parse agent.json, validate IDs }

discoverAgentPackages() should return bundled plus user packages with collision handling.


Skill Discovery Rules

The skill system must scan every discovered agent package's skills/ directory.

Bundled agent skills should remain sourceKind: "agents" and imported/system-owned.

User agent skills under ~/.bot0/agents/*/skills should be treated as editable user skills:

ts
{ sourceKind: "bot0", sourceScope: "global", ownershipKind: "user", canEdit: true, canDelete: true, canPublish: true }

Implementation shape in packages/daemon/src/skill/discovery.ts:

ts
for (const skillsRoot of getBundledAgentSkillRoots()) { await scanOnce(skillsRoot, "agents", "global"); } for (const skillsRoot of getUserAgentSkillRoots()) { await scanOnce(skillsRoot, "bot0", "global"); }

Runtime Contract

For a channel message:

  1. channel adapter normalizes the event.
  2. route resolver resolves agentId.
  3. agent registry resolves agentId to an AgentTarget.
  4. file-backed target builds a lean package-owned system prompt from agent.json, package directories, state directory, and the entry skill SKILL.md.
  5. target invokes the normal task runner with that systemPrompt override and the package allowedTools.
  6. task runner skips the default interactive bot0 system prompt because the channel-agent prompt is already supplied.
  7. the agent uses its embedded entry skill and optional sub-skills to produce the channel reply.

The final reply must be plain text suitable for the channel adapter.

The generated agent-owned prompt includes:

txt
You are <label>, a file-backed channel agent. Agent id: <agent-id> Agent package directory: <agent-package-directory> Agent skills directory: <skills-directory> Agent state directory: <agent-package-directory>/state Entry skill: <entry-skill> Runtime contract: respond directly to the channel user, stay inside the package boundary, never store secrets, and use execute_skill for sub-workflows. <entry_skill name="<entry-skill>"> ...contents of skills/<entry-skill>/SKILL.md... </entry_skill>

The channel context still carries normalized channel metadata and route facts. It should not be used to make the agent load the entry skill; the entry skill is already embedded in the agent prompt.


Permission Contract

Agent packages do not get their own independent permission profile in V1. They inherit daemon and channel permissions, and agent.json.allowedTools can only narrow the effective tool set.

Effective tools should be computed as:

txt
daemon permission profile ∩ channel route policy ∩ agent allowedTools ∩ called skill tools_used when execute_skill is used

This prevents an editable agent package from expanding its own authority by changing local files. Per-agent permission profiles can be added later if the product needs persistent policy controls per deployed agent.

The skill tool is not implicitly added for packaged agents. The entry skill is embedded into the agent-owned prompt. Add skill to allowedTools only when the package explicitly needs that tool.


Cache And Reload

The daemon should not require restart after agent_create or file edits.

Acceptable V1 approaches:

  1. rebuild AgentTargetRegistry on each channel message with a short TTL.
  2. watch ~/.bot0/agents and clear agent/skill discovery caches.
  3. explicitly clear caches after agent_* tools mutate workspace files.

The minimum acceptable behavior is: agent_create clears discovery caches, and listAgentTargets returns the new agent immediately.


State Contract

New user-created agents should default to agent-local state:

txt
~/.bot0/agents/<agent-id>/state/

Shared runtime or system-owned state may continue to live under feature-specific daemon data directories such as:

txt
~/.bot0/data/<feature>/

Skills should access durable state through tools, not hardcoded paths. Existing runtime state can stay behind its current tool abstraction until there is a safe migration reason to move it.

Validation rejects skill files that hardcode the local agent workspace path. Skills should refer to the runtime-provided agent state directory from context.


Secrets Contract

Agent files may reference external connections by ID, but must never contain raw secrets.

Allowed:

json
{ "connectionId": "telegram_default", "provider": "telegram" }

Not allowed:

json
{ "botToken": "123:secret" }

Secrets stay in existing config, proxy, local secure storage, or provider connector stores.


Desktop Surface

The Agents panel should be the primary Desktop surface for agent packages.

It should show:

  • agent package metadata
  • channel routes
  • deployment status
  • entry skill
  • package files
  • validation errors

The Skills panel may still show agent-owned skills, but those skills should link back to their parent agent. Users should not have to understand a multi-skill agent workflow by browsing disconnected skills.


Cloud Sync And Publishing

V1 is local-only.

After the local file model is stable, cloud sync can treat an agent package as a bundle:

txt
agent.json skills/* templates/* non-secret config

State and secrets should not publish by default. A later draft/publish lifecycle can mirror the skills system, but the local runtime contract should land first.


Agent Workspace Tools

Generic tools live in packages/daemon/src/tools/agent-workspace.ts:

  • agent_list
  • agent_read
  • agent_create
  • agent_update_manifest
  • agent_validate
  • agent_deploy_channel

Channel routing helpers live in packages/daemon/src/tools/channel-workspace.ts:

  • channel_connection_list
  • channel_route_list
  • channel_route_preflight

bot0 should use the protected channel-agent-builder system skill when a user asks to create, modify, validate, or deploy a channel agent. The skill gives the daemon the expected file contract and tool sequence so these tasks do not depend on ad hoc inference.

The builder skill must not rely on bot0 source code, repo docs, or direct config-file reads. A real user daemon may not have the monorepo checked out, so channel agent creation should use only:

  • agent_* tools for agent package state
  • channel_connection_list for safe channel connection metadata
  • channel_route_list for existing route metadata
  • channel_route_preflight before deployment
  • agent_deploy_channel after preflight passes

The builder must also avoid reading existing agent package files as examples. agent_list is for metadata and id collision checks only; agent_create is the canonical source for package skeletons. For multi-step agents, pass subSkills and stateFiles into agent_create instead of copying structure from bundled or user packages.

Tool guardrails:

  • agent_list returns metadata only and does not expose package paths
  • write only under getUserAgentDirectory()
  • validate agent ID and skill names
  • reject path traversal
  • reject built-in ID collisions unless an explicit migration flag is present
  • never accept secret-looking values in agent.json
  • clear skill and agent discovery caches after writes
  • reject hardcoded local agent workspace paths in skill files during validation
  • support direct sub-skill and state-file scaffolding through agent_create
  • redact secrets from channel connection metadata
  • preflight channel routes before deployment
  • reject active route conflicts unless the caller explicitly passes disableConflictingRoutes=true

agent_deploy_channel should update channelRoutes in daemon config using existing route shape:

json
{ "id": "telegram-my-agent-dm", "name": "My Agent Telegram DM", "enabled": true, "channel": "telegram", "connectionId": "telegram_default", "agentId": "my-agent", "scope": { "type": "dm" } }

Use normalizeChannelRoutes() before saving. Because route normalization keeps only one enabled route per connection, agent_deploy_channel should reject a conflicting enabled deployment by default rather than silently saving a route that becomes disabled.


Acceptance Criteria

  • A user can create ~/.bot0/agents/example-agent/agent.json.
  • listAgentTargets returns example-agent without rebuilding the app.
  • a channel route with agentId: "example-agent" invokes that agent.
  • skill("example-agent") can load the agent's entry skill.
  • edits to the entry skill are picked up without daemon restart after cache expiry or explicit cache clear.
  • invalid manifests are ignored with a useful daemon log.
  • built-in IDs cannot be overridden accidentally.
  • agent_create can scaffold sub-skills and state files without copying existing packages.
  • agent_list does not expose package paths.
  • agent_validate rejects hardcoded local agent workspace paths.
  • channel_connection_list exposes safe metadata without secrets.
  • channel_route_preflight reports missing connections, disabled connections, broad scopes, and active route conflicts.
  • agent_deploy_channel rejects active route conflicts unless explicitly approved.

Test Plan

Add tests for:

  • user agent package discovery
  • bundled/user collision behavior
  • invalid agent.json handling
  • user agent skill root discovery
  • skill ownership classification for ~/.bot0/agents/*/skills
  • agent_create path traversal rejection
  • agent_create sub-skill and state-file scaffolding
  • metadata-only agent_list
  • hardcoded local path validation
  • agent_deploy_channel route normalization
  • agent_deploy_channel route conflict rejection
  • channel_connection_list secret redaction
  • channel_route_preflight conflict reporting
  • route to file-backed user agent
  • route to missing agent returns a clear error
  • route sees newly created agent after cache clear

Implemented Code Surface

The runtime and tool surface spans:

txt
packages/daemon/src/agents/packages.ts - user agent root - bundled/user package scanning - bundled/user skill roots packages/daemon/src/agents/package-target.ts - package-owned channel-agent system prompt - package, skills, and state directory runtime facts packages/daemon/src/agents/index.ts - exports file-backed package helpers packages/daemon/src/skill/discovery.ts - user agent skill roots - user agent skills classified as editable user skills - protected channel-agent-builder system skill packages/daemon/src/ipc/server.ts - allowedTools narrowing for channel agents packages/daemon/src/tools/agent-workspace.ts - agent_list, agent_read, agent_create, agent_update_manifest - agent_validate, agent_deploy_channel packages/daemon/src/tools/channel-workspace.ts - channel_connection_list - channel_route_list - channel_route_preflight packages/daemon/src/tools/channel-routing.ts - safe connection/route summaries - preflight conflict detection packages/daemon/src/index.ts - register agent workspace tools - register channel workspace tools - install protected built-in system skills packages/daemon/src/skill/system-skills.ts - channel-agent-builder skill content and installer

Product-specific channel agents should live as user-created agent packages, not as baked-in daemon source.


Resolved Design Decisions

  1. User agent packages live in ~/.bot0/agents for V1. Project-local .bot0/agents is deferred behind future explicit dev/test opt-in.
  2. Agents inherit daemon and channel permissions. agent.json.allowedTools and skill tools_used can narrow tool access, not expand it.
  3. New user agents default to ~/.bot0/agents/<agent-id>/state, with tools abstracting state paths from skills.
  4. Desktop should make the Agents panel the primary surface. Agent-owned skills can appear in Skills, but should link back to the parent agent.
  5. Cloud sync and draft/publish lifecycle are deferred until the local file runtime is stable. Future publishing should bundle non-secret package files only.
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