From d947d52bb7647168684a6d3d24370ee223cc14d1 Mon Sep 17 00:00:00 2001 From: CraftBot Date: Sat, 8 Aug 2026 10:16:46 +0900 Subject: [PATCH 1/2] update mkdocs, including living UI page --- mkdocs/.gitignore | 2 + mkdocs/docs/assets/android-chrome-192x192.png | Bin 0 -> 4361 bytes .../craftbot_logo_text_no_border_dark.png | Bin 0 -> 14917 bytes .../craftbot_logo_text_no_border_light.png | Bin 0 -> 15557 bytes mkdocs/docs/assets/favicon.ico | Bin 15406 -> 15406 bytes mkdocs/docs/connections.md | 230 ---------- mkdocs/docs/core/commands/builtin.md | 187 ++++++++ mkdocs/docs/core/commands/cli-anything.md | 83 ++++ mkdocs/docs/core/commands/index.md | 62 +++ .../core/concepts/actions-and-action-sets.md | 103 +++++ mkdocs/docs/core/concepts/agent-bundles.md | 95 ++++ .../docs/core/concepts/agent-file-system.md | 83 ++++ mkdocs/docs/core/concepts/agent-loop.md | 98 ++++ mkdocs/docs/core/concepts/context-engine.md | 96 ++++ mkdocs/docs/core/concepts/event-stream.md | 106 +++++ mkdocs/docs/core/concepts/logs.md | 100 ++++ mkdocs/docs/core/concepts/memory.md | 92 ++++ mkdocs/docs/core/concepts/prompts.md | 73 +++ mkdocs/docs/core/concepts/scheduling.md | 108 +++++ mkdocs/docs/core/concepts/skills.md | 97 ++++ mkdocs/docs/core/concepts/sub-agents.md | 66 +++ mkdocs/docs/core/concepts/task-sessions.md | 112 +++++ mkdocs/docs/core/concepts/triggers.md | 123 +++++ .../core/configuration/agent-config-yaml.md | 98 ++++ mkdocs/docs/core/configuration/config-json.md | 189 ++++++++ mkdocs/docs/core/configuration/index.md | 47 ++ mkdocs/docs/core/index.md | 123 +++++ mkdocs/docs/core/interfaces/browser.md | 106 +++++ mkdocs/docs/core/interfaces/cli.md | 91 ++++ mkdocs/docs/core/interfaces/index.md | 66 +++ mkdocs/docs/core/interfaces/ui-layer.md | 74 +++ mkdocs/docs/core/modes/complex-task.md | 138 ++++++ mkdocs/docs/core/modes/index.md | 65 +++ mkdocs/docs/core/modes/proactive.md | 133 ++++++ mkdocs/docs/core/modes/simple-task.md | 90 ++++ mkdocs/docs/core/modes/special-workflows.md | 63 +++ mkdocs/docs/core/providers/index.md | 65 +++ mkdocs/docs/core/providers/llm.md | 106 +++++ .../docs/core/providers/subscription-auth.md | 71 +++ mkdocs/docs/core/providers/vlm-and-media.md | 82 ++++ mkdocs/docs/develop/architecture.md | 177 +++++++ mkdocs/docs/develop/contributing.md | 103 +++++ mkdocs/docs/develop/custom-action.md | 225 +++++++++ mkdocs/docs/develop/custom-agent.md | 169 +++++++ mkdocs/docs/develop/custom-integration.md | 281 ++++++++++++ mkdocs/docs/develop/index.md | 85 ++++ mkdocs/docs/develop/skills/craftbot-skill.md | 204 +++++++++ mkdocs/docs/develop/skills/external-skill.md | 69 +++ mkdocs/docs/develop/skills/index.md | 37 ++ mkdocs/docs/getting-started.md | 124 ----- mkdocs/docs/guides/daily-briefing.md | 142 ++++++ mkdocs/docs/guides/first-skill.md | 192 ++++++++ mkdocs/docs/guides/github-pr-review.md | 144 ++++++ mkdocs/docs/guides/index.md | 45 ++ mkdocs/docs/guides/mcp-server.md | 147 ++++++ mkdocs/docs/guides/telegram-assistant.md | 123 +++++ mkdocs/docs/index.md | 133 ++++-- mkdocs/docs/integrations/credentials.md | 80 ++++ mkdocs/docs/integrations/discord.md | 226 +++++++++ mkdocs/docs/integrations/github.md | 265 +++++++++++ mkdocs/docs/integrations/gmail.md | 131 ++++++ mkdocs/docs/integrations/google-calendar.md | 130 ++++++ mkdocs/docs/integrations/google-docs.md | 130 ++++++ mkdocs/docs/integrations/google-drive.md | 142 ++++++ mkdocs/docs/integrations/google-youtube.md | 96 ++++ mkdocs/docs/integrations/hubspot.md | 283 ++++++++++++ mkdocs/docs/integrations/index.md | 66 +++ mkdocs/docs/integrations/jira.md | 191 ++++++++ mkdocs/docs/integrations/lark.md | 351 ++++++++++++++ mkdocs/docs/integrations/line.md | 196 ++++++++ mkdocs/docs/integrations/linkedin.md | 155 +++++++ mkdocs/docs/integrations/mcp.md | 105 +++++ mkdocs/docs/integrations/notion.md | 149 ++++++ mkdocs/docs/integrations/outlook.md | 137 ++++++ mkdocs/docs/integrations/slack.md | 212 +++++++++ mkdocs/docs/integrations/stripe.md | 274 +++++++++++ mkdocs/docs/integrations/telegram-bot.md | 197 ++++++++ mkdocs/docs/integrations/telegram-user.md | 115 +++++ mkdocs/docs/integrations/twitter.md | 197 ++++++++ mkdocs/docs/integrations/whatsapp-business.md | 98 ++++ mkdocs/docs/integrations/whatsapp-web.md | 157 +++++++ mkdocs/docs/living-ui/a2app-protocol.md | 294 ++++++++++++ mkdocs/docs/living-ui/framework.md | 141 ++++++ mkdocs/docs/living-ui/index.md | 86 ++++ mkdocs/docs/living-ui/managing.md | 98 ++++ mkdocs/docs/reference/agent-md-files.md | 239 ++++++++++ mkdocs/docs/reference/env-vars.md | 164 +++++++ mkdocs/docs/reference/events.md | 89 ++++ mkdocs/docs/reference/faq.md | 87 ++++ mkdocs/docs/reference/index.md | 34 ++ .../reference/troubleshooting/connections.md | 62 +++ .../docs/reference/troubleshooting/index.md | 51 +++ .../reference/troubleshooting/providers.md | 81 ++++ .../docs/reference/troubleshooting/runtime.md | 94 ++++ mkdocs/docs/start/first-task.md | 118 +++++ mkdocs/docs/start/index.md | 68 +++ mkdocs/docs/start/install.md | 174 +++++++ mkdocs/docs/start/learning-path.md | 78 ++++ mkdocs/docs/start/onboarding.md | 103 +++++ mkdocs/docs/start/quickstart.md | 147 ++++++ mkdocs/docs/start/service-mode.md | 128 ++++++ mkdocs/docs/stylesheets/extra.css | 432 +++++++++++++++--- mkdocs/mkdocs.yml | 191 +++++++- mkdocs/overrides/partials/logo.html | 15 + mkdocs/overrides/partials/social.html | 33 ++ mkdocs/requirements.txt | 5 + mkdocs/scripts/gen_default_actions.py | 269 +++++++++++ 107 files changed, 12808 insertions(+), 479 deletions(-) create mode 100644 mkdocs/.gitignore create mode 100644 mkdocs/docs/assets/android-chrome-192x192.png create mode 100644 mkdocs/docs/assets/craftbot_logo_text_no_border_dark.png create mode 100644 mkdocs/docs/assets/craftbot_logo_text_no_border_light.png delete mode 100644 mkdocs/docs/connections.md create mode 100644 mkdocs/docs/core/commands/builtin.md create mode 100644 mkdocs/docs/core/commands/cli-anything.md create mode 100644 mkdocs/docs/core/commands/index.md create mode 100644 mkdocs/docs/core/concepts/actions-and-action-sets.md create mode 100644 mkdocs/docs/core/concepts/agent-bundles.md create mode 100644 mkdocs/docs/core/concepts/agent-file-system.md create mode 100644 mkdocs/docs/core/concepts/agent-loop.md create mode 100644 mkdocs/docs/core/concepts/context-engine.md create mode 100644 mkdocs/docs/core/concepts/event-stream.md create mode 100644 mkdocs/docs/core/concepts/logs.md create mode 100644 mkdocs/docs/core/concepts/memory.md create mode 100644 mkdocs/docs/core/concepts/prompts.md create mode 100644 mkdocs/docs/core/concepts/scheduling.md create mode 100644 mkdocs/docs/core/concepts/skills.md create mode 100644 mkdocs/docs/core/concepts/sub-agents.md create mode 100644 mkdocs/docs/core/concepts/task-sessions.md create mode 100644 mkdocs/docs/core/concepts/triggers.md create mode 100644 mkdocs/docs/core/configuration/agent-config-yaml.md create mode 100644 mkdocs/docs/core/configuration/config-json.md create mode 100644 mkdocs/docs/core/configuration/index.md create mode 100644 mkdocs/docs/core/index.md create mode 100644 mkdocs/docs/core/interfaces/browser.md create mode 100644 mkdocs/docs/core/interfaces/cli.md create mode 100644 mkdocs/docs/core/interfaces/index.md create mode 100644 mkdocs/docs/core/interfaces/ui-layer.md create mode 100644 mkdocs/docs/core/modes/complex-task.md create mode 100644 mkdocs/docs/core/modes/index.md create mode 100644 mkdocs/docs/core/modes/proactive.md create mode 100644 mkdocs/docs/core/modes/simple-task.md create mode 100644 mkdocs/docs/core/modes/special-workflows.md create mode 100644 mkdocs/docs/core/providers/index.md create mode 100644 mkdocs/docs/core/providers/llm.md create mode 100644 mkdocs/docs/core/providers/subscription-auth.md create mode 100644 mkdocs/docs/core/providers/vlm-and-media.md create mode 100644 mkdocs/docs/develop/architecture.md create mode 100644 mkdocs/docs/develop/contributing.md create mode 100644 mkdocs/docs/develop/custom-action.md create mode 100644 mkdocs/docs/develop/custom-agent.md create mode 100644 mkdocs/docs/develop/custom-integration.md create mode 100644 mkdocs/docs/develop/index.md create mode 100644 mkdocs/docs/develop/skills/craftbot-skill.md create mode 100644 mkdocs/docs/develop/skills/external-skill.md create mode 100644 mkdocs/docs/develop/skills/index.md delete mode 100644 mkdocs/docs/getting-started.md create mode 100644 mkdocs/docs/guides/daily-briefing.md create mode 100644 mkdocs/docs/guides/first-skill.md create mode 100644 mkdocs/docs/guides/github-pr-review.md create mode 100644 mkdocs/docs/guides/index.md create mode 100644 mkdocs/docs/guides/mcp-server.md create mode 100644 mkdocs/docs/guides/telegram-assistant.md create mode 100644 mkdocs/docs/integrations/credentials.md create mode 100644 mkdocs/docs/integrations/discord.md create mode 100644 mkdocs/docs/integrations/github.md create mode 100644 mkdocs/docs/integrations/gmail.md create mode 100644 mkdocs/docs/integrations/google-calendar.md create mode 100644 mkdocs/docs/integrations/google-docs.md create mode 100644 mkdocs/docs/integrations/google-drive.md create mode 100644 mkdocs/docs/integrations/google-youtube.md create mode 100644 mkdocs/docs/integrations/hubspot.md create mode 100644 mkdocs/docs/integrations/index.md create mode 100644 mkdocs/docs/integrations/jira.md create mode 100644 mkdocs/docs/integrations/lark.md create mode 100644 mkdocs/docs/integrations/line.md create mode 100644 mkdocs/docs/integrations/linkedin.md create mode 100644 mkdocs/docs/integrations/mcp.md create mode 100644 mkdocs/docs/integrations/notion.md create mode 100644 mkdocs/docs/integrations/outlook.md create mode 100644 mkdocs/docs/integrations/slack.md create mode 100644 mkdocs/docs/integrations/stripe.md create mode 100644 mkdocs/docs/integrations/telegram-bot.md create mode 100644 mkdocs/docs/integrations/telegram-user.md create mode 100644 mkdocs/docs/integrations/twitter.md create mode 100644 mkdocs/docs/integrations/whatsapp-business.md create mode 100644 mkdocs/docs/integrations/whatsapp-web.md create mode 100644 mkdocs/docs/living-ui/a2app-protocol.md create mode 100644 mkdocs/docs/living-ui/framework.md create mode 100644 mkdocs/docs/living-ui/index.md create mode 100644 mkdocs/docs/living-ui/managing.md create mode 100644 mkdocs/docs/reference/agent-md-files.md create mode 100644 mkdocs/docs/reference/env-vars.md create mode 100644 mkdocs/docs/reference/events.md create mode 100644 mkdocs/docs/reference/faq.md create mode 100644 mkdocs/docs/reference/index.md create mode 100644 mkdocs/docs/reference/troubleshooting/connections.md create mode 100644 mkdocs/docs/reference/troubleshooting/index.md create mode 100644 mkdocs/docs/reference/troubleshooting/providers.md create mode 100644 mkdocs/docs/reference/troubleshooting/runtime.md create mode 100644 mkdocs/docs/start/first-task.md create mode 100644 mkdocs/docs/start/index.md create mode 100644 mkdocs/docs/start/install.md create mode 100644 mkdocs/docs/start/learning-path.md create mode 100644 mkdocs/docs/start/onboarding.md create mode 100644 mkdocs/docs/start/quickstart.md create mode 100644 mkdocs/docs/start/service-mode.md create mode 100644 mkdocs/overrides/partials/logo.html create mode 100644 mkdocs/overrides/partials/social.html create mode 100644 mkdocs/requirements.txt create mode 100644 mkdocs/scripts/gen_default_actions.py diff --git a/mkdocs/.gitignore b/mkdocs/.gitignore new file mode 100644 index 00000000..b44ca6cb --- /dev/null +++ b/mkdocs/.gitignore @@ -0,0 +1,2 @@ +site/ +.cache/ diff --git a/mkdocs/docs/assets/android-chrome-192x192.png b/mkdocs/docs/assets/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..115f4c5d856662a268a4c5d197f07c2507d12e5a GIT binary patch literal 4361 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 literal 0 HcmV?d00001 diff --git a/mkdocs/docs/assets/craftbot_logo_text_no_border_dark.png b/mkdocs/docs/assets/craftbot_logo_text_no_border_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..d5943133e88b429db749fcfefadb479680d972a7 GIT binary patch literal 14917 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 Dd#fOY literal 0 HcmV?d00001 diff --git a/mkdocs/docs/assets/craftbot_logo_text_no_border_light.png b/mkdocs/docs/assets/craftbot_logo_text_no_border_light.png new file mode 100644 index 0000000000000000000000000000000000000000..4874b22e365417a069dccebc12487d88ad91d555 GIT binary patch literal 15557 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 literal 15406 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 [name]` | Register a guild after adding the bot | -| `/discord login ` | Connect your own Discord bot | -| `/discord login-user ` | Connect a Discord user account | -| `/discord status` | Show all Discord connections | -| `/discord logout [id]` | Remove a connection | - -### Prerequisites - -- **Invite:** Requires `DISCORD_SHARED_BOT_ID` env var (set by CraftOS admin) -- **Login:** Create a bot at [discord.com/developers](https://discord.com/developers/applications), copy the bot token - ---- - -## Slack - -Send messages, list channels/users, search messages, read history, and upload files. - -**Available actions:** `send_slack_message`, `list_slack_channels`, `get_slack_channel_history`, `list_slack_users`, `search_slack_messages`, `upload_slack_file` - -### Connect - -| Command | Description | -|---------|-------------| -| `/slack invite` | Install the CraftOS Slack app to your workspace (OAuth flow) | -| `/slack login [workspace_name]` | Connect your own Slack bot token | -| `/slack status` | Show connected workspaces | -| `/slack logout [workspace_id]` | Remove a workspace connection | - -### Prerequisites - -- **Invite:** Requires `SLACK_SHARED_CLIENT_ID` and `SLACK_SHARED_CLIENT_SECRET` env vars -- **Login:** Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps), install to workspace, copy the Bot User OAuth Token (`xoxb-...`) - ---- - -## Telegram - -Send messages/photos, get updates, look up chats, and search contacts. - -**Bot actions:** `send_telegram_bot_message`, `send_telegram_photo`, `get_telegram_updates`, `get_telegram_chat`, `search_telegram_contact` - -**User account actions:** `send_telegram_user_message`, `send_telegram_user_file`, `get_telegram_chats`, `read_telegram_messages`, `search_telegram_user_contacts` - -### Connect - -| Command | Description | -|---------|-------------| -| `/telegram invite` | Connect the CraftOS Telegram bot (opens t.me link) | -| `/telegram login ` | Connect your own bot from @BotFather | -| `/telegram login-user ` | Step 1: Send verification code to your phone | -| `/telegram login-user [2fa_password]` | Step 2: Complete user account login | -| `/telegram status` | Show all Telegram connections | -| `/telegram logout [id]` | Remove a connection | - -### Prerequisites - -- **Invite:** Requires `TELEGRAM_SHARED_BOT_TOKEN` and `TELEGRAM_SHARED_BOT_USERNAME` env vars -- **Login:** Message [@BotFather](https://t.me/BotFather) on Telegram to create a bot and get the token -- **Login-user:** Requires `TELEGRAM_API_ID` and `TELEGRAM_API_HASH` env vars. Get them from [my.telegram.org](https://my.telegram.org) → API development tools. Also requires Telethon (`pip install telethon`). - ---- - -## Notion - -Search pages/databases, create and update pages, query databases. - -**Available actions:** `search_notion`, `get_notion_page`, `create_notion_page`, `query_notion_database`, `update_notion_page` - -### Connect - -| Command | Description | -|---------|-------------| -| `/notion invite` | Authorize the CraftOS Notion integration (OAuth flow) | -| `/notion login ` | Connect your own Notion integration | -| `/notion status` | Show connected workspaces | -| `/notion logout [workspace_id]` | Remove a workspace connection | - -### Prerequisites - -- **Invite:** Requires `NOTION_SHARED_CLIENT_ID` and `NOTION_SHARED_CLIENT_SECRET` env vars -- **Login:** Create an integration at [notion.so/my-integrations](https://www.notion.so/my-integrations), copy the Internal Integration Secret - ---- - -## Google Workspace - -Send/read emails (Gmail), create calendar events with Google Meet, manage Google Drive files. - -**Available actions:** `send_gmail`, `list_gmail`, `get_gmail`, `read_top_emails`, `create_google_meet`, `check_calendar_availability`, `list_drive_files`, `create_drive_folder`, `move_drive_file` - -### Connect - -| Command | Description | -|---------|-------------| -| `/google login` | Authenticate via Google OAuth (opens browser) | -| `/google status` | Show connected Google accounts | -| `/google logout [email]` | Remove a Google account | - -### Prerequisites - -Requires `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` env vars. Create OAuth credentials in [Google Cloud Console](https://console.cloud.google.com/apis/credentials). - -Scopes requested: Gmail, Calendar, Drive, Contacts, UserInfo. - ---- - -## LinkedIn - -View profile, create posts, search jobs, get connections, send messages. - -**Available actions:** `get_linkedin_profile`, `create_linkedin_post`, `search_linkedin_jobs`, `get_linkedin_connections`, `send_linkedin_message` - -### Connect - -| Command | Description | -|---------|-------------| -| `/linkedin login` | Authenticate via LinkedIn OAuth (opens browser) | -| `/linkedin status` | Show connected accounts | -| `/linkedin logout [linkedin_id]` | Remove an account | - -### Prerequisites - -Requires `LINKEDIN_CLIENT_ID` and `LINKEDIN_CLIENT_SECRET` env vars. Create an app at [linkedin.com/developers](https://www.linkedin.com/developers/). - ---- - -## Zoom - -Create, list, get details, and delete Zoom meetings. - -**Available actions:** `create_zoom_meeting`, `list_zoom_meetings`, `get_zoom_meeting`, `delete_zoom_meeting` - -### Connect - -| Command | Description | -|---------|-------------| -| `/zoom login` | Authenticate via Zoom OAuth (opens browser) | -| `/zoom status` | Show connected Zoom accounts | -| `/zoom logout [zoom_user_id]` | Remove an account | - -### Prerequisites - -Requires `ZOOM_CLIENT_ID` and `ZOOM_CLIENT_SECRET` env vars. Create an OAuth app at [marketplace.zoom.us](https://marketplace.zoom.us/). - ---- - -## WhatsApp - -Send text and media messages via WhatsApp Web. - -**Available actions:** `send_whatsapp_web_text_message`, `send_whatsapp_web_media_message`, `get_whatsapp_chat_history`, `get_whatsapp_unread_chats`, `search_whatsapp_contact` - -### Connect - -| Command | Description | -|---------|-------------| -| `/whatsapp login [phone_number]` | Connect via WhatsApp Web (scan QR code) | -| `/whatsapp status` | Show all WhatsApp connections | -| `/whatsapp logout [id]` | Remove a connection | - -### Prerequisites - -Requires Playwright (`pip install playwright && playwright install chromium`). A QR code will open in your browser — scan it with your phone's WhatsApp camera to connect. - ---- - -## Recall.ai - -Create meeting bots that join calls to record and transcribe (Zoom, Google Meet, Teams). - -**Available actions:** `create_recall_bot`, `get_recall_bot`, `get_recall_transcript`, `recall_leave_meeting` - -### Connect - -| Command | Description | -|---------|-------------| -| `/recall login [region]` | Connect with Recall.ai API key (region: `us` or `eu`) | -| `/recall status` | Show connection status | -| `/recall logout` | Remove the credential | - -### Prerequisites - -Get an API key from [recall.ai](https://www.recall.ai/). Default region is `us`. - ---- - -## Environment Variables Reference - -Set these in your environment or `.env` file: - -| Variable | Integration | Required for | -|----------|-------------|-------------| -| `GOOGLE_CLIENT_ID` | Google | `/google login` | -| `GOOGLE_CLIENT_SECRET` | Google | `/google login` | -| `LINKEDIN_CLIENT_ID` | LinkedIn | `/linkedin login` | -| `LINKEDIN_CLIENT_SECRET` | LinkedIn | `/linkedin login` | -| `ZOOM_CLIENT_ID` | Zoom | `/zoom login` | -| `ZOOM_CLIENT_SECRET` | Zoom | `/zoom login` | -| `DISCORD_SHARED_BOT_TOKEN` | Discord | Shared bot operations | -| `DISCORD_SHARED_BOT_ID` | Discord | `/discord invite` | -| `SLACK_SHARED_CLIENT_ID` | Slack | `/slack invite` | -| `SLACK_SHARED_CLIENT_SECRET` | Slack | `/slack invite` | -| `TELEGRAM_SHARED_BOT_TOKEN` | Telegram | `/telegram invite` | -| `TELEGRAM_SHARED_BOT_USERNAME` | Telegram | `/telegram invite` | -| `TELEGRAM_API_ID` | Telegram | `/telegram login-user` | -| `TELEGRAM_API_HASH` | Telegram | `/telegram login-user` | -| `NOTION_SHARED_CLIENT_ID` | Notion | `/notion invite` | -| `NOTION_SHARED_CLIENT_SECRET` | Notion | `/notion invite` | diff --git a/mkdocs/docs/core/commands/builtin.md b/mkdocs/docs/core/commands/builtin.md new file mode 100644 index 00000000..c9fa7e01 --- /dev/null +++ b/mkdocs/docs/core/commands/builtin.md @@ -0,0 +1,187 @@ +# Built-in commands + +Every slash command shipped with CraftBot, with its aliases, subcommands, and behavior. All of them work in both [interfaces](../interfaces/index.md). The two marked otherwise say so. + +At a glance: + +| Command | Aliases | What it does | +|---|---|---| +| [`/help [command]`](#help) | `/h`, `/?` | List commands, or detail one | +| [`/clear`](#clear) | `/cls` | Clear the chat and action log | +| [`/clear-tasks`](#clear-tasks) | `/cleartasks` | Remove finished tasks from the task panel | +| [`/reset`](#reset) | (none) | Reset agent state and clear history | +| [`/exit`](#exit) | `/quit`, `/q` | Shut down CraftBot | +| [`/menu`](#menu) | (none) | Open the settings menu (browser only) | +| [`/provider [name] [key]`](#provider) | (none) | View or change the LLM provider | +| [`/mcp `](#mcp) | (none) | Manage MCP servers | +| [`/skill `](#skill) | (none) | Manage skills | +| [`/cred `](#cred) | (none) | Credentials and integration status | +| [`/update [--check]`](#update) | `/upgrade` | Check for and install updates | +| [`/tokens`](#tokens) | (none) | Show this session's token usage | + +Beyond these, the registry also holds [integration commands](#integration-commands) (`/gmail`, `/slack`, ...) and [skill commands](#skill-commands) (`/pdf`, `/docx`, ...), covered at the end. + +## /help + +``` +/help # list all commands with descriptions and aliases +/help mcp # usage, subcommands, and examples for one command +``` + +The leading slash on the argument is optional (`/help mcp` and `/help /mcp` both work). Skill shortcuts are hidden from the main list to keep it short. `/skill list` shows them. + +## /clear + +Clears the chat transcript and the action log in the current interface, and also drops the agent's persisted conversation memory, so a restart won't resurrect the cleared chat. Task history and dashboard data are unaffected. Use it when the conversation is cluttered. Use [`/reset`](#reset) when the *agent* needs a fresh start. + +## /clear-tasks + +Removes tasks whose status is completed, failed, or cancelled from the task panel, along with their child actions. Running and waiting tasks are preserved, and dashboard usage data and task history are untouched. Requires an action panel, so it's effectively a browser command. In the CLI it reports that no action panel is available. + +## /reset + +Resets the agent to its initial state: clears the current task, action history, and conversation context, and wipes the chat view. Saved settings and credentials are **not** affected. Feedback arrives as system messages while the reset runs in the background. + +## /exit + +Stops the agent cleanly and ends the session. In [service mode](../../start/service-mode.md) the service manager may restart it. Use `python craftbot.py stop` to keep it down. + +## /menu + +Opens the settings menu. Browser only. In the CLI it points you to `/help` instead. (In practice you'll click **Settings** in the sidebar; the command exists mainly for keyboard-first use.) + +## /provider + +View or switch the LLM provider without opening settings. + +``` +/provider # show current provider and masked API key +/provider anthropic # switch provider (keeps any stored key) +/provider anthropic sk-ant-... # switch and set the key in one line +``` + +Accepted names: + +| Name | Provider | Key | +|---|---|---| +| `openai` | OpenAI | `OPENAI_API_KEY` | +| `gemini` | Google Gemini | `GOOGLE_API_KEY` | +| `anthropic` | Anthropic | `ANTHROPIC_API_KEY` | +| `byteplus` | BytePlus | `BYTEPLUS_API_KEY` | +| `deepseek` | DeepSeek | `DEEPSEEK_API_KEY` | +| `grok` | Grok (xAI) | `XAI_API_KEY` | +| `glm` | Z.ai (GLM) | `ZAI_API_KEY` | +| `fugu` | Sakana (Fugu) | `SAKANA_API_KEY` | +| `openrouter` | OpenRouter | `OPENROUTER_API_KEY` | +| `remote` | Ollama (local) | none | + +The change is saved to `settings.json` and the LLM reinitializes immediately, with no restart. Model selection, base URLs, and subscription login live in **Settings → Model**; see [LLM providers](../providers/llm.md). + +## /mcp + +Manage [MCP servers](../../integrations/mcp.md). `/mcp` with no arguments prints usage. + +| Subcommand | Does | +|---|---| +| `list [--all]` | List enabled servers; `--all` includes disabled ones | +| `add --transport stdio -- ` | Add a stdio server (everything after `--` is the launch command) | +| `add --transport http ` | Add an HTTP server | +| `add-json ''` | Add a server from a JSON config block | +| `remove ` | Remove a server | +| `enable ` / `disable ` | Toggle a server without removing it | +| `env ` | Set an environment variable for a server | + +``` +/mcp add myserver --transport stdio -- python server.py +/mcp add remote-tools --transport http https://example.com/mcp +/mcp env myserver API_KEY my-secret-key +/mcp list --all +``` + +The browser's **Settings → MCPs** page edits the same configuration. + +## /skill + +Manage [skills](../concepts/skills.md). `/skill` with no arguments prints usage. + +| Subcommand | Does | +|---|---| +| `list [--all]` | List enabled skills; `--all` includes disabled ones | +| `info ` | Description, version, author, path, and the skill's actions | +| `enable ` / `disable ` | Toggle a skill; this also registers/unregisters its slash command | +| `install ` | Install from a local directory | +| `install ` | Install from a GitHub/GitLab URL | +| `create [description]` | Scaffold a new skill | +| `remove ` | Remove a skill | +| `reload` | Re-scan skills from disk | +| `dirs` | Show the directories scanned for skills | + +``` +/skill info pdf +/skill enable cli-anything +/skill install https://github.com/user/skill.git +/skill create my_skill "My custom skill" +``` + +## /cred + +Read-only overview of credentials and integrations. Connecting happens with the [per-integration commands](#integration-commands) or **Settings → Integrations**. + +| Subcommand | Does | +|---|---| +| `list` | Every integration with connected / not connected | +| `status` | Same, with account names for connected integrations and a connected count | +| `integrations` | Every available integration with its `/command` and description | + +## /update + +``` +/update # check GitHub for a newer version and install it +/update --check # check only, don't install +``` + +An update pulls the latest code, installs dependencies, and restarts CraftBot automatically, streaming progress as system messages. If you're already current, it says so. + +## /tokens + +Prints the cumulative token usage of the chat it's typed in, as a system message: + +``` +Session token usage + Input: 79,022 + Cached: 312,455 + Output: 5,770 + Total: 84,792 +``` + +**Input** is genuinely new prompt tokens (cache reads excluded), **Cached** is prompt tokens served from the provider's cache, and **Total** is Input + Output. Totals accumulate across every run in the session and survive restarts and [`/clear`](#clear) (clearing wipes the conversation, not the session's lifetime counters). A brand-new chat that hasn't sent a message yet reports zeros. Sessions created before this command existed start counting from their next run. + +## Integration commands + +Every available [integration](../../integrations/index.md) registers its own command named after itself: `/gmail`, `/slack`, `/discord`, `/telegram_bot`, `/notion`, and so on (run `/cred integrations` for the live list). Each supports: + +``` +/ # help, including integration-specific subcommands +/ connect # start the connect flow (token, OAuth, or interactive/QR) +/ disconnect +/ status # connection state and accounts +``` + +Token-based integrations take their credentials as arguments, like `/telegram_bot connect ` (running `connect` bare tells you which fields it needs). OAuth ones open the provider's flow. Interactive ones (like WhatsApp Web) walk you through it. Some handlers add extra subcommands (QR login, invites), listed in that integration's help. Details per service are on the [integration pages](../../integrations/index.md). + +## Skill commands + +Every **enabled** skill is also a slash command: `/pdf`, `/docx`, `/xlsx`, and whatever else you've enabled. These don't run UI code. They hand your text to the agent with that skill pre-selected: + +``` +/pdf merge report-a.pdf and report-b.pdf into final.pdf +``` + +Arguments flow into the skill's instructions through `$ARGUMENTS` substitution (skills can also grab positional pieces with `$ARGUMENTS[0]`, `$1`, ...). Invoked bare, the agent asks what you need. Enabling or disabling a skill registers or removes its command immediately, and a skill whose name collides with an existing command is skipped. They're hidden from `/help`. See them with `/skill list` or the browser's `/` autocomplete. + +## Related + +- [Commands overview](index.md): how dispatch works +- [CLI-anything](cli-anything.md): the desktop-app automation skill +- [UI layer](../interfaces/ui-layer.md): the registry these commands live in +- [Credentials](../../integrations/credentials.md): where connected secrets are stored diff --git a/mkdocs/docs/core/commands/cli-anything.md b/mkdocs/docs/core/commands/cli-anything.md new file mode 100644 index 00000000..e381992f --- /dev/null +++ b/mkdocs/docs/core/commands/cli-anything.md @@ -0,0 +1,83 @@ +# CLI-anything + +CLI-anything is a bundled [skill](../concepts/skills.md) that lets the agent drive real desktop applications (GIMP, Blender, LibreOffice, Audacity, and two dozen others) from the command line, on Windows, macOS, and Linux. You describe the task ("convert report.docx to PDF", "resize photo.jpg to 1920×1080"). The agent picks the right app, installs it if it's missing, runs it, and reports the result. You never name the app, and you never run a command yourself. + +!!! note "Disabled by default" + CLI-anything ships in the `disabled_skills` list of `app/config/skills_config.json`. Turn it on with `/skill enable cli-anything` or in **Settings → Skills**. Once enabled, it activates automatically whenever a task matches a supported app. Like any enabled skill, it also gets a `/cli-anything` slash command. + +## What it can automate + +Each app is driven through a cross-platform harness command, `cli-anything-`, so the agent never touches platform-specific binaries or paths: + +| Task | App | Harness | +|---|---|---| +| Resize / crop / filter / convert images | GIMP | `cli-anything-gimp` | +| SVG and vector graphics, logo export | Inkscape | `cli-anything-inkscape` | +| Digital painting, `.kra` export | Krita | `cli-anything-krita` | +| DOCX / XLSX / PPTX → PDF, office macros | LibreOffice | `cli-anything-libreoffice` | +| Trim / convert / export audio | Audacity | `cli-anything-audacity` | +| Render and edit video | Kdenlive, Shotcut | `cli-anything-kdenlive`, `cli-anything-shotcut` | +| Screen recording and streaming | OBS Studio | `cli-anything-obs` | +| 3D modeling and rendering, `.blend` files | Blender | `cli-anything-blender` | +| Diagrams (`.drawio`) | Draw.io | `cli-anything-draw-io` | +| Render Mermaid diagram code | Mermaid | `cli-anything-mermaid` | +| AI image generation | Stable Diffusion, ComfyUI | `cli-anything-stable-diffusion`, `cli-anything-comfyui` | +| Run a local LLM | Ollama | `cli-anything-ollama` | +| AI content generation | AnyGen | `cli-anything-anygen` | +| AI research / PDF summarization | NotebookLM | `cli-anything-notebooklm` | +| Execute Jupyter notebooks | JupyterLab | `cli-anything-jupyterlab` | +| CAD, `.fcstd` → STL/STEP | FreeCAD | `cli-anything-freecad` | +| GIS maps, `.qgz` export | QGIS | `cli-anything-qgis` | +| Monitoring dashboards | Grafana | `cli-anything-grafana` | +| Git hosting, repo creation | Gitea, GitLab | `cli-anything-gitea` | +| CI/CD pipelines | Jenkins | `cli-anything-jenkins` | +| Cloud file sync | NextCloud | `cli-anything-nextcloud` | +| Network-wide ad blocking | AdGuard Home | `cli-anything-adguard-home` | +| Video conferencing | Zoom | `cli-anything-zoom` | +| Knowledge outlines | Mubu | `cli-anything-mubu` | + +Ask the agent "what can cli-anything do" and it replies with this catalogue directly, without running anything. + +## How routing works + +The skill's instructions (`skills/cli-anything/SKILL.md`) contain a routing table mapping task descriptions to apps. When the skill is enabled and your request matches ("convert this DOCX", "render this .blend file") the agent selects the app and follows a fixed execution flow: + +1. **Detect the OS** (Windows / macOS / Linux). +2. **Check the app is installed** (`gimp --version` and equivalents). +3. **Install it if missing**, one attempt only, via the platform's package manager: `winget` on Windows, `brew` on macOS, `apt-get` on Linux. A few apps use their own path instead (ComfyUI and Stable Diffusion via `git clone`, Mermaid via npm, JupyterLab via pip, Ollama on Linux via its install script; web apps like Mubu and NotebookLM need no install and are driven through the browser-automation skill). +4. **Check the harness** (`cli-anything- --version`); if missing, install `cli-anything-hub` via pip and pull the harness with `cli-hub install `. If the hub fails, the agent generates a minimal harness itself. +5. **Run the task** using only harness commands, for example: + + ``` + cli-anything-gimp image resize input.jpg output.jpg 1920 1080 + cli-anything-libreoffice convert doc.docx output.pdf + cli-anything-blender render scene.blend --output frames/ --format PNG + ``` + +6. **Report** in a sentence or two: what was produced and where. + +Every step runs as a shell action, so you can watch the whole flow (version checks, installs, the task command) in the [task panel](../interfaces/browser.md#tasks), and it all lands in [logs](../concepts/logs.md). + +The skill hard-bans the failure modes of driving desktop apps directly: no `.exe` suffixes, no hardcoded `C:\Program Files\...` paths, no `&&` command chaining, no raw `soffice`/`gimp`/`blender` invocations. The harness resolves app locations and flags per platform, which is what makes the same task work on all three OSes. + +## Python fallback + +CLI-anything is the first choice, not the only one. If a harness command fails after one retry, the agent falls back to a pure-Python route (PIL for images, python-docx for documents, pydub for audio, moviepy for video), completes the task anyway, and tells you what it actually used (with a note that installing the app gives better results next time). Installs are never retried, timeouts are never looped on, and after repeated failures on one step the agent stops and reports rather than spinning. + +## Requirements + +- **Enable the skill** (see the note above). It does nothing while disabled. +- **Action sets:** the skill declares `shell` and `file_operations`. It works through ordinary shell actions, no extra plumbing. +- **A package manager** for auto-install: `winget` (Windows), Homebrew (macOS), or `apt` (Linux, where installs run under `sudo`). `pip` is needed for the harness hub. +- **Internet access** the first time any given app or harness is installed. +- **File paths:** give the agent full paths to input files (`C:\Users\you\Desktop\photo.jpg`, `/home/user/photo.jpg`) for the smoothest run. + +!!! warning "It installs software" + By design, this skill can install real applications on your machine (silently, with license agreements auto-accepted) and run them with your privileges. Each install is a visible shell action in the task panel, and installs are attempted at most once. If you don't want the agent installing anything, keep the skill disabled or preinstall the apps you care about. + +## Related + +- [Skills](../concepts/skills.md): how skills are enabled, discovered, and invoked +- [Built-in commands](builtin.md#skill): `/skill enable cli-anything` and friends +- [Actions and action sets](../concepts/actions-and-action-sets.md): the shell actions underneath +- [Living UI](../../living-ui/index.md): a different kind of "agent builds it for you" diff --git a/mkdocs/docs/core/commands/index.md b/mkdocs/docs/core/commands/index.md new file mode 100644 index 00000000..2bb4a143 --- /dev/null +++ b/mkdocs/docs/core/commands/index.md @@ -0,0 +1,62 @@ +# Commands + +Commands are slash-prefixed inputs (`/help`, `/provider anthropic sk-...`, `/skill enable pdf`) that the [UI layer](../interfaces/ui-layer.md) intercepts before anything reaches the agent. They're how you configure providers, integrations, skills, and MCP servers (instantly, without spending tokens or starting a task) and they work identically in the [browser](../interfaces/browser.md) and the [CLI](../interfaces/cli.md). + +
+ +- :material-book-alphabet:{ .lg .middle } __[Built-in commands](builtin.md)__ + + --- + + The complete reference: every command, alias, and subcommand. + +- :material-console-line:{ .lg .middle } __[CLI-anything](cli-anything.md)__ + + --- + + The bundled skill that automates desktop apps (GIMP, Blender, LibreOffice, ...) through cross-platform command-line harnesses. + +
+ +## How dispatch works + +When you submit input, the UI controller offers it to the command executor first. Input starting with `/` is split into a name and arguments, resolved against the command registry (aliases included, case-insensitive), and executed; the result comes back as a system message. Anything not starting with `/` goes to the agent as a normal chat message. + +An unknown command like `/frobnicate` returns `Unknown command`. It is **not** forwarded to the agent. Commands and conversation are handled separately. + +In the browser, typing `/` opens an autocomplete listing everything registered, so you rarely need to memorize names. In the CLI, `/help` prints the same list. + +## Four kinds of commands + +The registry is populated from four sources at startup: + +| Kind | Examples | Where they come from | +|---|---|---| +| **Built-in** | `/help`, `/provider`, `/mcp`, `/skill`, `/cred`, `/update` | Shipped in `app/ui_layer/commands/builtin/`; always present | +| **Integration** | `/gmail`, `/slack`, `/telegram_bot`, `/notion` | One per available [integration](../../integrations/index.md), each with `connect` / `disconnect` / `status` plus integration-specific subcommands | +| **Skill** | `/pdf`, `/docx`, `/pptx` | One per **enabled** [skill](../concepts/skills.md); registered and unregistered live as you toggle skills | +| **Agent-provided** | varies | Commands the agent runtime registers programmatically, wrapped into the same registry | + +Built-in and integration commands run immediately in the UI layer. Skill commands are different: `/pdf merge these three files` doesn't run UI code. It routes your text to the agent with that skill pre-selected, and the argument text is substituted into the skill's instructions via `$ARGUMENTS`. It's a shortcut for "do this task, using this skill." + +## The commands you'll actually use + +``` +/help # list every command; /help mcp for details on one +/provider [name] [key] # view or switch the LLM provider +/mcp list # manage MCP servers +/skill list # manage skills +/cred status # see which integrations are connected +/update --check # check for a new CraftBot version +/clear # clear the chat /reset # reset agent state +/exit # shut down +``` + +The full catalogue with every subcommand is in [Built-in commands](builtin.md). + +## Related + +- [Interfaces](../interfaces/index.md): where you type commands +- [UI layer](../interfaces/ui-layer.md): the registry and executor behind dispatch +- [Skills](../concepts/skills.md): the packages behind skill slash commands +- [MCP](../../integrations/mcp.md): what `/mcp` manages diff --git a/mkdocs/docs/core/concepts/actions-and-action-sets.md b/mkdocs/docs/core/concepts/actions-and-action-sets.md new file mode 100644 index 00000000..948e84a6 --- /dev/null +++ b/mkdocs/docs/core/concepts/actions-and-action-sets.md @@ -0,0 +1,103 @@ +# Actions and action sets + +An **action** is one concrete thing the agent can do: write a file, search the web, send a Slack message, run a shell command. The action registry is the agent's entire vocabulary: if no action exists for something, the agent cannot do it. Action **sets** group related actions so each task carries only the vocabulary it needs. + +## Overview +Three layers, each answering a different question: + +| Layer | Question it answers | When it's decided | +|---|---|---| +| **Registry** | What can this agent do at all? | At startup (import time), plus whenever MCP servers connect | +| **Action sets** | What can *this task* do? | Once, at task creation (adjustable mid-task) | +| **Router** | What happens *this turn*? | Every iteration of the [agent loop](agent-loop.md) | + +CraftBot ships 1,176 actions: 56 core actions (task management, files, web, documents, media, shell, scheduling, memory, messaging) and 1,120 integration actions under `app/data/action/integrations//`. The full catalogue is in the [actions reference](default-actions.md). + +## Anatomy of an action + +An action is a Python function with an `@action` decorator that registers it at import time. The metadata (not the implementation) is what the selection LLM sees, so the `description` field does most of the work: + +| Field | What it does | +|---|---| +| `name` | Unique identifier, e.g. `web_search` | +| `description` | What the LLM reads to decide when to pick this action | +| `input_schema` / `output_schema` | Parameter and result contracts, shown to the LLM | +| `action_sets` | Which sets contain this action (an action can be in several) | +| `mode` | Interface visibility (which interface contexts offer the action); `"ALL"` means everywhere | +| `execution_mode` | `"internal"` (in-process) or `"sandboxed"` (isolated venv) | +| `platforms` | `windows` / `linux` / `darwin` / `all`; see platform dispatch below | +| `requirement` | pip packages the action needs; installed automatically before it runs | +| `parallelizable` | Whether it may run alongside other actions in one turn (`False` for writes, state changes, `send_message`) | +| `irreversible` | Marks side effects that can't be undone once they reach the outside world (send email, post publicly) | +| `default`, `test_payload` | Always-available flag (legacy; prefer `action_sets`), and data for simulated test runs | + +**Irreversible actions get a crash guard.** Before an `irreversible=True` action executes, its intent is durably recorded in an activity ledger. After execution the outcome is recorded too. If CraftBot crashes between the send and the record, the guard refuses a blind re-execution and surfaces a warning instead. The agent verifies or asks you rather than sending your email twice. + +**Platform dispatch.** The registry stores implementations per platform. One logical name like `run_shell` can have Windows, macOS, and Linux variants. Lookup picks the current platform's implementation and falls back to the generic `all` one. + +## Action sets + +Sets are labels declared in each action's metadata. The registry discovers them dynamically by scanning, so a custom action declaring `action_sets=["my_tools"]` creates the `my_tools` set with no other registration step. The built-in sets: + +| Set | Contains | +|---|---| +| `core` | 35 always-included actions: messaging, task control, file operations, search, web research, shell and HTTP, clipboard, scheduling, integration management, skill and set management, memory search, and sub-agent spawning | +| `document_processing` | PDF reading, editing, and conversion, markdown conversion, OCR, image description, video understanding | +| `image` | `describe_image`, `generate_image`, `perform_ocr`, `understand_video` | +| `video` | `generate_video`, `perform_ocr`, `understand_video` | +| `content_creation` | `generate_image`, `generate_video` | +| `scheduler` | Schedule management actions (also present in `core`) | +| `proactive` | Recurring-task management for `PROACTIVE.md` plus schedule management | +| `living_ui` | The 7 Living UI lifecycle actions (scaffold, launch, restart, import, data access) | + +Connected integrations contribute their own sets, and each connected [MCP server](../../integrations/mcp.md) becomes a set named `mcp_` (see below). + +## How a task gets its actions + +When a task starts, one LLM call selects both the [skills](skills.md) and the action sets for it, based on the task description: a report-writing task gets `document_processing`, a Living UI build gets `living_ui`. Sets recommended by the selected skill are merged in automatically, `core` is always included, and the union is compiled into a static action list that the task carries for its lifetime. + +This compile-once design is deliberate: during execution there is no retrieval step and no searching for tools. The task's vocabulary is a fixed list the router reads directly. + +The list can still change, though. Mid-task, the agent can call `list_action_sets`, `add_action_sets`, and `remove_action_sets` (all in `core`, so always available) to expand or trim its own vocabulary when it discovers it needs something. These calls appear in the action panel when a task discovers mid-way that it needs another capability. + +## Per-turn selection + +Every iteration of the [agent loop](agent-loop.md), the router makes **one LLM call** that returns reasoning plus a list of one *or more* actions: + +```json +{"reasoning": "...", "actions": [{"action_name": "web_search", "parameters": {...}}, + {"action_name": "task_update_todos", "parameters": {...}}]} +``` + +Rules applied to that list before execution: + +- **Parallel execution.** Multiple actions in one decision run concurrently, up to 10 per batch. +- **Non-parallelizable wins alone.** If any selected action has `parallelizable=False`, it runs by itself and the rest are dropped with an error the agent sees next turn. +- **Format errors retry, then abort.** Malformed LLM output gets up to 3 retries with the parse error fed back. After that the task aborts rather than wasting tokens. +- **Conversation mode is narrow.** Outside a task, the candidates are only `send_message`, `task_start`, `ignore`, plus messaging actions for connected platforms. Real work requires a task. + +Each execution logs `action_start` / `action_end` events to the task's [event stream](event-stream.md), which is what the action panel in the browser renders live. + +## Internal vs sandboxed execution + +`execution_mode` decides where the function body runs: + +- **`internal`** runs in the CraftBot process. Used by actions that touch agent state (task management, messaging, memory) and by MCP tools. Declared `requirement` packages are pip-installed into the main environment before the first run. +- **`sandboxed`** runs in a separate process using a persistent virtual environment (`~/.craftbot/sandbox_venv`, created lazily on first use and reused). Requirements install into the sandbox venv once and persist across calls. A timeout kills runaway executions. + +Either way, the action returns a dict that flows back into the event stream as the observation for the next turn. + +## MCP tools join the same registry + +Servers configured in `app/config/mcp_config.json` (or managed via the `/mcp` command and **Settings**) expose tools that are converted into ordinary actions at connect time: each tool becomes an action named `mcp__`, its JSON Schema becomes the `input_schema`, and all of a server's tools land in an action set named `mcp_` (configurable per server). From that point nothing downstream knows the difference: MCP sets appear in task-creation selection, MCP actions appear in the router's candidates, and disabling a server unregisters its actions. Details and server setup: [MCP](../../integrations/mcp.md). + +!!! note "Implementation files" + Registry and decorator: `agent_core/core/action_framework/registry.py`. Set compilation: `app/action/action_set.py`. Set/skill selection at task creation: `app/internal_action_interface.py`. Per-turn routing: `agent_core/core/impl/action/router.py`. Execution: `agent_core/core/impl/action/manager.py` and `executor.py`. MCP conversion: `agent_core/core/impl/mcp/adapter.py`. + +## Next + +- [Actions reference](default-actions.md): the full catalogue, action by action +- [Write a custom action](../../develop/custom-action.md): one file, one decorator +- [Skills](skills.md): strategy injected on top of the action vocabulary +- [Agent loop](agent-loop.md): where selection and execution sit in the cycle +- [MCP](../../integrations/mcp.md): connecting external tool servers diff --git a/mkdocs/docs/core/concepts/agent-bundles.md b/mkdocs/docs/core/concepts/agent-bundles.md new file mode 100644 index 00000000..c8a61e84 --- /dev/null +++ b/mkdocs/docs/core/concepts/agent-bundles.md @@ -0,0 +1,95 @@ +# Agent bundles and profiles + +An agent profile is the portable part of a CraftBot agent: its personality files, enabled skills, enabled MCP servers, and Living UI apps, packaged into a single `.craftbot` file. Exporting a profile lets you move your configured agent to another machine or share it. Importing a profile turns a stock CraftBot into a configured specialist in one step. + +An **agent bundle** is a pre-built profile authored for a specific role (CEO agent, senior Python engineer, finance agent, and others). CraftOS publishes 42 of them in the [CraftBot Agent Bundles repository](https://github.com/CraftOS-dev/craftbot-agent-bundles), free to download and import. + +## Example bundles + +The 42 bundles are grouped by domain. Each one wires the agent with the skills, MCP servers, and operating rules a senior practitioner of that role uses, so the agent executes the work rather than only describing it. A sample: + +| Bundle | Domain | What it does | +|---|---|---| +| `ceo-agent` | Executive | Drafts strategy documents, board packs, and investor updates, and tracks OKRs. | +| `finance-agent` | Finance | Builds FP&A models, runs capital-allocation analysis, and drafts term sheets. | +| `project-manager` | Operations | Writes charters, work-breakdown structures, Gantt schedules, and status reports. | +| `senior-python-engineer` | Engineering | Reviews Python code, debugs failures, and proposes optimizations. | +| `devops-engineer` | Engineering | Works with containers, Kubernetes, infrastructure-as-code, and GitOps pipelines. | +| `marketing-agent` | Marketing | Plans campaigns and produces content across channels. | +| `seo-specialist` | Marketing | Audits sites, researches keywords, and drafts on-page and technical SEO fixes. | +| `sales-agent` | Sales | Researches prospects, drafts outreach, and maintains pipeline records. | +| `data-analyst` | Research | Queries data, runs analysis, and turns results into reports and charts. | +| `legal-counsel` | Legal | Reviews contracts, flags risk, and drafts standard clauses. | +| `recruiter` | People | Writes job descriptions, sources candidates, and screens applicants. | +| `personal-assistant` | Personal | Manages your inbox, calendar, and day-to-day tasks. | + +The repository lists all 42 with a description of each, spanning executive and strategy, engineering, marketing and growth, sales and customer, content and documentation, research and analytics, legal and compliance, and people and product roles. + +## What a profile contains + +A `.craftbot` file is a ZIP archive. It carries the parts of an agent that are portable between installs: + +| Included | Notes | +|---|---| +| Personality files | `SOUL.md`, `USER.md`, and the other `agent_file_system` markdown that defines behavior. See [Agent file system](agent-file-system.md). | +| Enabled skills | The skill folders you have enabled. Skills already present on the target install are not re-shipped. See [Skills](skills.md). | +| Enabled MCP servers | Server definitions from `mcp_config.json`, with secret environment values stripped out. See [MCP servers](../../integrations/mcp.md). | +| Living UI apps | Any apps you built. See [Living UI](../../living-ui/index.md). | +| `manifest.json` and `README.md` | Bundle metadata and a human-readable summary of the contents. | + +Four things are deliberately **never** included, so a bundle is safe to share: + +- API keys and provider credentials. +- OAuth tokens and integration secrets. +- Personal memory (`MEMORY.md` and the memory index). +- Conversation history. + +## Importing a profile + +You import a profile from the browser interface. + +1. Open **Settings → General** and choose **Import Agent Profile**. +2. Select or drag in a `.craftbot` file. To use a pre-built bundle, download one from the [agent bundles repository](https://github.com/CraftOS-dev/craftbot-agent-bundles) first (see [Using a pre-built bundle](#using-a-pre-built-bundle) below). +3. CraftBot inspects the bundle and shows a preview: which skills and MCP servers it contains, which of those you already have installed, and which MCP servers need environment values (API keys) that were stripped on export. +4. Choose an import mode: + +| Mode | Effect on existing personality files | +|---|---| +| **Merge and Replace** | Applies the bundle's skills, MCP servers, and apps, and replaces the personality files the bundle provides. Files the bundle does not include are left alone. | +| **Overwrite** | Replaces the personality files with the bundle's versions. | + +5. Confirm. CraftBot installs the bundle's skills into your `skills/` directory and enables them, adds its MCP servers (disabled until you supply the missing keys), applies the personality files, and registers any Living UI apps. + +### What import does not change + +- **Your model and provider stay the same.** A bundle's `agent.yaml` names a recommended model, but importing never switches your configured provider or API key. Set the model yourself from **Settings → Model** if you want the recommended one. See [LLM providers](../providers/llm.md). +- **Your existing credentials stay.** The importer only adds skills, servers, personality files, and apps. It does not touch your keys or connected integrations. +- **Skills are added, not deleted.** If a skill folder already exists, the importer keeps it and reports it as skipped rather than overwriting your version. + +After import, the agent typically asks a few questions about your routines and proposes recurring tasks for `PROACTIVE.md`. See [Proactive mode](../modes/proactive.md). + +### Using a pre-built bundle + +1. Open the [CraftBot Agent Bundles repository](https://github.com/CraftOS-dev/craftbot-agent-bundles). +2. Open the `bundles/` folder and download the file for the role you want. Files are named `-.craftbot`, for example `ceo-agent-20260611.craftbot`. The most recent date is the current version. +3. Import it with the steps above. +4. Fill in API keys for the MCP servers you actually plan to use. The preview lists exactly which servers need keys. You do not need to configure servers for tools you will not use. + +The repository lists all 42 bundles grouped by domain (executive, engineering, marketing, sales, research, legal, and others), each with a description of what it does. + +## Exporting your own profile + +Once you have configured an agent you like, you can export it. + +1. Open **Settings → General** and choose **Export Agent Profile**. +2. Optionally add a description. CraftBot writes a `.craftbot` file named `craftbot--.craftbot`. + +The export includes only enabled skills and enabled MCP servers, so the file stays small and does not carry the roughly 157 disabled default servers or machine-specific command paths. Secrets are stripped, so the recipient supplies their own keys. + +Use export to move your agent to another machine, back up a configuration before experimenting, or share a persona you built. To publish a polished persona to the public repository, follow the authoring and submission process in [Create an agent bundle](../../develop/custom-agent.md). + +## Next + +- [Create an agent bundle](../../develop/custom-agent.md): author your own persona and publish it to the repository +- [Agent bundle config](../configuration/agent-config-yaml.md): the `agent.yaml` manifest fields +- [Skills](skills.md) and [MCP servers](../../integrations/mcp.md): the two capability layers a bundle carries diff --git a/mkdocs/docs/core/concepts/agent-file-system.md b/mkdocs/docs/core/concepts/agent-file-system.md new file mode 100644 index 00000000..ba12fb12 --- /dev/null +++ b/mkdocs/docs/core/concepts/agent-file-system.md @@ -0,0 +1,83 @@ +# Agent file system + +`agent_file_system/` at the project root is the agent's home directory: a dozen markdown files that hold its identity, its knowledge of you, and its memory of everything that's happened, plus a `workspace/` where task outputs land. Understanding who writes each file, and which ones you're meant to edit, is the single highest-leverage way to tune your agent. + +## Overview +The files fall into three categories. Some are **yours to write**: the personality brief, your profile, the style guide. Some are **the agent's working notes**: the ops manual it consults and the task list it maintains. The rest are **records**: event logs, task history, and distilled memory, maintained by harness subsystems rather than by you or the agent. + +The agent reads and writes these files with ordinary file actions, and a few (SOUL.md, AGENT.md pointers, USER.md) feed directly into every LLM call. Edit one and behavior changes on the next turn, with no restart. + +The directory is seeded from templates in `app/data/agent_file_system_template/` on first run, and the `/reset` command restores the markdown files from those templates. + +## The files + +| File | Who writes it | May I edit it? | What it does | +|---|---|---|---| +| `SOUL.md` | You (agent only on your explicit request) | **Yes, the main personality knob** | Personality, tone, behavior. Injected into the system prompt every turn | +| `USER.md` | Onboarding wizard; agent, after confirming with you | **Yes** | Your profile: identity, timezone, communication preferences, life goals | +| `FORMAT.md` | You | **Yes** | Formatting standards the agent reads before generating any document | +| `GLOBAL_LIVING_UI.md` | You | **Yes** | Global design preferences for every [Living UI](../../living-ui/index.md) project: colors, theme, enforced rules | +| `AGENT.md` | Ships with CraftBot; agent appends learned operational fixes | Yes, carefully | The agent's versioned operations manual: runtime, errors, integrations, conventions. The agent greps it by `## ` | +| `PROACTIVE.md` | `recurring_*` actions and the planners | Prefer the actions; preserve the `` markers | Recurring proactive tasks plus the planner's Goals / Plan / Status; see [Proactive mode](../modes/proactive.md) | +| `MEMORY.md` | Memory processor only (nightly job) | **No** | Distilled long-term memory, one timestamped fact per line; see [Memory](memory.md) | +| `EVENT.md` | Event stream manager | **No** | Append-only chronological log of every event (actions, messages, errors) | +| `EVENT_UNPROCESSED.md` | Event stream manager | **No** | Staging buffer of events awaiting the nightly memory run; cleared after each run | +| `TASK_HISTORY.md` | Appended on every `task_end` | **No** | One summary section per finished task: status, timestamps, outcome, skills used | +| `CONVERSATION_HISTORY.md` | Event stream manager | **No** | Rolling transcript of every user ↔ agent exchange, never auto-cleared | +| `MISSION_INDEX_TEMPLATE.md` | Static template | **No** | Copied into `workspace/missions//INDEX.md` when a mission starts | + +The "No" files are harness-managed. Hand-editing them creates inconsistencies the agent can't recover from: the memory pipeline expects `MEMORY.md` in its exact line format, and the event logs are the ground truth other subsystems replay. Read them freely, but never write to them. + +## Files you should edit + +Three files do most of the customization work: + +**SOUL.md** shapes *how* the agent behaves. It's injected into the system prompt on every single turn, so edits take effect immediately and affect every interaction. Want it more terse, more playful, stricter about asking before acting? Say so here, or just tell the agent to update its soul, and it will ask for confirmation before saving. + +**USER.md** shapes *who it's working for*. Onboarding fills the skeleton (identity, timezone, communication preferences, goals). Keep it current as things change. The agent reads it at the start of user-facing tasks and only writes durable, confirmed facts back. One-off requests don't land here. + +**FORMAT.md** shapes *what it produces*. The agent consults it before generating any file. A `## global` section sets universal rules (colors, typography, writing style), and per-filetype sections (`## pptx`, `## docx`, `## xlsx`, `## pdf`) override it for that format. If every deck the agent makes has the wrong brand color, fix it once here and every future document follows. + +`GLOBAL_LIVING_UI.md` plays the same role for generated apps: design preferences and enforced rules applied to every Living UI project, with per-project answers overriding when they conflict. + +A useful side-effect to know: `AGENT.md`, `PROACTIVE.md`, `MEMORY.md`, `USER.md`, and `EVENT_UNPROCESSED.md` are indexed for the agent's semantic memory search, and a file watcher re-indexes them the moment they change, so edits to these files become retrievable knowledge, not just prompt text. + +## workspace/ + +Everything a task produces lands under `agent_file_system/workspace/`. Four zones with different lifecycles: + +```text +workspace/ +├── Persistent task outputs: reports, exports, +│ anything you asked for. Never auto-cleaned. +├── tmp// Per-task scratch: drafts, downloads, +│ intermediate state. Auto-created when the task +│ starts; auto-deleted on task end AND at startup. +├── missions// Multi-session initiatives. INDEX.md (from the +│ template) records goal, findings, next steps; +│ it's what a future task reads to restore context. +│ Never auto-cleaned. +└── living_ui/_/ Living UI projects: self-contained apps managed + by their own lifecycle actions. Don't rename or + delete these by hand. +``` + +The practical rules: + +- **Deliverables go in the workspace root.** That's where "save it as frameworks.md" ends up, and where you go looking for outputs. +- **Anything in `tmp/` is disposable by design.** If a task saved something there that you want, move it out before the task ends. +- **Missions are for work bigger than one task** (a job hunt, a research program). The mission's `INDEX.md` is the durable state. Individual tasks come and go. + +## Configuration and limits + +- **Location:** `agent_file_system/` in the project root. The template lives at `app/data/agent_file_system_template/`. +- **Edits apply on the next trigger**, with no restart. `SOUL.md` in particular takes effect on the very next turn. +- **Reset:** `/reset` deletes the markdown files and re-copies the templates. Workspace contents are handled separately, and Living UI projects are preserved by the generic reset (they have their own teardown). +- **Growth:** `EVENT.md` auto-rotates on size. `CONVERSATION_HISTORY.md` and `TASK_HISTORY.md` grow indefinitely, and `EVENT_UNPROCESSED.md` is cleared by each successful memory run. +- **Not for secrets:** API keys and credentials live in `app/config/settings.json` and `.credentials/`, not in these markdown files. + +## Next + +- [Memory](memory.md): how events become `MEMORY.md` facts, and how the agent retrieves them +- [Proactive mode](../modes/proactive.md): the system that reads and maintains `PROACTIVE.md` +- [Living UI](../../living-ui/index.md): the projects living under `workspace/living_ui/` diff --git a/mkdocs/docs/core/concepts/agent-loop.md b/mkdocs/docs/core/concepts/agent-loop.md new file mode 100644 index 00000000..a4ff63db --- /dev/null +++ b/mkdocs/docs/core/concepts/agent-loop.md @@ -0,0 +1,98 @@ +# Agent loop + +The agent loop is the cycle CraftBot runs every time something wakes it up: claim a [trigger](triggers.md), route it to a workflow, let the LLM pick actions, execute them, and queue the follow-up. This cycle explains why tasks tick forward one step at a time, why the agent can wait hours for your reply without burning tokens, and why a restart doesn't lose work in flight. + +## Overview +CraftBot does not run continuously. The agent sleeps until a trigger fires (your message, a schedule, a task's own "continue" note) then runs **exactly one turn** and goes back to sleep. The design has three properties: + +1. **One trigger, one turn.** A turn is a single pass through the loop: the LLM picks one or more [actions](actions-and-action-sets.md), CraftBot executes them, and the results land on the [event stream](event-stream.md). +2. **Continuation is a new trigger, not a loop.** A ten-step task is not a `while` loop held in memory. Each turn ends by enqueuing a fresh continuation trigger for the same session. The next turn picks it up. Waiting is just a trigger with a `fire_at` timestamp in the future. +3. **State lives outside the process.** Progress is recorded in the task's todos, its event stream, and the durable trigger queue, so a crash or restart between turns re-delivers the pending trigger and the task resumes where it left off. + +This is also why several tasks can run "at once": their triggers interleave through the same loop, each turn scoped to its own [session](task-sessions.md). + +## The outer loop + +A single consumer drives everything: + +| Step | What happens | +|---|---| +| 1. Claim | `trigger_service.next()` waits for the next due trigger and marks its durable record as claimed | +| 2. React | `agent.react(trigger)` runs one full turn (everything below) | +| 3. Settle | On success the trigger is `ack()`ed (done); on an exception it is `nack()`ed, which retries it with backoff | + +A crash between claim and settle leaves the trigger claimed. The next boot re-delivers it. The guarantee is *at-least-once*, never silently lost. The details are on the [Triggers](triggers.md) page. + +!!! note "Implementation files" + The consumer is `_consume_triggers()` in `app/ui_layer/controller/ui_controller.py`. The turn itself is `AgentBase.react()` in `app/agent_base.py`. Claim/ack/nack live in `app/triggers/service.py`. + +## Inside a turn: routing + +`react()` checks the trigger, then the session's state, in a fixed order. First match wins: + +| Order | Condition | What runs | +|---|---|---| +| 1 | Trigger is a restart notice | Posts the prebuilt "I was restarted" message to chat and returns; no LLM call | +| 2 | Trigger source is `memory` | Memory workflow: spawns a task that distills recent events into long-term [memory](memory.md) | +| 3 | Trigger source is `proactive_heartbeat` / `proactive_planner` | [Proactive](../modes/proactive.md) workflow: collects due recurring tasks or runs a planner | +| 4 | Task waiting for your reply, and this trigger carries no message | Re-schedules the wait for another 3 hours and returns; the task keeps sleeping | +| 5 | Session has a running **complex** task | Complex-task workflow: todo-driven, approval-gated | +| 6 | Session has a running **simple** task | Simple-task workflow: linear, auto-completing | +| 7 | Anything else | Conversation workflow: no task exists yet | + +Before steps 4–7, the turn initializes the session and, if the trigger carries a user message routed in mid-task, records it onto the event stream so the LLM sees it. + +The three main workflows (5–7) differ in prompt shape, todo handling, and caching (compared side by side in [Task modes](../modes/index.md)) but they all execute the same four-phase pipeline. + +## The turn pipeline + +Every conversation, simple-task, and complex-task turn runs the same four phases: + +1. **Select.** One LLM call chooses one or more actions and their inputs, based on the task instruction, todos, and the event stream. In conversation mode the menu is deliberately tiny: reply, start a task (several in parallel is allowed), or deliberately ignore a message that needs no reaction. +2. **Prepare.** Each selected action is resolved by name from the task's action sets and its inputs are bound. +3. **Execute.** The actions run, in parallel when more than one was selected. Every action logs `action_start` / `action_end` events, which is what the action panel in the browser renders live. +4. **Finalize.** The action output is inspected: did it create a task? ask for a delay (`wait`)? flag `waiting_for_user_reply`? Then a **new continuation trigger** is enqueued for the session (or for each task that a parallel `task_start` created) and the turn ends. + +The finalize phase drives multi-step work. A complex task making twenty tool calls is roughly twenty turns, each handed to the next by a `task_continuation` trigger. Between turns the agent is idle, free to run a different task's turn or to sleep. + +## What happens when you send a message + +Putting it together, end to end: + +1. Your message is durably recorded, then [session routing](task-sessions.md) decides whether it continues an existing task or opens a fresh session. +2. A `user_message` trigger fires. The consumer claims it and calls `react()`. +3. No task is running for the fresh session, so the conversation workflow runs: the LLM either answers directly (`send_message`) or calls `task_start`. +4. If a task started, finalize queues a continuation trigger. Each subsequent turn works a todo, until the agent sends you a result and (for complex tasks) waits for your approval before `task_end`. +5. If the agent asked you something mid-task, the task flips to waiting-for-reply and its trigger sleeps. Your answer routes back and wakes it immediately. + +## Watch it run + +- **In the browser.** The task card, todo list, and action panel are a live rendering of the loop: each visible action is one entry in a turn's execute phase. +- **In the logs.** Every run writes to `logs/` ([Logs](logs.md)). Grep for the loop's own tags: + +```bash +grep -E "\[REACT\]|\[WORKFLOW|\[ACTION\]|\[TRIGGER" logs/.log +``` + +```text +[REACT] starting... +[WORKFLOW: CONVERSATION] Query: what's the weather in Tokyo +[ACTION] Ready to run 1 action(s): ['task_start'] +[TRIGGER] Creating new trigger for session: 4f2c1a +``` + +- **On disk.** Every event a turn produces is also appended to `agent_file_system/EVENT.md` ([Event stream](event-stream.md)). + +## Limits and error handling + +- **Per-task budgets.** Each task counts its actions and tokens. At 80% of either limit the agent gets a warning event telling it to wrap up. At 100% the task pauses and you get a Continue/Abort choice in chat. Nothing runs unbounded. +- **Waiting costs nothing.** A task waiting for your reply re-schedules itself in 3-hour hops without invoking the LLM (step 4 in the routing table). +- **Errors don't kill the loop.** Exceptions inside a turn are caught by `react()` itself, logged, and surfaced to the affected session. The consumer keeps running. Failures that escape a turn entirely cause a `nack()`: retry with exponential backoff, then a dead-letter message in chat rather than silent loss (see [Triggers](triggers.md)). +- **Feature switches.** Disabling memory or proactive mode in settings makes their triggers no-ops. Routing steps 2 and 3 return without doing anything. + +## Next + +- [Triggers](triggers.md): everything that wakes the loop, and what survives a restart +- [Task sessions](task-sessions.md): how messages find the right task, and how tasks live and end +- [Event stream](event-stream.md): the record each turn reads from and writes to +- [Task modes](../modes/index.md): conversation vs simple vs complex, compared diff --git a/mkdocs/docs/core/concepts/context-engine.md b/mkdocs/docs/core/concepts/context-engine.md new file mode 100644 index 00000000..7a9d145f --- /dev/null +++ b/mkdocs/docs/core/concepts/context-engine.md @@ -0,0 +1,96 @@ +# Context engine + +Every time CraftBot calls the LLM, the context engine decides what the model actually sees: who the agent is, who you are, what's happening right now, and what it's being asked to decide. Understanding its layout explains most of CraftBot's token costs, most of its speed, and most of "why did the agent know that?" + +## Overview +Every LLM call is two halves: + +| Half | Contents | Changes between calls? | Cached? | +|---|---|---|---| +| **Static prefix** (system prompt) | Agent identity, your profile, personality, policy, environment, file-system map | No; byte-identical within a session | Yes (provider KV cache) | +| **Dynamic tail** (user prompt) | The decision template, current task, conversation history, live event stream, your query | Yes, every call | Only incrementally | + +The split is the whole design. LLM providers cache a prompt *prefix*: as long as the opening bytes of a call are identical to a previous call, those tokens are nearly free and fast. So the engine pushes everything stable to the front and everything volatile to the back. A follow-up call in a long task pays full price only for the events that happened since the last call, not for the agent's entire identity again. + +One consequence worth internalizing: **anything that varies call-to-call is banned from the prefix.** The clearest example is the current date and time. It would be natural to put "it is 14:32 on Thursday" in the system prompt, but that would change the prefix every call and bust the cache (Gemini's implicit caching is prefix-based, so even one changed byte invalidates everything after it). The engine deliberately keeps date/time out of the cached prefix. A dedicated `current_datetime_block` renders it for the dynamic tail, and every event in the stream carries its own timestamp, so the model still knows when things happened. + +## System prompt contents + +The engine assembles the system prompt from fixed sections in a fixed order: + +| # | Section | What it contains | You control it via | +|---|---|---|---| +| 1 | Agent info | Capabilities, task system, working ethic, format standards | Nothing (built-in) | +| 2 | User profile | Your `USER.md`, verbatim | Edit [`USER.md`](agent-file-system.md) | +| 3 | Soul | Your `SOUL.md`, verbatim: personality and tone | Edit [`SOUL.md`](agent-file-system.md) | +| 4 | Language instruction | "Use the user's preferred language" rule | Language preference in `USER.md` | +| 5 | Policy | Safety, privacy, prompt-injection defense | Nothing (built-in) | +| 6 | Role info | Agent name + role persona | [Onboarding](../../start/onboarding.md) sets the name | +| 7 | Environment | Timezone, working directory, OS (stable facts only) | Nothing (detected) | +| 8 | File system | Map of `agent_file_system/`: what each file is for | Nothing (built-in) | +| 9 | Base instruction | One-line closing instruction | Nothing (built-in) | + +Sections 2 and 3 are read from disk at prompt-build time, which is why editing `USER.md` or `SOUL.md` changes behavior on the very next call, with no restart. It also means an edit invalidates the cached prefix once. The first call after the edit pays full price, then caching resumes. The prompt templates behind each section are covered in [Prompts](prompts.md). + +## Per-turn message contents + +The tail is built per call and per session. Its ingredients, roughly back-to-front: + +**The decision template.** Which one depends on what's being decided: conversation-mode action selection, in-task selection, session routing, and so on (see [Prompts](prompts.md)). Within the tail, static template text still comes first and volatile content last, for the same caching reason. + +**``** holds the active task's name, instruction, and mode, plus **``** (the instructions of any skill selected for the task) and agent state. + +**``** holds the most recent user/agent messages (default **20**) from *before* the current task. This is context, not work: it lets a task understand "the thing we discussed a minute ago" without those messages polluting the task's own record. + +**``** is the live snapshot of the current session's [event stream](event-stream.md): every action started and finished, every message, every error, in order, with timestamps. This is the working memory of the task. + +The two are easy to conflate but behave differently: + +| | `` | `` | +|---|---|---| +| Contains | Chat messages before the task | Everything during the task | +| Scope | Global, shared context | One per task session | +| Growth | Capped at recent 20 messages | Grows until summarized | +| Marked as | "historical context" | "the current situation" | + +**``** appears when the triggering message came from an external platform (Telegram, Slack, Discord, ...). This small block identifies the platform, whether it's you or a third party, the sender, and the channel. This is how the agent replies on the right platform and how it knows a third-party message isn't an instruction from you. + +**Memory** is deliberately *not* injected by the engine itself. When a message arrives or a task starts, the memory system logs a single `relevant_memories` event into the event stream: pointers to matching facts, not full content. The model sees memory as just another event, right next to the message that triggered the lookup. Full lifecycle in [Memory](memory.md). + +## Cache behavior and cost + +CraftBot uses two cache levels: + +- **Prefix cache**: the static system prompt. Used for every call, including plain conversation. After the first call, the identity/profile/policy block is served from cache. +- **Session cache**: for tasks, the growing context is cached per task and per call type, and subsequent calls send only *delta events*, the events appended since the last sync. A 50-step task doesn't resend 49 steps of history on step 50. + +Conversation mode uses prefix caching only. Tasks add session caching on top. The mechanics differ per provider (Anthropic uses `cache_control` blocks, Gemini an explicit context cache, BytePlus server-side prefix/session caches, OpenAI-style providers cache automatically), but the engine's prompt layout is what makes any of them effective. + +The cost implication: a long task's per-step price is dominated by *new* events, not accumulated context. The corollary: anything that invalidates the prefix (editing `SOUL.md` mid-task, switching models) makes the next call pay full price. And when the event stream hits its summarization threshold, older events are compacted and session sync points reset. The next call repopulates the cache from the summarized stream. + +## Inspecting the assembled prompt + +- **Cache metrics in logs.** Grep `logs/` for `[CACHE METRICS]` lines. They report hits, misses, and the percentage of tokens served from cache per provider and call type. A healthy long task shows a high token-cache rate after the first few steps. +- **Memory injections.** `relevant_memories` events appear in the event stream panel like any other event, so you can see exactly which memories the model saw and when. +- **`[CONTEXT]` warnings** in logs flag failures to read `USER.md`/`SOUL.md`. If your profile edits seem ignored, look here first. + +## Configuration + +Cache behavior is tuned in the `cache` section of [`settings.json`](../configuration/config-json.md): + +| Key | Default | Meaning | +|---|---|---| +| `cache.prefix_ttl` | `3600` | Seconds the system-prompt prefix cache is kept | +| `cache.session_ttl` | `7200` | Seconds a per-task session cache is kept (long tasks) | +| `cache.min_tokens` | `500` | Skip caching for prompts shorter than this | + +The conversation-history window (20 messages) and the section order are code-level defaults, not settings. The event stream's summarization thresholds (which bound how large the dynamic tail can grow) are covered in [Event stream](event-stream.md). + +!!! note "Implementation files" + The engine is `agent_core/core/impl/context/engine.py` (`ContextEngine`). `make_prompt()` assembles the system sections in the order above. `get_event_stream()`, `get_task_state()`, and `get_message_source_block()` build the dynamic tail. `get_event_stream_delta()` / `mark_event_stream_synced()` implement session-cache delta tracking. Prompt templates live in `agent_core/core/prompts/`. + +## Next + +- [Prompts](prompts.md): the templates the engine assembles, and the files you edit to steer them +- [Event stream](event-stream.md): the dynamic half: summarization, delta tracking, thresholds +- [Memory](memory.md): how `relevant_memories` events get into the stream diff --git a/mkdocs/docs/core/concepts/event-stream.md b/mkdocs/docs/core/concepts/event-stream.md new file mode 100644 index 00000000..ffcea6c7 --- /dev/null +++ b/mkdocs/docs/core/concepts/event-stream.md @@ -0,0 +1,106 @@ +# Event stream + +The event stream is the agent's working record: an append-only log of everything that happens in a [task session](task-sessions.md) (messages, reasoning, action starts and results, task boundaries). It is simultaneously what the chat UI renders, what the LLM reads as history on every turn, and the raw material the [memory pipeline](memory.md) distills. If you want to know "what did the agent actually see when it made that decision", the answer is always: its event stream at that moment. + +## Overview +- **One stream per session.** A main stream carries conversation-mode activity. Every task gets its own stream when it starts, so parallel tasks never read each other's history. +- **Recent events stay verbatim while old events get folded.** Each stream keeps a tail of full-fidelity events plus a rolling `head_summary`. When the tail grows past a token threshold, the oldest chunk is summarized by the LLM into the head and dropped from the tail. +- **Everything is an `Event`**: a message, a typed category, a severity, and optional structured fields (action inputs/outputs, platform, task status). Repeated identical events are collapsed into one record with a repeat counter instead of flooding the log. + +What the LLM sees each turn is the stream's *prompt snapshot* (the head summary followed by the recent tail) assembled into context by the [context engine](context-engine.md). + +## Event types + +Every event carries a typed category. This is a closed set. Consumers route on it, never on message text: + +| Event type | Recorded when | +|---|---| +| `user_message` | You send a message (locally or via a connected platform) | +| `agent_message` | The agent replies; this is what appears as a chat bubble | +| `reasoning` | The LLM explains why it picked the next action(s) | +| `action_start` / `action_end` | An action begins / finishes; carries the action name, a paired id, and structured input/output | +| `task_start` / `task_end` | A task's boundaries; `task_end` carries the final status | +| `todos` | The todo list changed | +| `waiting_for_user` | The task paused for your reply | +| `relevant_memories` | Memory retrieval injected context pointers | +| `system` / `error` | Harness notices and failures | +| `internal` | Bookkeeping the UI hides | + +## How the UI renders it + +The chat and the action panel are direct projections of streams: + +- The UI watches all streams (main + every task) and routes each event **by its `event_type` only**: `agent_message` becomes a chat bubble, `action_start`/`action_end` become the live action rows, `todos` updates the checklist, `waiting_for_user` flips the status bar. +- `action_start` and `action_end` share an `action_id`, so the panel can pair them even when several copies of the same action run in parallel. +- Events may carry a shorter `display_message` for the UI while keeping the full `message` for the LLM and for debugging. + +Nothing happens off the record: if the agent did it, there is an event for it, and the UI shows the ones that concern you. + +!!! note "Implementation files" + The event model and type enum are `agent_core/core/event_stream/event.py`. The per-stream mechanics (tail, summary, snapshots) are `agent_core/core/impl/event_stream/event_stream.py`. Stream creation per task and the file logging below are `EventStreamManager` in `agent_core/core/impl/event_stream/manager.py`. + +## EVENT.md and EVENT_UNPROCESSED.md + +Every event is also appended to markdown files in `agent_file_system/` (see [Agent file system](agent-file-system.md)), one line per event: + +| File | Contents | +|---|---| +| `EVENT.md` | The complete history: every event from every stream, in `[YYYY/MM/DD HH:MM:SS] [kind]: message` format. Auto-rotated when it grows too large. | +| `EVENT_UNPROCESSED.md` | The staging buffer for the [memory pipeline](memory.md): the subset of events awaiting distillation into `MEMORY.md`, cleared after each processing run. | + +Routine event kinds that the memory processor would always discard (action starts/ends, reasoning, todos, errors, waiting notices, memory-retrieval pointers) are filtered out at write time, so `EVENT_UNPROCESSED.md` contains only dialogue and meaningful state changes. During a memory-processing task the buffer is frozen entirely, so the processor's own events can't loop back into it. + +These files are also the agent's own audit trail: when it troubleshoots itself, `EVENT.md` is the first place it greps. + +## Automatic stream summarization + +The stream is re-read by the LLM every turn, so each stream compacts itself: + +1. When the tail exceeds **30,000 tokens**, the oldest events (down to a **10,000-token** surviving tail) are packaged with the existing head summary and sent to the LLM. +2. The LLM returns an updated summary. It replaces the head, and the summarized events are dropped from the tail. +3. A few protected event kinds (notably the task's recorded requirements) are never folded into a summary. They survive verbatim so the task's definition of done can't be summarized away. +4. If the LLM provider is failing, the stream falls back to pruning the oldest events *without* a summary rather than hammering a dead endpoint. + +You can see this in the [logs](logs.md): + +```text +[EventStream] Triggering summarization: 31204 tokens >= 30000 threshold +[EventStream] Summarization complete. Tokens: 9845 +``` + +Separately, any single event message longer than about **16,000 characters** (a huge web page, a big file read) never enters the stream at all. It is written to the task's temp directory and replaced by a pointer event containing the file path and extracted keywords. The agent reads the file back with its file actions only if it actually needs the content. One oversized action result can't blow up every subsequent turn's prompt. + +## Relation to caching and memory + +- **Prompt caching.** Streams track per-call-type sync points so that, on cached turns, only events added since the last call are sent as a delta instead of re-sending the whole history. Summarization invalidates those sync points (the indices shift), which triggers a cache rebuild. The full story is in [Context engine](context-engine.md). +- **Long-term memory.** The stream is working memory. It ends with its task. Anything worth keeping across sessions flows through `EVENT_UNPROCESSED.md` into the memory pipeline (see [Memory](memory.md)). + +## Where events appear in the UI and logs + +- **The chat itself.** Bubbles, action rows, and todo updates are the stream, rendered. +- **On disk.** Follow the master log while you interact: + +```bash +tail -f agent_file_system/EVENT.md +``` + +```text +[2026/07/17 10:14:02] [action_start]: web_search +[2026/07/17 10:14:04] [action_end]: web_search -> success (5 results) +[2026/07/17 10:14:09] [agent message to platform: CraftBot Interface]: Here's what I found... +``` + +- **In logs.** Grep `EventStream` in `logs/` for summarization and stream lifecycle activity. + +## Limits + +- The summarization thresholds (30k trigger / 10k keep) are constructor defaults of the stream, not user settings. They are tuned to balance context quality against per-turn cost. +- A summary is lossy by design. Recent events are exact. Older history is the LLM's condensation of it. Durable facts belong in [memory](memory.md), not in the stream. +- Task streams are removed when their task ends. The permanent records are `EVENT.md`, `TASK_HISTORY.md`, and whatever memory distilled. + +## Next + +- [Agent loop](agent-loop.md): the producer: every turn writes here +- [Task sessions](task-sessions.md): why each task gets its own stream +- [Context engine](context-engine.md): how snapshots and deltas reach the LLM +- [Memory](memory.md): how events become long-term memory diff --git a/mkdocs/docs/core/concepts/logs.md b/mkdocs/docs/core/concepts/logs.md new file mode 100644 index 00000000..ed5a7cae --- /dev/null +++ b/mkdocs/docs/core/concepts/logs.md @@ -0,0 +1,100 @@ +# Logs + +When the agent does something unexpected (a task stalls, a schedule doesn't fire, an action errors) the logs are the ground truth. Every run writes a timestamped folder under `logs/` at the project root, capturing what every subsystem did, down to module and line number. + +## Overview +CraftBot logs with **Loguru**, and each process run gets **one folder**: `logs//` (e.g. `logs/20260717085754/`). Inside, the same stream is split three ways by *who was speaking*: + +| File | Contains | Read it when | +|---|---|---| +| `main.log` | Only the main agent (plus framework startup) | You want the primary agent's story without sub-agent noise | +| `all.log` | Everything, interleaved in true time order: main agent and every sub-agent | You're debugging anything that crosses agents, or just want the full picture. **Start here** | +| `sub__.log` | One file per sub-agent spawned during the run (e.g. `sub_research_agent_2a707e74.log`) | A specific delegated job misbehaved; see [Sub-agents](sub-agents.md) | + +The split works through an attribution tag: every line carries an `agent` field: `main` for the main agent, `sub::` for lines emitted inside a sub-agent's run (including its actions and LLM calls). `main.log` and the per-sub-agent files are filtered views of the same stream. `all.log` keeps the cross-agent ordering that the filtered files lose. + +## Reading a line + +``` +2026-07-17 02:17:32.811 | INFO | main | app.scheduler.manager:initialize:83 - [SCHEDULER] Initialized with 5 schedule(s) +^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +timestamp level agent module:function:line message +``` + +- **Level:** `DEBUG` < `INFO` < `WARNING` < `ERROR`. The file threshold is INFO, and the harness narrates generously at INFO, so most context is captured by default. +- **Agent:** `main` or `sub::` (the per-sub-agent files omit this column because the filename already says it). +- **module:function:line** points at the exact source location. Open the module and jump to the line for full context. +- Errors include full tracebacks (`backtrace` and `diagnose` are enabled). + +Note that the loguru sinks are file-only. The console is not a log sink, so tail the files rather than watching the terminal. + +## Subsystem tags + +Most subsystems prefix their messages with a bracketed tag, which makes grep the natural interface: + +| Tag | Covers | +|---|---| +| `[REACT]` | The agent loop: each trigger consumed, each reaction; `[REACT ERROR]` for caught loop-level exceptions | +| `[ACTION]` | Action preparation and execution | +| `[TASK]` | Task lifecycle: create, update, end | +| `[MEMORY]` | Memory indexing, processing, retrieval. See [Memory](memory.md) | +| `[MCP]` | MCP server init, connection, tool calls | +| `[SCHEDULER]` | Schedule loops: sleep-until times, wakes, fires. See [Scheduling](scheduling.md) | +| `[PROACTIVE]` | Proactive heartbeat and planners. See [Proactive mode](../modes/proactive.md) | +| `[LIMIT]` | Action/token budget warnings and the continue/abort gate | + +## Grep recipes + +Find the newest run first, since it's the one you almost always want: + +```bash +cd logs && ls -t | head -2 # newest run folders +``` + +**Why did a task fail?** Errors first, then rewind for the story leading up to them: + +```bash +grep -n "ERROR" logs//all.log | tail -20 +grep -n "\[REACT ERROR\]\|\[TASK\]" logs//all.log +``` + +Then open `all.log` at the line numbers you found and read upward. The `[ACTION]` and `[REACT]` lines just before an error usually name the exact action and input that broke. + +**Follow one action end to end.** Every action is logged by name at preparation and execution: + +```bash +grep -n "web_fetch" logs//all.log # one action's full trail +grep -n "\[ACTION\]" logs//all.log | tail # recent action activity +``` + +**Watch the scheduler live.** This shows whether a schedule fired and when it fires next: + +```bash +tail -f logs//all.log | grep "\[SCHEDULER\]" +``` + +You'll see each loop's `sleeping until