Experimental Research fork — APIs and behaviour may change without notice. Read disclaimer →

Module Map

picobot/
  cli/commands.py                 — typer app: init, wipe, clean, agent, status, cron, daemon, kb
  agent/
    loop.py                       — ReAct loop with budget management, error recovery, retry
    context.py                    — system prompt assembly (identity + workspace tree + builtin tools + KB + skills + TODOs)
    events.py                     — streaming event types (TextDelta, ReasoningDelta, ToolCall*)
    knowledge.py                  — progressive KB (topics, YAML frontmatter, search, git sync)
    skills.py                     — skill loading from workspace (seeded from builtins on init)
    subagent.py                   — background sub-agent execution with depth tracking
    registry.py                   — persistent sub-agent registry (spawn/complete/fail/deliver)
    budget.py                     — token budget estimation, history compression, result truncation
    trace.py                      — structured tracing (per-turn timing, tokens, cost)
    tools/
      base.py                     — abstract Tool base class with JSON schema validation
      registry.py                 — dynamic tool registration and dispatch
      pathguard.py                — centralized path access control (allow/block lists)
      filesystem.py               — read/write/edit/listdir
      shell.py                    — exec with deny patterns and path guard
      web.py                      — web search (Brave) + readability fetch
      firecrawl.py                — Firecrawl scrape + search (optional)
      browser.py                  — Playwright browser automation (9 actions)
      knowledge.py                — kb tool (search, read, store, topics, sync)
      cron.py                     — schedule/list/remove cron jobs
      spawn.py                    — spawn subagent tool (depth-aware)
      subagent_status.py          — check/list sub-agent status
  channels/
    json_channel.py               — NDJSON stdin/stdout protocol for programmatic access
  daemon/
    service.py                    — OS service file generation (launchd plist, systemd unit)
    logging.py                    — rotating file handler for daemon logs
  dashboard/
    app.py                        — Textual TUI dashboard (6 live-updating panels)
    data.py                       — read-only data readers for dashboard panels
    widgets.py                    — Textual widget components
  web/
    app.py                        — FastAPI + HTMX web dashboard with SSE
    templates/                    — Jinja2 templates (index + 6 partials)
    static/                       — CSS styles
  cron/
    types.py                      — CronSchedule, CronJob, CronStore dataclasses
    service.py                    — scheduling engine (at/every/cron, persistence, async timer)
  providers/
    base.py                       — LLMProvider ABC with token/cost tracking
    litellm_provider.py           — LiteLLM wrapper (any OpenAI-compatible or Anthropic model)
  session/manager.py              — JSONL session persistence with history trimming
  config/schema.py                — Pydantic config models
  workspace/tracker.py            — tool call JSONL logger + workspace git wrapper
  skills/                         — 14 builtin skill templates

Agent Loop

The core is process_direct() in loop.py — a ReAct (Reason + Act) loop:

  1. Build system prompt (identity → workspace tree → env → AGENTS.md → builtin tools → KB → tool groups → TODO → skills)
  2. Send messages to LLM via LiteLLM provider
  3. If LLM returns tool calls, execute them and feed results back
  4. If LLM returns text, that's the response
  5. Repeat until done or budget exhausted

Budget management compresses history when token estimate exceeds 80% of budget. Error recovery retries transient LLM errors (rate limits, timeouts) with backoff. Stuck detection catches repeated identical tool calls.

Sub-agents

The agent can spawn background sub-agents via the spawn tool. Each sub-agent gets: - Lean core tool set (read/write/edit/list/exec) — extras opt-in via tools param - Efficiency-focused system prompt (combine shell ops, minimize LLM round-trips) - Depth tracking (configurable max, default 2) to prevent infinite recursion - TODO.md anchoring so spawned tasks survive context truncation

Supervision model: - Result delivery: In-loop message queue (drained each ReAct iteration) + cross-session registry - Stop hook: Prevents premature loop exit when sub-agents are running - Waiting: wait_for_subagents blocks via asyncio.wait at zero token cost - Monitoring: check_subagents shows live state (iteration, tool calls, files written) - Intervention: check_subagents(action="cancel") kills stuck sub-agents - Timers: expected_duration triggers overdue nudge, timeout triggers hard cancel - Token tracking: Sub-agent tokens accumulated and included in AgentResult.total_tokens

Knowledge Base

Structured topic tree (self/, user/, projects/, domains/, journal/) with: - YAML frontmatter (title, topic, tags, created, updated) - Two-phase search: frontmatter tags/title first, full-text fallback - Duplicate detection (title >60% overlap or content >70% overlap) - Git sync with sharing.yaml for topic-level privacy control - LWW conflict resolution on updated timestamp - Obsidian-compatible (point Obsidian at ~/.picobot/workspace/knowledge/)

Tools

Tool Description
read_file Read file contents (PathGuard enforced)
write_file Write content to file
edit_file Replace exact text in file
list_dir List directory contents
exec Shell command with deny patterns and timeout
web_search Brave Search API
web_fetch URL fetch with Readability extraction
firecrawl_scrape Firecrawl URL scrape (optional)
firecrawl_search Firecrawl web search (optional)
browser Playwright: navigate, click, type, screenshot, get_text, evaluate, select, wait_for, get_elements
kb Knowledge base: search, read, store, topics, sync
cron Schedule: add, list, remove
spawn Launch background sub-agent (depth-aware)
check_subagents List/check sub-agent status

Workspace-Relative Paths

Filesystem tools resolve relative paths against the workspace directory instead of os.getcwd(). This means the agent can use data/config.json instead of the full absolute path, saving tokens and reducing errors when the workspace is a long temp directory (e.g. during eval runs).

The resolution logic lives in _resolve_path() in filesystem.py: - Relative path + workspace set → resolve against workspace - Absolute path (/...) → use as-is - Tilde path (~/...) → expand then resolve

The workspace is passed to all four filesystem tool constructors in both loop.py (_register_default_tools) and subagent.py (_run_subagent). The system prompt (context.py) and sub-agent preambles tell the LLM to prefer relative paths.

The shell tool's _guard_command() also resolves relative path tokens in commands against the working directory and checks them via PathGuard, preventing path traversal escapes like ../../etc/passwd in workspace-restricted mode.

Dashboards

TUI (picobot dashboard): Textual app with 6 panels — status header, sub-agent table, cron jobs, activity log, KB tree, TODO display. Polls data files every 2-10s.

Web (picobot serve): FastAPI + HTMX with the same 6 panels as HTML partials. JSON API at /api/*. SSE at /events for real-time updates.

Both dashboards are read-only — they read the same data files the agent writes.

Daemon

picobot daemon run runs cron + heartbeat in foreground. picobot daemon install generates an OS service file: - macOS: launchd plist at ~/Library/LaunchAgents/com.picobot.agent.plist - Linux: systemd unit at ~/.config/systemd/user/picobot.service

PID file locking via fcntl.flock ensures single instance. Signal handling for graceful shutdown.

JSON Channel

picobot agent --json activates NDJSON protocol on stdin/stdout for programmatic integration. Message types: message, ping, shutdown (input); ready, response, progress, error, pong (output). Progress events stream tool calls and text deltas in real time.