@librechat/agents 3.2.38 → 3.2.41

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 (105) 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/hooks/createWorkspacePolicyHook.cjs +4 -3
  6. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
  7. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +20 -4
  8. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  9. package/dist/cjs/llm/bedrock/index.cjs +7 -1
  10. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  11. package/dist/cjs/llm/bedrock/toolCache.cjs +5 -4
  12. package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
  13. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +34 -17
  14. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  15. package/dist/cjs/llm/openrouter/index.cjs +1 -0
  16. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  17. package/dist/cjs/llm/openrouter/toolCache.cjs +18 -5
  18. package/dist/cjs/llm/openrouter/toolCache.cjs.map +1 -1
  19. package/dist/cjs/main.cjs +4 -0
  20. package/dist/cjs/messages/anthropicToolCache.cjs +75 -13
  21. package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -1
  22. package/dist/cjs/messages/cache.cjs +91 -35
  23. package/dist/cjs/messages/cache.cjs.map +1 -1
  24. package/dist/cjs/summarization/node.cjs +3 -2
  25. package/dist/cjs/summarization/node.cjs.map +1 -1
  26. package/dist/cjs/tools/ReadFile.cjs +2 -2
  27. package/dist/cjs/tools/ReadFile.cjs.map +1 -1
  28. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +11 -11
  29. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
  30. package/dist/cjs/tools/local/LocalCodingTools.cjs +11 -11
  31. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
  32. package/dist/esm/agents/AgentContext.mjs +26 -9
  33. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  34. package/dist/esm/graphs/Graph.mjs +8 -5
  35. package/dist/esm/graphs/Graph.mjs.map +1 -1
  36. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +4 -3
  37. package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
  38. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +20 -4
  39. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  40. package/dist/esm/llm/bedrock/index.mjs +7 -1
  41. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  42. package/dist/esm/llm/bedrock/toolCache.mjs +5 -4
  43. package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
  44. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +34 -17
  45. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  46. package/dist/esm/llm/openrouter/index.mjs +1 -0
  47. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  48. package/dist/esm/llm/openrouter/toolCache.mjs +18 -5
  49. package/dist/esm/llm/openrouter/toolCache.mjs.map +1 -1
  50. package/dist/esm/main.mjs +2 -2
  51. package/dist/esm/messages/anthropicToolCache.mjs +75 -13
  52. package/dist/esm/messages/anthropicToolCache.mjs.map +1 -1
  53. package/dist/esm/messages/cache.mjs +88 -36
  54. package/dist/esm/messages/cache.mjs.map +1 -1
  55. package/dist/esm/summarization/node.mjs +4 -3
  56. package/dist/esm/summarization/node.mjs.map +1 -1
  57. package/dist/esm/tools/ReadFile.mjs +2 -2
  58. package/dist/esm/tools/ReadFile.mjs.map +1 -1
  59. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +11 -11
  60. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
  61. package/dist/esm/tools/local/LocalCodingTools.mjs +11 -11
  62. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
  63. package/dist/types/agents/AgentContext.d.ts +11 -0
  64. package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +2 -0
  65. package/dist/types/llm/bedrock/index.d.ts +13 -0
  66. package/dist/types/llm/bedrock/toolCache.d.ts +2 -1
  67. package/dist/types/llm/openrouter/index.d.ts +8 -0
  68. package/dist/types/llm/openrouter/toolCache.d.ts +2 -1
  69. package/dist/types/messages/anthropicToolCache.d.ts +2 -1
  70. package/dist/types/messages/cache.d.ts +49 -5
  71. package/dist/types/tools/ReadFile.d.ts +4 -4
  72. package/dist/types/types/llm.d.ts +14 -0
  73. package/package.json +1 -1
  74. package/src/agents/AgentContext.ts +64 -17
  75. package/src/agents/__tests__/AgentContext.anthropic.live.test.ts +6 -2
  76. package/src/agents/__tests__/AgentContext.bedrock.live.test.ts +7 -5
  77. package/src/agents/__tests__/AgentContext.openrouter.live.test.ts +1 -1
  78. package/src/agents/__tests__/AgentContext.test.ts +31 -19
  79. package/src/agents/__tests__/promptCacheLiveHelpers.ts +6 -2
  80. package/src/graphs/Graph.ts +40 -4
  81. package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +12 -12
  82. package/src/hooks/createWorkspacePolicyHook.ts +7 -6
  83. package/src/llm/anthropic/utils/message_inputs.ts +33 -6
  84. package/src/llm/bedrock/index.ts +21 -1
  85. package/src/llm/bedrock/llm.spec.ts +61 -0
  86. package/src/llm/bedrock/toolCache.test.ts +24 -0
  87. package/src/llm/bedrock/toolCache.ts +12 -7
  88. package/src/llm/bedrock/utils/message_inputs.ts +57 -40
  89. package/src/llm/openrouter/index.ts +9 -0
  90. package/src/llm/openrouter/toolCache.test.ts +52 -1
  91. package/src/llm/openrouter/toolCache.ts +40 -6
  92. package/src/messages/__tests__/anthropicToolCache.test.ts +168 -0
  93. package/src/messages/anthropicToolCache.ts +118 -15
  94. package/src/messages/cache.test.ts +175 -0
  95. package/src/messages/cache.ts +133 -48
  96. package/src/summarization/node.ts +21 -2
  97. package/src/tools/ReadFile.ts +2 -2
  98. package/src/tools/__tests__/LocalExecutionTools.test.ts +25 -25
  99. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +5 -5
  100. package/src/tools/__tests__/ReadFile.test.ts +3 -3
  101. package/src/tools/__tests__/ToolNode.session.test.ts +2 -2
  102. package/src/tools/__tests__/workspaceSeam.test.ts +2 -2
  103. package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +11 -11
  104. package/src/tools/local/LocalCodingTools.ts +14 -14
  105. package/src/types/llm.ts +14 -0
