[FEATURE]:Support sessionID Filter for SSE Event Subscription #6964

Open
opened 2026-02-16 18:05:44 -05:00 by yindo · 1 comment
Owner

Originally created by @jf0606 on GitHub (Jan 20, 2026).

Originally assigned to: @thdxr on GitHub.

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

[Feature Request] Support sessionID Filter for SSE Event Subscription

Summary

Add support for sessionID query parameter in the /event SSE endpoint to allow clients to subscribe to events for specific sessions only, rather than receiving all events and filtering client-side.

Problem Statement

Currently, the /event endpoint broadcasts all events to all subscribers. Clients must filter events by sessionID on their own. This design causes significant issues in multi-session and multi-node deployment scenarios.

Current Behavior

GET /event?directory=/path/to/project

Returns: ALL events from all sessions in that directory.

Evidence from Source Code

Even OpenCode's internal code filters by sessionID on the client side:

packages/opencode/src/tool/task.ts:111-112

const unsub = Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
  if (evt.properties.part.sessionID !== session.id) return  // Client-side filtering
  // ...
})

packages/opencode/src/cli/cmd/github.ts:840-841

Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
  if (evt.properties.part.sessionID !== session.id) return  // Client-side filtering
  // ...
})

packages/opencode/src/server/server.ts:475

const unsub = Bus.subscribeAll(async (event) => {  // Subscribes to ALL events
  await stream.writeSSE({
    data: JSON.stringify(event),
  })
})

Use Cases

1. Cloud SaaS Deployment

                    ┌─────────────────────────────────┐
                    │      Load Balancer (Nginx)      │
                    └───────────────┬─────────────────┘
                ┌───────┬───────────┼───────────┬───────┐
                ▼       ▼           ▼           ▼       ▼
            Backend Backend    Backend    Backend Backend
            Node 1  Node 2     Node 3     Node 4  Node 5
                └───────┴───────────┴───────────┴───────┘
                                    │
                                    ▼
                            OpenCode Server

Problems:

  • Each backend node needs its own SSE connection
  • All nodes receive ALL events (massive bandwidth waste)
  • Each node must filter events for its sessions
  • Linear scaling of connections and data transfer

2. Multi-Session Web IDE

A web-based IDE with multiple sessions open simultaneously:

// User has 3 sessions open
// Current: Must subscribe once and filter ALL events
const events = await fetch('/event?directory=/project');

// For each event, check which session it belongs to
if (event.properties.part.sessionID === 'ses_session1') { /* update UI 1 */ }
if (event.properties.part.sessionID === 'ses_session2') { /* update UI 2 */ }
if (event.properties.part.sessionID === 'ses_session3') { /* update UI 3 */ }

3. Backend Service (Telegram Bot, Slack Bot, etc.)

As described in Issue #6573:

A Node.js service uses the @opencode-ai/sdk to interact with OpenCode via REST API, polling session status and streaming responses back to users. Sessions hang indefinitely when the LLM uses the Task tool to spawn subagents, because subagent events come through the same subscription.

Proposed Solution

Option A: Add sessionID Query Parameter (Recommended)

GET /event?sessionID=ses_xxx
GET /event?directory=/path/to/project&sessionID=ses_xxx

Benefits:

  • Simple and straightforward
  • Backward compatible (existing /event behavior unchanged)
  • Reduces bandwidth waste significantly
  • Enables proper multi-node deployment

Implementation Suggestion:

// packages/opencode/src/server/server.ts

.get("/event", async (c) => {
  const sessionID = c.req.query("sessionID");

  return streamSSE(c, async (stream) => {
    stream.writeSSE({
      data: JSON.stringify({ type: "server.connected", properties: {} }),
    });

    const unsub = Bus.subscribeAll(async (event) => {
      // If sessionID filter is specified, only send matching events
      if (sessionID) {
        const eventSessionID =
          event.properties?.part?.sessionID ||
          event.properties?.info?.sessionID ||
          event.properties?.info?.id ||
          event.properties?.sessionID;

        if (eventSessionID && eventSessionID !== sessionID) {
          return;  // Skip events for other sessions
        }
      }

      await stream.writeSSE({ data: JSON.stringify(event) });
    });

    // ... rest of the handler
  });
})

Option B: Support Multiple Session Subscription

GET /event?sessionIDs=ses_xxx,ses_yyy,ses_zzz

Option C: WebSocket with Dynamic Subscription

const ws = new WebSocket('/ws/events');

// Subscribe to specific sessions
ws.send(JSON.stringify({
  type: 'subscribe',
  sessionIDs: ['ses_xxx', 'ses_yyy']
}));

// Dynamically add/remove subscriptions
ws.send(JSON.stringify({
  type: 'unsubscribe',
  sessionIDs: ['ses_xxx']
}));

Performance Impact

Current (Without sessionID Filter)

With 100 concurrent sessions and 10 backend nodes:

Metric Value
SSE Connections 10 (one per node)
Events Transferred 10x (every event sent to all nodes)
Client CPU High (filtering 100 sessions worth of events)
Network Bandwidth 10x waste

Proposed (With sessionID Filter)

Metric Value
SSE Connections 100 (one per session, distributed across nodes)
Events Transferred 1x (each event sent once to correct subscriber)
Client CPU Minimal (no filtering needed)
Network Bandwidth Optimal

Related Issues

Current Workaround

We currently use Redis Pub/Sub as middleware to handle session-specific event routing:

OpenCode Server
    ↓ (1 SSE connection)
Dedicated Subscriber Service
    ↓ (Parse sessionID, publish to channel)
Redis Pub/Sub (channel per session)
    ↓ (Subscribe by channel)
Backend Nodes (N instances)

This works but adds significant architectural complexity and operational overhead.

Environment

  • OpenCode Version: 1.1.27
  • Deployment: Multi-node (4+ instances behind load balancer)
  • Use Case: Cloud SaaS Platform with multiple concurrent users

Additional Context

I have verified through source code analysis that:

  1. The current Bus implementation broadcasts all events (Bus.subscribeAll)
  2. Even OpenCode's own internal code filters by sessionID client-side
  3. The SSE endpoint in server.ts:475 simply forwards all Bus events without filtering

I am willing to contribute a PR for this feature if the approach is approved.


Labels: enhancement, api, sse, multi-tenant

Originally created by @jf0606 on GitHub (Jan 20, 2026). Originally assigned to: @thdxr on GitHub. ### Feature hasn't been suggested before. - [ ] I have verified this feature I'm about to request hasn't been suggested before. ### Describe the enhancement you want to request # [Feature Request] Support `sessionID` Filter for SSE Event Subscription ## Summary Add support for `sessionID` query parameter in the `/event` SSE endpoint to allow clients to subscribe to events for specific sessions only, rather than receiving all events and filtering client-side. ## Problem Statement Currently, the `/event` endpoint broadcasts **all events** to all subscribers. Clients must filter events by `sessionID` on their own. This design causes significant issues in multi-session and multi-node deployment scenarios. ### Current Behavior ``` GET /event?directory=/path/to/project ``` Returns: **ALL events** from all sessions in that directory. ### Evidence from Source Code Even OpenCode's internal code filters by sessionID on the client side: **`packages/opencode/src/tool/task.ts:111-112`** ```typescript const unsub = Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => { if (evt.properties.part.sessionID !== session.id) return // Client-side filtering // ... }) ``` **`packages/opencode/src/cli/cmd/github.ts:840-841`** ```typescript Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => { if (evt.properties.part.sessionID !== session.id) return // Client-side filtering // ... }) ``` **`packages/opencode/src/server/server.ts:475`** ```typescript const unsub = Bus.subscribeAll(async (event) => { // Subscribes to ALL events await stream.writeSSE({ data: JSON.stringify(event), }) }) ``` ## Use Cases ### 1. Cloud SaaS Deployment ``` ┌─────────────────────────────────┐ │ Load Balancer (Nginx) │ └───────────────┬─────────────────┘ ┌───────┬───────────┼───────────┬───────┐ ▼ ▼ ▼ ▼ ▼ Backend Backend Backend Backend Backend Node 1 Node 2 Node 3 Node 4 Node 5 └───────┴───────────┴───────────┴───────┘ │ ▼ OpenCode Server ``` **Problems:** - Each backend node needs its own SSE connection - All nodes receive ALL events (massive bandwidth waste) - Each node must filter events for its sessions - Linear scaling of connections and data transfer ### 2. Multi-Session Web IDE A web-based IDE with multiple sessions open simultaneously: ```javascript // User has 3 sessions open // Current: Must subscribe once and filter ALL events const events = await fetch('/event?directory=/project'); // For each event, check which session it belongs to if (event.properties.part.sessionID === 'ses_session1') { /* update UI 1 */ } if (event.properties.part.sessionID === 'ses_session2') { /* update UI 2 */ } if (event.properties.part.sessionID === 'ses_session3') { /* update UI 3 */ } ``` ### 3. Backend Service (Telegram Bot, Slack Bot, etc.) As described in [Issue #6573](https://github.com/sst/opencode/issues/6573): > A Node.js service uses the @opencode-ai/sdk to interact with OpenCode via REST API, polling session status and streaming responses back to users. Sessions hang indefinitely when the LLM uses the Task tool to spawn subagents, because **subagent events come through the same subscription**. ## Proposed Solution ### Option A: Add `sessionID` Query Parameter (Recommended) ``` GET /event?sessionID=ses_xxx GET /event?directory=/path/to/project&sessionID=ses_xxx ``` **Benefits:** - Simple and straightforward - Backward compatible (existing `/event` behavior unchanged) - Reduces bandwidth waste significantly - Enables proper multi-node deployment **Implementation Suggestion:** ```typescript // packages/opencode/src/server/server.ts .get("/event", async (c) => { const sessionID = c.req.query("sessionID"); return streamSSE(c, async (stream) => { stream.writeSSE({ data: JSON.stringify({ type: "server.connected", properties: {} }), }); const unsub = Bus.subscribeAll(async (event) => { // If sessionID filter is specified, only send matching events if (sessionID) { const eventSessionID = event.properties?.part?.sessionID || event.properties?.info?.sessionID || event.properties?.info?.id || event.properties?.sessionID; if (eventSessionID && eventSessionID !== sessionID) { return; // Skip events for other sessions } } await stream.writeSSE({ data: JSON.stringify(event) }); }); // ... rest of the handler }); }) ``` ### Option B: Support Multiple Session Subscription ``` GET /event?sessionIDs=ses_xxx,ses_yyy,ses_zzz ``` ### Option C: WebSocket with Dynamic Subscription ```javascript const ws = new WebSocket('/ws/events'); // Subscribe to specific sessions ws.send(JSON.stringify({ type: 'subscribe', sessionIDs: ['ses_xxx', 'ses_yyy'] })); // Dynamically add/remove subscriptions ws.send(JSON.stringify({ type: 'unsubscribe', sessionIDs: ['ses_xxx'] })); ``` ## Performance Impact ### Current (Without sessionID Filter) With 100 concurrent sessions and 10 backend nodes: | Metric | Value | |--------|-------| | SSE Connections | 10 (one per node) | | Events Transferred | 10x (every event sent to all nodes) | | Client CPU | High (filtering 100 sessions worth of events) | | Network Bandwidth | 10x waste | ### Proposed (With sessionID Filter) | Metric | Value | |--------|-------| | SSE Connections | 100 (one per session, distributed across nodes) | | Events Transferred | 1x (each event sent once to correct subscriber) | | Client CPU | Minimal (no filtering needed) | | Network Bandwidth | Optimal | ## Related Issues - [#6573 - Sessions hang indefinitely when Task tool spawns subagents via REST API](https://github.com/sst/opencode/issues/6573) - Root cause is mixed events from parent and child sessions - [#6142 - Add sessionID to experimental.chat.system.transform hook](https://github.com/sst/opencode/issues/6142) - Related sessionID tracking request - [#2168 - API documentation](https://github.com/sst/opencode/issues/2168) - Questions about session event handling ## Current Workaround We currently use Redis Pub/Sub as middleware to handle session-specific event routing: ``` OpenCode Server ↓ (1 SSE connection) Dedicated Subscriber Service ↓ (Parse sessionID, publish to channel) Redis Pub/Sub (channel per session) ↓ (Subscribe by channel) Backend Nodes (N instances) ``` This works but adds significant architectural complexity and operational overhead. ## Environment - **OpenCode Version:** 1.1.27 - **Deployment:** Multi-node (4+ instances behind load balancer) - **Use Case:** Cloud SaaS Platform with multiple concurrent users ## Additional Context I have verified through source code analysis that: 1. The current Bus implementation broadcasts all events (`Bus.subscribeAll`) 2. Even OpenCode's own internal code filters by sessionID client-side 3. The SSE endpoint in `server.ts:475` simply forwards all Bus events without filtering I am willing to contribute a PR for this feature if the approach is approved. --- **Labels:** `enhancement`, `api`, `sse`, `multi-tenant`
yindo added the discussion label 2026-02-16 18:05:44 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Jan 20, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #7451: Does APIs of opencode support session-level SSE event listener? (Direct duplicate question)
  • #5627: ACP event subscription architecture causes cross-session pollution and duplicate events (Root cause)
  • #6573: Sessions hang indefinitely when Task tool spawns subagents via REST API (Specific bug caused by lack of sessionID filtering)
  • #3758: Session filters (Earlier feature request)
  • #5784: MCP auth configuration for multi-tenant serve deployments (Related multi-tenant concern)
  • #9154: Memory leak: ACP session event streams never aborted (Related subscription management)

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Jan 20, 2026): This issue might be a duplicate of existing issues. Please check: - #7451: Does APIs of opencode support session-level SSE event listener? (Direct duplicate question) - #5627: ACP event subscription architecture causes cross-session pollution and duplicate events (Root cause) - #6573: Sessions hang indefinitely when Task tool spawns subagents via REST API (Specific bug caused by lack of sessionID filtering) - #3758: Session filters (Earlier feature request) - #5784: MCP auth configuration for multi-tenant `serve` deployments (Related multi-tenant concern) - #9154: Memory leak: ACP session event streams never aborted (Related subscription management) Feel free to ignore if none of these address your specific case.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#6964