Agent governance for the terminal.
Permission rules that never match git add && git commit. An "always allow" that saves
the whole command line and so never matches again. Unattended runs that stall, or are denied with
nothing said. Session Sitter answers each prompt from your team's written practices instead —
rules that hold across a whole command, approvals that generalise, and a record of every
decision, each one citing the clause it applied.
Silence is never approval.
What changes · Install · Supervision · vs. first-party · Docs
The panel — every live session, and what the supervisor decided. More shots.
The agent asked to force-push. It was not blocked — it was corrected, and told why:
{"tool":"Bash","inputSummary":"git push --force origin main",
"light":"yellow","decision":"allow","clause":"practices §force-push","actor":"policy",
"latencyMs":4,"rewritten":true,
"note":"corrected — practices §force-push: --force replaced with --force-with-lease so the
push refuses rather than overwriting commits pushed by someone else"}That is a real record, from a real session — not an illustration. The push it corrected was not a
no-op either: another clone had already pushed a commit our repo had never fetched, so the original
--force would have destroyed it. The lease refused instead —
! [rejected] main -> main (stale info) — and the other commit is still on the remote. The agent
worked out what had happened on its own: "(stale info) is not an error plain git push --force
produces… something between me and git converted the force push into a lease-checked one."
4 ms in the hook, no model call. The full transcripts, the verbatim hook input and output, and
the other thirteen decisions ship as docs/EVIDENCE.md alongside the plugin itself.
Every decision names the practice it applied. That is the whole difference between a governance
layer and a classifier: Blocked by classifier is not something you can hand to your security
lead, and it is not something an agent can act on.
# Grab the .vsix from the latest release, then:
code --install-extension session-sitter-*.vsixLatest release ↓ · or in the IDE:
Extensions panel → ··· → Install from VSIX… · reload the window and you are done.
Note
Not on the VS Code Marketplace yet, so the extension installs by VSIX. The extension is not the
only way in, and for a terminal it is not the recommended one — the Claude Code plugin and the
session-sitter command have both landed:
/plugin marketplace add eranra/session-sitter # governance in the terminal
/plugin install session-sitter@session-sitter
npx github:eranra/session-sitter status # the command, on its ownEvery pull request also attaches a build, under the CI run's Artifacts — handy for trying a change before it lands.
Or from source:
git clone https://github.com/eranra/session-sitter.git
cd session-sitter
make installThat builds the extension and installs it. Then reload the window — Ctrl+Shift+P →
Developer: Reload Window. Done.
Installing into IBM Bob IDE, Cursor, or another VS Code build
make install shells out to code. Point it at any other CLI:
make install CODE=bobide # IBM Bob IDE
make install CODE=cursor # Cursor
make install CODE=code-insidersHacking on it
npm ci # once
make check # type-check + lint + the test suiteThen press F5 for an Extension Development Host with live reloading — no packaging step.
make on its own lists every target.
A coding agent that pauses for approval and gets no answer is not safe — it is stopped, or it is waved through. Session Sitter is the layer that answers, in writing, and keeps the receipt.
| Without it | With it | |
|---|---|---|
| An overnight run | stalls at the first prompt — or, in a session that cannot prompt, is silently denied — unless you run under --dangerously-skip-permissions and hope |
keeps going on the actions your rules already allow, and a denial says which clause denied it |
| A safe, boring prompt | you approve read_file for the ninetieth time |
resolved by rule, before any model call |
| A compound command † | Bash(git:*) never matches git add x && git commit -m y — and a deny rule you wrote is walked past the same way |
every part of the line is judged, and the most restrictive verdict wins |
| "Always allow", clicked † | the literal command line is saved, so the next near-identical one asks again | the approval is generalised from the practice that justified it |
| An unsafe action | approved, or blocked with nothing to act on | blocked, and the agent is handed the safe alternatives instead |
| A judgment call | it waits until you look | a decision card on your phone with a countdown — and on timeout it is denied, never approved |
| "What did it actually do?" | scroll four transcripts | one durable JSON record per decision, plus the activity feed |
| An agent stuck in another window | you find out when you get there | it is in this window's worklist, marked as waiting on you |
| A session on another machine | you go and look for it | it is in the same list, one click to focus the window that owns it |
† These two are in the open pull requests behind the Claude Code plugin, not in the released extension — same status as the plugin itself.
Coding agents do not stop when you close the laptop. Supervision is for the moments you are not there: it classifies each action an agent pauses on, against your own written practices, and acts.
The deterministic tier is what keeps a governance layer off the critical path of every read, and the two timeout edges are the whole principle: an unanswered card is a denial.
Every one of those decisions lands in the activity feed, saying which light it was, who decided — 🧠 AI or ⚙ rule — and what the agent was actually asked to do:
One card per decision. An orange card names its options and its countdown; a red one says why it stands.
Everything is a setting — no environment variables to maintain, no .env. Open
☰ → All settings… in the panel, or search sessionSitter in the Settings UI. There is nothing
to run: the supervisor lives inside the extension, with no daemon and no interpreter.
→ docs/SUPERVISION.md — turning it on, the lifecycle, the CLI, troubleshooting.
→ docs/CONFIGURATION.md — every setting, flag and command.
→ docs/KNOWLEDGE.md — the practices files the classifier reads.
Your active sessions as topics in a Telegram forum group. The thread you type in is the session you are talking to, so there is no mode to get wrong.
GROUP "Session Sitter" (Topics enabled)
# General ← the active list, /sessions /history /new /who /help
# 🟠 sitter / sort order · claude 2
# 🔄 payments / refund flow · bob
# ⚪ scratch / spike · codex@laptop2
The group holds the same sessions the Sessions panel does — one shared rule, so the two cannot
disagree — and every name reads status, workspace, title, then the agent and the machine. A session
that leaves the active list has its topic closed, keeping its scrollback. /history reaches the rest:
tap a row to open its topic and bring the session back.
Open a topic and you get that session's turns as they happen, its supervision cards, a Full transcript upload, Focus in IDE, and a text box that sends straight into the agent.
Reading works for all four agents. Writing works for Bob — any task, live or historical — and for Claude sessions open in their own window. Codex and VS Code Chat expose no message API, so their topics say they are read-only rather than dropping what you type.
Turning it on, once per machine:
plus a group with Topics enabled, a bot with privacy mode disabled (otherwise it cannot see what
you type in a topic), and TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID in your environment or .env.
Two things to know before you start:
allowedUserIdsis empty by default, and empty authorises nobody. A group is not a private chat, and acting on a message means typing into a live coding agent.- Use one bot per machine. A bot token has a single message stream and reading it is destructive, so two machines sharing one steal each other's messages. Add each machine's bot to the same group; the fleet view is the union.
Full walk-through, including the failure table: → docs/TELEGRAM.md
Leave Auto mode on. It is a better default than approving by reflex. What it does not do is the part that has to be yours, because the policy is yours:
| Claude Code, first-party | Session Sitter | |
|---|---|---|
| Who decides | Auto mode, with Anthropic's judgment | your team's written practices |
| What you are told | Blocked by classifier |
the clause it applied — denied — practices §4: never force-push to a shared branch |
| An unsafe call | blocked | rewritten into the safe one — git push --force → --force-with-lease — so the run continues |
| Afterwards | nothing to hand anyone | one durable JSONL record per decision, queryable |
| Unattended | a session that cannot prompt denies silently | a standing policy decides, and deny-on-timeout is explicit |
| Scope | Claude Code, one machine (Agent view is local by design) | Claude Code, IBM Bob, Codex, VS Code Chat — unioned across windows and peer machines |
Both layers run at once, and the supervision half of this extension is off until you turn it on.
- Practices as policy, with the clause cited — every decision names the rule it applied, not a fixed string.
- Deny on timeout, always — an escalated decision with no answer is denied, and the denial says which clause denied it. Silence never writes an approval, and never stalls a run without a reason.
- A durable record per decision — which light, who decided (🧠 AI or ⚙ rule), what was asked, what happened. JSON on disk, plus an activity feed that expands failures to their recorded error.
- A deterministic tier in front of the classifier — read-only actions never cost a model call, so governance stays off the critical path.
- The correction lane — an unsafe call is rewritten into the safe one and re-checked against your deny rules, so a blocked agent gets a way forward instead of a wall.
- Four agents, one policy — Claude Code, IBM Bob IDE, Codex CLI, VS Code Chat, by reading only what they already write to disk. → how
- Across windows and across machines — peers are discovered from the remote windows your IDE already opened and probed over SSH, with nothing to install on the far side. One click focuses the window that owns a session, even on another box.
- Six live states, not three — working, waiting for your approval, waiting for your answer, finished-unread, finished-read, dormant. An agent that is stuck is not an agent that is busy.
- Copy transcript as handoff-clean markdown — prose only, tool calls stripped, all four sources.
- Telegram remote control (off by default) — every session becomes a topic in a Telegram forum group; read it, and type into it, from your phone. → above
You already have a coding agent. It can do the setup, and it can tell you why a setting is not
taking effect — docs/onboarding/ is an Agent Skill for exactly that.
Read docs/onboarding/SKILL.md and set up Session Sitter for me.
It walks the configuration in six layers, from the session panel (which needs nothing) up to the fast classifier, asks about each, and validates what it wrote — because VS Code ignores an unrecognised setting key silently, so an invented id gives you a configuration that looks complete and does nothing. The same skill reconfigures an existing install and reviews one:
Turn on Telegram decision cards.
Why isn't my auto-approve rule firing?
Review my Session Sitter configuration.
To have it in every Claude Code session, copy it into your skills directory:
mkdir -p ~/.claude/skills/configuring-session-sitter
cp -r docs/onboarding/* ~/.claude/skills/configuring-session-sitter/The validator runs on its own too — no dependencies, no network, and it writes nothing:
node docs/onboarding/scripts/ss-config.mjs where # which settings.json is the live one
node docs/onboarding/scripts/ss-config.mjs check # what is configured, and what is brokenwhere matters more than it sounds: on WSL and on a remote IDE several settings.json files exist
and only one is read, which is the commonest reason a change appears to do nothing.
Not using an agent? docs/onboarding/SKILL.md reads as a getting-started
guide, and docs/CONFIGURATION.md is the full reference.
Open the Secondary Sidebar — Ctrl+Alt+B, or View → Secondary Side Bar. The
Session Sitter panel is there. Open a Claude or Bob session and it shows up within seconds.
| I want to… | Do this |
|---|---|
| Switch to a session | Click the row |
| Close its tab | Click × on the row |
| Start a new session | Click + (Claude) or +B (Bob) |
| Peek at the conversation | Hover a row |
| Reorder the list | Click ⇅ — recency, machine, workspace, agent, title, or needs-you-first |
| Colour a project | ☰ → Settings — one colour per workspace, or auto for all of them |
| See older sessions | Click History ▶ |
| Copy a transcript | Right-click → Copy transcript → editor / clipboard / file |
| Open About or Settings | Click ☰ |
The main list is a live worklist — only sessions you can act on right now. Claude and Bob are judged by what their extension hosts report as open, unioned across every window, so a session open in another window still appears here. Codex and Chat expose no such signal, so they count as active while recently updated. Everything else moves to History.
Each row carries a status marker — six states, distinguished by shape as well as colour, so they survive a screenshot and a colour-blind reader: working · waiting for your approval · waiting for your answer · finished-unread · finished-read · dormant. Titles come from each agent's own naming — Claude's generated title, Bob's task title, Codex's thread name, Chat's first request — and the non-recency sort orders hold rows still while sessions update, so you do not lose your place.
Needs-you-first ordering · hovering a row previews the conversation
| VS Code or IBM Bob IDE | 1.65 or later |
| Linux, WSL or macOS | for Claude liveness detection — /proc/<pid>/stat on Linux, ps elsewhere |
python3 |
to read IBM Bob's SQLite store. Only needed for Bob sessions. |
| Node 20+ | only to build from source |
Every document is indexed in docs/README.md. The short version:
| Document | What it covers |
|---|---|
docs/onboarding/ |
the Agent Skill that configures all of it: the interview, seven worked configurations, and the validator |
docs/ARCHITECTURE.md |
components, session detection, cross-machine, the supervision layer, the agent bridges |
docs/STATUS-INDICATORS.md |
the six row markers, and the rules that pick one — per agent |
docs/CLI.md |
the session-sitter terminal command: status, log, digest, policy check |
docs/SUPERVISION.md |
the traffic lights, the lifecycle, the CLI, troubleshooting |
docs/TELEGRAM.md |
the remote interface: setup, ownership, write limits, troubleshooting |
docs/KNOWLEDGE.md |
the BDI schema, the three tiers, routing |
docs/CORPUS.md |
collecting sessions, bulk import, secret masking |
docs/CONFIGURATION.md |
every setting, environment variable, flag and command |
docs/PLUGIN.md |
the Claude Code plugin: install, the hooks, the practices format, the audit trail |
- On Windows, Claude liveness loses its recycled-PID guard — Linux and WSL cross-check the PID
against
/proc/<pid>/stat, macOS againstps -o lstart=. With neither available thekill(pid, 0)signal alone decides, so sessions still list but a recycled PID could in principle look live. python3is required for Bob sessions — a VS Code extension has no SQLite driver, and a native module would break VSIX portability. Confined to one file, read-only. → why- Claude message injection cannot always pick a session — the sessionId↔channel link is searched for at send time and is not exposed by every Claude build. When a window has several Claude sessions open and none matches, nothing is sent and you are told: delivering a prompt to the wrong agent is worse than not delivering it.
- Supervision needs a classifier CLI —
boborclaudeon yourPATH. - Telegram remote control needs a bot per machine — a bot token has one destructive message
stream, so machines cannot share one. →
docs/TELEGRAM.md - Telegram cannot write to Codex or VS Code Chat sessions — neither exposes a message API. Their topics are read-only and say so.
- Peer visibility is one-way — a machine behind NAT with no reachable sshd can see its peers and will not be seen by them. Unreachable peers are named as unreachable rather than shown empty.
- No motion demo yet — the screenshots are real; the 20-second GIF of a correction being made
needs a machine with a screen. Shot list:
docs/screenshots/README.md.
The per-agent liveness caveats behind these — when a new Claude session first appears, what Bob can
and cannot report about its sidebar, and why Codex and Chat fall back to recency — are in
docs/ARCHITECTURE.md.
make check — type-check, lint and tests, the same gate CI applies. Then make on its own lists
every target. Issues and pull requests welcome; the guards, the TypeScript-only rule and the
release process are in CONTRIBUTING.md. For a vulnerability please use the
private path in SECURITY.md rather than an issue.
MIT

{ "sessionSitter.telegram.remoteControl": true, "sessionSitter.telegram.allowedUserIds": ["123456789"] }