Skip to content

createAgentSession 装配

源码版本v0.73.1

createAgentSession は SDK のコア入り口だ。AuthStorageModelRegistrySettingsManagerSessionManagerResourceLoader といった外部サービスに、ツール定義と拡張ランタイムを加え、使える AgentSession に組み立てる。main.ts は直接これを呼ばず、createAgentSessionRuntime 経由で createAgentSessionServices を走らせるが、SDK の呼び出し側 (pi を別プログラムに組み込む) はこの関数を直接使う。

責務

  1. サービス解決:各外部サービスは渡しても自動生成してもよく、デフォルトパスは agentDir (~/.pi/agent) に基づく。packages/coding-agent/src/core/sdk.ts:193-211 参照。
  2. モデル復元:session データがある時、session header から model を取り戻し、無ければ settings のデフォルトから探す。モデルが復元できない時は modelFallbackMessage を付ける。packages/coding-agent/src/core/sdk.ts:214-248 参照。
  3. thinking level クランプ:session か settings から thinking level を取り、clampThinkingLevel でモデル能力の範囲内に制限する。packages/coding-agent/src/core/sdk.ts:250-269 参照。
  4. stream 関数注入:AgentstreamFn は直接 streamSimple を呼ばず、先に modelRegistry.getApiKeyAndHeaders で auth を取り、getAttributionHeaders のテレメトリヘッダを併せる。packages/coding-agent/src/core/sdk.ts:328-346 参照。
  5. convertToLlm ラップ:blockImages 設定が有効な時、すべての ImageContent をプレースホルダーテキストに置き換える。settings を動的に読むので、session 中途半ばで設定を変えても効く。packages/coding-agent/src/core/sdk.ts:282-316 参照。
  6. attribution ヘッダ:OpenRouter と Cloudflare には归类用の専属 HTTP ヘッダを付ける。packages/coding-agent/src/core/sdk.ts:128-156 参照。

設計動機

なぜ呼び出し側に new Agent して new AgentSession させないのか? 組み立て手順が十数個のサービスの相互依存にまたがるからだ。auth が model が使えるかを決め、model が thinking level の範囲を決め、settings が retry/transport/steering を決め、resource loader が拡張を読み込んで extensionRunnerRef に注入する。これを呼び出し側に晒せば、誰もが同じ罠を踏み直すことになる。createAgentSession は組み立てルールを 1 関数に集め、SDK 呼び出し側は cwd と (任意の) model だけを渡せばいい。

getAttributionHeaders が独立して切り出されているのは、provider ごとに必要なヘッダが違うからだ:OpenRouter は HTTP-Referer + X-OpenRouter-* を見て、Cloudflare は User-Agent を見る。一箇所に集めないと stream パスに散らばる。

主要ファイル

streamFn 注入箇所で、auth 失敗がどうショートサーキットし、attribution ヘッダがどうマージされるかが分かる:

typescript
// packages/coding-agent/src/core/sdk.ts:328-346
streamFn: async (model, context, options) => {
	const auth = await modelRegistry.getApiKeyAndHeaders(model);
	if (!auth.ok) {
		throw new Error(auth.error);
	}
	const providerRetrySettings = settingsManager.getProviderRetrySettings();
	const attributionHeaders = getAttributionHeaders(model, settingsManager);
	return streamSimple(model, context, {
		...options,
		apiKey: auth.apiKey,
		timeoutMs: options?.timeoutMs ?? providerRetrySettings.timeoutMs,
		// ...
		headers:
			attributionHeaders || auth.headers || options?.headers
				? { ...attributionHeaders, ...auth.headers, ...options?.headers }
				: undefined,
	});
},

convertToLlm ラッパー層は blockImages が有効な時、画像をテキストプレースホルダーに置き換え、連続プレースホルダーを重複排除する:

typescript
// packages/coding-agent/src/core/sdk.ts:282-298
const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {
	const converted = convertToLlm(messages);
	if (!settingsManager.getBlockImages()) {
		return converted;
	}
	return converted.map((msg) => {
		if (msg.role === "user" || msg.role === "toolResult") {
			const content = msg.content;
			if (Array.isArray(content)) {
				const hasImages = content.some((c) => c.type === "image");
				if (hasImages) {
					const filteredContent = content
						.map((c) =>
							c.type === "image" ? { type: "text" as const, text: "Image reading is disabled." } : c,
						)
						// ... 重複排除 ...

データフロー

組み立て順序:

境界と失敗

  • モデル復元不能:modelFallbackMessage は「保存されたモデルを復元できなかった」と「設定済みモデルが一つもない」の両方を伝える。両方とも返して、UI にどう表示するかは任せる。packages/coding-agent/src/core/sdk.ts:243-247 参照。
  • model がない時は thinking off:if (!model) thinkingLevel = "off" で、空モデルに thinking パラメータを送らないようにする。
  • session はあるが thinking entry がない:復元時に一条 appendThinkingLevelChange を補い、後続の fork/resume が状態を取れるようにする。packages/coding-agent/src/core/sdk.ts:378-390 参照。
  • 拡張 transformContext:extensionRunnerRef.current が無ければ元の messages をそのまま返す。throw せず、拡張が未バインドでも正常に動くようにする。

小ねた

createAgentSession は十数個のサービスの組み立てルールを 1 関数に集め、SDK 呼び出し側は cwd と任意の model だけを渡せばいい。組み立ての詳細は下に AgentSession オーケストレーション層、ツールファクトリは ツール集 read/bash/edit/write/grep/find/ls、メッセージ変換は メッセージ型と convertToLlm 参照。