Approvals & Audit
Two halves of the same idea. The audit record says what an agent did after the fact. Remote approval lets you answer, from anywhere, the question it stopped to ask.
The audit record
Every agent run appends to .codeep/audit/ in the project — one JSON line per event, one file per day. Read it back with /audit:
The refusal on that last line is the reason this exists. A capability boundary that nothing records is a boundary you have to take on faith — the run looks identical whether the agent stayed inside it or was never tested against it.
What it does and does not keep
| Recorded | Not recorded |
|---|---|
File paths, commands, URLs | File contents, ever |
Reads, writes, searches, fetches | Anything from a project you did not run in |
Tool calls the boundary refused | Your prompts to other agents |
Which custom agent ran, and what it was granted |
Contents are deliberately absent. Undo already keeps them — that is what /undo and checkpoints are for. This record answers a different question, and a record you might hand to someone else should not carry your source with it.
.codeep/, which most projects already ignore — but Codeep never edits your .gitignore, so check that yours does.On every surface, in one place
The record lives with the project, not with the app that wrote it — same directory, same format, same field names everywhere. A project you work on from the terminal on Monday and the Mac app on Tuesday has one history, and either can read the other's runs.
| Reading it | Turning it off | |
|---|---|---|
CLI | /audit | /audit off |
Mac app | Open the project in the CLI, or read .codeep/audit/ directly | Settings → Tools & Privacy → Audit record |
VS Code | Codeep: Show Audit Record | Uses the CLI's setting |
Records already written are kept. Nothing is deleted by turning it off.
Approval on your phone
When a run stops on a dangerous tool it waits — and if you have walked away, it waits for as long as you are gone. Connect Telegram and the same question arrives on your phone with the same three answers. Answer in either place; whichever comes first decides, and the other is taken down rather than left showing buttons that no longer do anything.
codeep review --fix, the ACP server, CI — has nobody to ask, and blocking one on an answer that cannot come would hang a pipeline. Those decide from agentConfirmation as before.Setting it up
1. Make a bot. Open Telegram, message @BotFather, send /newbot, and follow the prompts. It replies with a token that looks like 123456789:AAE….
2. Send your bot a message. Any message. A bot cannot start a conversation with you, so this step is what makes the next one possible.
3. Find your chat ID. This is the part that is not obvious. Open this URL in a browser, with your token in place of <TOKEN>:
https://api.telegram.org/bot<TOKEN>/getUpdatesLook for "chat":{"id":…} in the response. That number is your chat ID.
4. Enter both. Where depends on which you are setting up — the token goes to your OS keychain either way, and the chat ID is ordinary settings.
| Token | Chat ID and the switch | |
|---|---|---|
CLI | /telegram — the input is masked | /settings → Telegram chat ID, then Answer confirmations on Telegram |
Mac app | Settings → Tools & Privacy → Approval on your phone | Same panel, same section |
What it looks like
The message names the tool, shows the command, and offers three buttons — Run, Skip, Cancel — the same three the terminal and the desktop sheet offer. Answer there instead and the message updates to say where it was decided, rather than leaving buttons that no longer do anything.
There is deliberately no Always allow on the phone. A standing permission is a decision that belongs at the keyboard, where you can see what you are granting it to.
If it does not arrive
| Symptom | Usually |
|---|---|
Nothing ever arrives | The switch is off, or the chat ID is empty |
"chat not found" | The ID belongs to a chat with a different bot — or you have not messaged this one yet |
Worked once, then stopped | Token was regenerated in BotFather — enter the new one |
Buttons do nothing | The question was already answered on the Mac |
getUpdates returns an empty result | You have not messaged the bot yet — do step 2 |