@librechat/agents 3.2.37 → 3.2.39

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.
Files changed (78) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +25 -8
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +7 -4
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +20 -4
  6. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  7. package/dist/cjs/llm/bedrock/index.cjs +7 -1
  8. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  9. package/dist/cjs/llm/bedrock/toolCache.cjs +5 -4
  10. package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
  11. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +34 -17
  12. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  13. package/dist/cjs/llm/openrouter/index.cjs +1 -0
  14. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  15. package/dist/cjs/llm/openrouter/toolCache.cjs +18 -5
  16. package/dist/cjs/llm/openrouter/toolCache.cjs.map +1 -1
  17. package/dist/cjs/main.cjs +4 -0
  18. package/dist/cjs/messages/anthropicToolCache.cjs +75 -13
  19. package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -1
  20. package/dist/cjs/messages/cache.cjs +91 -35
  21. package/dist/cjs/messages/cache.cjs.map +1 -1
  22. package/dist/cjs/summarization/node.cjs +3 -2
  23. package/dist/cjs/summarization/node.cjs.map +1 -1
  24. package/dist/esm/agents/AgentContext.mjs +26 -9
  25. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  26. package/dist/esm/graphs/Graph.mjs +8 -5
  27. package/dist/esm/graphs/Graph.mjs.map +1 -1
  28. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +20 -4
  29. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  30. package/dist/esm/llm/bedrock/index.mjs +7 -1
  31. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  32. package/dist/esm/llm/bedrock/toolCache.mjs +5 -4
  33. package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
  34. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +34 -17
  35. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  36. package/dist/esm/llm/openrouter/index.mjs +1 -0
  37. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  38. package/dist/esm/llm/openrouter/toolCache.mjs +18 -5
  39. package/dist/esm/llm/openrouter/toolCache.mjs.map +1 -1
  40. package/dist/esm/main.mjs +2 -2
  41. package/dist/esm/messages/anthropicToolCache.mjs +75 -13
  42. package/dist/esm/messages/anthropicToolCache.mjs.map +1 -1
  43. package/dist/esm/messages/cache.mjs +88 -36
  44. package/dist/esm/messages/cache.mjs.map +1 -1
  45. package/dist/esm/summarization/node.mjs +4 -3
  46. package/dist/esm/summarization/node.mjs.map +1 -1
  47. package/dist/types/agents/AgentContext.d.ts +11 -0
  48. package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +2 -0
  49. package/dist/types/llm/bedrock/index.d.ts +13 -0
  50. package/dist/types/llm/bedrock/toolCache.d.ts +2 -1
  51. package/dist/types/llm/openrouter/index.d.ts +8 -0
  52. package/dist/types/llm/openrouter/toolCache.d.ts +2 -1
  53. package/dist/types/messages/anthropicToolCache.d.ts +2 -1
  54. package/dist/types/messages/cache.d.ts +49 -5
  55. package/dist/types/types/llm.d.ts +14 -0
  56. package/package.json +7 -5
  57. package/src/agents/AgentContext.ts +64 -17
  58. package/src/agents/__tests__/AgentContext.anthropic.live.test.ts +6 -2
  59. package/src/agents/__tests__/AgentContext.bedrock.live.test.ts +7 -5
  60. package/src/agents/__tests__/AgentContext.openrouter.live.test.ts +1 -1
  61. package/src/agents/__tests__/AgentContext.test.ts +31 -19
  62. package/src/agents/__tests__/promptCacheLiveHelpers.ts +6 -2
  63. package/src/graphs/Graph.ts +40 -4
  64. package/src/llm/anthropic/utils/message_inputs.ts +33 -6
  65. package/src/llm/bedrock/index.ts +21 -1
  66. package/src/llm/bedrock/llm.spec.ts +61 -0
  67. package/src/llm/bedrock/toolCache.test.ts +24 -0
  68. package/src/llm/bedrock/toolCache.ts +12 -7
  69. package/src/llm/bedrock/utils/message_inputs.ts +57 -40
  70. package/src/llm/openrouter/index.ts +9 -0
  71. package/src/llm/openrouter/toolCache.test.ts +52 -1
  72. package/src/llm/openrouter/toolCache.ts +40 -6
  73. package/src/messages/__tests__/anthropicToolCache.test.ts +168 -0
  74. package/src/messages/anthropicToolCache.ts +118 -15
  75. package/src/messages/cache.test.ts +175 -0
  76. package/src/messages/cache.ts +133 -48
  77. package/src/summarization/node.ts +21 -2
  78. package/src/types/llm.ts +14 -0
