feat(sdk): add completion notifier middleware for async subagents#2119
Open
Hunter Lovell (hntrl) wants to merge 19 commits intomainfrom
Open
feat(sdk): add completion notifier middleware for async subagents#2119Hunter Lovell (hntrl) wants to merge 19 commits intomainfrom
Hunter Lovell (hntrl) wants to merge 19 commits intomainfrom
Conversation
…structor arg, derive task_id from config
Hunter Lovell (hntrl)
added a commit
to langchain-ai/deepagentsjs
that referenced
this pull request
Mar 20, 2026
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. - New createCompletionNotifierMiddleware with afterAgent and wrapModelCall hooks - Uses @langchain/langgraph-sdk Client to send runs.create() to the supervisor's thread - Reads parent_thread_id from subagent state (injected by start_async_task) - Derives task_id from runtime.configurable.thread_id - Silently no-ops if parent context is missing - Guards against duplicate notifications - 22 unit tests covering all hooks, edge cases, and error paths
Colin Francis (colifran)
pushed a commit
to langchain-ai/deepagentsjs
that referenced
this pull request
Mar 24, 2026
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. - New createCompletionNotifierMiddleware with afterAgent and wrapModelCall hooks - Uses @langchain/langgraph-sdk Client to send runs.create() to the supervisor's thread - Reads parent_thread_id from subagent state (injected by start_async_task) - Derives task_id from runtime.configurable.thread_id - Silently no-ops if parent context is missing - Guards against duplicate notifications - 22 unit tests covering all hooks, edge cases, and error paths
Colin Francis (colifran)
pushed a commit
to langchain-ai/deepagentsjs
that referenced
this pull request
Mar 24, 2026
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. - New createCompletionNotifierMiddleware with afterAgent and wrapModelCall hooks - Uses @langchain/langgraph-sdk Client to send runs.create() to the supervisor's thread - Reads parent_thread_id from subagent state (injected by start_async_task) - Derives task_id from runtime.configurable.thread_id - Silently no-ops if parent context is missing - Guards against duplicate notifications - 22 unit tests covering all hooks, edge cases, and error paths
Colin Francis (colifran)
pushed a commit
to langchain-ai/deepagentsjs
that referenced
this pull request
Mar 24, 2026
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. - New createCompletionNotifierMiddleware with afterAgent and wrapModelCall hooks - Uses @langchain/langgraph-sdk Client to send runs.create() to the supervisor's thread - Reads parent_thread_id from subagent state (injected by start_async_task) - Derives task_id from runtime.configurable.thread_id - Silently no-ops if parent context is missing - Guards against duplicate notifications - 22 unit tests covering all hooks, edge cases, and error paths fix(deepagents): make url required in completion notifier (no ASGI in JS) JS does not have ASGI transport like Python, so the url parameter must be provided explicitly. Removed all ASGI references from docs and the localhost fallback default. fix(deepagents): throw on built-in tool collision (#330) * add error * Create big-horses-fail.md * add config error class * cr --------- Co-authored-by: Christian Bromann <git@bromann.dev> fix(deepagents): use `crypto.randomUUID()` instead of uuid (#336) * fix(deepagents): use crypto.randomUUID() instead of uuid * update pnpm-lock * Create grumpy-weeks-wave.md * Update libs/deepagents/src/middleware/fs.int.test.ts feat(deepagent): add LangSmithSandbox (#324) * feat(deepagent): add LangSmithSandbox * Change deepagents version from patch to minor * format * fix tests * format * make it a patch * cr * cr * fix * cr regen lockfile linting linting add missing url property chore: version packages (#321) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> changeset regen lockfil
Colin Francis (colifran)
pushed a commit
to langchain-ai/deepagentsjs
that referenced
this pull request
Mar 24, 2026
…nts (#334) 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. - New createCompletionNotifierMiddleware with afterAgent and wrapModelCall hooks - Uses @langchain/langgraph-sdk Client to send runs.create() to the supervisor's thread - Reads parent_thread_id from subagent state (injected by start_async_task) - Derives task_id from runtime.configurable.thread_id - Silently no-ops if parent context is missing - Guards against duplicate notifications - 22 unit tests covering all hooks, edge cases, and error paths fix(deepagents): make url required in completion notifier (no ASGI in JS) JS does not have ASGI transport like Python, so the url parameter must be provided explicitly. Removed all ASGI references from docs and the localhost fallback default. fix(deepagents): throw on built-in tool collision (#330) * add error * Create big-horses-fail.md * add config error class * cr --------- Co-authored-by: Christian Bromann <git@bromann.dev> fix(deepagents): use `crypto.randomUUID()` instead of uuid (#336) * fix(deepagents): use crypto.randomUUID() instead of uuid * update pnpm-lock * Create grumpy-weeks-wave.md * Update libs/deepagents/src/middleware/fs.int.test.ts feat(deepagent): add LangSmithSandbox (#324) * feat(deepagent): add LangSmithSandbox * Change deepagents version from patch to minor * format * fix tests * format * make it a patch * cr * cr * fix * cr regen lockfile linting linting add missing url property chore: version packages (#321) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> changeset regen lockfil
Hunter Lovell (hntrl)
added a commit
to langchain-ai/deepagentsjs
that referenced
this pull request
Mar 24, 2026
…nts (#334) 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. - New createCompletionNotifierMiddleware with afterAgent and wrapModelCall hooks - Uses @langchain/langgraph-sdk Client to send runs.create() to the supervisor's thread - Reads parent_thread_id from subagent state (injected by start_async_task) - Derives task_id from runtime.configurable.thread_id - Silently no-ops if parent context is missing - Guards against duplicate notifications - 22 unit tests covering all hooks, edge cases, and error paths fix(deepagents): make url required in completion notifier (no ASGI in JS) JS does not have ASGI transport like Python, so the url parameter must be provided explicitly. Removed all ASGI references from docs and the localhost fallback default. fix(deepagents): throw on built-in tool collision (#330) * add error * Create big-horses-fail.md * add config error class * cr --------- Co-authored-by: Christian Bromann <git@bromann.dev> fix(deepagents): use `crypto.randomUUID()` instead of uuid (#336) * fix(deepagents): use crypto.randomUUID() instead of uuid * update pnpm-lock * Create grumpy-weeks-wave.md * Update libs/deepagents/src/middleware/fs.int.test.ts feat(deepagent): add LangSmithSandbox (#324) * feat(deepagent): add LangSmithSandbox * Change deepagents version from patch to minor * format * fix tests * format * make it a patch * cr * cr * fix * cr regen lockfile linting linting add missing url property chore: version packages (#321) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> changeset regen lockfil
Hunter Lovell (hntrl)
added a commit
to langchain-ai/deepagentsjs
that referenced
this pull request
Apr 1, 2026
…nts (#334) 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. - New createCompletionNotifierMiddleware with afterAgent and wrapModelCall hooks - Uses @langchain/langgraph-sdk Client to send runs.create() to the supervisor's thread - Reads parent_thread_id from subagent state (injected by start_async_task) - Derives task_id from runtime.configurable.thread_id - Silently no-ops if parent context is missing - Guards against duplicate notifications - 22 unit tests covering all hooks, edge cases, and error paths fix(deepagents): make url required in completion notifier (no ASGI in JS) JS does not have ASGI transport like Python, so the url parameter must be provided explicitly. Removed all ASGI references from docs and the localhost fallback default. fix(deepagents): throw on built-in tool collision (#330) * add error * Create big-horses-fail.md * add config error class * cr --------- Co-authored-by: Christian Bromann <git@bromann.dev> fix(deepagents): use `crypto.randomUUID()` instead of uuid (#336) * fix(deepagents): use crypto.randomUUID() instead of uuid * update pnpm-lock * Create grumpy-weeks-wave.md * Update libs/deepagents/src/middleware/fs.int.test.ts feat(deepagent): add LangSmithSandbox (#324) * feat(deepagent): add LangSmithSandbox * Change deepagents version from patch to minor * format * fix tests * format * make it a patch * cr * cr * fix * cr regen lockfile linting linting add missing url property chore: version packages (#321) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> changeset regen lockfil
Colin Francis (colifran)
added a commit
to langchain-ai/deepagentsjs
that referenced
this pull request
Apr 2, 2026
* Revert "revert: "feat(deepagents): support multimodal files for backends (#298)" (#352)" (#353) This reverts commit 03ea1c9. * revert: "revert: "feat(sdk): add async subagent middleware for remote LangGraph servers (#323)" (#351)" (#354) * Revert "revert: "feat(sdk): add async subagent middleware for remote LangGraph servers (#323)" (#351)" This reverts commit 367e43a. * use any backend protocol * Reapply "chore(deepagents): refactor backend method names - `lsInfo` -> `ls`, …" (#349) (#356) This reverts commit 573479d. * Reapply "chore(sdk): unify sync subagents and async subagents into a single pr…" (#348) (#355) This reverts commit 96dc34c. * chore: align alpha with main (#358) * fix(deepagents): remove orphaned ToolMessages for Gemini compatibility (#335) * fix(deepagents): remove orphaned ToolMessages for Gemini compatibility * Fix ToolMessages for Gemini compatibility --------- Co-authored-by: Christian Bromann <git@bromann.dev> * fix(deepagents): throw on built-in tool collision (#330) * add error * Create big-horses-fail.md * add config error class * cr --------- Co-authored-by: Christian Bromann <git@bromann.dev> * fix(deepagents): use `crypto.randomUUID()` instead of uuid (#336) * fix(deepagents): use crypto.randomUUID() instead of uuid * update pnpm-lock * Create grumpy-weeks-wave.md * Update libs/deepagents/src/middleware/fs.int.test.ts * feat(deepagent): add LangSmithSandbox (#324) * feat(deepagent): add LangSmithSandbox * Change deepagents version from patch to minor * format * fix tests * format * make it a patch * cr * cr * fix * cr * chore: version packages (#321) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * regen lockfile * fix langsmith tests so that they use backend protocol v2 methods * format --------- Co-authored-by: pawel-twardziak <pawel.twardziak.dev@gmail.com> Co-authored-by: Christian Bromann <git@bromann.dev> Co-authored-by: Maahir Sachdev <maahir.sachdev@langchain.dev> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * feat(deepagents): add completion notifier middleware for async subagents (#334) 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. - New createCompletionNotifierMiddleware with afterAgent and wrapModelCall hooks - Uses @langchain/langgraph-sdk Client to send runs.create() to the supervisor's thread - Reads parent_thread_id from subagent state (injected by start_async_task) - Derives task_id from runtime.configurable.thread_id - Silently no-ops if parent context is missing - Guards against duplicate notifications - 22 unit tests covering all hooks, edge cases, and error paths fix(deepagents): make url required in completion notifier (no ASGI in JS) JS does not have ASGI transport like Python, so the url parameter must be provided explicitly. Removed all ASGI references from docs and the localhost fallback default. fix(deepagents): throw on built-in tool collision (#330) * add error * Create big-horses-fail.md * add config error class * cr --------- Co-authored-by: Christian Bromann <git@bromann.dev> fix(deepagents): use `crypto.randomUUID()` instead of uuid (#336) * fix(deepagents): use crypto.randomUUID() instead of uuid * update pnpm-lock * Create grumpy-weeks-wave.md * Update libs/deepagents/src/middleware/fs.int.test.ts feat(deepagent): add LangSmithSandbox (#324) * feat(deepagent): add LangSmithSandbox * Change deepagents version from patch to minor * format * fix tests * format * make it a patch * cr * cr * fix * cr regen lockfile linting linting add missing url property chore: version packages (#321) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> changeset regen lockfil * chore: enter alpha pre-release * chore: target alpha for releases * chore: version packages (alpha) (#359) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * chore(deepagents): extend supported backend file types (#363) * extend supported file types * Create strong-tigers-share.md --------- Co-authored-by: Hunter Lovell <40191806+hntrl@users.noreply.github.com> * chore(deepagents): implement async subagents + use stream example (#360) * async subagents + use stream example * fix lockfile * format * linting * readme and linting * format * proactively send responses when subagents complete * better examples * feat(deepagents): rename completion notifier to completion callback and align with Python (#361) * feat(deepagents): rename completion notifier to completion callback and align with Python PR - Rename completion_notifier.ts -> completion_callback.ts to match Python's completion_callback.py naming - Rename exports: createCompletionNotifierMiddleware -> createCompletionCallbackMiddleware, CompletionNotifierOptions -> CompletionCallbackOptions - Rename state key: parent_thread_id -> callbackThreadId, option: parentGraphId -> callbackGraphId - Make url optional (Python allows omitting for ASGI transport) - Match Python's strict error behavior: throw on empty messages, non-AIMessage types, and missing callbackThreadId - Add truncation suffix with task_id hint for long messages - Use generic error message in wrapModelCall (don't leak error details) - Remove duplicate notification guard (Python notifies on every error) - Add extractCallbackContext to async_subagents.ts: injects callbackThreadId into subagent input state when launching via start_async_task - Add tests for extractCallbackContext and callback context injection * cr * Rename completion notifier to completion callback Renamed completion notifier to completion callback for consistency with Python. * fix(sdk): `AsyncTask` `updatedAt` field doesn't update on task status changes (#400) * update updatedAt field to change on any task update * added changeset * chore: set up self hosted async subagent example (#399) * self hosted async subagent example * with postgres * formatting * eslint disable no console * fix dockerfile and readme * Update examples/async-subagent-server/server.ts Co-authored-by: Christian Bromann <git@bromann.dev> --------- Co-authored-by: Christian Bromann <git@bromann.dev> * chore(sdk): update async subagent middleware for agent protocol (#394) * update async subagent middleware for agent protocol * add changeset * Update libs/deepagents/src/middleware/async_subagents.ts Co-authored-by: Hunter Lovell <40191806+hntrl@users.noreply.github.com> * Update libs/deepagents/src/middleware/async_subagents.ts Co-authored-by: Hunter Lovell <40191806+hntrl@users.noreply.github.com> * Update libs/deepagents/src/middleware/async_subagents.ts Co-authored-by: Hunter Lovell <40191806+hntrl@users.noreply.github.com> * differentiate agent protocol --------- Co-authored-by: Hunter Lovell <40191806+hntrl@users.noreply.github.com> * chore(repo): migrate linting and formatting to oxc tooling (#391) * chore(repo): migrate linting and formatting to oxc tooling * cr * cr * chore(lint): clean up console disables for oxlint * cr * Apply suggestions from code review Co-authored-by: Christian Bromann <git@bromann.dev> --------- Co-authored-by: Christian Bromann <git@bromann.dev> * refactor(deepagents): clean up createDeepAgent middleware wiring (#392) * refactor(deepagents): clean up createDeepAgent middleware wiring * fix(deepagents): avoid duplicate HITL middleware on subagents * add comments, remove iife * Create ten-masks-flow.md * fix(deepagents): align prompt templates with runtime behavior (#393) * fix(deepagents): align prompt templates with runtime behavior * chore: add changeset for prompt alignment fixes * cr * cr * fix store backend and tests * lint * fix rests and resolveBackend * lint * fix failing tests * revert adapt resolve backend * fix resolve backend * better variable name * fix backend factory to return a maybe promise * mark resolve backend as internal * format --------- Co-authored-by: Colin Francis <131073567+colifran@users.noreply.github.com> Co-authored-by: pawel-twardziak <pawel.twardziak.dev@gmail.com> Co-authored-by: Christian Bromann <git@bromann.dev> Co-authored-by: Maahir Sachdev <maahir.sachdev@langchain.dev> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Colin Francis <colin.francis@langchain.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a pre-bundled
CompletionNotifierMiddlewarethat 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 callscheck_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.This is an opt-in middleware added to the subagent's stack, not the supervisor's. If the subagent doesn't use it, the extra input keys are silently ignored by LangGraph (verified against both in-process
invoke()andlanggraph devvia SDK).Architecture
Parent context propagation: The supervisor's
start_async_tasktool now includesparent_thread_id,parent_assistant_id, andtask_idin the subagent's input state. The notifier reads these from state (not config), which means they survive thread interrupts andupdate_async_taskcalls. If the subagent doesn't have the middleware, these keys are silently dropped.Notification format: Includes
task_idso the supervisor can correlate notifications back to specific tracked jobs when multiple tasks of the same subagent type run concurrently:Changes
New:
CompletionNotifierMiddleware(deepagents/middleware/completion_notifier.py)aafter_agenthook sends a completion notification with result summary (truncated to 500 chars)awrap_model_callhook catches errors and sends an error notification before re-raisingCompletionNotifierStateaddsparent_thread_id,parent_assistant_id, andtask_idto the subagent's state schemaget_client()with no URL (ASGI transport for same-deployment)Modified:
async_subagents.py_extract_parent_context()to read supervisor's thread_id and assistant_id from tool runtime configstart_async_task(sync + async) now includestask_id,parent_thread_id,parent_assistant_idin the subagent's inputModified:
pyproject.tomllanggraph-sdk>=0.1.51as a required dependency (was already imported unconditionally)Exports
CompletionNotifierMiddlewareexported fromdeepagents.middlewareand top-leveldeepagentsOpen questions
assistant_idavailability: Need to validate thatconfig.metadata.assistant_idis always present in deployed contexts. If there are cases where it's missing, the notifier silently no-ops (safe), but the notification won't fire.How to review
The core middleware is in
completion_notifier.py-- start there. Theasync_subagents.pychanges are mechanical (extract parent context, spread into input). Tests cover all the hooks, edge cases, and the parent context propagation.Note
This PR was developed with assistance from an AI coding agent.