SubagentRunner:子タスクの独立コンテキスト
役割
SubagentRunner は Cline が「LLM を独自のコンテキストウィンドウで子タスクとして走らせる」ことをカプセル化した実行器である。主 agent が subagent というツール呼び出しに遭遇した時 (new SubagentRunner:215)、各 prompt に対して runner を起動する。runner 内部ではミニ agent loop を回す——ストリームを引く、tool_calls をパース、実行、結果を対話に押し戻す——が、完全に独立した conversation 配列と独立した ContextManager を使い、主 Task のメッセージ履歴と混ざらない。
位置は主 Task の下、具体ツールの上である。主 Task は prompt と TaskConfig を渡し、runner は SubagentBuilder を使ってこの subagent が使えるツールサブセット、システムプロンプト、API handler を算出し、自身の run でループする。子 agent が attempt_completion を呼べば終了し、結果文字列がそのまま主 agent に返る。主 agent には通常のツール結果として見える。この設計により主 Task は「コードを探索、複数ファイルを読み、答えを総合する」といった高コスト操作を隔離されたウィンドウ内に閉じ込め、中間 token で主コンテキストを汚さずに済む。
設計動機
- 独立コンテキストウィンドウ:子 agent は自身の
ClineStorageMessage[]配列を使う。主 Task は子 agent の中間 tool 呼び出しを見られない (conversation init:463)。 - ツールホワイトリスト:子 agent はデフォルトで読み取りのみ。
SUBAGENT_DEFAULT_ALLOWED_TOOLSは file_read / list_files / search / list_code_def / bash / use_skill / attempt_completion のみを許可 (default allowed tools:14)。 - attempt_completion の強制:ユーザー設定にかかわらず
ATTEMPTは常に追加 (force ATTEMPT:96)。さもなくば子 agent が終了できない。 - agent 設定でモデルを上書き:
AgentConfigLoaderが個別 agent のmodelIdを読み、SubagentBuilder.applyModelOverrideが apiHandler を差し替える (applyModelOverride:57)。子 agent ごとに異なるモデルを使える。 - 自動コンテキスト圧縮:runner は各ラウンドのリクエスト前に前回リクエストの token 数をチェックし、しきい値超過なら
compactConversationForContextWindowで file read 最適化を先に行い、その後 truncation を呼ぶ (shouldCompactBeforeNextRequest:488)。 - 空応答のリトライ:モデルがこのラウンドで tool_use を出さなければ空応答とみなし、
noToolsUsedプロンプトを送って再問する。MAX_EMPTY_ASSISTANT_RETRIES(3) を超えたら直接失敗 (empty response retry:662)。 - 並列マルチ runner の同時キャンセル:1 つの subagent ツール呼び出しは複数の prompt を持てる。複数 runner が並列実行され、abort を 100ms ごとにポーリング (
abort poll:216)し、全 runner を同時に止める。
主要ファイル
SubagentRunner class:254— agent、apiHandler、allowedTools、abort 状態を保持。run method:330— 主ループ。各ラウンドでストリーム、tool_calls パース、実行、押し戻し。while loop:485— 永真ループ。attempt_completion または abort で抜ける。createMessageWithInitialChunkRetry:519— ストリームを引き、先頭 chunk が「context window exceeded」を報告したら圧縮してリトライ。tool whitelist check:726— allowedTools に入っていなければ toolError を返し、実行しない。attempt_completion handling:702— attempt 命中で result 文字列を主 agent に返して終了。abort method:272— api.abort を呼び、実行中のコマンドもキャンセル。compactConversationForContextWindow:895— まず file read 最適化を試し、次にgetNextTruncationRangeで 1 段削除。shouldCompactBeforeNextRequest:977—useAutoCondense+ next-gen モデルは 0.75 しきい値、それ以外はmaxAllowedSize。buildSystemPrompt:73—<generated> + <agent identity> + SUBAGENT_SYSTEM_SUFFIXに組み上げる。spawn runners:215— 主 agent が実際に SubagentRunner を起動する場所。
データフロー
runner 起動時、まずシステムプロンプトと初期 user content を用意する。初期対話には 2 件だけ置く。ユーザーの prompt と workspace メタデータブロックで、後者は server-side task loop の検証用である:
// apps/vscode/src/core/task/tools/subagent/SubagentRunner.ts
const conversation: ClineStorageMessage[] = [
{
role: "user",
content: [
{ type: "text", text: prompt } as ClineTextContentBlock,
// Server-side task loop checks require workspace metadata to be present in the
// initial user message of subagent runs.
...(workspaceMetadataEnvironmentBlock
? [{ type: "text", text: workspaceMetadataEnvironmentBlock } as ClineTextContentBlock]
: []),
],
},
];
while (true) {
if (
usageState.lastRequest &&
this.shouldCompactBeforeNextRequest(usageState.lastRequest.totalTokens, api, providerInfo.model.id)
) {
const compactResult = this.compactConversationForContextWindow(
contextManager,
conversation,
contextState.conversationHistoryDeletedRange,
);
contextState.conversationHistoryDeletedRange = compactResult.conversationHistoryDeletedRange;
// ...
}
// ...
}この処理は conversation + while:463 にある。ループに入った後、各ラウンドで createMessageWithInitialChunkRetry がストリームを引き、stream chunk を usage/text/tool_calls/reasoning に分類する。ストリーム終了後、確定した tool calls を順に実行し、attempt_completion に命中した call は result を直接主 agent に返す (return on attempt:723)。他のツールは coordinator.getHandler(toolName).execute で実行し、結果を user content として押し戻してループを継続する。
境界と失敗
- ツールがホワイトリスト外なら直接 toolError:子 agent が
write_to_fileなどを呼んでも弾かれる。エラー文字列を user content に詰め (whitelist check:726)、ループは継続し、タスク全体は中断しない。 - attempt_completion の result 欠損:result が空なら
missingToolParameterErrorを詰めて終了しない (missing result:705)。 - 先頭 chunk が context window exceeded ならリトライ:先頭 chunk 失敗かつエラーが context window 関連なら、即座に conversation を圧縮して再試行 (
context window retry:1036)。最大MAX_INITIAL_STREAM_ATTEMPTS回。 - abort 時の実行中コマンド:abort は API ストリームをキャンセルするだけでなく、
cancelRunningCommandToolに委譲して現在の bash も止める (cancel running command:281)。 - native vs non-native tool calls のフォールバック:non-native モードで構造化 tool_calls chunk を受け取った時、実行はするが結果をプレーンテキストにシリアライズして押し戻す。tool_result ペアのずれを回避するため (
non-native fallback:631)。 - stats の累積:各 chunk ごとに inputTokens/outputTokens/cacheWrite/cacheRead/totalCost を更新し、
onProgressでリアルタイムにフロントへ送る (stats accumulation:534)。 - empty assistant も 1 ラウンド扱い:assistant content が完全に空の時は「Failure: I did not provide a response.」を詰めてから noToolsUsed を送る (
empty assistant:671)。
まとめ
SubagentRunner により、主 agent は「広範な探索」を隔離された mini-agent に外注できる。これが依存するコンテキスト圧縮戦略は context-manager、主 Task がどうツール結果を押し戻して再帰するかは agent-loop/task-class、MCP ツールが主 agent からどう呼ばれるかは mcp-hub を参照のこと。