···6677| tool | description | params=[default] |
88| ---------- | ----------------------------------- | --------------------------------------------------- |
99-| `list` | list all sessions | `dir=[pwd]` constraint |
99+| `list` | list all sessions | `dir=[pwd]` constraint, `active=[false]` |
1010| `call` | send a request or message | `session=[new]`, `elaborate=[true]`, `fork=[false]` |
1111| `response` | multitool for queues responses, get | `get=[top]`, `list=[false]` |
+291
doc/PLAN-sense-active.md
···11+# Sensing Active OpenCode Instances and Sessions
22+33+## Goal
44+55+Detect all running OpenCode instances and identify which sessions are currently active/busy in order to manage agent lifecycle and avoid conflicts.
66+77+## Detection Strategy
88+99+### Priority Order
1010+1111+1. **mDNS Discovery** (Preferred - if enabled)
1212+2. **Process Detection** (Fallback - parse running processes)
1313+3. **Port Scanning** (Last resort - scan localhost)
1414+1515+---
1616+1717+## 1. mDNS Discovery
1818+1919+OpenCode can broadcast itself via mDNS/Bonjour when configured.
2020+2121+### Detection
2222+2323+- Query mDNS for `_opencode._tcp` service
2424+- Each response provides:
2525+ - Hostname/IP
2626+ - Port number
2727+ - Service name (can be used to identify instance)
2828+2929+### Prerequisites
3030+3131+- OpenCode must have `mdns: true` in config OR `--mdns` flag enabled
3232+- This is off by default
3333+3434+### Advantages
3535+3636+- Clean, standard service discovery
3737+- Multiple instances on different hosts can be discovered
3838+- No special permissions needed
3939+4040+### Disadvantages
4141+4242+- Disabled by default, so won't work in most scenarios
4343+- Requires mDNS library (e.g., `bonjour-service`, `mdns-js`, `node-dnssd`)
4444+4545+---
4646+4747+## 2. Process Detection
4848+4949+Parse `/proc` entries on Linux to find OpenCode processes.
5050+5151+### Detection Steps
5252+5353+1. List all processes in `/proc`
5454+2. Find processes matching OpenCode (e.g., `bun opencode`, `node opencode`)
5555+3. For each matching process:
5656+ - Read `/proc/[PID]/cmdline` to get full command line
5757+ - Parse `--port <N>` argument to extract port
5858+ - Parse working directory from `/proc/[PID]/cwd`
5959+ - Parse `--dir` or directory from `/proc/[PID]/environ`
6060+6161+### Information Extracted
6262+6363+| Process ID | Port | Working Directory | Args |
6464+| ---------- | ---- | ---------------- | ---- |
6565+| 12345 | 4096 | /home/user/project | --model claude-3-5 |
6666+6767+### Prerequisites
6868+6969+- Linux system with `/proc` filesystem
7070+- Read access to `/proc` entries
7171+7272+### Advantages
7373+7474+- Works even if mDNS is disabled
7575+- Extracts port and working directory directly
7676+- No network overhead
7777+7878+### Disadvantages
7979+8080+- Linux-specific (won't work on macOS/Windows)
8181+- Requires reading `/proc` (may fail with restricted permissions)
8282+- Assumes port is in command line args (could be in config file)
8383+8484+---
8585+8686+## 3. Port Scanning
8787+8888+Scan localhost for HTTP servers that respond like OpenCode.
8989+9090+### Detection Steps
9191+9292+1. Scan ports `4096-65535` (or configurable range)
9393+2. For each open port:
9494+ - Send `GET /api/session/status` request
9595+ - If response is JSON with status info, it's an OpenCode instance
9696+3. Extract instance info:
9797+ - Port number
9898+ - Session statuses from `/api/session/status` endpoint
9999+100100+### Information Extracted
101101+102102+| Port | Session ID | Status |
103103+| ---- | ---------- | ------ |
104104+| 4096 | ses_abc123 | busy |
105105+| 5173 | ses_def456 | idle |
106106+107107+### Prerequisites
108108+109109+- OpenCode server running and accessible via HTTP
110110+111111+### Advantages
112112+113113+- Works on any OS
114114+- Directly retrieves session status (no parsing needed)
115115+- Can detect sessions across multiple instances
116116+117117+### Disadvantages
118118+119119+- Slow to scan many ports
120120+- Only finds instances with HTTP server running
121121+- May miss instances bound to specific IPs (not 0.0.0.0 or 127.0.0.1)
122122+123123+---
124124+125125+## 4. Additional Fallbacks (Lower Priority)
126126+127127+### Lock Files
128128+129129+- Check for lock files in `~/.local/share/opencode/` or `$XDG_RUNTIME_DIR/opencode/`
130130+- Format: `opencode-<port>.lock` containing PID
131131+- Remove after checking if process still exists
132132+133133+### Unix Domain Sockets
134134+135135+- Check for sockets in `$XDG_RUNTIME_DIR/opencode/`
136136+- Can query socket for status
137137+138138+### Systemd/User Service Status
139139+140140+- Check `systemctl --user status opencode*` for running services
141141+- Extract port from unit file or ExecStart command
142142+143143+### PID Files
144144+145145+- Check for PID files in standard locations
146146+- Use `systemd-run` or similar to manage instance lifecycle
147147+148148+---
149149+150150+## Session Status API
151151+152152+Once an instance is detected, query it for session activity.
153153+154154+### Endpoint
155155+156156+```
157157+GET /api/session/status
158158+```
159159+160160+### Response Format
161161+162162+```json
163163+{
164164+ "ses_abc123": {
165165+ "type": "busy"
166166+ },
167167+ "ses_def456": {
168168+ "type": "idle"
169169+ },
170170+ "ses_ghi789": {
171171+ "type": "retry",
172172+ "attempt": 3,
173173+ "message": "Connection error",
174174+ "next": 1737789123456
175175+ }
176176+}
177177+```
178178+179179+### Status Types
180180+181181+| Type | Meaning |
182182+| ---- | ------- |
183183+| `idle` | Session not actively processing |
184184+| `busy` | Session currently processing a prompt |
185185+| `retry` | Session encountered error and retrying (includes attempt count and message) |
186186+187187+---
188188+189189+## Implementation Plan
190190+191191+### Phase 1: Basic Detection
192192+193193+1. Implement process detection (Linux `/proc` parsing)
194194+2. Implement port scanning with `/api/session/status` check
195195+3. Return list of instances: `{ pid, port, cwd, sessions[] }`
196196+197197+### Phase 2: mDNS Support
198198+199199+1. Add mDNS discovery library
200200+2. Query for `_opencode._tcp` services
201201+3. Merge with other discovery methods
202202+203203+### Phase 3: Cross-Platform
204204+205205+1. Add macOS process detection (using `ps` command)
206206+2. Add Windows process detection (using `wmic` or PowerShell)
207207+3. Use lock files as universal fallback
208208+209209+### Phase 4: API Client
210210+211211+1. Create simple HTTP client to query OpenCode API
212212+2. Add helper: `getActiveSessions(port)` → returns `sessionID[]` with `type: "busy"`
213213+3. Add helper: `getSessionStatus(port, sessionID)` → returns session details
214214+215215+---
216216+217217+## Configuration
218218+219219+Users can configure discovery:
220220+221221+```json
222222+{
223223+ "discovery": {
224224+ "methods": ["mdns", "process", "port"],
225225+ "portRange": [4096, 65535],
226226+ "timeout": 5000,
227227+ "scanInterval": 30000
228228+ }
229229+}
230230+```
231231+232232+### Options
233233+234234+- `methods`: List of discovery methods to try, in priority order
235235+- `portRange`: Port range to scan for port-based discovery
236236+- `timeout`: Timeout for HTTP requests to instances (ms)
237237+- `scanInterval`: How often to re-scan for instances (ms)
238238+239239+---
240240+241241+## Use Cases
242242+243243+### Finding an idle session to attach to
244244+245245+```typescript
246246+const instances = await discoverOpenCodeInstances()
247247+for (const instance of instances) {
248248+ const sessions = await getInstanceSessions(instance.port)
249249+ const idle = sessions.filter(s => s.status.type === 'idle')
250250+ if (idle.length > 0) {
251251+ return { instance, session: idle[0] }
252252+ }
253253+}
254254+// Create new session if none available
255255+```
256256+257257+### Checking if a session is busy before sending commands
258258+259259+```typescript
260260+const instance = await discoverInstanceAtPort(4096)
261261+const status = await getSessionStatus(instance.port, 'ses_abc123')
262262+if (status.type === 'busy') {
263263+ console.log('Session is busy, waiting...')
264264+ await waitForIdle(instance.port, 'ses_abc123')
265265+}
266266+```
267267+268268+### Listing all active sessions across all instances
269269+270270+```typescript
271271+const instances = await discoverOpenCodeInstances()
272272+const allSessions: Array<{sessionID: string, port: number, status: string}> = []
273273+274274+for (const instance of instances) {
275275+ const sessions = await getInstanceSessions(instance.port)
276276+ for (const [id, status] of Object.entries(sessions)) {
277277+ allSessions.push({ sessionID: id, port: instance.port, status: status.type })
278278+ }
279279+}
280280+281281+console.table(allSessions.filter(s => s.status === 'busy'))
282282+```
283283+284284+---
285285+286286+## Notes
287287+288288+- Session status is **in-memory only** - lost when server restarts
289289+- Multiple OpenCode instances can run simultaneously on different ports
290290+- Default port is `4096` but can be auto-assigned if `0` is specified
291291+- mDNS uses hostname `0.0.0.0` when enabled, making it discoverable on LAN