PicoBot/AGENTS.md

9.4 KiB

PicoBot

This file is the operational contract for coding agents working in this repository. Read docs/ARCHITECTURE.md before making architectural or lifecycle changes. Code and tests are the final source of truth; update the document when an architectural invariant changes.

Build & Run

  • cargo build — build the binary
  • cargo run -- gateway — start gateway server (binds 127.0.0.1:19876 by default)
  • cargo run -- chat — connect to gateway as CLI client (default ws://127.0.0.1:19876/ws)
  • WebUI — start Gateway, then open http://127.0.0.1:19876/; no separate frontend build is required
  • cd webui && npm ci && npm run check && npm run build — validate the Svelte WebUI independently (Node.js 20+); its local dist/ is ignored
  • cargo build automatically runs an incremental WebUI production build into Cargo OUT_DIR; it runs npm ci only when package-lock.json is not represented by the installed dependency stamp
  • picobot service install|start|stop|status|restart|uninstall — manage the Linux systemd user service (picobot.service)

Config

  • Config load order: ~/.picobot/config.json then fallback to ./config.json (Config::load_default in src/config/mod.rs)
  • .env (cwd) is loaded with a custom parser, not via dotenv crate; env var placeholders <VAR_NAME> in config JSON are substituted
  • Config example: resources/templates/config.example.json (released to ~/.picobot/ on first run)
  • CLI TUI identity is stored in ~/.picobot/tui_client_id; it is a non-secret stable chat scope used to restore dialogs across reconnects

Tests

  • cargo test --lib — run unit tests (runs all #[test] in src/)
  • cargo clippy --all-targets --all-features -- -D warnings — required for Rust changes
  • cargo test --test test_scheduler and cargo test --test test_request_format — offline integration/protocol tests
  • cargo test --test test_integration -- --ignored and cargo test --test test_tool_calling -- --ignored — model API integration tests
  • API-backed tests require tests/test.env with real API keys; copy from tests/test.env.example and fill in keys
  • API-backed tests are #[ignore] by default; use -- --ignored to run them

Reference

  • reference/ — third-party reference implementations; not part of this project; do not modify

Architecture

Modes

  • Gateway mode (cargo run -- gateway): HTTP/WebSocket server; owns GatewayState which holds all services
  • Client mode (cargo run -- chat): TUI chat client; connects to gateway via WebSocket, purely for user interaction

Core Data Flow

Channel → MessageBus.inbound → Gateway processor → SessionManager → per-session worker → AgentLoop
   ↑                                                                           │
   └── Channel ← OutboundDispatcher ← per-conversation lane ← MessageBus.outbound

WebSocket/Channel → MessageBus.control → Gateway processor → SessionManager (dialog operations)
Scheduler → SessionManager scheduled execution → AgentLoop → Scheduler delivery policy → SessionManager/MessageBus

Modules

Module Responsibility Key Types
gateway Server lifecycle, HTTP/WS/WebUI endpoints, owns GatewayState GatewayState, run()
client TUI rendering, WebSocket client for CLI chat App, run()
channels External integrations (Feishu, CLI chat) ChannelManager, Channel trait
bus Bounded async queues and ordered outbound delivery lanes MessageBus, OutboundDispatcher, InboundMessage, OutboundMessage, ControlMessage
session Conversation lifecycle, dialog operations, per-session serialization, persistence coordination SessionManager, Session
agent LLM call loop, tool execution, context compression AgentLoop
providers LLM API clients (OpenAI-compatible, Anthropic) LLMProvider trait, factory create_provider()
tools Agent tools (bash, file ops, http, web, get_skill) ToolRegistry, Tool trait
skills Skills loading, management, and prompt building SkillsLoader, Skill
storage SQLite persistence for sessions and messages Storage, SessionMeta, MessageMeta
scheduler Cron-based job scheduling, next-run computation Scheduler, Schedule, next_run_for_schedule()
observability Observer pattern for agent/tool telemetry events Observer trait, ObserverEvent, MultiObserver
protocol WebSocket protocol message types WsInbound, WsOutbound, SessionSummary, HistoryMessage
config Config loading, env substitution, path resolution Config, LLMProviderConfig
logging Tracing initialization with file rotation init_logging(), init_logging_console_only()
task_supervisor Owns, cancels, and boundedly joins gateway background tasks TaskSupervisor

Functional Boundaries

  • Channels only send/receive messages via MessageBus; they know nothing about sessions or LLM
  • MessageBus owns bounded inbound/outbound/control queues; outbound routing and retries belong to OutboundDispatcher, not the queue
  • SessionManager owns session state, dialog operations, context construction, per-session work queues, and persistence coordination
  • Scheduler supports legacy direct-delivery jobs and managed task/monitor jobs; managed agents cannot call send_message, and on_alert suppresses only healthy informational results
  • AgentLoop is stateless across turns; it receives prepared history, calls LLM providers, executes tools, and returns one result
  • WebUI management APIs only expose allowlisted config/profile/log/storage operations; keep response limits, secret redaction, atomic config writes, and profile path allowlists intact
  • WebUI slash completion must consume the existing get_slash_commands WebSocket response; do not duplicate the backend command list in frontend source
  • Providers are pure HTTP clients; no bus/session/channel awareness
  • Tools are executed by AgentLoop; they receive raw arguments and return string results

Concurrency and Lifecycle Invariants

  • Messages in one session are processed serially through a bounded queue; different sessions may run concurrently
  • Outbound messages are ordered per (channel, chat_id); a slow destination must not block unrelated destinations
  • Never hold a Session mutex across model, network, or database I/O unless a documented invariant requires it
  • Slow work derived from session state must validate worker_generation/state_version before committing results
  • Related durable mutations use Storage transaction APIs; persistence failure must not leave silent memory/database divergence
  • Long-lived gateway tasks must be owned by TaskSupervisor; connection-local tasks must be explicitly joined or aborted by their owner
  • Connection, retry sleep, queue wait, and shutdown join paths must observe cancellation and have hard time bounds
  • Retry only errors explicitly classified as transient; permanent failures must surface immediately
  • Never log secrets, authorization headers, or full connection URLs containing temporary credentials
  • The workspace cwd is not a filesystem sandbox: default file tools accept absolute paths and Bash inherits process permissions; add explicit canonical-path/process isolation when a hard boundary is required

Key Constraints

  • Gateway changes working directory to workspace in GatewayState::new (src/gateway/mod.rs)
  • Session/message persistence uses SQLite via sqlx; DB stored in workspace as picobot.db by default
  • ChannelManager owns the MessageBus and all channel instances
  • OutboundDispatcher routes outbound messages to the correct channel via ChannelManager
  • Config .env loading uses unsafe { env::set_var(...) } — don't refactor to safer patterns without understanding side effects

Change Workflow

  1. Inspect the relevant implementation, tests, and docs/ARCHITECTURE.md; do not rely on names or old reports alone.
  2. Search reference/ only for comparison. Never edit it or copy behavior without checking PicoBot's boundaries.
  3. Preserve unrelated user changes in a dirty worktree. Use rg for search and apply_patch for edits.
  4. Add regression tests for bugs, especially cancellation, timeout, queue saturation, stale state, persistence failure, and retry classification.
  5. For WebUI changes run npm run check and npm run build in webui/, then run cargo build to verify the OUT_DIR embedding path. Do not commit generated dist/; verify there is no external runtime dependency and keep browser chat on the existing /ws protocol.
  6. For Rust changes run targeted tests, cargo test --lib, Clippy with warnings denied, and cargo build. Integration tests require real credentials.
  7. For documentation-only changes verify links, commands, paths, and git diff --check.
  8. Update README, this file, and the architecture document together when public behavior or an architectural invariant changes.

Documentation Roles

  • README.md — user-facing overview, setup, capabilities, and navigation
  • docs/ARCHITECTURE.md — maintainer-facing runtime design, invariants, lifecycle, and extension guidance
  • AGENTS.md — concise operational rules for repository agents
  • resources/skills/about-picobot/references/ — runtime knowledge shipped to PicoBot; update it only when the assistant's built-in product knowledge must change