--- url: https://bishop.agentdeployment.co/index.md description: >- What Bishop is, what you need, and the commands that get an agent answering in Slack, email or Teams. --- # Quick Start Bishop puts an agent where your team already works. Point it at a directory an agent already works in, and the agent becomes something your team can talk to: a Slack thread, an email thread, a Teams conversation, or all of them at once. Bishop handles the connection and the mapping between threads and agent sessions. ```sh npm install -g @agentdeploymentco/bishop cd path/to/agent bishop slack setup bishop ``` Invite the bot to a channel, or DM it directly. ## Set up with a coding agent These docs are published for agents too. * : every page, indexed as markdown * : the whole site in one file To have Claude Code, Codex, or Cursor walk you through setup, run it in your agent's directory and paste: ```text Read https://bishop.agentdeployment.co/llms.txt and the pages it links to. Then help me install Bishop and connect the agent in this directory to Slack, email, or Microsoft Teams. Ask me which interface I want before running any setup command, and set an allow list so only the people I name can reach the agent. ``` ## What you need * Node 24 or newer. * A directory the agent works in, meaning `claude` or `codex` runs there and does something useful. * At least one interface: [Slack](/interfaces/slack), [email](/interfaces/email), or [Microsoft Teams](/interfaces/teams). Each page lists what its setup needs. [Install](/install) has the details, including building from source. ## What setup writes ``` .bishop/config.json configuration, commit this .env credentials, gitignored for you ``` Setup also adds `bishop.db*` to `.gitignore`. `bishop` creates the database on its first run to hold the thread-to-session mapping. ## Next steps * **[How It Works](/how-it-works):** threads, sessions, and what Bishop does with each message. * **[Choosing the Agent](/running/agent):** keep Bishop's files apart from the agent's, or run an agent straight from a git repository. * **[Access](/running/access):** decide who can use the agent before you point anyone at it. * **[Deploying](/deploying/):** run Bishop under systemd or in a container. --- --- url: https://bishop.agentdeployment.co/install.md description: Installing Bishop from npm or from source, and the Node version it needs. --- # Install Bishop is an npm package that needs Node 24 or newer. ```sh npm install -g @agentdeploymentco/bishop ``` That puts `bishop` on your PATH. Bishop treats the directory it runs from as its own home, and as the agent unless [`--agent`](/running/agent) points elsewhere, so run it from the agent's directory. The [container image](/deploying/container) is the other way to run it, and [Linux and systemd](/deploying/linux) covers a host without Docker. ## Requirements * Node 24 or newer. Bishop imports `node:sqlite`. * A directory the agent works in, meaning `claude` or `codex` runs there and does something useful. Bishop manages no model credentials: each harness authenticates however its own CLI does in that directory. * At least one interface, each with its own setup requirements: * **Slack:** the [`slack` CLI](https://docs.slack.dev/tools/slack-cli/), logged in with `slack auth login`, and permission to install an app in your workspace. Without the CLI, you can [set the app up by hand](/interfaces/slack#without-the-slack-cli). * **Email:** a Google Workspace account, a Google Cloud project, and a Workspace user for the agent. * **Microsoft Teams:** the [Teams Developer CLI](https://microsoft.github.io/teams-sdk/), logged in with `teams login`, unless an administrator creates the app for you, plus a way for Teams to reach this host over HTTPS. Reading or sending a file in a Teams channel also needs the [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli). ## From source The published package is minified and carries no source maps. If you work on Bishop, and so can read its repository, build it instead: ```sh git clone https://github.com/Agent-Deployment-Co/bishop.git cd bishop npm install npm run build npm link # optional, puts `bishop` on your PATH ``` Without `npm link`, run it as `node /path/to/bishop/dist/cli.js`. ## Upgrading Install the new version and restart. [Upgrading](/deploying/upgrading) covers database migrations and how to roll one back. --- --- url: https://bishop.agentdeployment.co/how-it-works.md description: The pieces of Bishop, what happens during a turn, and what it keeps on disk. --- # How It Works Bishop is an agent gateway: one process that runs one agent and connects it to the places people already talk. ## The pieces * **Agent.** A directory with an agent in it, run by a harness. Bishop doesn't decide what the agent does or knows. Its own configuration does, like `CLAUDE.md` and `.claude/settings.json`. * **Harness.** The software that runs the agent: Claude Code through the Claude Agent SDK, or Codex through OpenAI's Codex SDK. One Bishop runs one of them. See [Harness and Model](/running/harness). * **Interface.** A place people talk to the agent from: [Slack](/interfaces/slack), [email](/interfaces/email) or [Microsoft Teams](/interfaces/teams). Bishop runs every interface whose credentials are present, in the same process. * **Thread.** One conversation: a Slack thread or DM, an email thread, a Teams chat or channel thread. * **Turn.** One exchange inside a thread. Messages arrive, the agent works, and an answer goes back. * **Session.** The agent's memory of a thread. Bishop records which session belongs to which thread, and hands it back on the next message so the conversation picks up where it left off, including after a restart. * **Schedule.** A prompt that runs on a clock instead of on a message. See [Scheduled Prompts](/running/schedules). ## A turn 1. A message arrives on an interface. Bishop checks the sender against that interface's [allow list](/running/access), and refuses the message if they're not on it. 2. Bishop finds the thread's session, or starts one, and hands the agent the message along with who sent it and where the conversation is. 3. The agent works in its directory, or in the thread's own [worktree](/running/worktrees). Slack and Teams show the work as it happens. Email sends one reply when the turn is over. 4. Files people attached reach the agent as paths on disk, and files the agent sends go back as attachments. See [Files](/running/files). Two threads are two independent conversations, even with the same person. ## What Bishop keeps Everything is in Bishop's own directory, the one you run it from: ``` .bishop/config.json configuration .env credentials bishop.db threads, sessions, schedules .bishop/files/ files people shared, one directory per thread .bishop/agents/ agents cloned from a git URL .bishop/backups/ database copies taken before a migration ``` The harnesses keep their own session transcripts under the home directory of the user Bishop runs as, and a thread can't resume without them. Bishop [collects](/deploying/cleanup) what old threads leave behind. ## What Bishop serves Slack and Gmail need outbound access only: Slack holds a socket open and Gmail polls. Teams POSTs every message to an HTTPS endpoint, so Bishop serves one when Teams credentials are present. Bishop also serves the agent its own tools, like scheduling a prompt and sending a file, on a loopback port with a credential that lasts one turn. Every port binds loopback. Nothing is reachable from off the machine unless you put a tunnel in front of it. --- --- url: https://bishop.agentdeployment.co/interfaces/slack.md description: >- Connecting Bishop to Slack over Socket Mode, how threads work, and the scopes it needs. --- # Slack Bishop connects to Slack over Socket Mode, so it needs outbound access only. ## Setup From the agent's directory: ```sh bishop slack setup ``` It asks for an app name, a display name, and a description, defaulting the name to the directory name. Pass them as flags to skip the questions: ```sh bishop slack setup --name salesforce-guy \ --display-name "Salesforce Guy" \ --description "Answers Salesforce admin questions" ``` `--no-prompt` takes the defaults instead of asking. Bishop assumes it when there's no terminal, so this works in a script. Setup creates the Slack app, installs it, writes the bot and app tokens to `.env`, and records the app in `.bishop/config.json`. Then start Bishop: ```sh bishop ``` Invite the bot to a channel, or DM it directly. **Set `slack.allow` before inviting anyone.** Without it, anyone who can reach the bot can use the agent. See [Access](/running/access). ### Without the Slack CLI If you can't install the CLI, or somebody else owns app creation in your workspace, Bishop can adopt an app you set up by hand. First get the manifest, so the scopes and events are right by construction: ```sh bishop slack manifest > manifest.json ``` Create the app in Slack from that file (**Create New App**, then **From a manifest**), install it to the workspace, then collect two tokens from App Settings: the bot token (`xoxb-`) under OAuth & Permissions, and an app-level token (`xapp-`) with `connections:write` under Basic Information. ```sh bishop slack setup --manual ``` It asks for both tokens, checks them against Slack, and builds the config from what Slack reports. You don't have to type the app ID or name: `auth.test` returns them. One thing it can't check is which events the app subscribes to. If the agent never answers anything, that's the first place to look. ### How setup gets the tokens Socket Mode needs an app-level (`xapp-`) token, and no Slack API will mint one. The `slack` CLI does, and hands it to a project it runs. So setup writes a throwaway Slack project, runs it once to collect both tokens, and stops it. The CLI creates that app as a dev app and appends ` (local)` to its name, so setup pushes the intended manifest over it afterward. That changes no permissions and needs no reinstall. The CLI's login expires after 12 hours. If setup says the app kept its `(local)` suffix, run `slack auth login` again, or rename the app in Slack. ## Using it **DM the bot** and every message reaches the agent. **@-mention it in a channel** to start a thread. Bishop replies in that thread and follows the conversation there. It ignores everything else in the channel, including messages in threads it isn't part of. Each thread is its own agent session with its own history, and picks up where it left off, including after Bishop restarts. **A turn shows its work.** Slack marks the thread as working for as long as the agent is, which is also the stop button. Everything the agent writes lands as its own message as it writes it, and the tools it calls in between share a card that says what it is doing now. When the run of tool calls ends, the card becomes a line saying how many it used. The turn closes with the answer and a line saying how long it took, or how long it ran before you stopped it. [`! verbosity`](/interfaces/commands#verbosity) turns this down. **It doesn't answer every message in a busy thread.** In a thread where more than one other person is talking, the agent can leave a message alone when it wasn't for the agent. Slack still shows it thinking for a moment, the way somebody starts typing and then doesn't send. It always answers a message that @-mentions it, and always answers in a thread where you're the only other person talking. **Write while it's working.** A message sent mid-turn gets an :hourglass_flowing_sand: and waits, rather than starting a second turn in the same conversation. When the turn ends, everything waiting goes to the agent as one prompt, with who wrote each one attached. Edit or delete a waiting message and the agent sees the change. **Share files with it.** Attach a file to a message and the agent gets the path, screenshots included. Tag the agent into a thread that already has files in it and it's told what was shared, then downloads the ones it needs. See [Files](/running/files). **Mentions work both ways.** The agent reads mentions of other people as names, and when it writes `@name` Bishop turns it into a real mention. A name that matches nobody, or more than one account, stays plain text. `@here`, `@channel`, and `@everyone` are never converted, so an agent can't wake a workspace. **Stop a turn.** In a DM, Slack shows a stop button for as long as the turn runs. [`! stop`](/interfaces/commands#stop) does the same in any thread, and anything sent while the turn was running is answered next. **Long answers are split.** Slack caps messages near 4000 characters. Slack also doesn't render tables and handles nested lists badly, so agent output written for a terminal will sometimes look wrong in a thread. ## Other agents Pull two agents into the same thread and they can address each other, and the one you're talking to is told which messages came from another agent rather than a person. * **Neither can start the exchange.** A bot's message is answered only in a thread that already has a session, so a bot can't DM the agent or open a thread with it. * **Apps that post under a name rather than an account**, like an incoming webhook, are never answered. * **An exchange is capped** at three turns in a row by default, after which Bishop says it's holding off and waits for a person. `slack.maxBotTurns` changes the cap, and `0` means the agent never answers another bot. * **A turn another agent drew can't use Bishop's own tools**, so it can't leave a schedule behind. Another app is an [allow list](/running/access) entry like any other. ## Scopes An app installed by an older Bishop can be missing scopes a newer one uses. To grant them, update the app from the manifest `bishop slack manifest` prints and reinstall it from App Settings. * **`files:read` and `files:write`** are what [files](/running/files) need. Bishop warns at startup about each one missing, and until they're granted the agent sees a note in place of each file and a file it sends fails to upload. * **`channels:read`, `groups:read` and `im:write`** are what adding a [schedule](/running/schedules) with a Slack destination needs. `bishop schedules add` names the missing one. --- --- url: https://bishop.agentdeployment.co/interfaces/email.md description: >- Connecting Bishop to a Google Workspace mailbox, and which mail the agent never answers. --- # Email Bishop reads a Google Workspace mailbox and answers mail in the same thread. The agent needs its own Workspace user, say `salesforce-guy@example.com`. That address is what people write to, and its mailbox is what Bishop reads. Don't point this at a person's account. ## Setup Create an OAuth client, once: 1. In the [Google Cloud console](https://console.cloud.google.com/), pick or create a project and **enable the Gmail API**. 2. Configure the OAuth consent screen with user type **Internal**. This is what lets you skip Google's app verification. 3. Create an OAuth client ID of type **Desktop app**. 4. Put both values in `.env`: ```sh BISHOP_GMAIL_CLIENT_ID=... BISHOP_GMAIL_CLIENT_SECRET=... ``` Then authorize the mailbox: ```sh bishop gmail setup ``` A browser opens. Sign in **as the agent's Workspace user**, not as yourself, and approve. Bishop catches the redirect, writes `BISHOP_GMAIL_REFRESH_TOKEN` to `.env`, and tells you which mailbox it reached. Re-running it later checks the credentials instead of authorizing again, so it doubles as a health check in a deploy script. It exits non-zero when the mailbox can't be reached. **Set `gmail.allow` before pointing anyone at it.** Without it, every address that can reach the mailbox can use the agent, and a Workspace mailbox can be reached from anywhere. ```json { "gmail": { "allow": ["you@example.com", "@example.com"] } } ``` ### Moving it to a server The refresh token isn't tied to the machine you authorized on. Copy three lines to the server's `.env` and start Bishop there: ```sh BISHOP_GMAIL_CLIENT_ID=... BISHOP_GMAIL_CLIENT_SECRET=... BISHOP_GMAIL_REFRESH_TOKEN=... ``` To set it up on the server directly instead, run `bishop gmail setup` there. It notices it's over SSH, prints a URL instead of opening one, and asks you to paste back where the browser ended up. The browser lands on a `http://127.0.0.1` address that fails to load, which is expected: the code is in the address bar, and pasting the whole address is enough. `--no-browser` forces that mode anywhere. ### When it stops working Google revokes Gmail refresh tokens when the mailbox's password is reset. The agent goes quiet, the log says the token is no longer valid, and `bishop gmail setup` fixes it by authorizing again. If that isn't acceptable, use domain-wide delegation instead. Nothing about it expires and no person is ever needed again, but it costs more to set up: a service account key, which most organizations now block by default, plus a Workspace super admin to grant the delegation. Set `BISHOP_GMAIL_USER` and `BISHOP_GMAIL_SERVICE_ACCOUNT` instead of the three OAuth variables, and run `bishop gmail setup` to check it. It prints the exact client ID and scope to paste into the admin console when the grant is missing. ## Using it Anyone on the allow list emails the agent, and the reply arrives in the same thread. An email thread is an agent session, exactly as a Slack thread is: reply to the agent's answer and it picks up where it left off. * **Nothing happens until the turn is over.** No streaming, no progress, no sign the agent is working. Then one plain-text reply arrives with everything it had to say. This makes a long turn fine, which it isn't in Slack. * **The reply goes to everyone on the thread.** So Bishop refuses a thread with anyone on it who isn't on the allow list. If you're on the list and copied someone who isn't, it tells you why. If you're not on the list, it sends nothing at all. * **Labels are the only progress you get.** Bishop marks each thread `Bishop/Working` while it runs, then `Bishop/Done`, `Bishop/Failed`, or `Bishop/Refused`. Open the mailbox to see what the agent is doing. * **Attachments come through.** Bishop downloads what was attached and hands the agent the path, the same as in Slack. * **No stop button.** Nothing in email can cancel a turn. ## Mail the agent never answers Bishop never answers automated mail: autoresponders, mailing lists, bounces, its own address, or anything Gmail marked spam. It also refuses mail whose sender Gmail's own verdict doesn't clear, without writing back, since the from address is the one thing known to be untrustworthy there. That check reads DMARC rather than a bare SPF or DKIM pass, and only from the topmost `Authentication-Results` header, because a sender can write that header themselves and only Gmail's own copy means anything. **Bishop only ever writes to an address on the allow list.** Someone not on it gets no reply, not even one explaining why. The thread is labelled `Bishop/Refused` and the reason goes in the log. Without this, pointing the agent at a mailbox that receives ordinary mail means it answers every stranger who writes in. **Bishop never answers mail that arrived before you pointed it at the mailbox.** It records that moment the first time it connects and refuses anything older. Startup logs the cutoff as `answeringMailAfter`. Restarts still catch up on whatever arrived while it was down, because the cutoff is when it first attached, not when the process started. There is no setting that moves it earlier. ## Configuration The `gmail` block takes `allow`, `pollSeconds`, `labelPrefix`, `markRead` and `internalDomains`. [Configuration](/reference/config#gmail) describes each. --- --- url: https://bishop.agentdeployment.co/interfaces/teams.md description: >- Connecting Bishop to Microsoft Teams, including the HTTPS endpoint Teams delivers to. --- # Microsoft Teams Teams needs a Teams app, and the app needs a URL to deliver to. Slack holds a socket open, but Teams POSTs every message to an HTTPS endpoint Bishop serves. Nothing binds a port unless the Teams credentials are set. ## Setup Install the Teams Developer CLI with these flags, then log in: ```sh npm install -g --allow-scripts=@azure/msal-node-extensions,keytar @microsoft/teams.cli teams login ``` The CLI keeps its login in a native module that npm refuses to build by default, and falls back to holding the token in memory rather than reporting it. Installed without the flags it logs in, says it worked, and starts every later command logged out. Bishop says so when it sees that happen. **Run this on a workstation.** Over SSH the CLI has no browser to open and falls back to the device code flow, which Microsoft blocks by default in every tenant, so no login can succeed there. Set up on a laptop and copy the `BISHOP_TEAMS_*` lines to the server, or use the [administrator path](#when-somebody-else-owns-the-app), which needs no CLI at all. Then create the app: ```sh bishop teams setup --endpoint https://your-host.example/api/messages ``` It creates the app, writes the three `BISHOP_TEAMS_*` credentials to `.env`, records the app in `.bishop/config.json`, and prints a link to install the app in Teams. You can leave `--endpoint` off and set it later. Setup also asks the app for these, each as a further write, since `teams app create` takes no flags for them: * `ChannelMessage.Read.Group` and `ChatMessage.Read.Chat`, which let the agent hear a reply that doesn't @-mention it. * `ChannelSettings.Read.Group` and `TeamSettings.Read.Group`, which tell the agent who can read a channel. * `supportsFiles`, which lets anyone attach a file in a chat with it. A Developer Portal that refuses one costs that setting rather than the setup: it says what is missing and what to run. One grant isn't in the Teams manifest, so setup makes it with the [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) instead. `Files.ReadWrite.All` on the app registration lets the agent read a file attached in a channel and send one there. Consenting it takes an administrator, so setup tries and prints the commands when it can't. Install `az` and run `az login --allow-no-subscriptions` before setup if you can make the grant yourself. Nothing else needs `az`, and an agent that only works in direct chats never needs the grant. Run setup again and it reports what's configured, including where the app currently points and whatever is still missing, and changes nothing. If only half of setup is on disk, an app with no credentials or credentials with no app, it says which half and stops rather than create a second app. **Adding or changing permissions on an app that's already installed means removing it and installing it again.** Teams asks for them at install time and at no other time, so an app updated in place keeps the consent it was installed with, whatever its manifest now says. Remove it from every team and chat it's in, then add it back. This catches out anyone upgrading an agent that was already running in Teams. ### The endpoint Bishop serves `/api/messages` on port 3978 by default, on loopback, so something has to carry public HTTPS to it. A tunnel does that without opening a port on the host: ```sh cloudflared tunnel --url http://localhost:3978 ``` That gives you the URL to hand `bishop teams setup --endpoint`. An ephemeral tunnel hostname changes on every restart, and a Teams app pointed at the old one goes quiet with nothing in the log to say why, so use a stable hostname for anything but a trial. To terminate TLS in Bishop itself instead, give it a certificate: ```json { "teams": { "port": 443, "host": "0.0.0.0", "tls": { "cert": "/etc/bishop/fullchain.pem", "key": "/etc/bishop/privkey.pem" } } } ``` When the URL changes: ```sh bishop teams endpoint https://new-host.example/api/messages ``` That touches the app and nothing else. When the change rewrites the app's manifest, which moving to a different domain does, it says so: the app has to be reinstalled in Teams before it receives anything again. ### When somebody else owns the app In many tenants only an administrator can create it, and installing custom apps can be turned off tenant-wide. `bishop teams instructions` prints what to send them. When they send back a client ID, a client secret, and a tenant ID: ```sh bishop teams setup --manual ``` It checks the three against Microsoft before saving them, using the same request Bishop makes on every turn, so a bad secret fails here instead of silently on the first message. ### Installing the app Last, install the app in Teams by opening the link setup printed. When this is blocked the link does nothing at all: no activity is delivered, so Bishop's log stays silent and a working endpoint looks broken. Sideloading has to be allowed in two places, and either one blocks it alone. `bishop teams setup` names whichever is in the way. Both live in the Teams admin center at `admin.teams.microsoft.com`: * "Let users interact with custom apps" under Teams apps, Manage apps, Org-wide app settings. * "Upload custom apps" in the app setup policy assigned to the account, under Teams apps, Setup policies. An administrator can skip the per-user policy by uploading the app package under Teams apps, Manage apps, which publishes it to the whole org and takes a few hours to appear. ## Using it Message the app directly, or @-mention it in a channel. A channel reply starts a thread and the agent follows that thread. A chat has no threads, so the whole chat is one session. * **A turn shows itself while it runs.** What the agent writes lands as it writes it, and a run of tool calls shares one message that is rewritten as the work moves. A turn ends with a line saying what it cost. [`! verbosity`](/interfaces/commands#verbosity) turns this down. There's no typing indicator: Teams hides it while a stream is open, and outside one it lapses in seconds. * **Commands work as they do in Slack.** Type the command in backticks, like `` `! model opus` ``, and Teams formats it as code. The whole message has to be the code. See [Commands](/interfaces/commands). * **Tables don't render.** Bold, italics, links, code and lists work, and the agent is told to write a list where it would otherwise reach for a table. * **@-mention it once per thread.** Teams delivers a channel or group-chat message to an app only when the app is @-mentioned. The resource-specific consent permissions setup asks for lift that, so once the agent has answered in a thread it hears replies without being tagged. Without them it still works, and has to be tagged every time. * **It can stay out of a conversation.** In a thread with several people talking, a message that didn't name the agent is one it may leave alone. A message that names it is always answered, as is anything in a one-to-one chat. * **It catches up when it's pulled in partway.** @-mention it halfway down a channel thread or a group chat and its first answer has read what was said before. Only messages from people on the allow list are quoted to it. This uses the same two permissions, so an app installed without them answers from the mention alone. * **Stop a turn with [`! stop`](/interfaces/commands#stop).** Teams has no stop button for Bishop to draw. ## Files * **In a direct chat**, attach a file and the agent gets the path. An image pasted into a message reaches it in any scope. * **In a channel**, Teams keeps the file off the message it delivers, so Bishop reads it back through Microsoft Graph. That needs `Files.ReadWrite.All`. Without it the agent is told a file was attached and says it couldn't read it. An app that already holds `Files.Read.All` can read channel files but can't send them. * **In a group chat**, an attached file may not come through. That case is untested. Files the agent sends are covered in [Files](/running/files#files-the-agent-sends). ## Access The allow list takes user principal names (`hayes@example.com`), `@example.com` for everyone at a domain, or an Entra object ID: ```json { "teams": { "allow": ["hayes@example.com", "@example.com"] } } ``` Leave it out and anyone in the tenant who can reach the app can use the agent. Somebody not on the list is told so, rather than left with silence that looks like the agent being broken. --- --- url: https://bishop.agentdeployment.co/interfaces/commands.md description: >- Messages to Bishop itself in Slack or Teams, like changing verbosity or the model, or stopping a turn. --- # Commands A command is a message to Bishop rather than to the agent, sent in a Slack or Teams conversation. Bishop runs it and answers in the thread, and the agent is never told about either. Email has no commands. A message is a command when the whole message is inline code opening with `! `, like `` `! verbosity low` ``. What follows the `!` has to look like a command name, so code and pasted output reach the agent as they always did. A command inside a sentence goes to the agent. `` `! help` `` lists the commands, and any of them takes `--help`. ## verbosity How much of a running turn the conversation shows. | Level | Shows | |---|---| | `high` | The agent's commentary, a card for each run of tool calls, and a closing line. The default. | | `medium` | The same without the tool calls. | | `low` | The answer alone. In Teams, also without the closing line saying what a turn cost. | `` `! verbosity` `` shows where the conversation stands, and `` `! verbosity default` `` follows the configured level again. It applies to the turn that's running, so you can quiet one mid-flight. `slack.verbosity` and `teams.verbosity` in the [configuration](/reference/config) set where every conversation starts. Slack always marks a thread as working, whatever the level. ## model Which model this conversation runs, and how hard it thinks. ``` ! model opus the model ! model terra high the model and the effort ! model --effort high the effort, leaving the model alone ! model where the conversation stands ! model default default follow the configured model and effort again ``` Names go to the harness as you type them, in its own naming, so `opus` or `gpt-5.1-codex-max` means whatever it means there. Bishop only checks that it looks like a model name. A name the harness doesn't know does what a bad `agent.model` does: Codex fails the turn, and Claude says so in prose about a model and silently ignores an effort it doesn't recognize. `` `! model` `` is how you see what a conversation runs with, which is the only sign of a typo in the silent case. It takes effect on the next turn, not the one running, and lasts for the life of the conversation. ## stop Stops the turn running in this conversation. The turn closes with a line saying how long it ran, and anything sent while it was running is answered next. --- --- url: https://bishop.agentdeployment.co/running/agent.md description: >- Pointing Bishop at an agent in a directory or a git repository, and picking up a push without restarting. --- # Choosing the Agent By default Bishop treats the current directory as both its own home and the agent, so its config, credentials and database land beside the agent. `--agent` separates the two, and takes a directory or a git URL. ## A directory ```sh bishop --agent /srv/agents/salesforce-guy ``` Bishop's config, credentials, and database stay in the directory you run from. The agent directory is only read, by the agent itself, so its `CLAUDE.md` and `.claude/settings.json` govern as always and nothing of Bishop's is written into it. **Bishop never updates a directory you point it at**, whether or not it's a git checkout. Pull, branch, and edit it whenever you like. The exceptions are [worktree mode](/running/worktrees), where the agent creates worktrees under `.claude/worktrees/` in its own directory, and [cleanup](/deploying/cleanup), which removes an old thread's worktree. This is how you swap the agent underneath a running deployment. Point Bishop at a checkout, a worktree, or a release directory, and change which one it points at without touching any interface configuration or losing thread history. The [sandbox](/running/sandbox) needs this split: Bishop's own directory can't be the agent directory with the sandbox on. ## A git repository `--agent` also takes a git URL, which Bishop clones on startup: ```sh bishop --agent https://github.com/acme/salesforce-guy bishop --agent https://github.com/acme/salesforce-guy/tree/staging bishop --agent git@github.com:acme/salesforce-guy.git ``` GitLab's `/-/tree/` form works too, as does any URL git can clone. A bare `owner/repo` is treated as a local directory, since nothing distinguishes it from a relative path. Bishop uses whatever git credentials the machine already has, so a private repo works if `git clone` works there. Point at a repository and Bishop owns the checkout, under `.bishop/agents/-`: ``` .bishop/agents/acme-salesforce-guy/ ├── mirror.git/ bare clone, the only thing a fetch touches └── snapshots/ ├── current -> b4c5d6e what the next new thread starts on ├── a1b2c3d/ an older commit, still in use by a thread └── b4c5d6e/ ``` A snapshot is one commit, checked out and never rewritten. **A thread picks its snapshot on its first message and stays there for good**, so a conversation always sees one consistent set of files. Threads on the same commit share a snapshot. `current` is for you, not for Bishop. Nothing reads it while a thread runs. It records what the next new thread would start on. A fresh snapshot has no git identity of its own, so set one for the user Bishop runs as, or every commit the agent makes fails with "Author identity unknown". `git config --global` works on a host, and [the container](/deploying/container) sets it through the environment. ## Picking up a push without restarting `--auto-update` checks the remote at the start of every new thread: ```sh bishop --agent https://github.com/acme/salesforce-guy --auto-update ``` A new thread gets whatever is on the branch now. Threads already going keep their own snapshot, so nobody's conversation changes underneath them. Without the flag, Bishop checks once at startup and every thread that process handles starts on that commit. Threads waking together share one fetch. If the fetch fails, new threads start on the snapshot Bishop already has and the reason is logged, so an expired credential doesn't take the agent offline. It doesn't install anything. A commit that changes `package.json` gets a snapshot with no `node_modules`, and what to do about that is the agent's business. It also doesn't touch Bishop's own config or credentials, which are read from the directory you ran it from and still need a restart to change. ## Commands that don't take an agent `bishop slack setup` and `bishop slack manifest` don't take `--agent`. They only deal with Bishop's own directory and the Slack app. --- --- url: https://bishop.agentdeployment.co/running/harness.md description: >- Choosing between the Claude and Codex harnesses, model credentials, and model and effort settings. --- # Harness and Model The harness is what runs the agent. `claude` is the default and runs the Claude Agent SDK. `codex` runs OpenAI's Codex SDK. Both come with Bishop, so there's no separate agent CLI to install. ```json { "agent": { "harness": "codex" } } ``` `--harness` overrides it for one run, and an unsupported name fails at startup naming the ones that exist: ``` $ bishop --harness codex $ bishop --harness gemini unknown harness "gemini"; supported: claude, codex ``` ## Model credentials Bishop manages no model credentials. Each harness authenticates however its own CLI does in that directory, so make `claude` or `codex` work there first. * **Claude** uses `ANTHROPIC_API_KEY` or `CLAUDE_CODE_OAUTH_TOKEN` if one is set, and otherwise whatever login `claude` has on that machine. Bishop logs which it used at startup. * **Codex** uses `codex login`, or `CODEX_API_KEY` in the environment or `.env`. Bishop warns at startup when it can find neither, since the first turn would otherwise fail on authentication with nothing to explain it. `OPENAI_API_KEY` isn't read, because `codex` doesn't read it either. With only `OPENAI_API_KEY` set and no login, `codex` sends no credential at all and the request comes back `401 Missing bearer`. Picking it up would also override a working `codex login` and move billing to the API key without saying so. ## Switching harness A session belongs to the harness that started it, so switching leaves running threads unable to resume. Bishop notices, logs it, and starts each thread a fresh session on its next message rather than failing the turn. Nothing else is lost. Codex threads read differently in one way that shows: their tool cards are vaguer, because a command comes through as a raw shell line and nothing else, which Bishop won't put in a thread. ## Model and effort Every harness has a model and a reasoning level, so both sit at the top of `agent` rather than inside `agent.options`: ```json { "agent": { "harness": "codex", "model": "gpt-5.6-codex", "effort": "high" } } ``` Both are free strings, passed to the harness as written. The names belong to the harness and change with it, so Bishop doesn't check them: `claude` takes `claude-sonnet-5` and `low` through `max`, `codex` takes `gpt-5.6-codex` and `modelReasoningEffort`'s levels. Nothing checks the value before the first turn. A name the harness doesn't know surfaces in a thread instead, differently by harness. Codex fails the turn with the API's own complaint about the field. Claude answers a bad model with the agent saying in prose that the model is wrong, and ignores an effort level it doesn't recognize. So an effort typo under Claude is silent: check the log line at startup, which names the model and effort Bishop passed. `--model` and `--effort` override the config for one run: ```sh bishop --harness claude --model claude-opus-5 --effort high ``` Setting the same thing again in `agent.options` loses to these, and startup logs a warning naming the key it overrode. [`! model`](/interfaces/commands#model) changes them for one conversation. ## Agent options `agent.options` is passed to the harness unvalidated. Under Claude it's the Agent SDK's own options object, not a settings file: anything from [Claude Code's settings reference](https://code.claude.com/docs/en/settings-reference) goes in the nested `settings` object, which is shaped like a `.claude/settings.json`. A settings key written at the top level is dropped by the SDK without a word, so put it a level down: ```json { "agent": { "options": { "maxTurns": 30, "settings": { "permissions": { "allow": ["Bash(python3:*)"] } } } } } ``` These layer over the agent's own settings files rather than replacing them, so a project that sets `model` in `.claude/settings.json` keeps it unless `agent.options` names `model` too. Bishop sets `cwd`, `resume`, `systemPrompt`, `abortController`, `sessionId`, `forkSession`, and `canUseTool` itself and ignores them here, logging a warning for each. They decide which agent runs, which conversation it continues, and how Bishop tells the agent who it's talking to. Bishop sets two defaults, both overridable: * **`permissionMode: "auto"`.** Nothing in a conversation can answer a permission prompt, and an agent needing approval for a tool reports itself blocked instead of doing the work. Override it if you want something stricter. * **`settings.autoMemoryEnabled: false`.** Auto memory writes to `~/.claude/projects//memory`, outside the agent's own repository, so it doesn't travel when you deploy the agent somewhere else, and a git-URL agent's directory changes with every commit. Turn it back on with `"settings": { "autoMemoryEnabled": true }`. Bishop warns if you put it at the top of `agent.options`, where the harness ignores it. `hooks` and `settings` merge instead of being replaced. Your hooks run alongside the one [worktree mode](/running/worktrees) installs, and your `settings` object keeps Bishop's defaults for the keys it doesn't name. Pointing `settings` at a settings file instead opts out of both defaults, which Bishop warns about. ### Under Codex `agent.options` takes Codex's own fields instead: ```json { "agent": { "harness": "codex", "options": { "sandboxMode": "danger-full-access", "additionalDirectories": ["/srv/data"] } } } ``` Bishop's defaults are `approvalPolicy: "never"`, `sandboxMode: "workspace-write"`, `networkAccessEnabled: true`, and `skipGitRepoCheck: true`. Nothing in a conversation can answer an approval prompt, so an agent that asks for one stalls the turn. The sandbox is the compensation: writes stay inside the working directory, and network access stays on because an agent that can't push, install, or fetch fails at something in most threads. Override any of them here. Bishop sets `workingDirectory` and `developer_instructions` itself and ignores them here. `config` and `configOverrides` reach Codex's own `--config` mechanism, which is how anything in `config.toml` can be set per agent. A `config` object you set is merged with Bishop's rather than replacing it. ## One config for both harnesses `agent.options` is written in one harness's vocabulary, so a config meant to run either one keeps a block per harness: ```json { "agent": { "harness": "claude", "worktrees": true, "effort": "high", "harnesses": { "claude": { "options": { "maxTurns": 30 } }, "codex": { "model": "gpt-5.6-codex", "options": { "sandboxMode": "danger-full-access" } } } } } ``` `bishop` runs Claude with its block, and `bishop --harness codex` runs Codex with its own model and sandbox setting. A block takes `model`, `effort`, and `options`. `model` and `effort` layer over the ones beside `harness`, so `effort` above applies to both harnesses and Codex adds a model. `options` doesn't layer: it replaces `agent.options` outright, since merging two objects written in different vocabularies would splice one harness's keys into the other's call. `agent.options` is then the options for whichever harness has no block of its own. Set both for the same harness and startup warns that the block wins. Switch to a harness with no block and startup warns that it's being handed options meant for the other one. A harness name Bishop doesn't run fails at startup, so a misspelled block can't quietly do nothing. --- --- url: https://bishop.agentdeployment.co/running/access.md description: Each interface's allow list, which decides who can reach the agent. --- # Access Each interface has its own `allow` list, and a message from anyone not on it never reaches the agent. **Leave an interface's list out and anyone who can reach the agent there can use it.** Set one before pointing anyone at the agent. Every message is checked, not just the one that starts a thread, so someone who isn't allowed can't join a conversation another person started. Scheduled prompts are re-checked at every firing too, so removing someone stops what they scheduled. ## Slack ```json { "slack": { "allow": ["hayes.davis@example.com", "revops", "@sales-managers"] } } ``` `slack.allow` takes usernames, emails, and group names in one list, and Bishop works out which is which. A leading `@` is optional. Groups expand to their members, and Bishop refreshes the list every few minutes so a membership change lands without a restart. Someone not on it gets a reply only they can see. Bishop warns at startup when the list is missing. An entry matching two different things, like a name that's both a person and a group, stops startup and says which candidates it found. An entry matching nothing logs a warning and Bishop keeps running, so a departed colleague's email doesn't take the agent down. Another app is an entry like any other: list it by name and the agent will answer it, subject to the [limits on a bot-only exchange](/interfaces/slack#other-agents). Without a list, any app posting in a thread someone started can draw a turn, and what it posts is text nobody in the thread wrote. `slack.maxBotTurns: 0` keeps every app out. ## Email ```json { "gmail": { "allow": ["you@example.com", "@example.com"] } } ``` `gmail.allow` takes addresses and `@domain` entries, and an entry that is neither stops startup. A turn runs only when the sender **and** everyone else on the thread is allowed, because the reply goes to all of them. Someone not on it gets no reply at all. See [Email](/interfaces/email#mail-the-agent-never-answers). ## Microsoft Teams ```json { "teams": { "allow": ["hayes@example.com", "@example.com"] } } ``` `teams.allow` takes user principal names, `@domain` entries, or Entra object IDs. Without it, anyone in the tenant who can reach the app can use the agent. Somebody not on the list is told so. ## What an allowed person can do An allowed person can direct the agent, which has a shell. Keep the list to people you'd trust with that shell, and consider the [sandbox](/running/sandbox) to keep the agent out of Bishop's own credentials and database. --- --- url: https://bishop.agentdeployment.co/running/github.md description: >- Giving the agent its own GitHub credential, limiting what it can reach, and pushing with git. --- # GitHub An agent that opens pull requests, reads issues, or pushes branches needs a GitHub credential of its own. Without one it borrows whatever login happens to be on the machine, and in a container there's nothing to borrow. ```sh bishop github setup ``` It takes the token from the [`gh` CLI](https://github.com/cli/cli), offering to run `gh auth login` if you aren't logged in, verifies it against GitHub, and writes `GH_TOKEN` to `.env`. From there it reaches the agent's environment, and `gh` picks it up on its own. `GH_TOKEN` is read at startup, so changing it needs a restart. The report names the account the token acts as and the scopes it carries, and prints the `gh auth refresh` for any scope the agent needs that's missing. Re-running it checks the token instead of issuing another, so it doubles as a health check in a deploy script. It exits non-zero when GitHub won't accept the token. ## Limiting what the agent can reach **A token from `gh auth login` acts as you, in every repository you can reach.** To limit the agent instead, create a [fine-grained personal access token](https://github.com/settings/personal-access-tokens) scoped to the repositories it should touch, put it in `.env` as `GH_TOKEN`, and run setup to check it. GitHub reports no scopes for a fine-grained token, so setup says what it can't tell you rather than guessing. Setup never replaces a `GH_TOKEN` already in `.env`. ## Pushing with git git never reads `GH_TOKEN`, so `git push` still asks for a password until the credential helper is installed. Setup says so when it's missing: ```sh gh auth setup-git ``` SSH keys cover clone and push, but API calls like `gh pr create` still need the token. ## Keeping your own login **`gh auth login` replaces whichever account the `gh` CLI is signed in as.** On a machine where you're signed in as yourself, `--temporary` runs the login in a `gh` configuration directory it creates and deletes, so your own login survives: ```sh bishop github setup --temporary ``` The token still lands in `.env`. Because the temporary directory has no account in it, this always logs in rather than reusing one, and it needs a terminal. A `GH_TOKEN` already in `.env` is checked as usual and no login happens at all. ## Secrets somewhere else For a deployment whose secrets live somewhere other than a `.env` file, `--print` writes the credential to stdout as JSON and nothing to disk, with the report and any prompting on stderr: ``` $ bishop github setup --print {"GH_TOKEN":"gho_..."} ``` `--no-prompt` never offers the login, and fails with the command to run instead. `--temporary` and `--print` combine: log in as the agent, pipe the token into a secret store, leave nothing behind. ## With the sandbox on The [sandbox](/running/sandbox) keeps the agent out of `~/.ssh` and the `gh` CLI's login. `bishop nono setup` writes a profile that grants both, so a sandboxed agent can still push. --- --- url: https://bishop.agentdeployment.co/running/worktrees.md description: >- Giving each thread its own git worktree so threads don't write over each other's files. --- # Worktree Mode Threads share one working directory by default, so two people directing an agent in two threads can have it write the same files. Worktree mode gives each thread its own git worktree: ```json { "agent": { "worktrees": true } } ``` Each thread works in a worktree named `bishop---`, and stays in it for every later message. It lives in `.claude/worktrees/` at the root of the repository the agent is in. That's the agent directory itself, unless `--agent` is a git URL, where it's `.bishop/agents//mirror.git/.claude/worktrees/`. `git worktree list` always says. How it gets there depends on the harness: * **Claude** creates the worktree the first time the agent decides to edit a file, so a thread that only asks questions never gets one. Bishop names it, tells the agent to enter it, and refuses edits in the shared checkout until it has. The branch is the worktree name prefixed with `worktree-`. * **Codex** has no equivalent tool, so Bishop creates the worktree itself and starts the session inside it. Every thread gets one whether or not it edits anything, and the branch is the worktree name exactly. Either way the agent commits on that branch and says which one it is. ## Missing files in a worktree A new worktree holds only tracked files, so `node_modules` and anything else your repository ignores is missing. Under Claude, symlink what the build needs: ```json { "agent": { "worktrees": true, "options": { "settings": { "worktree": { "symlinkDirectories": ["node_modules"], "baseRef": "head" } } } } } ``` Bishop sets `baseRef` to `head` so a worktree branches from wherever the agent directory is now. Set it to `fresh` to branch from `origin/` instead. Everything else the harness offers here, including `sparsePaths` for a large monorepo, works the same way. Under Codex, Bishop always branches from `HEAD` and there's nothing to configure. ## What it guarantees Under Claude, Bishop refuses `Edit`, `Write`, and `NotebookEdit` in the shared directory until the agent has entered its worktree, and tells the agent why. Bash isn't checked, since a command's target can't be read off its text. So this keeps threads out of each other's way, but it isn't a sandbox: an agent determined to write outside its worktree can. Under Codex the session starts in the worktree, and `sandboxMode` limits where it can write, or nono when the [sandbox](/running/sandbox) is on. **When the agent directory isn't a git repository**, there's nothing to isolate. Bishop warns at startup and says so once in each thread, then edits the directory directly. ## Removing a worktree A worktree outlives the thread that made it, and Bishop [collects](/deploying/cleanup) it along with its branch once the thread has been quiet long enough. To remove one sooner under Claude, unlock it first, since the harness locks each one while its session holds it and the lock outlives the process: ```sh git worktree list git worktree unlock .claude/worktrees/bishop-T1-C1-1712345678-123456 git worktree remove .claude/worktrees/bishop-T1-C1-1712345678-123456 ``` Add `--force` to `remove` to discard uncommitted changes in it. Check `git -C status` first: that's where a thread's unfinished work is. --- --- url: https://bishop.agentdeployment.co/running/sandbox.md description: >- Running the agent under nono so it can't read Bishop's credentials, database or other threads' files. --- # Sandbox Turning the sandbox on runs the agent, and everything it starts, under [nono](https://nono.sh/os-sandbox), which removes its access to `.bishop/` entirely. Bishop therefore has to run from a directory outside the agent repo. Under Codex, Bishop also switches Codex's own sandbox off (`sandboxMode` becomes `danger-full-access`) and logs that it did, because nono replaces it and Codex's own failed to start inside nono when tested on Linux. ## Install nono On Debian or Ubuntu: ```sh VERSION=$(curl -sIL https://github.com/nolabs-ai/nono/releases/latest | grep -i location | grep -oP 'v\K[0-9a-zA-Z.-]+') ARCH=$(dpkg --print-architecture) wget https://github.com/nolabs-ai/nono/releases/download/v${VERSION}/nono-cli_${VERSION}_${ARCH}.deb sudo dpkg -i nono-cli_${VERSION}_${ARCH}.deb ``` On macOS, or Linux with Homebrew, `brew install nono`. Other distributions are covered in [nono's install guide](https://nono.sh/docs/cli/getting_started/installation). Then check that this machine can enforce it: ```sh nono setup --check-only ``` Linux needs kernel 5.13 or later, and macOS 10.15 or later. nono has to be on the `PATH` Bishop runs with, which for a systemd unit is the unit's own `Environment=PATH=…`. ## Turn it on Bishop's own directory can't be the agent directory with the sandbox on, because the kernel can't grant a directory while hiding part of it. Run Bishop from a directory of its own and point [`--agent`](/running/agent) at the agent, either a path or a git URL: ```sh mkdir ~/bishop-home && cd ~/bishop-home bishop --agent ~/agents/salesforce-guy ``` Move `.env` and `.bishop/` from the old directory into the new one first, or run setup there again. Then turn it on in `.bishop/config.json`: ```json { "agent": { "sandbox": true } } ``` Bishop checks nono works before it starts, and refuses to start rather than run the agent unconfined. The log says `sandbox is on` when it is, and warns when `agent.sandbox` names no profile, since the agent then can't reach toolchains under your home directory or push. An agent that needs neither can ignore the warning. ## What the agent can reach Each turn can read and write its working directory (the thread's worktree in [worktree mode](/running/worktrees)), the repository's git directory so it can commit, the temp directory, and Claude Code's own `~/.claude`. It can read the files shared in its own thread, and your git config. Network access is unchanged. Everything else under your home directory is out of reach, and a command that tries gets "Permission denied", which the agent can report like any other error. That includes tools installed under your home directory, like `nvm`, `pyenv` or `~/.cargo`. Grant them with a nono profile, which adds to Bishop's grants rather than replacing them. Save one as `~/.config/nono/profiles/toolchain.json`: ```json { "meta": { "name": "toolchain" }, "filesystem": { "read": ["$HOME/.nvm", "$HOME/.cargo"] } } ``` and name it: ```json { "agent": { "sandbox": { "profile": "toolchain" } } } ``` `nono profile guide` explains the format. Don't add a `filesystem.deny` for anything Bishop grants: on Linux nono refuses to start with one, and so does Bishop. ## Letting the agent push The sandbox keeps the agent out of `~/.ssh` and the `gh` CLI's login, so it can't push. Run this from Bishop's own directory: ```sh bishop nono setup ``` It writes a nono profile named `bishop` (or whatever `--name` says) to `~/.config/nono/profiles/` that grants both, plus nono's `node-dev` toolchain and `~/.npm`, and names it in `agent.sandbox`, turning the sandbox on if it was off. A profile already at that path isn't replaced, and if `agent.sandbox` already names another profile the command leaves it and explains how to combine the two. The profile lets the agent read your SSH private keys, so give Bishop's user keys meant for the agent. ## Notes * `file_share` sends only files the agent could read itself. A file somewhere else has to be copied into its working directory first. * Leave Claude Code's own `sandbox` setting off under nono. One sandbox is easier to reason about, and Claude's has an escape hatch that retries a blocked command outside it. * The container image doesn't include nono, and the sandbox isn't supported there yet. * macOS is supported by nono but hasn't been tested with Bishop. * `nono why --path --op read` says which rule decided a "Permission denied". --- --- url: https://bishop.agentdeployment.co/running/files.md description: How files people attach reach the agent, and how the agent sends files back. --- # Files Files go both ways: people attach them for the agent, and the agent sends them back. ## Files people share Bishop downloads what people attach and saves it under `.bishop/files`, one directory per thread, [collected](/deploying/cleanup) with everything else that thread left behind. The agent reads it from disk, so any file type works: what the agent can do with a `.parquet` is up to the agent. A file shared earlier in the conversation is named to the agent rather than downloaded, and the agent fetches the ones it wants with a tool. That keeps being tagged into a busy thread cheap, and it means the agent can go back for a file it was told about turns ago. A reference only works in the conversation the file was shared in. ```json { "files": { "maxMb": 100 } } ``` The default is 100. A file over the limit isn't refused: the agent is told its name and that it was too large, so it can say so. What each interface needs: * **Slack** needs the `files:read` and `files:write` scopes. See [Scopes](/interfaces/slack#scopes). * **Email** needs nothing more. * **Teams** needs `supportsFiles` on the app's bot, which `bishop teams setup` sets on a new app and reports missing on an old one. A file in a channel also needs `Files.ReadWrite.All`. See [Teams files](/interfaces/teams#files). ## Files the agent sends The agent can send a file back: an upload in the Slack thread, an attachment on the mail in email, a file card in Teams. It registers the file with a tool, which checks it and hands back a reference, and writes that reference into its reply where the file should appear. In Slack the file arrives on the message carrying the words around it, the way a person sends one. A file it registers and never places is still sent, after the answer, so nothing is silently dropped. In a Teams personal chat the recipient gets a card asking them to accept the file, and it arrives once they do. In a channel the file is uploaded to the channel's SharePoint files folder, where it stays after the conversation and anyone with access to that folder can open it, including people who were never in the thread. Sending to a channel needs `Files.ReadWrite.All`, and without it the message goes out without the file. Group chats can't carry a file, and an agent that tries in one is told so. The limit is 25MB per answer in Slack and Teams and 4MB by email, across everything one answer sends. Neither is configurable. Anything past it is dropped with a line in the log and the message still goes. A scheduled report can carry files too, and an agent that reports nothing sends nothing, attachment included. With the [sandbox](/running/sandbox) on, the agent can only send files it could read itself. --- --- url: https://bishop.agentdeployment.co/running/schedules.md description: >- Running a prompt on a schedule and posting the answer only when the agent has something to say. --- # Scheduled Prompts Bishop can run a prompt on a clock and post the answer where you say, only when the agent has something to say. ```sh bishop schedules add \ --name "CI check" \ --cron "0 9 * * 1-5" \ --to "#ops" \ --prompt "Check CI on main. If nothing is broken, answer ." ``` `--to` takes `#channel` or `@person` for Slack, or an email address for Gmail, and never an id: Bishop resolves it once, when you add the schedule, and shows it back the same way. A Slack destination needs [three scopes](/interfaces/slack#scopes) an older app may not have. ```sh bishop schedules list # everything scheduled here, in your own time zone bishop schedules disable # stop it firing, keep it bishop schedules enable # start it again, from now bishop schedules rm # remove it ``` Schedules live in `bishop.db`, not in a config file, so `bishop schedules list` is the only way to see what the agent will do tomorrow. ## What to expect * **Silence is the normal outcome.** Bishop posts nothing when the agent answers `` or says nothing at all. `NOTHING TO REPORT` still works. Tell the agent in the prompt what's worth reporting: that criterion is yours, not Bishop's. * **Nothing streams.** A scheduled turn is invisible while it runs, on Slack as much as on email, because Bishop can't unsay a report it turns out not to want. There's no way to stop one mid-turn. * **Every firing is a fresh session.** The agent doesn't remember the last one, so tell it to write down anything it needs to carry over. * **A report is a thread.** Reply to it and the agent picks up from that report. * **`--cron` is five fields**, read in `--tz` (defaults to this machine's zone), and no more often than every five minutes. `@daily` and friends work. * **Nothing fires while Bishop is stopped, and nothing catches up.** A firing missed while it was down is skipped, and the log says so. A firing whose previous run is still going is skipped too. * **A failure is never silent.** Bishop posts to the destination on the first failed run, records the reason in `schedules list`, and after five failures in a row turns the schedule off and says so. Re-enable it with `bishop schedules enable`. * **A schedule stops when its author loses access.** Bishop re-checks the allow list at every firing, so removing someone stops what they scheduled. ## Asking the agent for one You can also just ask, in a thread or an email: > @agent send me a report every morning at 9am in #ops, and stay quiet if there's nothing to say The agent writes the schedule itself, and Bishop wakes up and delivers it. Leave the destination out and it posts back into the conversation you asked in. Slack refuses a channel the app isn't in, and Gmail refuses an address that isn't on `gmail.allow`, and you're told so in the thread rather than finding out at nine tomorrow. You can ask by email for something to land in Slack: the destination decides where it goes, not where you asked from. Two limits the command line doesn't have, both because the agent is picking rather than you: * **It won't start DMing someone who isn't on `slack.allow`**, and won't send mail at all unless `gmail.allow` is set. Posting in a channel is fine, since the app had to be invited there. Use `bishop schedules add` to aim somewhere the recipient hasn't opted into. * **Twenty schedules per person.** Ask for a twenty-first and it'll tell you to remove one first. What the agent can see is narrower. It lists and removes schedules you created, plus any that post into the conversation you're in, and nothing else, so asking it won't name private channels you aren't in. `bishop schedules list` still shows everything, naming who asked for each. --- --- url: https://bishop.agentdeployment.co/deploying.md description: >- Running Bishop under any process supervisor, with what it needs from the network, where it logs, and what has to persist. --- # Deploying Bishop is one long-running process that logs JSON to stdout and errors to stderr, so any process supervisor works. It runs in the foreground and never restarts itself. Two ways to run it: * **[Linux and systemd](/deploying/linux):** the npm package on a host, with a hardened unit. The host is the agent's shell, so you install what the agent needs. * **[Container](/deploying/container):** `ghcr.io/agent-deployment-co/bishop`, which already carries the agent's usual tools. ## Network Slack and Gmail need outbound access only, since one holds a socket open and the other polls. Teams POSTs activities to an endpoint, so Bishop serves one when Teams credentials are present. That endpoint binds loopback like every other port Bishop opens, and a tunnel gives it a public hostname and a certificate without an inbound firewall rule. See [The endpoint](/interfaces/teams#the-endpoint). ## Logs ```sh BISHOP_LOG_LEVEL=info bishop ``` `BISHOP_LOG_LEVEL=debug` adds tool activity and every routing decision. Pipe through `npx pino-pretty` to read it by eye. It works in `.env` as well as exported, and an exported one wins. A value that isn't a level (`trace`, `debug`, `info`, `warn`, `error`, `fatal`, `silent`) logs a warning and leaves the level at `info`. ## Startup checks At startup Bishop checks that the agent can launch, resolves the allow lists, and connects to each interface, failing with a specific message if any of those doesn't work. A clean start proves the harness is installed and authenticated, and costs nothing. A first turn that fails after that is usually a bad `--model` or `--effort`, which nothing validates at startup. ## What has to persist * **Bishop's own directory**, the one it runs from: `.env`, `bishop.db` with its `-wal` and `-shm` files, and `.bishop/`. * **The home directory of the user Bishop runs as**, which holds both harnesses' logins and session transcripts. Wiping it breaks every existing thread, because Bishop resumes a thread by handing the harness a session it no longer has. Back up `bishop.db` with the process stopped, or through SQLite's backup API. Copying the file while Bishop is running misses whatever is still in `bishop.db-wal`. Bishop takes its own copy before a schema migration, which covers an upgrade but not a disk. ## Restarts **A restart kills whichever turn asked for it.** An agent that edits its own unit file and restarts the service takes down the process running that same turn. It comes back clean, since thread state lives in Bishop's own directory, so the conversation resumes on the next message. But the response that triggered the restart cuts off mid-stream, which reads as a crash to whoever is waiting on it unless they're told to expect it. Bishop notices neither a revoked Slack app nor a dead socket, so a supervised process can look healthy while receiving nothing. Restart it if the agent goes quiet. --- --- url: https://bishop.agentdeployment.co/deploying/linux.md description: >- Running Bishop on a Linux host under systemd, with the packages, directories and unit it needs. --- # Linux and systemd What to install, where things live, and the unit that runs it, for a host that runs the agent as an ordinary user account instead of the [container image](/deploying/container), which already carries all of this. Verified on Ubuntu 24.04 LTS (x86_64), Node 24.20.0, uv 0.12.10, CPython 3.14.7. ## Requirements | | | |---|---| | Node | 24 or newer. Bishop imports `node:sqlite`. | | Architecture | x86_64 or arm64. Install on the host that will run it, never copy a tree across architectures. | | Supervisor | systemd, or any supervisor. Bishop runs in the foreground and never restarts itself. | | Network | Outbound only, unless you run Teams. See [Network](/deploying/#network). | | Not needed | `tini` (container only), `build-essential` (no native modules). | ## Node ```sh curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash export NVM_DIR="$HOME/.nvm" && . "$NVM_DIR/nvm.sh" nvm install v24.20.0 mkdir -p ~/.local/bin ln -sfn "$HOME/.nvm/versions/node/v24.20.0/bin/node" ~/.local/bin/node ln -sfn "$HOME/.nvm/versions/node/v24.20.0/bin/npm" ~/.local/bin/npm ``` Pin the version. The unit needs an absolute path to `node`, since a systemd service gets no login shell and no `nvm`. The symlink in `~/.local/bin` lets the unit survive a Node upgrade without an edit. NodeSource's apt repo works too. ## OS packages The host is the agent's shell as well as Bishop's, so install what the agent will reach for: ```sh sudo apt-get update sudo apt-get install -y --no-install-recommends \ ca-certificates \ curl \ git \ jq \ less \ openssh-client \ python3 \ ripgrep \ tzdata \ unzip \ wget ``` `gh`, which isn't in the Ubuntu or Debian archives: ```sh curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \ | sudo tee /usr/share/keyrings/githubcli-archive-keyring.gpg >/dev/null sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ | sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null sudo apt-get update && sudo apt-get install -y gh ``` Every package here is reachable by a prompt injection. Add to the list deliberately. nono, for the [sandbox](/running/sandbox), is optional. With the sandbox on, `WorkingDirectory` in the unit below has to be a directory of Bishop's own rather than the agent directory, and `--agent` names the agent. ## Python ```sh # As the service account, from a directory it can read. curl -fsSL https://astral.sh/uv/install.sh | sh uv python install 3.14 ``` * Set `UV_PYTHON_PREFERENCE=managed` and `UV_LINK_MODE=copy`. * Put `~/.local/bin` on the service `PATH`, appended and never prepended. The agent can write it. * Leave the distribution's `python3` alone. * Provisioning as root: create each level of `~/.local` with the right owner, and `cd` out of `/root` before running `uv`. ## Bishop ```sh npm install -g @agentdeploymentco/bishop ``` The package is public, so the host needs no GitHub access. | | | |---|---| | Script the unit runs | `$(npm root -g)/@agentdeploymentco/bishop/dist/cli.js` | | Upgrade | `npm install -g @agentdeploymentco/bishop@latest`, then restart | | After a Node upgrade | Reinstall. Under `nvm` the global tree moves with the Node version. | Both harnesses come out of `node_modules`. There's no separate agent CLI to install. [Install](/install#from-source) covers building from source. ## Credentials Credentials go in `.env` in Bishop's own directory, mode `0600`, owned by the service account. [Credentials](/reference/credentials) lists every variable. * `CODEX_HOME`, for the `codex` harness, points somewhere persistent. Put it in the unit rather than `.env`. * SSH keys cover clone and push, and API calls like `gh pr create` need `GH_TOKEN`. Check both with `gh auth status` and `gh api user --jq .login`. * `--auto-update` needs git credentials that work non-interactively at runtime. ## Directories | Directory | Holds | |---|---| | Bishop's directory (the unit's `WorkingDirectory`) | `.env`, `bishop.db` and its `-wal`/`-shm`, `.bishop/`, and the agent's workspace unless `--agent` points elsewhere | | `$HOME` of the service account | Both harnesses' session transcripts. Wiping it breaks every existing thread. | * Keep them separate. Don't set `$HOME` to Bishop's directory. * If the agent directory's owner doesn't match the service account, fix the ownership, or run `git config --global --add safe.directory ''` as that account. * Set a git identity for the service account (`git config --global user.name ...`), or every commit the agent makes in a fresh snapshot fails with "Author identity unknown". * Schedule `bishop gc` if you want it on your own terms. Bishop also [cleans up](/deploying/cleanup) on its own while running. ## The unit ```ini [Unit] Description=Bishop agent After=network-online.target Wants=network-online.target [Service] Type=simple User= WorkingDirectory= Environment=HOME= Environment=PATH=/usr/local/bin:/usr/bin:/bin:/.local/bin ExecStart=/.local/bin/node /@agentdeploymentco/bishop/dist/cli.js --agent --auto-update Restart=always RestartSec=10 SyslogIdentifier=bishop NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=read-only ReadWritePaths= [Install] WantedBy=multi-user.target ``` Required: * `Environment=HOME=`. systemd won't set it from the passwd entry. * `ReadWritePaths` covering `$HOME`, or `ProtectHome=read-only` breaks thread resumption. Same with `ProtectSystem=strict`. * `WorkingDirectory` at Bishop's directory. That's where Bishop reads `.env`. * `/.local/bin` last in `PATH`. * `ExecStart` running `node` against `dist/cli.js`, not the `bishop` symlink, whose `#!/usr/bin/env node` resolves against that `PATH`. * No `EnvironmentFile=` pointing at the `.env` Bishop already loads. ```sh sudo systemctl enable --now bishop.service journalctl -u bishop -f ``` **Replace a flag in `ExecStart`, don't append one.** A backslash-continued `ExecStart` loses its trailing `\` when a new line gets pasted below the old last one. systemd then ends the directive there, and the line below becomes a bare, invalid line in `[Service]`. `systemctl status` warns that the unit changed on disk but not about the syntax error, so read the file back after editing. ## Letting the agent restart itself Restarting the unit is a polkit decision, not a sudo one. `/etc/polkit-1/rules.d/49-.rules`: ```javascript // Let the agent restart its own unit, and nothing else. polkit.addRule(function (action, subject) { if (action.id === "org.freedesktop.systemd1.manage-units" && action.lookup("unit") === ".service" && ["restart", "try-restart", "reload-or-restart"].indexOf(action.lookup("verb")) >= 0 && subject.user === "") { return polkit.Result.YES; } }); ``` This grants `restart` but not `stop`, needs no `sudo`, and leaves `NoNewPrivileges=true` in place. The alternative is letting the agent kill its own process and letting `Restart=always` bring it back. Don't add a sudoers entry, which only works with `NoNewPrivileges` removed. A restart cuts off the turn that asked for it. See [Restarts](/deploying/#restarts). Claude Code's own permission model may also refuse to let a session restart the service running it, so a person may end up applying the restart by hand either way. ## Verify As the service account, before starting the service: ```sh node --version node -e 'require("node:sqlite"); console.log("sqlite ok")' git --version && gh --version && rg --version && jq --version uv python find 3.14 bishop --version ls -l "$(npm root -g)"/@agentdeploymentco/bishop/node_modules/@openai/ # codex-linux-{x64,arm64} gh api user --jq .login bishop gc ``` ## Checklist * \[ ] Node >= 24, pinned, at an absolute path * \[ ] `ca-certificates curl git jq less openssh-client python3 ripgrep tzdata unzip wget` * \[ ] `gh` from GitHub's apt repository * \[ ] nono, and a `WorkingDirectory` separate from the agent, if running with `agent.sandbox` * \[ ] `uv` and a managed interpreter, `~/.local/bin` appended to `PATH` * \[ ] `npm install -g @agentdeploymentco/bishop` on the target architecture * \[ ] `.env` at `0600` in the unit's `WorkingDirectory` * \[ ] Persistent writable `$HOME`, with `HOME=` set in the unit * \[ ] `CODEX_HOME`, if running the `codex` harness * \[ ] A GitHub token `gh api user` accepts, not just an SSH key * \[ ] Git credentials that work non-interactively, and a global git identity * \[ ] A polkit rule, if the agent should restart itself * \[ ] Unprivileged service account: no `sudo`, no `docker` group --- --- url: https://bishop.agentdeployment.co/deploying/container.md description: Running the Bishop container image, with its volumes, setup commands and tags. --- # Container `ghcr.io/agent-deployment-co/bishop` runs Bishop on Debian with Node 24, for `linux/amd64` and `linux/arm64`. The image is the agent's shell as well as Bishop's, so it carries git, `gh`, ripgrep, jq, curl, ssh, and python3 alongside both harness SDKs. `uv` and `uvx` are there too, with CPython 3.14 already installed and on `PATH` as `python3.14`, since Debian's `python3` is externally managed and won't let anything install into it. ## Volumes Two directories to persist. `/data` is the working directory, holding everything Bishop writes: ``` /data/.bishop/config.json configuration /data/.bishop/agents/ cloned agents, when --agent is a git URL /data/bishop.db the thread-to-session mapping ``` `/home/node` holds what the harnesses write: their logins, and the session transcripts they resume from. A home directory that dies with the container leaves every existing thread asking for a transcript the harness no longer has. Mount it or lose every conversation on the next deploy. Bishop runs as uid 1000 and needs to write both volumes. On Kubernetes that means a `fsGroup` in the pod's security context, or volumes already owned by 1000. ## Running it Credentials go in the environment rather than a `.env` file: ```sh docker run -d --name bishop \ -v bishop-data:/data -v bishop-home:/home/node \ -e BISHOP_SLACK_BOT_TOKEN -e BISHOP_SLACK_APP_TOKEN \ -e ANTHROPIC_API_KEY \ -e GIT_CONFIG_COUNT=2 \ -e GIT_CONFIG_KEY_0=user.name -e GIT_CONFIG_VALUE_0="Agent" \ -e GIT_CONFIG_KEY_1=user.email -e GIT_CONFIG_VALUE_1="agent@example.com" \ ghcr.io/agent-deployment-co/bishop \ --agent https://github.com/you/your-agent.git --auto-update ``` The entrypoint is `bishop`, so arguments after the image are its flags, and any other command works the same way: ```sh docker run --rm -v bishop-data:/data ghcr.io/agent-deployment-co/bishop gc ``` Set a git identity, as above, or the agent can't commit what it does in a worktree. `GIT_CONFIG_COUNT` reaches every snapshot and worktree Bishop creates, which a `git config` run in one directory doesn't. ## Setup commands Setup commands that need a browser or a terminal, like `bishop slack setup` and `bishop gmail setup`, are meant to be run on a laptop. Carry the tokens they produce to the container as environment variables, and the `.bishop/config.json` they write into the `/data` volume. ## Tags `latest` and `sha-` from `main`, plus `` and `.` for each release. ## Limits The image doesn't include nono, so the [sandbox](/running/sandbox) isn't supported in it yet. --- --- url: https://bishop.agentdeployment.co/deploying/cleanup.md description: >- How Bishop removes what old conversations leave behind, and how to run that yourself. --- # Cleaning Up A finished conversation leaves things behind: the snapshot it was pinned to, the worktree and branch it edited in, the files people shared in it, and its rows in Bishop's database. A schedule leaves a set every time it fires, which for an hourly one is 24 a day. Bishop collects them itself, at startup and every six hours after, so a gateway left running for months doesn't fill a disk. Everything a thread left is kept for 30 days past its last turn: ```json { "gc": { "maxAgeDays": 30 } } ``` A snapshot a thread is still using is kept however old the commit is, and so is `current`. Nothing a live conversation is using is ever touched. **It deletes what it collects.** A worktree goes with its branch, including work the agent committed there and nobody merged, and a thread that comes back after that starts fresh on the current files. If a thread's work matters, merge it before it ages out, or raise `maxAgeDays`. **A collected thread stops being one Bishop is listening to.** In a Slack channel, mention the agent to pick it up again: a reply without a mention in a thread that old is ignored. DMs and email are unaffected. ## On your own terms With Bishop stopped, or from cron: ```sh bishop gc bishop gc --max-age-days 7 ``` It reads the database and the disk and nothing else, so it needs no credentials. It leaves the database copies in `.bishop/backups/` alone. See [Upgrading](/deploying/upgrading). --- --- url: https://bishop.agentdeployment.co/deploying/upgrading.md description: Upgrading Bishop, database migrations, and rolling back. --- # Upgrading Install the new version and restart. A version that changes the database migrates it on the first start, and says so in the log. The [changelog](/reference/changelog) says what each version changed, including which ones migrate. ```sh npm install -g @agentdeploymentco/bishop@latest ``` In a container, pull the new image and start it against the same volumes. ## Rolling back **Bishop copies the database before it migrates.** The copy lands in `.bishop/backups/`, named for the schema it holds. To go back, stop Bishop, install the version you were on, and restore it: ```sh rm bishop.db bishop.db-wal bishop.db-shm cp .bishop/backups/bishop.db.v8.backup bishop.db ``` **Delete `bishop.db-wal` first, as above.** Bishop runs the database in write-ahead logging mode, so recent changes live in that file rather than in `bishop.db`. A Bishop that stopped cleanly leaves nothing there, but one that was killed does, and SQLite replays it into whatever database file it finds beside it without checking that the two belong together. Restore without removing it and you get the migrated database back, looking exactly like a successful rollback. You can't tell which case you're in by looking, so remove them every time. Nothing deletes the copies, and `bishop gc` leaves them alone. There's one per schema version this database has been migrated from, so a handful at most. Upgrading again from the same version replaces that version's copy, so if you restore, run the old version for a while and upgrade again, the copy covers that stretch too. If Bishop can't take the copy it refuses to start and says nothing was migrated, which usually means the disk is full or something else is under that name. Nothing has changed at that point, so fix the reason and start again. ## Teams An upgrade that adds a Teams permission needs the app removed and installed again before the permission takes effect. See [Microsoft Teams](/interfaces/teams#setup). --- --- url: https://bishop.agentdeployment.co/deploying/troubleshooting.md description: >- What to check when Bishop won't start, an agent doesn't answer, or setup fails. --- # Troubleshooting Start with the log. `BISHOP_LOG_LEVEL=debug` adds every routing decision, which is usually where a message that got no answer went. ## Startup **"--agent directory doesn't exist"** or **"is not a directory"** means the path is wrong. Bishop checks it before anything else, because the harness reports a bad directory as a native binary failing to launch. **"The Claude agent couldn't start"** means the harness didn't launch. Check that `claude` works in that directory first. A wrong `ANTHROPIC_API_KEY` passes this check and fails on the first real message, since only a live call tests a key. **"agent.sandbox is on, but nono is not installed or not on PATH"** means what it says. Install it as in [Sandbox](/running/sandbox), and check `which nono` as the user and `PATH` Bishop runs with. **"The sandbox would have to grant …, which contains …/.bishop"** means Bishop's own directory is inside the agent directory. Run Bishop from a directory of its own and point `--agent` at the agent. ## No answer **The bot doesn't answer in a Slack channel.** It only replies to mentions and to threads it's already in. Confirm it's in the channel and that you mentioned it. **The bot doesn't answer at all, and the log shows a refusal.** The sender isn't on the [allow list](/running/access). **The agent never answers, and nothing appears in the log.** If you set the Slack app up by hand, check its Event Subscriptions. Bishop can't see them, and an app subscribed to nothing looks identical to an idle one. In Teams, check the app is installed and the endpoint is current: a blocked install or a stale tunnel hostname delivers nothing, so the log stays silent. See [Installing the app](/interfaces/teams#installing-the-app). **The agent went quiet after running fine.** A revoked Slack app or a dead socket goes unnoticed, so restart Bishop. For email, a reset mailbox password revokes the refresh token, and `bishop gmail setup` authorizes again. **The agent in Teams only answers when tagged.** The app was installed without the resource-specific consent permissions, or was updated in place after they were added. Remove it and install it again. ## The agent **The agent says it's blocked on permissions.** Something needs approval that `auto` mode declined. Add an allow rule under `agent.options.settings.permissions.allow`. See [Agent options](/running/harness#agent-options). **A first turn fails after a clean start.** Usually a bad `--model` or `--effort`, which nothing validates at startup. See [Model and effort](/running/harness#model-and-effort). **The agent says it can't edit a file.** In worktree mode Bishop refuses writes in the shared directory until the agent enters its worktree, which it normally does unprompted. If the agent directory isn't a git repository, that can't work, and Bishop says so in the thread. **The agent says "Permission denied" with the sandbox on.** It reached for something outside its grants. If it needs it, add the path to a nono profile. `nono why --path --op read` says which rule decided. **Commits fail with "Author identity unknown".** A fresh snapshot or container has no git identity. Set one globally for the user Bishop runs as, or through `GIT_CONFIG_*` in a [container](/deploying/container). ## Worktrees **You can't find what a thread changed.** In worktree mode the work is on that thread's branch, not in the agent directory. `git worktree list` shows every one, and `git branch --list 'worktree-bishop-*'` shows the branches. **A build or test fails inside a worktree.** A fresh worktree has only tracked files, so `node_modules` and anything else the repository ignores isn't there. Add it to `agent.options.settings.worktree.symlinkDirectories`. ## Setup **Slack setup says the app kept a `(local)` suffix.** The `slack` CLI's login expires after 12 hours, which stops Bishop from correcting the app name. Run `slack auth login` again, or rename the app in Slack. **Teams CLI commands start logged out after a successful login.** The CLI was installed without `--allow-scripts`. Reinstall it as in [Microsoft Teams](/interfaces/teams#setup). ## Sessions **Sessions get confused.** Stop Bishop and delete `bishop.db` to make every thread start fresh. That also deletes every schedule, so note what `bishop schedules list` shows first. --- --- url: https://bishop.agentdeployment.co/reference/config.md description: Every field in .bishop/config.json. --- # Configuration `.bishop/config.json` in Bishop's own directory holds everything except credentials, which is what lets you commit it. Setup writes it, and you can edit it by hand. A missing file is an empty config. ```json { "slack": { "allow": ["hayes.davis@example.com", "revops", "@sales-managers"] }, "gmail": { "allow": ["hayes.davis@example.com", "@example.com"], "pollSeconds": 20 }, "agent": { "harness": "claude", "model": "claude-sonnet-5", "effort": "high" } } ``` Every block is optional. An interface runs when its [credentials](/reference/credentials) are set, not when its block is here, and its block only configures it. Every key is checked at startup, so a typo fails with the path to it rather than being ignored. The exception is `agent.options`, which is passed to the harness unvalidated. Setup rewrites the file as formatted JSON, so key order and layout don't survive it. ## `slack` | Key | What it does | |---|---| | `allow` | Usernames, emails, and group names. See [Access](/running/access#slack). | | `verbosity` | `low`, `medium`, or `high` (the default). Where every thread starts. See [verbosity](/interfaces/commands#verbosity). | | `maxBotTurns` | How many turns in a row another bot may draw in one thread before Bishop waits for a person. Defaults to 3, and `0` means never answer a bot. See [Other agents](/interfaces/slack#other-agents). | | `workspace` | Written by setup to record which workspace the app belongs to. Nothing reads it. | | `app` | `id`, `name`, `displayName`, `description`, written by setup. Changing them afterwards doesn't change the app in Slack. | ## `gmail` | Key | What it does | |---|---| | `allow` | Addresses and `@domain` entries. An entry that is neither stops startup. See [Access](/running/access#email). | | `pollSeconds` | How often the inbox is checked. Defaults to 20. | | `labelPrefix` | Names the labels Bishop puts on threads. Defaults to `Bishop`, giving `Bishop/Working`, `Bishop/Done`, `Bishop/Failed`, and `Bishop/Refused`. | | `markRead` | Marks a message read once the agent has been given it. Off by default, since a person may also be reading the mailbox. | | `internalDomains` | The domains whose addresses count as inside the organization, which decides whether the agent is told a thread is internal or external. Defaults to the mailbox's own domain, except on `gmail.com` and `googlemail.com`, where nobody is. | ## `teams` | Key | What it does | |---|---| | `allow` | User principal names, `@domain` entries, and Entra object IDs. See [Access](/running/access#microsoft-teams). | | `verbosity` | `low`, `medium`, or `high` (the default). `low` also drops the line saying what a turn cost. | | `port` | Where the messaging endpoint listens. Defaults to 3978. | | `host` | What it binds to. Defaults to loopback, which is where a tunnel reaches it. | | `tls` | `cert` and `key`, paths to PEM files, for Bishop to terminate TLS itself. Leave it out behind a tunnel or proxy. | | `app` | `id`, `name`, `displayName`, `description`, written by setup. Every `teams` command finds the app by `id`. | The messaging endpoint isn't in the file. The Teams app holds it, and `bishop teams setup` reads it back and reports it. ## `agent` | Key | What it does | |---|---| | `harness` | `claude` (the default) or `codex`. See [Harness and Model](/running/harness). | | `model` | Passed to the harness as written. | | `effort` | Passed to the harness as written. | | `worktrees` | `true` gives each thread its own git worktree. See [Worktree Mode](/running/worktrees). | | `sandbox` | `true`, or `{ "profile": "" }` to add a nono profile's grants. Off by default. See [Sandbox](/running/sandbox). | | `options` | The harness's own options, unvalidated. See [Agent options](/running/harness#agent-options). | | `harnesses` | A block per harness, each taking `model`, `effort`, and `options`. See [One config for both harnesses](/running/harness#one-config-for-both-harnesses). | `--harness`, `--model` and `--effort` override these for one run. `model` and `effort` layer, weakest first: `agent.model`, then the running harness's block, then the flag. A block's `options` replaces `agent.options` rather than merging with it. ## `files` | Key | What it does | |---|---| | `maxMb` | The largest attachment Bishop downloads. Defaults to 100. A file over it reaches the agent as its name and the reason it isn't there. See [Files](/running/files). | ## `gc` | Key | What it does | |---|---| | `maxAgeDays` | How long everything a quiet thread left behind is kept past its last turn. Defaults to 30. There's no switch to turn collection off. See [Cleaning Up](/deploying/cleanup). | --- --- url: https://bishop.agentdeployment.co/reference/credentials.md description: Every credential Bishop reads, and where it reads them from. --- # Credentials Credentials live in `.env` in Bishop's own directory, or in the environment, and never in `.bishop/config.json`. Setup writes each interface's to `.env` and gitignores it. An exported variable wins over the same one in `.env`. On a host, keep `.env` at mode `0600`, owned by the user Bishop runs as. In a [container](/deploying/container), pass them as environment variables. Bishop runs every interface whose credentials are set. | Variable | For | |---|---| | `BISHOP_SLACK_BOT_TOKEN`, `BISHOP_SLACK_APP_TOKEN` | Slack. The `xoxb-` and `xapp-` tokens, written by `bishop slack setup`. | | `BISHOP_GMAIL_CLIENT_ID`, `BISHOP_GMAIL_CLIENT_SECRET`, `BISHOP_GMAIL_REFRESH_TOKEN` | Gmail over OAuth. You set the first two, and `bishop gmail setup` writes the third. | | `BISHOP_GMAIL_USER`, `BISHOP_GMAIL_SERVICE_ACCOUNT` | Gmail over domain-wide delegation, instead of the OAuth three. The service account is a path to its JSON key, or the JSON itself. | | `BISHOP_TEAMS_CLIENT_ID`, `BISHOP_TEAMS_CLIENT_SECRET`, `BISHOP_TEAMS_TENANT_ID` | Microsoft Teams, written by `bishop teams setup`. | | `ANTHROPIC_API_KEY` or `CLAUDE_CODE_OAUTH_TOKEN` | The `claude` harness. Without either, it uses whatever login `claude` has on the machine. | | `CODEX_API_KEY` | The `codex` harness. Without it, it uses `codex login`. `OPENAI_API_KEY` isn't read. | | `CODEX_HOME` | Where the `codex` harness keeps its state, if not `~/.codex`. | | `GH_TOKEN` | The agent's GitHub credential, written by [`bishop github setup`](/running/github). | | `BISHOP_LOG_LEVEL` | The log level, `info` by default. The one setting of Bishop's own that isn't in the config file. | Anything else in `.env` reaches the agent's environment too, which is how `GH_TOKEN` gets there. Bishop reads `.env` at startup, so a change needs a restart. Setup commands that open a browser are meant to run on a laptop. Copy the lines they write to the server's `.env`. --- --- url: https://bishop.agentdeployment.co/reference/cli.md description: Every bishop command and flag, generated from bishop --help. --- # CLI Reference Every command and flag, as `bishop --help` prints them. `bishop` with no command starts the gateway. Everything else runs and exits. Options before a command belong to `bishop` itself, and options after it belong to the command. ## `bishop` ``` Usage: bishop [options] [command] An agent gateway. Runs an agent and connects it to Slack, email, and Teams. Options: -v, --version output the version number --agent the agent's directory, or a git URL to clone; defaults to the current directory --harness which SDK runs the agent --model the model to run, in the harness's own naming --effort how hard the model should think, in the harness's own naming --auto-update for a git-URL --agent, check for a new commit at the start of every new thread -h, --help display help for command Commands: gc [options] Collect the snapshots, worktrees, and rows old conversations left behind schedules Prompts the agent runs on a clock, and where their answers go gmail Gmail interface commands github GitHub credentials for the agent nono The nono sandbox the agent runs under slack Slack interface commands teams Microsoft Teams interface commands ``` ### `bishop gc` ``` Usage: bishop gc [options] Collect the snapshots, worktrees, and rows old conversations left behind Options: --max-age-days how long a quiet thread's remains are kept; defaults to gc.maxAgeDays, then 30 --agent only this agent's snapshots; defaults to every agent Bishop has cloned here -h, --help display help for command ``` ### `bishop schedules` ``` Usage: bishop schedules [options] [command] Prompts the agent runs on a clock, and where their answers go Options: -h, --help display help for command Commands: list Everything scheduled here, in your own time zone add [options] Add a schedule rm Remove a schedule disable Stop a schedule firing, keeping it enable Start a disabled schedule firing again, from now help [command] display help for command ``` #### `bishop schedules list` ``` Usage: bishop schedules list [options] Everything scheduled here, in your own time zone Options: -h, --help display help for command ``` #### `bishop schedules add` ``` Usage: bishop schedules add [options] Add a schedule Options: --name what a person sees in a list, and the subject of an email --cron five-field cron, or @daily and friends --prompt what the agent is asked, including when to stay quiet --to #channel or @person for Slack, an address for Gmail --tz IANA zone the expression is read in; defaults to this machine --interface which interface delivers, when --to is ambiguous -h, --help display help for command ``` #### `bishop schedules rm` ``` Usage: bishop schedules rm [options] Remove a schedule Options: -h, --help display help for command ``` #### `bishop schedules disable` ``` Usage: bishop schedules disable [options] Stop a schedule firing, keeping it Options: -h, --help display help for command ``` #### `bishop schedules enable` ``` Usage: bishop schedules enable [options] Start a disabled schedule firing again, from now Options: -h, --help display help for command ``` ### `bishop gmail` ``` Usage: bishop gmail [options] [command] Gmail interface commands Options: -h, --help display help for command Commands: setup [options] Authorize a Gmail mailbox, or check the one already configured help [command] display help for command ``` #### `bishop gmail setup` ``` Usage: bishop gmail setup [options] Authorize a Gmail mailbox, or check the one already configured Options: --no-browser print a URL to open elsewhere instead of opening one here -h, --help display help for command ``` ### `bishop github` ``` Usage: bishop github [options] [command] GitHub credentials for the agent Options: -h, --help display help for command Commands: setup [options] Give the agent a GitHub token, or check the one it's using help [command] display help for command ``` #### `bishop github setup` ``` Usage: bishop github setup [options] Give the agent a GitHub token, or check the one it's using Options: --no-prompt don't offer to run `gh auth login` --print write the credential to stdout as JSON instead of to .env --temporary log in without touching this machine's own gh login -h, --help display help for command ``` ### `bishop nono` ``` Usage: bishop nono [options] [command] The nono sandbox the agent runs under Options: -h, --help display help for command Commands: setup [options] Write a nono profile that lets a sandboxed agent push help [command] display help for command ``` #### `bishop nono setup` ``` Usage: bishop nono setup [options] Write a nono profile that lets a sandboxed agent push Options: --name the profile name (default: "bishop") -h, --help display help for command ``` ### `bishop slack` ``` Usage: bishop slack [options] [command] Slack interface commands Options: -h, --help display help for command Commands: setup [options] Create and install a Slack app, and save its credentials here manifest [options] Print the app manifest, for creating an app in Slack by hand help [command] display help for command ``` #### `bishop slack setup` ``` Usage: bishop slack setup [options] Create and install a Slack app, and save its credentials here Options: --name app name; defaults to the current directory name --display-name bot display name in Slack; defaults to --name --description short description shown in Slack --no-prompt don't prompt for anything left off --manual adopt an app you created in Slack yourself, using its tokens -h, --help display help for command ``` #### `bishop slack manifest` ``` Usage: bishop slack manifest [options] Print the app manifest, for creating an app in Slack by hand Options: --name app name; defaults to config or the directory name --display-name bot display name; defaults to --name --description short description shown in Slack -h, --help display help for command ``` ### `bishop teams` ``` Usage: bishop teams [options] [command] Microsoft Teams interface commands Options: -h, --help display help for command Commands: setup [options] Create a Teams app and save its credentials here, or check the one configured endpoint [options] Point the Teams app at a different URL, changing nothing else instructions [options] Print what to send whoever creates the Teams app, when that is an administrator help [command] display help for command ``` #### `bishop teams setup` ``` Usage: bishop teams setup [options] Create a Teams app and save its credentials here, or check the one configured Options: --name app name; defaults to the current directory name --display-name app name shown in Teams; defaults to --name --description short description shown in Teams --endpoint the URL Teams POSTs to; can be set later --app-id with --manual, the Teams app ID you were given --no-prompt don't prompt for anything left off --manual adopt an app somebody else created, using its credentials -h, --help display help for command ``` #### `bishop teams endpoint` ``` Usage: bishop teams endpoint [options] Point the Teams app at a different URL, changing nothing else Options: --app-id the Teams app to point; defaults to the one in config -h, --help display help for command ``` #### `bishop teams instructions` ``` Usage: bishop teams instructions [options] Print what to send whoever creates the Teams app, when that is an administrator Options: --endpoint the URL Teams should POST to, if you already know it -h, --help display help for command ``` --- --- url: https://bishop.agentdeployment.co/reference/limits.md description: What this version of Bishop doesn't do. --- # Limits What this version of Bishop doesn't do. * **One agent per process.** One Bishop directory runs one agent, and there's no agent registry. Run a second Bishop from a second directory for a second agent. * **Threads share a working directory unless worktree mode is on.** [Worktree mode](/running/worktrees) keeps concurrent threads out of each other's files by convention, and needs a git repository. The [sandbox](/running/sandbox) keeps the agent out of Bishop's own state and other threads' shared files, but not out of other threads' checkouts. * **No approval flow.** Permission decisions are the harness's, and a conversation has no way to ask a person for one. * **No way to turn Bishop's own tools off.** Bishop serves them on `127.0.0.1`, on an ephemeral port, authenticated with a credential minted per turn and destroyed when the turn ends. Nothing is reachable from off the machine, but anything running on it can reach the port and will be refused. * **No health signal for a dead Slack connection.** Bishop notices neither a revoked Slack app nor a dead socket, so a supervised process can look healthy while receiving nothing. Restart it if the agent goes quiet. * **Group chat files in Teams are untested.** A direct chat and a channel are the two that are known to work. * **No sandbox in the container image**, since it doesn't include nono. --- --- url: https://bishop.agentdeployment.co/reference/changelog.md description: What changed in each released version of Bishop. --- # Changelog What changed in each released version, newest first. A version's heading is dated the day it was tagged. `CLAUDE.md` covers how a release is cut. ## 0.5.3 (2026-10-07) * A `review` block in `.bishop/config.json` names a message reviewer, a command or an HTTPS endpoint. Startup refuses one that can't run or that the sandboxed agent could change. Nothing calls it yet, so a configured reviewer logs a warning that no message is reviewed (#262). * With `agent.sandbox` on and no nono profile named, startup warns and points at `bishop nono setup` (#266). * Commands Bishop runs, like the `git status` behind `bishop upgrade`, finish writing their output before Bishop reads it. Before, output could occasionally arrive truncated or empty (#252). * The Sandbox docs page opens by saying Bishop's home must sit outside the agent repo (#268). * The Claude Agent SDK moves to 0.3.292 and the Codex SDK to 0.160.1 (#260, #274). * `@modelcontextprotocol/sdk` moves to 1.32.1 and `proxy-addr` to 2.0.8 for security advisories (#261, #275). ## 0.5.2 (2026-10-05) * The Claude Agent SDK moves to 0.3.289 (#244, #245). * The docs site publishes `llms.txt`, `llms-full.txt`, and a markdown copy of every page, so a coding agent can read the docs directly. The Quick Start gives a setup prompt to paste into one (#247). ## 0.5.1 (2026-10-02) * The agent can read a Slack message someone links to or mentions, along with a few around it, through `slack_read_message`. It reads only from the conversation the turn is running in, and needs no new Slack scopes (#241). * The operator documentation moves from the README to a docs site built from `docs/`, for bishop.agentdeployment.co. `npm run docs:build` builds it (#219). ## 0.5.0 (2026-10-02) * The agent can change a schedule in place with `schedule_update`, keeping its id. Whoever changes a schedule becomes its owner, and a new cron or time zone moves the next firing (#215). * The Claude Agent SDK moves to 0.3.287 and the Codex SDK to 0.160.0 (#225, #238). * `npm audit` reports nothing: audited transitive dependencies are refreshed, and Vitest moves to 5 for development (#221, #239). ## 0.4.0 (2026-09-30) * `agent.sandbox: true` runs the Claude or Codex agent, and everything it starts, under nono, so the kernel keeps it out of Bishop's database, config, backups, `.env`, and files shared in other threads. Startup refuses rather than run the agent unconfined once the sandbox is asked for (#185, #186). * `bishop nono setup` writes a nono profile that lets a sandboxed agent push with SSH and the `gh` CLI, and turns the sandbox on with it. `--name` names the profile, `bishop` by default (#199). * Teams and Gmail agents are told where a conversation is, who can read it, and whether anyone outside the organization is in it, as Slack agents already were (#202). * After `! verbosity`, a Codex agent's next turn is told what the new level shows. Before, Codex kept the guidance its thread started with (#207). * Bishop keeps a transcript of what each turn handed the agent and what the agent said, collected with its thread. The upgrade adds a table for it (#211). * Bishop recognizes the agent's own Slack bot, mailbox, and Teams bot among stored senders. The upgrade adds a column for it (#210). * New schedules get `sch_` ids. Existing schedules keep their ids, and both forms work everywhere a schedule is named (#203). * The Claude Agent SDK moves to 0.3.285 and the Codex SDK to 0.159.2 (#204, #206, #212). ## 0.3.5 (2026-09-28) * The agent gets the same description of every sender on every interface, including whether it is a person or a bot and whether it is inside the organization, and Bishop remembers each sender under one id across threads. Scheduled turns come from Bishop's own principal. The upgrade migrates existing schedule owners (#192). * The agent knows what kind of Slack conversation it is in: a DM, a private channel or a public one, and whether anyone outside the organization can read it. It needs no new Slack scope (#198). * `npm run dev:teams` runs the built branch against a dev Teams app behind a Tailscale Funnel, the way `npm run dev:slack` does for Slack. Each dev instance now gets only its own interface's credentials, and its state moves to `.dev/` (#200). * The README and design docs say Teams can send files, and name `Files.ReadWrite.All` as the grant channel files need (#189). ## 0.3.4 (2026-09-24) * Teams takes the same command messages Slack does, like `! verbosity`, `! model`, and `! help` (#173). * The agent reads what was said earlier in a Teams channel thread or group chat when it is mentioned partway through, as it already does in Slack and Gmail (#177). * `! stop` stops a running turn in Teams and Slack. It is the only way to stop one in Teams (#179). ## 0.3.3 (2026-09-23) * Codex agents can commit from linked worktrees under the workspace-write sandbox (#171). ## 0.3.2 (2026-09-22) * Teams can send files from personal chats and channel threads, using consent cards for chats and SharePoint uploads for channels. Scheduled reports use the same paths (#165). * Claude and Codex SDK dependencies are pinned to exact versions so installs stay reproducible (#168). ## 0.3.1 (2026-09-21) * The agent can send a file back: an upload in a Slack thread, an attachment on the mail in email. It registers the file with `file_share` and writes `` where the file should appear. Teams can't carry one yet and refuses the tool with a reason (#142). * `! model` changes the model and the reasoning effort for one thread, either half on its own, with `default` returning it to the configured value. The setting lasts the life of the thread, so moving one conversation onto a harder model no longer means editing config and restarting every other conversation with it (#162). * The log line that closes a turn names the model that ran, and so does the line for a turn that errored (#162). * `npm run dev:slack` runs the built branch against a dev Slack app, so a change can be used in a real thread before it merges. It detaches, keeps its state in a gitignored `.dev/`, refuses a second instance on the same app, and stops itself after ten minutes of quiet (#162). * The container image no longer builds on pull requests (#160). * `CLAUDE.md` says how an issue gets filed and what a technical note is worth, and drops what its design docs already say (#156, #159). ## 0.3.0 (2026-09-17) * Bishop runs on Microsoft Teams: it serves the messaging endpoint from its own process, shows a turn while it runs, carries files people attach in a chat or a channel, and `bishop teams setup` walks an operator from an agent directory to a working app (#124, #125, #126, #130, #135, #138, #143, #145, #148, #152). * The agent stays out of an email thread it was only copied on (#128). * `BISHOP_LOG_LEVEL` works in `.env` (#146). Releases before 0.3.0 predate this file. `git log v0.2.0` is the record.