Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ opensessions/
│ │ │ │ ├── amp.ts
│ │ │ │ ├── claude-code.ts
│ │ │ │ ├── codex.ts
│ │ │ │ └── opencode.ts
│ │ │ │ ├── devin.ts
│ │ │ │ ├── opencode.ts
│ │ │ │ └── pi.ts
│ │ │ ├── mux/ # Mux registry and detection helpers
│ │ │ ├── server/ # WebSocket server internals and launcher
│ │ │ ├── shared.ts # Shared types, constants, palette
Expand All @@ -47,7 +49,7 @@ opensessions/
## Key Architecture Decisions

1. **Monorepo**: Turborepo + Bun workspaces, with `apps/` for runnable entrypoints and `packages/` for reusable libraries.
2. **Built-in agent watchers**: Core ships with `AmpAgentWatcher`, `ClaudeCodeAgentWatcher`, `CodexAgentWatcher`, and `OpenCodeAgentWatcher` that watch agent data directories directly. External agents integrate via the `AgentWatcher` plugin interface.
2. **Built-in agent watchers**: Core ships with `AmpAgentWatcher`, `ClaudeCodeAgentWatcher`, `CodexAgentWatcher`, `DevinAgentWatcher`, `OpenCodeAgentWatcher`, and `PiAgentWatcher` that watch agent data directories directly. External agents integrate via the `AgentWatcher` plugin interface.
3. **Mux-agnostic**: `MuxProvider` interface abstracts all mux operations. `TmuxProvider` is the reference implementation.
4. **MuxProvider is SYNC**: All methods use `Bun.spawnSync` — matches the existing pattern and keeps the server simple.
5. **Auto-detect mux**: `detectMux()` checks `$TMUX`, `$ZELLIJ_SESSION_NAME` env vars. Config file override planned.
Expand Down Expand Up @@ -99,7 +101,7 @@ interface AgentWatcher {
- **Sync mux calls**: MuxProvider methods are synchronous. Don't make them async.
- **Preserve optimizations**: Batched tmux calls, 5s git cache with HEAD watchers, lightweight focus-only broadcasts.
- **Sidebar resize work**: Before changing sidebar spawning, width sync, tmux resize handling, or `sidebar-coordinator`, read `docs/explanation/sidebar-behavior.md` and preserve those invariants unless you update the doc in the same change.
- **Built-in watchers in runtime**: Amp, Claude Code, Codex, and OpenCode have built-in watchers in `packages/runtime/src/agents/watchers/`. Community agents use the `AgentWatcher` plugin interface.
- **Built-in watchers in runtime**: Amp, Claude Code, Codex, Devin, OpenCode, and Pi have built-in watchers in `packages/runtime/src/agents/watchers/`. Community agents use the `AgentWatcher` plugin interface.
- **OpenTUI Solid**: JSX needs `bunfig.toml` preload and `jsxImportSource: "@opentui/solid"` in tsconfig. Build needs `solidPlugin`.
- **Never call `process.exit()` directly in TUI**: Use `renderer.destroy()`.

Expand Down
23 changes: 22 additions & 1 deletion CONTRACTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ For end-user setup, start with the docs linked from [README.md](./README.md). Fo

## Built-In Watchers

opensessions currently registers four built-in watchers at server startup.
opensessions currently registers six built-in watchers at server startup.

### Amp

Expand All @@ -32,13 +32,34 @@ opensessions currently registers four built-in watchers at server startup.
- Resolves mux sessions from `turn_context.cwd` inside the transcript.
- Treats `user_message`, tool activity, and assistant `commentary` as `running`, assistant `final_answer` and `task_complete` as `done`, and `turn_aborted` as `interrupted`.

### Devin

- Polls `~/.local/share/devin/cli/sessions.db` or `$DEVIN_CLI_DB_PATH`.
- Uses `bun:sqlite` in read-only mode.
- Polls every 3 seconds.
- Skips sessions whose `last_activity_at` is older than 5 minutes (timestamps are stored in seconds).
- Resolves mux sessions from the Devin session row's `working_directory` field.
- Derives status from the head node referenced by `main_chain_id` in the `message_nodes` tree:
- `role=assistant` + `finish_reason=stop` → `done`
- `role=assistant` + `finish_reason=tool_calls` → `running`
- `role=user` / `role=tool` / streaming assistant → `running`
- `role=system` with content beginning `[Response interrupted by user]` → `interrupted`
- Promotes `running` → `stale` when `last_activity_at` does not advance for 15 seconds (assumed process death).

### OpenCode

- Polls `~/.local/share/opencode/opencode.db` or `$OPENCODE_DB_PATH`.
- Uses `bun:sqlite` in read-only mode.
- Polls every 3 seconds.
- Resolves mux sessions from the OpenCode session row's `directory` field.

### Pi

- Watches `~/.pi/agent/sessions/<encoded-path>/<timestamp>_<id>.jsonl`.
- Uses recursive `fs.watch` plus a 2 second polling pass.
- Skips stale transcript files older than 5 minutes.
- Resolves mux sessions from the `cwd` recorded on the `session` header entry.

## Agent Model

### `AgentStatus`
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Then remove the `set -g @plugin 'Ataraxy-Labs/opensessions'` line from `~/.tmux.

## Today

- Live agent state across sessions for Amp, Claude Code, Codex, and OpenCode.
- Live agent state across sessions for Amp, Claude Code, Codex, Devin, OpenCode, and Pi.
- Per-thread unseen markers for `done`, `error`, and `interrupted` states.
- Session context in the UI: branch in the list, working directory in the detail panel, thread names, and detected localhost ports.
- Programmatic metadata API: agents and scripts push status, progress, and logs to the sidebar via HTTP.
Expand Down Expand Up @@ -144,7 +144,9 @@ For the full tmux workflow with keybindings, troubleshooting, and configuration
- Amp watcher reads `~/.local/share/amp/threads/*.json` and clears unseen state from Amp's `session.json` when a thread becomes seen there.
- Claude Code watcher reads JSONL transcripts in `~/.claude/projects/`.
- Codex watcher reads transcript JSONL files in `~/.codex/sessions/` or `$CODEX_HOME/sessions/` and resolves sessions from `turn_context.cwd`.
- Devin watcher polls the SQLite database in `~/.local/share/devin/cli/sessions.db` (override with `DEVIN_CLI_DB_PATH`) and resolves sessions from each row's `working_directory`.
- OpenCode watcher polls the SQLite database in `~/.local/share/opencode/opencode.db`.
- Pi watcher reads JSONL transcripts in `~/.pi/agent/sessions/`.
- Hidden sidebars are stashed in a tmux session named `_os_stash`, so they can come back without restarting the sidebar process.
- Clicking a detected port opens `http://localhost:<port>`.

Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
AmpAgentWatcher,
ClaudeCodeAgentWatcher,
CodexAgentWatcher,
DevinAgentWatcher,
OpenCodeAgentWatcher,
PiAgentWatcher,
PluginLoader,
Expand Down Expand Up @@ -66,6 +67,7 @@ if (extraProviders.length > 0) {
loader.registerWatcher(new AmpAgentWatcher());
loader.registerWatcher(new ClaudeCodeAgentWatcher());
loader.registerWatcher(new CodexAgentWatcher());
loader.registerWatcher(new DevinAgentWatcher());
loader.registerWatcher(new OpenCodeAgentWatcher());
loader.registerWatcher(new PiAgentWatcher());

Expand Down
2 changes: 1 addition & 1 deletion docs/explanation/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ If no healthy server is listening on `127.0.0.1:7391`, `ensureServer()` launches
2. dynamically registers the built-in mux providers from `@opensessions/mux-tmux` and `@opensessions/mux-zellij`
3. loads local plugins and configured package plugins
4. resolves the primary mux provider
5. registers the built-in Amp, Claude Code, Codex, and OpenCode watchers
5. registers the built-in Amp, Claude Code, Codex, Devin, OpenCode, and Pi watchers
6. starts the WebSocket and HTTP control server

## State Assembly
Expand Down
1 change: 1 addition & 0 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ All other tmux options fall back to the defaults shown in the table above.

| Variable | Used by | Notes |
| --- | --- | --- |
| `DEVIN_CLI_DB_PATH` | Devin watcher | Overrides the default Devin CLI SQLite path |
| `OPENCODE_DB_PATH` | OpenCode watcher | Overrides the default SQLite path |
| `OPENSESSIONS_DIR` | tmux helper scripts and server | Helps helper scripts find the repo checkout |
| `OPENSESSIONS_HOST` | helper shell scripts | Script-level override only; the app runtime still uses `127.0.0.1` |
Expand Down
Loading