@@ -1,3 +1,4 @@
1
+ import { buildAnthropicCacheControl } from "./cache.mjs";
1
2
  //#region src/messages/anthropicToolCache.ts
2
3
  const ANTHROPIC_BUILT_IN_TOOL_PREFIXES = [
3
4
  "text_editor_",
@@ -12,25 +13,69 @@ const ANTHROPIC_BUILT_IN_TOOL_PREFIXES = [
12
13
  "tool_search_",
13
14
  "mcp_toolset"
14
15
  ];
15
- const CACHE_CONTROL = { type: "ephemeral" };
16
16
  function isAnthropicBuiltInTool(tool) {
17
17
  const { type } = tool;
18
18
  return typeof type === "string" && ANTHROPIC_BUILT_IN_TOOL_PREFIXES.some((prefix) => type.startsWith(prefix));
19
19
  }
20
+ /**
21
+ * Whether a tool already carries a cache breakpoint. Built-ins use a direct
22
+ * `cache_control`; custom tools normally carry it under `extras`, but a
23
+ * caller-supplied Anthropic-native tool object can also put it directly on the
24
+ * block — check both so stale markers are never missed.
25
+ */
20
26
  function hasCacheControl(tool) {
21
27
  if (isAnthropicBuiltInTool(tool)) return tool.cache_control != null;
22
- return tool.extras?.cache_control != null;
28
+ return tool.cache_control != null || tool.extras?.cache_control != null;
29
+ }
30
+ /**
31
+ * Return the `cache_control` from the location that actually reaches the
32
+ * Anthropic payload for this tool's shape: directly on the block for
33
+ * provider-shaped tools (built-ins and raw Anthropic tools), or under `extras`
34
+ * for LangChain custom tools (the adapter promotes only that). Returns
35
+ * `undefined` when no marker sits in the effective location — a marker in the
36
+ * wrong place (e.g. a direct marker on a custom tool) does not count.
37
+ */
38
+ function getEffectiveCacheControl(tool) {
39
+ return isProviderShapedTool(tool) ? tool.cache_control : tool.extras?.cache_control;
23
40
  }
24
- function markCacheControl(tool) {
41
+ /**
42
+ * Return a clone of `tool` with any `cache_control` removed — both the direct
43
+ * block marker and the `extras` marker — preserving the prototype chain. Used
44
+ * to clear stray markers off tools that must not anchor a competing breakpoint.
45
+ */
46
+ function stripCacheControl(tool) {
25
47
  const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;
26
- if (isAnthropicBuiltInTool(tool)) {
48
+ const wrapped = { ...tool };
49
+ delete wrapped.cache_control;
50
+ if (wrapped.extras != null) {
51
+ wrapped.extras = { ...wrapped.extras };
52
+ delete wrapped.extras.cache_control;
53
+ }
54
+ return Object.assign(Object.create(prototype), wrapped);
55
+ }
56
+ /**
57
+ * Whether `tool` is already in the Anthropic provider payload shape — an
58
+ * Anthropic built-in or a raw Anthropic tool object (has `input_schema`). These
59
+ * carry `cache_control` directly on the block; the LangChain adapter does NOT
60
+ * promote `extras.cache_control` for them. LangChain StructuredTools, by
61
+ * contrast, expose the marker via `extras`.
62
+ */
63
+ function isProviderShapedTool(tool) {
64
+ return isAnthropicBuiltInTool(tool) || "input_schema" in tool;
65
+ }
66
+ function markCacheControl(tool, ttl) {
67
+ const cacheControl = buildAnthropicCacheControl(ttl);
68
+ const prototype = Object.getPrototypeOf(tool) ?? Object.prototype;
69
+ if (isProviderShapedTool(tool)) {
27
70
  const wrapped = { ...tool };
28
71
  delete wrapped.extras;
29
- return Object.assign(Object.create(prototype), wrapped, { cache_control: CACHE_CONTROL });
72
+ return Object.assign(Object.create(prototype), wrapped, { cache_control: cacheControl });
30
73
  }
31
- return Object.assign(Object.create(prototype), tool, { extras: {
74
+ const wrapped = { ...tool };
75
+ delete wrapped.cache_control;
76
+ return Object.assign(Object.create(prototype), wrapped, { extras: {
32
77
  ...tool.extras ?? {},
33
- cache_control: CACHE_CONTROL
78
+ cache_control: cacheControl
34
79
  } });
35
80
  }
36
81
  /**
@@ -59,7 +104,7 @@ function makeIsDeferred(toolDefinitions) {
59
104
  * `instanceof` checks still pass. For custom tools, `extras` is merged
60
105
  * so any existing `providerToolDefinition` / other extras are kept.
61
106
  */
62
- function partitionAndMarkAnthropicToolCache(tools, isDeferred) {
107
+ function partitionAndMarkAnthropicToolCache(tools, isDeferred, ttl) {
63
108
  if (tools == null || tools.length === 0) return tools;
64
109
  const staticTools = [];
65
110
  const deferredTools = [];
@@ -68,13 +113,30 @@ function partitionAndMarkAnthropicToolCache(tools, isDeferred) {
68
113
  if (typeof name === "string" && isDeferred(name)) deferredTools.push(tool);
69
114
  else staticTools.push(tool);
70
115
  }
71
- if (staticTools.length === 0) return tools;
116
+ let mutated = false;
117
+ for (let i = 0; i < deferredTools.length; i++) {
118
+ const candidate = deferredTools[i];
119
+ if (hasCacheControl(candidate)) {
120
+ deferredTools[i] = stripCacheControl(candidate);
121
+ mutated = true;
122
+ }
123
+ }
124
+ if (staticTools.length === 0) return mutated ? [...deferredTools] : tools;
125
+ for (let i = 0; i < staticTools.length - 1; i++) {
126
+ const candidate = staticTools[i];
127
+ if (hasCacheControl(candidate)) {
128
+ staticTools[i] = stripCacheControl(candidate);
129
+ mutated = true;
130
+ }
131
+ }
72
132
  const last = staticTools[staticTools.length - 1];
73
- if (hasCacheControl(last)) {
74
- if (deferredTools.length === 0) return tools;
75
- return [...staticTools, ...deferredTools];
133
+ const desiredTtl = ttl === "1h" ? "1h" : void 0;
134
+ const effective = getEffectiveCacheControl(last);
135
+ if (!(effective != null && (effective.ttl === "1h" ? "1h" : void 0) === desiredTtl)) {
136
+ staticTools[staticTools.length - 1] = markCacheControl(last, ttl);
137
+ mutated = true;
76
138
  }
77
- staticTools[staticTools.length - 1] = markCacheControl(last);
139
+ if (!mutated && deferredTools.length === 0) return tools;
78
140
  return [...staticTools, ...deferredTools];
79
141
  }
80
142
  //#endregion
@@ -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