Skip to content

fix: suppress stop notifications for subagent sessions#22

Merged
harryalbert merged 3 commits into
warpdotdev:mainfrom
aqeelat:fix/subagent-idle-status
Jun 5, 2026
Merged

fix: suppress stop notifications for subagent sessions#22
harryalbert merged 3 commits into
warpdotdev:mainfrom
aqeelat:fix/subagent-idle-status

Conversation

@aqeelat

@aqeelat aqeelat commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

What

Subagent sessions (e.g. from the Task tool) fire session.idle when they complete, causing duplicate stop notifications in Warp.

How

On session.idle, fetch the session via client.session.get() and check parentID. If set, return early — no stop notification is sent for child sessions.

If the fetch fails, the notification is sent anyway to avoid silently dropping it.

Trade-off

This adds one API call (session.get) per session.idle event. Since idle fires once per session completion, the overhead is negligible.

Fetch the session on session.idle to check for parentID. If the session
is a child (subagent) session, return early without sending a stop
notification. Falls through on fetch failure so notifications are never
silently dropped.

Bump version to 0.1.7.
@aqeelat

aqeelat commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

@harryalbert
Hi Harry
Can you please take a look?

harryalbert commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

@harryalbert
Hi Harry
Can you please take a look?

​Can do! Feel free to tag me as a reviewer as well going forward (easier to track on my end)

@harryalbert harryalbert self-requested a review June 2, 2026 15:14
Comment thread src/index.ts Outdated
const session = await client.session.get({
path: { id: sessionId },
})
if (session.data?.parentID) return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you only make this change for the session.idle event? Feels like we might want it for other events too (like prompt_submit, session_start, tool_complete, etc)

@aqeelat aqeelat Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harryalbert
Good point. I originally scoped this to session.idle because that was the noisy notification I observed, but the same subagent semantics should apply across all session-scoped notifications.

I've updated the PR to extract a shared isSubagentSession() helper and apply it to every notification handler: session.created, session.idle, permission.updated, permission.replied, permission.asked, chat.message, tool.execute.before, and tool.execute.after.

One thing I considered but left out: each handler independently calls client.session.get() to check parentID. Since parentID is immutable for a session's lifetime, a simple Map<string, boolean> cache would eliminate redundant lookups when multiple events fire for the same session in quick succession (e.g., tool.execute.beforetool.execute.aftersession.idle). I left it out for now since it's a minor optimization and the current approach is correct. Happy to add it if you'd prefer.

@aqeelat aqeelat force-pushed the fix/subagent-idle-status branch 2 times, most recently from f8cc4fb to 853cafe Compare June 2, 2026 17:14

@harryalbert harryalbert left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for iterating! Just had a few more thoughts

Comment thread src/index.ts
async function isSubagentSession(sessionId?: string): Promise<boolean> {
if (!sessionId) return false
try {
const session = await client.session.get({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mentioned this in your comment, but I think this is worth caching if it doesn't change. Would rather avoid an async call for every hook (especially frequently activated hooks like tool.execute.after) if we can

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aqeelat what do you think of this suggestion

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harryalbert sorry i had already worked on it but forgot to push.

Comment thread src/index.ts Outdated
switch (event.type) {
case "session.created": {
const sessionId = event.properties.info.id
if (await isSubagentSession(sessionId)) return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of adding this boundary at every hook (which feels brittle in case we add more hooks and forget to add the check for any of those new hooks), I'd rather this just be one unified gate that we check before surfacing a notification. We could define a wrapper around warpNotify (something like maybeWarpNotify, which would live inside index.ts) that checks the sub-agent predicate and, if it passes, internally calls warpNotify.

@aqeelat aqeelat force-pushed the fix/subagent-idle-status branch from 853cafe to 5107303 Compare June 3, 2026 06:48
@aqeelat aqeelat force-pushed the fix/subagent-idle-status branch from 5107303 to c935ffa Compare June 4, 2026 15:08
@aqeelat

aqeelat commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

@harryalbert Both suggestions are addressed in the latest push

I added a Map<string, boolean> cache to isSubagentSession and introduced a maybeWarpNotify wrapper that centralizes the subagent check so all notification paths go through a single gate. Let me know if the implementation matches what you had in mind.

@harryalbert harryalbert left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@harryalbert harryalbert merged commit 8469420 into warpdotdev:main Jun 5, 2026
1 check passed
xiaogaozi added a commit to xiaogaozi/opencode-warp that referenced this pull request Jun 24, 2026
## Summary

Suppress Warp notifications for subagent (child) sessions so background sub-tasks no longer generate notification noise (session start/stop, permission prompts, question prompts). Ports the behavior of upstream `warpdotdev#22` and adapts it to this fork's event-based handler.

## Changes Made

- `src/index.ts`:
  - Add a cached `isSubagentSession()` helper that resolves a session via `client.session.get()` and checks its `parentID`. On fetch failure it falls through and notifies anyway, so notifications are never silently dropped.
  - `session.created`: early-return when `info.parentID` is present.
  - `session.idle`: suppress all notifications (both the `session_start` resend and the `stop` notification) for subagent sessions.
  - `permission.updated`, `permission.replied`, `permission.asked`, `question.asked`: gate notifications behind the subagent check.

## Technical Details

- Subagent detection relies on the `parentID` field of the OpenCode SDK `Session` type.
- Lookups are memoized per session id via a `Map<string, boolean>` to avoid repeated API calls on frequent events.
- Preserves the fork's existing `{ success, error }` return + logging pattern; `src/notify.ts` is unchanged.

## Testing

- `bun run typecheck` — passes.
- `bun test` — 26/26 pass.

## Related

- Ports upstream `warpdotdev#22`.
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.

2 participants