Skip to content

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

Closed as not planned
pcelebrado/opencode
#1

Description

@jf0606

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

Activity

  1. added
    discussionUsed for feature requests, proposals, ideas, etc. Open discussion
    on Jan 20, 2026
  2. github-actions commented on Jan 20, 2026

    @github-actions
    Contributor

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

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

  3. dammitpogi commented on Mar 1, 2026

    @dammitpogi

    +1 on session-scoped SSE filtering — we're about to need this for our Jules integration.
    We're adding Google Jules support (it's a session-based agent that uses polling), and we want the IDE experience to feel native. Our plan is to emit normalized jules.* Bus events — things like jules.run.created, status, plan, activity, artifact — and have the UI consume them through /event.
    The problem is that /event currently broadcasts every bus event to every subscriber. For long-running agent sessions, that gets noisy fast. A simple /event?sessionID=... filter would help us:

    • Reduce bandwidth and client-side processing (especially when activities and artifacts stream in frequently)
    • Avoid the "SSE is super chatty" issues that have come up in performance discussions
    • Make agent-run UIs more scalable as we add more providers down the road
      On our end, we'll always include openCodeSessionID in the properties field of every jules.* event, so client-side routing works right away. Server-side filtering would just let us drop irrelevant events earlier in the pipeline.
  4. github-actions commented on May 1, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

discussionUsed for feature requests, proposals, ideas, etc. Open discussion

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions