macchiatoBot — LLM schedule agent (tool-driven); includes remote worker CLI
Project description
macchiatoBot
English | 中文
macchiatoBot is an LLM assistant built around a tool-driven, kernel-style runtime. The project is designed for long-running use: interactive chat, scheduled automation, multi-session concurrency, and frontend integrations all go through the same execution pipeline.
The current codebase separates reasoning from execution:
AgentCorehandles prompt building, LLM calls, memory recall, and the tool-calling loop.AgentKernelexecutes tools, enforces tool visibility and permissions, and handles context compression.KernelSchedulerand the automation layer own session lifecycle, IPC, queueing, and background jobs.
What It Supports
- Interactive CLI backed by a long-running daemon
- Feishu WebSocket gateway with shared sessions and slash commands
- Local MCP stdio server
- Scheduled jobs and notification-oriented automation
- Tool registry with file, bash, memory, web, multimodal, Canvas, Shuiyuan, and SJTU helpers
- Multi-provider LLM routing with runtime model switching
- Working memory, chat history retrieval, and long-term/content memory
- Subagent and multi-agent tooling
Architecture
Frontend / External Trigger
├─ CLI
├─ Feishu gateway
├─ MCP stdio server
└─ Automation jobs
│
▼
Automation IPC / Core Gateway / Task Queue
│
▼
KernelScheduler / CorePool / SessionSummarizer
│
▼
AgentKernel
├─ tool execution
├─ permission checks
├─ path resolution
└─ context compression
│
▼
AgentCore
├─ prompt assembly
├─ LLM provider routing
├─ working set + tool selection
├─ memory recall / persistence
└─ multi-turn reasoning loop
Layer Map
| Layer | Main modules | Responsibility |
|---|---|---|
| Frontend | main.py, feishu_ws_gateway.py, mcp_server.py, src/frontend/* |
User entrypoints and channel-specific adapters |
| Automation | automation_daemon.py, src/system/automation/* |
IPC server/client, scheduled jobs, queue consumption, session rotation |
| Kernel | src/system/kernel/* |
Core pooling, scheduling, terminal shell, output routing, summarization |
| Agent runtime | src/agent_core/agent/*, src/agent_core/llm/*, src/agent_core/context/* |
Prompting, LLM loop, checkpoints, multimodal staging, session state |
| Tooling | src/system/tools/*, src/agent_core/tools/*, src/agent_core/mcp/* |
Tool registry, permissions, MCP proxying, runtime tools |
| Integrations | src/frontend/feishu/*, src/frontend/shuiyuan_integration/*, src/frontend/canvas_integration/* |
External platform integration and connector logic |
Repository Layout
src/
├── agent_core/
│ ├── agent/ # AgentCore, checkpoints, prompt builder, workspace/memory paths
│ ├── llm/ # Provider resolution and OpenAI-compatible adapters
│ ├── memory/ # Working memory, long-term memory, chat history DB
│ ├── tools/ # Core tools such as bash / ask_user / permission flow
│ ├── mcp/ # MCP client, pool, and proxy tools
│ └── prompts/ # System prompts and skills
├── system/
│ ├── automation/ # Daemon runtime, queue, IPC, connectors, config sync
│ ├── kernel/ # AgentKernel, scheduler, core pool, terminal
│ ├── tools/ # App-level tools: memory, web, canvas, shuiyuan, planner
│ └── multi_agent/ # Multi-agent registry and constants
└── frontend/
├── cli/ # Interactive CLI loop
├── feishu/ # Feishu gateway, cards, callbacks, routing
├── mcp_server/ # Local MCP stdio server
├── canvas_integration/
└── shuiyuan_integration/
Quick Start
1. Install dependencies
uv sync --all-groups
Optional helper:
source init.sh
init.sh is a convenience script. It runs uv sync, exports PYTHONPATH, and loads .env into the current shell. You do not need to source it before every command if your environment is already set up.
2. Prepare config
cp config/config.example.yaml config/config.yaml
cp .env.example .env
Then fill provider keys in .env, for example OPENAI_API_KEY, DASHSCOPE_API_KEY, GEMINI_API_KEY, DEEPSEEK_API_KEY, or KIMI_CODE_API_KEY.
3. Start the daemon
uv run automation_daemon.py
The daemon is the shared runtime for:
- CLI requests
- Feishu requests
- scheduled automation jobs
- session expiration and rotation
4. Start a frontend
uv run main.py
uv run feishu_ws_gateway.py
For a single command:
uv run main.py "schedule a meeting tomorrow at 3pm"
Optional session identity override:
SCHEDULE_USER_ID=root SCHEDULE_SOURCE=cli uv run main.py
Runtime Model
Daemon-first workflow
main.py is a thin IPC client. It does not run the full agent locally; it connects to automation_daemon.py over a Unix socket. If the daemon is not running, CLI exits with an error.
What the daemon does
- loads config and tool registry
- syncs
automation.jobsfromconfig/config.yaml - runs queue consumers and job scheduling
- hosts the IPC server used by CLI and other frontends
- centralizes session expiration, rotation, and summarization
Common Commands
CLI and Feishu share the same slash command surface through IPC:
/help/model/model list/model <name>/session/session whoami/session list/session new [id]/session switch <id>/session delete <id>/remote-use <login> [path]/remote-status/remote-releaseor/cloud-use
Example:
/session
/session new cli:work
/session list
/session switch cli:root
Remote Workspaces
Remote workspaces are the planned path for letting a cloud-hosted macchiatoBot
session operate on a user-authorized folder on another machine, such as your
laptop. The local machine runs a lightweight macchiato-remote worker; the
cloud daemon keeps the LLM, memory, Feishu, scheduler, and permission flow.
Deployment model:
- macchiato-bot — full agent (cloud server, or a Mac that runs the bot locally).
- macchiato-remote — lightweight worker only (cluster nodes, or install without the full stack).
- Both share the same remote protocol; a machine with the full bot can also run
macchiato-remote startto expose folders to the cloud. - The automation daemon exposes a WebSocket gateway (default
0.0.0.0:9380, overridable viaMACCHIATO_REMOTE_HOST/MACCHIATO_REMOTE_PORT; setMACCHIATO_REMOTE_TOKENSfor per-login worker tokens, orMACCHIATO_REMOTE_TOKENfor a shared fallback). - The same port now also serves a lightweight remote login panel:
http://<host>:<port>/remote/login(health:/remote/healthz). /remote-use,/remote-status, and/remote-releaseopen or release a remote workspace over IPC / Feishu; when active,bash,read_file,write_file, andmodify_fileare routed to the connected worker.- When remote mode is active, the system prompt gets a remote workspace note at the very end, so the agent knows the current workspace backend changed.
Install
| Where | Package | Command |
|---|---|---|
| Cloud / dev (full agent) | macchiato-bot |
uv sync in repo, or uv tool install macchiato-bot |
| Mac (full bot + optional worker) | macchiato-bot |
same; includes macchiato-remote CLI and websockets |
| Laptop / cluster (worker only) | macchiato-remote |
uv tool install macchiato-remote or pipx install macchiato-remote |
Worker-only install (recommended; published on PyPI):
uv tool install macchiato-remote
macchiato-remote --version
See https://pypi.org/project/macchiato-remote/
After uv tool install macchiato-bot you get macchiato (CLI), macchiato-daemon, and macchiato-remote.
From a git tag:
uv tool install "macchiato-remote @ git+https://github.com/<org>/macchiatoBot.git@v0.2.0#subdirectory=packages/macchiato-remote"
From a release wheel (see GitHub Releases on tag push):
uv tool install https://github.com/<org>/macchiatoBot/releases/download/v0.2.0/macchiato_remote-0.2.0-py3-none-any.whl
Helper script: deploy/install-macchiato-remote.sh (INSTALLER=uv|pipx|pip).
Develop from this repository:
cd /path/to/macchiatoBot
uv sync
uv run macchiato-remote status
# or install editable worker package:
uv tool install -e packages/macchiato-remote
The worker does not need config/config.yaml, .env, Feishu settings, LLM keys, or the automation daemon on that machine.
Configure the Local Login
Choose a login alias. This is the value used by /remote-use; it is intentionally
not hard-coded to a device name.
On the cloud server, generate a token for this worker from the repository root; you do not need to invent one by hand:
uv run macchiato-remote gen-token --login personal
# optional: uv run macchiato-remote gen-token --login personal --bytes 48
The command registers a sha256 digest for that login in the server-side
data/automation/remote_worker_tokens.json registry. The plaintext token is
shown only as the first output line; pass that value to the local worker's
login --token. For multiple machines, run the command again with a different
login.
MACCHIATO_REMOTE_TOKENS='personal=<token1>,work-mbp=<token2>' and
MACCHIATO_REMOTE_TOKEN=<token> still work as environment override/fallbacks,
but systemd deploys do not need token environment variables in the common case.
Point --server at your daemon host including the WebSocket port (default
9380, e.g. http://203.0.113.10:9380 or behind TLS).
macchiato-remote login \
--server http://your-macchiato-server.example.com:9380 \
--login personal \
--token '<paste gen-token output>'
You can also use positional server syntax:
macchiato-remote login your-macchiato-server.example.com:9380 --login personal
If --token is omitted, the CLI starts a device-login flow.
Preferred mode (bootstrap exchange):
- Set
MACCHIATO_REMOTE_LOGIN_BOOTSTRAP_TOKENon server - Run
macchiato-remote login <server> --login <alias> --auth-token '<bootstrap-token>' - Server verifies bootstrap token and immediately issues a worker token
- CLI stores the issued worker token in local config
Fallback mode (manual approval panel):
- CLI requests a one-time code from
/remote/login/start - You open
/remote/loginand approve with serverMACCHIATO_REMOTE_LOGIN_APPROVER_SECRET - CLI polls
/remote/login/poll, receives a short-lived onboarding token, and saves it to local config
Feishu approval mode (recommended for no-token UX):
- Configure server Feishu bot and set
feishu.automation_activity_chat_id - Configure approver allowlist:
MACCHIATO_REMOTE_LOGIN_APPROVER_OPEN_IDSMACCHIATO_REMOTE_LOGIN_APPROVER_USER_IDS
- Client runs only:
macchiato-remote login <server-ip>:9380 --login <alias>
- Server sends an interactive Feishu approval card; approver clicks Approve/Reject
- CLI keeps polling and auto-saves token after approval
Check local configuration:
macchiato-remote status
Start the worker in the foreground while debugging:
macchiato-remote start
For daily use, run it as a lightweight background process (pid + log file, no launchd/systemd required):
macchiato-remote start --background
macchiato-remote status
macchiato-remote stop
Background logs are written to ~/.local/state/macchiato/remote-worker.log; the
pid file is ~/.local/state/macchiato/remote-worker.pid.
If public WebSocket access is unreliable in your network, save an SSH tunnel
configuration once and let the worker manage the tunnel automatically (no daily
manual ssh -L):
macchiato-remote login \
--server http://203.0.113.10:9380 \
--login macbook \
--token '<same token as daemon>' \
--ssh-tunnel ubuntu@203.0.113.10
macchiato-remote start --background
With --ssh-tunnel configured, start, start --background, and probe open
127.0.0.1:19380 -> SSH_HOST:127.0.0.1:9380 automatically, and the worker
connects through that local tunnel. Tune it with --ssh-local-port,
--ssh-remote-host, and --ssh-remote-port; remove it with --clear-ssh-tunnel.
If you see InvalidMessage('did not receive a valid HTTP response'), run:
macchiato-remote probe
This sends the same WebSocket upgrade over a stdlib blocking socket (no
asyncio, no HTTP_PROXY). If the first line is HTTP/1.1 101 Switching Protocols,
the path to the server is fine and the failure is specific to the websockets
stack or your environment. If probe shows garbage/HTML or hangs, check Clash
TUN / VPN (turning off TUN mode or bypassing the server IP often fixes it;
empty http_proxy in the shell is not enough when TUN captures all IP traffic).
The start command also prints the underlying __cause__ of handshake errors
after upgrading the package.
If Clash is off but you only see TimeoutError (and probe also times out) while
nc -zv used to work, suspect stale routes or utun leftovers after VPN/TUN — reboot
the Mac, try another network (e.g. phone hotspot), and re-check nc -zv plus the cloud
security group for TCP 9380.
If nc works only while Clash is on and fails after quitting, the TUN exit likely
did not restore the default gateway. Reboot the Mac or use Clash’s normal quit path
that restores routes; killing the app alone often leaves a broken routing table.
Keeping TUN on (recommended for daily use)
Add a high-priority rule so traffic to your daemon’s public IP uses DIRECT
(still inside TUN, but not steered into a broken proxy chain). Place it before the
catch-all MATCH rule, and replace the IP with yours (example uses RFC 5737 TEST-NET-3):
rules:
- IP-CIDR,203.0.113.10/32,DIRECT,no-resolve
no-resolve makes the rule match by IP without DNS quirks. Reload Clash, then
macchiato-remote probe should show HTTP/1.1 101 Switching Protocols. If handshakes
still flake, try switching the TUN stack (gvisor vs system) in Clash Meta / Verge.
Use From Feishu Or CLI
With the daemon reachable from your machine at the --server URL and the worker
running, switch the current session into remote workspace mode:
/remote-use personal ~/Project
/remote-use personal ~/Project --profile dev --ttl 30m
Useful companion commands:
/remote-status
/remote-release
/cloud-use
Remote mode is session-scoped. It does not globally change every core. While it
is active, the agent is instructed that bash, read_file, write_file, and
modify_file should be treated as operating on the remote workspace, with
/workspace, ~, and relative paths referring to the authorized local folder.
Permission profiles are designed as:
| Profile | Intent |
|---|---|
strict |
Only the explicitly authorized workspace, minimal host exposure |
dev |
Developer-friendly sandbox with project access and common toolchain/cache mounts |
host-user |
Short-lived, user-confirmed access as the local OS user |
host-admin |
Highest-risk mode for explicit, per-command admin actions |
The default profile is dev. Higher-privilege modes should be short-lived and
audited; they are meant for explicit elevation, not as the default workspace.
Configuration
Main config: config/config.yaml. Example: config/config.example.yaml.
Important areas:
| Key | Purpose |
|---|---|
llm.* |
active provider, vision provider, provider fragments, request defaults |
multimodal.* |
multimodal input limits and timeout |
agent.* |
iteration limits, subagent caps, working set size |
tools.* |
core tool exposure and template-based tool sets |
memory.* |
working-memory limits, recall policy, persistent memory |
automation.jobs |
scheduled jobs managed by the daemon |
file_tools.* |
file read/write/modify controls |
command_tools.* |
bash enablement, workspace isolation, writable roots |
canvas.* |
Canvas integration |
shuiyuan.* |
Shuiyuan integration |
sjtu_jw.* |
SJTU course schedule sync |
mcp.* |
external MCP server configuration |
feishu.* |
Feishu app and gateway settings |
Provider fragments live in config/llm/providers.d/*.yaml. The active provider is selected by llm.active, and can be changed at runtime with /model <name>.
MCP
Local MCP entry:
uv run mcp_server.py
External MCP servers can be configured under mcp.servers in config/config.yaml.
Development
uv sync --all-groups
uv run pytest tests/ -v
The repository currently has broad coverage across agent runtime, automation, permissions, multimodal handling, frontend integrations, and tool behavior.
If you want the shell to inherit values from .env, either use source init.sh once for that shell, or load .env with your own workflow.
Additional Docs
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file macchiato_bot-0.1.1-py3-none-any.whl.
File metadata
- Download URL: macchiato_bot-0.1.1-py3-none-any.whl
- Upload date:
- Size: 706.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c9d6226189585a838696bca4266778a037b9674c97994d594e29fe9426917ef
|
|
| MD5 |
a90834d0f1134ed34e98f23ab959144e
|
|
| BLAKE2b-256 |
dc492d3e6be1fec69f1af620f3840718c8e70814838c336b537efbef83af8846
|