@@ -1 +1 @@
1
- {"version":3,"file":"anthropicToolCache.mjs","names":[],"sources":["../../../src/messages/anthropicToolCache.ts"],"sourcesContent":["/**\n * Anthropic prompt-caching helper for the `tools[]` request field.\n *\n * Anthropic accepts `cache_control: { type: 'ephemeral' }` on individual\n * tool definitions. Whichever tool carries the marker becomes the end of\n * a cached prefix: `tools[0..N]` (everything up to and including the\n * marked tool) is cached and rebated on subsequent matching requests.\n *\n * For agents that mix static and deferred (lazily-discovered) tools, the\n * winning configuration is:\n *\n * 1. Stable-partition tools so all *static* (non-deferred) tools come\n * first and discovered-deferred tools come last.\n * 2. Stamp `cache_control` on the LAST static tool.\n *\n * That way, the cached prefix covers exactly the static tool inventory.\n * Discovered tools that show up later (or vary turn-to-turn as new ones\n * get discovered) never invalidate the prefix because they sit *after*\n * the breakpoint.\n *\n * LangChain's Anthropic adapter passes the marker through via\n * `tool.extras.cache_control` for custom tools, while Anthropic built-ins\n * require direct `cache_control`. Either way, we stamp a fresh wrapper —\n * never mutating the original tool instance, since callers may share them\n * across runs.\n */\n\nimport type { GraphTools } from '@/types';\n\nconst ANTHROPIC_BUILT_IN_TOOL_PREFIXES = [\n 'text_editor_',\n 'computer_',\n 'bash_',\n 'web_search_',\n 'web_fetch_',\n 'str_replace_editor_',\n 'str_replace_based_edit_tool_',\n 'code_execution_',\n 'memory_',\n 'tool_search_',\n 'mcp_toolset',\n] as const;\n\nconst CACHE_CONTROL = { type: 'ephemeral' as const };\n\ntype AnthropicToolCacheCandidate = {\n name?: unknown;\n type?: unknown;\n extras?: Record<string, unknown>;\n cache_control?: unknown;\n};\n\nfunction isAnthropicBuiltInTool(\n tool: AnthropicToolCacheCandidate\n): tool is AnthropicToolCacheCandidate & { type: string } {\n const { type } = tool;\n return (\n typeof type === 'string' &&\n ANTHROPIC_BUILT_IN_TOOL_PREFIXES.some((prefix) => type.startsWith(prefix))\n );\n}\n\nfunction hasCacheControl(tool: AnthropicToolCacheCandidate): boolean {\n if (isAnthropicBuiltInTool(tool)) {\n return tool.cache_control != null;\n }\n return tool.extras?.cache_control != null;\n}\n\nfunction markCacheControl(\n tool: AnthropicToolCacheCandidate\n): AnthropicToolCacheCandidate {\n const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;\n if (isAnthropicBuiltInTool(tool)) {\n const wrapped = { ...tool };\n delete wrapped.extras;\n return Object.assign(Object.create(prototype), wrapped, {\n cache_control: CACHE_CONTROL,\n });\n }\n\n return Object.assign(Object.create(prototype), tool, {\n extras: {\n ...(tool.extras ?? {}),\n cache_control: CACHE_CONTROL,\n },\n });\n}\n\n/**\n * Returns a callable that reports whether a given tool *name* is deferred\n * according to the supplied registry of tool definitions. Tools without\n * a registry entry are treated as non-deferred (i.e. static), matching\n * the host-supplied `graphTools` semantics elsewhere in the SDK.\n */\nexport function makeIsDeferred(\n toolDefinitions:\n | ReadonlyArray<{ name: string; defer_loading?: boolean }>\n | undefined\n): (toolName: string) => boolean {\n if (toolDefinitions == null || toolDefinitions.length === 0) {\n return () => false;\n }\n const deferred = new Set<string>();\n for (const def of toolDefinitions) {\n if (def.defer_loading === true) deferred.add(def.name);\n }\n if (deferred.size === 0) return () => false;\n return (name) => deferred.has(name);\n}\n\n/**\n * Stable-partition `tools` into [static..., deferred...] and stamp the\n * Anthropic `cache_control: ephemeral` marker on the last static tool.\n *\n * If `tools` is undefined or empty, or no entry has a usable `.name`,\n * returns the input unchanged. If there are no static tools at all,\n * also returns unchanged (nothing to cache).\n *\n * The original tool instances are never mutated. The marked entry is a\n * shallow wrapper that preserves the prototype chain so downstream\n * `instanceof` checks still pass. For custom tools, `extras` is merged\n * so any existing `providerToolDefinition` / other extras are kept.\n */\nexport function partitionAndMarkAnthropicToolCache(\n tools: GraphTools | undefined,\n isDeferred: (toolName: string) => boolean\n): GraphTools | undefined {\n if (tools == null || tools.length === 0) return tools;\n\n // Use unknown[] internally to avoid GraphTools' union-array variance\n // (each member is one of three array types). We cast back to\n // GraphTools when returning.\n const staticTools: unknown[] = [];\n const deferredTools: unknown[] = [];\n\n for (const tool of tools) {\n const name = (tool as { name?: unknown }).name;\n if (typeof name === 'string' && isDeferred(name)) {\n deferredTools.push(tool);\n } else {\n staticTools.push(tool);\n }\n }\n\n if (staticTools.length === 0) {\n return tools;\n }\n\n const last = staticTools[\n staticTools.length - 1\n ] as AnthropicToolCacheCandidate;\n // Already marked? Don't double-clone.\n if (hasCacheControl(last)) {\n if (deferredTools.length === 0) return tools;\n return [...staticTools, ...deferredTools] as GraphTools;\n }\n\n staticTools[staticTools.length - 1] = markCacheControl(last);\n return [...staticTools, ...deferredTools] as GraphTools;\n}\n"],"mappings":";AA6BA,MAAM,mCAAmC;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,gBAAgB,EAAE,MAAM,YAAqB;AASnD,SAAS,uBACP,MACwD;CACxD,MAAM,EAAE,SAAS;CACjB,OACE,OAAO,SAAS,YAChB,iCAAiC,MAAM,WAAW,KAAK,WAAW,MAAM,CAAC;AAE7E;AAEA,SAAS,gBAAgB,MAA4C;CACnE,IAAI,uBAAuB,IAAI,GAC7B,OAAO,KAAK,iBAAiB;CAE/B,OAAO,KAAK,QAAQ,iBAAiB;AACvC;AAEA,SAAS,iBACP,MAC6B;CAC7B,MAAM,YAAY,OAAO,eAAe,IAAI,KAAK,OAAO;CACxD,IAAI,uBAAuB,IAAI,GAAG;EAChC,MAAM,UAAU,EAAE,GAAG,KAAK;EAC1B,OAAO,QAAQ;EACf,OAAO,OAAO,OAAO,OAAO,OAAO,SAAS,GAAG,SAAS,EACtD,eAAe,cACjB,CAAC;CACH;CAEA,OAAO,OAAO,OAAO,OAAO,OAAO,SAAS,GAAG,MAAM,EACnD,QAAQ;EACN,GAAI,KAAK,UAAU,CAAC;EACpB,eAAe;CACjB,EACF,CAAC;AACH;;;;;;;AAQA,SAAgB,eACd,iBAG+B;CAC/B,IAAI,mBAAmB,QAAQ,gBAAgB,WAAW,GACxD,aAAa;CAEf,MAAM,2BAAW,IAAI,IAAY;CACjC,KAAK,MAAM,OAAO,iBAChB,IAAI,IAAI,kBAAkB,MAAM,SAAS,IAAI,IAAI,IAAI;CAEvD,IAAI,SAAS,SAAS,GAAG,aAAa;CACtC,QAAQ,SAAS,SAAS,IAAI,IAAI;AACpC;;;;;;;;;;;;;;AAeA,SAAgB,mCACd,OACA,YACwB;CACxB,IAAI,SAAS,QAAQ,MAAM,WAAW,GAAG,OAAO;CAKhD,MAAM,cAAyB,CAAC;CAChC,MAAM,gBAA2B,CAAC;CAElC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,OAAQ,KAA4B;EAC1C,IAAI,OAAO,SAAS,YAAY,WAAW,IAAI,GAC7C,cAAc,KAAK,IAAI;OAEvB,YAAY,KAAK,IAAI;CAEzB;CAEA,IAAI,YAAY,WAAW,GACzB,OAAO;CAGT,MAAM,OAAO,YACX,YAAY,SAAS;CAGvB,IAAI,gBAAgB,IAAI,GAAG;EACzB,IAAI,cAAc,WAAW,GAAG,OAAO;EACvC,OAAO,CAAC,GAAG,aAAa,GAAG,aAAa;CAC1C;CAEA,YAAY,YAAY,SAAS,KAAK,iBAAiB,IAAI;CAC3D,OAAO,CAAC,GAAG,aAAa,GAAG,aAAa;AAC1C"}
1
+ {"version":3,"file":"anthropicToolCache.mjs","names":[],"sources":["../../../src/messages/anthropicToolCache.ts"],"sourcesContent":["/**\n * Anthropic prompt-caching helper for the `tools[]` request field.\n *\n * Anthropic accepts `cache_control: { type: 'ephemeral' }` on individual\n * tool definitions. Whichever tool carries the marker becomes the end of\n * a cached prefix: `tools[0..N]` (everything up to and including the\n * marked tool) is cached and rebated on subsequent matching requests.\n *\n * For agents that mix static and deferred (lazily-discovered) tools, the\n * winning configuration is:\n *\n * 1. Stable-partition tools so all *static* (non-deferred) tools come\n * first and discovered-deferred tools come last.\n * 2. Stamp `cache_control` on the LAST static tool.\n *\n * That way, the cached prefix covers exactly the static tool inventory.\n * Discovered tools that show up later (or vary turn-to-turn as new ones\n * get discovered) never invalidate the prefix because they sit *after*\n * the breakpoint.\n *\n * LangChain's Anthropic adapter passes the marker through via\n * `tool.extras.cache_control` for custom tools, while Anthropic built-ins\n * require direct `cache_control`. Either way, we stamp a fresh wrapper —\n * never mutating the original tool instance, since callers may share them\n * across runs.\n */\n\nimport type { GraphTools } from '@/types';\nimport {\n buildAnthropicCacheControl,\n type PromptCacheTtl,\n} from '@/messages/cache';\n\nconst ANTHROPIC_BUILT_IN_TOOL_PREFIXES = [\n 'text_editor_',\n 'computer_',\n 'bash_',\n 'web_search_',\n 'web_fetch_',\n 'str_replace_editor_',\n 'str_replace_based_edit_tool_',\n 'code_execution_',\n 'memory_',\n 'tool_search_',\n 'mcp_toolset',\n] as const;\n\ntype AnthropicToolCacheCandidate = {\n name?: unknown;\n type?: unknown;\n extras?: Record<string, unknown>;\n cache_control?: unknown;\n};\n\nfunction isAnthropicBuiltInTool(\n tool: AnthropicToolCacheCandidate\n): tool is AnthropicToolCacheCandidate & { type: string } {\n const { type } = tool;\n return (\n typeof type === 'string' &&\n ANTHROPIC_BUILT_IN_TOOL_PREFIXES.some((prefix) => type.startsWith(prefix))\n );\n}\n\n/**\n * Whether a tool already carries a cache breakpoint. Built-ins use a direct\n * `cache_control`; custom tools normally carry it under `extras`, but a\n * caller-supplied Anthropic-native tool object can also put it directly on the\n * block — check both so stale markers are never missed.\n */\nfunction hasCacheControl(tool: AnthropicToolCacheCandidate): boolean {\n if (isAnthropicBuiltInTool(tool)) {\n return tool.cache_control != null;\n }\n return tool.cache_control != null || tool.extras?.cache_control != null;\n}\n\n/**\n * Return the `cache_control` from the location that actually reaches the\n * Anthropic payload for this tool's shape: directly on the block for\n * provider-shaped tools (built-ins and raw Anthropic tools), or under `extras`\n * for LangChain custom tools (the adapter promotes only that). Returns\n * `undefined` when no marker sits in the effective location — a marker in the\n * wrong place (e.g. a direct marker on a custom tool) does not count.\n */\nfunction getEffectiveCacheControl(\n tool: AnthropicToolCacheCandidate\n): { ttl?: unknown } | undefined {\n return (\n isProviderShapedTool(tool) ? tool.cache_control : tool.extras?.cache_control\n ) as { ttl?: unknown } | undefined;\n}\n\n/**\n * Return a clone of `tool` with any `cache_control` removed — both the direct\n * block marker and the `extras` marker — preserving the prototype chain. Used\n * to clear stray markers off tools that must not anchor a competing breakpoint.\n */\nfunction stripCacheControl(\n tool: AnthropicToolCacheCandidate\n): AnthropicToolCacheCandidate {\n const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;\n const wrapped = { ...tool };\n delete wrapped.cache_control;\n if (wrapped.extras != null) {\n wrapped.extras = { ...wrapped.extras };\n delete wrapped.extras.cache_control;\n }\n return Object.assign(Object.create(prototype), wrapped);\n}\n\n/**\n * Whether `tool` is already in the Anthropic provider payload shape — an\n * Anthropic built-in or a raw Anthropic tool object (has `input_schema`). These\n * carry `cache_control` directly on the block; the LangChain adapter does NOT\n * promote `extras.cache_control` for them. LangChain StructuredTools, by\n * contrast, expose the marker via `extras`.\n */\nfunction isProviderShapedTool(tool: AnthropicToolCacheCandidate): boolean {\n return (\n isAnthropicBuiltInTool(tool) ||\n 'input_schema' in (tool as Record<string, unknown>)\n );\n}\n\nfunction markCacheControl(\n tool: AnthropicToolCacheCandidate,\n ttl?: PromptCacheTtl\n): AnthropicToolCacheCandidate {\n const cacheControl = buildAnthropicCacheControl(ttl);\n const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;\n if (isProviderShapedTool(tool)) {\n // Built-ins and raw Anthropic tool objects carry cache_control directly on\n // the block; `extras` is not promoted onto the payload for these shapes.\n const wrapped = { ...tool };\n delete wrapped.extras;\n return Object.assign(Object.create(prototype), wrapped, {\n cache_control: cacheControl,\n });\n }\n\n // LangChain custom tools: drop any direct marker and expose the breakpoint via\n // `extras`, which the Anthropic adapter promotes onto the payload.\n const wrapped = { ...tool };\n delete wrapped.cache_control;\n return Object.assign(Object.create(prototype), wrapped, {\n extras: {\n ...(tool.extras ?? {}),\n cache_control: cacheControl,\n },\n });\n}\n\n/**\n * Returns a callable that reports whether a given tool *name* is deferred\n * according to the supplied registry of tool definitions. Tools without\n * a registry entry are treated as non-deferred (i.e. static), matching\n * the host-supplied `graphTools` semantics elsewhere in the SDK.\n */\nexport function makeIsDeferred(\n toolDefinitions:\n | ReadonlyArray<{ name: string; defer_loading?: boolean }>\n | undefined\n): (toolName: string) => boolean {\n if (toolDefinitions == null || toolDefinitions.length === 0) {\n return () => false;\n }\n const deferred = new Set<string>();\n for (const def of toolDefinitions) {\n if (def.defer_loading === true) deferred.add(def.name);\n }\n if (deferred.size === 0) return () => false;\n return (name) => deferred.has(name);\n}\n\n/**\n * Stable-partition `tools` into [static..., deferred...] and stamp the\n * Anthropic `cache_control: ephemeral` marker on the last static tool.\n *\n * If `tools` is undefined or empty, or no entry has a usable `.name`,\n * returns the input unchanged. If there are no static tools at all,\n * also returns unchanged (nothing to cache).\n *\n * The original tool instances are never mutated. The marked entry is a\n * shallow wrapper that preserves the prototype chain so downstream\n * `instanceof` checks still pass. For custom tools, `extras` is merged\n * so any existing `providerToolDefinition` / other extras are kept.\n */\nexport function partitionAndMarkAnthropicToolCache(\n tools: GraphTools | undefined,\n isDeferred: (toolName: string) => boolean,\n ttl?: PromptCacheTtl\n): GraphTools | undefined {\n if (tools == null || tools.length === 0) return tools;\n\n // Use unknown[] internally to avoid GraphTools' union-array variance\n // (each member is one of three array types). We cast back to\n // GraphTools when returning.\n const staticTools: unknown[] = [];\n const deferredTools: unknown[] = [];\n\n for (const tool of tools) {\n const name = (tool as { name?: unknown }).name;\n if (typeof name === 'string' && isDeferred(name)) {\n deferredTools.push(tool);\n } else {\n staticTools.push(tool);\n }\n }\n\n // Anthropic serializes ALL tools before system/messages, so a stray\n // cache_control on any tool — static or deferred — that survives the resolved\n // breakpoint would violate the longer-TTL-first ordering. Strip stale markers\n // off the deferred tools first (they sit after the breakpoint but still before\n // system/messages, and the all-deferred case has no breakpoint of its own).\n let mutated = false;\n for (let i = 0; i < deferredTools.length; i++) {\n const candidate = deferredTools[i] as AnthropicToolCacheCandidate;\n if (hasCacheControl(candidate)) {\n deferredTools[i] = stripCacheControl(candidate);\n mutated = true;\n }\n }\n\n if (staticTools.length === 0) {\n return mutated ? ([...deferredTools] as GraphTools) : tools;\n }\n\n // Strip any stray cache_control off the earlier static tools so a leftover\n // 5-minute marker never sits ahead of the resolved breakpoint, then stamp (or\n // re-stamp) only the last static tool with the resolved TTL.\n for (let i = 0; i < staticTools.length - 1; i++) {\n const candidate = staticTools[i] as AnthropicToolCacheCandidate;\n if (hasCacheControl(candidate)) {\n staticTools[i] = stripCacheControl(candidate);\n mutated = true;\n }\n }\n\n const last = staticTools[\n staticTools.length - 1\n ] as AnthropicToolCacheCandidate;\n const desiredTtl: '1h' | undefined = ttl === '1h' ? '1h' : undefined;\n // \"Already correct\" requires a marker IN the effective location (one that\n // reaches the payload for this tool's shape) carrying the resolved TTL. A\n // marker in the wrong place — e.g. a direct `cache_control` on a LangChain\n // custom tool — is ineffective and must be re-stamped.\n const effective = getEffectiveCacheControl(last);\n const lastAlreadyCorrect =\n effective != null &&\n (effective.ttl === '1h' ? '1h' : undefined) === desiredTtl;\n if (!lastAlreadyCorrect) {\n staticTools[staticTools.length - 1] = markCacheControl(last, ttl);\n mutated = true;\n }\n\n // Return the original reference only when nothing changed AND partitioning\n // moved nothing. When deferred tools exist they must end up after the cache\n // breakpoint, so the partitioned array is returned even if no marker changed.\n if (!mutated && deferredTools.length === 0) {\n return tools;\n }\n return [...staticTools, ...deferredTools] as GraphTools;\n}\n"],"mappings":"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"}
@@ -4,6 +4,42 @@ import { withMessageRole } from "./format.mjs";
4
4
  import { AIMessage, BaseMessage, HumanMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
5
5
  //#region src/messages/cache.ts
6
6
  /**
7
+ * Default TTL applied wherever a prompt-cache breakpoint is added. The 1-hour
8
+ * extended cache keeps prefixes warm across longer gaps between turns, at the
9
+ * cost of a higher one-time cache-write multiplier (2x vs 1.25x for 5m).
10
+ */
11
+ const DEFAULT_PROMPT_CACHE_TTL = "1h";
12
+ /**
13
+ * Resolve an optionally-configured TTL to a concrete value, defaulting to the
14
+ * 1-hour extended cache. Used at the Anthropic/Bedrock prompt-cache call sites.
15
+ */
16
+ function resolvePromptCacheTtl(ttl) {
17
+ return ttl ?? "1h";
18
+ }
19
+ /**
20
+ * Build an Anthropic `cache_control` breakpoint for the given TTL. `'5m'` (or
21
+ * `undefined`) omits the `ttl` field — that is the provider default, so the
22
+ * payload stays byte-identical to the legacy 5-minute marker. `'1h'` adds the
23
+ * explicit extended-cache `ttl`.
24
+ */
25
+ function buildAnthropicCacheControl(ttl) {
26
+ return ttl === "1h" ? {
27
+ type: "ephemeral",
28
+ ttl: "1h"
29
+ } : { type: "ephemeral" };
30
+ }
31
+ /**
32
+ * Build a Bedrock `cachePoint` for the given TTL. Mirrors
33
+ * {@link buildAnthropicCacheControl}: `'5m'`/`undefined` omits `ttl` (the
34
+ * legacy default), `'1h'` adds the extended-cache `ttl`.
35
+ */
36
+ function buildBedrockCachePoint(ttl) {
37
+ return ttl === "1h" ? {
38
+ type: "default",
39
+ ttl: "1h"
40
+ } : { type: "default" };
41
+ }
42
+ /**
7
43
  * Deep clones a message's content to prevent mutation of the original.
8
44
  */
9
45
  function deepCloneContent(content) {
@@ -56,25 +92,41 @@ function cloneMessage(message, content) {
56
92
  }
57
93
  return cloned;
58
94
  }
59
- function stripAnthropicCacheControlFromBlocks(content) {
95
+ /**
96
+ * Sanitize a Bedrock system message: strip Anthropic `cache_control` (Bedrock
97
+ * conversion can't use it) and normalize any existing `cachePoint` to the
98
+ * resolved TTL. The normalization matters because Bedrock requires longer-TTL
99
+ * checkpoints to appear before shorter ones — a stale 5-minute system cachePoint
100
+ * (host-supplied or carried over from a 5m config) left ahead of a 1-hour
101
+ * message tail would make the request invalid. System messages are never
102
+ * anchored as the tail breakpoint; this only fixes markers already present.
103
+ */
104
+ function sanitizeBedrockSystemMessage(message, ttl) {
105
+ const content = message.content;
106
+ if (!Array.isArray(content)) return message;
107
+ const sanitized = [];
60
108
  let modified = false;
61
- return {
62
- content: content.map((block) => {
63
- if (!("cache_control" in block)) return block;
109
+ for (const block of content) {
110
+ if (isCachePoint(block)) {
111
+ const existing = block.cachePoint;
112
+ const desired = buildBedrockCachePoint(ttl);
113
+ if (existing?.ttl !== desired.ttl) {
114
+ modified = true;
115
+ sanitized.push({ cachePoint: desired });
116
+ } else sanitized.push(block);
117
+ continue;
118
+ }
119
+ if ("cache_control" in block) {
64
120
  const cloned = { ...block };
65
121
  delete cloned.cache_control;
66
122
  modified = true;
67
- return cloned;
68
- }),
69
- modified
70
- };
71
- }
72
- function sanitizeBedrockSystemMessage(message) {
73
- const content = message.content;
74
- if (!Array.isArray(content)) return message;
75
- const stripped = stripAnthropicCacheControlFromBlocks(content);
76
- if (!stripped.modified) return message;
77
- return cloneMessage(message, stripped.content);
123
+ sanitized.push(cloned);
124
+ continue;
125
+ }
126
+ sanitized.push(block);
127
+ }
128
+ if (!modified) return message;
129
+ return cloneMessage(message, sanitized);
78
130
  }
79
131
  /**
80
132
  * Anthropic API: Adds cache control to the appropriate user messages in the payload.
@@ -85,7 +137,7 @@ function sanitizeBedrockSystemMessage(message) {
85
137
  * @param messages - The array of message objects.
86
138
  * @returns - A new array of message objects with cache control added.
87
139
  */
88
- function addCacheControl(messages) {
140
+ function addCacheControl(messages, ttl) {
89
141
  if (!Array.isArray(messages) || messages.length < 2) return messages;
90
142
  const updatedMessages = [...messages];
91
143
  let userMessagesModified = 0;
@@ -118,14 +170,14 @@ function addCacheControl(messages) {
118
170
  }
119
171
  if (!modified && !needsCacheAdd) continue;
120
172
  if (needsCacheAdd && lastTextIndex >= 0) {
121
- workingContent[lastTextIndex].cache_control = { type: "ephemeral" };
173
+ workingContent[lastTextIndex].cache_control = buildAnthropicCacheControl(ttl);
122
174
  userMessagesModified++;
123
175
  }
124
176
  } else if (typeof content === "string" && needsCacheAdd) {
125
177
  workingContent = [{
126
178
  type: "text",
127
179
  text: content,
128
- cache_control: { type: "ephemeral" }
180
+ cache_control: buildAnthropicCacheControl(ttl)
129
181
  }];
130
182
  userMessagesModified++;
131
183
  } else continue;
@@ -196,7 +248,7 @@ function isTailCacheableBlock(block) {
196
248
  *
197
249
  * Returns a new array; only messages that require modification are cloned.
198
250
  */
199
- function addTailCacheControl(messages) {
251
+ function addTailCacheControl(messages, ttl) {
200
252
  if (!Array.isArray(messages) || messages.length === 0) return messages;
201
253
  const updatedMessages = [...messages];
202
254
  let markerPlaced = false;
@@ -227,7 +279,7 @@ function addTailCacheControl(messages) {
227
279
  workingContent.push(cloned);
228
280
  }
229
281
  if (canPlaceMarker && tailIndex >= 0) {
230
- workingContent[tailIndex].cache_control = { type: "ephemeral" };
282
+ workingContent[tailIndex].cache_control = buildAnthropicCacheControl(ttl);
231
283
  markerPlaced = true;
232
284
  modified = true;
233
285
  }
@@ -236,7 +288,7 @@ function addTailCacheControl(messages) {
236
288
  workingContent = [{
237
289
  type: "text",
238
290
  text: content,
239
- cache_control: { type: "ephemeral" }
291
+ cache_control: buildAnthropicCacheControl(ttl)
240
292
  }];
241
293
  markerPlaced = true;
242
294
  } else continue;
@@ -273,7 +325,7 @@ function isAssistantConversationMessage(message) {
273
325
  function hasCacheMarker(message) {
274
326
  return Array.isArray(message.content) && message.content.some((block) => "cache_control" in block);
275
327
  }
276
- function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage) {
328
+ function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage, ttl) {
277
329
  if (!Array.isArray(messages) || messages.length === 0 || maxCachePoints <= 0) return messages;
278
330
  const updatedMessages = [...messages];
279
331
  let cachePointsAdded = 0;
@@ -307,7 +359,7 @@ function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage
307
359
  workingContent.push(cloned);
308
360
  }
309
361
  if (canAddCache && lastNonEmptyTextIndex >= 0) {
310
- workingContent[lastNonEmptyTextIndex].cache_control = { type: "ephemeral" };
362
+ workingContent[lastNonEmptyTextIndex].cache_control = buildAnthropicCacheControl(ttl);
311
363
  cachePointsAdded++;
312
364
  modified = true;
313
365
  }
@@ -316,7 +368,7 @@ function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage
316
368
  workingContent = [{
317
369
  type: "text",
318
370
  text: content,
319
- cache_control: { type: "ephemeral" }
371
+ cache_control: buildAnthropicCacheControl(ttl)
320
372
  }];
321
373
  cachePointsAdded++;
322
374
  } else continue;
@@ -324,10 +376,10 @@ function addCacheControlToRecentMessages(messages, maxCachePoints, canUseMessage
324
376
  }
325
377
  return updatedMessages;
326
378
  }
327
- function addCacheControlToStablePrefixMessages(messages, maxCachePoints) {
328
- const assistantMarked = addCacheControlToRecentMessages(messages, maxCachePoints, isAssistantConversationMessage);
379
+ function addCacheControlToStablePrefixMessages(messages, maxCachePoints, ttl) {
380
+ const assistantMarked = addCacheControlToRecentMessages(messages, maxCachePoints, isAssistantConversationMessage, ttl);
329
381
  if (assistantMarked.some(hasCacheMarker)) return assistantMarked;
330
- return addCacheControlToRecentMessages(messages, maxCachePoints, isCacheableConversationMessage);
382
+ return addCacheControlToRecentMessages(messages, maxCachePoints, isCacheableConversationMessage, ttl);
331
383
  }
332
384
  /**
333
385
  * Checks if a message's content has Anthropic cache_control fields.
@@ -391,7 +443,7 @@ function stripBedrockCacheControl(messages) {
391
443
  * @param messages - The array of message objects.
392
444
  * @returns - A new array of message objects with cache points added.
393
445
  */
394
- function addBedrockCacheControl(messages) {
446
+ function addBedrockCacheControl(messages, ttl) {
395
447
  if (!Array.isArray(messages) || messages.length === 0) return messages;
396
448
  const updatedMessages = [...messages];
397
449
  let cachePointsAdded = 0;
@@ -400,7 +452,7 @@ function addBedrockCacheControl(messages) {
400
452
  const messageType = "getType" in originalMessage && typeof originalMessage.getType === "function" ? originalMessage.getType() : void 0;
401
453
  const messageRole = "role" in originalMessage && typeof originalMessage.role === "string" ? originalMessage.role : void 0;
402
454
  if (messageType === "system" || messageRole === "system") {
403
- updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage);
455
+ updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage, ttl);
404
456
  continue;
405
457
  }
406
458
  const isToolMessage = messageType === "tool" || messageRole === "tool";
@@ -436,14 +488,14 @@ function addBedrockCacheControl(messages) {
436
488
  }
437
489
  if (!modified && !needsCacheAdd) continue;
438
490
  if (needsCacheAdd && lastNonEmptyTextIndex >= 0) {
439
- workingContent.splice(lastNonEmptyTextIndex + 1, 0, { cachePoint: { type: "default" } });
491
+ workingContent.splice(lastNonEmptyTextIndex + 1, 0, { cachePoint: buildBedrockCachePoint(ttl) });
440
492
  cachePointsAdded++;
441
493
  }
442
494
  } else if (typeof content === "string" && needsCacheAdd) {
443
495
  workingContent = [{
444
496
  type: "text",
445
497
  text: content
446
- }, { cachePoint: { type: "default" } }];
498
+ }, { cachePoint: buildBedrockCachePoint(ttl) }];
447
499
  cachePointsAdded++;
448
500
  } else if (typeof content === "string" && hasSerializationProps) workingContent = content;
449
501
  else continue;
@@ -468,7 +520,7 @@ function addBedrockCacheControl(messages) {
468
520
  *
469
521
  * Returns a new array - only clones messages that require modification.
470
522
  */
471
- function addBedrockTailCacheControl(messages) {
523
+ function addBedrockTailCacheControl(messages, ttl) {
472
524
  if (!Array.isArray(messages) || messages.length === 0) return messages;
473
525
  const updatedMessages = [...messages];
474
526
  let cachePointPlaced = false;
@@ -477,7 +529,7 @@ function addBedrockTailCacheControl(messages) {
477
529
  const messageType = "getType" in originalMessage && typeof originalMessage.getType === "function" ? originalMessage.getType() : void 0;
478
530
  const messageRole = "role" in originalMessage && typeof originalMessage.role === "string" ? originalMessage.role : void 0;
479
531
  if (messageType === "system" || messageRole === "system") {
480
- updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage);
532
+ updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage, ttl);
481
533
  continue;
482
534
  }
483
535
  const content = originalMessage.content;
@@ -511,7 +563,7 @@ function addBedrockTailCacheControl(messages) {
511
563
  }
512
564
  if (!modified && !canPlaceCachePoint) continue;
513
565
  if (canPlaceCachePoint && lastNonEmptyTextIndex >= 0) {
514
- workingContent.splice(lastNonEmptyTextIndex + 1, 0, { cachePoint: { type: "default" } });
566
+ workingContent.splice(lastNonEmptyTextIndex + 1, 0, { cachePoint: buildBedrockCachePoint(ttl) });
515
567
  cachePointPlaced = true;
516
568
  modified = true;
517
569
  }
@@ -519,7 +571,7 @@ function addBedrockTailCacheControl(messages) {
519
571
  workingContent = [{
520
572
  type: "text",
521
573
  text: content
522
- }, { cachePoint: { type: "default" } }];
574
+ }, { cachePoint: buildBedrockCachePoint(ttl) }];
523
575
  cachePointPlaced = true;
524
576
  } else if (typeof content === "string" && hasSerializationProps) workingContent = content;
525
577
  else continue;
@@ -528,6 +580,6 @@ function addBedrockTailCacheControl(messages) {
528
580
  return updatedMessages;
529
581
  }
530
582
  //#endregion
531
- export { addBedrockCacheControl, addBedrockTailCacheControl, addCacheControl, addCacheControlToStablePrefixMessages, addTailCacheControl, cloneMessage, stripAnthropicCacheControl, stripBedrockCacheControl };
583
+ export { DEFAULT_PROMPT_CACHE_TTL, addBedrockCacheControl, addBedrockTailCacheControl, addCacheControl, addCacheControlToStablePrefixMessages, addTailCacheControl, buildAnthropicCacheControl, buildBedrockCachePoint, cloneMessage, resolvePromptCacheTtl, stripAnthropicCacheControl, stripBedrockCacheControl };
532
584
 
533
585
  //# sourceMappingURL=cache.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache.mjs","names":[],"sources":["../../../src/messages/cache.ts"],"sourcesContent":["import {\n AIMessage,\n BaseMessage,\n ToolMessage,\n HumanMessage,\n SystemMessage,\n MessageContentComplex,\n} from '@langchain/core/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport type { AnthropicMessage } from '@/types/messages';\nimport { toLangChainContent } from './langchain';\nimport { ContentTypes } from '@/common/enum';\nimport { withMessageRole } from './format';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\ntype MessageContentWithCacheControl = MessageContentComplex & {\n cache_control?: unknown;\n};\n\n/**\n * Deep clones a message's content to prevent mutation of the original.\n */\nfunction deepCloneContent<T extends string | MessageContentComplex[]>(\n content: T\n): T {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content.map((block) => ({ ...block })) as T;\n }\n return content;\n}\n\n/**\n * Clones a message with new content. For LangChain BaseMessage instances,\n * constructs a proper class instance so that `instanceof` checks are preserved\n * in downstream code (e.g., ensureThinkingBlockInMessages).\n * For plain objects (AnthropicMessage), uses object spread.\n */\nexport function cloneMessage<T extends MessageWithContent>(\n message: T,\n content: string | MessageContentComplex[]\n): T {\n if (message instanceof BaseMessage) {\n const baseParams = {\n content: toLangChainContent(content),\n additional_kwargs: { ...message.additional_kwargs },\n response_metadata: { ...message.response_metadata },\n id: message.id,\n name: message.name,\n };\n\n const msgType = message.getType();\n switch (msgType) {\n case 'ai':\n return withMessageRole(\n new AIMessage({\n ...baseParams,\n tool_calls: (message as unknown as AIMessage).tool_calls,\n }),\n 'assistant'\n ) as unknown as T;\n case 'human':\n return withMessageRole(\n new HumanMessage(baseParams),\n 'user'\n ) as unknown as T;\n case 'system':\n return withMessageRole(\n new SystemMessage(baseParams),\n 'system'\n ) as unknown as T;\n case 'tool':\n return withMessageRole(\n new ToolMessage({\n ...baseParams,\n tool_call_id: (message as unknown as ToolMessage).tool_call_id,\n }),\n 'tool'\n ) as unknown as T;\n default:\n break;\n }\n }\n\n const {\n lc_kwargs: _lc_kwargs,\n lc_serializable: _lc_serializable,\n lc_namespace: _lc_namespace,\n ...rest\n } = message as T & {\n lc_kwargs?: unknown;\n lc_serializable?: unknown;\n lc_namespace?: unknown;\n };\n\n const cloned = { ...rest, content } as T;\n\n // LangChain messages don't have a direct 'role' property - derive it from getType()\n if (\n 'getType' in message &&\n typeof message.getType === 'function' &&\n !('role' in cloned)\n ) {\n const msgType = (message as unknown as BaseMessage).getType();\n const roleMap: Record<string, string> = {\n human: 'user',\n ai: 'assistant',\n system: 'system',\n tool: 'tool',\n };\n (cloned as Record<string, unknown>).role = roleMap[msgType] || msgType;\n }\n\n return cloned;\n}\n\nfunction stripAnthropicCacheControlFromBlocks(\n content: MessageContentComplex[]\n): { content: MessageContentComplex[]; modified: boolean } {\n let modified = false;\n const strippedContent = content.map((block) => {\n if (!('cache_control' in block)) {\n return block;\n }\n\n const cloned: MessageContentWithCacheControl = { ...block };\n delete cloned.cache_control;\n modified = true;\n return cloned;\n });\n\n return { content: strippedContent, modified };\n}\n\nfunction sanitizeBedrockSystemMessage<T extends MessageWithContent>(\n message: T\n): T {\n const content = message.content;\n if (!Array.isArray(content)) {\n return message;\n }\n\n const stripped = stripAnthropicCacheControlFromBlocks(content);\n if (!stripped.modified) {\n return message;\n }\n\n return cloneMessage(message, stripped.content);\n}\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,\n * then adds fresh cache control to the last 2 user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let userMessagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const isUserMessage =\n ('getType' in originalMessage && originalMessage.getType() === 'human') ||\n ('role' in originalMessage && originalMessage.role === 'user');\n const hasArrayContent = Array.isArray(content);\n const needsCacheAdd =\n userMessagesModified < 2 &&\n isUserMessage &&\n !isSyntheticMetaMessage(originalMessage) &&\n (typeof content === 'string' || hasArrayContent);\n\n // Skip messages that don't need any work\n if (!needsCacheAdd && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n // Single pass: clone blocks, strip cache markers and cache points,\n // find last text block index for cache insertion — all at once.\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue; // skip cache point blocks\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n if ('type' in cloned && cloned.type === 'text') {\n lastTextIndex = workingContent.length;\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !needsCacheAdd) {\n continue; // nothing to strip and no cache to add\n }\n\n // Add cache control to the last text block for user messages\n if (needsCacheAdd && lastTextIndex >= 0) {\n (\n workingContent[lastTextIndex] as Anthropic.TextBlockParam\n ).cache_control = {\n type: 'ephemeral',\n };\n userMessagesModified++;\n }\n } else if (typeof content === 'string' && needsCacheAdd) {\n workingContent = [\n { type: 'text', text: content, cache_control: { type: 'ephemeral' } },\n ] as unknown as MessageContentComplex[];\n userMessagesModified++;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a content block is a cache point\n */\nfunction isCachePoint(block: MessageContentComplex): boolean {\n return 'cachePoint' in block && !('type' in block);\n}\n\n/**\n * Block types that must never anchor the tail cache breakpoint, because the\n * marker would not survive to the model call:\n * - `thinking` / `redacted_thinking`: native Anthropic reasoning — the API\n * rejects `cache_control` on these blocks.\n * - `reasoning_content` / `reasoning` / `think`: foreign reasoning (Bedrock,\n * Google, LibreChat) that `_convertMessagesToAnthropicPayload` DROPS on\n * assistant turns during a cross-provider handoff.\n * - `input_json_delta`: persisted partial tool-input deltas, also DROPPED by\n * `_convertMessagesToAnthropicPayload` (the assembled input is restored onto\n * the tool_use block).\n * Anchoring the only breakpoint on a block that is about to disappear silently\n * loses tail caching, so all of these are excluded.\n */\nconst NON_ANCHORABLE_BLOCK_TYPES = new Set([\n 'thinking',\n 'redacted_thinking',\n 'reasoning_content',\n 'reasoning',\n 'think',\n 'input_json_delta',\n]);\n\n/**\n * A block can anchor the tail cache breakpoint when it is a real content block\n * that the Anthropic API accepts `cache_control` on and that survives provider\n * conversion. Reasoning / dropped-delta blocks are excluded (see\n * {@link NON_ANCHORABLE_BLOCK_TYPES}), and empty text blocks are not cacheable,\n * so both are skipped.\n */\nfunction isTailCacheableBlock(block: MessageContentComplex): boolean {\n if (isCachePoint(block)) {\n return false;\n }\n const type = (block as { type?: string }).type;\n if (type == null || NON_ANCHORABLE_BLOCK_TYPES.has(type)) {\n return false;\n }\n if (type === 'text') {\n const text = (block as { text?: string }).text;\n return text != null && text.trim() !== '';\n }\n return true;\n}\n\n/**\n * Anthropic API: single tail cache breakpoint (default strategy).\n *\n * Places exactly ONE `cache_control` marker on the last cacheable block of the\n * final non-synthetic message, mirroring the Claude Code strategy\n * (`markerIndex = messages.length - 1`). Because the marker always rides the\n * true tail, the entire conversation prefix is written once and read back on\n * the next turn as the history grows append-only — instead of the rolling\n * \"last two user messages\" markers, which leave freshly appended tool/assistant\n * turns outside the cached prefix and re-write large spans every step.\n *\n * Stale markers (Anthropic `cache_control` and Bedrock cache points) are\n * stripped from every message in a single backward pass so exactly one marker\n * survives. Synthetic skill/meta messages are skipped as anchors (their volatile\n * content must not pin the cache) but still have stale markers removed.\n *\n * Returns a new array; only messages that require modification are cloned.\n */\nexport function addTailCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length === 0) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let markerPlaced = false;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const hasArrayContent = Array.isArray(content);\n const canPlaceMarker =\n !markerPlaced && !isSyntheticMetaMessage(originalMessage);\n\n // Earlier string-content messages carry no markers to strip.\n if (!canPlaceMarker && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n const src = content as MessageContentComplex[];\n workingContent = [];\n let tailIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n if (\n canPlaceMarker &&\n isTailCacheableBlock(cloned as MessageContentComplex)\n ) {\n tailIndex = workingContent.length;\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (canPlaceMarker && tailIndex >= 0) {\n (workingContent[tailIndex] as Anthropic.TextBlockParam).cache_control =\n {\n type: 'ephemeral',\n };\n markerPlaced = true;\n modified = true;\n }\n\n if (!modified) {\n continue;\n }\n } else if (\n typeof content === 'string' &&\n canPlaceMarker &&\n content.trim() !== ''\n ) {\n workingContent = [\n { type: 'text', text: content, cache_control: { type: 'ephemeral' } },\n ] as unknown as MessageContentComplex[];\n markerPlaced = true;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\nfunction getMessageRole(message: MessageWithContent): string | undefined {\n if (message instanceof BaseMessage) {\n return message.getType();\n }\n if ('role' in message && typeof message.role === 'string') {\n return message.role;\n }\n return undefined;\n}\n\nconst SKILL_MESSAGE_SOURCE = 'skill';\n\n/**\n * Synthetic skill/meta messages (reconstructed skill bodies, primed SKILL.md\n * instructions) are re-injected every turn and are not stable conversation\n * turns. They must not anchor a fresh prompt-cache marker — doing so pins the\n * cache to a volatile/duplicated prefix. Stale markers are still stripped from\n * them; only the *adding* of new markers is suppressed. Detected via\n * `additional_kwargs.isMeta === true` or `additional_kwargs.source === 'skill'`.\n */\nfunction isSyntheticMetaMessage(message: MessageWithContent): boolean {\n const { additional_kwargs: kwargs } = message as {\n additional_kwargs?: { isMeta?: unknown; source?: unknown };\n };\n if (kwargs == null) {\n return false;\n }\n return kwargs.isMeta === true || kwargs.source === SKILL_MESSAGE_SOURCE;\n}\n\nfunction isCacheableConversationMessage(message: MessageWithContent): boolean {\n const role = getMessageRole(message);\n return (\n role === 'human' || role === 'user' || role === 'ai' || role === 'assistant'\n );\n}\n\nfunction isAssistantConversationMessage(message: MessageWithContent): boolean {\n const role = getMessageRole(message);\n return role === 'ai' || role === 'assistant';\n}\n\nfunction hasCacheMarker(message: MessageWithContent): boolean {\n return (\n Array.isArray(message.content) &&\n message.content.some((block) => 'cache_control' in block)\n );\n}\n\nfunction addCacheControlToRecentMessages<\n T extends AnthropicMessage | BaseMessage,\n>(\n messages: T[],\n maxCachePoints: number,\n canUseMessage: (message: MessageWithContent) => boolean\n): T[] {\n if (\n !Array.isArray(messages) ||\n messages.length === 0 ||\n maxCachePoints <= 0\n ) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let cachePointsAdded = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const hasArrayContent = Array.isArray(content);\n const canAddCache =\n cachePointsAdded < maxCachePoints &&\n canUseMessage(originalMessage) &&\n !isSyntheticMetaMessage(originalMessage);\n\n if (!canAddCache && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastNonEmptyTextIndex = -1;\n\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n\n if ('type' in cloned && cloned.type === 'text') {\n const text = (cloned as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n lastNonEmptyTextIndex = workingContent.length;\n }\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (canAddCache && lastNonEmptyTextIndex >= 0) {\n (\n workingContent[lastNonEmptyTextIndex] as Anthropic.TextBlockParam\n ).cache_control = {\n type: 'ephemeral',\n };\n cachePointsAdded++;\n modified = true;\n }\n\n if (!modified) {\n continue;\n }\n } else if (\n typeof content === 'string' &&\n content.trim() !== '' &&\n canAddCache\n ) {\n workingContent = [\n { type: 'text', text: content, cache_control: { type: 'ephemeral' } },\n ] as unknown as MessageContentComplex[];\n cachePointsAdded++;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\nexport function addCacheControlToStablePrefixMessages<\n T extends AnthropicMessage | BaseMessage,\n>(messages: T[], maxCachePoints: number): T[] {\n const assistantMarked = addCacheControlToRecentMessages(\n messages,\n maxCachePoints,\n isAssistantConversationMessage\n );\n\n if (assistantMarked.some(hasCacheMarker)) {\n return assistantMarked;\n }\n\n return addCacheControlToRecentMessages(\n messages,\n maxCachePoints,\n isCacheableConversationMessage\n );\n}\n\n/**\n * Checks if a message's content has Anthropic cache_control fields.\n */\nfunction hasAnthropicCacheControl(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if ('cache_control' in content[i]) return true;\n }\n return false;\n}\n\n/**\n * Removes all Anthropic cache_control fields from messages\n * Used when switching from Anthropic to Bedrock provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripAnthropicCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasAnthropicCacheControl(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content);\n for (let j = 0; j < clonedContent.length; j++) {\n const block = clonedContent[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a message's content has Bedrock cachePoint blocks.\n */\nfunction hasBedrockCachePoint(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if (isCachePoint(content[i])) return true;\n }\n return false;\n}\n\n/**\n * Removes all Bedrock cachePoint blocks from messages\n * Used when switching from Bedrock to Anthropic provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripBedrockCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasBedrockCachePoint(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content).filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n );\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points to the latest two user messages.\n * Inserts `{ cachePoint: { type: 'default' } }` as a separate content block\n * immediately after the last text block in each targeted message.\n * Strips ALL existing cache control (both Bedrock and Anthropic formats) from all messages,\n * then adds fresh cache points to the latest two non-tool user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache points added.\n */\nexport function addBedrockCacheControl<\n T extends MessageWithContent & { getType?: () => string; role?: string },\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length === 0) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let cachePointsAdded = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const messageType =\n 'getType' in originalMessage &&\n typeof originalMessage.getType === 'function'\n ? originalMessage.getType()\n : undefined;\n const messageRole =\n 'role' in originalMessage && typeof originalMessage.role === 'string'\n ? originalMessage.role\n : undefined;\n\n const isSystemMessage =\n messageType === 'system' || messageRole === 'system';\n if (isSystemMessage) {\n updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage);\n continue;\n }\n\n const isToolMessage = messageType === 'tool' || messageRole === 'tool';\n const isUserMessage = messageType === 'human' || messageRole === 'user';\n const content = originalMessage.content;\n const hasSerializationProps =\n 'lc_kwargs' in originalMessage ||\n 'lc_serializable' in originalMessage ||\n 'lc_namespace' in originalMessage;\n const hasArrayContent = Array.isArray(content);\n const isEmptyString = typeof content === 'string' && content === '';\n const needsCacheAdd =\n cachePointsAdded < 2 &&\n isUserMessage &&\n !isToolMessage &&\n !isEmptyString &&\n !isSyntheticMetaMessage(originalMessage) &&\n (typeof content === 'string' || hasArrayContent);\n\n if (!needsCacheAdd && !hasArrayContent && !hasSerializationProps) {\n continue;\n }\n\n let workingContent: string | MessageContentComplex[];\n let modified = hasSerializationProps;\n\n if (hasArrayContent) {\n // Single pass: clone blocks, strip cache markers, find last\n // non-empty text block for cache point insertion — all at once.\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastNonEmptyTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n const type = (cloned as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (cloned as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n lastNonEmptyTextIndex = workingContent.length;\n }\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !needsCacheAdd) {\n continue;\n }\n\n // Insert cache point after the last non-empty text block.\n // Skip if no cacheable text content exists (whitespace-only messages).\n if (needsCacheAdd && lastNonEmptyTextIndex >= 0) {\n workingContent.splice(lastNonEmptyTextIndex + 1, 0, {\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n cachePointsAdded++;\n }\n } else if (typeof content === 'string' && needsCacheAdd) {\n workingContent = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: { type: 'default' } } as MessageContentComplex,\n ];\n cachePointsAdded++;\n } else if (typeof content === 'string' && hasSerializationProps) {\n workingContent = content;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(originalMessage, workingContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Bedrock Converse API: single tail cache breakpoint (default strategy).\n *\n * The Bedrock counterpart of {@link addTailCacheControl}. Strips ALL existing\n * cache control (Bedrock cache points and Anthropic `cache_control`) from every\n * message, then inserts exactly ONE `{ cachePoint: { type: 'default' } }` block\n * immediately after the last non-empty text block of the most recent\n * non-synthetic, non-system message. Anchoring on the rolling tail keeps the\n * cached prefix append-only as the conversation grows, instead of re-writing\n * large spans every turn with the legacy \"last two user messages\" cache points.\n *\n * System messages are sanitized (Anthropic `cache_control` stripped) but never\n * anchored. Synthetic skill/meta messages are skipped as anchors so their\n * volatile content cannot pin the cache.\n *\n * Returns a new array - only clones messages that require modification.\n */\nexport function addBedrockTailCacheControl<\n T extends MessageWithContent & { getType?: () => string; role?: string },\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length === 0) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let cachePointPlaced = false;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const messageType =\n 'getType' in originalMessage &&\n typeof originalMessage.getType === 'function'\n ? originalMessage.getType()\n : undefined;\n const messageRole =\n 'role' in originalMessage && typeof originalMessage.role === 'string'\n ? originalMessage.role\n : undefined;\n\n const isSystemMessage =\n messageType === 'system' || messageRole === 'system';\n if (isSystemMessage) {\n updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage);\n continue;\n }\n\n const content = originalMessage.content;\n const hasSerializationProps =\n 'lc_kwargs' in originalMessage ||\n 'lc_serializable' in originalMessage ||\n 'lc_namespace' in originalMessage;\n const hasArrayContent = Array.isArray(content);\n const isEmptyString = typeof content === 'string' && content === '';\n const canPlaceCachePoint =\n !cachePointPlaced &&\n !isEmptyString &&\n !isSyntheticMetaMessage(originalMessage) &&\n (typeof content === 'string' || hasArrayContent);\n\n if (!canPlaceCachePoint && !hasArrayContent && !hasSerializationProps) {\n continue;\n }\n\n let workingContent: string | MessageContentComplex[];\n let modified = hasSerializationProps;\n\n if (hasArrayContent) {\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastNonEmptyTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n const type = (cloned as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (cloned as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n lastNonEmptyTextIndex = workingContent.length;\n }\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !canPlaceCachePoint) {\n continue;\n }\n\n if (canPlaceCachePoint && lastNonEmptyTextIndex >= 0) {\n workingContent.splice(lastNonEmptyTextIndex + 1, 0, {\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n cachePointPlaced = true;\n modified = true;\n }\n } else if (typeof content === 'string' && canPlaceCachePoint) {\n workingContent = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: { type: 'default' } } as MessageContentComplex,\n ];\n cachePointPlaced = true;\n } else if (typeof content === 'string' && hasSerializationProps) {\n workingContent = content;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(originalMessage, workingContent);\n }\n\n return updatedMessages;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cache.mjs","names":[],"sources":["../../../src/messages/cache.ts"],"sourcesContent":["import {\n AIMessage,\n BaseMessage,\n ToolMessage,\n HumanMessage,\n SystemMessage,\n MessageContentComplex,\n} from '@langchain/core/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport type { AnthropicMessage } from '@/types/messages';\nimport { toLangChainContent } from './langchain';\nimport { ContentTypes } from '@/common/enum';\nimport { withMessageRole } from './format';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\ntype MessageContentWithCacheControl = MessageContentComplex & {\n cache_control?: unknown;\n};\n\n/**\n * Prompt-cache breakpoint TTL.\n *\n * Both Anthropic (`cache_control.ttl`) and Bedrock (`cachePoint.ttl`) accept\n * `'5m'` (the legacy provider default) and `'1h'` (the extended cache). When\n * prompt caching is enabled the SDK now defaults to the 1-hour extended cache\n * (see {@link DEFAULT_PROMPT_CACHE_TTL}); pass `'5m'` to opt back into the\n * legacy 5-minute behavior.\n */\nexport type PromptCacheTtl = '5m' | '1h';\n\n/**\n * Default TTL applied wherever a prompt-cache breakpoint is added. The 1-hour\n * extended cache keeps prefixes warm across longer gaps between turns, at the\n * cost of a higher one-time cache-write multiplier (2x vs 1.25x for 5m).\n */\nexport const DEFAULT_PROMPT_CACHE_TTL: PromptCacheTtl = '1h';\n\n/**\n * Resolve an optionally-configured TTL to a concrete value, defaulting to the\n * 1-hour extended cache. Used at the Anthropic/Bedrock prompt-cache call sites.\n */\nexport function resolvePromptCacheTtl(\n ttl: PromptCacheTtl | undefined\n): PromptCacheTtl {\n return ttl ?? DEFAULT_PROMPT_CACHE_TTL;\n}\n\n/** Anthropic `cache_control` shape (the SDK accepts an optional `ttl`). */\ntype AnthropicCacheControl = { type: 'ephemeral'; ttl?: '1h' };\n\n/**\n * Build an Anthropic `cache_control` breakpoint for the given TTL. `'5m'` (or\n * `undefined`) omits the `ttl` field — that is the provider default, so the\n * payload stays byte-identical to the legacy 5-minute marker. `'1h'` adds the\n * explicit extended-cache `ttl`.\n */\nexport function buildAnthropicCacheControl(\n ttl?: PromptCacheTtl\n): AnthropicCacheControl {\n return ttl === '1h'\n ? { type: 'ephemeral', ttl: '1h' }\n : { type: 'ephemeral' };\n}\n\n/** Bedrock `cachePoint` shape (the SDK accepts an optional `ttl`). */\ntype BedrockCachePoint = { type: 'default'; ttl?: '1h' };\n\n/**\n * Build a Bedrock `cachePoint` for the given TTL. Mirrors\n * {@link buildAnthropicCacheControl}: `'5m'`/`undefined` omits `ttl` (the\n * legacy default), `'1h'` adds the extended-cache `ttl`.\n */\nexport function buildBedrockCachePoint(\n ttl?: PromptCacheTtl\n): BedrockCachePoint {\n return ttl === '1h' ? { type: 'default', ttl: '1h' } : { type: 'default' };\n}\n\n/**\n * Deep clones a message's content to prevent mutation of the original.\n */\nfunction deepCloneContent<T extends string | MessageContentComplex[]>(\n content: T\n): T {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content.map((block) => ({ ...block })) as T;\n }\n return content;\n}\n\n/**\n * Clones a message with new content. For LangChain BaseMessage instances,\n * constructs a proper class instance so that `instanceof` checks are preserved\n * in downstream code (e.g., ensureThinkingBlockInMessages).\n * For plain objects (AnthropicMessage), uses object spread.\n */\nexport function cloneMessage<T extends MessageWithContent>(\n message: T,\n content: string | MessageContentComplex[]\n): T {\n if (message instanceof BaseMessage) {\n const baseParams = {\n content: toLangChainContent(content),\n additional_kwargs: { ...message.additional_kwargs },\n response_metadata: { ...message.response_metadata },\n id: message.id,\n name: message.name,\n };\n\n const msgType = message.getType();\n switch (msgType) {\n case 'ai':\n return withMessageRole(\n new AIMessage({\n ...baseParams,\n tool_calls: (message as unknown as AIMessage).tool_calls,\n }),\n 'assistant'\n ) as unknown as T;\n case 'human':\n return withMessageRole(\n new HumanMessage(baseParams),\n 'user'\n ) as unknown as T;\n case 'system':\n return withMessageRole(\n new SystemMessage(baseParams),\n 'system'\n ) as unknown as T;\n case 'tool':\n return withMessageRole(\n new ToolMessage({\n ...baseParams,\n tool_call_id: (message as unknown as ToolMessage).tool_call_id,\n }),\n 'tool'\n ) as unknown as T;\n default:\n break;\n }\n }\n\n const {\n lc_kwargs: _lc_kwargs,\n lc_serializable: _lc_serializable,\n lc_namespace: _lc_namespace,\n ...rest\n } = message as T & {\n lc_kwargs?: unknown;\n lc_serializable?: unknown;\n lc_namespace?: unknown;\n };\n\n const cloned = { ...rest, content } as T;\n\n // LangChain messages don't have a direct 'role' property - derive it from getType()\n if (\n 'getType' in message &&\n typeof message.getType === 'function' &&\n !('role' in cloned)\n ) {\n const msgType = (message as unknown as BaseMessage).getType();\n const roleMap: Record<string, string> = {\n human: 'user',\n ai: 'assistant',\n system: 'system',\n tool: 'tool',\n };\n (cloned as Record<string, unknown>).role = roleMap[msgType] || msgType;\n }\n\n return cloned;\n}\n\n/**\n * Sanitize a Bedrock system message: strip Anthropic `cache_control` (Bedrock\n * conversion can't use it) and normalize any existing `cachePoint` to the\n * resolved TTL. The normalization matters because Bedrock requires longer-TTL\n * checkpoints to appear before shorter ones — a stale 5-minute system cachePoint\n * (host-supplied or carried over from a 5m config) left ahead of a 1-hour\n * message tail would make the request invalid. System messages are never\n * anchored as the tail breakpoint; this only fixes markers already present.\n */\nfunction sanitizeBedrockSystemMessage<T extends MessageWithContent>(\n message: T,\n ttl?: PromptCacheTtl\n): T {\n const content = message.content;\n if (!Array.isArray(content)) {\n return message;\n }\n\n const sanitized: MessageContentComplex[] = [];\n let modified = false;\n for (const block of content) {\n if (isCachePoint(block)) {\n const existing = (block as { cachePoint?: { ttl?: unknown } }).cachePoint;\n const desired = buildBedrockCachePoint(ttl);\n if (existing?.ttl !== desired.ttl) {\n modified = true;\n sanitized.push({ cachePoint: desired } as MessageContentComplex);\n } else {\n sanitized.push(block);\n }\n continue;\n }\n if ('cache_control' in block) {\n const cloned: MessageContentWithCacheControl = { ...block };\n delete cloned.cache_control;\n modified = true;\n sanitized.push(cloned);\n continue;\n }\n sanitized.push(block);\n }\n\n if (!modified) {\n return message;\n }\n\n return cloneMessage(message, sanitized);\n}\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,\n * then adds fresh cache control to the last 2 user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[],\n ttl?: PromptCacheTtl\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let userMessagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const isUserMessage =\n ('getType' in originalMessage && originalMessage.getType() === 'human') ||\n ('role' in originalMessage && originalMessage.role === 'user');\n const hasArrayContent = Array.isArray(content);\n const needsCacheAdd =\n userMessagesModified < 2 &&\n isUserMessage &&\n !isSyntheticMetaMessage(originalMessage) &&\n (typeof content === 'string' || hasArrayContent);\n\n // Skip messages that don't need any work\n if (!needsCacheAdd && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n // Single pass: clone blocks, strip cache markers and cache points,\n // find last text block index for cache insertion — all at once.\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue; // skip cache point blocks\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n if ('type' in cloned && cloned.type === 'text') {\n lastTextIndex = workingContent.length;\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !needsCacheAdd) {\n continue; // nothing to strip and no cache to add\n }\n\n // Add cache control to the last text block for user messages\n if (needsCacheAdd && lastTextIndex >= 0) {\n (\n workingContent[lastTextIndex] as Anthropic.TextBlockParam\n ).cache_control = buildAnthropicCacheControl(ttl);\n userMessagesModified++;\n }\n } else if (typeof content === 'string' && needsCacheAdd) {\n workingContent = [\n {\n type: 'text',\n text: content,\n cache_control: buildAnthropicCacheControl(ttl),\n },\n ] as unknown as MessageContentComplex[];\n userMessagesModified++;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a content block is a cache point\n */\nfunction isCachePoint(block: MessageContentComplex): boolean {\n return 'cachePoint' in block && !('type' in block);\n}\n\n/**\n * Block types that must never anchor the tail cache breakpoint, because the\n * marker would not survive to the model call:\n * - `thinking` / `redacted_thinking`: native Anthropic reasoning — the API\n * rejects `cache_control` on these blocks.\n * - `reasoning_content` / `reasoning` / `think`: foreign reasoning (Bedrock,\n * Google, LibreChat) that `_convertMessagesToAnthropicPayload` DROPS on\n * assistant turns during a cross-provider handoff.\n * - `input_json_delta`: persisted partial tool-input deltas, also DROPPED by\n * `_convertMessagesToAnthropicPayload` (the assembled input is restored onto\n * the tool_use block).\n * Anchoring the only breakpoint on a block that is about to disappear silently\n * loses tail caching, so all of these are excluded.\n */\nconst NON_ANCHORABLE_BLOCK_TYPES = new Set([\n 'thinking',\n 'redacted_thinking',\n 'reasoning_content',\n 'reasoning',\n 'think',\n 'input_json_delta',\n]);\n\n/**\n * A block can anchor the tail cache breakpoint when it is a real content block\n * that the Anthropic API accepts `cache_control` on and that survives provider\n * conversion. Reasoning / dropped-delta blocks are excluded (see\n * {@link NON_ANCHORABLE_BLOCK_TYPES}), and empty text blocks are not cacheable,\n * so both are skipped.\n */\nfunction isTailCacheableBlock(block: MessageContentComplex): boolean {\n if (isCachePoint(block)) {\n return false;\n }\n const type = (block as { type?: string }).type;\n if (type == null || NON_ANCHORABLE_BLOCK_TYPES.has(type)) {\n return false;\n }\n if (type === 'text') {\n const text = (block as { text?: string }).text;\n return text != null && text.trim() !== '';\n }\n return true;\n}\n\n/**\n * Anthropic API: single tail cache breakpoint (default strategy).\n *\n * Places exactly ONE `cache_control` marker on the last cacheable block of the\n * final non-synthetic message, mirroring the Claude Code strategy\n * (`markerIndex = messages.length - 1`). Because the marker always rides the\n * true tail, the entire conversation prefix is written once and read back on\n * the next turn as the history grows append-only — instead of the rolling\n * \"last two user messages\" markers, which leave freshly appended tool/assistant\n * turns outside the cached prefix and re-write large spans every step.\n *\n * Stale markers (Anthropic `cache_control` and Bedrock cache points) are\n * stripped from every message in a single backward pass so exactly one marker\n * survives. Synthetic skill/meta messages are skipped as anchors (their volatile\n * content must not pin the cache) but still have stale markers removed.\n *\n * Returns a new array; only messages that require modification are cloned.\n */\nexport function addTailCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[],\n ttl?: PromptCacheTtl\n): T[] {\n if (!Array.isArray(messages) || messages.length === 0) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let markerPlaced = false;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const hasArrayContent = Array.isArray(content);\n const canPlaceMarker =\n !markerPlaced && !isSyntheticMetaMessage(originalMessage);\n\n // Earlier string-content messages carry no markers to strip.\n if (!canPlaceMarker && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n const src = content as MessageContentComplex[];\n workingContent = [];\n let tailIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n if (\n canPlaceMarker &&\n isTailCacheableBlock(cloned as MessageContentComplex)\n ) {\n tailIndex = workingContent.length;\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (canPlaceMarker && tailIndex >= 0) {\n (workingContent[tailIndex] as Anthropic.TextBlockParam).cache_control =\n buildAnthropicCacheControl(ttl);\n markerPlaced = true;\n modified = true;\n }\n\n if (!modified) {\n continue;\n }\n } else if (\n typeof content === 'string' &&\n canPlaceMarker &&\n content.trim() !== ''\n ) {\n workingContent = [\n {\n type: 'text',\n text: content,\n cache_control: buildAnthropicCacheControl(ttl),\n },\n ] as unknown as MessageContentComplex[];\n markerPlaced = true;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\nfunction getMessageRole(message: MessageWithContent): string | undefined {\n if (message instanceof BaseMessage) {\n return message.getType();\n }\n if ('role' in message && typeof message.role === 'string') {\n return message.role;\n }\n return undefined;\n}\n\nconst SKILL_MESSAGE_SOURCE = 'skill';\n\n/**\n * Synthetic skill/meta messages (reconstructed skill bodies, primed SKILL.md\n * instructions) are re-injected every turn and are not stable conversation\n * turns. They must not anchor a fresh prompt-cache marker — doing so pins the\n * cache to a volatile/duplicated prefix. Stale markers are still stripped from\n * them; only the *adding* of new markers is suppressed. Detected via\n * `additional_kwargs.isMeta === true` or `additional_kwargs.source === 'skill'`.\n */\nfunction isSyntheticMetaMessage(message: MessageWithContent): boolean {\n const { additional_kwargs: kwargs } = message as {\n additional_kwargs?: { isMeta?: unknown; source?: unknown };\n };\n if (kwargs == null) {\n return false;\n }\n return kwargs.isMeta === true || kwargs.source === SKILL_MESSAGE_SOURCE;\n}\n\nfunction isCacheableConversationMessage(message: MessageWithContent): boolean {\n const role = getMessageRole(message);\n return (\n role === 'human' || role === 'user' || role === 'ai' || role === 'assistant'\n );\n}\n\nfunction isAssistantConversationMessage(message: MessageWithContent): boolean {\n const role = getMessageRole(message);\n return role === 'ai' || role === 'assistant';\n}\n\nfunction hasCacheMarker(message: MessageWithContent): boolean {\n return (\n Array.isArray(message.content) &&\n message.content.some((block) => 'cache_control' in block)\n );\n}\n\nfunction addCacheControlToRecentMessages<\n T extends AnthropicMessage | BaseMessage,\n>(\n messages: T[],\n maxCachePoints: number,\n canUseMessage: (message: MessageWithContent) => boolean,\n ttl?: PromptCacheTtl\n): T[] {\n if (\n !Array.isArray(messages) ||\n messages.length === 0 ||\n maxCachePoints <= 0\n ) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let cachePointsAdded = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const hasArrayContent = Array.isArray(content);\n const canAddCache =\n cachePointsAdded < maxCachePoints &&\n canUseMessage(originalMessage) &&\n !isSyntheticMetaMessage(originalMessage);\n\n if (!canAddCache && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastNonEmptyTextIndex = -1;\n\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n\n if ('type' in cloned && cloned.type === 'text') {\n const text = (cloned as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n lastNonEmptyTextIndex = workingContent.length;\n }\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (canAddCache && lastNonEmptyTextIndex >= 0) {\n (\n workingContent[lastNonEmptyTextIndex] as Anthropic.TextBlockParam\n ).cache_control = buildAnthropicCacheControl(ttl);\n cachePointsAdded++;\n modified = true;\n }\n\n if (!modified) {\n continue;\n }\n } else if (\n typeof content === 'string' &&\n content.trim() !== '' &&\n canAddCache\n ) {\n workingContent = [\n {\n type: 'text',\n text: content,\n cache_control: buildAnthropicCacheControl(ttl),\n },\n ] as unknown as MessageContentComplex[];\n cachePointsAdded++;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\nexport function addCacheControlToStablePrefixMessages<\n T extends AnthropicMessage | BaseMessage,\n>(messages: T[], maxCachePoints: number, ttl?: PromptCacheTtl): T[] {\n const assistantMarked = addCacheControlToRecentMessages(\n messages,\n maxCachePoints,\n isAssistantConversationMessage,\n ttl\n );\n\n if (assistantMarked.some(hasCacheMarker)) {\n return assistantMarked;\n }\n\n return addCacheControlToRecentMessages(\n messages,\n maxCachePoints,\n isCacheableConversationMessage,\n ttl\n );\n}\n\n/**\n * Checks if a message's content has Anthropic cache_control fields.\n */\nfunction hasAnthropicCacheControl(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if ('cache_control' in content[i]) return true;\n }\n return false;\n}\n\n/**\n * Removes all Anthropic cache_control fields from messages\n * Used when switching from Anthropic to Bedrock provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripAnthropicCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasAnthropicCacheControl(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content);\n for (let j = 0; j < clonedContent.length; j++) {\n const block = clonedContent[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a message's content has Bedrock cachePoint blocks.\n */\nfunction hasBedrockCachePoint(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if (isCachePoint(content[i])) return true;\n }\n return false;\n}\n\n/**\n * Removes all Bedrock cachePoint blocks from messages\n * Used when switching from Bedrock to Anthropic provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripBedrockCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasBedrockCachePoint(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content).filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n );\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points to the latest two user messages.\n * Inserts `{ cachePoint: { type: 'default' } }` as a separate content block\n * immediately after the last text block in each targeted message.\n * Strips ALL existing cache control (both Bedrock and Anthropic formats) from all messages,\n * then adds fresh cache points to the latest two non-tool user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache points added.\n */\nexport function addBedrockCacheControl<\n T extends MessageWithContent & { getType?: () => string; role?: string },\n>(messages: T[], ttl?: PromptCacheTtl): T[] {\n if (!Array.isArray(messages) || messages.length === 0) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let cachePointsAdded = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const messageType =\n 'getType' in originalMessage &&\n typeof originalMessage.getType === 'function'\n ? originalMessage.getType()\n : undefined;\n const messageRole =\n 'role' in originalMessage && typeof originalMessage.role === 'string'\n ? originalMessage.role\n : undefined;\n\n const isSystemMessage =\n messageType === 'system' || messageRole === 'system';\n if (isSystemMessage) {\n updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage, ttl);\n continue;\n }\n\n const isToolMessage = messageType === 'tool' || messageRole === 'tool';\n const isUserMessage = messageType === 'human' || messageRole === 'user';\n const content = originalMessage.content;\n const hasSerializationProps =\n 'lc_kwargs' in originalMessage ||\n 'lc_serializable' in originalMessage ||\n 'lc_namespace' in originalMessage;\n const hasArrayContent = Array.isArray(content);\n const isEmptyString = typeof content === 'string' && content === '';\n const needsCacheAdd =\n cachePointsAdded < 2 &&\n isUserMessage &&\n !isToolMessage &&\n !isEmptyString &&\n !isSyntheticMetaMessage(originalMessage) &&\n (typeof content === 'string' || hasArrayContent);\n\n if (!needsCacheAdd && !hasArrayContent && !hasSerializationProps) {\n continue;\n }\n\n let workingContent: string | MessageContentComplex[];\n let modified = hasSerializationProps;\n\n if (hasArrayContent) {\n // Single pass: clone blocks, strip cache markers, find last\n // non-empty text block for cache point insertion — all at once.\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastNonEmptyTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n const type = (cloned as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (cloned as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n lastNonEmptyTextIndex = workingContent.length;\n }\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !needsCacheAdd) {\n continue;\n }\n\n // Insert cache point after the last non-empty text block.\n // Skip if no cacheable text content exists (whitespace-only messages).\n if (needsCacheAdd && lastNonEmptyTextIndex >= 0) {\n workingContent.splice(lastNonEmptyTextIndex + 1, 0, {\n cachePoint: buildBedrockCachePoint(ttl),\n } as MessageContentComplex);\n cachePointsAdded++;\n }\n } else if (typeof content === 'string' && needsCacheAdd) {\n workingContent = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: buildBedrockCachePoint(ttl) } as MessageContentComplex,\n ];\n cachePointsAdded++;\n } else if (typeof content === 'string' && hasSerializationProps) {\n workingContent = content;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(originalMessage, workingContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Bedrock Converse API: single tail cache breakpoint (default strategy).\n *\n * The Bedrock counterpart of {@link addTailCacheControl}. Strips ALL existing\n * cache control (Bedrock cache points and Anthropic `cache_control`) from every\n * message, then inserts exactly ONE `{ cachePoint: { type: 'default' } }` block\n * immediately after the last non-empty text block of the most recent\n * non-synthetic, non-system message. Anchoring on the rolling tail keeps the\n * cached prefix append-only as the conversation grows, instead of re-writing\n * large spans every turn with the legacy \"last two user messages\" cache points.\n *\n * System messages are sanitized (Anthropic `cache_control` stripped) but never\n * anchored. Synthetic skill/meta messages are skipped as anchors so their\n * volatile content cannot pin the cache.\n *\n * Returns a new array - only clones messages that require modification.\n */\nexport function addBedrockTailCacheControl<\n T extends MessageWithContent & { getType?: () => string; role?: string },\n>(messages: T[], ttl?: PromptCacheTtl): T[] {\n if (!Array.isArray(messages) || messages.length === 0) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let cachePointPlaced = false;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const messageType =\n 'getType' in originalMessage &&\n typeof originalMessage.getType === 'function'\n ? originalMessage.getType()\n : undefined;\n const messageRole =\n 'role' in originalMessage && typeof originalMessage.role === 'string'\n ? originalMessage.role\n : undefined;\n\n const isSystemMessage =\n messageType === 'system' || messageRole === 'system';\n if (isSystemMessage) {\n updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage, ttl);\n continue;\n }\n\n const content = originalMessage.content;\n const hasSerializationProps =\n 'lc_kwargs' in originalMessage ||\n 'lc_serializable' in originalMessage ||\n 'lc_namespace' in originalMessage;\n const hasArrayContent = Array.isArray(content);\n const isEmptyString = typeof content === 'string' && content === '';\n const canPlaceCachePoint =\n !cachePointPlaced &&\n !isEmptyString &&\n !isSyntheticMetaMessage(originalMessage) &&\n (typeof content === 'string' || hasArrayContent);\n\n if (!canPlaceCachePoint && !hasArrayContent && !hasSerializationProps) {\n continue;\n }\n\n let workingContent: string | MessageContentComplex[];\n let modified = hasSerializationProps;\n\n if (hasArrayContent) {\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastNonEmptyTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n const type = (cloned as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (cloned as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n lastNonEmptyTextIndex = workingContent.length;\n }\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !canPlaceCachePoint) {\n continue;\n }\n\n if (canPlaceCachePoint && lastNonEmptyTextIndex >= 0) {\n workingContent.splice(lastNonEmptyTextIndex + 1, 0, {\n cachePoint: buildBedrockCachePoint(ttl),\n } as MessageContentComplex);\n cachePointPlaced = true;\n modified = true;\n }\n } else if (typeof content === 'string' && canPlaceCachePoint) {\n workingContent = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: buildBedrockCachePoint(ttl) } as MessageContentComplex,\n ];\n cachePointPlaced = true;\n } else if (typeof content === 'string' && hasSerializationProps) {\n workingContent = content;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(originalMessage, workingContent);\n }\n\n return updatedMessages;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import "../common/enum.mjs";
2
2
  import "../common/index.mjs";
3
3
  import { emitAgentLog, safeDispatchCustomEvent } from "../utils/events.mjs";
4
- import { addTailCacheControl } from "../messages/cache.mjs";
4
+ import { addTailCacheControl, resolvePromptCacheTtl } from "../messages/cache.mjs";
5
5
  import { createRemoveAllMessage } from "../messages/reducer.mjs";
6
6
  import { splitAtRecencyBoundary } from "../messages/recency.mjs";
7
7
  import { getChunkContent } from "../stream.mjs";
@@ -330,6 +330,7 @@ async function executeSummarizationWithFallback(params) {
330
330
  provider: clientConfig.provider,
331
331
  reasoningKey: agentContext.reasoningKey,
332
332
  usePromptCache,
333
+ promptCacheTtl: clientConfig.provider === "anthropic" || clientConfig.provider === "openrouter" ? resolvePromptCacheTtl(clientConfig.clientOptions.promptCacheTtl) : void 0,
333
334
  log
334
335
  });
335
336
  summaryText = result.text;
@@ -709,12 +710,12 @@ function traceConfig(config, stage) {
709
710
  * Providers with prompt caching get a cache hit on the system prompt +
710
711
  * tool definitions prefix.
711
712
  */
712
- async function summarizeWithCacheHit({ model, messages, promptText, updatePromptText, priorSummaryText, config, stepId, provider, reasoningKey, usePromptCache, log }) {
713
+ async function summarizeWithCacheHit({ model, messages, promptText, updatePromptText, priorSummaryText, config, stepId, provider, reasoningKey, usePromptCache, promptCacheTtl, log }) {
713
714
  const instruction = buildSummarizationInstruction(promptText, updatePromptText, priorSummaryText);
714
715
  const fullMessages = [...messages, new HumanMessage(instruction)];
715
716
  const responseMsg = (await attemptInvoke({
716
717
  model,
717
- messages: usePromptCache === true ? addTailCacheControl(fullMessages) : fullMessages,
718
+ messages: usePromptCache === true ? addTailCacheControl(fullMessages, promptCacheTtl) : fullMessages,
718
719
  provider,
719
720
  onChunk: createSummarizationChunkHandler({
720
721
  stepId,