Skip to content

Assemblage createAgentSession

源码版本v0.73.1

createAgentSession est l'entrée centrale du SDK. Elle relie AuthStorage, ModelRegistry, SettingsManager, SessionManager, ResourceLoader — les services externes — aux définitions d'outils et au runner d'extensions, pour produire un AgentSession utilisable. main.ts ne l'appelle pas directement : il passe par createAgentSessionRuntimecreateAgentSessionServices, mais un appelant SDK (qui embarque pi dans un autre programme) utilise cette fonction directement.

Responsabilités

  1. Résolution des services : chaque service externe peut être passé ou auto-créé, avec des chemins par défaut fondés sur agentDir (~/.pi/agent). Voir packages/coding-agent/src/core/sdk.ts:193-211.
  2. Restauration du modèle : si des données de session existent, on retrouve model depuis le header de session, sinon depuis les settings ; quand le modèle ne peut pas être restauré, on renseigne modelFallbackMessage. Voir packages/coding-agent/src/core/sdk.ts:214-248.
  3. Clamp du thinking level : on récupère le thinking level depuis la session ou les settings, puis clampThinkingLevel le restreint aux capacités du modèle. Voir packages/coding-agent/src/core/sdk.ts:250-269.
  4. Injection de la fonction de stream : le streamFn de l'Agent n'appelle pas directement streamSimple — il passe d'abord par modelRegistry.getApiKeyAndHeaders pour l'auth, puis y ajoute les headers de télémétrie de getAttributionHeaders. Voir packages/coding-agent/src/core/sdk.ts:328-346.
  5. Wrapping de convertToLlm : quand blockImages est activé, on remplace tout ImageContent par du texte placeholder, en relisant dynamiquement les settings pour qu'un changement en cours de session soit pris en compte. Voir packages/coding-agent/src/core/sdk.ts:282-316.
  6. Headers d'attribution : OpenRouter et Cloudflare reçoivent des headers HTTP dédiés pour le catégorisation. Voir packages/coding-agent/src/core/sdk.ts:128-156.

Motivation de design

Pourquoi ne pas laisser l'appelant faire lui-même new Agent puis new AgentSession ? Parce que l'assemblage implique une dizaine de services aux dépendances croisées : auth détermine si le modèle est disponible, modèle détermine la plage de thinking level, settings détermine retry/transport/steering, le resource loader charge les extensions et les injecte dans extensionRunnerRef. Exposer tout ça à l'appelant reviendrait à lui faire refaire les mêmes erreurs à chacun. createAgentSession concentre les règles d'assemblage dans une seule fonction — l'appelant SDK ne fournit que cwd et (optionnellement) model.

getAttributionHeaders est isolé dans sa propre fonction parce que les headers attendus varient selon le provider : OpenRouter regarde HTTP-Referer + X-OpenRouter-*, Cloudflare regarde User-Agent. Centraliser évite la dispersion le long du chemin de stream.

Fichiers clés

Le point d'injection de streamFn montre comment un échec d'auth court-circuite et comment les headers d'attribution sont fusionnés :

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,
	});
},

Le wrapper convertToLlm, quand blockImages est activé, remplace les images par un texte placeholder et déduplique les placeholders consécutifs :

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

Flux de données

L'ordre d'assemblage :

Limites et échecs

  • Modèle non restaurable : modelFallbackMessage couvre à la fois « impossible de restaurer le modèle enregistré » et « aucun modèle configuré », en renvoyant les deux — c'est l'UI qui décide comment l'afficher. Voir packages/coding-agent/src/core/sdk.ts:243-247.
  • Pas de modèle ⇒ thinking off : if (!model) thinkingLevel = "off", pour éviter d'envoyer des paramètres de thinking à un modèle vide.
  • Session existante sans entrée thinking : à la restauration, on ajoute un appendThinkingLevelChange pour que les fork/resume ultérieurs retrouvent l'état. Voir packages/coding-agent/src/core/sdk.ts:378-390.
  • transformContext des extensions : si extensionRunnerRef.current n'existe pas, on renvoie directement les messages d'origine — pas de throw, pour continuer à fonctionner quand l'extension n'est pas encore liée.

Résumé

createAgentSession concentre les règles d'assemblage d'une dizaine de services dans une seule fonction ; l'appelant SDK ne fournit que cwd et un modèle optionnel. Pour les détails de l'assemblage en aval, voir AgentSession : couche d'orchestration ; pour les factories d'outils, voir Ensemble d'outils read/bash/edit/write/grep/find/ls ; pour la conversion des messages, voir Types de messages et convertToLlm.