Skip to content

Ensamblaje createAgentSession ​

源码版本v0.73.1

createAgentSession es la entrada central del SDK. Toma AuthStorage, ModelRegistry, SettingsManager, SessionManager, ResourceLoader como servicios externos, más las definiciones de herramientas y el runner de extensiones, y los ensambla en un AgentSession usable. main.ts no la llama directamente, sino a través de createAgentSessionRuntime que pasa por createAgentSessionServices; pero los llamadores del SDK (que incrustan pi en otros programas) la usan tal cual.

Responsabilidades ​

  1. Resolución de servicios: cada servicio externo puede pasarse o crearse automáticamente; las rutas por defecto se basan en agentDir (~/.pi/agent). Ver packages/coding-agent/src/core/sdk.ts:193-211.
  2. Recuperación de modelo: si ya hay datos de sesión, recupera el model del header de la sesión; si no, del settings; si no se puede recuperar, rellena modelFallbackMessage. Ver packages/coding-agent/src/core/sdk.ts:214-248.
  3. Clampeo de thinking level: toma el thinking level de la sesión o de settings, y luego clampThinkingLevel lo acota al rango soportado por el modelo. Ver packages/coding-agent/src/core/sdk.ts:250-269.
  4. Inyección de stream function: el streamFn del Agent no llama a streamSimple directamente, sino que primero pide auth con modelRegistry.getApiKeyAndHeaders y luego suma las cabeceras de telemetría de getAttributionHeaders. Ver packages/coding-agent/src/core/sdk.ts:328-346.
  5. Envoltura de convertToLlm: cuando blockImages está activo, reemplaza todo ImageContent por texto placeholder, leyendo settings de forma dinámica para que el cambio a mitad de sesión también aplique. Ver packages/coding-agent/src/core/sdk.ts:282-316.
  6. Cabeceras de attribution: OpenRouter y Cloudflare reciben cabeceras HTTP específicas para clasificación. Ver packages/coding-agent/src/core/sdk.ts:128-156.

Motivación de diseño ​

¿Por qué no dejar que el llamador haga new Agent y luego new AgentSession? Porque el ensamblaje involucra más de una decena de servicios con dependencias mutuas: auth determina si el modelo está disponible, modelo determina el rango de thinking level, settings determina retry/transport/steering, el resource loader carga extensiones e inyecta el extensionRunnerRef. Exponer todo eso al llamador es invitar a cada uno a pisar las mismas piedras. createAgentSession concentra las reglas de ensamblaje en una sola función; el llamador del SDK sólo pasa cwd y (opcional) model.

getAttributionHeaders se extrae aparte porque cada provider necesita cabeceras distintas: OpenRouter usa HTTP-Referer + X-OpenRouter-*, Cloudflare usa User-Agent. Centralizarlo evita que se desparrame por la ruta de stream.

Archivos clave ​

En el punto de inyección de streamFn se ve cómo un fallo de auth cortocircuita y cómo se combinan las cabeceras de 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,
	});
},

La envoltura de convertToLlm reemplaza imágenes por un placeholder cuando blockImages está activo, y deduplica placeholders consecutivos:

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,
						)
						// ... deduplica ...

Flujo de datos ​

Orden de ensamblaje:

Límites y fallos ​

  • Modelo no recuperable: modelFallbackMessage avisa de "no se pudo recuperar el modelo guardado" y también de "no hay ningún modelo configurado"; ambos se devuelven y la UI decide cómo mostrarlo. Ver packages/coding-agent/src/core/sdk.ts:243-247.
  • Sin modelo, thinking off: if (!model) thinkingLevel = "off", evita enviar parámetros de thinking con modelo vacío.
  • Sesión ya existente sin entrada thinking: al recuperar, se añade un appendThinkingLevelChange para que fork/resume posteriores tengan estado. Ver packages/coding-agent/src/core/sdk.ts:378-390.
  • transformContext de extensiones: si extensionRunnerRef.current no existe, devuelve los messages originales sin lanzar, permitiendo que funcione sin extensión atada.

Resumen ​

createAgentSession concentra las reglas de ensamblaje de más de una decena de servicios en una sola función; el llamador del SDK sólo pasa cwd y un model opcional. Los detalles hacia abajo en capa de orquestación AgentSession; las fábricas de herramientas en conjunto de herramientas read/bash/edit/write/grep/find/ls; la conversión de mensajes en tipos de mensaje y convertToLlm.