Skip to content

feat(server): send plugins only their agent's subagent updates - #102

Open
jtomaszewski wants to merge 3 commits into
fshfrom
feat/provider-subagent-event-filter
Open

jtomaszewski wants to merge 3 commits into
fshfrom
feat/provider-subagent-event-filter

Conversation

@jtomaszewski

@jtomaszewski jtomaszewski commented Nov 14, 2026 •

Copy link
Copy Markdown

Follow-up to #101. A plugin that follows provider subagents now receives only its own agent's subagent changes. Before, every providerSubagents.subscribe() received the whole host's subagent stream, including every streamed timeline row, and dropped most of it. A subscription the host refuses now reaches onError instead of ending silently.

🔧 The daemon filters subagent updates per subscription

  • session.events.set_subscription.request takes an optional providerSubagentFilter: { parentAgentIds?: string[]; kinds?: ("upsert" | "timeline" | "remove")[] }.
    • The daemon applies it per subscription before sending agent.provider_subagents.update. An omitted field doesn't filter.
    • The daemon advertises server_info.features.providerSubagentEventFilter. COMPAT tags are on the field, the flag and the client gate.
  • agents.ref(id).providerSubagents.subscribe() asks for { parentAgentIds: [id], kinds: ["upsert", "remove"] }, only from hosts that advertise the flag.
    • Older hosts keep sending everything.
    • The handle still filters what arrives, so results are the same either way.
  • providerSubagents.subscribe() and backgroundWork.subscribe() now pass a failed event subscription to onError. The subscription ends; subscribe again to retry. Before, it ended silently and only the client log showed it.
  • No change for the app's existing unfiltered subscriptions.

✅ Testing

  • Server: a session test pins filtering by parent agent and kind, and fails without the filter. session.test.ts passes (162/162).
  • Protocol: the schema accepts the filter and rejects unknown kinds.
  • Client:
    • the filter is sent to hosts that advertise it and omitted for hosts that don't;
    • a refused subscription reaches onError for both handles;
    • index.test.ts passes (40/40).
  • Workspace typecheck and lint pass.
  • Local review (om-code-review rubric): 1 round, 3 fresh reviewers, all approved. No blocker or major, so nothing was fixed in the loop. Their minors are listed below.

⚠️ Worth knowing

These are minors from the review, left on purpose for the reviewer to decide:

  • Doc wording. The docs say onError fires when the host refuses the subscription. It fires on any failure of the subscribe request, including a timeout or a failure on resubscribe after a reconnect, and the subscription ends in each case. The wording should say "fails".
  • Test gaps.
    • The server test has one filtered subscription. Add an unfiltered one beside it, plus single-field filters.
    • The client test doesn't check that onError stays silent after unsubscribe.
  • Feature check runs once. The client reads the flag when the subscription is created. A subscription made before server_info arrives, or against an old host that later upgrades, stays unfiltered. Results are still correct, just less efficient.
  • No-socket path. The daemon's socketless fallback path (COMPAT ownedSubscriptions, test harnesses only) sends without the filter.
  • Filter shape. The filter is a top-level field specific to one event. A keyed filters map would generalize better. An empty array matches nothing, and the arrays have no size limit.

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

jtomaszewski and others added 3 commits November 14, 2026 15:13
session.events.set_subscription.request takes an optional
providerSubagentFilter (parentAgentIds, kinds). The daemon applies it
before sending agent.provider_subagents.update to that subscription and
advertises features.providerSubagentEventFilter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…used subscriptions

providerSubagents.subscribe() sends a filter for its agent's upserts and
removes when the host supports it. Both providerSubagents and
backgroundWork subscribe() now pass a refused event subscription to
onError.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant