fix: fork subagents recursed forever in the Claude Code export
A fork subagent's transcript opens with a copy of the parent turn that spawned it, its own Agent call included; folding that call re-entered the same fork until RecursionError, failing every daily sync since 2026-09-24. Skip a spawn call while its own subagent is being expanded, and strip the <fork-boilerplate> preamble. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LbnmGHnFqDjyhPcCg1SEfF
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b6ce636891
commit
64068bb19b
@@ -118,7 +118,10 @@ _HARNESS_TAG_RE = re.compile(
|
||||
r"|<command-args>.*?</command-args>"
|
||||
r"|<command-contents>.*?</command-contents>"
|
||||
r"|<local-command-stdout>.*?</local-command-stdout>"
|
||||
r"|<system-reminder>.*?</system-reminder>",
|
||||
r"|<system-reminder>.*?</system-reminder>"
|
||||
# A fork subagent's first user turn: generic worker rules ahead of the
|
||||
# fork's actual "Your directive: …", which is kept.
|
||||
r"|<fork-boilerplate>.*?</fork-boilerplate>",
|
||||
re.DOTALL,
|
||||
)
|
||||
|
||||
@@ -530,6 +533,7 @@ def _extract_messages(
|
||||
policy: str,
|
||||
subagents: dict | None = None,
|
||||
include_sidechain: bool = False,
|
||||
expanding: frozenset[str] = frozenset(),
|
||||
) -> list[dict]:
|
||||
"""Normalize Claude Code records into messages.
|
||||
|
||||
@@ -540,6 +544,13 @@ def _extract_messages(
|
||||
those recursive subagent passes (subagent records are flagged
|
||||
``isSidechain``); the top-level pass keeps skipping sidechain records so a
|
||||
subagent is never also emitted as a stray top-level turn.
|
||||
|
||||
``expanding`` holds the spawn ids of the subagents being folded around
|
||||
this pass. A ``fork`` subagent's transcript opens with a copy of the parent
|
||||
turn that spawned it, its own spawn call included; that copy is dropped,
|
||||
since the enclosing subagent block already stands for it. Expanding it
|
||||
again recursed without end (RecursionError, every daily sync from
|
||||
2026-09-24).
|
||||
"""
|
||||
subagents = subagents or {}
|
||||
messages: list[dict] = []
|
||||
@@ -620,6 +631,8 @@ def _extract_messages(
|
||||
elif item_type == "tool_use":
|
||||
name = item.get("name") or "tool"
|
||||
tool_id = item.get("id")
|
||||
if tool_id in expanding:
|
||||
continue
|
||||
if name in _SUBAGENT_TOOL_NAMES and tool_id in subagents:
|
||||
# A Task/Agent spawn: fold its separate transcript inline
|
||||
# instead of collapsing it. Its own tool traffic is
|
||||
@@ -633,6 +646,7 @@ def _extract_messages(
|
||||
policy,
|
||||
subagents,
|
||||
include_sidechain=True,
|
||||
expanding=expanding | {tool_id},
|
||||
)
|
||||
blocks.append(
|
||||
make_subagent_block(
|
||||
|
||||
Reference in New Issue
Block a user