Refine course progression and runtime safety

This commit is contained in:
Haoran
2026-08-11 15:13:13 +08:00
parent b36dbcd84f
commit ab35e59672
83 changed files with 5291 additions and 2267 deletions

View File

@@ -24,7 +24,7 @@
<!-- Title -->
<rect x="0" y="0" width="800" height="48" fill="url(#header)" rx="8"/>
<rect x="0" y="40" width="800" height="8" fill="url(#header)"/>
<text x="400" y="31" fill="#fff" font-size="16" font-weight="700" text-anchor="middle">Subagent — Independent messages[], All Intermediate Steps Discarded</text>
<text x="400" y="31" fill="#fff" font-size="16" font-weight="700" text-anchor="middle">Subagent — Fresh messages[], Final Text Returns</text>
<!-- ===== Parent Agent (left) ===== -->
<rect x="30" y="68" width="310" height="268" rx="12" fill="#f0f4ff" stroke="#2563eb" stroke-width="2"/>
@@ -54,9 +54,9 @@
<text x="122" y="206" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">Base Tools</text>
<text x="122" y="219" fill="#64748b" font-size="8" text-anchor="middle">bash / read / write / ...</text>
<!-- task → spawn -->
<!-- task runs the nested loop -->
<rect x="200" y="193" width="110" height="26" rx="4" fill="#ede9fe" stroke="#7c3aed" stroke-width="1.5"/>
<text x="255" y="210" fill="#5b21b6" font-size="10" font-weight="600" text-anchor="middle">task → spawn</text>
<text x="255" y="210" fill="#5b21b6" font-size="10" font-weight="600" text-anchor="middle">task → run</text>
<!-- Parent tool_result target -->
<rect x="190" y="270" width="120" height="34" rx="5" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
@@ -86,16 +86,16 @@
<rect x="455" y="150" width="300" height="56" rx="8" fill="#ede9fe" stroke="#7c3aed" stroke-width="1" stroke-dasharray="4,2"/>
<text x="605" y="170" fill="#5b21b6" font-size="10" font-weight="600" text-anchor="middle">Own while loop (max 30 rounds)</text>
<text x="605" y="186" fill="#5b21b6" font-size="9" text-anchor="middle">bash · read · write · edit · glob</text>
<text x="605" y="198" fill="#94a3b8" font-size="8" text-anchor="middle">No task — recursive spawn forbidden</text>
<text x="605" y="198" fill="#94a3b8" font-size="8" text-anchor="middle">No task — one delegation level</text>
<!-- intermediate results → discard -->
<rect x="460" y="218" width="290" height="44" rx="6" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="4,2"/>
<text x="605" y="238" fill="#64748b" font-size="10" text-anchor="middle">Intermediate 30+ tool calls + results</text>
<text x="605" y="254" fill="#dc2626" font-size="10" font-weight="600" text-anchor="middle">All discarded ✗</text>
<!-- intermediate results remain in the local message list -->
<rect x="460" y="218" width="290" height="44" rx="6" fill="#f8fafc" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4,2"/>
<text x="605" y="238" fill="#64748b" font-size="10" text-anchor="middle">Subagent tool calls + results</text>
<text x="605" y="254" fill="#64748b" font-size="10" font-weight="600" text-anchor="middle">Not copied to parent messages[]</text>
<!-- extract only last text -->
<rect x="460" y="272" width="290" height="28" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
<text x="605" y="290" fill="#166534" font-size="10" font-weight="600" text-anchor="middle">✓ Extract only final text → return to Parent</text>
<text x="605" y="290" fill="#166534" font-size="10" font-weight="600" text-anchor="middle">Final text → Parent tool_result</text>
<!-- ===== dispatch line: Parent → Subagent (top) ===== -->
<path d="M 310 206 L 362 206 Q 370 206 370 198 L 370 126 Q 370 118 378 118 L 450 118" fill="none" stroke="#7c3aed" stroke-width="2.5" marker-end="url(#arrow-purple)"/>
@@ -111,15 +111,15 @@
<rect x="60" y="370" width="680" height="56" rx="8" fill="#f1f5f9"/>
<rect x="80" y="384" width="16" height="12" rx="3" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
<text x="104" y="394" fill="#334155" font-size="10">s05 Preserved: loop, hooks, todo_write, 6 base tools</text>
<text x="104" y="394" fill="#334155" font-size="10">Parent tools: 5 base tools + task</text>
<rect x="80" y="404" width="16" height="12" rx="3" fill="#ede9fe" stroke="#7c3aed" stroke-width="1"/>
<text x="104" y="414" fill="#334155" font-size="10">s06 New: task tool + spawn_subagent() — independent messages[], returns only summary</text>
<text x="104" y="414" fill="#334155" font-size="10">Subagent tools: 5 base tools, no task</text>
<!-- Data flow labels -->
<rect x="430" y="440" width="310" height="44" rx="6" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
<text x="445" y="458" fill="#7c3aed" font-size="10" font-weight="600">① Parent → Sub:</text>
<text x="580" y="458" fill="#64748b" font-size="10">task description (a short string)</text>
<text x="580" y="458" fill="#64748b" font-size="10">task prompt (a short string)</text>
<text x="445" y="476" fill="#16a34a" font-size="10" font-weight="600">② Sub → Parent:</text>
<text x="580" y="476" fill="#64748b" font-size="10">extract_text() (final conclusion only)</text>
</svg>

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -24,7 +24,7 @@
<!-- タイトル -->
<rect x="0" y="0" width="800" height="48" fill="url(#header)" rx="8"/>
<rect x="0" y="40" width="800" height="8" fill="url(#header)"/>
<text x="400" y="31" fill="#fff" font-size="14" font-weight="700" text-anchor="middle">Subagent — 独立した messages[]、中間過程はすべて破棄</text>
<text x="400" y="31" fill="#fff" font-size="14" font-weight="700" text-anchor="middle">Subagent — 新しい messages[]、最終テキストを親へ返す</text>
<!-- ===== 親 Agent左側 ===== -->
<rect x="30" y="68" width="310" height="268" rx="12" fill="#f0f4ff" stroke="#2563eb" stroke-width="2"/>
@@ -54,9 +54,9 @@
<text x="122" y="206" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">基本ツール</text>
<text x="122" y="219" fill="#64748b" font-size="8" text-anchor="middle">bash / read / write / ...</text>
<!-- task → spawn -->
<!-- task が入れ子のループを実行 -->
<rect x="200" y="193" width="110" height="26" rx="4" fill="#ede9fe" stroke="#7c3aed" stroke-width="1.5"/>
<text x="255" y="210" fill="#5b21b6" font-size="10" font-weight="600" text-anchor="middle">task → spawn</text>
<text x="255" y="210" fill="#5b21b6" font-size="10" font-weight="600" text-anchor="middle">task → run</text>
<!-- Parent tool_result target -->
<rect x="190" y="270" width="120" height="34" rx="5" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
@@ -86,16 +86,16 @@
<rect x="455" y="150" width="300" height="56" rx="8" fill="#ede9fe" stroke="#7c3aed" stroke-width="1" stroke-dasharray="4,2"/>
<text x="605" y="170" fill="#5b21b6" font-size="10" font-weight="600" text-anchor="middle">独自の while ループ(最大 30 ラウンド)</text>
<text x="605" y="186" fill="#5b21b6" font-size="9" text-anchor="middle">bash · read · write · edit · glob</text>
<text x="605" y="198" fill="#94a3b8" font-size="8" text-anchor="middle">task なし — 再帰 spawn 禁止</text>
<text x="605" y="198" fill="#94a3b8" font-size="8" text-anchor="middle">task なし — 委任は 1 階層</text>
<!-- 中間結果 → 破棄 -->
<rect x="460" y="218" width="290" height="44" rx="6" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="4,2"/>
<text x="605" y="238" fill="#64748b" font-size="10" text-anchor="middle">中間 30+ ラウンドのツール呼び出し + 結果</text>
<text x="605" y="254" fill="#dc2626" font-size="10" font-weight="600" text-anchor="middle">すべて破棄 ✗</text>
<!-- 中間結果は子の messages にだけ残る -->
<rect x="460" y="218" width="290" height="44" rx="6" fill="#f8fafc" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4,2"/>
<text x="605" y="238" fill="#64748b" font-size="10" text-anchor="middle">のツール呼び出し結果</text>
<text x="605" y="254" fill="#64748b" font-size="10" font-weight="600" text-anchor="middle">親 messages[] へコピーしない</text>
<!-- 最後のテキストのみ抽出 -->
<rect x="460" y="272" width="290" height="28" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
<text x="605" y="290" fill="#166534" font-size="10" font-weight="600" text-anchor="middle">✓ 最後のテキストのみ抽出 → 親に返却</text>
<text x="605" y="290" fill="#166534" font-size="10" font-weight="600" text-anchor="middle">最終テキスト → Parent tool_result</text>
<!-- ===== ディスパッチ線:親 → サブエージェント(上) ===== -->
<path d="M 310 206 L 362 206 Q 370 206 370 198 L 370 126 Q 370 118 378 118 L 450 118" fill="none" stroke="#7c3aed" stroke-width="2.5" marker-end="url(#arrow-purple)"/>
@@ -111,15 +111,15 @@
<rect x="60" y="370" width="680" height="56" rx="8" fill="#f1f5f9"/>
<rect x="80" y="384" width="16" height="12" rx="3" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
<text x="104" y="394" fill="#334155" font-size="10">s05 保持ループ、フック、todo_write、6 つの基本ツール</text>
<text x="104" y="394" fill="#334155" font-size="10">親 Agent のツール5 つの基本ツール + task</text>
<rect x="80" y="404" width="16" height="12" rx="3" fill="#ede9fe" stroke="#7c3aed" stroke-width="1"/>
<text x="104" y="414" fill="#334155" font-size="10">s06 新規task ツール + spawn_subagent() — 独立 messages[]、要約のみ返却</text>
<text x="104" y="414" fill="#334155" font-size="10">子 Agent のツール5 つの基本ツール、task なし</text>
<!-- データフローラベル -->
<rect x="430" y="440" width="310" height="44" rx="6" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
<text x="445" y="458" fill="#7c3aed" font-size="10" font-weight="600">① 親 → サブ:</text>
<text x="580" y="458" fill="#64748b" font-size="10">task description(短い文字列)</text>
<text x="580" y="458" fill="#64748b" font-size="10">task prompt(短い文字列)</text>
<text x="445" y="476" fill="#16a34a" font-size="10" font-weight="600">② サブ → 親:</text>
<text x="580" y="476" fill="#64748b" font-size="10">extract_text()(最終結論のみ)</text>
</svg>

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@@ -24,7 +24,7 @@
<!-- 标题 -->
<rect x="0" y="0" width="800" height="48" fill="url(#header)" rx="8"/>
<rect x="0" y="40" width="800" height="8" fill="url(#header)"/>
<text x="400" y="31" fill="#fff" font-size="16" font-weight="700" text-anchor="middle">Subagent — 独立 messages[]中间过程全部丢弃</text>
<text x="400" y="31" fill="#fff" font-size="16" font-weight="700" text-anchor="middle">Subagent — 全新 messages[]最终文本返回父循环</text>
<!-- ===== Parent Agent左侧 ===== -->
<rect x="30" y="68" width="310" height="268" rx="12" fill="#f0f4ff" stroke="#2563eb" stroke-width="2"/>
@@ -54,9 +54,9 @@
<text x="122" y="206" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">基础工具</text>
<text x="122" y="219" fill="#64748b" font-size="8" text-anchor="middle">bash / read / write / ...</text>
<!-- task → spawn -->
<!-- task 运行嵌套循环 -->
<rect x="200" y="193" width="110" height="26" rx="4" fill="#ede9fe" stroke="#7c3aed" stroke-width="1.5"/>
<text x="255" y="210" fill="#5b21b6" font-size="10" font-weight="600" text-anchor="middle">task → spawn</text>
<text x="255" y="210" fill="#5b21b6" font-size="10" font-weight="600" text-anchor="middle">task → run</text>
<!-- Parent tool_result target -->
<rect x="190" y="270" width="120" height="34" rx="5" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
@@ -86,16 +86,16 @@
<rect x="455" y="150" width="300" height="56" rx="8" fill="#ede9fe" stroke="#7c3aed" stroke-width="1" stroke-dasharray="4,2"/>
<text x="605" y="170" fill="#5b21b6" font-size="10" font-weight="600" text-anchor="middle">自己的 while 循环(最多 30 轮)</text>
<text x="605" y="186" fill="#5b21b6" font-size="9" text-anchor="middle">bash · read · write · edit · glob</text>
<text x="605" y="198" fill="#94a3b8" font-size="8" text-anchor="middle">无 task — 禁止递归 spawn</text>
<text x="605" y="198" fill="#94a3b8" font-size="8" text-anchor="middle">无 task — 只允许一层委派</text>
<!-- intermediate results → discard -->
<rect x="460" y="218" width="290" height="44" rx="6" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="4,2"/>
<text x="605" y="238" fill="#64748b" font-size="10" text-anchor="middle">中间 30+ 轮工具调用 + 结果</text>
<text x="605" y="254" fill="#dc2626" font-size="10" font-weight="600" text-anchor="middle">全部丢弃 ✗</text>
<!-- 中间结果只保留在子 Agent 消息列表中 -->
<rect x="460" y="218" width="290" height="44" rx="6" fill="#f8fafc" stroke="#94a3b8" stroke-width="1" stroke-dasharray="4,2"/>
<text x="605" y="238" fill="#64748b" font-size="10" text-anchor="middle">子 Agent 的工具调用结果</text>
<text x="605" y="254" fill="#64748b" font-size="10" font-weight="600" text-anchor="middle">不复制到父 messages[]</text>
<!-- extract only last text -->
<rect x="460" y="272" width="290" height="28" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
<text x="605" y="290" fill="#166534" font-size="10" font-weight="600" text-anchor="middle">✓ 只提取最后一段文本 → 返回给 Parent</text>
<text x="605" y="290" fill="#166534" font-size="10" font-weight="600" text-anchor="middle">最终文本 → Parent tool_result</text>
<!-- ===== dispatch 线Parent → Subagent走上面 ===== -->
<path d="M 310 206 L 362 206 Q 370 206 370 198 L 370 126 Q 370 118 378 118 L 450 118" fill="none" stroke="#7c3aed" stroke-width="2.5" marker-end="url(#arrow-purple)"/>
@@ -111,15 +111,15 @@
<rect x="60" y="370" width="680" height="56" rx="8" fill="#f1f5f9"/>
<rect x="80" y="384" width="16" height="12" rx="3" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
<text x="104" y="394" fill="#334155" font-size="10">s05 保留循环、hook、todo_write、6 个基础工具</text>
<text x="104" y="394" fill="#334155" font-size="10">父 Agent 工具5 个基础工具 + task</text>
<rect x="80" y="404" width="16" height="12" rx="3" fill="#ede9fe" stroke="#7c3aed" stroke-width="1"/>
<text x="104" y="414" fill="#334155" font-size="10">s06 新增task 工具 + spawn_subagent() — 独立 messages[],只回传摘要</text>
<text x="104" y="414" fill="#334155" font-size="10">子 Agent 工具5 个基础工具,无 task</text>
<!-- 数据流标注 -->
<rect x="430" y="440" width="310" height="44" rx="6" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
<text x="445" y="458" fill="#7c3aed" font-size="10" font-weight="600">① Parent → Sub</text>
<text x="580" y="458" fill="#64748b" font-size="10">task description(一小段文字)</text>
<text x="580" y="458" fill="#64748b" font-size="10">task prompt(一小段文字)</text>
<text x="445" y="476" fill="#16a34a" font-size="10" font-weight="600">② Sub → Parent</text>
<text x="580" y="476" fill="#64748b" font-size="10">extract_text()(只有最终结论)</text>
</svg>

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -52,7 +52,7 @@
<text x="556" y="94" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">TOOL DISPATCH (Lead 16 tools)</text>
<text x="394" y="109" fill="#2563eb" font-size="7.5">bash · read · write · task(4) · send · inbox</text>
<text x="394" y="121" fill="#7c3aed" font-size="7.5" font-weight="700">request_shutdown · request_plan · review_plan</text>
<text x="394" y="133" fill="#b45309" font-size="7.5" font-weight="700">create_worktree · remove_worktree</text>
<text x="394" y="133" fill="#b45309" font-size="7.5" font-weight="700">create_worktree · host cleanup</text>
<text x="394" y="145" fill="#dc2626" font-size="7.5" font-weight="700">★ connect_mcp + dynamic mcp__server__tool tools</text>
<!-- Loop back -->

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -52,7 +52,7 @@
<text x="556" y="94" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">TOOL DISPATCHLead 16 tools</text>
<text x="394" y="109" fill="#2563eb" font-size="7.5">bash · read · write · task(4) · send · inbox</text>
<text x="394" y="121" fill="#7c3aed" font-size="7.5" font-weight="700">request_shutdown · request_plan · review_plan</text>
<text x="394" y="133" fill="#b45309" font-size="7.5" font-weight="700">create_worktree · remove_worktree</text>
<text x="394" y="133" fill="#b45309" font-size="7.5" font-weight="700">create_worktree · host cleanup</text>
<text x="394" y="145" fill="#dc2626" font-size="7.5" font-weight="700">★ connect_mcp + 動的 mcp__server__tool ツール</text>
<!-- Loop back -->

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -52,7 +52,7 @@
<text x="556" y="94" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">TOOL DISPATCH (Lead 16 tools)</text>
<text x="394" y="109" fill="#2563eb" font-size="7.5">bash · read · write · task(4) · send · inbox</text>
<text x="394" y="121" fill="#7c3aed" font-size="7.5" font-weight="700">request_shutdown · request_plan · review_plan</text>
<text x="394" y="133" fill="#b45309" font-size="7.5" font-weight="700">create_worktree · remove_worktree</text>
<text x="394" y="133" fill="#b45309" font-size="7.5" font-weight="700">create_worktree · host cleanup</text>
<text x="394" y="145" fill="#dc2626" font-size="7.5" font-weight="700">★ connect_mcp + 动态 mcp__server__tool 工具</text>
<!-- Loop back -->

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -80,6 +80,6 @@
<text x="95" y="548" fill="#334155" font-size="9">durable work: task tools · cron tools</text>
<text x="510" y="512" fill="#334155" font-size="9">team: spawn_teammate · send_message · typed protocols</text>
<text x="510" y="530" fill="#334155" font-size="9">protocol: request_shutdown · request_plan · review_plan</text>
<text x="510" y="548" fill="#334155" font-size="9">workdir/plugin: create/remove_worktree · connect_mcp</text>
<text x="510" y="548" fill="#334155" font-size="9">workdir/plugin: create_worktree · connect_mcp</text>
<path d="M 850 518 L 890 518 L 890 152 L 850 152" fill="none" stroke="#475569" stroke-width="1.2" marker-end="url(#arrow)" stroke-dasharray="4,4"/>
</svg>

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -80,6 +80,6 @@
<text x="95" y="548" fill="#334155" font-size="9">durable work: task tools · cron tools</text>
<text x="510" y="512" fill="#334155" font-size="9">team: spawn_teammate · send_message · typed protocols</text>
<text x="510" y="530" fill="#334155" font-size="9">protocol: request_shutdown · request_plan · review_plan</text>
<text x="510" y="548" fill="#334155" font-size="9">workdir/plugin: create/remove_worktree · connect_mcp</text>
<text x="510" y="548" fill="#334155" font-size="9">workdir/plugin: create_worktree · connect_mcp</text>
<path d="M 850 518 L 890 518 L 890 152 L 850 152" fill="none" stroke="#475569" stroke-width="1.2" marker-end="url(#arrow)" stroke-dasharray="4,4"/>
</svg>

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -100,6 +100,6 @@
<text x="95" y="548" fill="#334155" font-size="9">durable work: create/list/get/claim/complete_task · schedule/list/cancel_cron</text>
<text x="510" y="512" fill="#334155" font-size="9">team: spawn_teammate · send_message · typed protocols</text>
<text x="510" y="530" fill="#334155" font-size="9">protocol: request_shutdown · request_plan · review_plan</text>
<text x="510" y="548" fill="#334155" font-size="9">workdir/plugin: create/remove_worktree · connect_mcp</text>
<text x="510" y="548" fill="#334155" font-size="9">workdir/plugin: create_worktree · connect_mcp</text>
<path d="M 850 518 L 890 518 L 890 152 L 850 152" fill="none" stroke="#475569" stroke-width="1.2" marker-end="url(#arrow)" stroke-dasharray="4,4"/>
</svg>

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -35,7 +35,7 @@
<line x1="312" y1="145" x2="352" y2="145" stroke="#22c55e" stroke-width="2" marker-end="url(#arrow-green)"/>
<rect x="354" y="118" width="220" height="54" rx="6" fill="#ffffff" stroke="#1a1a1a" stroke-width="1.5"/>
<text x="464" y="140" text-anchor="middle" fill="#1a1a1a" font-size="11" font-weight="700" font-family="monospace">Workflow({script, args})</text>
<text x="464" y="140" text-anchor="middle" fill="#1a1a1a" font-size="11" font-weight="700" font-family="monospace">Workflow({name, args})</text>
<text x="464" y="159" text-anchor="middle" fill="#888888" font-size="8.5" font-family="monospace">resume_from_run_id?</text>
<rect x="715" y="110" width="205" height="70" rx="6" fill="#fafafa" stroke="#1a1a1a" stroke-width="1.5"/>
@@ -78,7 +78,7 @@
<text x="588" y="371" fill="#22c55e" font-size="9" font-weight="600">agent()</text>
<rect x="475" y="390" width="190" height="64" rx="6" fill="#ffffff" stroke="#1a1a1a" stroke-width="1.5"/>
<text x="570" y="413" text-anchor="middle" fill="#1a1a1a" font-size="11" font-weight="700">Subagents × N</text>
<text x="570" y="413" text-anchor="middle" fill="#1a1a1a" font-size="11" font-weight="700">Agent runner calls × N</text>
<text x="570" y="432" text-anchor="middle" fill="#888888" font-size="9">schema validation · token budget</text>
<text x="570" y="447" text-anchor="middle" fill="#888888" font-size="8.5">parallel work, structured results</text>

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -93,14 +93,14 @@ function extractFunctions(
lines: string[]
): { name: string; signature: string; startLine: number }[] {
const functions: { name: string; signature: string; startLine: number }[] = [];
const funcPattern = /^def\s+(\w+)\((.*?)\)/;
const funcPattern = /^(async\s+)?def\s+(\w+)\((.*?)\)/;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(funcPattern);
if (!match) continue;
functions.push({
name: match[1],
signature: `def ${match[1]}(${match[2]})`,
name: match[2],
signature: `${match[1] ?? ""}def ${match[2]}(${match[3]})`,
startLine: i + 1,
});
}
@@ -108,12 +108,71 @@ function extractFunctions(
return functions;
}
function assignmentBody(source: string, openIndex: number): string {
const open = source[openIndex];
const close = open === "[" ? "]" : "}";
let depth = 0;
let quote = "";
let triple = false;
let escaped = false;
let comment = false;
for (let index = openIndex; index < source.length; index++) {
const char = source[index];
const nextThree = source.slice(index, index + 3);
if (comment) {
if (char === "\n") comment = false;
continue;
}
if (quote) {
if (escaped) {
escaped = false;
continue;
}
if (char === "\\") {
escaped = true;
continue;
}
if (triple && nextThree === quote.repeat(3)) {
quote = "";
triple = false;
index += 2;
} else if (!triple && char === quote) {
quote = "";
}
continue;
}
if (char === "#") {
comment = true;
continue;
}
if (char === '"' || char === "'") {
quote = char;
triple = nextThree === char.repeat(3);
if (triple) index += 2;
continue;
}
if (char === open) depth += 1;
if (char === close) {
depth -= 1;
if (depth === 0) return source.slice(openIndex, index + 1);
}
}
return "";
}
function extractTools(source: string): string[] {
const assignmentPattern = /^(?:TOOLS|BASE_TOOLS|BUILTIN_TOOLS|SUB_TOOLS|TASK_TOOL|WORKFLOW_TOOL)\s*=\s*([\[{])/gm;
const toolPattern = /"name"\s*:\s*"([\w-]+)"/g;
const tools = new Set<string>();
let match;
while ((match = toolPattern.exec(source)) !== null) {
tools.add(match[1]);
let assignment;
while ((assignment = assignmentPattern.exec(source)) !== null) {
const openIndex = assignment.index + assignment[0].lastIndexOf(assignment[1]);
const body = assignmentBody(source, openIndex);
let tool;
while ((tool = toolPattern.exec(body)) !== null) {
tools.add(tool[1]);
}
}
return Array.from(tools);
}
@@ -207,18 +266,24 @@ function rewriteChapterMarkdown(
}
function buildRootVersions(chapters: ChapterSource[]): AgentVersion[] {
return chapters.map((chapter) => {
const versions: AgentVersion[] = [];
for (const chapter of chapters) {
const source = fs.readFileSync(chapter.codePath, "utf-8");
const lines = source.split("\n");
const meta = VERSION_META[chapter.id];
const localTools = extractTools(source);
const inheritedId = source.match(/^INHERITS_TOOLS_FROM\s*=\s*"(s\d{2})"/m)?.[1];
const inheritedTools = inheritedId
? versions.find((version) => version.id === inheritedId)?.tools ?? []
: [];
return {
versions.push({
id: chapter.id,
filename: `${chapter.dirName}/code.py`,
title: meta?.title ?? chapter.id,
subtitle: meta?.subtitle ?? "",
loc: countLoc(lines),
tools: extractTools(source),
tools: Array.from(new Set([...inheritedTools, ...localTools])),
newTools: [] as string[],
coreAddition: meta?.coreAddition ?? "",
keyInsight: meta?.keyInsight ?? "",
@@ -227,8 +292,9 @@ function buildRootVersions(chapters: ChapterSource[]): AgentVersion[] {
layer: meta?.layer ?? "tools",
source,
images: copyChapterAssets(chapter),
};
});
});
}
return versions;
}
function buildLegacyVersions(): AgentVersion[] {

View File

@@ -12,7 +12,7 @@ const visualizations: Record<
s03: lazy(() => import("./s03-permission")),
s04: lazy(() => import("./s04-hooks")),
s05: lazy(() => import("./s03-todo-write")),
s06: lazy(() => import("./s04-subagent")),
s06: lazy(() => import("./s06-subagent")),
s07: lazy(() => import("./s05-skill-loading")),
s08: lazy(() => import("./s06-context-compact")),
s09: lazy(() => import("./s09-memory")),

View File

@@ -29,7 +29,7 @@ const CHILD_WORK_MESSAGES: MessageBlock[] = [
const SUMMARY_BLOCK: MessageBlock = {
id: "summary",
label: "summary: 3 tests written, all passing",
label: "final: 3 tests written, all passing",
color: "bg-teal-500",
};
@@ -40,9 +40,9 @@ const STEPS = [
"The parent agent has accumulated messages from the conversation.",
},
{
title: "Spawn Subagent",
title: "Run Subagent",
description:
"Task tool creates a child with fresh messages[]. Only the task description is passed.",
"Task runs a nested agent loop with fresh messages[]. Only the task prompt is passed.",
},
{
title: "Independent Work",
@@ -50,19 +50,19 @@ const STEPS = [
"The child has its own context. It doesn't see the parent's history.",
},
{
title: "Compress Result",
title: "Final Response",
description:
"The child's full conversation compresses into one summary.",
"The subagent finishes with a text response.",
},
{
title: "Return Summary",
title: "Return Final Text",
description:
"Only the summary returns. The child's full context is discarded.",
"The final text becomes the task tool result in the parent conversation.",
},
{
title: "Clean Context",
title: "Parent Continues",
description:
"The parent gets a clean summary without context bloat. This is fresh-context isolation via messages[].",
"The parent continues without copying the subagent's intermediate messages.",
},
];
@@ -112,12 +112,12 @@ export default function SubagentIsolation({ title }: { title?: string }) {
>
{/* Main layout: two containers side by side */}
<div className="relative flex gap-4" style={{ minHeight: 340 }}>
{/* Parent Process Container */}
{/* Parent agent loop */}
<div className="flex-1 rounded-xl border-2 border-blue-300 bg-blue-50/50 p-4 dark:border-blue-700 dark:bg-blue-950/20">
<div className="mb-3 flex items-center gap-2">
<div className="h-3 w-3 rounded-full bg-blue-500" />
<span className="text-sm font-bold text-blue-700 dark:text-blue-300">
Parent Process
Parent agent loop
</span>
</div>
<div className="mb-2 font-mono text-xs text-zinc-400">
@@ -146,7 +146,7 @@ export default function SubagentIsolation({ title }: { title?: string }) {
transition={{ delay: 0.5 }}
className="mt-3 rounded border border-blue-200 bg-white/60 px-2 py-1 text-center text-xs text-blue-600 dark:border-blue-700 dark:bg-blue-950/30 dark:text-blue-300"
>
3 original + 1 summary = clean context
parent receives one task result
</motion.div>
)}
</div>
@@ -161,12 +161,12 @@ export default function SubagentIsolation({ title }: { title?: string }) {
className="rounded bg-zinc-200 px-2 py-1 text-center font-mono text-[10px] text-zinc-500 dark:bg-zinc-700 dark:text-zinc-400"
style={{ writingMode: "vertical-rl", textOrientation: "mixed" }}
>
ISOLATION
MESSAGE BOUNDARY
</motion.div>
<div className="h-full w-px border-l-2 border-dashed border-zinc-300 dark:border-zinc-600" />
</div>
{/* Child Process Container */}
{/* Nested subagent loop */}
<div
className={`flex-1 rounded-xl border-2 p-4 transition-colors duration-300 ${
showChildEmpty
@@ -195,7 +195,7 @@ export default function SubagentIsolation({ title }: { title?: string }) {
: "text-purple-700 dark:text-purple-300"
}`}
>
Child Process
Subagent loop
</span>
</div>
<div className="mb-2 font-mono text-xs text-zinc-400">
@@ -209,7 +209,7 @@ export default function SubagentIsolation({ title }: { title?: string }) {
className="flex h-24 items-center justify-center rounded-lg border border-dashed border-zinc-200 dark:border-zinc-700"
>
<span className="text-xs text-zinc-400">
not yet spawned
not yet started
</span>
</motion.div>
)}
@@ -237,7 +237,7 @@ export default function SubagentIsolation({ title }: { title?: string }) {
animate={{ opacity: 1, scale: 1 }}
className="mt-3 rounded border border-amber-300 bg-amber-50 px-2 py-1 text-center text-xs text-amber-700 dark:border-amber-600 dark:bg-amber-900/20 dark:text-amber-300"
>
Compressing full context into summary...
Preparing final response...
</motion.div>
)}
@@ -247,7 +247,7 @@ export default function SubagentIsolation({ title }: { title?: string }) {
animate={{ opacity: 1 }}
className="mt-3 rounded border border-red-200 bg-red-50 px-2 py-1 text-center text-xs text-red-500 dark:border-red-800 dark:bg-red-900/20 dark:text-red-400"
>
context discarded
local messages released
</motion.div>
)}
</div>

View File

@@ -19,32 +19,32 @@ const STEPS = [
{
title: "Confirm a Small Team",
desc: "The Lead proposes focused roles and waits for the user before starting persistent teammates.",
event: "user confirmed: backend + tests",
event: "lead proposes: backend + tests",
},
{
title: "Deliver a Typed Assignment",
desc: "The runtime writes the assignment to a mailbox and correlates plan approval with a request id.",
event: "plan_response(req_7, approved=true)",
title: "Claim Atomically",
desc: "A ready task moves to one owner while the task-store file lock protects the persisted transition.",
event: "task_store_lock: task_...0042 -> backend",
},
{
title: "Idle Teammates Scan the Board",
desc: "A teammate with no direct message looks only for pending, unowned work whose dependencies are complete.",
event: "scan_ready_tasks(backend) -> task_auth",
},
{
title: "Claim Under One Lock",
desc: "Ownership and status change atomically, so another teammate cannot take the same task.",
event: "task_lock: task_auth -> backend",
title: "Require and Review a Plan",
desc: "The gate is active before the teammate starts; the Lead reviews the typed request for this task and work version.",
event: "review_plan(req_000007, approve=true)",
},
{
title: "Route Tools to the Task Directory",
desc: "The claimed task carries its worktree binding; bash, read, and write derive their cwd from that record.",
event: "cwd -> .worktrees/auth-refactor",
},
{
title: "Execute the Approved Work",
desc: "Mutating tools run only after the current assignment's plan is approved.",
event: "bash / write: allowed",
},
{
title: "Return the Result, Keep the Teammate",
desc: "The runtime delivers the result to the Lead and moves the teammate back to IDLE for newly ready work.",
event: "result(auth complete) -> idle_notification",
desc: "Completion keeps the task cwd through the turn; IDLE releases the assignment and keeps the teammate available.",
event: "complete -> result -> IDLE",
},
] as const;
@@ -98,7 +98,7 @@ function RuntimePanel({ step }: { step: number }) {
</span>
<span className="text-zinc-500 dark:text-zinc-400">Protocol</span>
<span className="break-all font-mono text-zinc-700 dark:text-zinc-300">
{step < 1 ? "-" : "request_id=req_7"}
{step < 2 ? "-" : "request_id=req_000007"}
</span>
</div>
@@ -107,10 +107,10 @@ function RuntimePanel({ step }: { step: number }) {
<span className="break-words leading-relaxed">
{step === 0
? "No mailbox is created before confirmation."
: step === 1
? "Assignment and plan approval travel through typed messages."
: step === 2
? "The approval is tied to the claimed task and work version."
: step === 5
? "The result wakes the Lead; IDLE is a reusable state."
? "The result wakes the Lead; IDLE releases the cwd lease."
: "The runtime owns delivery while the teammate works."}
</span>
</div>
@@ -119,8 +119,8 @@ function RuntimePanel({ step }: { step: number }) {
}
function TaskPanel({ step }: { step: number }) {
const status = step < 3 ? "pending" : step < 5 ? "in_progress" : "completed";
const owner = step < 3 ? "-" : "backend";
const status = step < 1 ? "pending" : step < 5 ? "in_progress" : "completed";
const owner = step < 1 ? "-" : "backend";
const tone = status === "pending" ? "zinc" : status === "in_progress" ? "amber" : "emerald";
return (
@@ -135,7 +135,7 @@ function TaskPanel({ step }: { step: number }) {
<div className="mt-4 rounded-md border border-zinc-200 p-3 dark:border-zinc-700">
<div className="font-mono text-[11px] text-zinc-500 dark:text-zinc-400">
task_auth
task_1712345678_0042
</div>
<div className="mt-1 text-sm font-semibold text-zinc-900 dark:text-zinc-100">
Refactor authentication
@@ -151,18 +151,18 @@ function TaskPanel({ step }: { step: number }) {
<div
className={cn(
"mt-3 flex min-h-[56px] items-center gap-2 rounded-md border px-3 py-2 text-xs",
step === 2 || step === 3
step === 1
? "border-amber-300 bg-amber-50 text-amber-800 dark:border-amber-800 dark:bg-amber-950/30 dark:text-amber-200"
: "border-zinc-200 bg-zinc-50 text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"
)}
>
{step < 3 ? <Search size={15} className="shrink-0" /> : <LockKeyhole size={15} className="shrink-0" />}
{step < 1 ? <Search size={15} className="shrink-0" /> : <LockKeyhole size={15} className="shrink-0" />}
<span className="break-words">
{step < 2
{step < 1
? "Waiting for the teammate loop."
: step === 2
? "Ready filter: pending + unowned + dependencies complete."
: "The claim check and update share one lock."}
: step === 1
? "The claim check and update share one lock."
: "The claimed task remains owned through the work turn."}
</span>
</div>
</div>
@@ -170,7 +170,8 @@ function TaskPanel({ step }: { step: number }) {
}
function WorkspacePanel({ step }: { step: number }) {
const bound = step >= 4;
const routed = step >= 3 && step < 5;
const retained = step >= 5;
return (
<div className="min-h-[250px] rounded-lg border border-zinc-200 bg-white p-3 dark:border-zinc-700 dark:bg-zinc-900">
@@ -179,14 +180,17 @@ function WorkspacePanel({ step }: { step: number }) {
<GitBranch size={16} className="shrink-0 text-emerald-500" />
<span>Task directory</span>
</div>
<StateBadge label={bound ? "bound" : "reserved"} tone={bound ? "emerald" : "zinc"} />
<StateBadge
label={routed ? "active cwd" : retained ? "binding retained" : "reserved"}
tone={routed ? "emerald" : "zinc"}
/>
</div>
<div className="mt-4 space-y-2">
<div
className={cn(
"rounded-md border p-3 transition-colors",
!bound
!routed
? "border-blue-300 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30"
: "border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800"
)}
@@ -199,11 +203,11 @@ function WorkspacePanel({ step }: { step: number }) {
</div>
</div>
<motion.div
animate={bound ? { y: [0, -2, 0] } : { y: 0 }}
transition={{ duration: 0.8, repeat: bound ? Infinity : 0 }}
animate={routed ? { y: [0, -2, 0] } : { y: 0 }}
transition={{ duration: 0.8, repeat: routed ? Infinity : 0 }}
className={cn(
"rounded-md border p-3 transition-colors",
bound
routed
? "border-emerald-300 bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-950/30"
: "border-dashed border-zinc-300 bg-white dark:border-zinc-700 dark:bg-zinc-900"
)}
@@ -212,14 +216,24 @@ function WorkspacePanel({ step }: { step: number }) {
.worktrees/auth-refactor
</div>
<div className="mt-1 text-[11px] text-zinc-500 dark:text-zinc-400">
{bound ? "bash / read / write cwd" : "task.worktree binding"}
{routed
? "bash / read / write cwd"
: retained
? "task binding remains after IDLE"
: "task.worktree binding"}
</div>
</motion.div>
</div>
<div className="mt-3 flex items-center gap-2 text-xs text-zinc-600 dark:text-zinc-300">
{bound ? <Terminal size={15} /> : <GitBranch size={15} />}
<span>{bound ? "Tools follow the claimed task." : "No implicit directory switching."}</span>
{routed ? <Terminal size={15} /> : <GitBranch size={15} />}
<span>
{routed
? "Tools follow the claimed task."
: retained
? "IDLE released active tool routing."
: "No implicit directory switching."}
</span>
</div>
</div>
);

View File

@@ -4,43 +4,43 @@
{
"id": "fresh-subagent-context",
"title": "Subagents Start with Fresh Messages",
"description": "The child agent receives only the delegated prompt. This isolates exploratory work and prevents the parent context from filling with every intermediate tool result.",
"alternatives": "Sharing the full parent history gives more context, but it defeats the purpose of delegation as context isolation.",
"description": "The subagent receives only the delegated prompt. Its intermediate tool calls stay in a separate message list instead of being copied into the parent conversation.",
"alternatives": "Passing the parent history would give the subagent more context, but would no longer demonstrate a fresh-message boundary.",
"zh": {
"title": "子代理从全新 Messages 开始",
"description": "子代理只收到被委派的 prompt。这样探索性工作被隔离,父上下文不会塞满每个中间工具结果。"
"description": "子 Agent 只收到被委派的 prompt。中间工具调用留在另一份消息列表中,不复制到父对话。"
},
"ja": {
"title": "サブエージェントは新しい messages で始まる",
"description": "エージェントは委任された prompt だけを受け取ります。探索作業を隔離し、親コンテキストが中間 tool result で膨らむのを防ぎます。"
"description": "サブエージェントは委任された prompt だけを受け取る。中間ツール呼び出しは別のメッセージリストに残り、親会話へコピーされない。"
}
},
{
"id": "summary-only-return",
"title": "Only the Summary Returns to the Parent",
"description": "The parent receives a compact final answer, not the child's full transcript. That gives delegation a predictable context cost.",
"alternatives": "Returning the full transcript can help debugging, but it makes large subagent runs expensive to continue.",
"title": "Only the Final Text Returns to the Parent",
"description": "The parent receives the subagent's final text as the task result, not the subagent's full message list.",
"alternatives": "Returning the full message list would expose more detail, but it would remove the boundary shown in this lesson.",
"zh": {
"title": "只有摘要返回父循环",
"description": "父循环收到的是压缩后的最终答案,而不是子代理的完整 transcript。这样委派的上下文成本可预测。"
"title": "只有最终文本返回父循环",
"description": "父循环收到的是作为 task 结果返回的最终文本,而不是子 Agent 的完整消息列表。"
},
"ja": {
"title": "親に戻るのは要約だけ",
"description": "親が受け取るのは子の完全な transcript ではなく、圧縮された最終回答です。委任のコンテキストコストを予測可能にします。"
"title": "親に戻るのは最終テキストだけ",
"description": "親が受け取るのは task result となる最終テキストであり、サブエージェントの完全なメッセージリストではない。"
}
},
{
"id": "no-recursive-task-tool",
"title": "Subagents Cannot Spawn Subagents",
"description": "The child tool set omits task, preventing recursive delegation from exploding. The lesson keeps isolation visible before adding richer team behavior later.",
"alternatives": "Allowing recursion is powerful, but much harder to bound and explain.",
"title": "The Subagent Has No Task Tool",
"description": "The subagent tool set omits task, so this lesson has one delegation level.",
"alternatives": "Recursive delegation is possible, but requires additional lifecycle and limit controls not introduced here.",
"zh": {
"title": "子代理不能再创建子代理",
"description": "子代理工具集中不包含 task避免递归委派失控。课程先把隔离讲清楚,再在后续章节加入更复杂团队行为。"
"title": "子 Agent 没有 task 工具",
"description": "子 Agent 的工具集中不包含 task因此本章只有一层委派。"
},
"ja": {
"title": "サブエージェントはさらにサブエージェントを作れない",
"description": "のツールセットから task を外し、再帰的委任の爆発を防ぎます。まず隔離を明確にし、後の章でより豊かなチーム動作を扱います。"
"title": "サブエージェントに task ツールはない",
"description": "サブエージェントのツールセット task はなく、本章の委任は 1 階層となる。"
}
}
]

View File

@@ -578,7 +578,7 @@ const CURRENT_FLOW_OVERRIDES: Record<string, FlowDefinition> = {
{ id: "llm", label: "LLM Call", type: "process", x: COL_CENTER, y: 120 },
{ id: "tool", label: "tool_use?", type: "decision", x: COL_CENTER, y: 210 },
{ id: "todo", label: "todo_write?", type: "decision", x: COL_LEFT, y: 310 },
{ id: "update", label: "Update\ncurrent_todos", type: "process", x: COL_LEFT, y: 410 },
{ id: "update", label: "TodoManager\n.update()", type: "process", x: COL_LEFT, y: 410 },
{ id: "other", label: "Run Tool", type: "subprocess", x: COL_CENTER, y: 410 },
{ id: "reminder", label: "3 rounds?\nInject Reminder", type: "process", x: COL_RIGHT, y: 500 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 590 },
@@ -602,9 +602,9 @@ const CURRENT_FLOW_OVERRIDES: Record<string, FlowDefinition> = {
{ id: "start", label: "User Input", type: "start", x: COL_CENTER, y: 30 },
{ id: "parent", label: "Parent LLM", type: "process", x: COL_CENTER, y: 120 },
{ id: "task_check", label: "task tool?", type: "decision", x: COL_CENTER, y: 220 },
{ id: "spawn", label: "Spawn Subagent\nfresh messages[]", type: "subprocess", x: COL_LEFT, y: 330 },
{ id: "run", label: "Run Subagent\nfresh messages[]", type: "subprocess", x: COL_LEFT, y: 330 },
{ id: "subloop", label: "Subagent Loop\nmax 30 turns", type: "process", x: COL_LEFT, y: 430 },
{ id: "summary", label: "Return Summary\nOnly", type: "process", x: COL_LEFT, y: 530 },
{ id: "final_text", label: "Return Final\nText", type: "process", x: COL_LEFT, y: 530 },
{ id: "tool", label: "Run Parent Tool", type: "subprocess", x: COL_RIGHT, y: 330 },
{ id: "append", label: "Append Result", type: "process", x: COL_CENTER, y: 630 },
{ id: "end", label: "Output", type: "end", x: COL_RIGHT, y: 220 },
@@ -612,12 +612,12 @@ const CURRENT_FLOW_OVERRIDES: Record<string, FlowDefinition> = {
edges: [
{ from: "start", to: "parent" },
{ from: "parent", to: "task_check" },
{ from: "task_check", to: "spawn", label: "task" },
{ from: "task_check", to: "run", label: "task" },
{ from: "task_check", to: "tool", label: "other" },
{ from: "task_check", to: "end", label: "done" },
{ from: "spawn", to: "subloop" },
{ from: "subloop", to: "summary" },
{ from: "summary", to: "append" },
{ from: "run", to: "subloop" },
{ from: "subloop", to: "final_text" },
{ from: "final_text", to: "append" },
{ from: "tool", to: "append" },
{ from: "append", to: "parent" },
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -17,8 +17,8 @@
{
"type": "tool_result",
"toolName": "todo_write",
"content": "Todos updated.",
"annotation": "The current todo list is kept by the harness."
"content": "[>] Inspect parser\n[ ] Refactor parsing branch\n[ ] Add regression test\n\n(0/3 completed)",
"annotation": "The harness returns the current list to the model."
},
{
"type": "tool_call",
@@ -28,8 +28,8 @@
},
{
"type": "system_event",
"content": "<reminder>Update todo status after 3 rounds without todo_write.</reminder>",
"annotation": "The nag reminder keeps the plan fresh during long work."
"content": "<reminder>Update your todos.</reminder>",
"annotation": "After three rounds without todo_write, the harness appends a reminder."
},
{
"type": "assistant_text",

View File

@@ -1,7 +1,7 @@
{
"version": "s06",
"title": "Subagent",
"description": "The task tool spawns a fresh subagent context and returns only a final summary to the parent.",
"description": "The task tool runs a nested agent loop with fresh messages and returns its final text to the parent.",
"steps": [
{
"type": "user_message",
@@ -16,20 +16,20 @@
},
{
"type": "system_event",
"content": "spawn_subagent: messages=[{role:\"user\", content: prompt}], tools=bash/read/write/edit/glob",
"annotation": "The child receives a fresh message history and no recursive task tool."
"content": "run_subagent: messages=[{role:\"user\", content: prompt}], tools=bash/read/write/edit/glob",
"annotation": "The subagent receives fresh messages and no task tool."
},
{
"type": "tool_call",
"toolName": "read_file",
"content": "{\"path\":\"cli.py\"}",
"annotation": "Inside the child loop, intermediate tool calls stay private."
"annotation": "The tool call stays in the subagent's local message list."
},
{
"type": "tool_result",
"toolName": "task",
"content": "Summary: cli.py parses args, selects a command handler, then dispatches.",
"annotation": "Only the subagent's final summary returns to the parent."
"annotation": "The subagent's final text becomes the parent's task result."
},
{
"type": "assistant_text",

View File

@@ -51,8 +51,8 @@
{
"type": "tool_call",
"toolName": "spawn_teammate",
"content": "{\"name\":\"backend\",\"role\":\"backend engineer\",\"prompt\":\"Claim the authentication task and propose a plan.\"}",
"annotation": "A persistent teammate receives focused work through the team runtime."
"content": "{\"name\":\"backend\",\"role\":\"backend engineer\",\"prompt\":\"Claim the authentication task and propose a plan.\",\"require_plan\":true}",
"annotation": "The plan gate is active before the teammate thread starts, so claim and mutation cannot race ahead of approval."
},
{
"type": "tool_call",
@@ -62,13 +62,30 @@
},
{
"type": "system_event",
"content": "plan_request(req_plan_7) -> plan_response(req_plan_7, approved=true)",
"annotation": "Typed correlation and an approval gate protect mutating tools."
"content": "claim_next_task(backend) -> task_1712345678_0042; task_store_lock commits owner=backend",
"annotation": "The ownership check and persisted state transition share the cross-process task-store lock."
},
{
"type": "tool_call",
"toolName": "request_plan",
"content": "{\"teammate\":\"backend\",\"task\":\"Inspect the claimed authentication task and submit a plan before changing files.\"}",
"annotation": "The Lead delivers the plan request for the current assignment; the gate was already active before the teammate thread started."
},
{
"type": "system_event",
"content": "claim_next_task(backend) -> task_1712345678_0042; task_lock commits owner=backend",
"annotation": "The ownership check and state transition are atomic."
"content": "backend submit_plan -> plan_approval_request(request_id=req_000007, task_id=task_1712345678_0042)",
"annotation": "The request records the task and work version that the plan is meant to authorize."
},
{
"type": "tool_call",
"toolName": "review_plan",
"content": "{\"request_id\":\"req_000007\",\"approve\":true,\"feedback\":\"Proceed with the scoped refactor.\"}",
"annotation": "Approval is correlated by request ID and cannot carry into a different assignment."
},
{
"type": "system_event",
"content": "plan_approval_response(request_id=req_000007, approve=true) -> backend",
"annotation": "The teammate receives the typed response before mutating tools are released."
},
{
"type": "system_event",
@@ -84,7 +101,7 @@
{
"type": "system_event",
"content": "backend -> Lead: result(auth refactor complete) -> idle_notification",
"annotation": "Result and idle state are separate events; the teammate remains available."
"annotation": "The task directory stays selected through the completion turn, then IDLE releases the assignment."
},
{
"type": "system_event",

View File

@@ -62,9 +62,14 @@
{
"type": "tool_call",
"toolName": "bash",
"content": "{\"command\":\"npm run lint && npm run test\",\"run_in_background\":true}",
"content": "{\"command\":\"python -m unittest tests.test_agent_teams_runtime\",\"run_in_background\":true}",
"annotation": "Long-running validation goes through the background task path."
},
{
"type": "system_event",
"content": "permission: user approved the exact test command",
"annotation": "Team confirmation does not authorize shell execution; the foreground turn asks separately before dispatch."
},
{
"type": "tool_call",
"toolName": "connect_mcp",
@@ -85,7 +90,7 @@
},
{
"type": "system_event",
"content": "recover: background task done, teammate replied, deploy status result appended",
"content": "task_notification(status=completed): tests passed; teammate result and deploy status appended",
"annotation": "The integrated runtime folds asynchronous results back into the loop."
},
{

View File

@@ -11,12 +11,12 @@
{
"type": "tool_call",
"toolName": "Workflow",
"content": "{\"name\":\"review-changes\",\"description\":\"Review changed files across dimensions and verify each finding\",\"phases\":[\"Review\",\"Verify\"]}",
"annotation": "One tool call hands deterministic orchestration to the workflow runtime."
"content": "{\"name\":\"review-changes\",\"args\":{\"budget\":null}}",
"annotation": "The model selects a saved workflow and arguments; the host registry supplies its trusted metadata and script."
},
{
"type": "system_event",
"content": "async_launched(runId=wf_review-changes_6779) -> task_started",
"content": "async_launched(runId=wf_review-changes_0000000000001a7b) -> task_started",
"annotation": "The runtime emits launch lifecycle events before it executes the script; this is not a tool result."
},
{
@@ -36,19 +36,19 @@
},
{
"type": "system_event",
"content": "task_notification(status=completed, outputFile=.runtime/wf_review-changes_6779.output.json)",
"content": "task_notification(status=completed, outputFile=.runtime/wf_review-changes_0000000000001a7b.output.json)",
"annotation": "The task emits its final lifecycle event after output is written."
},
{
"type": "tool_result",
"toolName": "Workflow",
"content": "{\"launched\":{\"status\":\"async_launched\",\"runId\":\"wf_review-changes_6779\"},\"result\":{\"confirmed\":[]},\"task\":{\"status\":\"completed\"}}",
"annotation": "The completed call returns once, with launch metadata, the workflow result, and task state together."
"content": "{\"launched\":{\"status\":\"async_launched\",\"taskId\":\"local_workflow_wf_review-changes_0000000000001a7b\",\"taskType\":\"local_workflow\",\"runId\":\"wf_review-changes_0000000000001a7b\",\"workflowName\":\"review-changes\"},\"result\":{\"confirmed\":[{\"dimension\":\"security\",\"title\":\"audit:security #1\",\"severity\":\"high\"},{\"dimension\":\"style\",\"title\":\"audit:style #1\",\"severity\":\"high\"},{\"dimension\":\"security\",\"title\":\"audit:security #2\",\"severity\":\"medium\"},{\"dimension\":\"performance\",\"title\":\"audit:performance #2\",\"severity\":\"medium\"},{\"dimension\":\"correctness\",\"title\":\"audit:correctness #1\",\"severity\":\"low\"},{\"dimension\":\"performance\",\"title\":\"audit:performance #1\",\"severity\":\"low\"}]},\"task\":{\"taskId\":\"local_workflow_wf_review-changes_0000000000001a7b\",\"taskType\":\"local_workflow\",\"runId\":\"wf_review-changes_0000000000001a7b\",\"workflowName\":\"review-changes\",\"status\":\"completed\",\"usage\":{\"agents\":11,\"tokens\":352},\"progress\":[{\"type\":\"workflow_phase\",\"title\":\"Review\"},{\"type\":\"workflow_agent\",\"label\":\"audit:correctness\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_phase\",\"title\":\"Verify\"},{\"type\":\"workflow_agent\",\"label\":\"audit:security\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"audit:performance\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"audit:style\",\"phase\":\"Review\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:correctness:audit:correctness #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:security:audit:security #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:security:audit:security #2\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:performance:audit:performance #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:performance:audit:performance #2\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:style:audit:style #1\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_agent\",\"label\":\"verify:style:audit:style #2\",\"phase\":\"Verify\",\"status\":\"done\"},{\"type\":\"workflow_log\",\"message\":\"confirmed 6 real finding(s)\"}]}}",
"annotation": "The deterministic sample returns its six fixture findings and measured runner usage; these are not claims about the repository."
},
{
"type": "system_event",
"content": "append Workflow tool_result -> messages[]",
"annotation": "The main loop receives that single result and continues with the updated conversation."
"annotation": "A main-loop integration can append this JSON-safe result and continue with the updated conversation."
}
]
}