Build1 distinct publisher3 min readPublished
The framework never intersects a child's tool list with its caller's, so a supervisor holding one tool can spawn a writer that searches the web. The intersection is a middleware you have to write yourself.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
The seam LangChain gives you here is well placed. A middleware's `wrap_tool_call` receives the `ToolCallRequest` and the handler that would execute the tool; skip the handler and the tool body never runs, and that is a documented parameter rather than a monkeypatch [8]. So the intersection nobody ships is a membership test you own: keep the parent's tool names in a set, compare, return a `ToolMessage` with `status="error"` on a miss [8]. In the second run of the same tree, with `BoundedByParent({"write_brief"})` on the writer spec, only `write_brief` executed [9].
Why the parent cannot catch this itself is the part to keep. Deep Agents folds a subagent's whole transcript into one `ToolMessage`, so printing the supervisor's messages shows "Brief written." and no sign of the search [6]. The supervisor's record of its own delegation reads like a well-drilled employee's status update. Recording what a child called has to happen at the tool boundary; that is the only place the data exists [6].
Transfer conditions matter more than the demo. The run is pinned to langchain 1.3.15 and deepagents 0.7.6, dated 2026-09-02, with no API key and a scripted model [7]. A scripted model means this shows only that the framework performs no check; the trigger was a note planted in the subagent's input, and a production model's tendency to take that bait is a separate question this test does not settle [2]. For the finding to reach your tree, two things have to hold: some subagent spec names a tool its caller does not hold [1], and content you do not control lands in that subagent's input [2]. If every spec is a subset of the parent by construction, the guard is a no-op you keep for the day someone adds a tool to one role.
Names are the cheap version, and the author of the dev.to writeup says they were not enough for him: a child holding `web_search` with a 10,000 row limit has a much wider reach than one holding it with 50 [10]. His follow-on library replaces names with scopes, ceilings and a lifetime, computing the child as the meet of parent and request [10]. In its sample output the researcher asked for `admin.export`, `web.*`, 10,000 rows and a 9,999 second TTL, and was granted `web.search`, 50 rows and 3,600 seconds [11]: a 200x cut to the row ceiling and 6,399 seconds off the lifetime [13][14]. Those figures, and the hash-chained audit that verifies over 8 events [12], come from the author's own tool demonstrating itself. The two-line finding above does not depend on them.
Upstream, core still has no subagent middleware. Issue #33879 was filed by a maintainer on 2025-11-07 and remains open, with one PR closed unmerged and one still a draft [5]. That is roughly ten months of open issue by the date of this test [15]. Anyone deferring the local fix is scheduling around a draft PR.
Ranked by verification strength, evidence, and original report placement.
In a test on Deep Agents 0.7.6, a supervisor agent was given exactly one tool, write_brief; it spawned a writer subagent whose spec also listed web_search.
The writer subagent ran a web search for 'site:exfil.example internal customer list' because a note in its input told it to, and nothing checked that call against what the supervisor held; the tool bodies that ran were web_search and write_brief.
LangChain's Deep Agents permissions page states: 'Subagents inherit the parent agent's permissions by default... This replaces the parent's rules entirely.'
In Deep Agents each subagent spec carries its own tools list, and deepagents/middleware/subagents.py compiles each spec with create_sub_agent(spec); there is no step that intersects that list with the caller's.
LangChain core has no subagent middleware yet; issue #33879, 'Add subagent middleware', was filed by a LangChain maintainer on 2025-11-07 and is still open, with PR 33484 closed unmerged and PR 39019 a draft.
Deep Agents collapses a subagent's whole transcript into a single ToolMessage for the supervisor; printing the supervisor's messages shows 'Brief written.' and not the writer's search, so to know what a subagent called you have to record it at the tool boundary yourself.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · September 2, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
A twelve-word joke became a discipline, and one seven-step chain had no loop to remove1 distinct publisher
build
Microsoft ships an MIT-licensed agent kernel: policy rings, Ed25519 identity, kill switch1 distinct publisher
build
AWS puts agent evaluation on OpenTelemetry, and on exactly three span roles2 distinct publishers
build
Every LangGraph node needs its own try/catch by about task 201 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Re-runnable, but only one person has run it
Almost everything about the framework half can be checked by a stranger: package versions pinned, the compiling file named, the escalation quoted from LangChain's own permissions page, the issue and both pull requests numbered, and a control run sitting beside the guarded one. What holds the score down is the witness count — a single self-published post, one machine, and a scripted model standing in for a real one, so the escalation is demonstrated by construction rather than observed in the wild.
One laptop, one stalled issue
The deployment record is two runs on the author's own machine plus a library you can pip install. Upstream, the request for a subagent middleware has sat open roughly ten months with a closed pull request and a draft behind it — which is evidence that the default persists, not that anyone has adopted a fix. No user, download count, or third-party tree appears anywhere in this reporting.
Straight on the framework, taller on the library
The headline finding is, if anything, underclaimed — LangChain's manual says the child spec replaces the parent's rules, so 'fresh grant instead of narrowing' is a reading of the documentation rather than an accusation. The library half tilts the other way: integrity, monotonicity, containment and anchor=verified is a lot of certification vocabulary for lines printed by the tool being sold, and the real limits — a direct .invoke() bypasses the seam, a spec missing the middleware is a hole — arrive after the pitch rather than with it.
The finder sells the fix
The person who found the gap ships the remedy in the same post, with the install line and the clone command. He is open about it, and the eight-line middleware is genuinely free — but the escalation demo, the row-ceiling argument and the signed evidence bundle all funnel toward attenu-guard, and a self-publishing platform applies no editorial friction to that arc. Worth noting the counterweight: he pins a test that is designed to fail the day upstream makes his premise wrong.
Solid on the behaviour, thin on witnesses
We are confident about what Deep Agents 0.7.6 does and about the state of the upstream issue, because both are quoted, versioned and checkable in minutes. Confidence drops sharply past that line: one publisher, one author, a scripted model instead of a live one, and every attenuation and audit result attested by the software being promoted.