@averyyy/pi-coding-agent 0.87.0-piclient.2 → 0.87.1-piclient.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +25 -711
- package/dist/bundle/chunks/{anthropic-messages-MYU5ZMRF.js → anthropic-messages-J5WXPPPC.js} +1 -1
- package/dist/bundle/chunks/{chunk-6OYWS3W7.js → chunk-BZKWNWMI.js} +1 -1
- package/dist/bundle/chunks/{chunk-KEXYQWZW.js → chunk-X2F52Z34.js} +45 -29
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{openai-completions-XHML6MTL.js → openai-completions-OBX42CLD.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-5W7KEXM3.js → virtual-modules-WWGGM27U.js} +1 -1
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +14 -4
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +73 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +10 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +82 -23
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/pi-server-client.d.ts +5 -0
- package/dist/core/pi-server-client.d.ts.map +1 -1
- package/dist/core/pi-server-client.js +3 -0
- package/dist/core/pi-server-client.js.map +1 -1
- package/dist/core/pi-server-protocol.d.ts +2 -1
- package/dist/core/pi-server-protocol.d.ts.map +1 -1
- package/dist/core/pi-server-protocol.js +16 -0
- package/dist/core/pi-server-protocol.js.map +1 -1
- package/docs/cli-integration.md +106 -0
- package/docs/cli.md +268 -0
- package/docs/compaction.md +22 -22
- package/docs/configuration.md +45 -0
- package/docs/containerization.md +109 -82
- package/docs/custom-provider.md +132 -784
- package/docs/docs.json +139 -99
- package/docs/environment-variables.md +3 -5
- package/docs/extensions.md +134 -3020
- package/docs/how-pi-works.md +49 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/index.md +24 -69
- package/docs/json.md +193 -65
- package/docs/keybindings.md +57 -102
- package/docs/llama-cpp.md +3 -3
- package/docs/message-types.md +261 -0
- package/docs/models.md +55 -565
- package/docs/packages.md +66 -167
- package/docs/prompt-templates.md +31 -68
- package/docs/providers.md +102 -240
- package/docs/quickstart.md +61 -106
- package/docs/rpc-commands.md +854 -0
- package/docs/rpc-extension-ui.md +200 -0
- package/docs/rpc.md +129 -1556
- package/docs/sdk.md +76 -1171
- package/docs/security.md +70 -32
- package/docs/session-format.md +10 -214
- package/docs/sessions.md +35 -141
- package/docs/settings.md +109 -387
- package/docs/shell-aliases.md +85 -5
- package/docs/skills.md +51 -190
- package/docs/slash-commands.md +60 -0
- package/docs/terminal-setup.md +105 -78
- package/docs/termux.md +74 -83
- package/docs/themes.md +68 -280
- package/docs/tmux.md +31 -39
- package/docs/tui.md +69 -923
- package/docs/usage.md +54 -272
- package/docs/windows.md +43 -17
- package/examples/README.md +13 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-client.ts +35 -0
- package/examples/rpc-extension-ui.ts +25 -5
- package/examples/sdk/README.md +1 -1
- package/package.json +8 -8
- package/docs/development.md +0 -90
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-resolver.js","sourceRoot":"","sources":["../../src/core/model-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model resolution, scoping, and initial selection\n */\n\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype Api,\n\ttype AuthOperationOptions,\n\ttype KnownProvider,\n\ttype Model,\n\tmodelsAreEqual,\n} from \"@earendil-works/pi-ai\";\nimport chalk from \"chalk\";\nimport { minimatch } from \"minimatch\";\nimport { isValidThinkingLevel } from \"../cli/args.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\n\n/** Default model IDs for each known provider */\nexport const defaultModelPerProvider: Record<KnownProvider, string> = {\n\t\"amazon-bedrock\": \"us.anthropic.claude-opus-4-6-v1\",\n\t\"ant-ling\": \"Ring-2.6-1T\",\n\tanthropic: \"claude-opus-4-8\",\n\topenai: \"gpt-5.5\",\n\t\"azure-openai-responses\": \"gpt-5.4\",\n\t\"openai-codex\": \"gpt-5.5\",\n\tradius: \"balanced\",\n\tnvidia: \"nvidia/nemotron-3-super-120b-a12b\",\n\tdeepseek: \"deepseek-v4-pro\",\n\tgoogle: \"gemini-3.1-pro-preview\",\n\t\"google-vertex\": \"gemini-3.1-pro-preview\",\n\t\"github-copilot\": \"gpt-5.4\",\n\topenrouter: \"moonshotai/kimi-k2.6\",\n\t\"vercel-ai-gateway\": \"zai/glm-5.1\",\n\txai: \"grok-4.6\",\n\tgroq: \"openai/gpt-oss-120b\",\n\tcerebras: \"gpt-oss-120b\",\n\tzai: \"glm-5.3\",\n\t\"zai-coding-cn\": \"glm-5.3\",\n\tmistral: \"devstral-medium-latest\",\n\tminimax: \"MiniMax-M2.7\",\n\t\"minimax-cn\": \"MiniMax-M2.7\",\n\tmoonshotai: \"kimi-k2.6\",\n\t\"moonshotai-cn\": \"kimi-k2.6\",\n\thuggingface: \"moonshotai/Kimi-K2.6\",\n\tfireworks: \"accounts/fireworks/models/kimi-k2p6\",\n\ttogether: \"moonshotai/Kimi-K2.6\",\n\tbaseten: \"zai-org/GLM-5.2\",\n\topencode: \"kimi-k2.6\",\n\t\"opencode-go\": \"kimi-k2.6\",\n\t\"kimi-coding\": \"kimi-for-coding\",\n\tmeta: \"muse-spark-1.3\",\n\t\"cloudflare-workers-ai\": \"@cf/moonshotai/kimi-k2.6\",\n\t\"cloudflare-ai-gateway\": \"workers-ai/@cf/moonshotai/kimi-k2.6\",\n\t\"qwen-token-plan\": \"qwen3.7-max\",\n\t\"qwen-token-plan-cn\": \"qwen3.7-max\",\n\t\"qwen-token-plan-individual\": \"qwen3.8-max\",\n\txiaomi: \"mimo-v2.5-pro\",\n\t\"xiaomi-token-plan-cn\": \"mimo-v2.5-pro\",\n\t\"xiaomi-token-plan-ams\": \"mimo-v2.5-pro\",\n\t\"xiaomi-token-plan-sgp\": \"mimo-v2.5-pro\",\n};\n\nexport interface ScopedModel {\n\tmodel: Model<Api>;\n\t/** Thinking level if explicitly specified in pattern (e.g., \"model:high\"), undefined otherwise */\n\tthinkingLevel?: ThinkingLevel;\n}\n\n/**\n * Helper to check if a model ID looks like an alias (no date suffix)\n * Dates are typically in format: -20241022 or -20250929\n */\nfunction isAlias(id: string): boolean {\n\t// Check if ID ends with -latest\n\tif (id.endsWith(\"-latest\")) return true;\n\n\t// Check if ID ends with a date pattern (-YYYYMMDD)\n\tconst datePattern = /-\\d{8}$/;\n\treturn !datePattern.test(id);\n}\n\n/**\n * Find an exact model reference match.\n * Supports either a bare model id or a canonical provider/modelId reference.\n * When matching by bare id, ambiguous matches across providers are rejected.\n */\nexport function findExactModelReferenceMatch(\n\tmodelReference: string,\n\tavailableModels: Model<Api>[],\n): Model<Api> | undefined {\n\tconst trimmedReference = modelReference.trim();\n\tif (!trimmedReference) {\n\t\treturn undefined;\n\t}\n\n\tconst normalizedReference = trimmedReference.toLowerCase();\n\n\tconst canonicalMatches = availableModels.filter(\n\t\t(model) => `${model.provider}/${model.id}`.toLowerCase() === normalizedReference,\n\t);\n\tif (canonicalMatches.length === 1) {\n\t\treturn canonicalMatches[0];\n\t}\n\tif (canonicalMatches.length > 1) {\n\t\treturn undefined;\n\t}\n\n\tconst slashIndex = trimmedReference.indexOf(\"/\");\n\tif (slashIndex !== -1) {\n\t\tconst provider = trimmedReference.substring(0, slashIndex).trim();\n\t\tconst modelId = trimmedReference.substring(slashIndex + 1).trim();\n\t\tif (provider && modelId) {\n\t\t\tconst providerMatches = availableModels.filter(\n\t\t\t\t(model) =>\n\t\t\t\t\tmodel.provider.toLowerCase() === provider.toLowerCase() &&\n\t\t\t\t\tmodel.id.toLowerCase() === modelId.toLowerCase(),\n\t\t\t);\n\t\t\tif (providerMatches.length === 1) {\n\t\t\t\treturn providerMatches[0];\n\t\t\t}\n\t\t\tif (providerMatches.length > 1) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst idMatches = availableModels.filter((model) => model.id.toLowerCase() === normalizedReference);\n\treturn idMatches.length === 1 ? idMatches[0] : undefined;\n}\n\n/**\n * Try to match a pattern to a model from the available models list.\n * Returns the matched model or undefined if no match found.\n */\nfunction tryMatchModel(modelPattern: string, availableModels: Model<Api>[]): Model<Api> | undefined {\n\tconst exactMatch = findExactModelReferenceMatch(modelPattern, availableModels);\n\tif (exactMatch) {\n\t\treturn exactMatch;\n\t}\n\n\t// No exact match - fall back to partial matching\n\tconst matches = availableModels.filter(\n\t\t(m) =>\n\t\t\tm.id.toLowerCase().includes(modelPattern.toLowerCase()) ||\n\t\t\tm.name?.toLowerCase().includes(modelPattern.toLowerCase()),\n\t);\n\n\tif (matches.length === 0) {\n\t\treturn undefined;\n\t}\n\n\t// Separate into aliases and dated versions\n\tconst aliases = matches.filter((m) => isAlias(m.id));\n\tconst datedVersions = matches.filter((m) => !isAlias(m.id));\n\n\tif (aliases.length > 0) {\n\t\t// Prefer alias - if multiple aliases, pick the one that sorts highest\n\t\taliases.sort((a, b) => b.id.localeCompare(a.id));\n\t\treturn aliases[0];\n\t} else {\n\t\t// No alias found, pick latest dated version\n\t\tdatedVersions.sort((a, b) => b.id.localeCompare(a.id));\n\t\treturn datedVersions[0];\n\t}\n}\n\nexport interface ParsedModelResult {\n\tmodel: Model<Api> | undefined;\n\t/** Thinking level if explicitly specified in pattern, undefined otherwise */\n\tthinkingLevel?: ThinkingLevel;\n\twarning: string | undefined;\n}\n\nfunction buildFallbackModel(provider: string, modelId: string, availableModels: Model<Api>[]): Model<Api> | undefined {\n\tconst providerModels = availableModels.filter((m) => m.provider === provider);\n\tif (providerModels.length === 0) return undefined;\n\n\tconst defaultId = defaultModelPerProvider[provider as KnownProvider];\n\tconst baseModel = defaultId\n\t\t? (providerModels.find((m) => m.id === defaultId) ?? providerModels[0])\n\t\t: providerModels[0];\n\n\treturn {\n\t\t...baseModel,\n\t\tid: modelId,\n\t\tname: modelId,\n\t};\n}\n\n/**\n * Parse a pattern to extract model and thinking level.\n * Handles models with colons in their IDs (e.g., OpenRouter's :exacto suffix).\n *\n * Algorithm:\n * 1. Try to match full pattern as a model\n * 2. If found, return it with \"off\" thinking level\n * 3. If not found and has colons, split on last colon:\n * - If suffix is valid thinking level, use it and recurse on prefix\n * - If suffix is invalid, warn and recurse on prefix with \"off\"\n *\n * @internal Exported for testing\n */\nexport function parseModelPattern(\n\tpattern: string,\n\tavailableModels: Model<Api>[],\n\toptions?: { allowInvalidThinkingLevelFallback?: boolean },\n): ParsedModelResult {\n\t// Try exact match first\n\tconst exactMatch = tryMatchModel(pattern, availableModels);\n\tif (exactMatch) {\n\t\treturn { model: exactMatch, thinkingLevel: undefined, warning: undefined };\n\t}\n\n\t// No match - try splitting on last colon if present\n\tconst lastColonIndex = pattern.lastIndexOf(\":\");\n\tif (lastColonIndex === -1) {\n\t\t// No colons, pattern simply doesn't match any model\n\t\treturn { model: undefined, thinkingLevel: undefined, warning: undefined };\n\t}\n\n\tconst prefix = pattern.substring(0, lastColonIndex);\n\tconst suffix = pattern.substring(lastColonIndex + 1);\n\n\tif (isValidThinkingLevel(suffix)) {\n\t\t// Valid thinking level - recurse on prefix and use this level\n\t\tconst result = parseModelPattern(prefix, availableModels, options);\n\t\tif (result.model) {\n\t\t\t// Only use this thinking level if no warning from inner recursion\n\t\t\treturn {\n\t\t\t\tmodel: result.model,\n\t\t\t\tthinkingLevel: result.warning ? undefined : suffix,\n\t\t\t\twarning: result.warning,\n\t\t\t};\n\t\t}\n\t\treturn result;\n\t} else {\n\t\t// Invalid suffix\n\t\tconst allowFallback = options?.allowInvalidThinkingLevelFallback ?? true;\n\t\tif (!allowFallback) {\n\t\t\t// In strict mode (CLI --model parsing), treat it as part of the model id and fail.\n\t\t\t// This avoids accidentally resolving to a different model.\n\t\t\treturn { model: undefined, thinkingLevel: undefined, warning: undefined };\n\t\t}\n\n\t\t// Scope mode: recurse on prefix and warn\n\t\tconst result = parseModelPattern(prefix, availableModels, options);\n\t\tif (result.model) {\n\t\t\treturn {\n\t\t\t\tmodel: result.model,\n\t\t\t\tthinkingLevel: undefined,\n\t\t\t\twarning: `Invalid thinking level \"${suffix}\" in pattern \"${pattern}\". Using default instead.`,\n\t\t\t};\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Resolve model patterns to actual Model objects with optional thinking levels\n * Format: \"pattern:level\" where :level is optional\n * For each pattern, finds all matching models and picks the best version:\n * 1. Prefer alias (e.g., claude-sonnet-4-5) over dated versions (claude-sonnet-4-5-20250929)\n * 2. If no alias, pick the latest dated version\n *\n * Supports models with colons in their IDs (e.g., OpenRouter's model:exacto).\n * The algorithm tries to match the full pattern first, then progressively\n * strips colon-suffixes to find a match.\n */\nexport interface ModelScopeDiagnostic {\n\ttype: \"warning\";\n\tcode: \"no-match\" | \"invalid-thinking-level\";\n\tmessage: string;\n\tpattern: string;\n}\n\nexport interface ResolveModelScopeResult {\n\tscopedModels: ScopedModel[];\n\tdiagnostics: ModelScopeDiagnostic[];\n}\n\nexport function resolveModelScopeFromModels(\n\tpatterns: string[],\n\tmodels: readonly Model<Api>[],\n): ResolveModelScopeResult {\n\tconst availableModels = [...models];\n\tconst scopedModels: ScopedModel[] = [];\n\tconst diagnostics: ModelScopeDiagnostic[] = [];\n\n\tfor (const pattern of patterns) {\n\t\t// Check if pattern contains glob characters\n\t\tif (pattern.includes(\"*\") || pattern.includes(\"?\") || pattern.includes(\"[\")) {\n\t\t\t// Extract optional thinking level suffix (e.g., \"provider/*:high\")\n\t\t\tconst colonIdx = pattern.lastIndexOf(\":\");\n\t\t\tlet globPattern = pattern;\n\t\t\tlet thinkingLevel: ThinkingLevel | undefined;\n\n\t\t\tif (colonIdx !== -1) {\n\t\t\t\tconst suffix = pattern.substring(colonIdx + 1);\n\t\t\t\tif (isValidThinkingLevel(suffix)) {\n\t\t\t\t\tthinkingLevel = suffix;\n\t\t\t\t\tglobPattern = pattern.substring(0, colonIdx);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst exactMatch = findExactModelReferenceMatch(globPattern, availableModels);\n\t\t\tif (exactMatch) {\n\t\t\t\tif (!scopedModels.find((sm) => modelsAreEqual(sm.model, exactMatch))) {\n\t\t\t\t\tscopedModels.push({ model: exactMatch, thinkingLevel });\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Match against \"provider/modelId\" format OR just model ID\n\t\t\t// This allows \"*sonnet*\" to match without requiring \"anthropic/*sonnet*\"\n\t\t\tconst matchingModels = availableModels.filter((m) => {\n\t\t\t\tconst fullId = `${m.provider}/${m.id}`;\n\t\t\t\treturn minimatch(fullId, globPattern, { nocase: true }) || minimatch(m.id, globPattern, { nocase: true });\n\t\t\t});\n\n\t\t\tif (matchingModels.length === 0) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tcode: \"no-match\",\n\t\t\t\t\tmessage: `No models match pattern \"${pattern}\"`,\n\t\t\t\t\tpattern,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const model of matchingModels) {\n\t\t\t\tif (!scopedModels.find((sm) => modelsAreEqual(sm.model, model))) {\n\t\t\t\t\tscopedModels.push({ model, thinkingLevel });\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { model, thinkingLevel, warning } = parseModelPattern(pattern, availableModels);\n\n\t\tif (warning) {\n\t\t\tdiagnostics.push({ type: \"warning\", code: \"invalid-thinking-level\", message: warning, pattern });\n\t\t}\n\n\t\tif (!model) {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"warning\",\n\t\t\t\tcode: \"no-match\",\n\t\t\t\tmessage: `No models match pattern \"${pattern}\"`,\n\t\t\t\tpattern,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Avoid duplicates\n\t\tif (!scopedModels.find((sm) => modelsAreEqual(sm.model, model))) {\n\t\t\tscopedModels.push({ model, thinkingLevel });\n\t\t}\n\t}\n\n\treturn { scopedModels, diagnostics };\n}\n\nexport async function resolveModelScopeWithDiagnostics(\n\tpatterns: string[],\n\tmodelRuntime: ModelRuntime,\n\toptions?: AuthOperationOptions,\n): Promise<ResolveModelScopeResult> {\n\treturn resolveModelScopeFromModels(patterns, await modelRuntime.getAvailable(undefined, options));\n}\n\nexport async function resolveModelScope(\n\tpatterns: string[],\n\tmodelRuntime: ModelRuntime,\n\toptions?: AuthOperationOptions,\n): Promise<ScopedModel[]> {\n\tconst { scopedModels, diagnostics } = await resolveModelScopeWithDiagnostics(patterns, modelRuntime, options);\n\tfor (const diagnostic of diagnostics) {\n\t\tconsole.warn(chalk.yellow(`Warning: ${diagnostic.message}`));\n\t}\n\treturn scopedModels;\n}\n\nexport interface ResolveCliModelResult {\n\tmodel: Model<Api> | undefined;\n\tthinkingLevel?: ThinkingLevel;\n\twarning: string | undefined;\n\t/**\n\t * Error message suitable for CLI display.\n\t * When set, model will be undefined.\n\t */\n\terror: string | undefined;\n}\n\n/**\n * Resolve a single model from CLI flags.\n *\n * Supports:\n * - --provider <provider> --model <pattern>\n * - --model <provider>/<pattern>\n * - Fuzzy matching (same rules as model scoping: exact id, then partial id/name)\n *\n * Note: This does not apply the thinking level by itself, but it may *parse* and\n * return a thinking level from \"<pattern>:<thinking>\" so the caller can apply it.\n */\nexport function resolveCliModel(options: {\n\tcliProvider?: string;\n\tcliModel?: string;\n\tcliThinking?: ThinkingLevel;\n\tmodelRuntime: ModelRuntime;\n}): ResolveCliModelResult {\n\tconst { cliProvider, cliModel, cliThinking, modelRuntime } = options;\n\n\tif (!cliModel) {\n\t\treturn { model: undefined, warning: undefined, error: undefined };\n\t}\n\n\t// Important: use *all* models here, not just models with pre-configured auth.\n\t// This allows \"--api-key\" to be used for first-time setup.\n\tconst availableModels = [...modelRuntime.getModels()];\n\tif (availableModels.length === 0) {\n\t\treturn {\n\t\t\tmodel: undefined,\n\t\t\twarning: undefined,\n\t\t\terror: \"No models available. Check your installation or add models to models.json.\",\n\t\t};\n\t}\n\n\t// Build canonical provider lookup (case-insensitive)\n\tconst providerMap = new Map<string, string>();\n\tfor (const m of availableModels) {\n\t\tproviderMap.set(m.provider.toLowerCase(), m.provider);\n\t}\n\n\tlet provider = cliProvider ? providerMap.get(cliProvider.toLowerCase()) : undefined;\n\tif (cliProvider && !provider) {\n\t\treturn {\n\t\t\tmodel: undefined,\n\t\t\twarning: undefined,\n\t\t\terror: `Unknown provider \"${cliProvider}\". Use --list-models to see available providers/models.`,\n\t\t};\n\t}\n\n\t// If no explicit --provider, try to interpret \"provider/model\" format first.\n\t// When the prefix before the first slash matches a known provider, prefer that\n\t// interpretation over matching models whose IDs literally contain slashes\n\t// (e.g. \"zai/glm-5\" should resolve to provider=zai, model=glm-5, not to a\n\t// vercel-ai-gateway model with id \"zai/glm-5\").\n\tlet pattern = cliModel;\n\tlet inferredProvider = false;\n\n\tif (!provider) {\n\t\tconst slashIndex = cliModel.indexOf(\"/\");\n\t\tif (slashIndex !== -1) {\n\t\t\tconst maybeProvider = cliModel.substring(0, slashIndex);\n\t\t\tconst canonical = providerMap.get(maybeProvider.toLowerCase());\n\t\t\tif (canonical) {\n\t\t\t\tprovider = canonical;\n\t\t\t\tpattern = cliModel.substring(slashIndex + 1);\n\t\t\t\tinferredProvider = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If no provider was inferred from the slash, try exact matches without provider inference.\n\t// This handles models whose IDs naturally contain slashes (e.g. OpenRouter-style IDs).\n\t// Bare exact IDs can exist in multiple providers, so do not choose by catalog order.\n\t// Prefer the sole authenticated provider when there is one; otherwise require an\n\t// explicit provider to avoid silently selecting an unusable provider.\n\tif (!provider) {\n\t\tconst lower = cliModel.toLowerCase();\n\t\tconst exactMatches = availableModels.filter(\n\t\t\t(m) => m.id.toLowerCase() === lower || `${m.provider}/${m.id}`.toLowerCase() === lower,\n\t\t);\n\t\tif (exactMatches.length === 1) {\n\t\t\treturn { model: exactMatches[0], warning: undefined, thinkingLevel: undefined, error: undefined };\n\t\t}\n\t\tif (exactMatches.length > 1) {\n\t\t\tconst authenticatedExactMatches = exactMatches.filter((m) => modelRuntime.hasConfiguredAuth(m.provider));\n\t\t\tif (authenticatedExactMatches.length === 1) {\n\t\t\t\treturn {\n\t\t\t\t\tmodel: authenticatedExactMatches[0],\n\t\t\t\t\twarning: undefined,\n\t\t\t\t\tthinkingLevel: undefined,\n\t\t\t\t\terror: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst matches = exactMatches\n\t\t\t\t.map((m) => `${m.provider}/${m.id}`)\n\t\t\t\t.sort((a, b) => a.localeCompare(b))\n\t\t\t\t.join(\", \");\n\t\t\tconst authHint =\n\t\t\t\tauthenticatedExactMatches.length === 0\n\t\t\t\t\t? \"No matching provider is authenticated.\"\n\t\t\t\t\t: \"More than one matching provider is authenticated.\";\n\t\t\treturn {\n\t\t\t\tmodel: undefined,\n\t\t\t\twarning: undefined,\n\t\t\t\tthinkingLevel: undefined,\n\t\t\t\terror: `Model \"${cliModel}\" is ambiguous across providers: ${matches}. ${authHint} Use --provider or provider/model.`,\n\t\t\t};\n\t\t}\n\t}\n\n\tif (cliProvider && provider) {\n\t\t// If both were provided, tolerate --model <provider>/<pattern> by stripping the provider prefix\n\t\tconst prefix = `${provider}/`;\n\t\tif (cliModel.toLowerCase().startsWith(prefix.toLowerCase())) {\n\t\t\tpattern = cliModel.substring(prefix.length);\n\t\t}\n\t}\n\n\tconst candidates = provider ? availableModels.filter((m) => m.provider === provider) : availableModels;\n\tconst { model, thinkingLevel, warning } = parseModelPattern(pattern, candidates, {\n\t\tallowInvalidThinkingLevelFallback: false,\n\t});\n\n\tif (model) {\n\t\t// If provider inference matched an unauthenticated provider/model pair, prefer\n\t\t// one exact raw model-id match that is authenticated. This keeps\n\t\t// \"provider/model\" syntax preferred when usable, but handles models whose\n\t\t// literal id starts with a known provider name (for example\n\t\t// commandcode model id \"xiaomi/mimo-v2.5-pro\").\n\t\tif (inferredProvider) {\n\t\t\tconst rawExactMatches = availableModels.filter(\n\t\t\t\t(m) => m.id.toLowerCase() === cliModel.toLowerCase() && !modelsAreEqual(m, model),\n\t\t\t);\n\t\t\tif (rawExactMatches.length > 0 && !modelRuntime.hasConfiguredAuth(model.provider)) {\n\t\t\t\tconst authenticatedRawMatches = rawExactMatches.filter((m) => modelRuntime.hasConfiguredAuth(m.provider));\n\t\t\t\tif (authenticatedRawMatches.length === 1) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tmodel: authenticatedRawMatches[0],\n\t\t\t\t\t\tthinkingLevel: undefined,\n\t\t\t\t\t\twarning: undefined,\n\t\t\t\t\t\terror: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn { model, thinkingLevel, warning, error: undefined };\n\t}\n\n\t// If we inferred a provider from the slash but found no match within that provider,\n\t// fall back to matching the full input as a raw model id across all models.\n\t// This handles OpenRouter-style IDs like \"openai/gpt-4o:extended\" where \"openai\"\n\t// looks like a provider but the full string is actually a model id on openrouter.\n\tif (inferredProvider) {\n\t\tconst lower = cliModel.toLowerCase();\n\t\tconst exact = availableModels.find(\n\t\t\t(m) => m.id.toLowerCase() === lower || `${m.provider}/${m.id}`.toLowerCase() === lower,\n\t\t);\n\t\tif (exact) {\n\t\t\treturn { model: exact, warning: undefined, thinkingLevel: undefined, error: undefined };\n\t\t}\n\t\t// Also try parseModelPattern on the full input against all models\n\t\tconst fallback = parseModelPattern(cliModel, availableModels, {\n\t\t\tallowInvalidThinkingLevelFallback: false,\n\t\t});\n\t\tif (fallback.model) {\n\t\t\treturn {\n\t\t\t\tmodel: fallback.model,\n\t\t\t\tthinkingLevel: fallback.thinkingLevel,\n\t\t\t\twarning: fallback.warning,\n\t\t\t\terror: undefined,\n\t\t\t};\n\t\t}\n\t}\n\n\tif (provider) {\n\t\t// Parse thinking level suffix from the pattern before building the fallback model,\n\t\t// but only when --thinking is not explicitly provided.\n\t\t// e.g. \"zai-org/GLM-5.1-FP8:high\" → modelId=\"zai-org/GLM-5.1-FP8\", fallbackThinking=\"high\"\n\t\tlet fallbackPattern = pattern;\n\t\tlet fallbackThinking: ThinkingLevel | undefined;\n\t\tif (!cliThinking) {\n\t\t\tconst lastColon = pattern.lastIndexOf(\":\");\n\t\t\tif (lastColon !== -1) {\n\t\t\t\tconst suffix = pattern.substring(lastColon + 1);\n\t\t\t\tif (isValidThinkingLevel(suffix)) {\n\t\t\t\t\tfallbackPattern = pattern.substring(0, lastColon);\n\t\t\t\t\tfallbackThinking = suffix;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst fallbackModel = buildFallbackModel(provider, fallbackPattern, availableModels);\n\t\tif (fallbackModel) {\n\t\t\tconst requestedThinking = cliThinking ?? fallbackThinking;\n\t\t\tconst model =\n\t\t\t\trequestedThinking && requestedThinking !== \"off\" ? { ...fallbackModel, reasoning: true } : fallbackModel;\n\t\t\tconst fallbackWarning = warning\n\t\t\t\t? `${warning} Model \"${fallbackPattern}\" not found for provider \"${provider}\". Using custom model id.`\n\t\t\t\t: `Model \"${fallbackPattern}\" not found for provider \"${provider}\". Using custom model id.`;\n\t\t\treturn { model, thinkingLevel: fallbackThinking, warning: fallbackWarning, error: undefined };\n\t\t}\n\t}\n\n\tconst display = provider ? `${provider}/${pattern}` : cliModel;\n\treturn {\n\t\tmodel: undefined,\n\t\tthinkingLevel: undefined,\n\t\twarning,\n\t\terror: `Model \"${display}\" not found. Use --list-models to see available models.`,\n\t};\n}\n\nexport interface InitialModelResult {\n\tmodel: Model<Api> | undefined;\n\tthinkingLevel: ThinkingLevel;\n\tfallbackMessage: string | undefined;\n}\n\n/**\n * Find the initial model to use based on priority:\n * 1. CLI args (provider + model)\n * 2. First model from scoped models (if not continuing/resuming)\n * 3. Restored from session (if continuing/resuming)\n * 4. Saved default from settings\n * 5. First available model with valid API key\n */\nexport async function findInitialModel(options: {\n\tcliProvider?: string;\n\tcliModel?: string;\n\tscopedModels: ScopedModel[];\n\tisContinuing: boolean;\n\tdefaultProvider?: string;\n\tdefaultModelId?: string;\n\tdefaultThinkingLevel?: ThinkingLevel;\n\tmodelThinkingLevels?: Record<string, ThinkingLevel>;\n\tmodelRuntime: ModelRuntime;\n}): Promise<InitialModelResult> {\n\tconst {\n\t\tcliProvider,\n\t\tcliModel,\n\t\tscopedModels,\n\t\tisContinuing,\n\t\tdefaultProvider,\n\t\tdefaultModelId,\n\t\tdefaultThinkingLevel,\n\t\tmodelThinkingLevels,\n\t\tmodelRuntime,\n\t} = options;\n\n\tlet model: Model<Api> | undefined;\n\tlet thinkingLevel: ThinkingLevel = DEFAULT_THINKING_LEVEL;\n\n\t// 1. CLI args take priority\n\tif (cliProvider && cliModel) {\n\t\tconst resolved = resolveCliModel({\n\t\t\tcliProvider,\n\t\t\tcliModel,\n\t\t\tmodelRuntime,\n\t\t});\n\t\tif (resolved.error) {\n\t\t\tconsole.error(chalk.red(resolved.error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (resolved.model) {\n\t\t\treturn { model: resolved.model, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };\n\t\t}\n\t}\n\n\t// 2. Use first model from scoped models (skip if continuing/resuming)\n\tif (scopedModels.length > 0 && !isContinuing) {\n\t\tconst scopedModel = scopedModels[0];\n\t\tconst perModel = modelThinkingLevels?.[`${scopedModel.model.provider}/${scopedModel.model.id}`];\n\t\treturn {\n\t\t\tmodel: scopedModel.model,\n\t\t\tthinkingLevel: scopedModel.thinkingLevel ?? perModel ?? defaultThinkingLevel ?? DEFAULT_THINKING_LEVEL,\n\t\t\tfallbackMessage: undefined,\n\t\t};\n\t}\n\n\t// 3. Try saved default from settings if auth is configured.\n\tif (defaultProvider && defaultModelId) {\n\t\tconst found = modelRuntime.getModel(defaultProvider, defaultModelId);\n\t\tif (found && modelRuntime.hasConfiguredAuth(found.provider)) {\n\t\t\tmodel = found;\n\t\t\tconst perModel = modelThinkingLevels?.[`${defaultProvider}/${defaultModelId}`];\n\t\t\tif (perModel) {\n\t\t\t\tthinkingLevel = perModel;\n\t\t\t} else if (defaultThinkingLevel) {\n\t\t\t\tthinkingLevel = defaultThinkingLevel;\n\t\t\t}\n\t\t\treturn { model, thinkingLevel, fallbackMessage: undefined };\n\t\t}\n\t}\n\n\t// 4. Try first available model with valid API key\n\tconst availableModels = [...modelRuntime.getAvailableSnapshot()];\n\n\tif (availableModels.length > 0) {\n\t\t// Try to find a default model from known providers\n\t\tfor (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) {\n\t\t\tconst defaultId = defaultModelPerProvider[provider];\n\t\t\tconst match = availableModels.find((m) => m.provider === provider && m.id === defaultId);\n\t\t\tif (match) {\n\t\t\t\treturn { model: match, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };\n\t\t\t}\n\t\t}\n\n\t\t// If no default found, use first available\n\t\treturn { model: availableModels[0], thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };\n\t}\n\n\t// 5. No model found\n\treturn { model: undefined, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };\n}\n\n/**\n * Restore model from session, with fallback to available models\n */\nexport async function restoreModelFromSession(\n\tsavedProvider: string,\n\tsavedModelId: string,\n\tcurrentModel: Model<Api> | undefined,\n\tshouldPrintMessages: boolean,\n\tmodelRuntime: ModelRuntime,\n): Promise<{ model: Model<Api> | undefined; fallbackMessage: string | undefined }> {\n\tconst restoredModel = modelRuntime.getModel(savedProvider, savedModelId);\n\n\t// Check if restored model exists and still has auth configured\n\tconst hasConfiguredAuth = restoredModel ? modelRuntime.hasConfiguredAuth(restoredModel.provider) : false;\n\n\tif (restoredModel && hasConfiguredAuth) {\n\t\tif (shouldPrintMessages) {\n\t\t\tconsole.log(chalk.dim(`Restored model: ${savedProvider}/${savedModelId}`));\n\t\t}\n\t\treturn { model: restoredModel, fallbackMessage: undefined };\n\t}\n\n\t// Model not found or no API key - fall back\n\tconst reason = !restoredModel ? \"model no longer exists\" : \"no auth configured\";\n\n\tif (shouldPrintMessages) {\n\t\tconsole.error(chalk.yellow(`Warning: Could not restore model ${savedProvider}/${savedModelId} (${reason}).`));\n\t}\n\n\t// If we already have a model, use it as fallback\n\tif (currentModel) {\n\t\tif (shouldPrintMessages) {\n\t\t\tconsole.log(chalk.dim(`Falling back to: ${currentModel.provider}/${currentModel.id}`));\n\t\t}\n\t\treturn {\n\t\t\tmodel: currentModel,\n\t\t\tfallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${currentModel.provider}/${currentModel.id}.`,\n\t\t};\n\t}\n\n\t// Try to find any available model\n\tconst availableModels = [...modelRuntime.getAvailableSnapshot()];\n\n\tif (availableModels.length > 0) {\n\t\t// Try to find a default model from known providers\n\t\tlet fallbackModel: Model<Api> | undefined;\n\t\tfor (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) {\n\t\t\tconst defaultId = defaultModelPerProvider[provider];\n\t\t\tconst match = availableModels.find((m) => m.provider === provider && m.id === defaultId);\n\t\t\tif (match) {\n\t\t\t\tfallbackModel = match;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// If no default found, use first available\n\t\tif (!fallbackModel) {\n\t\t\tfallbackModel = availableModels[0];\n\t\t}\n\n\t\tif (shouldPrintMessages) {\n\t\t\tconsole.log(chalk.dim(`Falling back to: ${fallbackModel.provider}/${fallbackModel.id}`));\n\t\t}\n\n\t\treturn {\n\t\t\tmodel: fallbackModel,\n\t\t\tfallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${fallbackModel.provider}/${fallbackModel.id}.`,\n\t\t};\n\t}\n\n\t// No models available\n\treturn { model: undefined, fallbackMessage: undefined };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model-resolver.js","sourceRoot":"","sources":["../../src/core/model-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model resolution, scoping, and initial selection\n */\n\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype Api,\n\ttype AuthOperationOptions,\n\ttype KnownProvider,\n\ttype Model,\n\tmodelsAreEqual,\n} from \"@earendil-works/pi-ai\";\nimport chalk from \"chalk\";\nimport { minimatch } from \"minimatch\";\nimport { isValidThinkingLevel } from \"../cli/args.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type { ModelRuntime } from \"./model-runtime.ts\";\n\n/** Default model IDs for each known provider */\nexport const defaultModelPerProvider: Record<KnownProvider, string> = {\n\t\"amazon-bedrock\": \"us.anthropic.claude-opus-4-6-v1\",\n\t\"ant-ling\": \"Ring-2.6-1T\",\n\tanthropic: \"claude-opus-4-8\",\n\topenai: \"gpt-5.5\",\n\t\"azure-openai-responses\": \"gpt-5.4\",\n\t\"openai-codex\": \"gpt-5.5\",\n\tradius: \"balanced\",\n\tnvidia: \"nvidia/nemotron-3-super-120b-a12b\",\n\tdeepseek: \"deepseek-v4-pro\",\n\tgoogle: \"gemini-3.1-pro-preview\",\n\t\"google-vertex\": \"gemini-3.1-pro-preview\",\n\t\"github-copilot\": \"gpt-5.4\",\n\topenrouter: \"moonshotai/kimi-k2.6\",\n\t\"vercel-ai-gateway\": \"zai/glm-5.1\",\n\txai: \"grok-4.7\",\n\tgroq: \"openai/gpt-oss-120b\",\n\tcerebras: \"gpt-oss-120b\",\n\tzai: \"glm-5.3\",\n\t\"zai-coding-cn\": \"glm-5.3\",\n\tmistral: \"devstral-medium-latest\",\n\tminimax: \"MiniMax-M2.7\",\n\t\"minimax-cn\": \"MiniMax-M2.7\",\n\tmoonshotai: \"kimi-k2.6\",\n\t\"moonshotai-cn\": \"kimi-k2.6\",\n\thuggingface: \"moonshotai/Kimi-K2.6\",\n\tfireworks: \"accounts/fireworks/models/kimi-k2p6\",\n\ttogether: \"moonshotai/Kimi-K2.6\",\n\tbaseten: \"zai-org/GLM-5.2\",\n\topencode: \"kimi-k2.6\",\n\t\"opencode-go\": \"kimi-k2.6\",\n\t\"kimi-coding\": \"kimi-for-coding\",\n\tmeta: \"muse-spark-1.3\",\n\t\"cloudflare-workers-ai\": \"@cf/moonshotai/kimi-k2.6\",\n\t\"cloudflare-ai-gateway\": \"workers-ai/@cf/moonshotai/kimi-k2.6\",\n\t\"qwen-token-plan\": \"qwen3.7-max\",\n\t\"qwen-token-plan-cn\": \"qwen3.7-max\",\n\t\"qwen-token-plan-individual\": \"qwen3.8-max\",\n\txiaomi: \"mimo-v2.5-pro\",\n\t\"xiaomi-token-plan-cn\": \"mimo-v2.5-pro\",\n\t\"xiaomi-token-plan-ams\": \"mimo-v2.5-pro\",\n\t\"xiaomi-token-plan-sgp\": \"mimo-v2.5-pro\",\n};\n\nexport interface ScopedModel {\n\tmodel: Model<Api>;\n\t/** Thinking level if explicitly specified in pattern (e.g., \"model:high\"), undefined otherwise */\n\tthinkingLevel?: ThinkingLevel;\n}\n\n/**\n * Helper to check if a model ID looks like an alias (no date suffix)\n * Dates are typically in format: -20241022 or -20250929\n */\nfunction isAlias(id: string): boolean {\n\t// Check if ID ends with -latest\n\tif (id.endsWith(\"-latest\")) return true;\n\n\t// Check if ID ends with a date pattern (-YYYYMMDD)\n\tconst datePattern = /-\\d{8}$/;\n\treturn !datePattern.test(id);\n}\n\n/**\n * Find an exact model reference match.\n * Supports either a bare model id or a canonical provider/modelId reference.\n * When matching by bare id, ambiguous matches across providers are rejected.\n */\nexport function findExactModelReferenceMatch(\n\tmodelReference: string,\n\tavailableModels: Model<Api>[],\n): Model<Api> | undefined {\n\tconst trimmedReference = modelReference.trim();\n\tif (!trimmedReference) {\n\t\treturn undefined;\n\t}\n\n\tconst normalizedReference = trimmedReference.toLowerCase();\n\n\tconst canonicalMatches = availableModels.filter(\n\t\t(model) => `${model.provider}/${model.id}`.toLowerCase() === normalizedReference,\n\t);\n\tif (canonicalMatches.length === 1) {\n\t\treturn canonicalMatches[0];\n\t}\n\tif (canonicalMatches.length > 1) {\n\t\treturn undefined;\n\t}\n\n\tconst slashIndex = trimmedReference.indexOf(\"/\");\n\tif (slashIndex !== -1) {\n\t\tconst provider = trimmedReference.substring(0, slashIndex).trim();\n\t\tconst modelId = trimmedReference.substring(slashIndex + 1).trim();\n\t\tif (provider && modelId) {\n\t\t\tconst providerMatches = availableModels.filter(\n\t\t\t\t(model) =>\n\t\t\t\t\tmodel.provider.toLowerCase() === provider.toLowerCase() &&\n\t\t\t\t\tmodel.id.toLowerCase() === modelId.toLowerCase(),\n\t\t\t);\n\t\t\tif (providerMatches.length === 1) {\n\t\t\t\treturn providerMatches[0];\n\t\t\t}\n\t\t\tif (providerMatches.length > 1) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst idMatches = availableModels.filter((model) => model.id.toLowerCase() === normalizedReference);\n\treturn idMatches.length === 1 ? idMatches[0] : undefined;\n}\n\n/**\n * Try to match a pattern to a model from the available models list.\n * Returns the matched model or undefined if no match found.\n */\nfunction tryMatchModel(modelPattern: string, availableModels: Model<Api>[]): Model<Api> | undefined {\n\tconst exactMatch = findExactModelReferenceMatch(modelPattern, availableModels);\n\tif (exactMatch) {\n\t\treturn exactMatch;\n\t}\n\n\t// No exact match - fall back to partial matching\n\tconst matches = availableModels.filter(\n\t\t(m) =>\n\t\t\tm.id.toLowerCase().includes(modelPattern.toLowerCase()) ||\n\t\t\tm.name?.toLowerCase().includes(modelPattern.toLowerCase()),\n\t);\n\n\tif (matches.length === 0) {\n\t\treturn undefined;\n\t}\n\n\t// Separate into aliases and dated versions\n\tconst aliases = matches.filter((m) => isAlias(m.id));\n\tconst datedVersions = matches.filter((m) => !isAlias(m.id));\n\n\tif (aliases.length > 0) {\n\t\t// Prefer alias - if multiple aliases, pick the one that sorts highest\n\t\taliases.sort((a, b) => b.id.localeCompare(a.id));\n\t\treturn aliases[0];\n\t} else {\n\t\t// No alias found, pick latest dated version\n\t\tdatedVersions.sort((a, b) => b.id.localeCompare(a.id));\n\t\treturn datedVersions[0];\n\t}\n}\n\nexport interface ParsedModelResult {\n\tmodel: Model<Api> | undefined;\n\t/** Thinking level if explicitly specified in pattern, undefined otherwise */\n\tthinkingLevel?: ThinkingLevel;\n\twarning: string | undefined;\n}\n\nfunction buildFallbackModel(provider: string, modelId: string, availableModels: Model<Api>[]): Model<Api> | undefined {\n\tconst providerModels = availableModels.filter((m) => m.provider === provider);\n\tif (providerModels.length === 0) return undefined;\n\n\tconst defaultId = defaultModelPerProvider[provider as KnownProvider];\n\tconst baseModel = defaultId\n\t\t? (providerModels.find((m) => m.id === defaultId) ?? providerModels[0])\n\t\t: providerModels[0];\n\n\treturn {\n\t\t...baseModel,\n\t\tid: modelId,\n\t\tname: modelId,\n\t};\n}\n\n/**\n * Parse a pattern to extract model and thinking level.\n * Handles models with colons in their IDs (e.g., OpenRouter's :exacto suffix).\n *\n * Algorithm:\n * 1. Try to match full pattern as a model\n * 2. If found, return it with \"off\" thinking level\n * 3. If not found and has colons, split on last colon:\n * - If suffix is valid thinking level, use it and recurse on prefix\n * - If suffix is invalid, warn and recurse on prefix with \"off\"\n *\n * @internal Exported for testing\n */\nexport function parseModelPattern(\n\tpattern: string,\n\tavailableModels: Model<Api>[],\n\toptions?: { allowInvalidThinkingLevelFallback?: boolean },\n): ParsedModelResult {\n\t// Try exact match first\n\tconst exactMatch = tryMatchModel(pattern, availableModels);\n\tif (exactMatch) {\n\t\treturn { model: exactMatch, thinkingLevel: undefined, warning: undefined };\n\t}\n\n\t// No match - try splitting on last colon if present\n\tconst lastColonIndex = pattern.lastIndexOf(\":\");\n\tif (lastColonIndex === -1) {\n\t\t// No colons, pattern simply doesn't match any model\n\t\treturn { model: undefined, thinkingLevel: undefined, warning: undefined };\n\t}\n\n\tconst prefix = pattern.substring(0, lastColonIndex);\n\tconst suffix = pattern.substring(lastColonIndex + 1);\n\n\tif (isValidThinkingLevel(suffix)) {\n\t\t// Valid thinking level - recurse on prefix and use this level\n\t\tconst result = parseModelPattern(prefix, availableModels, options);\n\t\tif (result.model) {\n\t\t\t// Only use this thinking level if no warning from inner recursion\n\t\t\treturn {\n\t\t\t\tmodel: result.model,\n\t\t\t\tthinkingLevel: result.warning ? undefined : suffix,\n\t\t\t\twarning: result.warning,\n\t\t\t};\n\t\t}\n\t\treturn result;\n\t} else {\n\t\t// Invalid suffix\n\t\tconst allowFallback = options?.allowInvalidThinkingLevelFallback ?? true;\n\t\tif (!allowFallback) {\n\t\t\t// In strict mode (CLI --model parsing), treat it as part of the model id and fail.\n\t\t\t// This avoids accidentally resolving to a different model.\n\t\t\treturn { model: undefined, thinkingLevel: undefined, warning: undefined };\n\t\t}\n\n\t\t// Scope mode: recurse on prefix and warn\n\t\tconst result = parseModelPattern(prefix, availableModels, options);\n\t\tif (result.model) {\n\t\t\treturn {\n\t\t\t\tmodel: result.model,\n\t\t\t\tthinkingLevel: undefined,\n\t\t\t\twarning: `Invalid thinking level \"${suffix}\" in pattern \"${pattern}\". Using default instead.`,\n\t\t\t};\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Resolve model patterns to actual Model objects with optional thinking levels\n * Format: \"pattern:level\" where :level is optional\n * For each pattern, finds all matching models and picks the best version:\n * 1. Prefer alias (e.g., claude-sonnet-4-5) over dated versions (claude-sonnet-4-5-20250929)\n * 2. If no alias, pick the latest dated version\n *\n * Supports models with colons in their IDs (e.g., OpenRouter's model:exacto).\n * The algorithm tries to match the full pattern first, then progressively\n * strips colon-suffixes to find a match.\n */\nexport interface ModelScopeDiagnostic {\n\ttype: \"warning\";\n\tcode: \"no-match\" | \"invalid-thinking-level\";\n\tmessage: string;\n\tpattern: string;\n}\n\nexport interface ResolveModelScopeResult {\n\tscopedModels: ScopedModel[];\n\tdiagnostics: ModelScopeDiagnostic[];\n}\n\nexport function resolveModelScopeFromModels(\n\tpatterns: string[],\n\tmodels: readonly Model<Api>[],\n): ResolveModelScopeResult {\n\tconst availableModels = [...models];\n\tconst scopedModels: ScopedModel[] = [];\n\tconst diagnostics: ModelScopeDiagnostic[] = [];\n\n\tfor (const pattern of patterns) {\n\t\t// Check if pattern contains glob characters\n\t\tif (pattern.includes(\"*\") || pattern.includes(\"?\") || pattern.includes(\"[\")) {\n\t\t\t// Extract optional thinking level suffix (e.g., \"provider/*:high\")\n\t\t\tconst colonIdx = pattern.lastIndexOf(\":\");\n\t\t\tlet globPattern = pattern;\n\t\t\tlet thinkingLevel: ThinkingLevel | undefined;\n\n\t\t\tif (colonIdx !== -1) {\n\t\t\t\tconst suffix = pattern.substring(colonIdx + 1);\n\t\t\t\tif (isValidThinkingLevel(suffix)) {\n\t\t\t\t\tthinkingLevel = suffix;\n\t\t\t\t\tglobPattern = pattern.substring(0, colonIdx);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst exactMatch = findExactModelReferenceMatch(globPattern, availableModels);\n\t\t\tif (exactMatch) {\n\t\t\t\tif (!scopedModels.find((sm) => modelsAreEqual(sm.model, exactMatch))) {\n\t\t\t\t\tscopedModels.push({ model: exactMatch, thinkingLevel });\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Match against \"provider/modelId\" format OR just model ID\n\t\t\t// This allows \"*sonnet*\" to match without requiring \"anthropic/*sonnet*\"\n\t\t\tconst matchingModels = availableModels.filter((m) => {\n\t\t\t\tconst fullId = `${m.provider}/${m.id}`;\n\t\t\t\treturn minimatch(fullId, globPattern, { nocase: true }) || minimatch(m.id, globPattern, { nocase: true });\n\t\t\t});\n\n\t\t\tif (matchingModels.length === 0) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tcode: \"no-match\",\n\t\t\t\t\tmessage: `No models match pattern \"${pattern}\"`,\n\t\t\t\t\tpattern,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const model of matchingModels) {\n\t\t\t\tif (!scopedModels.find((sm) => modelsAreEqual(sm.model, model))) {\n\t\t\t\t\tscopedModels.push({ model, thinkingLevel });\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { model, thinkingLevel, warning } = parseModelPattern(pattern, availableModels);\n\n\t\tif (warning) {\n\t\t\tdiagnostics.push({ type: \"warning\", code: \"invalid-thinking-level\", message: warning, pattern });\n\t\t}\n\n\t\tif (!model) {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"warning\",\n\t\t\t\tcode: \"no-match\",\n\t\t\t\tmessage: `No models match pattern \"${pattern}\"`,\n\t\t\t\tpattern,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Avoid duplicates\n\t\tif (!scopedModels.find((sm) => modelsAreEqual(sm.model, model))) {\n\t\t\tscopedModels.push({ model, thinkingLevel });\n\t\t}\n\t}\n\n\treturn { scopedModels, diagnostics };\n}\n\nexport async function resolveModelScopeWithDiagnostics(\n\tpatterns: string[],\n\tmodelRuntime: ModelRuntime,\n\toptions?: AuthOperationOptions,\n): Promise<ResolveModelScopeResult> {\n\treturn resolveModelScopeFromModels(patterns, await modelRuntime.getAvailable(undefined, options));\n}\n\nexport async function resolveModelScope(\n\tpatterns: string[],\n\tmodelRuntime: ModelRuntime,\n\toptions?: AuthOperationOptions,\n): Promise<ScopedModel[]> {\n\tconst { scopedModels, diagnostics } = await resolveModelScopeWithDiagnostics(patterns, modelRuntime, options);\n\tfor (const diagnostic of diagnostics) {\n\t\tconsole.warn(chalk.yellow(`Warning: ${diagnostic.message}`));\n\t}\n\treturn scopedModels;\n}\n\nexport interface ResolveCliModelResult {\n\tmodel: Model<Api> | undefined;\n\tthinkingLevel?: ThinkingLevel;\n\twarning: string | undefined;\n\t/**\n\t * Error message suitable for CLI display.\n\t * When set, model will be undefined.\n\t */\n\terror: string | undefined;\n}\n\n/**\n * Resolve a single model from CLI flags.\n *\n * Supports:\n * - --provider <provider> --model <pattern>\n * - --model <provider>/<pattern>\n * - Fuzzy matching (same rules as model scoping: exact id, then partial id/name)\n *\n * Note: This does not apply the thinking level by itself, but it may *parse* and\n * return a thinking level from \"<pattern>:<thinking>\" so the caller can apply it.\n */\nexport function resolveCliModel(options: {\n\tcliProvider?: string;\n\tcliModel?: string;\n\tcliThinking?: ThinkingLevel;\n\tmodelRuntime: ModelRuntime;\n}): ResolveCliModelResult {\n\tconst { cliProvider, cliModel, cliThinking, modelRuntime } = options;\n\n\tif (!cliModel) {\n\t\treturn { model: undefined, warning: undefined, error: undefined };\n\t}\n\n\t// Important: use *all* models here, not just models with pre-configured auth.\n\t// This allows \"--api-key\" to be used for first-time setup.\n\tconst availableModels = [...modelRuntime.getModels()];\n\tif (availableModels.length === 0) {\n\t\treturn {\n\t\t\tmodel: undefined,\n\t\t\twarning: undefined,\n\t\t\terror: \"No models available. Check your installation or add models to models.json.\",\n\t\t};\n\t}\n\n\t// Build canonical provider lookup (case-insensitive)\n\tconst providerMap = new Map<string, string>();\n\tfor (const m of availableModels) {\n\t\tproviderMap.set(m.provider.toLowerCase(), m.provider);\n\t}\n\n\tlet provider = cliProvider ? providerMap.get(cliProvider.toLowerCase()) : undefined;\n\tif (cliProvider && !provider) {\n\t\treturn {\n\t\t\tmodel: undefined,\n\t\t\twarning: undefined,\n\t\t\terror: `Unknown provider \"${cliProvider}\". Use --list-models to see available providers/models.`,\n\t\t};\n\t}\n\n\t// If no explicit --provider, try to interpret \"provider/model\" format first.\n\t// When the prefix before the first slash matches a known provider, prefer that\n\t// interpretation over matching models whose IDs literally contain slashes\n\t// (e.g. \"zai/glm-5\" should resolve to provider=zai, model=glm-5, not to a\n\t// vercel-ai-gateway model with id \"zai/glm-5\").\n\tlet pattern = cliModel;\n\tlet inferredProvider = false;\n\n\tif (!provider) {\n\t\tconst slashIndex = cliModel.indexOf(\"/\");\n\t\tif (slashIndex !== -1) {\n\t\t\tconst maybeProvider = cliModel.substring(0, slashIndex);\n\t\t\tconst canonical = providerMap.get(maybeProvider.toLowerCase());\n\t\t\tif (canonical) {\n\t\t\t\tprovider = canonical;\n\t\t\t\tpattern = cliModel.substring(slashIndex + 1);\n\t\t\t\tinferredProvider = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If no provider was inferred from the slash, try exact matches without provider inference.\n\t// This handles models whose IDs naturally contain slashes (e.g. OpenRouter-style IDs).\n\t// Bare exact IDs can exist in multiple providers, so do not choose by catalog order.\n\t// Prefer the sole authenticated provider when there is one; otherwise require an\n\t// explicit provider to avoid silently selecting an unusable provider.\n\tif (!provider) {\n\t\tconst lower = cliModel.toLowerCase();\n\t\tconst exactMatches = availableModels.filter(\n\t\t\t(m) => m.id.toLowerCase() === lower || `${m.provider}/${m.id}`.toLowerCase() === lower,\n\t\t);\n\t\tif (exactMatches.length === 1) {\n\t\t\treturn { model: exactMatches[0], warning: undefined, thinkingLevel: undefined, error: undefined };\n\t\t}\n\t\tif (exactMatches.length > 1) {\n\t\t\tconst authenticatedExactMatches = exactMatches.filter((m) => modelRuntime.hasConfiguredAuth(m.provider));\n\t\t\tif (authenticatedExactMatches.length === 1) {\n\t\t\t\treturn {\n\t\t\t\t\tmodel: authenticatedExactMatches[0],\n\t\t\t\t\twarning: undefined,\n\t\t\t\t\tthinkingLevel: undefined,\n\t\t\t\t\terror: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst matches = exactMatches\n\t\t\t\t.map((m) => `${m.provider}/${m.id}`)\n\t\t\t\t.sort((a, b) => a.localeCompare(b))\n\t\t\t\t.join(\", \");\n\t\t\tconst authHint =\n\t\t\t\tauthenticatedExactMatches.length === 0\n\t\t\t\t\t? \"No matching provider is authenticated.\"\n\t\t\t\t\t: \"More than one matching provider is authenticated.\";\n\t\t\treturn {\n\t\t\t\tmodel: undefined,\n\t\t\t\twarning: undefined,\n\t\t\t\tthinkingLevel: undefined,\n\t\t\t\terror: `Model \"${cliModel}\" is ambiguous across providers: ${matches}. ${authHint} Use --provider or provider/model.`,\n\t\t\t};\n\t\t}\n\t}\n\n\tif (cliProvider && provider) {\n\t\t// If both were provided, tolerate --model <provider>/<pattern> by stripping the provider prefix\n\t\tconst prefix = `${provider}/`;\n\t\tif (cliModel.toLowerCase().startsWith(prefix.toLowerCase())) {\n\t\t\tpattern = cliModel.substring(prefix.length);\n\t\t}\n\t}\n\n\tconst candidates = provider ? availableModels.filter((m) => m.provider === provider) : availableModels;\n\tconst { model, thinkingLevel, warning } = parseModelPattern(pattern, candidates, {\n\t\tallowInvalidThinkingLevelFallback: false,\n\t});\n\n\tif (model) {\n\t\t// If provider inference matched an unauthenticated provider/model pair, prefer\n\t\t// one exact raw model-id match that is authenticated. This keeps\n\t\t// \"provider/model\" syntax preferred when usable, but handles models whose\n\t\t// literal id starts with a known provider name (for example\n\t\t// commandcode model id \"xiaomi/mimo-v2.5-pro\").\n\t\tif (inferredProvider) {\n\t\t\tconst rawExactMatches = availableModels.filter(\n\t\t\t\t(m) => m.id.toLowerCase() === cliModel.toLowerCase() && !modelsAreEqual(m, model),\n\t\t\t);\n\t\t\tif (rawExactMatches.length > 0 && !modelRuntime.hasConfiguredAuth(model.provider)) {\n\t\t\t\tconst authenticatedRawMatches = rawExactMatches.filter((m) => modelRuntime.hasConfiguredAuth(m.provider));\n\t\t\t\tif (authenticatedRawMatches.length === 1) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tmodel: authenticatedRawMatches[0],\n\t\t\t\t\t\tthinkingLevel: undefined,\n\t\t\t\t\t\twarning: undefined,\n\t\t\t\t\t\terror: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn { model, thinkingLevel, warning, error: undefined };\n\t}\n\n\t// If we inferred a provider from the slash but found no match within that provider,\n\t// fall back to matching the full input as a raw model id across all models.\n\t// This handles OpenRouter-style IDs like \"openai/gpt-4o:extended\" where \"openai\"\n\t// looks like a provider but the full string is actually a model id on openrouter.\n\tif (inferredProvider) {\n\t\tconst lower = cliModel.toLowerCase();\n\t\tconst exact = availableModels.find(\n\t\t\t(m) => m.id.toLowerCase() === lower || `${m.provider}/${m.id}`.toLowerCase() === lower,\n\t\t);\n\t\tif (exact) {\n\t\t\treturn { model: exact, warning: undefined, thinkingLevel: undefined, error: undefined };\n\t\t}\n\t\t// Also try parseModelPattern on the full input against all models\n\t\tconst fallback = parseModelPattern(cliModel, availableModels, {\n\t\t\tallowInvalidThinkingLevelFallback: false,\n\t\t});\n\t\tif (fallback.model) {\n\t\t\treturn {\n\t\t\t\tmodel: fallback.model,\n\t\t\t\tthinkingLevel: fallback.thinkingLevel,\n\t\t\t\twarning: fallback.warning,\n\t\t\t\terror: undefined,\n\t\t\t};\n\t\t}\n\t}\n\n\tif (provider) {\n\t\t// Parse thinking level suffix from the pattern before building the fallback model,\n\t\t// but only when --thinking is not explicitly provided.\n\t\t// e.g. \"zai-org/GLM-5.1-FP8:high\" → modelId=\"zai-org/GLM-5.1-FP8\", fallbackThinking=\"high\"\n\t\tlet fallbackPattern = pattern;\n\t\tlet fallbackThinking: ThinkingLevel | undefined;\n\t\tif (!cliThinking) {\n\t\t\tconst lastColon = pattern.lastIndexOf(\":\");\n\t\t\tif (lastColon !== -1) {\n\t\t\t\tconst suffix = pattern.substring(lastColon + 1);\n\t\t\t\tif (isValidThinkingLevel(suffix)) {\n\t\t\t\t\tfallbackPattern = pattern.substring(0, lastColon);\n\t\t\t\t\tfallbackThinking = suffix;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst fallbackModel = buildFallbackModel(provider, fallbackPattern, availableModels);\n\t\tif (fallbackModel) {\n\t\t\tconst requestedThinking = cliThinking ?? fallbackThinking;\n\t\t\tconst model =\n\t\t\t\trequestedThinking && requestedThinking !== \"off\" ? { ...fallbackModel, reasoning: true } : fallbackModel;\n\t\t\tconst fallbackWarning = warning\n\t\t\t\t? `${warning} Model \"${fallbackPattern}\" not found for provider \"${provider}\". Using custom model id.`\n\t\t\t\t: `Model \"${fallbackPattern}\" not found for provider \"${provider}\". Using custom model id.`;\n\t\t\treturn { model, thinkingLevel: fallbackThinking, warning: fallbackWarning, error: undefined };\n\t\t}\n\t}\n\n\tconst display = provider ? `${provider}/${pattern}` : cliModel;\n\treturn {\n\t\tmodel: undefined,\n\t\tthinkingLevel: undefined,\n\t\twarning,\n\t\terror: `Model \"${display}\" not found. Use --list-models to see available models.`,\n\t};\n}\n\nexport interface InitialModelResult {\n\tmodel: Model<Api> | undefined;\n\tthinkingLevel: ThinkingLevel;\n\tfallbackMessage: string | undefined;\n}\n\n/**\n * Find the initial model to use based on priority:\n * 1. CLI args (provider + model)\n * 2. First model from scoped models (if not continuing/resuming)\n * 3. Restored from session (if continuing/resuming)\n * 4. Saved default from settings\n * 5. First available model with valid API key\n */\nexport async function findInitialModel(options: {\n\tcliProvider?: string;\n\tcliModel?: string;\n\tscopedModels: ScopedModel[];\n\tisContinuing: boolean;\n\tdefaultProvider?: string;\n\tdefaultModelId?: string;\n\tdefaultThinkingLevel?: ThinkingLevel;\n\tmodelThinkingLevels?: Record<string, ThinkingLevel>;\n\tmodelRuntime: ModelRuntime;\n}): Promise<InitialModelResult> {\n\tconst {\n\t\tcliProvider,\n\t\tcliModel,\n\t\tscopedModels,\n\t\tisContinuing,\n\t\tdefaultProvider,\n\t\tdefaultModelId,\n\t\tdefaultThinkingLevel,\n\t\tmodelThinkingLevels,\n\t\tmodelRuntime,\n\t} = options;\n\n\tlet model: Model<Api> | undefined;\n\tlet thinkingLevel: ThinkingLevel = DEFAULT_THINKING_LEVEL;\n\n\t// 1. CLI args take priority\n\tif (cliProvider && cliModel) {\n\t\tconst resolved = resolveCliModel({\n\t\t\tcliProvider,\n\t\t\tcliModel,\n\t\t\tmodelRuntime,\n\t\t});\n\t\tif (resolved.error) {\n\t\t\tconsole.error(chalk.red(resolved.error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (resolved.model) {\n\t\t\treturn { model: resolved.model, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };\n\t\t}\n\t}\n\n\t// 2. Use first model from scoped models (skip if continuing/resuming)\n\tif (scopedModels.length > 0 && !isContinuing) {\n\t\tconst scopedModel = scopedModels[0];\n\t\tconst perModel = modelThinkingLevels?.[`${scopedModel.model.provider}/${scopedModel.model.id}`];\n\t\treturn {\n\t\t\tmodel: scopedModel.model,\n\t\t\tthinkingLevel: scopedModel.thinkingLevel ?? perModel ?? defaultThinkingLevel ?? DEFAULT_THINKING_LEVEL,\n\t\t\tfallbackMessage: undefined,\n\t\t};\n\t}\n\n\t// 3. Try saved default from settings if auth is configured.\n\tif (defaultProvider && defaultModelId) {\n\t\tconst found = modelRuntime.getModel(defaultProvider, defaultModelId);\n\t\tif (found && modelRuntime.hasConfiguredAuth(found.provider)) {\n\t\t\tmodel = found;\n\t\t\tconst perModel = modelThinkingLevels?.[`${defaultProvider}/${defaultModelId}`];\n\t\t\tif (perModel) {\n\t\t\t\tthinkingLevel = perModel;\n\t\t\t} else if (defaultThinkingLevel) {\n\t\t\t\tthinkingLevel = defaultThinkingLevel;\n\t\t\t}\n\t\t\treturn { model, thinkingLevel, fallbackMessage: undefined };\n\t\t}\n\t}\n\n\t// 4. Try first available model with valid API key\n\tconst availableModels = [...modelRuntime.getAvailableSnapshot()];\n\n\tif (availableModels.length > 0) {\n\t\t// Try to find a default model from known providers\n\t\tfor (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) {\n\t\t\tconst defaultId = defaultModelPerProvider[provider];\n\t\t\tconst match = availableModels.find((m) => m.provider === provider && m.id === defaultId);\n\t\t\tif (match) {\n\t\t\t\treturn { model: match, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };\n\t\t\t}\n\t\t}\n\n\t\t// If no default found, use first available\n\t\treturn { model: availableModels[0], thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };\n\t}\n\n\t// 5. No model found\n\treturn { model: undefined, thinkingLevel: DEFAULT_THINKING_LEVEL, fallbackMessage: undefined };\n}\n\n/**\n * Restore model from session, with fallback to available models\n */\nexport async function restoreModelFromSession(\n\tsavedProvider: string,\n\tsavedModelId: string,\n\tcurrentModel: Model<Api> | undefined,\n\tshouldPrintMessages: boolean,\n\tmodelRuntime: ModelRuntime,\n): Promise<{ model: Model<Api> | undefined; fallbackMessage: string | undefined }> {\n\tconst restoredModel = modelRuntime.getModel(savedProvider, savedModelId);\n\n\t// Check if restored model exists and still has auth configured\n\tconst hasConfiguredAuth = restoredModel ? modelRuntime.hasConfiguredAuth(restoredModel.provider) : false;\n\n\tif (restoredModel && hasConfiguredAuth) {\n\t\tif (shouldPrintMessages) {\n\t\t\tconsole.log(chalk.dim(`Restored model: ${savedProvider}/${savedModelId}`));\n\t\t}\n\t\treturn { model: restoredModel, fallbackMessage: undefined };\n\t}\n\n\t// Model not found or no API key - fall back\n\tconst reason = !restoredModel ? \"model no longer exists\" : \"no auth configured\";\n\n\tif (shouldPrintMessages) {\n\t\tconsole.error(chalk.yellow(`Warning: Could not restore model ${savedProvider}/${savedModelId} (${reason}).`));\n\t}\n\n\t// If we already have a model, use it as fallback\n\tif (currentModel) {\n\t\tif (shouldPrintMessages) {\n\t\t\tconsole.log(chalk.dim(`Falling back to: ${currentModel.provider}/${currentModel.id}`));\n\t\t}\n\t\treturn {\n\t\t\tmodel: currentModel,\n\t\t\tfallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${currentModel.provider}/${currentModel.id}.`,\n\t\t};\n\t}\n\n\t// Try to find any available model\n\tconst availableModels = [...modelRuntime.getAvailableSnapshot()];\n\n\tif (availableModels.length > 0) {\n\t\t// Try to find a default model from known providers\n\t\tlet fallbackModel: Model<Api> | undefined;\n\t\tfor (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) {\n\t\t\tconst defaultId = defaultModelPerProvider[provider];\n\t\t\tconst match = availableModels.find((m) => m.provider === provider && m.id === defaultId);\n\t\t\tif (match) {\n\t\t\t\tfallbackModel = match;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// If no default found, use first available\n\t\tif (!fallbackModel) {\n\t\t\tfallbackModel = availableModels[0];\n\t\t}\n\n\t\tif (shouldPrintMessages) {\n\t\t\tconsole.log(chalk.dim(`Falling back to: ${fallbackModel.provider}/${fallbackModel.id}`));\n\t\t}\n\n\t\treturn {\n\t\t\tmodel: fallbackModel,\n\t\t\tfallbackMessage: `Could not restore model ${savedProvider}/${savedModelId} (${reason}). Using ${fallbackModel.provider}/${fallbackModel.id}.`,\n\t\t};\n\t}\n\n\t// No models available\n\treturn { model: undefined, fallbackMessage: undefined };\n}\n"]}
|
|
@@ -44,6 +44,11 @@ export interface PiServerCompactOptions extends SimpleStreamOptions {
|
|
|
44
44
|
customInstructions?: string;
|
|
45
45
|
settings?: unknown;
|
|
46
46
|
preparation?: CompactionPreparationOptions;
|
|
47
|
+
/** Fixed-size digest of the exact provider message overlay for this session. */
|
|
48
|
+
cacheContextHash?: string;
|
|
49
|
+
/** Fixed-size digest of the synchronized static prompt and tools. */
|
|
50
|
+
cacheStaticContextHash?: string;
|
|
51
|
+
cacheFallbackReason?: string;
|
|
47
52
|
sessionTree?: PiServerTreeSnapshot;
|
|
48
53
|
onHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;
|
|
49
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi-server-client.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport type {\n\tCompactionPreparationOptions,\n\tLegacyCompactResult,\n\tProxyAssistantMessageEvent,\n\tSessionTreeEntry,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Message,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport {\n\tbuildPiServerTreePrefixHashes,\n\thashPiServerSessionEntries,\n\thashPiServerStaticContext,\n} from \"./pi-server-protocol.ts\";\nimport { ChunkRequest } from \"./pi-server-request.ts\";\n\nclass PiServerEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\nfunction getServerUrl(): string {\n\treturn process.env.PI_SERVER_URL ?? \"http://127.0.0.1:4217\";\n}\n\nfunction getAuthToken(): string {\n\treturn process.env.PI_SERVER_AUTH_TOKEN ?? \"\";\n}\n\nfunction createPiServerRequest(signal?: AbortSignal): ChunkRequest {\n\treturn new ChunkRequest({\n\t\tserverUrl: getServerUrl(),\n\t\tauthToken: getAuthToken(),\n\t\tsignal,\n\t});\n}\n\nconst sessionStaticContextHashes = new Map<string, string>();\nconst sessionTreeHashes = new Map<string, string>();\nconst sessionTreeEntryCounts = new Map<string, number>();\nconst sessionTreeLeafIds = new Map<string, string | null>();\nconst sessionHasTemporaryTree = new Set<string>();\ninterface ActivePiServerOperation {\n\townerSessionId: string;\n\tsessionId: string;\n\trunId: string;\n\tsignal?: AbortSignal;\n\tcancelListener?: () => void;\n\tcancelPromise?: Promise<void>;\n\tcancelFailed?: boolean;\n\tcancelConfirmed?: boolean;\n}\n\nconst activePiServerOperations = new Map<string, Map<string, ActivePiServerOperation>>();\nconst RESPONSE_BODY_EXCERPT_CHARS = 500;\nconst TRANSIENT_PI_SERVER_RETRY_DELAYS_MS = [250, 750, 1500];\nconst TRANSIENT_PI_SERVER_STATUS_CODES = new Set([500, 502, 503, 504, 524, 530]);\nconst PI_SERVER_RUN_RECOVERY_POLL_MS = 250;\nconst DEFAULT_PI_SERVER_RUN_RECOVERY_TIMEOUT_MS = 10 * 60 * 1000;\n\nclass PiServerRunRecoveryTimeoutError extends Error {\n\tconstructor() {\n\t\tsuper(\"pi-server run recovery timed out; the server may still be processing the request\");\n\t\tthis.name = \"PiServerRunRecoveryTimeoutError\";\n\t}\n}\n\nexport function hashStaticContext(ctx: Context): string {\n\treturn hashPiServerStaticContext(ctx);\n}\n\nfunction hashEntries(entries: SessionTreeEntry[]): string {\n\treturn hashPiServerSessionEntries(entries);\n}\n\nfunction getLinearTreeFromMessages(messages: Message[]): { entries: SessionTreeEntry[]; leafId: string | null } {\n\tlet parentId: string | null = null;\n\tconst entries = messages.map((message, index): SessionTreeEntry => {\n\t\tconst id = `message-${index}`;\n\t\tconst entry: SessionTreeEntry = {\n\t\t\ttype: \"message\",\n\t\t\tid,\n\t\t\tparentId,\n\t\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\t\tmessage,\n\t\t};\n\t\tparentId = id;\n\t\treturn entry;\n\t});\n\treturn { entries, leafId: parentId };\n}\n\nexport function resetSessionTracking(sessionId: string): void {\n\tsessionStaticContextHashes.delete(sessionId);\n\tsessionTreeHashes.delete(sessionId);\n\tsessionTreeEntryCounts.delete(sessionId);\n\tsessionTreeLeafIds.delete(sessionId);\n\tsessionHasTemporaryTree.delete(sessionId);\n}\n\nexport function resetAllSessionTracking(): void {\n\tsessionStaticContextHashes.clear();\n\tsessionTreeHashes.clear();\n\tsessionTreeEntryCounts.clear();\n\tsessionTreeLeafIds.clear();\n\tsessionHasTemporaryTree.clear();\n\tactivePiServerOperations.clear();\n}\n\nfunction registerPiServerOperation(\n\townerSessionId: string | undefined,\n\tsessionId: string,\n\trunId: string,\n\tsignal?: AbortSignal,\n): ActivePiServerOperation | undefined {\n\tif (!ownerSessionId) return undefined;\n\tlet operations = activePiServerOperations.get(ownerSessionId);\n\tif (!operations) {\n\t\toperations = new Map();\n\t\tactivePiServerOperations.set(ownerSessionId, operations);\n\t}\n\tconst operation: ActivePiServerOperation = { ownerSessionId, sessionId, runId, signal };\n\toperations.set(runId, operation);\n\tif (signal) {\n\t\tconst onAbort = () => {\n\t\t\tvoid cancelPiServerOperation(operation).catch(() => {\n\t\t\t\t// The owner session retries cancellation and reports the failure.\n\t\t\t});\n\t\t};\n\t\toperation.cancelListener = onAbort;\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tif (signal.aborted) onAbort();\n\t}\n\treturn operation;\n}\n\nfunction removePiServerOperation(operation: ActivePiServerOperation): void {\n\tconst operations = activePiServerOperations.get(operation.ownerSessionId);\n\toperation.cancelConfirmed = true;\n\tif (operation.signal && operation.cancelListener) {\n\t\toperation.signal.removeEventListener(\"abort\", operation.cancelListener);\n\t}\n\toperation.cancelListener = undefined;\n\tif (!operations || operations.get(operation.runId) !== operation) return;\n\toperations.delete(operation.runId);\n\tif (operations.size === 0) activePiServerOperations.delete(operation.ownerSessionId);\n}\n\nasync function finishPiServerOperation(\n\toperation: ActivePiServerOperation | undefined,\n\taborted: boolean,\n): Promise<void> {\n\tif (!operation) return;\n\tif (aborted) {\n\t\tif (operation.cancelPromise) await operation.cancelPromise;\n\t} else {\n\t\tremovePiServerOperation(operation);\n\t}\n}\n\nasync function cancelPiServerOperation(operation: ActivePiServerOperation, retryFailed = false): Promise<void> {\n\tif (operation.cancelConfirmed) return;\n\tif (retryFailed && operation.cancelFailed) {\n\t\toperation.cancelFailed = false;\n\t\toperation.cancelPromise = undefined;\n\t}\n\tif (operation.cancelPromise) return operation.cancelPromise;\n\n\tconst cancellation = (async () => {\n\t\tconst request = createPiServerRequest();\n\t\tconst response = await request.postJson(\n\t\t\t`/api/session/${encodeURIComponent(operation.sessionId)}/runs/${encodeURIComponent(operation.runId)}/abort`,\n\t\t\t{ sessionId: operation.sessionId, runId: operation.runId },\n\t\t);\n\t\tconst result = await readPiServerJson<PiServerAbortResponse>(response, \"Session run cancellation failed\");\n\t\tif (result.sessionId !== operation.sessionId || result.runId !== operation.runId) {\n\t\t\tthrow new Error(\"Session run cancellation failed (response did not echo the cancelled run)\");\n\t\t}\n\t\tif (result.status !== \"aborted\" && result.status !== \"completed\" && result.status !== \"failed\") {\n\t\t\tthrow new Error(\"Session run cancellation failed (response was not terminal)\");\n\t\t}\n\t\tremovePiServerOperation(operation);\n\t})();\n\n\toperation.cancelPromise = cancellation;\n\ttry {\n\t\tawait cancellation;\n\t} catch (error) {\n\t\toperation.cancelFailed = true;\n\t\tthrow error;\n\t}\n}\n\n/** Cancel all provider operations owned by a coding-agent session and wait for terminal acknowledgements. */\nexport async function cancelPiServerOperations(ownerSessionId: string): Promise<void> {\n\tconst operations = [...(activePiServerOperations.get(ownerSessionId)?.values() ?? [])];\n\tconst results = await Promise.allSettled(operations.map((operation) => cancelPiServerOperation(operation, true)));\n\tconst failure = results.find((result): result is PromiseRejectedResult => result.status === \"rejected\");\n\tif (failure) throw failure.reason;\n}\n\ninterface SessionInitResponse {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash?: string;\n\tmessageCount: number;\n\tentryCount?: number;\n\tleafId?: string | null;\n\trevision?: number;\n\tfromCache?: boolean;\n}\n\nexport interface PiServerTreeSnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\treplace?: boolean;\n}\n\nexport interface PiServerCompactionResult {\n\tcompaction: Omit<LegacyCompactResult, \"retainedTail\">;\n\tcompactionEntry: SessionTreeEntry;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerHistorySnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerStreamOptions extends SimpleStreamOptions {\n\townerSessionId?: string;\n\tsessionTree?: PiServerTreeSnapshot;\n\tephemeralMessages?: Message[];\n\tcontextOverlay?: Message[];\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\ninterface PiServerErrorResponse {\n\terror: string;\n\tcode?: string;\n\tdetails?: Record<string, unknown>;\n}\n\ninterface PiServerResponseFailure {\n\tdetails: string;\n\tcode?: string;\n}\n\nclass PiServerResponseError extends Error {\n\tpublic readonly code?: string;\n\tpublic readonly details?: Record<string, unknown>;\n\n\tconstructor(message: string, code?: string, details?: Record<string, unknown>) {\n\t\tsuper(message);\n\t\tthis.name = \"PiServerResponseError\";\n\t\tthis.code = code;\n\t\tthis.details = details;\n\t}\n}\n\ninterface PiServerHistoryResponse extends Partial<PiServerHistorySnapshot> {\n\tsessionId: string;\n\ttreeHash?: string;\n\tentryCount?: number;\n\tleafId?: string | null;\n\ttreePatch?: PiServerTreePatch;\n}\n\ninterface PiServerTreePatch {\n\tentriesFrom: number;\n\tbaseRevision?: number;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n}\n\ninterface PiServerCompactionResponse extends Partial<PiServerCompactionResult> {\n\ttreePatch?: PiServerTreePatch & { baseTreeHash?: string };\n}\n\ninterface ServerSentEvent {\n\tevent: string;\n\tdata: string;\n}\n\nclass ServerSentEventParser {\n\tprivate buffer = \"\";\n\tprivate event = \"message\";\n\tprivate dataLines: string[] = [];\n\n\tfeed(text: string): ServerSentEvent[] {\n\t\tthis.buffer += text;\n\t\tconst lines = this.buffer.split(\"\\n\");\n\t\tthis.buffer = lines.pop() ?? \"\";\n\t\treturn lines.flatMap((line) => this.consumeLine(line));\n\t}\n\n\tfinish(): ServerSentEvent[] {\n\t\tconst events: ServerSentEvent[] = [];\n\t\tif (this.buffer.length > 0) {\n\t\t\tevents.push(...this.consumeLine(this.buffer));\n\t\t\tthis.buffer = \"\";\n\t\t}\n\t\tconst final = this.flush();\n\t\tif (final) events.push(final);\n\t\treturn events;\n\t}\n\n\tprivate consumeLine(rawLine: string): ServerSentEvent[] {\n\t\tconst line = rawLine.endsWith(\"\\r\") ? rawLine.slice(0, -1) : rawLine;\n\t\tif (line === \"\") {\n\t\t\tconst event = this.flush();\n\t\t\treturn event ? [event] : [];\n\t\t}\n\t\tif (line.startsWith(\":\")) return [];\n\t\tif (line.startsWith(\"event:\")) {\n\t\t\tthis.event = getServerSentEventFieldValue(line, \"event\");\n\t\t\treturn [];\n\t\t}\n\t\tif (line.startsWith(\"data:\")) {\n\t\t\tthis.dataLines.push(getServerSentEventFieldValue(line, \"data\"));\n\t\t}\n\t\treturn [];\n\t}\n\n\tprivate flush(): ServerSentEvent | undefined {\n\t\tif (this.dataLines.length === 0) return undefined;\n\t\tconst event = { event: this.event, data: this.dataLines.join(\"\\n\") };\n\t\tthis.event = \"message\";\n\t\tthis.dataLines = [];\n\t\treturn event;\n\t}\n}\n\ninterface PiServerRunResponse {\n\trunId: string;\n\tstatus: \"running\" | \"completed\" | \"failed\" | \"aborted\";\n\tmessage?: AssistantMessage;\n\tcompactResult?: PiServerCompactionResponse;\n\terrorMessage?: string;\n}\n\ninterface PiServerAbortResponse {\n\tsessionId: string;\n\trunId: string;\n\tstatus: \"aborted\" | \"completed\" | \"failed\";\n}\n\ninterface PiServerSyncOptions {\n\tsignal?: AbortSignal;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\ntype PiServerFailurePhase = \"session_init\" | \"tree_sync\" | \"provider_stream\" | \"history_reconcile\";\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getResponseStatus(response: Response): string {\n\tconst status = response.statusText ? `${response.status} ${response.statusText}` : String(response.status);\n\treturn response.status >= 500 ? `${status} (server error)` : status;\n}\n\nfunction getResponseContentType(response: Response): string {\n\treturn response.headers.get(\"content-type\") ?? \"unknown\";\n}\n\nfunction getBodyExcerpt(text: string): string {\n\tconst trimmed = text.trim();\n\tif (!trimmed) return \"<empty>\";\n\tconst excerpt = trimmed.slice(0, RESPONSE_BODY_EXCERPT_CHARS);\n\treturn excerpt.length < trimmed.length ? `${excerpt}...` : excerpt;\n}\n\nfunction getJsonErrorResponse(text: string): PiServerErrorResponse | undefined {\n\tif (!text) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\tif (!isObject(parsed) || typeof parsed.error !== \"string\" || parsed.error.length === 0) return undefined;\n\t\tconst code = parsed.code;\n\t\treturn {\n\t\t\terror: parsed.error,\n\t\t\tcode: typeof code === \"string\" ? code : undefined,\n\t\t\tdetails: isObject(parsed.details) ? parsed.details : undefined,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction getJsonErrorText(text: string): string | undefined {\n\treturn getJsonErrorResponse(text)?.error;\n}\n\nfunction formatResponseDetails(response: Response, bodyText: string): string {\n\tconst body = getJsonErrorText(bodyText) ?? getBodyExcerpt(bodyText);\n\treturn `${getResponseStatus(response)}; content-type: ${getResponseContentType(response)}; body excerpt: ${body}`;\n}\n\nasync function readPiServerFailure(response: Response): Promise<PiServerResponseFailure> {\n\tconst bodyText = await response.text();\n\tconst errorResponse = getJsonErrorResponse(bodyText);\n\treturn {\n\t\tdetails: formatResponseDetails(response, bodyText),\n\t\tcode: errorResponse?.code,\n\t};\n}\n\nasync function readPiServerJson<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst bodyText = await response.text();\n\tif (!response.ok) {\n\t\tconst errorResponse = getJsonErrorResponse(bodyText);\n\t\tif (errorResponse) {\n\t\t\tthrow new PiServerResponseError(\n\t\t\t\t`${errorPrefix} (${formatResponseDetails(response, bodyText)})`,\n\t\t\t\terrorResponse.code,\n\t\t\t\terrorResponse.details,\n\t\t\t);\n\t\t}\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)})`);\n\t}\n\ttry {\n\t\treturn JSON.parse(bodyText) as T;\n\t} catch {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected JSON)`);\n\t}\n}\n\nfunction getServerSentEventFieldValue(line: string, field: string): string {\n\tconst prefix = `${field}:`;\n\tconst value = line.slice(prefix.length);\n\treturn value.startsWith(\" \") ? value.slice(1) : value;\n}\n\nfunction parseServerSentEvents(bodyText: string): ServerSentEvent[] {\n\tconst parser = new ServerSentEventParser();\n\treturn [...parser.feed(bodyText), ...parser.finish()];\n}\n\nfunction parseServerSentEventData(event: ServerSentEvent, errorPrefix: string): unknown {\n\ttry {\n\t\treturn JSON.parse(event.data) as unknown;\n\t} catch {\n\t\tthrow new Error(`${errorPrefix} (invalid event-stream JSON data: ${getBodyExcerpt(event.data)})`);\n\t}\n}\n\nfunction formatServerSentEventError(response: Response, payload: unknown): string {\n\tconst message = isObject(payload) && typeof payload.error === \"string\" ? payload.error : JSON.stringify(payload);\n\treturn `${getResponseStatus(response)}; content-type: ${getResponseContentType(response)}; body excerpt: ${getBodyExcerpt(message)}`;\n}\n\nasync function readPiServerEventStreamJson<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst bodyText = await response.text();\n\tfor (const event of parseServerSentEvents(bodyText)) {\n\t\tif (event.event === \"error\") {\n\t\t\tthrow new Error(\n\t\t\t\t`${errorPrefix} (${formatServerSentEventError(response, parseServerSentEventData(event, errorPrefix))})`,\n\t\t\t);\n\t\t}\n\t\tif (event.event === \"result\") {\n\t\t\treturn parseServerSentEventData(event, errorPrefix) as T;\n\t\t}\n\t}\n\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected result event)`);\n}\n\nasync function readPiServerCompactResponse<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst contentType = getResponseContentType(response).toLowerCase().split(\";\")[0]?.trim();\n\tif (contentType === \"text/event-stream\") {\n\t\treturn readPiServerEventStreamJson<T>(response, errorPrefix);\n\t}\n\treturn readPiServerJson<T>(response, errorPrefix);\n}\n\nfunction isAbortError(error: unknown, signal: AbortSignal | undefined): boolean {\n\treturn signal?.aborted === true || (error instanceof Error && error.name === \"AbortError\");\n}\n\nfunction isTransientPiServerFetchError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn /fetch failed|network|socket hang up|terminated|ECONNRESET|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|CONNECT timeout/i.test(\n\t\tmessage,\n\t);\n}\n\nfunction isTransientPiServerError(error: unknown): boolean {\n\tif (isTransientPiServerFetchError(error)) return true;\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn (\n\t\t/\\b(?:500|502|503|504|524|530)\\b/.test(message) ||\n\t\t/CONNECT timeout|Bad Gateway|upstream|A timeout occurred/i.test(message)\n\t);\n}\n\nfunction isRetryablePiServerFailure(phase: PiServerFailurePhase, error: unknown): boolean {\n\tif (phase === \"provider_stream\") return true;\n\tif (phase === \"history_reconcile\") return false;\n\treturn isTransientPiServerError(error);\n}\n\nasync function waitForPiServerRetry(attempt: number, signal: AbortSignal | undefined): Promise<void> {\n\tawait sleep(TRANSIENT_PI_SERVER_RETRY_DELAYS_MS[attempt], undefined, { signal });\n}\n\nasync function postPiServerJsonWithTransientRetry<T>(\n\trequest: ChunkRequest,\n\tendpoint: string,\n\tbody: unknown,\n\terrorPrefix: string,\n): Promise<T> {\n\tfor (let attempt = 0; ; attempt++) {\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await request.postJson(endpoint, body);\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\tattempt < TRANSIENT_PI_SERVER_RETRY_DELAYS_MS.length &&\n\t\t\t\t!isAbortError(error, request.options.signal) &&\n\t\t\t\tisTransientPiServerFetchError(error)\n\t\t\t) {\n\t\t\t\tawait waitForPiServerRetry(attempt, request.options.signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst bodyText = await response.text();\n\t\tif (!response.ok) {\n\t\t\tif (\n\t\t\t\tattempt < TRANSIENT_PI_SERVER_RETRY_DELAYS_MS.length &&\n\t\t\t\t!request.options.signal?.aborted &&\n\t\t\t\tTRANSIENT_PI_SERVER_STATUS_CODES.has(response.status)\n\t\t\t) {\n\t\t\t\tawait waitForPiServerRetry(attempt, request.options.signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst errorResponse = getJsonErrorResponse(bodyText);\n\t\t\tif (errorResponse) {\n\t\t\t\tthrow new PiServerResponseError(\n\t\t\t\t\t`${errorPrefix} (${formatResponseDetails(response, bodyText)})`,\n\t\t\t\t\terrorResponse.code,\n\t\t\t\t\terrorResponse.details,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)})`);\n\t\t}\n\n\t\ttry {\n\t\t\treturn JSON.parse(bodyText) as T;\n\t\t} catch {\n\t\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected JSON)`);\n\t\t}\n\t}\n}\n\nasync function ensurePiServerEventStream(response: Response): Promise<void> {\n\tconst contentType = getResponseContentType(response);\n\tif (contentType.toLowerCase().split(\";\")[0]?.trim() === \"text/event-stream\") {\n\t\treturn;\n\t}\n\tconst bodyText = await response.text();\n\tthrow new Error(`pi-server error: ${formatResponseDetails(response, bodyText)}; expected text/event-stream`);\n}\n\nasync function ensureSessionInit(\n\tsessionId: string,\n\tcontext: Context,\n\trequest: ChunkRequest,\n): Promise<SessionInitResponse> {\n\tconst currentHash = hashStaticContext(context);\n\tconst previousHash = sessionStaticContextHashes.get(sessionId);\n\n\tif (previousHash === currentHash) {\n\t\treturn {\n\t\t\tsessionId,\n\t\t\tstaticContextHash: previousHash,\n\t\t\tmessageCount: 0,\n\t\t\tfromCache: true,\n\t\t};\n\t}\n\n\tconst staticContext = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n\n\tconst endpoint = previousHash === undefined ? \"/api/session/init\" : \"/api/session/update\";\n\n\tconst result = await postPiServerJsonWithTransientRetry<SessionInitResponse>(\n\t\trequest,\n\t\tendpoint,\n\t\t{ sessionId, staticContext },\n\t\t\"Session init failed\",\n\t);\n\tsessionStaticContextHashes.set(sessionId, currentHash);\n\tif (result.treeHash !== undefined) {\n\t\tsessionTreeHashes.set(sessionId, result.treeHash);\n\t\tsessionTreeLeafIds.set(sessionId, result.leafId ?? null);\n\t\tif (result.entryCount !== undefined) {\n\t\t\tsessionTreeEntryCounts.set(sessionId, result.entryCount);\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction getStaticContext(context: Context) {\n\treturn {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n}\n\nfunction markTreeSynced(sessionId: string, tree: PiServerTreeSnapshot): void {\n\tconst prefixHashes = buildPiServerTreePrefixHashes(tree.entries);\n\tsessionTreeHashes.set(sessionId, prefixHashes[prefixHashes.length - 1]);\n\tsessionTreeEntryCounts.set(sessionId, tree.entries.length);\n\tsessionTreeLeafIds.set(sessionId, tree.leafId);\n\tif (tree.replace) {\n\t\tsessionHasTemporaryTree.add(sessionId);\n\t} else {\n\t\tsessionHasTemporaryTree.delete(sessionId);\n\t}\n}\n\nasync function postTreeJson(\n\trequest: ChunkRequest,\n\tendpoint: string,\n\tbody: unknown,\n\terrorPrefix: string,\n): Promise<void> {\n\tawait postPiServerJsonWithTransientRetry<unknown>(request, endpoint, body, errorPrefix);\n}\n\nasync function fetchPiServerHistory(\n\tsessionId: string,\n\trequest: ChunkRequest,\n\tlocalTree?: PiServerTreeSnapshot,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tconst prefixHashes = localTree ? buildPiServerTreePrefixHashes(localTree.entries) : undefined;\n\tconst historyUrl =\n\t\tlocalTree && prefixHashes\n\t\t\t? `/api/session/${encodeURIComponent(sessionId)}/history?entriesFrom=${localTree.entries.length}&baseTreeHash=${encodeURIComponent(prefixHashes[prefixHashes.length - 1])}`\n\t\t\t: `/api/session/${encodeURIComponent(sessionId)}/history`;\n\tconst response = await request.getJson(historyUrl);\n\tif (response.status === 404) {\n\t\treturn undefined;\n\t}\n\tconst history = await readPiServerJson<PiServerHistoryResponse>(response, \"Session history reconciliation failed\");\n\tif (history.treePatch && localTree) {\n\t\treturn {\n\t\t\tentries: [...localTree.entries.slice(0, history.treePatch.entriesFrom), ...history.treePatch.entries],\n\t\t\tleafId: history.treePatch.leafId,\n\t\t\tmessages: history.messages ?? [],\n\t\t};\n\t}\n\tif (!history.entries || history.leafId === undefined || !history.messages) {\n\t\tthrow new Error(\"Session history reconciliation failed (response did not include the session tree)\");\n\t}\n\treturn {\n\t\tentries: history.entries,\n\t\tleafId: history.leafId,\n\t\tmessages: history.messages,\n\t};\n}\n\nasync function applyPiServerHistory(\n\tsessionId: string,\n\tsnapshot: PiServerHistorySnapshot,\n\tonHistoryReconciled: ((snapshot: PiServerHistorySnapshot) => void | Promise<void>) | undefined,\n): Promise<void> {\n\tmarkTreeSynced(sessionId, snapshot);\n\tawait onHistoryReconciled?.(snapshot);\n}\n\nasync function fetchPiServerRun(\n\tsessionId: string,\n\trunId: string,\n\trequest: ChunkRequest,\n): Promise<PiServerRunResponse | undefined> {\n\tconst response = await request.getJson(\n\t\t`/api/session/${encodeURIComponent(sessionId)}/runs/${encodeURIComponent(runId)}`,\n\t);\n\tif (response.status === 404) {\n\t\treturn undefined;\n\t}\n\treturn readPiServerJson<PiServerRunResponse>(response, \"Session run recovery failed\");\n}\n\nasync function waitForPiServerRunCompletion(\n\tsessionId: string,\n\trunId: string,\n\trequest: ChunkRequest,\n\toptions: PiServerStreamOptions | undefined,\n): Promise<PiServerRunResponse | undefined> {\n\tconst deadline = Date.now() + DEFAULT_PI_SERVER_RUN_RECOVERY_TIMEOUT_MS;\n\tfor (;;) {\n\t\tconst run = await fetchPiServerRun(sessionId, runId, request);\n\t\tif (!run || run.status !== \"running\") return run;\n\t\tif (Date.now() >= deadline) throw new PiServerRunRecoveryTimeoutError();\n\t\tawait sleep(PI_SERVER_RUN_RECOVERY_POLL_MS, undefined, { signal: options?.signal });\n\t}\n}\n\nfunction getKnownServerPrefixIds(sessionId: string, entries: SessionTreeEntry[]): Set<string> | undefined {\n\tconst entryCount = sessionTreeEntryCounts.get(sessionId);\n\tconst treeHash = sessionTreeHashes.get(sessionId);\n\tif (entryCount === undefined || treeHash === undefined || entryCount > entries.length) {\n\t\treturn undefined;\n\t}\n\tconst prefixHashes = buildPiServerTreePrefixHashes(entries);\n\tif (prefixHashes[entryCount] !== treeHash) {\n\t\treturn undefined;\n\t}\n\treturn new Set(entries.slice(0, entryCount).map((entry) => entry.id));\n}\n\nfunction isRecoverableTreeDivergenceCode(code: string | undefined): boolean {\n\treturn code === \"PARENT_ENTRY_NOT_FOUND\" || code === \"LEAF_ID_NOT_FOUND\" || code === \"ENTRY_ALREADY_EXISTS\";\n}\n\nfunction isRecoverableMissingServerStateCode(code: string | undefined): boolean {\n\treturn code === \"SESSION_NOT_FOUND\" || code === \"SESSION_NO_STATIC_CONTEXT\" || isRecoverableTreeDivergenceCode(code);\n}\n\nfunction isRecoverableTreeDivergenceError(error: unknown): boolean {\n\tif (error instanceof PiServerResponseError) {\n\t\treturn isRecoverableTreeDivergenceCode(error.code);\n\t}\n\tif (error && typeof error === \"object\" && \"code\" in error) {\n\t\treturn isRecoverableTreeDivergenceCode((error as { code?: string }).code);\n\t}\n\treturn false;\n}\n\nfunction isRecoverableMissingServerState(response: Response, errorCode?: string): boolean {\n\tif (response.status !== 400 && response.status !== 404) return false;\n\treturn isRecoverableMissingServerStateCode(errorCode);\n}\n\nasync function syncFullPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n): Promise<void> {\n\tawait postTreeJson(\n\t\trequest,\n\t\t\"/api/session/tree/sync\",\n\t\t{\n\t\t\tsessionId,\n\t\t\tentries: tree.entries,\n\t\t\tleafId: tree.leafId,\n\t\t\tstaticContext: getStaticContext(context),\n\t\t},\n\t\t\"Session tree sync failed\",\n\t);\n\tmarkTreeSynced(sessionId, tree);\n}\n\nfunction shouldUseServerHistory(sessionId: string, tree: PiServerTreeSnapshot): boolean {\n\treturn !tree.replace && !sessionHasTemporaryTree.has(sessionId) && (sessionTreeEntryCounts.get(sessionId) ?? 0) > 0;\n}\n\nasync function recoverPiServerTreeDivergence(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tif (shouldUseServerHistory(sessionId, tree)) {\n\t\tconst snapshot = await fetchPiServerHistory(sessionId, request, tree);\n\t\tif (snapshot && snapshot.entries.length > 0) {\n\t\t\tawait applyPiServerHistory(sessionId, snapshot, onHistoryReconciled);\n\t\t\treturn snapshot;\n\t\t}\n\t}\n\n\tawait syncFullPiServerTree(sessionId, context, tree, request);\n}\n\nexport async function syncPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\toptions?: PiServerSyncOptions,\n): Promise<void> {\n\tconst request = createPiServerRequest(options?.signal);\n\tawait ensureSessionInit(sessionId, context, request);\n\tconst reconciled = await syncPiServerTreeWithRequest(\n\t\tsessionId,\n\t\tcontext,\n\t\ttree,\n\t\trequest,\n\t\toptions?.onHistoryReconciled,\n\t);\n\tif (reconciled) {\n\t\tthrow new Error(\"pi-server history differed from local history; local session was reconciled to server history\");\n\t}\n}\n\nasync function syncPiServerTreeWithRequest(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tconst syncTree = tree;\n\tconst currentHash = hashEntries(syncTree.entries);\n\tconst previousHash = sessionTreeHashes.get(sessionId);\n\tconst previousLeafId = sessionTreeLeafIds.get(sessionId);\n\n\tif (!tree.replace && previousHash === currentHash) {\n\t\tif (previousLeafId !== syncTree.leafId) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/switch\",\n\t\t\t\t\t{ sessionId, leafId: syncTree.leafId },\n\t\t\t\t\t\"Session tree switch failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\treturn recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t}\n\t\t\tsessionTreeLeafIds.set(sessionId, syncTree.leafId);\n\t\t}\n\t\tmarkTreeSynced(sessionId, syncTree);\n\t\treturn;\n\t}\n\n\tconst syncedIds =\n\t\t!tree.replace && !sessionHasTemporaryTree.has(sessionId)\n\t\t\t? getKnownServerPrefixIds(sessionId, syncTree.entries)\n\t\t\t: undefined;\n\tif (syncedIds) {\n\t\tconst deltaEntries = syncTree.entries.filter((entry) => !syncedIds.has(entry.id));\n\t\tif (deltaEntries.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/append\",\n\t\t\t\t\t{\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tentries: deltaEntries,\n\t\t\t\t\t\tleafId: syncTree.leafId,\n\t\t\t\t\t\tstaticContext: getStaticContext(context),\n\t\t\t\t\t},\n\t\t\t\t\t\"Session tree append failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\treturn recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t}\n\t\t\tmarkTreeSynced(sessionId, syncTree);\n\t\t\treturn;\n\t\t}\n\t}\n\n\treturn recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n}\n\nfunction serializeOptions(options: SimpleStreamOptions | undefined): SimpleStreamOptions {\n\treturn {\n\t\ttemperature: options?.temperature,\n\t\tmaxTokens: options?.maxTokens,\n\t\treasoning: options?.reasoning,\n\t\tcacheRetention: options?.cacheRetention,\n\t\tsessionId: options?.sessionId,\n\t\tapiKey: options?.apiKey,\n\t\theaders: options?.headers,\n\t\tmetadata: options?.metadata,\n\t\ttransport: options?.transport,\n\t\tthinkingBudgets: options?.thinkingBudgets,\n\t\ttimeoutMs: options?.timeoutMs,\n\t\twebsocketConnectTimeoutMs: options?.websocketConnectTimeoutMs,\n\t\tmaxRetries: options?.maxRetries,\n\t\tmaxRetryDelayMs: options?.maxRetryDelayMs,\n\t};\n}\n\nexport interface PiServerCompactOptions extends SimpleStreamOptions {\n\townerSessionId?: string;\n\tcustomInstructions?: string;\n\tsettings?: unknown;\n\tpreparation?: CompactionPreparationOptions;\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nexport async function compactPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerCompactOptions,\n): Promise<PiServerCompactionResult> {\n\tconst sessionId = options?.sessionId ?? \"default\";\n\tconst runId = randomUUID();\n\tconst operation = registerPiServerOperation(options?.ownerSessionId ?? sessionId, sessionId, runId, options?.signal);\n\tconst request = createPiServerRequest(options?.signal);\n\n\ttry {\n\t\tawait ensureSessionInit(sessionId, context, request);\n\t\tlet tree = options?.sessionTree ?? getLinearTreeFromMessages(context.messages as Message[]);\n\t\tlet preparation = options?.preparation;\n\t\tconst syncTree = async () => {\n\t\t\tconst reconciled = await syncPiServerTreeWithRequest(\n\t\t\t\tsessionId,\n\t\t\t\tcontext,\n\t\t\t\ttree,\n\t\t\t\trequest,\n\t\t\t\toptions?.onHistoryReconciled,\n\t\t\t);\n\t\t\tif (reconciled) {\n\t\t\t\ttree = reconciled;\n\t\t\t\t// A forced cut point belongs to the old branch. Recompute it on the authoritative tree.\n\t\t\t\tpreparation = undefined;\n\t\t\t}\n\t\t};\n\t\tawait syncTree();\n\n\t\tconst makeBody = () => ({\n\t\t\tsessionId,\n\t\t\trunId,\n\t\t\tmodel,\n\t\t\toptions: serializeOptions(options),\n\t\t\tsettings: options?.settings,\n\t\t\tpreparation,\n\t\t\tcustomInstructions: options?.customInstructions,\n\t\t\tbaseTreeHash: sessionTreeHashes.get(sessionId) ?? hashEntries(tree.entries),\n\t\t\tstreamResponse: true,\n\t\t});\n\t\tlet response = await request.postJson(\"/api/session/compact\", makeBody());\n\t\tif (!response.ok) {\n\t\t\tlet failure = await readPiServerFailure(response);\n\t\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.code)) {\n\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\t\tawait syncTree();\n\t\t\t\tresponse = await request.postJson(\"/api/session/compact\", makeBody());\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tfailure = { details: \"\", code: undefined };\n\t\t\t\t} else {\n\t\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`Server compaction failed (${failure.details})`);\n\t\t\t}\n\t\t}\n\t\tlet result: PiServerCompactionResponse;\n\t\ttry {\n\t\t\tresult = await readPiServerCompactResponse<PiServerCompactionResponse>(response, \"Server compaction failed\");\n\t\t} catch (error) {\n\t\t\tif (options?.signal?.aborted || !getResponseContentType(response).startsWith(\"text/event-stream\")) throw error;\n\t\t\tconst run = await waitForPiServerRunCompletion(sessionId, runId, request, options);\n\t\t\tif (run?.status !== \"completed\" || !run.compactResult) {\n\t\t\t\tif (run?.errorMessage) throw new Error(`Server compaction failed: ${run.errorMessage}`);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tresult = run.compactResult;\n\t\t}\n\t\tif (!result.compaction) {\n\t\t\tthrow new Error(\"Server compaction response did not include a compaction result\");\n\t\t}\n\t\tconst resultEntries =\n\t\t\tresult.entries ??\n\t\t\t(result.treePatch\n\t\t\t\t? [...tree.entries.slice(0, result.treePatch.entriesFrom), ...result.treePatch.entries]\n\t\t\t\t: undefined);\n\t\tconst resultLeafId = result.leafId ?? result.treePatch?.leafId;\n\t\tif (!result.compactionEntry || !resultEntries || resultLeafId === undefined) {\n\t\t\tthrow new Error(\"Server compaction response did not include the updated session tree\");\n\t\t}\n\t\tmarkTreeSynced(sessionId, { entries: resultEntries, leafId: resultLeafId });\n\t\treturn {\n\t\t\tcompaction: result.compaction,\n\t\t\tcompactionEntry: result.compactionEntry,\n\t\t\tentries: resultEntries,\n\t\t\tleafId: resultLeafId,\n\t\t\tmessages: result.messages ?? [],\n\t\t};\n\t} finally {\n\t\tawait finishPiServerOperation(operation, options?.signal?.aborted === true);\n\t}\n}\n\nexport async function streamPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerStreamOptions,\n): Promise<PiServerEventStream> {\n\tconst sessionId = options?.sessionId ?? randomUUID();\n\tconst runId = randomUUID();\n\tconst isEphemeralSession = options?.sessionId === undefined;\n\tconst operation = registerPiServerOperation(options?.ownerSessionId ?? sessionId, sessionId, runId, options?.signal);\n\tconst stream = new PiServerEventStream();\n\n\tconst partial: AssistantMessage = {\n\t\trole: \"assistant\",\n\t\tstopReason: \"stop\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\ttimestamp: Date.now(),\n\t};\n\n\t(async () => {\n\t\tlet phase: PiServerFailurePhase = \"session_init\";\n\t\tlet streamOpened = false;\n\t\ttry {\n\t\t\tconst request = createPiServerRequest(options?.signal);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\n\t\t\tconst makeBody = () => ({\n\t\t\t\tsessionId,\n\t\t\t\trunId,\n\t\t\t\tmodel,\n\t\t\t\toptions: serializeOptions(options),\n\t\t\t\tcontextOverlay: options?.contextOverlay ?? [\n\t\t\t\t\t...(context.messages as Message[]),\n\t\t\t\t\t...(options?.ephemeralMessages ?? []),\n\t\t\t\t],\n\t\t\t});\n\t\t\tphase = \"provider_stream\";\n\t\t\tlet response = await request.postJson(\"/api/stream\", makeBody());\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet failure = await readPiServerFailure(response);\n\t\t\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.code)) {\n\t\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\t\t\tresponse = await request.postJson(\"/api/stream\", makeBody());\n\t\t\t\t\tif (response.ok) {\n\t\t\t\t\t\tfailure = { details: \"\", code: undefined };\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`pi-server error: ${failure.details}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait ensurePiServerEventStream(response);\n\t\t\tstreamOpened = true;\n\t\t\tconst reader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tconst parser = new ServerSentEventParser();\n\t\t\tlet terminalReceived = false;\n\n\t\t\tconst consumeEvents = (events: ServerSentEvent[]): void => {\n\t\t\t\tfor (const serverEvent of events) {\n\t\t\t\t\tconst proxyEvent = parseServerSentEventData(\n\t\t\t\t\t\tserverEvent,\n\t\t\t\t\t\t\"pi-server stream\",\n\t\t\t\t\t) as ProxyAssistantMessageEvent;\n\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\tif (!event) continue;\n\t\t\t\t\tif (event.type === \"done\" || event.type === \"error\") terminalReceived = true;\n\t\t\t\t\tstream.push(event);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tconsumeEvents(parser.feed(decoder.decode(value, { stream: true })));\n\t\t\t}\n\t\t\tconsumeEvents(parser.feed(decoder.decode()));\n\t\t\tconsumeEvents(parser.finish());\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\t\t\tif (!terminalReceived) {\n\t\t\t\tthrow new Error(\"pi-server stream ended before a terminal response event\");\n\t\t\t}\n\n\t\t\tif (isEphemeralSession) {\n\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tlet errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\t// Same-run recovery only applies to an interrupted event-stream. HTTP/proxy\n\t\t\t// failures before the stream starts (502/504/fetch failed) never opened a run\n\t\t\t// we can poll; attempting recovery against a down proxy reclassifies the error\n\t\t\t// as history_reconcile and blocks session auto-retry.\n\t\t\tif (!options?.signal?.aborted && phase === \"provider_stream\" && streamOpened) {\n\t\t\t\ttry {\n\t\t\t\t\tconst recoveredRun = await waitForPiServerRunCompletion(\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\trunId,\n\t\t\t\t\t\tcreatePiServerRequest(options?.signal),\n\t\t\t\t\t\toptions,\n\t\t\t\t\t);\n\t\t\t\t\tif (recoveredRun?.status === \"completed\" && recoveredRun.message) {\n\t\t\t\t\t\tswitch (recoveredRun.message.stopReason) {\n\t\t\t\t\t\t\tcase \"stop\":\n\t\t\t\t\t\t\tcase \"length\":\n\t\t\t\t\t\t\tcase \"toolUse\":\n\t\t\t\t\t\t\tcase \"deferred\":\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"done\",\n\t\t\t\t\t\t\t\t\treason: recoveredRun.message.stopReason,\n\t\t\t\t\t\t\t\t\tmessage: recoveredRun.message,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"aborted\":\n\t\t\t\t\t\t\t\tstream.push({ type: \"error\", reason: \"aborted\", error: recoveredRun.message });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"error\":\n\t\t\t\t\t\t\t\tstream.push({ type: \"error\", reason: \"error\", error: recoveredRun.message });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (recoveredRun?.status === \"failed\" && recoveredRun.errorMessage) {\n\t\t\t\t\t\terrorMessage = recoveredRun.errorMessage;\n\t\t\t\t\t}\n\t\t\t\t\tif (recoveredRun?.status === \"aborted\") {\n\t\t\t\t\t\terrorMessage = recoveredRun.errorMessage ?? \"pi-server run aborted\";\n\t\t\t\t\t}\n\t\t\t\t} catch (recoveryError) {\n\t\t\t\t\tphase = \"history_reconcile\";\n\t\t\t\t\terrorMessage = recoveryError instanceof Error ? recoveryError.message : String(recoveryError);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (options?.signal?.aborted && operation && !operation.cancelConfirmed) {\n\t\t\t\ttry {\n\t\t\t\t\tawait cancelPiServerOperation(operation);\n\t\t\t\t} catch (cancelError) {\n\t\t\t\t\terrorMessage = `${errorMessage}; cancellation failed: ${\n\t\t\t\t\t\tcancelError instanceof Error ? cancelError.message : String(cancelError)\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst reason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tpartial.diagnostics = [\n\t\t\t\t...(partial.diagnostics ?? []),\n\t\t\t\t{\n\t\t\t\t\ttype: \"pi_server_failure\",\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\terror: { name: error instanceof Error ? error.name : \"Error\", message: errorMessage },\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tphase,\n\t\t\t\t\t\tsource: \"pi-server\",\n\t\t\t\t\t\tretryable: isRetryablePiServerFailure(phase, error),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t];\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tawait finishPiServerOperation(operation, options?.signal?.aborted === true);\n\t\t\t} catch {\n\t\t\t\t// The owning session's cancellation helper reports the failure and keeps\n\t\t\t\t// this operation registered for a retry.\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn { type: \"text_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"text_end\", contentIndex: proxyEvent.contentIndex, content: content.text, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn { type: \"thinking_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"thinking_end\", contentIndex: proxyEvent.contentIndex, content: content.thinking, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} as any;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content };\n\t\t\t\treturn { type: \"toolcall_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tObject.assign(content, proxyEvent.toolCall);\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn { type: \"toolcall_end\", contentIndex: proxyEvent.contentIndex, toolCall: content, partial };\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t\tcase \"done\":\n\t\t\tif (proxyEvent.message) return { type: \"done\", reason: proxyEvent.reason, message: proxyEvent.message };\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\tpartial.deferred = proxyEvent.deferred;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nexport { getServerUrl, getAuthToken };\n"]}
|
|
1
|
+
{"version":3,"file":"pi-server-client.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport type {\n\tCompactionPreparationOptions,\n\tLegacyCompactResult,\n\tProxyAssistantMessageEvent,\n\tSessionTreeEntry,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Message,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport {\n\tbuildPiServerTreePrefixHashes,\n\thashPiServerSessionEntries,\n\thashPiServerStaticContext,\n} from \"./pi-server-protocol.ts\";\nimport { ChunkRequest } from \"./pi-server-request.ts\";\n\nclass PiServerEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\nfunction getServerUrl(): string {\n\treturn process.env.PI_SERVER_URL ?? \"http://127.0.0.1:4217\";\n}\n\nfunction getAuthToken(): string {\n\treturn process.env.PI_SERVER_AUTH_TOKEN ?? \"\";\n}\n\nfunction createPiServerRequest(signal?: AbortSignal): ChunkRequest {\n\treturn new ChunkRequest({\n\t\tserverUrl: getServerUrl(),\n\t\tauthToken: getAuthToken(),\n\t\tsignal,\n\t});\n}\n\nconst sessionStaticContextHashes = new Map<string, string>();\nconst sessionTreeHashes = new Map<string, string>();\nconst sessionTreeEntryCounts = new Map<string, number>();\nconst sessionTreeLeafIds = new Map<string, string | null>();\nconst sessionHasTemporaryTree = new Set<string>();\ninterface ActivePiServerOperation {\n\townerSessionId: string;\n\tsessionId: string;\n\trunId: string;\n\tsignal?: AbortSignal;\n\tcancelListener?: () => void;\n\tcancelPromise?: Promise<void>;\n\tcancelFailed?: boolean;\n\tcancelConfirmed?: boolean;\n}\n\nconst activePiServerOperations = new Map<string, Map<string, ActivePiServerOperation>>();\nconst RESPONSE_BODY_EXCERPT_CHARS = 500;\nconst TRANSIENT_PI_SERVER_RETRY_DELAYS_MS = [250, 750, 1500];\nconst TRANSIENT_PI_SERVER_STATUS_CODES = new Set([500, 502, 503, 504, 524, 530]);\nconst PI_SERVER_RUN_RECOVERY_POLL_MS = 250;\nconst DEFAULT_PI_SERVER_RUN_RECOVERY_TIMEOUT_MS = 10 * 60 * 1000;\n\nclass PiServerRunRecoveryTimeoutError extends Error {\n\tconstructor() {\n\t\tsuper(\"pi-server run recovery timed out; the server may still be processing the request\");\n\t\tthis.name = \"PiServerRunRecoveryTimeoutError\";\n\t}\n}\n\nexport function hashStaticContext(ctx: Context): string {\n\treturn hashPiServerStaticContext(ctx);\n}\n\nfunction hashEntries(entries: SessionTreeEntry[]): string {\n\treturn hashPiServerSessionEntries(entries);\n}\n\nfunction getLinearTreeFromMessages(messages: Message[]): { entries: SessionTreeEntry[]; leafId: string | null } {\n\tlet parentId: string | null = null;\n\tconst entries = messages.map((message, index): SessionTreeEntry => {\n\t\tconst id = `message-${index}`;\n\t\tconst entry: SessionTreeEntry = {\n\t\t\ttype: \"message\",\n\t\t\tid,\n\t\t\tparentId,\n\t\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\t\tmessage,\n\t\t};\n\t\tparentId = id;\n\t\treturn entry;\n\t});\n\treturn { entries, leafId: parentId };\n}\n\nexport function resetSessionTracking(sessionId: string): void {\n\tsessionStaticContextHashes.delete(sessionId);\n\tsessionTreeHashes.delete(sessionId);\n\tsessionTreeEntryCounts.delete(sessionId);\n\tsessionTreeLeafIds.delete(sessionId);\n\tsessionHasTemporaryTree.delete(sessionId);\n}\n\nexport function resetAllSessionTracking(): void {\n\tsessionStaticContextHashes.clear();\n\tsessionTreeHashes.clear();\n\tsessionTreeEntryCounts.clear();\n\tsessionTreeLeafIds.clear();\n\tsessionHasTemporaryTree.clear();\n\tactivePiServerOperations.clear();\n}\n\nfunction registerPiServerOperation(\n\townerSessionId: string | undefined,\n\tsessionId: string,\n\trunId: string,\n\tsignal?: AbortSignal,\n): ActivePiServerOperation | undefined {\n\tif (!ownerSessionId) return undefined;\n\tlet operations = activePiServerOperations.get(ownerSessionId);\n\tif (!operations) {\n\t\toperations = new Map();\n\t\tactivePiServerOperations.set(ownerSessionId, operations);\n\t}\n\tconst operation: ActivePiServerOperation = { ownerSessionId, sessionId, runId, signal };\n\toperations.set(runId, operation);\n\tif (signal) {\n\t\tconst onAbort = () => {\n\t\t\tvoid cancelPiServerOperation(operation).catch(() => {\n\t\t\t\t// The owner session retries cancellation and reports the failure.\n\t\t\t});\n\t\t};\n\t\toperation.cancelListener = onAbort;\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tif (signal.aborted) onAbort();\n\t}\n\treturn operation;\n}\n\nfunction removePiServerOperation(operation: ActivePiServerOperation): void {\n\tconst operations = activePiServerOperations.get(operation.ownerSessionId);\n\toperation.cancelConfirmed = true;\n\tif (operation.signal && operation.cancelListener) {\n\t\toperation.signal.removeEventListener(\"abort\", operation.cancelListener);\n\t}\n\toperation.cancelListener = undefined;\n\tif (!operations || operations.get(operation.runId) !== operation) return;\n\toperations.delete(operation.runId);\n\tif (operations.size === 0) activePiServerOperations.delete(operation.ownerSessionId);\n}\n\nasync function finishPiServerOperation(\n\toperation: ActivePiServerOperation | undefined,\n\taborted: boolean,\n): Promise<void> {\n\tif (!operation) return;\n\tif (aborted) {\n\t\tif (operation.cancelPromise) await operation.cancelPromise;\n\t} else {\n\t\tremovePiServerOperation(operation);\n\t}\n}\n\nasync function cancelPiServerOperation(operation: ActivePiServerOperation, retryFailed = false): Promise<void> {\n\tif (operation.cancelConfirmed) return;\n\tif (retryFailed && operation.cancelFailed) {\n\t\toperation.cancelFailed = false;\n\t\toperation.cancelPromise = undefined;\n\t}\n\tif (operation.cancelPromise) return operation.cancelPromise;\n\n\tconst cancellation = (async () => {\n\t\tconst request = createPiServerRequest();\n\t\tconst response = await request.postJson(\n\t\t\t`/api/session/${encodeURIComponent(operation.sessionId)}/runs/${encodeURIComponent(operation.runId)}/abort`,\n\t\t\t{ sessionId: operation.sessionId, runId: operation.runId },\n\t\t);\n\t\tconst result = await readPiServerJson<PiServerAbortResponse>(response, \"Session run cancellation failed\");\n\t\tif (result.sessionId !== operation.sessionId || result.runId !== operation.runId) {\n\t\t\tthrow new Error(\"Session run cancellation failed (response did not echo the cancelled run)\");\n\t\t}\n\t\tif (result.status !== \"aborted\" && result.status !== \"completed\" && result.status !== \"failed\") {\n\t\t\tthrow new Error(\"Session run cancellation failed (response was not terminal)\");\n\t\t}\n\t\tremovePiServerOperation(operation);\n\t})();\n\n\toperation.cancelPromise = cancellation;\n\ttry {\n\t\tawait cancellation;\n\t} catch (error) {\n\t\toperation.cancelFailed = true;\n\t\tthrow error;\n\t}\n}\n\n/** Cancel all provider operations owned by a coding-agent session and wait for terminal acknowledgements. */\nexport async function cancelPiServerOperations(ownerSessionId: string): Promise<void> {\n\tconst operations = [...(activePiServerOperations.get(ownerSessionId)?.values() ?? [])];\n\tconst results = await Promise.allSettled(operations.map((operation) => cancelPiServerOperation(operation, true)));\n\tconst failure = results.find((result): result is PromiseRejectedResult => result.status === \"rejected\");\n\tif (failure) throw failure.reason;\n}\n\ninterface SessionInitResponse {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash?: string;\n\tmessageCount: number;\n\tentryCount?: number;\n\tleafId?: string | null;\n\trevision?: number;\n\tfromCache?: boolean;\n}\n\nexport interface PiServerTreeSnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\treplace?: boolean;\n}\n\nexport interface PiServerCompactionResult {\n\tcompaction: Omit<LegacyCompactResult, \"retainedTail\">;\n\tcompactionEntry: SessionTreeEntry;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerHistorySnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerStreamOptions extends SimpleStreamOptions {\n\townerSessionId?: string;\n\tsessionTree?: PiServerTreeSnapshot;\n\tephemeralMessages?: Message[];\n\tcontextOverlay?: Message[];\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\ninterface PiServerErrorResponse {\n\terror: string;\n\tcode?: string;\n\tdetails?: Record<string, unknown>;\n}\n\ninterface PiServerResponseFailure {\n\tdetails: string;\n\tcode?: string;\n}\n\nclass PiServerResponseError extends Error {\n\tpublic readonly code?: string;\n\tpublic readonly details?: Record<string, unknown>;\n\n\tconstructor(message: string, code?: string, details?: Record<string, unknown>) {\n\t\tsuper(message);\n\t\tthis.name = \"PiServerResponseError\";\n\t\tthis.code = code;\n\t\tthis.details = details;\n\t}\n}\n\ninterface PiServerHistoryResponse extends Partial<PiServerHistorySnapshot> {\n\tsessionId: string;\n\ttreeHash?: string;\n\tentryCount?: number;\n\tleafId?: string | null;\n\ttreePatch?: PiServerTreePatch;\n}\n\ninterface PiServerTreePatch {\n\tentriesFrom: number;\n\tbaseRevision?: number;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n}\n\ninterface PiServerCompactionResponse extends Partial<PiServerCompactionResult> {\n\ttreePatch?: PiServerTreePatch & { baseTreeHash?: string };\n}\n\ninterface ServerSentEvent {\n\tevent: string;\n\tdata: string;\n}\n\nclass ServerSentEventParser {\n\tprivate buffer = \"\";\n\tprivate event = \"message\";\n\tprivate dataLines: string[] = [];\n\n\tfeed(text: string): ServerSentEvent[] {\n\t\tthis.buffer += text;\n\t\tconst lines = this.buffer.split(\"\\n\");\n\t\tthis.buffer = lines.pop() ?? \"\";\n\t\treturn lines.flatMap((line) => this.consumeLine(line));\n\t}\n\n\tfinish(): ServerSentEvent[] {\n\t\tconst events: ServerSentEvent[] = [];\n\t\tif (this.buffer.length > 0) {\n\t\t\tevents.push(...this.consumeLine(this.buffer));\n\t\t\tthis.buffer = \"\";\n\t\t}\n\t\tconst final = this.flush();\n\t\tif (final) events.push(final);\n\t\treturn events;\n\t}\n\n\tprivate consumeLine(rawLine: string): ServerSentEvent[] {\n\t\tconst line = rawLine.endsWith(\"\\r\") ? rawLine.slice(0, -1) : rawLine;\n\t\tif (line === \"\") {\n\t\t\tconst event = this.flush();\n\t\t\treturn event ? [event] : [];\n\t\t}\n\t\tif (line.startsWith(\":\")) return [];\n\t\tif (line.startsWith(\"event:\")) {\n\t\t\tthis.event = getServerSentEventFieldValue(line, \"event\");\n\t\t\treturn [];\n\t\t}\n\t\tif (line.startsWith(\"data:\")) {\n\t\t\tthis.dataLines.push(getServerSentEventFieldValue(line, \"data\"));\n\t\t}\n\t\treturn [];\n\t}\n\n\tprivate flush(): ServerSentEvent | undefined {\n\t\tif (this.dataLines.length === 0) return undefined;\n\t\tconst event = { event: this.event, data: this.dataLines.join(\"\\n\") };\n\t\tthis.event = \"message\";\n\t\tthis.dataLines = [];\n\t\treturn event;\n\t}\n}\n\ninterface PiServerRunResponse {\n\trunId: string;\n\tstatus: \"running\" | \"completed\" | \"failed\" | \"aborted\";\n\tmessage?: AssistantMessage;\n\tcompactResult?: PiServerCompactionResponse;\n\terrorMessage?: string;\n}\n\ninterface PiServerAbortResponse {\n\tsessionId: string;\n\trunId: string;\n\tstatus: \"aborted\" | \"completed\" | \"failed\";\n}\n\ninterface PiServerSyncOptions {\n\tsignal?: AbortSignal;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\ntype PiServerFailurePhase = \"session_init\" | \"tree_sync\" | \"provider_stream\" | \"history_reconcile\";\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getResponseStatus(response: Response): string {\n\tconst status = response.statusText ? `${response.status} ${response.statusText}` : String(response.status);\n\treturn response.status >= 500 ? `${status} (server error)` : status;\n}\n\nfunction getResponseContentType(response: Response): string {\n\treturn response.headers.get(\"content-type\") ?? \"unknown\";\n}\n\nfunction getBodyExcerpt(text: string): string {\n\tconst trimmed = text.trim();\n\tif (!trimmed) return \"<empty>\";\n\tconst excerpt = trimmed.slice(0, RESPONSE_BODY_EXCERPT_CHARS);\n\treturn excerpt.length < trimmed.length ? `${excerpt}...` : excerpt;\n}\n\nfunction getJsonErrorResponse(text: string): PiServerErrorResponse | undefined {\n\tif (!text) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\tif (!isObject(parsed) || typeof parsed.error !== \"string\" || parsed.error.length === 0) return undefined;\n\t\tconst code = parsed.code;\n\t\treturn {\n\t\t\terror: parsed.error,\n\t\t\tcode: typeof code === \"string\" ? code : undefined,\n\t\t\tdetails: isObject(parsed.details) ? parsed.details : undefined,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction getJsonErrorText(text: string): string | undefined {\n\treturn getJsonErrorResponse(text)?.error;\n}\n\nfunction formatResponseDetails(response: Response, bodyText: string): string {\n\tconst body = getJsonErrorText(bodyText) ?? getBodyExcerpt(bodyText);\n\treturn `${getResponseStatus(response)}; content-type: ${getResponseContentType(response)}; body excerpt: ${body}`;\n}\n\nasync function readPiServerFailure(response: Response): Promise<PiServerResponseFailure> {\n\tconst bodyText = await response.text();\n\tconst errorResponse = getJsonErrorResponse(bodyText);\n\treturn {\n\t\tdetails: formatResponseDetails(response, bodyText),\n\t\tcode: errorResponse?.code,\n\t};\n}\n\nasync function readPiServerJson<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst bodyText = await response.text();\n\tif (!response.ok) {\n\t\tconst errorResponse = getJsonErrorResponse(bodyText);\n\t\tif (errorResponse) {\n\t\t\tthrow new PiServerResponseError(\n\t\t\t\t`${errorPrefix} (${formatResponseDetails(response, bodyText)})`,\n\t\t\t\terrorResponse.code,\n\t\t\t\terrorResponse.details,\n\t\t\t);\n\t\t}\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)})`);\n\t}\n\ttry {\n\t\treturn JSON.parse(bodyText) as T;\n\t} catch {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected JSON)`);\n\t}\n}\n\nfunction getServerSentEventFieldValue(line: string, field: string): string {\n\tconst prefix = `${field}:`;\n\tconst value = line.slice(prefix.length);\n\treturn value.startsWith(\" \") ? value.slice(1) : value;\n}\n\nfunction parseServerSentEvents(bodyText: string): ServerSentEvent[] {\n\tconst parser = new ServerSentEventParser();\n\treturn [...parser.feed(bodyText), ...parser.finish()];\n}\n\nfunction parseServerSentEventData(event: ServerSentEvent, errorPrefix: string): unknown {\n\ttry {\n\t\treturn JSON.parse(event.data) as unknown;\n\t} catch {\n\t\tthrow new Error(`${errorPrefix} (invalid event-stream JSON data: ${getBodyExcerpt(event.data)})`);\n\t}\n}\n\nfunction formatServerSentEventError(response: Response, payload: unknown): string {\n\tconst message = isObject(payload) && typeof payload.error === \"string\" ? payload.error : JSON.stringify(payload);\n\treturn `${getResponseStatus(response)}; content-type: ${getResponseContentType(response)}; body excerpt: ${getBodyExcerpt(message)}`;\n}\n\nasync function readPiServerEventStreamJson<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst bodyText = await response.text();\n\tfor (const event of parseServerSentEvents(bodyText)) {\n\t\tif (event.event === \"error\") {\n\t\t\tthrow new Error(\n\t\t\t\t`${errorPrefix} (${formatServerSentEventError(response, parseServerSentEventData(event, errorPrefix))})`,\n\t\t\t);\n\t\t}\n\t\tif (event.event === \"result\") {\n\t\t\treturn parseServerSentEventData(event, errorPrefix) as T;\n\t\t}\n\t}\n\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected result event)`);\n}\n\nasync function readPiServerCompactResponse<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst contentType = getResponseContentType(response).toLowerCase().split(\";\")[0]?.trim();\n\tif (contentType === \"text/event-stream\") {\n\t\treturn readPiServerEventStreamJson<T>(response, errorPrefix);\n\t}\n\treturn readPiServerJson<T>(response, errorPrefix);\n}\n\nfunction isAbortError(error: unknown, signal: AbortSignal | undefined): boolean {\n\treturn signal?.aborted === true || (error instanceof Error && error.name === \"AbortError\");\n}\n\nfunction isTransientPiServerFetchError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn /fetch failed|network|socket hang up|terminated|ECONNRESET|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|CONNECT timeout/i.test(\n\t\tmessage,\n\t);\n}\n\nfunction isTransientPiServerError(error: unknown): boolean {\n\tif (isTransientPiServerFetchError(error)) return true;\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn (\n\t\t/\\b(?:500|502|503|504|524|530)\\b/.test(message) ||\n\t\t/CONNECT timeout|Bad Gateway|upstream|A timeout occurred/i.test(message)\n\t);\n}\n\nfunction isRetryablePiServerFailure(phase: PiServerFailurePhase, error: unknown): boolean {\n\tif (phase === \"provider_stream\") return true;\n\tif (phase === \"history_reconcile\") return false;\n\treturn isTransientPiServerError(error);\n}\n\nasync function waitForPiServerRetry(attempt: number, signal: AbortSignal | undefined): Promise<void> {\n\tawait sleep(TRANSIENT_PI_SERVER_RETRY_DELAYS_MS[attempt], undefined, { signal });\n}\n\nasync function postPiServerJsonWithTransientRetry<T>(\n\trequest: ChunkRequest,\n\tendpoint: string,\n\tbody: unknown,\n\terrorPrefix: string,\n): Promise<T> {\n\tfor (let attempt = 0; ; attempt++) {\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await request.postJson(endpoint, body);\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\tattempt < TRANSIENT_PI_SERVER_RETRY_DELAYS_MS.length &&\n\t\t\t\t!isAbortError(error, request.options.signal) &&\n\t\t\t\tisTransientPiServerFetchError(error)\n\t\t\t) {\n\t\t\t\tawait waitForPiServerRetry(attempt, request.options.signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst bodyText = await response.text();\n\t\tif (!response.ok) {\n\t\t\tif (\n\t\t\t\tattempt < TRANSIENT_PI_SERVER_RETRY_DELAYS_MS.length &&\n\t\t\t\t!request.options.signal?.aborted &&\n\t\t\t\tTRANSIENT_PI_SERVER_STATUS_CODES.has(response.status)\n\t\t\t) {\n\t\t\t\tawait waitForPiServerRetry(attempt, request.options.signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst errorResponse = getJsonErrorResponse(bodyText);\n\t\t\tif (errorResponse) {\n\t\t\t\tthrow new PiServerResponseError(\n\t\t\t\t\t`${errorPrefix} (${formatResponseDetails(response, bodyText)})`,\n\t\t\t\t\terrorResponse.code,\n\t\t\t\t\terrorResponse.details,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)})`);\n\t\t}\n\n\t\ttry {\n\t\t\treturn JSON.parse(bodyText) as T;\n\t\t} catch {\n\t\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected JSON)`);\n\t\t}\n\t}\n}\n\nasync function ensurePiServerEventStream(response: Response): Promise<void> {\n\tconst contentType = getResponseContentType(response);\n\tif (contentType.toLowerCase().split(\";\")[0]?.trim() === \"text/event-stream\") {\n\t\treturn;\n\t}\n\tconst bodyText = await response.text();\n\tthrow new Error(`pi-server error: ${formatResponseDetails(response, bodyText)}; expected text/event-stream`);\n}\n\nasync function ensureSessionInit(\n\tsessionId: string,\n\tcontext: Context,\n\trequest: ChunkRequest,\n): Promise<SessionInitResponse> {\n\tconst currentHash = hashStaticContext(context);\n\tconst previousHash = sessionStaticContextHashes.get(sessionId);\n\n\tif (previousHash === currentHash) {\n\t\treturn {\n\t\t\tsessionId,\n\t\t\tstaticContextHash: previousHash,\n\t\t\tmessageCount: 0,\n\t\t\tfromCache: true,\n\t\t};\n\t}\n\n\tconst staticContext = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n\n\tconst endpoint = previousHash === undefined ? \"/api/session/init\" : \"/api/session/update\";\n\n\tconst result = await postPiServerJsonWithTransientRetry<SessionInitResponse>(\n\t\trequest,\n\t\tendpoint,\n\t\t{ sessionId, staticContext },\n\t\t\"Session init failed\",\n\t);\n\tsessionStaticContextHashes.set(sessionId, currentHash);\n\tif (result.treeHash !== undefined) {\n\t\tsessionTreeHashes.set(sessionId, result.treeHash);\n\t\tsessionTreeLeafIds.set(sessionId, result.leafId ?? null);\n\t\tif (result.entryCount !== undefined) {\n\t\t\tsessionTreeEntryCounts.set(sessionId, result.entryCount);\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction getStaticContext(context: Context) {\n\treturn {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n}\n\nfunction markTreeSynced(sessionId: string, tree: PiServerTreeSnapshot): void {\n\tconst prefixHashes = buildPiServerTreePrefixHashes(tree.entries);\n\tsessionTreeHashes.set(sessionId, prefixHashes[prefixHashes.length - 1]);\n\tsessionTreeEntryCounts.set(sessionId, tree.entries.length);\n\tsessionTreeLeafIds.set(sessionId, tree.leafId);\n\tif (tree.replace) {\n\t\tsessionHasTemporaryTree.add(sessionId);\n\t} else {\n\t\tsessionHasTemporaryTree.delete(sessionId);\n\t}\n}\n\nasync function postTreeJson(\n\trequest: ChunkRequest,\n\tendpoint: string,\n\tbody: unknown,\n\terrorPrefix: string,\n): Promise<void> {\n\tawait postPiServerJsonWithTransientRetry<unknown>(request, endpoint, body, errorPrefix);\n}\n\nasync function fetchPiServerHistory(\n\tsessionId: string,\n\trequest: ChunkRequest,\n\tlocalTree?: PiServerTreeSnapshot,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tconst prefixHashes = localTree ? buildPiServerTreePrefixHashes(localTree.entries) : undefined;\n\tconst historyUrl =\n\t\tlocalTree && prefixHashes\n\t\t\t? `/api/session/${encodeURIComponent(sessionId)}/history?entriesFrom=${localTree.entries.length}&baseTreeHash=${encodeURIComponent(prefixHashes[prefixHashes.length - 1])}`\n\t\t\t: `/api/session/${encodeURIComponent(sessionId)}/history`;\n\tconst response = await request.getJson(historyUrl);\n\tif (response.status === 404) {\n\t\treturn undefined;\n\t}\n\tconst history = await readPiServerJson<PiServerHistoryResponse>(response, \"Session history reconciliation failed\");\n\tif (history.treePatch && localTree) {\n\t\treturn {\n\t\t\tentries: [...localTree.entries.slice(0, history.treePatch.entriesFrom), ...history.treePatch.entries],\n\t\t\tleafId: history.treePatch.leafId,\n\t\t\tmessages: history.messages ?? [],\n\t\t};\n\t}\n\tif (!history.entries || history.leafId === undefined || !history.messages) {\n\t\tthrow new Error(\"Session history reconciliation failed (response did not include the session tree)\");\n\t}\n\treturn {\n\t\tentries: history.entries,\n\t\tleafId: history.leafId,\n\t\tmessages: history.messages,\n\t};\n}\n\nasync function applyPiServerHistory(\n\tsessionId: string,\n\tsnapshot: PiServerHistorySnapshot,\n\tonHistoryReconciled: ((snapshot: PiServerHistorySnapshot) => void | Promise<void>) | undefined,\n): Promise<void> {\n\tmarkTreeSynced(sessionId, snapshot);\n\tawait onHistoryReconciled?.(snapshot);\n}\n\nasync function fetchPiServerRun(\n\tsessionId: string,\n\trunId: string,\n\trequest: ChunkRequest,\n): Promise<PiServerRunResponse | undefined> {\n\tconst response = await request.getJson(\n\t\t`/api/session/${encodeURIComponent(sessionId)}/runs/${encodeURIComponent(runId)}`,\n\t);\n\tif (response.status === 404) {\n\t\treturn undefined;\n\t}\n\treturn readPiServerJson<PiServerRunResponse>(response, \"Session run recovery failed\");\n}\n\nasync function waitForPiServerRunCompletion(\n\tsessionId: string,\n\trunId: string,\n\trequest: ChunkRequest,\n\toptions: PiServerStreamOptions | undefined,\n): Promise<PiServerRunResponse | undefined> {\n\tconst deadline = Date.now() + DEFAULT_PI_SERVER_RUN_RECOVERY_TIMEOUT_MS;\n\tfor (;;) {\n\t\tconst run = await fetchPiServerRun(sessionId, runId, request);\n\t\tif (!run || run.status !== \"running\") return run;\n\t\tif (Date.now() >= deadline) throw new PiServerRunRecoveryTimeoutError();\n\t\tawait sleep(PI_SERVER_RUN_RECOVERY_POLL_MS, undefined, { signal: options?.signal });\n\t}\n}\n\nfunction getKnownServerPrefixIds(sessionId: string, entries: SessionTreeEntry[]): Set<string> | undefined {\n\tconst entryCount = sessionTreeEntryCounts.get(sessionId);\n\tconst treeHash = sessionTreeHashes.get(sessionId);\n\tif (entryCount === undefined || treeHash === undefined || entryCount > entries.length) {\n\t\treturn undefined;\n\t}\n\tconst prefixHashes = buildPiServerTreePrefixHashes(entries);\n\tif (prefixHashes[entryCount] !== treeHash) {\n\t\treturn undefined;\n\t}\n\treturn new Set(entries.slice(0, entryCount).map((entry) => entry.id));\n}\n\nfunction isRecoverableTreeDivergenceCode(code: string | undefined): boolean {\n\treturn code === \"PARENT_ENTRY_NOT_FOUND\" || code === \"LEAF_ID_NOT_FOUND\" || code === \"ENTRY_ALREADY_EXISTS\";\n}\n\nfunction isRecoverableMissingServerStateCode(code: string | undefined): boolean {\n\treturn code === \"SESSION_NOT_FOUND\" || code === \"SESSION_NO_STATIC_CONTEXT\" || isRecoverableTreeDivergenceCode(code);\n}\n\nfunction isRecoverableTreeDivergenceError(error: unknown): boolean {\n\tif (error instanceof PiServerResponseError) {\n\t\treturn isRecoverableTreeDivergenceCode(error.code);\n\t}\n\tif (error && typeof error === \"object\" && \"code\" in error) {\n\t\treturn isRecoverableTreeDivergenceCode((error as { code?: string }).code);\n\t}\n\treturn false;\n}\n\nfunction isRecoverableMissingServerState(response: Response, errorCode?: string): boolean {\n\tif (response.status !== 400 && response.status !== 404) return false;\n\treturn isRecoverableMissingServerStateCode(errorCode);\n}\n\nasync function syncFullPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n): Promise<void> {\n\tawait postTreeJson(\n\t\trequest,\n\t\t\"/api/session/tree/sync\",\n\t\t{\n\t\t\tsessionId,\n\t\t\tentries: tree.entries,\n\t\t\tleafId: tree.leafId,\n\t\t\tstaticContext: getStaticContext(context),\n\t\t},\n\t\t\"Session tree sync failed\",\n\t);\n\tmarkTreeSynced(sessionId, tree);\n}\n\nfunction shouldUseServerHistory(sessionId: string, tree: PiServerTreeSnapshot): boolean {\n\treturn !tree.replace && !sessionHasTemporaryTree.has(sessionId) && (sessionTreeEntryCounts.get(sessionId) ?? 0) > 0;\n}\n\nasync function recoverPiServerTreeDivergence(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tif (shouldUseServerHistory(sessionId, tree)) {\n\t\tconst snapshot = await fetchPiServerHistory(sessionId, request, tree);\n\t\tif (snapshot && snapshot.entries.length > 0) {\n\t\t\tawait applyPiServerHistory(sessionId, snapshot, onHistoryReconciled);\n\t\t\treturn snapshot;\n\t\t}\n\t}\n\n\tawait syncFullPiServerTree(sessionId, context, tree, request);\n}\n\nexport async function syncPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\toptions?: PiServerSyncOptions,\n): Promise<void> {\n\tconst request = createPiServerRequest(options?.signal);\n\tawait ensureSessionInit(sessionId, context, request);\n\tconst reconciled = await syncPiServerTreeWithRequest(\n\t\tsessionId,\n\t\tcontext,\n\t\ttree,\n\t\trequest,\n\t\toptions?.onHistoryReconciled,\n\t);\n\tif (reconciled) {\n\t\tthrow new Error(\"pi-server history differed from local history; local session was reconciled to server history\");\n\t}\n}\n\nasync function syncPiServerTreeWithRequest(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tconst syncTree = tree;\n\tconst currentHash = hashEntries(syncTree.entries);\n\tconst previousHash = sessionTreeHashes.get(sessionId);\n\tconst previousLeafId = sessionTreeLeafIds.get(sessionId);\n\n\tif (!tree.replace && previousHash === currentHash) {\n\t\tif (previousLeafId !== syncTree.leafId) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/switch\",\n\t\t\t\t\t{ sessionId, leafId: syncTree.leafId },\n\t\t\t\t\t\"Session tree switch failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\treturn recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t}\n\t\t\tsessionTreeLeafIds.set(sessionId, syncTree.leafId);\n\t\t}\n\t\tmarkTreeSynced(sessionId, syncTree);\n\t\treturn;\n\t}\n\n\tconst syncedIds =\n\t\t!tree.replace && !sessionHasTemporaryTree.has(sessionId)\n\t\t\t? getKnownServerPrefixIds(sessionId, syncTree.entries)\n\t\t\t: undefined;\n\tif (syncedIds) {\n\t\tconst deltaEntries = syncTree.entries.filter((entry) => !syncedIds.has(entry.id));\n\t\tif (deltaEntries.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/append\",\n\t\t\t\t\t{\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tentries: deltaEntries,\n\t\t\t\t\t\tleafId: syncTree.leafId,\n\t\t\t\t\t\tstaticContext: getStaticContext(context),\n\t\t\t\t\t},\n\t\t\t\t\t\"Session tree append failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\treturn recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t}\n\t\t\tmarkTreeSynced(sessionId, syncTree);\n\t\t\treturn;\n\t\t}\n\t}\n\n\treturn recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n}\n\nfunction serializeOptions(options: SimpleStreamOptions | undefined): SimpleStreamOptions {\n\treturn {\n\t\ttemperature: options?.temperature,\n\t\tmaxTokens: options?.maxTokens,\n\t\treasoning: options?.reasoning,\n\t\tcacheRetention: options?.cacheRetention,\n\t\tsessionId: options?.sessionId,\n\t\tapiKey: options?.apiKey,\n\t\theaders: options?.headers,\n\t\tmetadata: options?.metadata,\n\t\ttransport: options?.transport,\n\t\tthinkingBudgets: options?.thinkingBudgets,\n\t\ttimeoutMs: options?.timeoutMs,\n\t\twebsocketConnectTimeoutMs: options?.websocketConnectTimeoutMs,\n\t\tmaxRetries: options?.maxRetries,\n\t\tmaxRetryDelayMs: options?.maxRetryDelayMs,\n\t};\n}\n\nexport interface PiServerCompactOptions extends SimpleStreamOptions {\n\townerSessionId?: string;\n\tcustomInstructions?: string;\n\tsettings?: unknown;\n\tpreparation?: CompactionPreparationOptions;\n\t/** Fixed-size digest of the exact provider message overlay for this session. */\n\tcacheContextHash?: string;\n\t/** Fixed-size digest of the synchronized static prompt and tools. */\n\tcacheStaticContextHash?: string;\n\tcacheFallbackReason?: string;\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nexport async function compactPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerCompactOptions,\n): Promise<PiServerCompactionResult> {\n\tconst sessionId = options?.sessionId ?? \"default\";\n\tconst runId = randomUUID();\n\tconst operation = registerPiServerOperation(options?.ownerSessionId ?? sessionId, sessionId, runId, options?.signal);\n\tconst request = createPiServerRequest(options?.signal);\n\n\ttry {\n\t\tawait ensureSessionInit(sessionId, context, request);\n\t\tlet tree = options?.sessionTree ?? getLinearTreeFromMessages(context.messages as Message[]);\n\t\tlet preparation = options?.preparation;\n\t\tconst syncTree = async () => {\n\t\t\tconst reconciled = await syncPiServerTreeWithRequest(\n\t\t\t\tsessionId,\n\t\t\t\tcontext,\n\t\t\t\ttree,\n\t\t\t\trequest,\n\t\t\t\toptions?.onHistoryReconciled,\n\t\t\t);\n\t\t\tif (reconciled) {\n\t\t\t\ttree = reconciled;\n\t\t\t\t// A forced cut point belongs to the old branch. Recompute it on the authoritative tree.\n\t\t\t\tpreparation = undefined;\n\t\t\t}\n\t\t};\n\t\tawait syncTree();\n\n\t\tconst makeBody = () => ({\n\t\t\tsessionId,\n\t\t\trunId,\n\t\t\tmodel,\n\t\t\toptions: serializeOptions(options),\n\t\t\tsettings: options?.settings,\n\t\t\tpreparation,\n\t\t\tcustomInstructions: options?.customInstructions,\n\t\t\tcacheContextHash: options?.cacheContextHash,\n\t\t\tcacheStaticContextHash: options?.cacheStaticContextHash,\n\t\t\tcacheFallbackReason: options?.cacheFallbackReason,\n\t\t\tbaseTreeHash: sessionTreeHashes.get(sessionId) ?? hashEntries(tree.entries),\n\t\t\tstreamResponse: true,\n\t\t});\n\t\tlet response = await request.postJson(\"/api/session/compact\", makeBody());\n\t\tif (!response.ok) {\n\t\t\tlet failure = await readPiServerFailure(response);\n\t\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.code)) {\n\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\t\tawait syncTree();\n\t\t\t\tresponse = await request.postJson(\"/api/session/compact\", makeBody());\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tfailure = { details: \"\", code: undefined };\n\t\t\t\t} else {\n\t\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`Server compaction failed (${failure.details})`);\n\t\t\t}\n\t\t}\n\t\tlet result: PiServerCompactionResponse;\n\t\ttry {\n\t\t\tresult = await readPiServerCompactResponse<PiServerCompactionResponse>(response, \"Server compaction failed\");\n\t\t} catch (error) {\n\t\t\tif (options?.signal?.aborted || !getResponseContentType(response).startsWith(\"text/event-stream\")) throw error;\n\t\t\tconst run = await waitForPiServerRunCompletion(sessionId, runId, request, options);\n\t\t\tif (run?.status !== \"completed\" || !run.compactResult) {\n\t\t\t\tif (run?.errorMessage) throw new Error(`Server compaction failed: ${run.errorMessage}`);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tresult = run.compactResult;\n\t\t}\n\t\tif (!result.compaction) {\n\t\t\tthrow new Error(\"Server compaction response did not include a compaction result\");\n\t\t}\n\t\tconst resultEntries =\n\t\t\tresult.entries ??\n\t\t\t(result.treePatch\n\t\t\t\t? [...tree.entries.slice(0, result.treePatch.entriesFrom), ...result.treePatch.entries]\n\t\t\t\t: undefined);\n\t\tconst resultLeafId = result.leafId ?? result.treePatch?.leafId;\n\t\tif (!result.compactionEntry || !resultEntries || resultLeafId === undefined) {\n\t\t\tthrow new Error(\"Server compaction response did not include the updated session tree\");\n\t\t}\n\t\tmarkTreeSynced(sessionId, { entries: resultEntries, leafId: resultLeafId });\n\t\treturn {\n\t\t\tcompaction: result.compaction,\n\t\t\tcompactionEntry: result.compactionEntry,\n\t\t\tentries: resultEntries,\n\t\t\tleafId: resultLeafId,\n\t\t\tmessages: result.messages ?? [],\n\t\t};\n\t} finally {\n\t\tawait finishPiServerOperation(operation, options?.signal?.aborted === true);\n\t}\n}\n\nexport async function streamPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerStreamOptions,\n): Promise<PiServerEventStream> {\n\tconst sessionId = options?.sessionId ?? randomUUID();\n\tconst runId = randomUUID();\n\tconst isEphemeralSession = options?.sessionId === undefined;\n\tconst operation = registerPiServerOperation(options?.ownerSessionId ?? sessionId, sessionId, runId, options?.signal);\n\tconst stream = new PiServerEventStream();\n\n\tconst partial: AssistantMessage = {\n\t\trole: \"assistant\",\n\t\tstopReason: \"stop\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\ttimestamp: Date.now(),\n\t};\n\n\t(async () => {\n\t\tlet phase: PiServerFailurePhase = \"session_init\";\n\t\tlet streamOpened = false;\n\t\ttry {\n\t\t\tconst request = createPiServerRequest(options?.signal);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\n\t\t\tconst makeBody = () => ({\n\t\t\t\tsessionId,\n\t\t\t\trunId,\n\t\t\t\tmodel,\n\t\t\t\toptions: serializeOptions(options),\n\t\t\t\tcontextOverlay: options?.contextOverlay ?? [\n\t\t\t\t\t...(context.messages as Message[]),\n\t\t\t\t\t...(options?.ephemeralMessages ?? []),\n\t\t\t\t],\n\t\t\t});\n\t\t\tphase = \"provider_stream\";\n\t\t\tlet response = await request.postJson(\"/api/stream\", makeBody());\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet failure = await readPiServerFailure(response);\n\t\t\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.code)) {\n\t\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\t\t\tresponse = await request.postJson(\"/api/stream\", makeBody());\n\t\t\t\t\tif (response.ok) {\n\t\t\t\t\t\tfailure = { details: \"\", code: undefined };\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`pi-server error: ${failure.details}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait ensurePiServerEventStream(response);\n\t\t\tstreamOpened = true;\n\t\t\tconst reader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tconst parser = new ServerSentEventParser();\n\t\t\tlet terminalReceived = false;\n\n\t\t\tconst consumeEvents = (events: ServerSentEvent[]): void => {\n\t\t\t\tfor (const serverEvent of events) {\n\t\t\t\t\tconst proxyEvent = parseServerSentEventData(\n\t\t\t\t\t\tserverEvent,\n\t\t\t\t\t\t\"pi-server stream\",\n\t\t\t\t\t) as ProxyAssistantMessageEvent;\n\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\tif (!event) continue;\n\t\t\t\t\tif (event.type === \"done\" || event.type === \"error\") terminalReceived = true;\n\t\t\t\t\tstream.push(event);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tconsumeEvents(parser.feed(decoder.decode(value, { stream: true })));\n\t\t\t}\n\t\t\tconsumeEvents(parser.feed(decoder.decode()));\n\t\t\tconsumeEvents(parser.finish());\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\t\t\tif (!terminalReceived) {\n\t\t\t\tthrow new Error(\"pi-server stream ended before a terminal response event\");\n\t\t\t}\n\n\t\t\tif (isEphemeralSession) {\n\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tlet errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\t// Same-run recovery only applies to an interrupted event-stream. HTTP/proxy\n\t\t\t// failures before the stream starts (502/504/fetch failed) never opened a run\n\t\t\t// we can poll; attempting recovery against a down proxy reclassifies the error\n\t\t\t// as history_reconcile and blocks session auto-retry.\n\t\t\tif (!options?.signal?.aborted && phase === \"provider_stream\" && streamOpened) {\n\t\t\t\ttry {\n\t\t\t\t\tconst recoveredRun = await waitForPiServerRunCompletion(\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\trunId,\n\t\t\t\t\t\tcreatePiServerRequest(options?.signal),\n\t\t\t\t\t\toptions,\n\t\t\t\t\t);\n\t\t\t\t\tif (recoveredRun?.status === \"completed\" && recoveredRun.message) {\n\t\t\t\t\t\tswitch (recoveredRun.message.stopReason) {\n\t\t\t\t\t\t\tcase \"stop\":\n\t\t\t\t\t\t\tcase \"length\":\n\t\t\t\t\t\t\tcase \"toolUse\":\n\t\t\t\t\t\t\tcase \"deferred\":\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: \"done\",\n\t\t\t\t\t\t\t\t\treason: recoveredRun.message.stopReason,\n\t\t\t\t\t\t\t\t\tmessage: recoveredRun.message,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"aborted\":\n\t\t\t\t\t\t\t\tstream.push({ type: \"error\", reason: \"aborted\", error: recoveredRun.message });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"error\":\n\t\t\t\t\t\t\t\tstream.push({ type: \"error\", reason: \"error\", error: recoveredRun.message });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (recoveredRun?.status === \"failed\" && recoveredRun.errorMessage) {\n\t\t\t\t\t\terrorMessage = recoveredRun.errorMessage;\n\t\t\t\t\t}\n\t\t\t\t\tif (recoveredRun?.status === \"aborted\") {\n\t\t\t\t\t\terrorMessage = recoveredRun.errorMessage ?? \"pi-server run aborted\";\n\t\t\t\t\t}\n\t\t\t\t} catch (recoveryError) {\n\t\t\t\t\tphase = \"history_reconcile\";\n\t\t\t\t\terrorMessage = recoveryError instanceof Error ? recoveryError.message : String(recoveryError);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (options?.signal?.aborted && operation && !operation.cancelConfirmed) {\n\t\t\t\ttry {\n\t\t\t\t\tawait cancelPiServerOperation(operation);\n\t\t\t\t} catch (cancelError) {\n\t\t\t\t\terrorMessage = `${errorMessage}; cancellation failed: ${\n\t\t\t\t\t\tcancelError instanceof Error ? cancelError.message : String(cancelError)\n\t\t\t\t\t}`;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst reason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tpartial.diagnostics = [\n\t\t\t\t...(partial.diagnostics ?? []),\n\t\t\t\t{\n\t\t\t\t\ttype: \"pi_server_failure\",\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\terror: { name: error instanceof Error ? error.name : \"Error\", message: errorMessage },\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tphase,\n\t\t\t\t\t\tsource: \"pi-server\",\n\t\t\t\t\t\tretryable: isRetryablePiServerFailure(phase, error),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t];\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tawait finishPiServerOperation(operation, options?.signal?.aborted === true);\n\t\t\t} catch {\n\t\t\t\t// The owning session's cancellation helper reports the failure and keeps\n\t\t\t\t// this operation registered for a retry.\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn { type: \"text_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"text_end\", contentIndex: proxyEvent.contentIndex, content: content.text, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn { type: \"thinking_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"thinking_end\", contentIndex: proxyEvent.contentIndex, content: content.thinking, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} as any;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content };\n\t\t\t\treturn { type: \"toolcall_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tObject.assign(content, proxyEvent.toolCall);\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn { type: \"toolcall_end\", contentIndex: proxyEvent.contentIndex, toolCall: content, partial };\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t\tcase \"done\":\n\t\t\tif (proxyEvent.message) return { type: \"done\", reason: proxyEvent.reason, message: proxyEvent.message };\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\tpartial.deferred = proxyEvent.deferred;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nexport { getServerUrl, getAuthToken };\n"]}
|
|
@@ -647,6 +647,9 @@ export async function compactPiServer(model, context, options) {
|
|
|
647
647
|
settings: options?.settings,
|
|
648
648
|
preparation,
|
|
649
649
|
customInstructions: options?.customInstructions,
|
|
650
|
+
cacheContextHash: options?.cacheContextHash,
|
|
651
|
+
cacheStaticContextHash: options?.cacheStaticContextHash,
|
|
652
|
+
cacheFallbackReason: options?.cacheFallbackReason,
|
|
650
653
|
baseTreeHash: sessionTreeHashes.get(sessionId) ?? hashEntries(tree.entries),
|
|
651
654
|
streamResponse: true,
|
|
652
655
|
});
|