-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Closed
Labels
bugSomething isn't workingSomething isn't workingperfIndicates a performance issue or need for optimizationIndicates a performance issue or need for optimization
Description
Description
The ACP adapter creates a new event subscription on every newSession() and loadSession() call, rather than using a single subscription like the TUI/Desktop does. This causes two bugs:
- Cross-session pollution - Events from session B are sent to session A
- Duplicate events - Loading the same session N times causes N× duplicate events
OpenCode version
v1.0.162
Steps to reproduce
Case 1 - Duplicate events from loadSession
Load the same session multiple times, send one prompt. Events are duplicated N× (one per subscription).
// repro-duplicates.ts - Run: bun repro-duplicates.ts
import { spawn } from 'node:child_process'
import { Readable, Writable } from 'node:stream'
import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk'
const events: string[] = []
await Bun.write('/tmp/ws/test.txt', 'hello')
const proc = spawn('opencode', ['acp'], { stdio: ['pipe', 'pipe', 'inherit'], cwd: '/tmp/ws' })
const conn = new ClientSideConnection(() => ({
async sessionUpdate(p) { events.push(p.update?.sessionUpdate) },
async requestPermission(p) { return { outcome: { outcome: 'selected', optionId: p.options[0]?.optionId } } },
async readTextFile() {}, async writeTextFile() {}, async createTerminal() {},
async terminalOutput() {}, async killTerminal() {}, async releaseTerminal() { return {} },
}), ndJsonStream(Writable.toWeb(proc.stdin!), Readable.toWeb(proc.stdout!)))
await conn.initialize({ protocolVersion: 1, clientCapabilities: {}, clientInfo: { name: 't', version: '1' } })
const { sessionId } = await conn.newSession({ cwd: '/tmp/ws', mcpServers: [] })
for (let i = 0; i < 4; i++) await conn.loadSession({ sessionId, cwd: '/tmp/ws', mcpServers: [] })
// Now have 5 subscriptions
events.length = 0
await conn.prompt({ sessionId, prompt: [{ type: 'text', text: 'Read test.txt' }] })
await new Promise(r => setTimeout(r, 2000))
console.log('tool_call events:', events.filter(e => e === 'tool_call').length)
proc.kill()
Expected: tool_call events: 1
Actual: tool_call events: 5 (one per subscription)
Case 2 - Cross-session pollution
Create two sessions in the same directory, prompt only session B. Session A incorrectly receives B's events.
// repro-cross-session.ts - Run: bun repro-cross-session.ts
import { spawn } from 'node:child_process'
import { Readable, Writable } from 'node:stream'
import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk'
let idA = '', idB = ''
const eventsA: string[] = [], eventsB: string[] = []
const proc = spawn('opencode', ['acp'], { stdio: ['pipe', 'pipe', 'inherit'], cwd: '/tmp/ws' })
const conn = new ClientSideConnection(() => ({
async sessionUpdate(p) {
if (p.sessionId === idA) eventsA.push(p.update?.sessionUpdate)
else if (p.sessionId === idB) eventsB.push(p.update?.sessionUpdate)
},
async requestPermission(p) { return { outcome: { outcome: 'selected', optionId: p.options[0]?.optionId } } },
async readTextFile() {}, async writeTextFile() {}, async createTerminal() {},
async terminalOutput() {}, async killTerminal() {}, async releaseTerminal() { return {} },
}), ndJsonStream(Writable.toWeb(proc.stdin!), Readable.toWeb(proc.stdout!)))
await conn.initialize({ protocolVersion: 1, clientCapabilities: {}, clientInfo: { name: 't', version: '1' } })
idA = (await conn.newSession({ cwd: '/tmp/ws', mcpServers: [] })).sessionId
idB = (await conn.newSession({ cwd: '/tmp/ws', mcpServers: [] })).sessionId
eventsA.length = 0; eventsB.length = 0
await conn.prompt({ sessionId: idB, prompt: [{ type: 'text', text: 'hi' }] })
await new Promise(r => setTimeout(r, 2000))
console.log('Session A events:', eventsA.length) // BUG: Should be 0
console.log('Session B events:', eventsB.length)
proc.kill()
Expected: Session A events: 0
Actual: Session A events: 3 (receives session B's events)
Screenshot and/or share link
No response
Operating System
macOS 15.6.1
Terminal
zsh
RAIT-09
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingperfIndicates a performance issue or need for optimizationIndicates a performance issue or need for optimization