@joshuaswarren/openclaw-engram 9.1.19 → 9.1.21

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.
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  CompoundingEngine,
4
4
  defaultTierMigrationCycleBudget
5
- } from "./chunk-OLJAEA5I.js";
6
- import "./chunk-DTCQ2KEX.js";
5
+ } from "./chunk-EM2BU45W.js";
6
+ import "./chunk-Y6DMH5LN.js";
7
7
  import "./chunk-SSIIJJKA.js";
8
8
  export {
9
9
  CompoundingEngine,
10
10
  defaultTierMigrationCycleBudget
11
11
  };
12
- //# sourceMappingURL=engine-63EMTVP7.js.map
12
+ //# sourceMappingURL=engine-ZPZYNGSE.js.map
@@ -1,9 +1,9 @@
1
1
  // openclaw-engram: Local-first memory plugin
2
2
  import {
3
3
  FallbackLlmClient
4
- } from "./chunk-KIA3HVQD.js";
4
+ } from "./chunk-FP3MUEPD.js";
5
5
  import "./chunk-SSIIJJKA.js";
6
6
  export {
7
7
  FallbackLlmClient
8
8
  };
9
- //# sourceMappingURL=fallback-llm-46JU5PST.js.map
9
+ //# sourceMappingURL=fallback-llm-3DXDWI5H.js.map
package/dist/index.js CHANGED
@@ -83,8 +83,8 @@ import {
83
83
  validateRouteTarget,
84
84
  validateWorkProductLedgerEntry,
85
85
  wrapWorkLayerContext
86
- } from "./chunk-KZMICTC7.js";
87
- import "./chunk-OLJAEA5I.js";
86
+ } from "./chunk-3ILF2OUT.js";
87
+ import "./chunk-EM2BU45W.js";
88
88
  import {
89
89
  getCausalTrajectoryStoreStatus
90
90
  } from "./chunk-IMMYYNXG.js";
@@ -107,11 +107,11 @@ import {
107
107
  readProjectedNativeKnowledgeChunks,
108
108
  sortMemoryLifecycleEvents,
109
109
  toMemoryPathRel
110
- } from "./chunk-DTCQ2KEX.js";
110
+ } from "./chunk-Y6DMH5LN.js";
111
111
  import {
112
112
  analyzeGraphHealth
113
113
  } from "./chunk-6KX4XLQJ.js";
114
- import "./chunk-KIA3HVQD.js";
114
+ import "./chunk-FP3MUEPD.js";
115
115
  import {
116
116
  assertIsoRecordedAt,
117
117
  assertSafePathSegment,
@@ -9250,7 +9250,7 @@ async function runRepairMemoryProjectionCliCommand(options) {
9250
9250
  });
9251
9251
  }
9252
9252
  async function runMemoryTimelineCliCommand(options) {
9253
- const storage = new (await import("./storage-GSXR3Q63.js")).StorageManager(options.memoryDir);
9253
+ const storage = new (await import("./storage-PK7DDBRU.js")).StorageManager(options.memoryDir);
9254
9254
  return storage.getMemoryTimeline(options.memoryId, options.limit);
9255
9255
  }
9256
9256
  async function runMemoryGovernanceCliCommand(options) {
@@ -9275,7 +9275,7 @@ async function runMemoryGovernanceRestoreCliCommand(options) {
9275
9275
  });
9276
9276
  }
