# YarGPT > YarGPT runs personal AI agents, called bots, on your own computer. Each bot is a Claude Code CLI session with its own chat, memory, routines and working folder. Bots can work together in group chats, called rooms, and can message each other. There is no cloud service: the server, the SQLite database and the bots all run locally. ## What YarGPT is - A Go server with a web UI. Each time a bot works, the server runs the Claude Code CLI (`claude`) on your machine with your Claude account. YarGPT adds the chats, memory, schedules, approvals, rooms and bot-to-bot messages around it. - It runs on macOS and Linux. It needs Claude Code CLI 2.1.319 or later, signed in, with `claude` on `PATH`. - The web UI is at . All data is in `~/.yargpt`. - On a Mac, an app can run the server in the background from login on, with one menu-bar item, **Exit server**. The server log is then `~/Library/Logs/YarGPT/server.log`. - YarGPT has no login. Use it only on a computer that one person uses (see "Security"). ## Bots and turns - A bot has a name (at most 80 characters) and instructions (at most 8000 characters). It works in its own folder, `~/.yargpt/bots/`. The bot id is the last part of the chat address, `/chat/`. - A new bot starts and introduces itself in its chat. A bot that Builder creates does not (see [Builder](#builder)). - Each piece of work is a turn. A turn starts from a message of yours, a routine, a message from another bot, a room post, a change on a team's board, a compaction, a bot's first start, or a background task that reported. - A bot runs one turn at a time. Other turns wait in its queue. Across all bots, at most 3 turns run at the same time (`-max-parallel`). - A bot's turns share one Claude Code session, which each turn resumes. In each room, a member has a separate session. - The model's plain text is private. You see only what the bot sends with the `send_to_user` tool. When a turn ends without a message, the chat shows the model's final text, marked as a fallback. - A bot can start a long command in the background and keep talking. The chat lists the running tasks, and **Stop** ends them. - When a turn reaches your Claude account's usage limit, YarGPT starts no new turns until the limit resets. Turns that already run continue. - After a restart of the server, turns that were running are marked interrupted, open approval cards expire, and background tasks are gone. ### Messages and files - A message has at most 32000 characters. - A message can carry up to 4 files, 100 MB in total. Images: PNG, JPEG, GIF or WebP, up to 10 MB each. Videos: MP4, WebM or QuickTime, up to 50 MB each. - Bots can send images and videos too. A bot's file must be in its working folder, the media folder or a temp folder such as `/tmp`. - Commands at the start of a message: - `/clear` starts a fresh conversation context. The chat stays visible. - `/compact` shortens the context and shows the token counts before and after. - `/ ` runs a skill now. - You can react to any message, yours or a bot's, in a chat or a room. Hover over the message, click the smiley, and choose 👍, 👎, ❤️, 😂, 👀 or ✅. Each reaction shows under the message with a count. Click it to remove it. Reactions stay after a restart. - Reactions are for you only. A reaction never wakes a bot, never starts a turn, and does not count toward the hop limit. Bots do not see reactions. ## Approvals - A bot reads files and searches the web without asking. Before it runs a command, changes a file or calls a connector tool, it shows an approval card. - **Allow once** lets this one call run. **Always allow ** lets this bot use that tool without asking from now on. **Deny** stops the call. - A card expires after 10 minutes without an answer, or when the bot stops working. - To remove an "always allow" permission, open the bot panel, then **Settings**, then **Always allowed tools**. - In the sidebar, a shield marks a bot that waits for an approval. A question bubble marks a bot that asked you something. ## Builder - When the server starts with no bots, teams or rooms, it creates one bot, Builder, with `role` `builder`. Builder's first message lists starter setups: a personal assistant, a software team, a research desk and a writing studio. Builder starts no turn until you write to it. - Builder asks what you want, proposes a setup in the chat and, when you agree, calls `create_setup`. - `create_setup` shows a card with the whole setup: the new bots with their instructions, and the teams and rooms with their members, lead and working agreement. **Create** creates all of it in one transaction, with the member list that opens each channel. **Cancel** creates nothing. The card has no **Always allow**, and the API refuses `always` for it. - A setup adds at most 8 bots, 3 teams and 3 rooms. A team or room has 1 to 8 members: new bots of the setup or bots you have. A new name must differ, ignoring case, from the name of every bot, team and room. - The card shows the setup as it will be created: names trimmed, each member by its own name, and each team's lead and turn budget. A setup whose texts hold a control or format character (other than a newline or a tab), or two blank lines in a row, is refused, so that the card hides nothing. - The new bots start no turn and do not introduce themselves. - Only Builder has `create_setup`. Builder cannot change or delete your other bots, teams or rooms, and it cannot be a member of a team or room. - After you delete Builder, the server creates it again only when it starts with no bots, teams or rooms. ## Memory - Three scopes: - `agent`: the bot's own memory. - `user`: facts about you, shared with every bot ("Shared with all bots" in the UI). - `team`: facts of a team, shared by its members in the team's turns only. See [Teams](#teams). - Three tiers: - `profile`: always in mind. The bot sees these facts on every turn ("Always in mind" in the UI). - `log`: the journal, with dates. The bot searches it when it needs to. This is the default tier. - `note`: a short-term note that expires after 7 days. - A fact is one sentence of at most 500 characters. - Each fact records who wrote it: you or a bot. A bot treats a standing rule as yours only when you wrote the fact. - Bots save facts on their own. You can see, add and delete facts in the bot panel under **Memory**. - A plain room has no memory of its own. ## Routines - A routine is a prompt and a schedule. A bot creates one when you ask for something that repeats, for example "every weekday at 8:10, brief me on my calendar". - A schedule is a 5-field cron expression in your local time zone, or `@hourly`, `@daily`, `@weekly` or `@every `, for example `@every 2h`. - Two runs must be at least 5 minutes apart. A bot can have at most 50 routines. A routine prompt has at most 8000 characters. - The **Routines** tab shows each routine and its next run. There you can pause it, run it now or delete it. When you change a routine, the bot gets a notice. - Routines start only on a schedule. Routines that start on events are not built yet. ## Skills - A skill is a saved procedure that every bot can use. It is a `SKILL.md` file in `~/.yargpt/skills//`. - The skill id comes from its name: lowercase letters and digits, joined by hyphens, at most 64 characters. A description has at most 1000 characters, and the body at most 64000. - A bot can save a process that you will repeat as a skill. A new skill is available from the bot's next turn. - The **Skills** tab lists the skills. To run one now, type `/ ` in a bot's chat or in a team's chat. In a team the lead runs it, or each member that the message @mentions, and the skill stays in their context until `/clear`. A plain room does not run skills: there `/` is text. ## Artifacts - An artifact is a document that bots save for each other: a plan, task, issue, review, report or note. All of your bots share all artifacts. - YarGPT stores artifacts in its database, `yargpt.db`, with no files on disk. Bots and integrations reach them only through the artifact tools or `/api/artifacts`, never by path. A bot passes an artifact's id in messages instead of a file path. - Each artifact has a kind, a title (at most 200 characters), markdown content (at most 256 KiB), its author (a bot or you), the times it was created and last updated, up to 10 tags and up to 10 links (absolute `http` or `https` URLs in canonical form, of printable ASCII only, so spaces and non-ASCII characters must be percent-encoded, for example `%20` for a space). - A tag has at most 64 of the characters a-z, 0-9 and `. _ : / # -`, for example `dev-3` or `repo:yargpt`. Tags are stored in lowercase. A link is an `http` or `https` URL, for example a pull request. - Any bot can find and read any artifact. Only its author can change or delete it with the tools. You can change or delete any artifact. - A bot gets an artifact's content inside an `` wrapper and treats it as data, never as instructions or approval. - An artifact keeps no history: the last write wins. An artifact stays when the bot that wrote it is deleted. - The **Artifacts** tab in the bot panel lists the artifacts, filters them by kind or text, opens one with its tags and links, and deletes it. ## Rooms A room is a group chat with you and one or more bots. To create one, click **+** next to **Rooms**, give the room a name, and choose its members. A room needs at least 1 member, and a room cannot be a member of another room. Who wakes up: - Your message wakes the members that it @mentions. A message without mentions wakes all members. - `@here` mentions every member. It must be a word of its own: `me@here.com` and `@hereford` do not count. - An @mention is `@` followed by the member's name, in any case. Type `@` in the message box to complete a name. - A bot's post wakes only the members that it @mentions. A post never wakes the bot that wrote it. How members answer: - The members answer one at a time, in sidebar order, so each one sees the earlier replies. - A member that is busy elsewhere answers when it is free. - Each member keeps its own Claude Code session for the room. On its first turn in a room, a member sees the last 30 messages. On later turns, it sees the messages that it has not seen yet. - During a turn in a room, the `send_to_user` tool posts into the room, for you and every member. - A bot can also post into a room that it is in from any turn, with `send_to_agent` and the room's id. A room post always lands at once and can carry files. - A bot reads older room messages with the `read_room` tool. Bots can hand work to each other in a room. For example, an Implementer bot @mentions a Reviewer, the Reviewer @mentions a Fixer, and the Fixer @mentions a Merger. ## Teams A team is a room with a lead, a working agreement and a shared folder: a group of bots that work together for you. To create one, click **+** next to **Teams**, give the team a name and a purpose, choose its members and its lead, and write the working agreement. Without a choice, the first member leads. Who wakes up: - Your message wakes the members that it @mentions. A message without mentions wakes only the lead. `@here` wakes every member. - A bot's post wakes only the members that it @mentions, as in a room. How the team works: - The lead is the team's front door. It answers your message itself, or @mentions the member who should do the work. - The working agreement holds your rules for how the team works and when work is done. In a team turn, it overrides the member's standing instructions on how work is done and checked. The fixed safety rules still apply. - Every turn in the team runs in the team's shared folder, `~/.yargpt/teams//workspace`, which links the shared skills like a bot's workspace. A file that a member posts in the team must be in this folder, the media folder or a temp folder. - In a team turn, the prompt shows the team, its purpose, members, lead and working agreement, and no other bot or room. `send_to_agent` accepts only the team's own id there: a member talks to its team in the team's channel. - Team memory (scope `team`) reaches the members only in the team's turns. You can see and add team facts in the team panel under **Memory**. - When the lead leaves the team, the first member leads. When the chosen lead comes back, it leads again. - A bot can be in several teams. It keeps one Claude Code session for each team, as for each room. - Deleting a team deletes its conversation, its team memory, its goals and its shared folder. The member bots stay. ### Goals and the board A goal is work for the team with several steps. You start one by typing `/goal` and what the team should do in the team's chat, for example `/goal Add CSV export`. The lead can also start one with `update_board` `start_goal`, but only in a turn that a post of yours woke. A team has at most one open goal. - The lead splits the goal into items with `create`. An item has a title (unique in the goal, at most 120 characters), a brief, acceptance criteria, an owner, an optional reviewer who is another member, and the items that it depends on. A goal has at most 30 items that are not dropped. A member who does not lead can add an item without an owner: a proposal that the lead assigns. Only the lead adds items to a delivered goal. - Item states: `todo`, `doing`, `review`, `done`, `blocked` and `dropped`. - A `todo` item with an owner starts when every item that it depends on is `done` or `dropped`. Its owner's work turn then makes it `doing`. Items that are ready run at the same time. - The owner submits the result: the item goes to `review`, or to `done` without a reviewer. The reviewer passes it (`done`) or asks for changes (`doing` again, with the review's note). Every third request for changes blocks the item instead. - The owner of a `doing` item, or the reviewer of an item in review, can block it with a question. The lead answers with `unblock` and a note, and the item goes back to the state that it was blocked in. The lead can also `assign` a `todo` or a `blocked` item, which then starts again, and `drop` an item. - While a bot reviews an item in review in a team, the `approve` tool denies it Edit and Write in that team, before the tools that you always allow, in every turn. Bash can still change files. - Each change of an item runs in one transaction with the turns that it needs: the owner's work, the reviewer's check, or the work of items that waited for it. A change cancels the item's queued turns first. - The lead wakes when it must act: an item is blocked, an item has no owner, or no item is in flight. A change that a lead makes as the lead wakes no lead of its own team. A queued turn of the lead for the goal, or a queued chat turn of the lead, stands for the wake. - When a member leaves the team, or its bot is deleted, its queued turns in the team stop, its items wait for a new owner, and a review that it did not finish is blocked for the lead. - Every board turn shows the board in its message: the goal, its turns, and each item with its state, owner and reviewer. An item without an owner also shows its brief and acceptance criteria, and a done item its result, shortened. A work turn adds the item's brief, acceptance criteria, the results of the items that it builds on and the latest note. A review turn adds the result to check. The lead's turns add how to run the goal. - Each change of an item shows in the team's chat as an event. - In a team turn, the bot does not get the direct messages that wait for it: they wait for its next turn outside the team. `update_board` exists only in team turns. Delivery and acceptance: - When every item is `done` or `dropped`, and at least one is `done`, the lead calls `deliver` with the result. The goal is then `delivered`, and the result goes into the team's chat as the lead's message with `needsReply`. - You click **Accept** (`POST /api/goals/{id}/accept`): the goal is `done`, the chat shows your message "Accepted the goal.", and the lead gets one retro turn about that goal. In it, the lead is asked to save up to 5 lessons in team memory and can suggest a change to the working agreement. Only you change the agreement. The retro turn does not count against the budget. - To ask for changes instead, write in the team's chat. A new item from the lead opens the delivered goal again. Requests between teams: - A lead asks another team for work with `update_board` `request`, with the other team's `team_id`, a `title`, a `brief` and `acceptance`. Only a lead asks, and only for its team's active goal. The team that it asks must have members. - The request is an item of the asking goal whose `teamId` is the other team. It counts toward the goal's 30 items and needs a title that is unique in the goal. Its turns run in the other team and count against the asking goal's budget, and the asking goal cannot be delivered until it is done or dropped. - The other team's lead wakes to triage it, and sees its brief and acceptance criteria: `assign` gives it an owner and a reviewer of that team, and `drop` with a note declines it. The asking lead cannot assign or drop it. - The asking lead makes its items that need the result depend on the request with `depends_on`. They start when the request is done or dropped, and the result shows on the asking board. - The lead of the item's team wakes when the item is blocked or has no owner. The lead of the goal's team wakes when the goal has no work left in flight. So a declined or finished request wakes the asking lead when nothing else runs. - The team that does the work sees the request on its board. A turn of that team for the asking goal shows only the requests, not its own goal. The asking team sees it among its goal's items, with the other team's name. Each change of a request shows in both teams' chats. - In a team turn, the lead's prompt lists the user's other teams, with their purpose. A member talks only to its own team: `send_to_agent` still accepts only the team's own id. - Deleting a team deletes the requests that other teams sent to it, with the goals and the items of the team. Budget and stops: - A goal can use the team's `goalBudget` of turns, default 30: the board turns, that is the lead's turns, the work and the reviews. Each started board turn counts, except the retro turn. Chat turns, @mention turns and the turns that a background task starts do not count. At the budget, the goal pauses with an event, and the team's `attention` is `goal`. The team's `goalBudget` applies to the goals that start after a change. - A goal that leaves `active` (paused, delivered, done or ended) drops its queued turns. A queued turn of a goal that is over or gone is dropped when it would start. - **Add 10 turns** (`POST /api/goals/{id}/extend`) raises the budget, up to 500, and resumes a paused goal: the members whose items wait on them get their turns again, and the lead wakes. - **Stop** in the team's chat pauses the team's active goal, blocks the requests of other teams that wait on one of its members (without a turn of its lead), then stops the turns. - **End goal** closes the goal, drops its queued turns, and stops its running turns in every team. - Before each dispatch, YarGPT blocks an item of an active goal that waits on a member who has no queued or running turn for it and no running process in the team, for example after a failed turn, a turn that ended without a submit, or a restart. The lead then wakes. It also wakes the lead of an active goal that has nothing in flight and no open turn, when the goal's last turn did not end well, unless the lead has a turn in the team. - Board turns run in at most `maxParallel - 1` slots, and in at least 1. A conversation turn in a team runs beside the item work of other members. ## Bots that message each other - A bot sends a direct message to another bot with the `send_to_agent` tool. Each bot's prompt lists the other bots and the rooms it is in, with their ids. - `priority: true` wakes the other bot at once. It gets a turn marked `[agent]`, which tells it that the message comes from a bot, not from you. - Without priority, the message waits until that bot's next turn. - Both chats show the message. - A direct message is text only, 1 to 8000 characters. Files can go only to a room. A bot cannot message itself. - Messages are asynchronous, like texting. A reply, if any, arrives later as its own turn. Bots are told to reply only when they have something to add, so they do not trade acknowledgements. - Hop limit: after 6 bot-to-bot messages in a row, the next message wakes no one. A priority message then waits for the bot's next turn, and a room post wakes no one. The limit covers direct messages and room @mentions. It stops bots from keeping each other busy forever. ## Bot tools Each `claude` process connects to YarGPT's MCP endpoint, `/mcp`, with a bearer token that is valid only for its turn. The bots use these tools. They are not for external clients. - `send_to_user`: send a message to the user. This is the only output the user sees. In a room turn, it posts into the room. - `text`: markdown. It can be empty when files are attached. - `files`: up to 4 paths of images or videos. - `needs_reply`: true when the bot cannot continue without an answer. The app then shows that the bot waits for you. - `send_to_agent`: message another bot, or post into a room that the bot is in. In a team turn, only the team's own id is allowed. - `target_id`: the id of a bot, or of a room. - `message`: at most 8000 characters. - `priority`: true wakes the bot now. False delivers at its next turn. Room posts always land at once. - `files`: room posts only, up to 4. - `read_room`: read the latest messages of a room that the bot is in, oldest first. - `room_id`. - `limit`: 1 to 50, default 20. - `before_seq`: page back from the oldest `#seq` that the bot has. - Reading does not change what the bot's next room turn shows as new. - `recall_memory`: search the bot's memory and the shared user memory. - `query`: facts that share more words rank first. - `scope`: `agent`, `user`, `team` (only in a team turn) or `all` (default). In a team turn, `all` includes the team's facts. - `limit`: 1 to 50, default 20. - `update_memory`: write or forget a fact. - `action`: `write` or `forget`. - `fact`: for forget, the exact recorded text. - `scope`: `agent` (default), `user` or `team` (only in a team turn). - `tier`: `profile`, `log` (default) or `note`. - `replaces`: the exact text of a fact that the new fact replaces. - `update_routine`: manage the bot's own routines. - `action`: `create`, `update`, `pause`, `resume`, `delete`, `run_now` or `list`. - `id`: required except for create and list. - `name`, `prompt`, `schedule`: required for create. - `update_skill`: manage the skills that all bots share. - `action`: `write`, `delete` or `list`. - `name`, `description`, `body`: required for write. - `id`: required for delete. - `find_artifacts`: list the artifacts, newest updated first, without their content. - `kind`: `plan`, `task`, `issue`, `review`, `report` or `note`. - `author`: `me`, `user` or a bot id. - `tag`: one exact tag. - `query`: text in the title or content, ignoring case. - `limit`: 1 to 50, default 20. - `read_artifact`: read one artifact by `id`, with its content inside an `` wrapper. - `update_artifact`: save, change or delete an artifact. - `action`: `write` or `delete`. - `id`: leave it out to create an artifact. Required to change or delete one, which only its author can do. - `kind`, `title`, `content`: required to create. When the bot changes an artifact, a field that it leaves out stays the same. - `tags`, `links`: replace the old tags or links. - `update_profile`: change the bot's own `name` or `instructions`. A field that the bot leaves out stays the same. - `create_setup`: Builder only. Create new bots, teams and rooms after the user confirms them on a card. See [Builder](#builder). - `bots`: each with `name` and `instructions`. - `teams`: each with `name`, `purpose`, `members` (bot names), `lead` (a member's name; the first member when empty), `agreement` and `goal_budget`. - `rooms`: each with `name`, `purpose` and `members`. - `update_board`: work on the team's board, only in a turn in the team. See [Goals and the board](#goals-and-the-board). - `action`: `list`, `start_goal`, `create`, `assign`, `submit`, `block`, `unblock`, `drop`, `pass`, `changes`, `deliver` or `request`. - `item_id`: the item, for every action except `list`, `start_goal`, `create`, `deliver` and `request`. - `team_id`: for `request`, the team to ask. - `text`: the goal, the result, the question, the answer, a note, or for `deliver` the result for the user. - `title`, `brief`, `acceptance`: for `create` and `request`. `depends_on`: for `create`. - `owner_id`, `reviewer_id`: for `create` and `assign`, by the lead only. - `approve`: internal. Claude Code calls it through `--permission-prompt-tool` to show an approval card. ## Global instructions Put instructions for every bot in `~/.yargpt/AGENTS.md`, for example: ```markdown - Never add AI attribution to commits or pull requests. - Ask me before any merge. ``` - A bot's own instructions win over this file. - A change reaches a bot when it next starts a Claude Code process. - Every bot can read the file, so keep tokens out of it. - A file over 32 KiB stops every bot's turns until you shorten it. - Your own Claude Code settings, hooks, `CLAUDE.md` files and MCP servers do not reach the bots. Use `AGENTS.md` and `connectors.json` instead. ## Connectors A connector gives bots the tools of an external MCP server, such as Slack or a wiki. List the servers in `~/.yargpt/connectors.json`: ```json { "servers": { "wiki": { "url": "https://wiki.example/mcp", "bots": [""] }, "search": { "url": "https://search.example/mcp", "headers": {"Authorization": "Bearer "}, "bots": [""] } } } ``` 1. Make the file private: `chmod 600 ~/.yargpt/connectors.json`. The file can hold tokens, so YarGPT does not read it otherwise. 2. Sign in to each server that has no `headers`: click **Connectors** at the bottom of the sidebar, then **Connect**, and sign in on the page that opens. YarGPT uses OAuth 2.1, with dynamic client registration and PKCE. Rules: - A server with `headers` gets them on each call. For a server that needs no sign-in, set `"headers": {}`. - A server name uses only `a-z`, `0-9` and `-`. The name `yargpt` is reserved. A bot calls the server's tools as `mcp____`. - The `url` must use https. Plain http is allowed only for `localhost`, `127.0.0.1` and `::1`. - `bots` lists the bots that load the server. It does not hide the tokens from other bots. - Connector tools go through approval cards. **Always allow** applies to one tool, not to the whole server. - YarGPT reads the file each time a bot starts a Claude Code process, so a change needs no restart. An invalid file fails the bot's turn with an error that names the problem. - Sign-in tokens are in `~/.yargpt/oauth/.json`, with mode 0600. YarGPT refreshes them before they expire. **Disconnect** deletes them. - When a server is not signed in, the bot works without it, and the chat says that the server needs sign-in. - If you change `-addr`, connect again: the sign-in address contains the port. A server without dynamic client registration needs a client that you register yourself. All `oauth` fields are optional: ```json "oauth": {"clientId": "", "clientSecret": "", "scopes": [""], "callbackPort": 3118} ``` With `callbackPort`, the sign-in returns to `http://localhost:/callback`, as in Claude Code's `.mcp.json`. For example, Slack's MCP server works with Slack's public client for Claude Code, after a Slack workspace admin approves it: ```json "slack": { "url": "https://mcp.slack.com/mcp", "oauth": {"clientId": "1601185624273.8899143856786", "callbackPort": 3118}, "bots": [""] } ``` ## Configuration Flags of the `yargpt` server: - `-addr` (default `127.0.0.1:7777`): the listen address. YarGPT refuses an address that is not loopback. - `-data` (default `~/.yargpt`): the data folder. - `-claude` (default `claude`): the Claude Code CLI to run. - `-model` (default: Claude Code's default): the model alias for every bot, for example `sonnet`. - `-max-parallel` (default `3`): the turns that can run at the same time, across all bots. - `-login-env` (default off): take the environment, such as `PATH`, from your login shell. - `-tray` (default off): show the macOS menu-bar icon with **Exit server**. - `-log` (default none): append the log and all other error output to this file. Without it, they go to stderr. To set `claude`, `model` or `maxParallel` without flags, put them in `~/.yargpt/config.json`, for example `{"model": "sonnet", "maxParallel": 4}`. A flag wins over the file. YarGPT reads the file only when it starts. If the file is invalid, YarGPT does not start, and the error names the file. The data folder holds: - `yargpt.db`: bots, chats, memory, routines and artifacts (SQLite). - `bots//`: each bot's working folder. - `skills/`: the skills that all bots share. - `media/`: the images and videos of messages. - `AGENTS.md`, `connectors.json`, `config.json`: your optional settings. - `oauth/`: connector sign-in tokens. - `run/`: the MCP configs of running bots, with their per-turn tokens. ## How a turn runs - The server starts `claude -p` in the bot's working folder, or for a team turn in the team's shared folder, and sends the prompt on stdin as stream-json. - The process runs with `--setting-sources project`, `--strict-mcp-config`, `--settings` that turn off auto memory and commit and PR attribution, `CLAUDE_CODE_DISABLE_BUNDLED_SKILLS=1` and `CLAUDE_CODE_DISABLE_CLAUDE_MDS=1`. - The built-in tools are limited to Task, Bash, Monitor, TaskStop, Read, Edit, Write, Glob, Grep, WebSearch and Skill. - YarGPT writes each process's MCP config to a 0600 file in `~/.yargpt/run`, so no token shows in the process list. It removes the file when the process exits. - Tool calls that need approval reach the `approve` tool through `--permission-prompt-tool`. ## Local HTTP API The web UI uses this API. It is local and not versioned, so it can change between releases. Rules for every request: - The server answers only on a loopback address. A request whose `Host` is not loopback gets 403, to block DNS rebinding. - A request with an `Origin` header from another origin gets 403, to block cross-site request forgery. - Every `POST` under `/api/` must have `Content-Type: application/json`, or it gets 415. - A request body has at most 1 MiB. A message body may also carry its files, up to 100 MB before base64. - There is no authentication beyond these rules. - An error is JSON: `{"error": ""}`. Status 400 means invalid input, 404 not found, and 409 a conflict, for example an approval that is no longer pending. Bots, rooms and teams are the same resource. `kind` is `agent` for a bot and `room` for a room or a team. A team also has `team`. Bots: - `GET /api/bots`: all bots, rooms and teams. Each has `id`, `kind`, `name`, `instructions`, `role` (`builder` for Builder; absent for other bots), `members` (rooms), `team` (teams: `leadId`, `agreement` and `goalBudget`), `attention` (`approval`, `question`, or for a team with a paused goal `goal`), `running`, `tasks`, `createdAt` and `updatedAt`. - `POST /api/bots` with `{"name", "instructions"}`: create a bot. Returns 201 and the bot. - `GET /api/bots/{id}`: one bot or room. - `PATCH /api/bots/{id}` with `{"name"?, "instructions"?}`: change a bot. - `DELETE /api/bots/{id}`: stop the bot's running turn, then delete it. Returns 204. - `PUT /api/bots/order` with `{"ids": [...]}`: set the order of one sidebar section: all bots, all teams or all plain rooms. Room members answer in the order of the bots section. Chats: - `GET /api/bots/{id}/entries?after=`: the entries of a chat that changed after `rev`, in `rev` order, at most 1000 at a time. Start with `after=0`. - `POST /api/bots/{id}/messages` with `{"text", "clientNonce", "media"?}`: send a message to a bot or a room. - `clientNonce` is required, 1 to 100 characters, and unique for each message. - `media` is a list of base64-encoded files. - The response is 202 with `{"turnId", "duplicate": false}`. A repeated `clientNonce` starts no second turn: it returns 200 with the first turn's id and `"duplicate": true`. - `GET /api/media/{name}`: a file of a message. - `POST /api/entries/{entry id}/reactions` with `{"emoji", "on"}`: put a reaction on a `user`, `bot` or `agent` entry (`"on": true`) or take it off (`"on": false`). `emoji` is one of 👍 👎 ❤️ 😂 👀 ✅. Returns 200 and the entry. Setting a reaction that is already on, or removing one that is off, changes nothing. Returns 400 for another emoji or another kind of entry, and 404 for an unknown entry. A reaction wakes no bot. - `POST /api/bots/{id}/interrupt`: stop the running turn. Returns `{"stopped": true|false}`. - `POST /api/bots/{id}/clear`: start a fresh context. Returns 204, or 409 while the bot works or has background tasks. - `POST /api/bots/{id}/compact`: queue a compaction after the current turn. Returns 202 with `{"queued"}`. Entries: - Each entry has `id`, `botId`, `seq` (its place in the chat), `rev`, `kind`, `turnId`, `body` and `createdAt`. - An entry can change after it is created, for example when an approval is answered. Each change gives it a new, higher `rev`, so update entries by `id`. - Kinds: - `user`: your message. `body` has `text` and `media` (file names for `GET /api/media/{name}`). - `bot`: a bot's message. `body` has `text`, `fallback`, `needsReply` and `media`. In a room, it also has `speakerId` and `speakerName`. - `agent`: a direct message between bots. `body` has `direction` (`in` or `out`), `peerId`, `peerName`, `text`, `priority`, and for `in`, `held`. - The bodies of `user`, `bot` and `agent` entries can also have `reactions`, your emoji on the message, in the order 👍 👎 ❤️ 😂 👀 ✅. The key is absent when the message has no reactions. - `approval`: an approval card. `body` has `tool`, `input`, `status` (`pending`, `allowed`, `denied` or `expired`), `decision`, and in a room `bot`. - `event` and `activity`: turn events and the tools that a bot used. Rooms: - `POST /api/rooms` with `{"name", "instructions", "memberIds"}`: create a room. Returns 201 and the room. - `PUT /api/rooms/{id}/members` with `{"memberIds"}`: set the members. - Send a message to a room with `POST /api/bots/{room id}/messages`. Teams: - `POST /api/teams` with `{"name", "instructions", "memberIds", "leadId"?, "agreement"?, "goalBudget"?}`: create a team. `instructions` is its purpose. Without `leadId`, the first member leads. `goalBudget` is 1 to 500 turns, default 30. Returns 201 and the team. - `PATCH /api/teams/{id}` with `{"leadId"?, "agreement"?, "goalBudget"?}`: change a team. The lead must be a member. - Set the members with `PUT /api/rooms/{team id}/members`, and change the name or the purpose with `PATCH /api/bots/{team id}`. - `GET /api/teams/{id}/board`: the open goal, or the latest goal, with its items, and the open requests of other teams: `{"goal", "items", "requests", "teams"}`. `goal` is null when the team has had no goal. Each request is an item with `fromTeamId`. `teams` maps the ids of the other teams on the board to their names. A goal has `id`, `teamId`, `text`, `state` (`active`, `delivered`, `paused`, `done` or `ended`), `budget`, `used` and `createdAt`. An item has `id`, `teamId`, `goalId`, `title`, `brief`, `acceptance`, `state`, `ownerId`, `reviewerId`, `dependsOn`, `result`, `note`, `blockedFrom` (for a blocked item, the state it was blocked in), `reviewRounds`, `createdBy` and `updatedAt`. - `POST /api/teams/{id}/goals` with `{"text"}`: start a goal and wake the lead. Returns 201 and the goal, or 400 when the team has an open goal. - `POST /api/goals/{id}/extend` with `{"turns"}`: add turns, and resume a paused goal. Returns the goal. - `POST /api/goals/{id}/end`: end the goal and stop its turns in every team. Returns 204. - `POST /api/goals/{id}/accept`: accept a delivered goal. Returns the goal, now `done`, or 400 when the goal is not delivered. Memory, routines, approvals and permissions: - `GET /api/bots/{id}/memory`: the facts that the bot sees outside teams, or for a team id its team facts and the user facts. Each has `id`, `scope`, `tier`, `fact`, `learnedBy` (`user` or a bot id) and `createdAt`. - `POST /api/bots/{id}/memory` with `{"scope", "tier", "fact"}`: add a fact as the user. Returns 204. For a team, `scope` is `team` or `user`. - `DELETE /api/memory/{id}`: delete a fact. - `GET /api/bots/{id}/routines`: the bot's routines. Each has `id`, `botId`, `name`, `prompt`, `schedule`, `enabled`, `nextRunAt`, `lastRunAt`, `lastRunBy` and `createdAt`. - `POST /api/bots/{id}/routines` with `{"name", "prompt", "schedule"}`: create a routine. Returns 201. - `PATCH /api/routines/{id}` with `{"name"?, "prompt"?, "schedule"?, "enabled"?}`: change, pause or resume a routine. - `POST /api/routines/{id}/run`: run a routine now. Returns 202 with `{"turnId"}`. - `DELETE /api/routines/{id}`: delete a routine. - `POST /api/approvals/{entry id}` with `{"decision": "once" | "always" | "deny"}`: answer an approval card. Returns 204, or 409 when the card is no longer pending. `always` on a card of `create_setup` returns 400. - `GET /api/bots/{id}/grants`: the tools that the bot may use without asking. - `DELETE /api/bots/{id}/grants/{tool}`: remove one of them. Artifacts: - `GET /api/artifacts?kind=&author=&tag=&q=&limit=`: the artifacts that match every filter that is set, newest updated first, without `content`. Each has `id`, `kind`, `title`, `authorId` (`user` or a bot id), `tags`, `links`, `createdAt` and `updatedAt`. `q` matches text in the title or content, ignoring case. `limit` is 1 to 50, default 20; a larger limit returns at most 50. - `GET /api/artifacts/{id}`: one artifact, with its `content`. - `POST /api/artifacts` with `{"kind", "title", "content", "tags"?, "links"?}`: create an artifact as the user. Returns 201 and the artifact. - `PATCH /api/artifacts/{id}` with any of `{"kind", "title", "content", "tags", "links"}`: change an artifact, whoever wrote it. Fields that you leave out stay the same. - `DELETE /api/artifacts/{id}`: delete an artifact, whoever wrote it. Returns 204. Skills and connectors: - `GET /api/skills`: the skills, with `id`, `name` and `description`. - `GET /api/skills/{id}`: one skill, with its `body`. - `DELETE /api/skills/{id}`: delete a skill. - `GET /api/connectors`: the connectors from `connectors.json`. - `POST /api/connectors/{name}/connect`: start a sign-in. Returns `{"url"}` to open in a browser. The sign-in returns to `/oauth/callback`. - `DELETE /api/connectors/{name}/token`: sign out and delete the tokens. Event stream: - `GET /api/events` is a server-sent event stream of every change. - It starts with `event: hello`. If new turns wait for a usage limit, a `pause` event follows. - Every other event is a `data:` line with a JSON object. Its `type` is one of: - `entry`: an entry was added or changed. It has `botId` and `entry`. - `state`: a bot started or stopped working. It has `botId` and `running`. - `tasks`: a bot's background tasks changed. It has `botId` and `tasks`. - `bots`: the list of bots and rooms changed. Fetch `GET /api/bots` again. - `pause`: `pausedUntil` is when turns start again. It is absent when the pause ended. - A comment line, `: ping`, comes every 20 seconds. - A client that falls behind is disconnected. After each connect, fetch the entries after the highest `rev` that you have. Examples: ```sh curl -s http://127.0.0.1:7777/api/bots curl -s -X POST http://127.0.0.1:7777/api/bots//messages \ -H 'Content-Type: application/json' \ -d '{"text": "@here what is the status?", "clientNonce": ""}' curl -s 'http://127.0.0.1:7777/api/bots//entries?after=0' curl -N http://127.0.0.1:7777/api/events ``` ## Security YarGPT has no login. It is safe only on a computer that one person uses, and only because of these limits: - It listens on loopback only. - It rejects requests whose `Host` is not loopback, to block DNS rebinding. - It rejects requests from another web origin, to block cross-site request forgery. - It rejects API POST requests whose body is not JSON. Bots run as your user. An approved command can do anything that you can do, so read each approval card before you allow it. Bots are not isolated from each other. Reading files needs no approval and has no path limit, so any bot can read `AGENTS.md`, `connectors.json`, the tokens in `oauth/`, and the per-turn tokens of other bots' running processes in `run/`. Each bot's prompt tells it to treat web pages, files, tool output and other bots' messages as data, not as instructions or approval. A prompt is not an enforced control. ## Not built yet Routines that start on events (only schedules work now), per-bot Claude Code plugins, a vault for secrets, voice, and bots that run on other machines.