[PR #334] [MERGED] feat(deepagents): add completion notifier middleware for async subagents #367

Closed
opened 2026-06-05 17:22:50 -04:00 by yindo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/langchain-ai/deepagentsjs/pull/334
Author: @hntrl
Created: 3/20/2026
Status: Merged
Merged: 3/24/2026
Merged by: @colifran

Base: 1.9.0-alpha.1Head: hunter/js-notifier-middleware


📝 Commits (1)

  • 628bcfb feat(deepagents): add completion notifier middleware for async subagents

📊 Changes

8 files changed (+769 additions, -19 deletions)

View changed files

.changeset/clear-years-end.md (+5 -0)
.changeset/fresh-ways-cross.md (+0 -5)
📝 libs/deepagents/package.json (+1 -1)
📝 libs/deepagents/src/index.ts (+3 -2)
libs/deepagents/src/middleware/completion_notifier.test.ts (+393 -0)
libs/deepagents/src/middleware/completion_notifier.ts (+356 -0)
📝 libs/deepagents/src/middleware/index.ts (+6 -0)
📝 pnpm-lock.yaml (+5 -11)

📄 Description

Summary

Port of langchain-ai/deepagents#2119 to TypeScript.

Adds a createCompletionNotifierMiddleware that async subagents can use to proactively notify their supervisor when they complete or error -- closing the gap where the supervisor only learns about completion when someone calls check_async_task.

Why

The async subagent protocol is fire-and-forget: the supervisor launches a task and only discovers its outcome when it (or the user) polls via check_async_task. This means the supervisor can't proactively relay results -- the user has to ask. The completion notifier solves this by having the subagent push a notification to the supervisor's thread when it finishes.

Architecture

Supervisor                    Subagent
    |                            |
    |--- start_async_task -----> |
    |<-- task_id (immediately) - |
    |                            |  (working...)
    |                            |  (done!)
    |                            |
    |<-- runs.create(            |
    |      supervisor_thread,    |
    |      "completed: ...")     |
    |                            |
    |  (wakes up, sees result)   |

This is an opt-in middleware added to the subagent's stack, not the supervisor's. The notifier calls runs.create() on the supervisor's thread via the @langchain/langgraph-sdk Client, which queues a new run that wakes the supervisor.

Parent context propagation: parent_thread_id is injected into the subagent's input state by the supervisor's start_async_task tool (from the Python PR). The parentGraphId (supervisor's graph/assistant ID) is passed as a constructor argument since it's static configuration known at deployment time.

Changes

New: createCompletionNotifierMiddleware (libs/deepagents/src/middleware/completion_notifier.ts)

  • afterAgent hook sends a completion notification with result summary (truncated to 500 chars)
  • wrapModelCall hook catches errors and sends an error notification before re-raising
  • State schema adds parent_thread_id to the subagent's state
  • Notifies only once per run (guards against duplicates)
  • Silently no-ops if parent context is missing (subagent launched without a supervisor)
  • Supports url and headers options for remote supervisors

New: completion_notifier.test.ts — 22 unit tests

  • Tests for extractLastMessage, notifyParent, afterAgent, wrapModelCall
  • Covers edge cases: missing parent context, duplicate notification guard, error swallowing

Modified: package.json

  • Added @langchain/langgraph-sdk as a required dependency

Exports

  • createCompletionNotifierMiddleware and CompletionNotifierOptions exported from deepagents top-level and middleware/index.ts

Usage

import { createCompletionNotifierMiddleware, createDeepAgent } from "deepagents";

// Same deployment (ASGI transport):
const notifier = createCompletionNotifierMiddleware({
  parentGraphId: "supervisor",
});

// Remote deployment:
const notifier = createCompletionNotifierMiddleware({
  parentGraphId: "supervisor",
  url: "https://supervisor.langsmith.dev",
});

const agent = createDeepAgent({
  model: "claude-sonnet-4-5-20250929",
  middleware: [notifier],
});

Divergence from Python

The Python implementation uses langgraph-sdk's get_client() with ASGI transport for same-deployment communication. The JS version uses @langchain/langgraph-sdk's Client class with a default localhost URL (http://localhost:8123) when no URL is provided. The parentGraphId parameter matches the Python parent_graph_id constructor arg.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/langchain-ai/deepagentsjs/pull/334 **Author:** [@hntrl](https://github.com/hntrl) **Created:** 3/20/2026 **Status:** ✅ Merged **Merged:** 3/24/2026 **Merged by:** [@colifran](https://github.com/colifran) **Base:** `1.9.0-alpha.1` ← **Head:** `hunter/js-notifier-middleware` --- ### 📝 Commits (1) - [`628bcfb`](https://github.com/langchain-ai/deepagentsjs/commit/628bcfb59ad90ffbc8b33ac076cba142d18fad10) feat(deepagents): add completion notifier middleware for async subagents ### 📊 Changes **8 files changed** (+769 additions, -19 deletions) <details> <summary>View changed files</summary> ➕ `.changeset/clear-years-end.md` (+5 -0) ➖ `.changeset/fresh-ways-cross.md` (+0 -5) 📝 `libs/deepagents/package.json` (+1 -1) 📝 `libs/deepagents/src/index.ts` (+3 -2) ➕ `libs/deepagents/src/middleware/completion_notifier.test.ts` (+393 -0) ➕ `libs/deepagents/src/middleware/completion_notifier.ts` (+356 -0) 📝 `libs/deepagents/src/middleware/index.ts` (+6 -0) 📝 `pnpm-lock.yaml` (+5 -11) </details> ### 📄 Description ## Summary Port of [langchain-ai/deepagents#2119](https://github.com/langchain-ai/deepagents/pull/2119) to TypeScript. Adds a `createCompletionNotifierMiddleware` that async subagents can use to proactively notify their supervisor when they complete or error -- closing the gap where the supervisor only learns about completion when someone calls `check_async_task`. ## Why The async subagent protocol is fire-and-forget: the supervisor launches a task and only discovers its outcome when it (or the user) polls via `check_async_task`. This means the supervisor can't proactively relay results -- the user has to ask. The completion notifier solves this by having the subagent push a notification to the supervisor's thread when it finishes. ## Architecture ``` Supervisor Subagent | | |--- start_async_task -----> | |<-- task_id (immediately) - | | | (working...) | | (done!) | | |<-- runs.create( | | supervisor_thread, | | "completed: ...") | | | | (wakes up, sees result) | ``` This is an opt-in middleware added to the **subagent's** stack, not the supervisor's. The notifier calls `runs.create()` on the supervisor's thread via the `@langchain/langgraph-sdk` Client, which queues a new run that wakes the supervisor. **Parent context propagation:** `parent_thread_id` is injected into the subagent's input state by the supervisor's `start_async_task` tool (from the Python PR). The `parentGraphId` (supervisor's graph/assistant ID) is passed as a constructor argument since it's static configuration known at deployment time. ## Changes ### New: `createCompletionNotifierMiddleware` (`libs/deepagents/src/middleware/completion_notifier.ts`) - `afterAgent` hook sends a completion notification with result summary (truncated to 500 chars) - `wrapModelCall` hook catches errors and sends an error notification before re-raising - State schema adds `parent_thread_id` to the subagent's state - Notifies only once per run (guards against duplicates) - Silently no-ops if parent context is missing (subagent launched without a supervisor) - Supports `url` and `headers` options for remote supervisors ### New: `completion_notifier.test.ts` — 22 unit tests - Tests for `extractLastMessage`, `notifyParent`, `afterAgent`, `wrapModelCall` - Covers edge cases: missing parent context, duplicate notification guard, error swallowing ### Modified: `package.json` - Added `@langchain/langgraph-sdk` as a required dependency ### Exports - `createCompletionNotifierMiddleware` and `CompletionNotifierOptions` exported from `deepagents` top-level and `middleware/index.ts` ## Usage ```typescript import { createCompletionNotifierMiddleware, createDeepAgent } from "deepagents"; // Same deployment (ASGI transport): const notifier = createCompletionNotifierMiddleware({ parentGraphId: "supervisor", }); // Remote deployment: const notifier = createCompletionNotifierMiddleware({ parentGraphId: "supervisor", url: "https://supervisor.langsmith.dev", }); const agent = createDeepAgent({ model: "claude-sonnet-4-5-20250929", middleware: [notifier], }); ``` ## Divergence from Python The Python implementation uses `langgraph-sdk`'s `get_client()` with ASGI transport for same-deployment communication. The JS version uses `@langchain/langgraph-sdk`'s `Client` class with a default localhost URL (`http://localhost:8123`) when no URL is provided. The `parentGraphId` parameter matches the Python `parent_graph_id` constructor arg. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
yindo added the pull-request label 2026-06-05 17:22:50 -04:00
yindo closed this issue 2026-06-05 17:22:50 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/deepagentsjs#367