9277
9277
  async function runMemoryReviewDispositionCliCommand(options) {
9278
- const storage = new (await import("./storage-GSXR3Q63.js")).StorageManager(options.memoryDir);
9278
+ const storage = new (await import("./storage-PK7DDBRU.js")).StorageManager(options.memoryDir);
9279
9279
  const memory = await storage.getMemoryById(options.memoryId);
9280
9280
  if (!memory) throw new Error(`memory not found: ${options.memoryId}`);
9281
9281
  const updated = await storage.writeMemoryFrontmatter(memory, {
@@ -9441,7 +9441,7 @@ async function runSemanticRulePromoteCliCommand(options) {
9441
9441
  });
9442
9442
  }
9443
9443
  async function runCompoundingPromoteCliCommand(options) {
9444
- const { CompoundingEngine } = await import("./engine-63EMTVP7.js");
9444
+ const { CompoundingEngine } = await import("./engine-ZPZYNGSE.js");
9445
9445
  const config = parseConfig({
9446
9446
  memoryDir: options.memoryDir,
9447
9447
  qmdEnabled: false,
@@ -5,7 +5,7 @@ import {
5
5
  normalizeEntityName,
6
6
  parseEntityFile,
7
7
  serializeEntityFile
8
- } from "./chunk-DTCQ2KEX.js";
8
+ } from "./chunk-Y6DMH5LN.js";
9
9
  import "./chunk-SSIIJJKA.js";
10
10
  export {
11
11
  ContentHashIndex,
@@ -14,4 +14,4 @@ export {
14
14
  parseEntityFile,
15
15
  serializeEntityFile
16
16
  };
17
- //# sourceMappingURL=storage-GSXR3Q63.js.map
17
+ //# sourceMappingURL=storage-PK7DDBRU.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshuaswarren/openclaw-engram",
3
- "version": "9.1.19",
3
+ "version": "9.1.21",
4
4
  "type": "module",
5
5
  "description": "Local-first memory plugin for OpenClaw. LLM-powered extraction, markdown storage, hybrid search via QMD.",
6
6
  "keywords": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/json-extract.ts","../src/openai-chat-compat.ts","../src/resolve-provider-secret.ts","../src/fallback-llm.ts"],"sourcesContent":["/**\n * Utilities for extracting JSON payloads from LLM outputs.\n *\n * We see common failure modes:\n * - \"Here's an example: {..}\\nHere's the real answer: {..}\" (multiple JSON blocks)\n * - fenced ```json blocks\n * - leading/trailing prose around JSON\n *\n * These helpers attempt multiple candidates and let callers validate with schemas.\n */\n\nexport function stripCodeFences(text: string): string {\n return text.replace(/```(?:json)?\\s*([\\s\\S]*?)```/gi, (_m, inner) => String(inner).trim());\n}\n\nexport function extractJsonCandidates(text: string): string[] {\n const trimmed = text.trim();\n const cleaned = stripCodeFences(trimmed);\n const candidates: string[] = [];\n\n if (cleaned.length > 0) candidates.push(cleaned);\n candidates.push(...scanBalancedJsonBlocks(cleaned));\n\n // Legacy regex fallback (single object)\n const objMatch = cleaned.match(/\\{[\\s\\S]*\\}/);\n if (objMatch) candidates.push(objMatch[0]);\n\n const seen = new Set<string>();\n return candidates\n .map((c) => c.trim())\n .filter((c) => c.length > 0)\n .filter((c) => {\n if (seen.has(c)) return false;\n seen.add(c);\n return true;\n });\n}\n\nfunction scanBalancedJsonBlocks(text: string): string[] {\n const out: string[] = [];\n const opens = new Set([\"{\", \"[\"]);\n const closes: Record<string, string> = { \"{\": \"}\", \"[\": \"]\" };\n\n for (let i = 0; i < text.length; i++) {\n const start = text[i];\n if (!opens.has(start)) continue;\n\n const expectedClose = closes[start];\n let depth = 0;\n let inString = false;\n let escape = false;\n\n for (let j = i; j < text.length; j++) {\n const ch = text[j];\n\n if (inString) {\n if (escape) {\n escape = false;\n } else if (ch === \"\\\\\") {\n escape = true;\n } else if (ch === \"\\\"\") {\n inString = false;\n }\n continue;\n }\n\n if (ch === \"\\\"\") {\n inString = true;\n continue;\n }\n\n if (ch === start) depth++;\n if (ch === expectedClose) depth--;\n\n if (depth === 0) {\n out.push(text.slice(i, j + 1).trim());\n i = j;\n break;\n }\n }\n }\n\n return out;\n}\n\n","function normalizedModel(model: string): string {\n return model.trim().toLowerCase();\n}\n\nfunction matchesModelFamily(normalized: string, familyPattern: RegExp): boolean {\n return familyPattern.test(normalized);\n}\n\nexport function shouldAssumeOpenAiChatCompletions(baseUrl?: string): boolean {\n if (!baseUrl) return true;\n try {\n return new URL(baseUrl).hostname.toLowerCase() === \"api.openai.com\";\n } catch {\n return false;\n }\n}\n\nexport function usesMaxCompletionTokens(model: string, options?: { assumeOpenAI?: boolean }): boolean {\n const normalized = normalizedModel(model);\n if (options?.assumeOpenAI !== true) return false;\n if (matchesModelFamily(normalized, /^gpt-5(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^gpt-4o(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^gpt-4\\.1(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^o1(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^o3(?:$|[-.])/)) return true;\n return matchesModelFamily(normalized, /^o4-mini(?:$|[-.])/);\n}\n\nexport function buildChatCompletionTokenLimit(\n model: string,\n maxTokens: number,\n options?: { assumeOpenAI?: boolean },\n): { max_tokens: number } | { max_completion_tokens: number } {\n const safeMaxTokens = Math.max(0, Math.floor(maxTokens));\n if (usesMaxCompletionTokens(model, options)) {\n return { max_completion_tokens: safeMaxTokens };\n }\n return { max_tokens: safeMaxTokens };\n}\n","import { log } from \"./logger.js\";\nimport { execFileSync } from \"node:child_process\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\n/**\n * Secret reference object format used by OpenClaw's models.json.\n */\ninterface SecretRef {\n source: \"exec\" | \"file\" | \"env\";\n provider?: string;\n id: string;\n command?: string;\n args?: string[];\n}\n\n/**\n * Auth profile entry from agent auth-profiles.json files.\n */\ninterface AuthProfile {\n type?: string;\n provider?: string;\n token?: string | SecretRef;\n access?: string;\n apiKey?: string;\n}\n\nconst resolvedCache = new Map<string, string | null>();\nconst DEFAULT_OP_VAULT = \"OpenClaw\";\nconst OP_SERVICE_ACCOUNT_TOKEN_PATH = path.join(os.homedir(), \".openclaw\", \"secrets\", \"op-service-account-token\");\nconst OP_VAULT_PATH = path.join(os.homedir(), \".openclaw\", \"secrets\", \"op-vault-name\");\n\nfunction getOpVault(): string {\n try {\n if (existsSync(OP_VAULT_PATH)) {\n const vault = readFileSync(OP_VAULT_PATH, \"utf-8\").trim();\n if (vault) return vault;\n }\n } catch { /* silent */ }\n return process.env.OP_VAULT ?? DEFAULT_OP_VAULT;\n}\n\n/**\n * Build an env object with the 1Password service account token set,\n * so op CLI works without desktop app integration.\n */\nfunction buildOpEnv(): NodeJS.ProcessEnv {\n // If already set in env, use it\n if (process.env.OP_SERVICE_ACCOUNT_TOKEN) {\n return process.env;\n }\n // Read from the standard OpenClaw secrets file\n try {\n if (existsSync(OP_SERVICE_ACCOUNT_TOKEN_PATH)) {\n const token = readFileSync(OP_SERVICE_ACCOUNT_TOKEN_PATH, \"utf-8\").trim();\n if (token) {\n return { ...process.env, OP_SERVICE_ACCOUNT_TOKEN: token };\n }\n }\n } catch {\n // Silent — fall through to default env\n }\n return process.env;\n}\n\n/**\n * Resolve a provider API key from various OpenClaw secret formats.\n *\n * Supported formats:\n * - Plain string (not a marker) → used as-is\n * - SecretRef object (`{ source, provider, id }`) → resolved via exec/file/env\n * - `\"secretref-managed\"` → looked up from auth profiles and openclaw.json auth config\n * - `\"minimax-oauth\"`, `\"ollama-local\"`, etc. → treated as non-secret markers, skipped\n *\n * Results are cached per provider to avoid repeated exec calls.\n */\nexport async function resolveProviderApiKey(\n providerId: string,\n apiKeyValue: unknown,\n gatewayConfig?: { auth?: { profiles?: Record<string, unknown> } },\n): Promise<string | undefined> {\n // Fast path: plain string that isn't a marker\n if (typeof apiKeyValue === \"string\") {\n if (apiKeyValue === \"secretref-managed\") {\n return resolveSecretRefManaged(providerId, gatewayConfig);\n }\n // Known non-secret markers — skip\n if (\n apiKeyValue.endsWith(\"-oauth\") ||\n apiKeyValue.endsWith(\"-local\") ||\n apiKeyValue === \"lm-studio\" ||\n apiKeyValue.startsWith(\"gcp-\")\n ) {\n return undefined;\n }\n // Looks like an actual key\n return apiKeyValue;\n }\n\n // SecretRef object\n if (isSecretRefObject(apiKeyValue)) {\n return resolveSecretRef(providerId, apiKeyValue as SecretRef);\n }\n\n return undefined;\n}\n\nfunction isSecretRefObject(value: unknown): value is SecretRef {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"source\" in value &&\n \"id\" in value &&\n typeof (value as SecretRef).source === \"string\" &&\n typeof (value as SecretRef).id === \"string\"\n );\n}\n\nasync function resolveSecretRef(\n providerId: string,\n ref: SecretRef,\n): Promise<string | undefined> {\n const cacheKey = `ref:${ref.source}:${ref.provider ?? \"\"}:${ref.id}:${ref.command ?? \"\"}:${(ref.args ?? []).join(\",\")}`;\n const cached = resolvedCache.get(cacheKey);\n if (cached !== undefined) {\n // Don't cache failures permanently — only cache successes\n if (cached !== null) return cached;\n }\n\n let resolved: string | undefined;\n\n try {\n switch (ref.source) {\n case \"exec\":\n resolved = resolveExecSecret(ref);\n break;\n case \"file\":\n resolved = resolveFileSecret(ref);\n break;\n case \"env\":\n resolved = process.env[ref.id] ?? undefined;\n break;\n }\n } catch (err) {\n log.warn(\n `secret resolution failed for provider \"${providerId}\" (${ref.source}/${ref.provider}): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // Only cache successful resolutions; failures are retried next time\n if (resolved) {\n resolvedCache.set(cacheKey, resolved);\n }\n\n if (resolved) {\n log.debug(`resolved API key for provider \"${providerId}\" via ${ref.source}/${ref.provider ?? \"default\"}`);\n }\n\n return resolved;\n}\n\nfunction resolveExecSecret(ref: SecretRef): string | undefined {\n const command = ref.command ?? (ref.provider === \"op\" ? \"op\" : undefined);\n if (!command) {\n log.warn(`exec secret ref has no command and provider \"${ref.provider}\" is not a known exec provider`);\n return undefined;\n }\n\n // Use execFileSync (not execSync) to avoid shell injection —\n // arguments are passed as an array, never interpolated into a shell string.\n const args = ref.args ?? (ref.provider === \"op\" ? [\"read\", ref.id] : [ref.id]);\n const env = ref.provider === \"op\" ? buildOpEnv() : process.env;\n try {\n const result = execFileSync(command, args, {\n encoding: \"utf-8\",\n timeout: 15_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env,\n }).trim();\n return result || undefined;\n } catch (err) {\n log.warn(`exec secret resolution failed (${command}): ${err instanceof Error ? err.message : String(err)}`);\n return undefined;\n }\n}\n\nfunction resolveFileSecret(ref: SecretRef): string | undefined {\n if (ref.provider === \"op\") {\n // OpenClaw's auto-migrate-secrets stores API keys as 1Password items.\n // ref.id can be:\n // - An item title (e.g., \"/agent-models-fireworks-apiKey\") → use `op item get`\n // - An op:// URI (e.g., \"op://vault/item/field\") → use `op read`\n const env = buildOpEnv();\n\n if (ref.id.startsWith(\"op://\")) {\n // Direct op:// secret reference — use `op read`\n try {\n const result = execFileSync(\"op\", [\"read\", ref.id], {\n encoding: \"utf-8\",\n timeout: 15_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env,\n }).trim();\n return result || undefined;\n } catch {\n // Silent — op may not be available\n }\n } else {\n // Item title — use `op item get` with vault\n const itemName = ref.id.replace(/^\\//, \"\");\n const vault = getOpVault();\n try {\n const result = execFileSync(\"op\", [\n \"item\", \"get\", itemName,\n \"--vault\", vault,\n \"--fields\", \"credential\",\n \"--format\", \"json\",\n ], {\n encoding: \"utf-8\",\n timeout: 15_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env,\n }).trim();\n if (result) {\n try {\n const parsed = JSON.parse(result);\n // op returns various shapes:\n // - Single field object: {\"value\": \"sk-...\", ...}\n // - Array of field objects: [{\"value\": \"sk-...\", ...}]\n // - Plain string (older op versions)\n const field = Array.isArray(parsed) ? parsed[0] : parsed;\n const value = field?.value ?? field;\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n } catch {\n if (result.length > 0 && !result.startsWith(\"{\") && !result.startsWith(\"[\")) {\n return result;\n }\n }\n }\n } catch {\n // Silent — op may not be available or item not found\n }\n }\n\n // Fallback: try reading from a local secrets file\n const secretsDir = path.join(os.homedir(), \".openclaw\", \"secrets\");\n const filePath = path.join(secretsDir, ref.id.replace(/^\\//, \"\"));\n if (existsSync(filePath)) {\n try {\n return readFileSync(filePath, \"utf-8\").trim() || undefined;\n } catch {\n // Silent\n }\n }\n } else {\n // Non-OP file provider: treat ref.id as a file path (absolute or relative to secrets dir)\n const filePath = path.isAbsolute(ref.id)\n ? ref.id\n : path.join(os.homedir(), \".openclaw\", \"secrets\", ref.id.replace(/^\\//, \"\"));\n if (existsSync(filePath)) {\n try {\n return readFileSync(filePath, \"utf-8\").trim() || undefined;\n } catch {\n // Silent\n }\n }\n }\n\n return undefined;\n}\n\nfunction resolveSecretRefManaged(\n providerId: string,\n gatewayConfig?: { auth?: { profiles?: Record<string, unknown> } },\n): string | undefined {\n const cacheKey = `managed:${providerId}`;\n const cached = resolvedCache.get(cacheKey);\n if (cached !== undefined && cached !== null) {\n return cached;\n }\n\n let resolved: string | undefined;\n\n // Look up auth profiles for this provider\n const profiles = gatewayConfig?.auth?.profiles;\n if (profiles) {\n // Try provider:default, provider:manual, etc.\n const candidates = [\n `${providerId}:default`,\n `${providerId}:manual`,\n providerId,\n ];\n\n for (const profileKey of candidates) {\n const raw = profiles[profileKey];\n if (!raw || typeof raw !== \"object\") continue;\n const profile = raw as AuthProfile;\n\n // Token-based auth\n if (profile.token) {\n if (typeof profile.token === \"string\" && !profile.token.startsWith(\"{\")) {\n resolved = profile.token;\n break;\n }\n if (isSecretRefObject(profile.token)) {\n // Synchronously resolve — this is at init time\n try {\n resolved = resolveSecretRefSync(providerId, profile.token as SecretRef);\n } catch {\n // Continue to next profile\n }\n if (resolved) break;\n }\n }\n\n // API key auth\n if (profile.apiKey && typeof profile.apiKey === \"string\") {\n resolved = profile.apiKey;\n break;\n }\n\n // Access token (OAuth)\n if (profile.access && typeof profile.access === \"string\") {\n resolved = profile.access;\n break;\n }\n }\n }\n\n // Fall back to environment variables\n if (!resolved) {\n const envCandidates = [\n `${providerId.toUpperCase().replace(/-/g, \"_\")}_API_KEY`,\n `${providerId.toUpperCase().replace(/-/g, \"_\")}_TOKEN`,\n ];\n for (const envVar of envCandidates) {\n if (process.env[envVar]) {\n resolved = process.env[envVar];\n break;\n }\n }\n }\n\n // Only cache successful resolutions; failures are retried next time\n if (resolved) {\n resolvedCache.set(cacheKey, resolved);\n log.debug(`resolved managed API key for provider \"${providerId}\" via auth profile`);\n } else {\n log.debug(`could not resolve managed API key for provider \"${providerId}\"`);\n }\n\n return resolved;\n}\n\nfunction resolveSecretRefSync(\n providerId: string,\n ref: SecretRef,\n): string | undefined {\n const cacheKey = `ref:${ref.source}:${ref.provider ?? \"\"}:${ref.id}:${ref.command ?? \"\"}:${(ref.args ?? []).join(\",\")}`;\n const cached = resolvedCache.get(cacheKey);\n if (cached !== undefined && cached !== null) {\n return cached;\n }\n\n let resolved: string | undefined;\n\n try {\n switch (ref.source) {\n case \"exec\":\n resolved = resolveExecSecret(ref);\n break;\n case \"file\":\n resolved = resolveFileSecret(ref);\n break;\n case \"env\":\n resolved = process.env[ref.id] ?? undefined;\n break;\n }\n } catch (err) {\n log.warn(\n `sync secret resolution failed for provider \"${providerId}\": ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n if (resolved) {\n resolvedCache.set(cacheKey, resolved);\n }\n return resolved;\n}\n\n/**\n * Clear the resolution cache (useful for testing or key rotation).\n */\nexport function clearSecretCache(): void {\n resolvedCache.clear();\n}\n","import { log } from \"./logger.js\";\nimport type { GatewayConfig, ModelProviderConfig, AgentPersona } from \"./types.js\";\nimport { extractJsonCandidates } from \"./json-extract.js\";\nimport {\n buildChatCompletionTokenLimit,\n shouldAssumeOpenAiChatCompletions,\n} from \"./openai-chat-compat.js\";\nimport { resolveProviderApiKey } from \"./resolve-provider-secret.js\";\n\nexport interface FallbackLlmOptions {\n temperature?: number;\n maxTokens?: number;\n timeoutMs?: number;\n /** Override which agent persona's model chain to use (by ID from agents.list[]). */\n agentId?: string;\n}\n\nexport interface FallbackLlmResponse {\n content: string;\n modelUsed: string;\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n };\n}\n\ninterface ModelRef {\n providerId: string;\n modelId: string;\n providerConfig: ModelProviderConfig;\n modelString: string;\n}\n\n/**\n * Generic fallback LLM client that uses the gateway's default AI configuration\n * and walks through the full fallback chain (primary + fallbacks).\n * Supports OpenAI and Anthropic API formats.\n */\nexport class FallbackLlmClient {\n private gatewayConfig: GatewayConfig | undefined;\n\n constructor(gatewayConfig?: GatewayConfig) {\n this.gatewayConfig = gatewayConfig;\n }\n\n /**\n * Check if fallback is available (gateway config has at least one model).\n */\n isAvailable(agentId?: string): boolean {\n const models = this.getModelChain(agentId);\n return models.length > 0;\n }\n\n /**\n * Make a chat completion request using the gateway's default AI chain.\n * Tries primary first, then each fallback in order.\n * When agentId is provided, uses that agent persona's model chain instead of defaults.\n */\n async chatCompletion(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions = {},\n ): Promise<FallbackLlmResponse | null> {\n const models = this.getModelChain(options.agentId);\n if (models.length === 0) {\n log.warn(\"fallback LLM: no models configured in gateway\");\n return null;\n }\n\n const runChain = async (): Promise<FallbackLlmResponse | null> => {\n // Try each model in the chain\n for (let i = 0; i < models.length; i++) {\n const model = models[i];\n const isFallback = i > 0;\n\n try {\n const result = await this.tryModel(model, messages, options);\n if (result) {\n if (isFallback) {\n log.info(`fallback LLM: succeeded using ${model.modelString} (fallback ${i})`);\n }\n return {\n content: result.content,\n modelUsed: model.modelString,\n usage: result.usage,\n };\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`fallback LLM: ${model.modelString} failed (${errorMsg}), trying next...`);\n // Continue to next model in chain\n }\n }\n\n log.warn(`fallback LLM: all ${models.length} models in chain failed`);\n return null;\n };\n\n if (typeof options.timeoutMs === \"number\") {\n if (options.timeoutMs <= 0) {\n log.warn(\"fallback LLM: timed out before request started\");\n return null;\n }\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n runChain(),\n new Promise<null>((resolve) => {\n timeoutHandle = setTimeout(() => {\n log.warn(`fallback LLM: timed out after ${options.timeoutMs}ms`);\n resolve(null);\n }, options.timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n }\n }\n\n return await runChain();\n }\n\n /**\n * Make a request with structured output (Zod schema).\n * Returns parsed JSON or null on failure.\n */\n async parseWithSchema<T>(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n schema: { parse: (data: unknown) => T },\n options: FallbackLlmOptions = {},\n ): Promise<T | null> {\n const detailed = await this.parseWithSchemaDetailed(messages, schema, options);\n return detailed?.result ?? null;\n }\n\n /**\n * Like parseWithSchema but also returns the model that was used,\n * so callers can emit accurate trace events.\n */\n async parseWithSchemaDetailed<T>(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n schema: { parse: (data: unknown) => T },\n options: FallbackLlmOptions = {},\n ): Promise<{ result: T; modelUsed: string } | null> {\n const response = await this.chatCompletion(messages, options);\n if (!response?.content) return null;\n\n try {\n const candidates = extractJsonCandidates(response.content);\n for (const c of candidates) {\n try {\n const parsed = JSON.parse(c);\n return { result: schema.parse(parsed), modelUsed: response.modelUsed };\n } catch {\n // keep trying other candidates\n }\n }\n return null;\n } catch (err) {\n log.warn(\"fallback LLM: failed to parse structured output:\", err);\n return null;\n }\n }\n\n /**\n * Get the full model chain from gateway config.\n * Returns array of models in order: [primary, fallback1, fallback2, ...]\n *\n * When agentId is provided, looks up the matching entry in agents.list[]\n * and uses that persona's model chain. Falls back to agents.defaults.model\n * if agentId is not found or not provided.\n */\n private getModelChain(agentId?: string): ModelRef[] {\n const chain: ModelRef[] = [];\n const providers = this.gatewayConfig?.models?.providers;\n\n if (!providers) return chain;\n\n // Resolve the model config: agent persona chain or global defaults\n let modelConfig: { primary?: string; fallbacks?: string[] } | undefined;\n\n if (agentId) {\n const persona = this.gatewayConfig?.agents?.list?.find(\n (a) => a.id === agentId,\n );\n if (persona?.model) {\n modelConfig = persona.model;\n log.debug(`fallback LLM: using agent persona \"${agentId}\" model chain`);\n } else {\n log.warn(\n `fallback LLM: agent persona \"${agentId}\" not found or has no model config, falling back to defaults`,\n );\n }\n }\n\n if (!modelConfig) {\n modelConfig = this.gatewayConfig?.agents?.defaults?.model;\n }\n\n // Build list of model strings: primary + fallbacks\n const modelStrings: string[] = [];\n\n if (modelConfig?.primary) {\n modelStrings.push(modelConfig.primary);\n }\n\n if (Array.isArray(modelConfig?.fallbacks)) {\n for (const fb of modelConfig.fallbacks) {\n if (typeof fb === \"string\" && !modelStrings.includes(fb)) {\n modelStrings.push(fb);\n }\n }\n }\n\n // Parse each model string and look up provider config\n for (const modelString of modelStrings) {\n const modelRef = this.parseModelString(modelString, providers);\n if (modelRef) {\n chain.push(modelRef);\n }\n }\n\n return chain;\n }\n\n /**\n * Parse a \"provider/model\" string and look up its config.\n */\n private parseModelString(\n modelString: string,\n providers: Record<string, ModelProviderConfig>,\n ): ModelRef | null {\n // Parse \"provider/model\" format (e.g., \"openai/gpt-5.2\", \"anthropic/claude-opus-4-6\")\n const parts = modelString.split(\"/\");\n if (parts.length < 2) {\n log.warn(`fallback LLM: invalid model format: ${modelString}`);\n return null;\n }\n\n const providerId = parts[0];\n const modelId = parts.slice(1).join(\"/\"); // Handle cases like \"openai/gpt-5.2-turbo\"\n\n const providerConfig = providers[providerId];\n if (!providerConfig) {\n log.warn(`fallback LLM: provider not found: ${providerId}`);\n return null;\n }\n\n return { providerId, modelId, providerConfig, modelString };\n }\n\n /**\n * Resolve the API key for a provider, handling OpenClaw secret ref formats.\n * Results are cached per provider so exec calls only happen once.\n */\n private async resolveApiKey(\n providerId: string,\n providerConfig: ModelProviderConfig,\n ): Promise<string | undefined> {\n return resolveProviderApiKey(\n providerId,\n providerConfig.apiKey,\n this.gatewayConfig as { auth?: { profiles?: Record<string, unknown> } } | undefined,\n );\n }\n\n /**\n * Try to call a single model.\n */\n private async tryModel(\n model: ModelRef,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n // Resolve the API key from secret refs before making the call.\n // If the raw key looks like an unresolved secret ref and resolution fails,\n // skip this provider entirely so the chain falls through to the next.\n const rawKey = model.providerConfig.apiKey;\n const needsResolution = rawKey === \"secretref-managed\"\n || (typeof rawKey === \"object\" && rawKey !== null);\n const resolvedApiKey = await this.resolveApiKey(model.providerId, model.providerConfig);\n\n if (needsResolution && !resolvedApiKey) {\n throw new Error(`API key for provider \"${model.providerId}\" could not be resolved from secret ref`);\n }\n\n const configWithResolvedKey = resolvedApiKey\n ? { ...model.providerConfig, apiKey: resolvedApiKey }\n : model.providerConfig;\n\n switch (model.providerConfig.api) {\n case \"anthropic-messages\":\n return await this.callAnthropic(configWithResolvedKey, model.modelId, messages, options);\n case \"openai-completions\":\n default:\n return await this.callOpenAI(\n configWithResolvedKey,\n model.modelId,\n messages,\n options,\n shouldAssumeOpenAiChatCompletions(model.providerConfig.baseUrl),\n );\n }\n }\n\n /**\n * Call OpenAI-compatible API.\n */\n private async callOpenAI(\n config: ModelProviderConfig,\n modelId: string,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n assumeOpenAI: boolean,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n const base = config.baseUrl.replace(/\\/$/, \"\");\n const url = base.endsWith(\"/v1\")\n ? `${base}/chat/completions`\n : `${base}/v1/chat/completions`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...config.headers,\n };\n\n // Handle auth — apiKey is already resolved to a string by tryModel()\n if (config.apiKey && typeof config.apiKey === \"string\") {\n if (config.authHeader !== false) {\n headers[\"Authorization\"] = `Bearer ${config.apiKey}`;\n }\n }\n\n const body = {\n model: modelId,\n messages,\n temperature: options.temperature ?? 0.3,\n ...buildChatCompletionTokenLimit(modelId, options.maxTokens ?? 4096, {\n assumeOpenAI,\n }),\n };\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`OpenAI API error: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n choices: Array<{\n message: {\n content: string;\n };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n };\n\n const content = data.choices?.[0]?.message?.content;\n if (!content) {\n throw new Error(\"Empty response from OpenAI API\");\n }\n\n return {\n content,\n usage: data.usage\n ? {\n inputTokens: data.usage.prompt_tokens,\n outputTokens: data.usage.completion_tokens,\n totalTokens: data.usage.total_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Call Anthropic Messages API.\n */\n private async callAnthropic(\n config: ModelProviderConfig,\n modelId: string,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n const url = `${config.baseUrl.replace(/\\/$/, \"\")}/messages`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"anthropic-version\": \"2023-06-01\",\n ...config.headers,\n };\n\n // Handle auth - Anthropic uses x-api-key header (apiKey resolved by tryModel)\n if (config.apiKey && typeof config.apiKey === \"string\") {\n headers[\"x-api-key\"] = config.apiKey;\n }\n\n // Extract system message (Anthropic handles it separately)\n const systemMessage = messages.find((m) => m.role === \"system\")?.content;\n const nonSystemMessages = messages.filter((m) => m.role !== \"system\");\n\n // Convert messages to Anthropic format\n const anthropicMessages = nonSystemMessages.map((m) => ({\n role: m.role,\n content: m.content,\n }));\n\n const body: Record<string, unknown> = {\n model: modelId,\n messages: anthropicMessages,\n max_tokens: options.maxTokens ?? 4096,\n temperature: options.temperature ?? 0.3,\n };\n\n if (systemMessage) {\n body.system = systemMessage;\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Anthropic API error: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n content: Array<{\n type: string;\n text: string;\n }>;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n };\n };\n\n const content = data.content?.[0]?.text;\n if (!content) {\n throw new Error(\"Empty response from Anthropic API\");\n }\n\n return {\n content,\n usage: data.usage\n ? {\n inputTokens: data.usage.input_tokens,\n outputTokens: data.usage.output_tokens,\n totalTokens: (data.usage.input_tokens ?? 0) + (data.usage.output_tokens ?? 0),\n }\n : undefined,\n };\n }\n}\n"],"mappings":"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","names":[]}