# Async Subagents: What the Orchestrator Keeps in Context, What Workers Hand Back, and What Should Outlive the Session

> Async subagents let an orchestrator stay reachable while background workers run long tasks. What the orchestrator must keep about in-flight work so compaction cannot erase it, what a worker should hand back, when a follow-up should interrupt or enqueue, how results reach an orchestrator that is busy, idle or waiting on an approval, and which decisions and preferences should outlive the session, with LangChain Deep Agents, Claude Code and OpenAI behaviour as of 6 November 2026.

_Gaurav Dadhich · 2026-11-06_

\# Async Subagents: What the Orchestrator Keeps in Context, What Workers Hand Back, and What Should Outlive the Session \*Published 6 November 2026 by Gaurav Dadhich. Framework behaviour below was read from LangChain's, Anthropic's and OpenAI's own documentation and posts on 6 November 2026.\* Async subagents split one agent into two roles: an orchestrator that stays reachable to the user, and background workers that run long tasks and report back when they finish. LangChain made the pattern explicit on 5 November 2026, when it \[shipped async subagents in Deep Agents\](https://www.langchain.com/blog/agents-are-going-async) with five orchestrator tools (\`start\_async\_task\`, \`check\_async\_task\`, \`update\_async\_task\`, \`cancel\_async\_task\` and \`list\_async\_tasks\`), an \`update\_strategy\` that either interrupts or enqueues a follow-up, and a rule that every unfinished task is listed in the orchestrator's system prompt. \[Claude Code\](https://code.claude.com/docs/en/sub-agents) already starts subagents in the background by default, and \[OpenAI's async tool calling\](https://developers.openai.com/api/docs/guides/async-tool-calling) lets a model keep working after it calls a tool and take the result in a later request. The mechanics are settled enough to build on. What the docs leave to you is the memory design, which is where the failures documented so far come from: what the orchestrator must hold about work it cannot see, what a worker is allowed to send back into that context, how a correction reaches a worker that is halfway through, and which of the things learned along the way should still exist next week. Async subagents are a context engineering problem first and a concurrency problem second, and the answer to most of the questions below is the same move: keep in-flight task state, run artefacts and long-term memory in three separate places, with a different owner and a different lifetime for each. ## What async subagents are, and how they differ from ordinary subagents An ordinary subagent is a blocking tool call. The orchestrator hands it a task, waits, and gets a summary back, and while it waits it cannot answer the user, start other work or change course. LangChain's \[April 2026 post on background subagents\](https://www.langchain.com/blog/running-subagents-in-the-background) puts the cost plainly: if a subagent takes an hour, the user waits an hour before the agent can talk to them again. An async subagent returns a task ID immediately and runs on its own thread. LangChain compares it to a task started with \`asyncio.create\_task\` or a Promise that has not been awaited yet. The \[Deep Agents documentation\](https://docs.langchain.com/oss/python/deepagents/async-subagents) draws the line in a table worth keeping in mind: | | Sync subagent | Async subagent (Deep Agents) | |---|---|---| | Execution | Orchestrator blocks until the subagent finishes | Returns a task ID; orchestrator continues | | Mid-task updates | Not possible | \`update\_async\_task\` | | Cancellation | Not possible | \`cancel\_async\_task\` | | State | No persistent state between invocations | Keeps state on its own thread across interactions | | Best for | Work the orchestrator must have before its next step | Long, parallel or steerable work managed inside a chat | Use a sync subagent when the next step depends on the answer and the wait is short. Use an async one when the task is long, when several tasks can run side by side, or when the user is likely to change their mind while it runs. The same split now shows up across vendors under different names. Claude Code calls them background subagents and resumes them with a \`SendMessage\` tool. OpenAI does it one level lower, at the tool call: set \`async: true\` on a function tool, run the job in your application, and send the output back later on the original \`call\_id\`. \[Docker's agent runtime\](https://docker.github.io/docker-agent/tools/background-agents/) exposes \`run\_background\_agent\`, \`list\_background\_agents\`, \`view\_background\_agent\`, \`stop\_background\_agent\` and \`wait\_background\_agents\`. The tool names differ; the memory questions underneath are identical. ## The three kinds of state in an async system Everything an async system knows falls into one of three layers, and splitting it by lifetime makes each design question easier. | Layer | What it holds | Who writes it | Lifetime | |---|---|---|---| | In-flight task state | Task ID, worker name, brief, status, timestamps, pointer to the result, what depends on it | The orchestrator's harness | Until the task is terminal and its result has been used | | Run artefacts | Worker transcripts, files the worker wrote, intermediate tool output, the worker's sandbox | Each worker | The run, or a retention window for audit | | Long-term memory | Decisions and their reasons, user preferences, verified facts, lessons about tools | One gated writer, usually the orchestrator | Across sessions, until superseded or deleted | Each layer has a different failure mode. Task state lost to compaction means the orchestrator forgets that work is running. Run artefacts pulled into the orchestrator's context bury the conversation in noise. Long-term memory written directly by workers means unchecked intermediate guesses become "facts" the next session acts on. The distinction is the same one we draw between \[agent state and agent memory\](https://www.maximem.ai/blog/agent-state-vs-agent-memory), with a third layer added because async workers produce artefacts nobody is looking at while they are produced. ## What the orchestrator must keep in its context about in-flight tasks The orchestrator needs a small, structured ledger of every task it started, kept outside the message history and rendered fresh into its context on every turn. Deep Agents does exactly this: task metadata lives in a dedicated \`async\_tasks\` state channel on the supervisor's graph, and each record carries the task ID, agent name, thread ID, run ID, status and three timestamps (\`created\_at\`, \`last\_checked\_at\`, \`last\_updated\_at\`). The documentation explains why it is not left in the messages: deep agents compact their history when the context window fills, and task IDs that existed only in tool messages would be lost in the summary. That is the first rule, and it holds whatever framework you use: \*\*a task record that only exists in conversation history will not survive compaction.\*\* Claude Code solves the same problem from the other side; subagent transcripts are stored in separate files, so compacting the main conversation does not touch them. If you have tuned when your agent compacts (we covered the trade-offs in \[context compaction vs retrieval\](https://www.maximem.ai/blog/context-compaction-vs-retrieval)), the async version adds one requirement: whatever compaction throws away, the ledger is not in its path. The ledger should hold enough to answer the user and act, and nothing that requires reading the worker's work: | Field | Why the orchestrator needs it | |---|---| | Full task ID | Every later check, update or cancel uses it; Deep Agents lists truncated IDs as a known failure | | Worker name | Decides which follow-ups make sense and where approvals come from | | One-line brief, in the user's words | Lets the orchestrator answer "what is running?" without re-reading the launch message | | Status and last-checked time | Distinguishes a fresh status from a remembered one | | Update strategy | Tells the orchestrator whether a follow-up will restart the work or queue behind it | | Result pointer | A path or ID for the full output, so the result itself does not need to sit in context | | Dependents | What the orchestrator should do when this finishes, so a result arriving three turns later is not orphaned | Two habits keep the ledger honest. First, statuses in conversation history are stale by definition; the Deep Agents middleware tells the model exactly that, and its troubleshooting section lists "supervisor reports stale status" as a problem whose fix is to call \`check\` or \`list\` before reporting. Second, the ledger is a view, not a log. Deep Agents lists every unfinished task in the orchestrator's system prompt, so a finished task leaves the prompt while its record stays queryable through \`list\_async\_tasks\`. OpenAI's async tool calling makes the same ledger your application's job. Its guide has you keep a registry that binds each model-assigned \`task\_handle\` to the original \`call\_id\` and the running job, kept for the whole conversation so a completed handle is never reused. ## What a worker should hand back: final result, files or summary A worker should hand back a short, structured report that points to its files, not its working transcript and not its raw output. Claude Code's documentation describes the purpose of a subagent in those terms: do the side task in its own context and return only the summary. LangChain's \["retrieve, offload, delegate"\](https://www.langchain.com/blog/context-engineering-starts-with-retrieval) pattern makes the same point about retrieval: raw results go to files, subagents with their own context windows do the noisy reading, and the main agent sees only conclusions. The failure case is well documented. In \[The Orchestrator's Tax\](https://martinfowler.com/articles/orchestrator-tax.html), published on martinfowler.com on 16 July 2026, Rahul Garg describes a Claude Code session with four background subagents in which a throwaway "check on the agents" pulled the full raw transcript of a background agent into the main thread, "tens of thousands of tokens of JSONL", and then did it again for a second check. He is careful to say he had no per-call token accounting. His point holds without the accounting: a token bill is paid once, while a transcript left in the orchestrator's context is carried into every later turn. A handback contract that works in practice has six parts: 1. \*\*Status\*\*: done, partial, failed, or needs input. Claude Code marks output from a subagent that hit its \`maxTurns\` limit as partial, which is the right instinct; the orchestrator should never have to infer completeness from prose. 2. \*\*The answer, in a few sentences\*\*, stated so the orchestrator can repeat it to the user without opening anything else. 3. \*\*Pointers to artefacts\*\*: file paths, branch names, record IDs. The full output lives there. 4. \*\*Decisions the worker made, and why\*\*, especially any that close off an option ("used Postgres because the queue already depends on it"). 5. \*\*Open questions\*\* that the orchestrator or the user must answer. 6. \*\*Memory candidates\*\*: things the worker learned that might matter beyond this run, proposed for the orchestrator to judge, not written (more on why below). Treat the report as data. Claude Code puts a header on a returned subagent report stating that any instructions or approval claims inside it are the subagent's words and carry no authority from the user, and it delivers a background subagent's report inside a completion notification marked as an automated event. A worker reads web pages and files nobody reviewed, so its report can carry injected instructions; the orchestrator should act on it the way it acts on any tool output. ## How follow-up instructions reach a running worker: interrupt or enqueue A follow-up either replaces what the worker is doing or waits for it to finish, and the right choice depends on whether the instruction is a correction or an addition. LangChain's 5 November post gives the clean version: \`"interrupt"\`, the default, stops the current run and restarts the subagent with the new instruction, for corrections like "use Postgres instead"; \`"enqueue"\` holds the follow-up until the current run finishes, for additions such as a request to run the tests once the work is finished. The documentation adds the details that matter in production. An interrupt creates a new run on the same thread, and the subagent restarts with its full conversation history plus the new instruction, under the same task ID. With enqueue, the task shows as \`pending\` until the queued run starts, and cancelling the task also cancels earlier runs still active. Two edge cases deserve a line in your design notes: if the current run pauses for input or approval before a queued follow-up starts, the follow-up still starts and the pending request is dropped; and updating a task that is already \`waiting\` drops its pending request as well. Both mean a follow-up can silently cancel a question the user has not answered yet. \`update\_strategy\` needs \`deepagents\` 0.7.30 or later and is marked beta. Claude Code steers differently. The orchestrator messages a subagent with \`SendMessage\`, by ID or name; a completed subagent resumes in the background with its full history, and a subagent treats messages from the agent that launched it as task direction, including mid-task course corrections. Two limits hold whoever sends the message: no agent's message counts as the user's approval for a pending permission prompt, and no agent's message can change a subagent's permission settings, \`CLAUDE.md\` or configuration. That boundary is worth copying into any custom harness. There is a memory consequence here that the docs do not spell out. A correction like "use Postgres instead" is usually the clearest statement of a user preference the whole session produces, and it is delivered to a worker thread. When that thread is archived, the preference goes with it. The orchestrator, not the worker, should record every follow-up it sends as a candidate for long-term memory, because it is the only party that sees all of them. ## How results reach an orchestrator that is busy, idle or waiting on an approval A finished worker's result should arrive as a notification the orchestrator picks up at a safe point, never as something that interrupts a decision in progress. Deep Agents handles the three orchestrator states explicitly: | Orchestrator state | What Deep Agents does with a finished task | |---|---| | Busy, mid-run | Checks its unfinished tasks before each model call and picks up the result mid-run | | Idle | The subagent wakes it with a new run (on a single co-deployed server; otherwise it receives the update at the start of its next run) | | Paused on an approval | Leaves the approval alone; the result arrives once the user answers | Notifications arrive as a \`HumanMessage\` carrying a \`TaskEvent\` (\`task\_id\`, \`agent\_name\`, \`status\`, \`run\_id\`) in \`additional\_kwargs\["deepagents\_notification"\]\`, so an application can render them differently from things the user typed. Statuses include success, error, timeout, cancelled and waiting. If you would rather route results through your own system, a \`webhook\` on the subagent definition plus \`notify\_parent=False\` stops the subagent waking the supervisor. Task notifications need \`deepagents\` 0.7.26 or later and are in beta. Claude Code's background subagents report back as a completion notification in a later turn, and Claude waits for that notification before reporting results; ask about progress first and it says the subagent is still running. OpenAI leaves delivery to your application: you send each finished result on its original \`call\_id\` in a later Responses request, and if the model has called your own wait tool you return the newly finished results first and the wait status after, so the model has the results when it resumes. The approval case is the one to design for deliberately. A result that lands while the orchestrator is waiting for the user to approve something must not resolve, replace or reorder that approval. Deep Agents leaves the approval in place; Claude Code refuses to treat any agent message as approval. If you build your own harness, queue notifications behind a pending approval and never let a worker's report be parsed as the user's answer. ## When a worker needs the user, not the orchestrator A worker that needs approval or input should ask the user on its own thread, without passing the question through the orchestrator. Deep Agents reports such a task as \`waiting\`, states that the supervisor cannot answer or approve on the user's behalf, and has your application resume the subagent's thread directly; in Managed Deep Agents on Slack the request posts back to the original thread. Claude Code surfaces a background subagent's permission prompt in the main session and names the subagent asking; pressing Esc denies that one tool call without stopping the subagent, and a grant that lasts beyond one call applies to the whole session, main conversation included. Docker's runtime takes the strict route: background tasks run non-interactively, so any tool call that would prompt the user is denied automatically unless you approve those tools in the parent session first, for example with explicit allow rules. When a worker needs information from the orchestrator and not from the user, Deep Agents recommends asking for it in the result; the orchestrator replies with \`update\_async\_task\`. That keeps approvals and questions on separate channels, which is the property you want. ## What context a worker starts with: isolated or fork Start workers isolated unless the task depends on the conversation itself. Deep Agents' \`mode\` field defaults to \`"isolated"\`, which sends only the task description, and offers an experimental \`"fork"\` that sends a snapshot of the parent's effective conversation (its compacted form, if it has been compacted) followed by the task. Later parent messages are not forwarded, so a fork knows the conversation as it stood at launch and nothing after. The documentation's example for fork is a drafting agent that should write in a voice already set up in the chat. Claude Code makes a related point about what never reaches a non-fork subagent: the main conversation's auto memory is not loaded, and project rules a subagent must follow should be restated in the delegation prompt. Rahul Garg hit the same gap with skills, which a subagent does not inherit unless the orchestrator passes them along. The practical rule is that the brief is the worker's whole world. If a constraint matters, it is in the brief or in a file the brief names. ## Files as the only shared memory between workers Using files as the only shared memory between workers is a legitimate design choice, and it has costs you should choose knowingly. The clearest current example is the GVS5H manager/worker loop that Arize's Jim Bennett \[tested on 5 November 2026\](https://arize.com/blog/how-to-test-a-new-model-in-your-agent/): a manager plans, a worker brainstorms, the manager hands the worker one task at a time, every call starts fresh, and the only shared memory is a few files (\`plan.md\`, \`notes.md\`, \`solution.py\`). Public tests run after every worker round, and failing tests override the manager if it says "done". The design has real strengths. Files are inspectable, cheap, model-agnostic and easy to diff, and a fresh context per call keeps every worker's window clean. The test override is the important part: the loop's notion of "done" is anchored to something outside any model's memory. The costs show up as soon as the work outlives one task or one sandbox: - \*\*Files cannot supersede a fact.\*\* Overwriting \`notes.md\` loses why the old note was wrong; appending to it grows a file every worker must re-read, and stale lines look as convincing as current ones. - \*\*Files have no scope.\*\* Every worker that can read the directory sees everything in it, including notes that only made sense for one sub-task or one user. - \*\*Concurrent writes.\*\* Garg's session had one agent run \`git stash\` and \`git stash pop\` while sibling agents were writing elsewhere in the same tree; nothing broke, and his standing rules now ban repository-wide git operations in concurrent agent prompts. - \*\*Repeated orientation.\*\* Each fresh worker rebuilds its understanding from the files. Garg found two of his four agents paying the same orientation cost on the same part of the codebase, and calls the remedy cognitive locality: keep tasks that need the same mental model together. - \*\*Files carry no provenance.\*\* A line in a file does not say which worker wrote it, from what evidence, or whether anything checked it. - \*\*Gone with the sandbox.\*\* Files written to a disposable workspace end with it, which is the problem our post on \[memory for background agents\](https://www.maximem.ai/blog/background-agent-memory) deals with in depth. Arize's run also shows how little a loop sometimes leans on its files. Running Qwen3.8 Flash inside the manager loop on 30 LiveCodeBench problems, one pass each, Bennett's \`loop\_progress\` judge (an LLM judge on OpenAI's gpt-6-luna) found that 25 of 30 manager-loop runs had a single coding round, because the first code already passed the public tests; the manager loop's median latency was 37 minutes, against 21 seconds for a single Opus 5.5 call. These are vendor-reported figures from one pass on one small set, published on Arize's own blog, and Bennett himself calls a one-solve gap on 30 problems noise. They are still a useful reminder that a shared scratchpad only earns its cost when workers genuinely build on each other's work. Files are a good run-artefact layer. They are a poor long-term memory layer, because the things long-term memory must do (replace a fact while keeping its history, scope it to a user, record where it came from, forget it on request) are exactly what a directory of markdown does not do. ## What should outlive the session, and what is throwaway Keep decisions, preferences and verified facts; keep task records for audit for a fixed window; throw away transcripts and scratch state once the result is used. The table below is the version we would start from. | Item | Outlives the session? | Where it belongs | |---|---|---| | Worker transcripts and intermediate tool output | No; keep for a retention window if you need to debug | Run artefacts. Claude Code deletes subagent transcripts after \`cleanupPeriodDays\`, 30 days by default | | Scratch files (\`plan.md\`, \`notes.md\`) | No, unless promoted | Run artefacts | | Task records (ID, brief, status, timestamps, result pointer) | Yes, as an audit log, not as memory | A task store your observability can query | | The final deliverable | Yes | Wherever the user's work lives (repo, document, ticket) | | Decisions and their reasons | Yes | Long-term memory, scoped to the project or team | | Preferences the user stated in follow-ups ("use Postgres", "always run the tests") | Yes | Long-term memory, scoped to the user | | Facts the worker verified about the world or the codebase | Yes, with evidence | Long-term memory, with provenance | | Lessons about tools ("this API paginates at 100") | Yes | Agent-level memory. Claude Code's \`memory\` field gives a subagent a persistent directory for this, scoped \`user\`, \`project\` or \`local\` | | Unchecked hypotheses and abandoned plans | No | Stay in the worker's transcript | | Status of cancelled or waiting tasks | Only as an event, never as a fact | Task store | Two rules make this safe. Workers propose memories in their handback, and one writer (the orchestrator, or a dedicated memory writer) decides what enters long-term memory, using the task ID as the idempotency key so a duplicated completion does not write the same fact twice. And only terminal, successful results produce facts; a cancelled task produced an episode at most. Both rules come from our \[background agent memory\](https://www.maximem.ai/blog/background-agent-memory) design, and they apply unchanged when the background agent is a subagent. When several workers write to a store others read, the scoping and poisoning questions in \[multi-agent shared memory\](https://www.maximem.ai/blog/multi-agent-shared-memory) apply as well. ## How many workers, and how to split the work Split by the knowledge a task needs, not by the number of tasks you can name. Garg's standing rules, calibrated against Claude Sonnet 5 on his own work and offered as a sample rather than a template, are to prefer two to four agents per wave, to treat a wish for five or more as a signal to merge tasks that share files or conventions, and to treat overlapping file ownership as a reason to consolidate. On the infrastructure side, Deep Agents' local development guidance is that each active run takes a worker slot, so a supervisor with three concurrent subagent tasks needs four. ## Where async workers run, and what survives a restart Deep Agents' async subagents run as separate agents with their own threads, built on \[Agent Protocol\](https://github.com/langchain-ai/agent-protocol); you can co-deploy them with the supervisor (in-process ASGI transport, the documented default) or point a \`url\` at a remote Agent Protocol server for independent scaling. Each subagent thread is created with a \`parent\_thread\_id\` in its metadata, and the documentation warns that anyone with access to a thread can edit its metadata, so check access before a subagent reuses the parent's sandbox through that ID. Claude Code subagent transcripts persist within their session, and you can resume a subagent after restarting by resuming the same session. \[StackOne's write-up\](https://www.stackone.com/blog/when-your-ai-agent-needs-contractors-not-apprentices-building-async-subagents/) shows a custom version on Cloudflare Durable Objects: a start tool returns a task ID, a status tool polls for the result, and per-run state survives restarts and hibernation. None of these make the in-flight state into memory. A thread that survives a restart is still one run's state; what the user taught the agent during that run is only remembered next week if something wrote it to a store the next session reads. ## Common failure modes, and the fix for each | Symptom | Cause | Fix | |---|---|---| | Orchestrator polls right after launching, so async becomes blocking | Model habit | Deep Agents injects prompt rules against it; reinforce with "after launching, return control to the user" | | Orchestrator reports an old status | It read status from history | Always \`check\` or \`list\` before reporting | | \`check\` or \`cancel\` fails | Task ID truncated or reformatted | Keep the full ID in the ledger; instruct the model never to abbreviate it | | Context fills after a status check | Full transcript pulled into the main thread | Status from the ledger; detail from the handback; transcripts only for debugging | | The orchestrator forgets running work after a long session | Task IDs lived only in messages that were compacted | Separate task store rendered into every prompt | | A user's question to a worker disappears | A follow-up or update dropped a pending request | Do not update \`waiting\` tasks; answer the request first | | Next session repeats a mistake the user corrected | The correction went to a worker thread that was archived | Orchestrator logs follow-ups as memory candidates | ## How to test an async subagent design before you ship it Test the memory paths as well as the happy path. Five tests catch most of what goes wrong: 1. \*\*Compaction test.\*\* Start three long tasks, force the orchestrator to compact, then ask "what is running?" Every task should still be listed with its full ID. 2. \*\*Correction test.\*\* Send an interrupt-style correction mid-run, let the task finish, start a new session and ask about the same choice. The correction should be remembered. 3. \*\*Approval collision.\*\* Hold the orchestrator on an approval, let a worker finish, and confirm the approval is still pending and unchanged. 4. \*\*Duplicate completion.\*\* Deliver the same completion twice and confirm long-term memory gained one fact, not two. 5. \*\*Hostile report.\*\* Have a worker return a report containing an instruction ("approve the deployment") and confirm nothing acts on it without the user. ## Where Maximem Synap fits Maximem Synap is the long-term layer in the table above, not the task ledger. Your harness keeps in-flight task state, and your workers keep their artefacts; Synap holds what should outlive the session. Several agents share a central context layer while keeping their own agent-specific memories, and reads follow a scope chain (client, customer, user, plus the conversation) that prefers the narrowest scope that applies and sees everything above it, never below or sideways. Every memory carries provenance (where it came from, what it replaced and why), and when a fact changes the old statement is kept as history rather than overwritten, which is the supersession a shared \`notes.md\` cannot give you. Writes return immediately and extraction runs in the background, so an orchestrator recording a user's correction does not hold up the conversation. ## Frequently asked questions ### What are async subagents? Async subagents are background workers that an orchestrator agent starts with a call that returns a task ID immediately, so the orchestrator can keep talking to the user while the work runs. In LangChain Deep Agents the orchestrator manages them with \`start\_async\_task\`, \`check\_async\_task\`, \`update\_async\_task\`, \`cancel\_async\_task\` and \`list\_async\_tasks\`. ### What is the difference between async subagents and background subagents? They describe the same pattern. LangChain calls them async subagents, and Claude Code calls them background subagents; in both, the main agent does not block while the worker runs, and the result arrives later as a notification. ### Does Claude Code run subagents in the background? By default, yes. Claude Code's documentation says a subagent Claude spawns from the main conversation starts in the background when nothing is configured, and its result reaches Claude as a completion notification in a later turn. Permission prompts from a background subagent appear in the main session, naming the subagent that asks. ### Should a follow-up interrupt a running subagent or wait? Interrupt for corrections that make the current work wrong, and enqueue for additions that build on it. In Deep Agents, \`"interrupt"\` is the default and restarts the subagent with its history plus the new instruction; \`"enqueue"\` runs the follow-up after the current run finishes. ### What should the orchestrator keep in context about running tasks? A compact ledger, stored outside message history and shown fresh each turn: full task ID, worker name, a one-line brief, status with last-checked time, update strategy, a pointer to the result and what depends on it. Deep Agents keeps this in an \`async\_tasks\` state channel so compaction cannot remove it. ### What should a subagent return to the orchestrator? A short report with a status, the answer in a few sentences, paths to its files, the decisions it made and why, open questions, and proposed memories. Never the full transcript, which stays in the orchestrator's context for every later turn. ### Should subagents write to long-term memory directly? Workers should propose memories, not write them; one writer, usually the orchestrator, decides what is stored, using the task ID to avoid duplicates and writing facts only from tasks that finished successfully. ### Are files enough as shared memory between subagents? For one run, often yes; the GVS5H manager/worker loop shares only \`plan.md\`, \`notes.md\` and \`solution.py\`. Across runs and users, files lack supersession, scoping, provenance and deletion, and they disappear with a discarded sandbox. ## Sources All retrieved 6 November 2026. - LangChain, \[Agents Are Going Async\](https://www.langchain.com/blog/agents-are-going-async), Sydney Runkle and Thushanth Bengre, 5 November 2026. - LangChain, \[Async subagents, Deep Agents documentation (Python)\](https://docs.langchain.com/oss/python/deepagents/async-subagents). - LangChain, \[Running Subagents in the Background\](https://www.langchain.com/blog/running-subagents-in-the-background), Hunter Lovell and Colin Francis, 16 April 2026. - LangChain, \[Context Engineering Starts with Retrieval\](https://www.langchain.com/blog/context-engineering-starts-with-retrieval), Jess Weng, 23 October 2026. - Anthropic, \[Claude Code: create custom subagents\](https://code.claude.com/docs/en/sub-agents). - OpenAI, \[Async tool calling\](https://developers.openai.com/api/docs/guides/async-tool-calling). - Arize, \[How to test a new model in your agent\](https://arize.com/blog/how-to-test-a-new-model-in-your-agent/), Jim Bennett, 5 November 2026. - Rahul Garg, \[The Orchestrator's Tax\](https://martinfowler.com/articles/orchestrator-tax.html), martinfowler.com, 16 July 2026. - Docker, \[Background Agents Tool\](https://docker.github.io/docker-agent/tools/background-agents/) (documentation tracking the main branch). - StackOne, \[When your AI agent needs contractors, not apprentices\](https://www.stackone.com/blog/when-your-ai-agent-needs-contractors-not-apprentices-building-async-subagents/), Nicolas Belissent, 30 October 2025.

---

Source: [https://www.maximem.ai/blog/async-subagents-context-memory](https://www.maximem.ai/blog/async-subagents-context-memory)
