@code-yeongyu/senpi-ai 2026.9.24 → 2026.9.25

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 (73) hide show
  1. package/dist/api/anthropic-tool-references.d.ts.map +1 -1
  2. package/dist/api/anthropic-tool-references.js +8 -35
  3. package/dist/api/anthropic-tool-references.js.map +1 -1
  4. package/dist/api/bedrock-converse-stream.js +16 -3
  5. package/dist/api/bedrock-converse-stream.js.map +1 -1
  6. package/dist/api/google-shared.d.ts.map +1 -1
  7. package/dist/api/google-shared.js +24 -17
  8. package/dist/api/google-shared.js.map +1 -1
  9. package/dist/api/openai-completions.d.ts.map +1 -1
  10. package/dist/api/openai-completions.js +67 -9
  11. package/dist/api/openai-completions.js.map +1 -1
  12. package/dist/api/openai-responses-prompt-cache.d.ts +24 -0
  13. package/dist/api/openai-responses-prompt-cache.d.ts.map +1 -0
  14. package/dist/api/openai-responses-prompt-cache.js +62 -0
  15. package/dist/api/openai-responses-prompt-cache.js.map +1 -0
  16. package/dist/api/openai-responses-shared.d.ts +6 -0
  17. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  18. package/dist/api/openai-responses-shared.js +16 -7
  19. package/dist/api/openai-responses-shared.js.map +1 -1
  20. package/dist/api/openai-responses.d.ts +11 -1
  21. package/dist/api/openai-responses.d.ts.map +1 -1
  22. package/dist/api/openai-responses.js +114 -8
  23. package/dist/api/openai-responses.js.map +1 -1
  24. package/dist/api/openai-responses.lazy.d.ts.map +1 -1
  25. package/dist/api/openai-responses.lazy.js +5 -0
  26. package/dist/api/openai-responses.lazy.js.map +1 -1
  27. package/dist/api/openrouter-images.js +1 -1
  28. package/dist/api/openrouter-images.js.map +1 -1
  29. package/dist/api/prompt-cache-warmers.d.ts +13 -0
  30. package/dist/api/prompt-cache-warmers.d.ts.map +1 -0
  31. package/dist/api/prompt-cache-warmers.js +8 -0
  32. package/dist/api/prompt-cache-warmers.js.map +1 -0
  33. package/dist/api/warm-prompt-cache.d.ts +4 -2
  34. package/dist/api/warm-prompt-cache.d.ts.map +1 -1
  35. package/dist/api/warm-prompt-cache.js +22 -0
  36. package/dist/api/warm-prompt-cache.js.map +1 -1
  37. package/dist/index.d.ts +3 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +3 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/models.d.ts +6 -0
  42. package/dist/models.d.ts.map +1 -1
  43. package/dist/models.js +15 -0
  44. package/dist/models.js.map +1 -1
  45. package/dist/openai-responses-compat.d.ts +17 -0
  46. package/dist/openai-responses-compat.d.ts.map +1 -1
  47. package/dist/openai-responses-compat.js +4 -1
  48. package/dist/openai-responses-compat.js.map +1 -1
  49. package/dist/providers/data/.manifest.json +1 -1
  50. package/dist/providers/data/cerebras.json +1 -1
  51. package/dist/providers/data/chatgpt-subscription.json +1 -1
  52. package/dist/providers/data/google-vertex.json +1 -1
  53. package/dist/providers/data/openai.json +1 -1
  54. package/dist/providers/data/openrouter.json +1 -1
  55. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  56. package/dist/types.d.ts +19 -0
  57. package/dist/types.d.ts.map +1 -1
  58. package/dist/types.js.map +1 -1
  59. package/dist/utils/prompt-cache-ttl.d.ts +30 -0
  60. package/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
  61. package/dist/utils/prompt-cache-ttl.js +71 -14
  62. package/dist/utils/prompt-cache-ttl.js.map +1 -1
  63. package/dist/utils/retry.d.ts.map +1 -1
  64. package/dist/utils/retry.js +8 -0
  65. package/dist/utils/retry.js.map +1 -1
  66. package/dist/utils/tool-choice-fallback.d.ts.map +1 -1
  67. package/dist/utils/tool-choice-fallback.js +7 -1
  68. package/dist/utils/tool-choice-fallback.js.map +1 -1
  69. package/dist/utils/tool-name-match.d.ts +30 -0
  70. package/dist/utils/tool-name-match.d.ts.map +1 -0
  71. package/dist/utils/tool-name-match.js +71 -0
  72. package/dist/utils/tool-name-match.js.map +1 -0
  73. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-tool-references.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-tool-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,4BAA4B,EAE5B,MAAM,uDAAuD,CAAC;AA4F/D,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,4BAA4B,GAAG,4BAA4B,CAkIlH"}
1
+ {"version":3,"file":"anthropic-tool-references.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-tool-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,4BAA4B,EAE5B,MAAM,uDAAuD,CAAC;AA8D/D,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,4BAA4B,GAAG,4BAA4B,CAkIlH"}
@@ -1,3 +1,4 @@
1
+ import { resolveToolNameMatch } from "../utils/tool-name-match.js";
1
2
  import { demotedToolCallText, demotedToolResultText } from "../utils/unavailable-tool-text.js";
2
3
  function isRecord(value) {
3
4
  return typeof value === "object" && value !== null;
@@ -27,14 +28,11 @@ function isRecord(value) {
27
28
  * The same wire path can also recase the tool it namespaces (`memory` comes
28
29
  * back as `mcp__a4e6__Memory`, `lsp_symbols` as `mcp__a4e6__LspSymbols`), so
29
30
  * the suffix alone no longer matches the request's tool name byte for byte.
30
- * Names are therefore compared with case and `_`/`-` separators folded away,
31
- * and a folded key resolves only when exactly one request tool owns it: the
32
- * fold never guesses between two candidates.
31
+ * Names are therefore resolved by the shared lenient matcher
32
+ * (`utils/tool-name-match.ts`, also used by the inbound tool-call resolver):
33
+ * case and `_`/`-` folded away, an `mcp_`/`mcp__` prefix stripped on either
34
+ * side, and a name resolves only when exactly one request tool matches.
33
35
  */
34
- const GATEWAY_TOOL_NAMESPACE = /^mcp__[^_]+__(.+)$/;
35
- function foldToolNameKey(name) {
36
- return name.toLowerCase().replaceAll(/[-_]/g, "");
37
- }
38
36
  function collectAvailableToolNames(tools) {
39
37
  const defined = new Set();
40
38
  if (Array.isArray(tools)) {
@@ -43,32 +41,7 @@ function collectAvailableToolNames(tools) {
43
41
  defined.add(tool.name);
44
42
  }
45
43
  }
46
- const folded = new Map();
47
- const ambiguous = new Set();
48
- for (const name of defined) {
49
- const key = foldToolNameKey(name);
50
- if (folded.has(key))
51
- ambiguous.add(key);
52
- else
53
- folded.set(key, name);
54
- }
55
- for (const key of ambiguous)
56
- folded.delete(key);
57
- return { defined, folded };
58
- }
59
- function resolveAvailableToolName(name, available) {
60
- const suffix = GATEWAY_TOOL_NAMESPACE.exec(name)?.[1];
61
- const candidates = suffix === undefined ? [name] : [name, suffix];
62
- for (const candidate of candidates) {
63
- if (available.defined.has(candidate))
64
- return candidate;
65
- }
66
- for (const candidate of candidates) {
67
- const folded = available.folded.get(foldToolNameKey(candidate));
68
- if (folded !== undefined)
69
- return folded;
70
- }
71
- return undefined;
44
+ return defined;
72
45
  }
73
46
  function isNativeToolSearchResultBlock(block) {
74
47
  return (isRecord(block) &&
@@ -82,7 +55,7 @@ export function demoteUnavailableToolReferences(params) {
82
55
  if (!Array.isArray(messages) || messages.length === 0)
83
56
  return params;
84
57
  const available = collectAvailableToolNames(params.tools);
85
- const resolve = (name) => resolveAvailableToolName(name, available);
58
+ const resolve = (name) => resolveToolNameMatch(name, available);
86
59
  const demotedCallNames = new Map();
87
60
  const renamedCallNames = new Map();
88
61
  for (const message of messages) {
@@ -99,7 +72,7 @@ export function demoteUnavailableToolReferences(params) {
99
72
  }
100
73
  }
101
74
  let changed = false;
102
- const availableToolNames = [...available.defined];
75
+ const availableToolNames = [...available];
103
76
  const seenDemotedCallNames = new Set();
104
77
  const rewrittenMessages = [];
105
78
  for (const message of messages) {
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-tool-references.js","sourceRoot":"","sources":["../../src/api/anthropic-tool-references.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tBetaContentBlockParam as ContentBlockParam,\n\tMessageCreateParamsStreaming,\n\tBetaMessageParam as MessageParam,\n} from \"@anthropic-ai/sdk/resources/beta/messages/messages.js\";\nimport { demotedToolCallText, demotedToolResultText } from \"../utils/unavailable-tool-text.ts\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\n/**\n * Anthropic validates that every tool referenced by the message history is\n * available in the same request — defined in `tools` or discovered through a\n * `tool_reference` block — and rejects the whole request otherwise\n * (\"Tool reference '<name>' not found in available tools\"). Sessions outlive\n * their tools: an MCP server can be absent after a resume, an extension can\n * stop registering a tool, or a payload hook can strip a definition while the\n * history still carries the call. Demote those references to plain text so\n * the turn can proceed; the matching tool_result is demoted in lockstep so no\n * orphan pairing error replaces the original one.\n *\n * Availability is decided by the request's `tools` array alone. A discovered\n * name never stands in for a missing definition: a `tool_reference` without a\n * definition is itself rejected, so it cannot keep a later `tool_use` alive.\n *\n * Native tool search results (`tool_search_tool_result`) replay verbatim on the\n * same model, and the wire path can hand their references back under a gateway\n * namespace (`mcp__<id>__<tool>`) that senpi never defined and that does not\n * survive across requests. Those references are folded back to the request's\n * own tool names; a reference that still does not resolve is dropped, and a\n * search pair left with no references is demoted to text.\n *\n * The same wire path can also recase the tool it namespaces (`memory` comes\n * back as `mcp__a4e6__Memory`, `lsp_symbols` as `mcp__a4e6__LspSymbols`), so\n * the suffix alone no longer matches the request's tool name byte for byte.\n * Names are therefore compared with case and `_`/`-` separators folded away,\n * and a folded key resolves only when exactly one request tool owns it: the\n * fold never guesses between two candidates.\n */\nconst GATEWAY_TOOL_NAMESPACE = /^mcp__[^_]+__(.+)$/;\n\ninterface AvailableToolNames {\n\treadonly defined: ReadonlySet<string>;\n\treadonly folded: ReadonlyMap<string, string>;\n}\n\nfunction foldToolNameKey(name: string): string {\n\treturn name.toLowerCase().replaceAll(/[-_]/g, \"\");\n}\n\nfunction collectAvailableToolNames(tools: unknown): AvailableToolNames {\n\tconst defined = new Set<string>();\n\tif (Array.isArray(tools)) {\n\t\tfor (const tool of tools) {\n\t\t\tif (isRecord(tool) && typeof tool.name === \"string\") defined.add(tool.name);\n\t\t}\n\t}\n\tconst folded = new Map<string, string>();\n\tconst ambiguous = new Set<string>();\n\tfor (const name of defined) {\n\t\tconst key = foldToolNameKey(name);\n\t\tif (folded.has(key)) ambiguous.add(key);\n\t\telse folded.set(key, name);\n\t}\n\tfor (const key of ambiguous) folded.delete(key);\n\treturn { defined, folded };\n}\n\nfunction resolveAvailableToolName(name: string, available: AvailableToolNames): string | undefined {\n\tconst suffix = GATEWAY_TOOL_NAMESPACE.exec(name)?.[1];\n\tconst candidates = suffix === undefined ? [name] : [name, suffix];\n\tfor (const candidate of candidates) {\n\t\tif (available.defined.has(candidate)) return candidate;\n\t}\n\tfor (const candidate of candidates) {\n\t\tconst folded = available.folded.get(foldToolNameKey(candidate));\n\t\tif (folded !== undefined) return folded;\n\t}\n\treturn undefined;\n}\n\nfunction isNativeToolSearchResultBlock(block: unknown): block is Record<string, unknown> & {\n\ttype: \"tool_search_tool_result\";\n\ttool_use_id: string;\n\tcontent: Record<string, unknown> & { tool_references: unknown[] };\n} {\n\treturn (\n\t\tisRecord(block) &&\n\t\tblock.type === \"tool_search_tool_result\" &&\n\t\ttypeof block.tool_use_id === \"string\" &&\n\t\tisRecord(block.content) &&\n\t\tArray.isArray(block.content.tool_references)\n\t);\n}\n\nexport function demoteUnavailableToolReferences(params: MessageCreateParamsStreaming): MessageCreateParamsStreaming {\n\tconst messages = params.messages;\n\tif (!Array.isArray(messages) || messages.length === 0) return params;\n\n\tconst available = collectAvailableToolNames(params.tools);\n\tconst resolve = (name: string): string | undefined => resolveAvailableToolName(name, available);\n\n\tconst demotedCallNames = new Map<string, string>();\n\tconst renamedCallNames = new Map<string, string>();\n\tfor (const message of messages) {\n\t\tif (message.role !== \"assistant\" || !Array.isArray(message.content)) continue;\n\t\tfor (const block of message.content) {\n\t\t\tif (!isRecord(block) || block.type !== \"tool_use\" || typeof block.name !== \"string\") continue;\n\t\t\tconst resolved = resolve(block.name);\n\t\t\tif (resolved === undefined) demotedCallNames.set(block.id, block.name);\n\t\t\telse if (resolved !== block.name) renamedCallNames.set(block.id, resolved);\n\t\t}\n\t}\n\n\tlet changed = false;\n\tconst availableToolNames = [...available.defined];\n\tconst seenDemotedCallNames = new Set<string>();\n\tconst rewrittenMessages: MessageParam[] = [];\n\tfor (const message of messages) {\n\t\tif (!Array.isArray(message.content)) {\n\t\t\trewrittenMessages.push(message);\n\t\t\tcontinue;\n\t\t}\n\t\tlet messageChanged = false;\n\t\t// A native search pair whose every reference stopped resolving is demoted\n\t\t// as a unit: the result decides, and its `server_tool_use` follows.\n\t\tconst droppedSearchUseIds = new Set<string>();\n\t\tconst droppedSearchNames = new Map<string, string[]>();\n\t\tif (message.role === \"assistant\") {\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (!isNativeToolSearchResultBlock(block)) continue;\n\t\t\t\tconst names = block.content.tool_references\n\t\t\t\t\t.filter((item): item is Record<string, unknown> => isRecord(item) && item.type === \"tool_reference\")\n\t\t\t\t\t.map((item) => (typeof item.tool_name === \"string\" ? item.tool_name : \"\"));\n\t\t\t\tif (names.length > 0 && names.every((name) => resolve(name) === undefined)) {\n\t\t\t\t\tdroppedSearchUseIds.add(block.tool_use_id);\n\t\t\t\t\tdroppedSearchNames.set(block.tool_use_id, names);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst content: ContentBlockParam[] = [];\n\t\tfor (const block of message.content) {\n\t\t\tif (message.role === \"assistant\" && isRecord(block) && block.type === \"tool_use\") {\n\t\t\t\tconst demotedName = demotedCallNames.get(block.id);\n\t\t\t\tif (demotedName !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tconst firstOccurrence = !seenDemotedCallNames.has(demotedName);\n\t\t\t\t\tseenDemotedCallNames.add(demotedName);\n\t\t\t\t\tcontent.push({\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: demotedToolCallText(demotedName, availableToolNames, firstOccurrence),\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst renamedName = renamedCallNames.get(block.id);\n\t\t\t\tif (renamedName !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontent.push({ ...block, name: renamedName } as ContentBlockParam);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (message.role === \"assistant\" && isRecord(block) && block.type === \"server_tool_use\") {\n\t\t\t\tif (typeof block.id === \"string\" && droppedSearchUseIds.has(block.id)) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (message.role === \"assistant\" && isNativeToolSearchResultBlock(block)) {\n\t\t\t\tconst omitted = droppedSearchNames.get(block.tool_use_id);\n\t\t\t\tif (omitted !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontent.push({ type: \"text\", text: `Tool reference unavailable: ${[...new Set(omitted)].join(\", \")}` });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst rewritten = rewriteToolReferenceItems(block.content.tool_references, resolve);\n\t\t\t\tif (rewritten !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t...block,\n\t\t\t\t\t\tcontent: { ...block.content, tool_references: rewritten.kept },\n\t\t\t\t\t} as ContentBlockParam);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isRecord(block) && block.type === \"tool_result\") {\n\t\t\t\tconst demotedName = demotedCallNames.get(block.tool_use_id);\n\t\t\t\tif (demotedName !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontent.push({ type: \"text\", text: demotedToolResultText(demotedName, toolResultText(block.content)) });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(block.content)) {\n\t\t\t\t\tconst rewritten = rewriteToolReferenceItems(block.content, resolve);\n\t\t\t\t\tif (rewritten !== undefined) {\n\t\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\t\tconst nextContent =\n\t\t\t\t\t\t\trewritten.kept.length > 0\n\t\t\t\t\t\t\t\t? rewritten.kept\n\t\t\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\t\ttext: `Tool reference unavailable: ${[...new Set(rewritten.omitted)].join(\", \")}`,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\tcontent.push({ ...block, content: nextContent } as ContentBlockParam);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontent.push(block);\n\t\t}\n\t\tif (content.length === 0) {\n\t\t\tchanged = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (messageChanged) {\n\t\t\tchanged = true;\n\t\t\trewrittenMessages.push({ ...message, content });\n\t\t\tcontinue;\n\t\t}\n\t\trewrittenMessages.push(message);\n\t}\n\n\tif (!changed) return params;\n\treturn { ...params, messages: rewrittenMessages };\n}\n\n/**\n * Folds every `tool_reference` item in `items` onto the request's own tool\n * name and drops the ones that still do not resolve. Returns undefined when\n * nothing changed so callers can keep the original block identity.\n */\nfunction rewriteToolReferenceItems(\n\titems: readonly unknown[],\n\tresolve: (name: string) => string | undefined,\n): { kept: unknown[]; omitted: string[] } | undefined {\n\tconst kept: unknown[] = [];\n\tconst omitted: string[] = [];\n\tlet rewritten = false;\n\tfor (const item of items) {\n\t\tif (!isRecord(item) || item.type !== \"tool_reference\" || typeof item.tool_name !== \"string\") {\n\t\t\tkept.push(item);\n\t\t\tcontinue;\n\t\t}\n\t\tconst resolved = resolve(item.tool_name);\n\t\tif (resolved === undefined) {\n\t\t\tomitted.push(item.tool_name);\n\t\t\trewritten = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (resolved !== item.tool_name) {\n\t\t\tkept.push({ ...item, tool_name: resolved });\n\t\t\trewritten = true;\n\t\t\tcontinue;\n\t\t}\n\t\tkept.push(item);\n\t}\n\treturn rewritten ? { kept, omitted } : undefined;\n}\n\nfunction toolResultText(content: unknown): string {\n\tif (typeof content === \"string\") return content;\n\tif (Array.isArray(content)) {\n\t\tconst parts: string[] = [];\n\t\tfor (const item of content) {\n\t\t\tif (!isRecord(item)) continue;\n\t\t\tif (item.type === \"text\" && typeof item.text === \"string\") parts.push(item.text);\n\t\t\telse if (typeof item.type === \"string\") parts.push(`[${item.type}]`);\n\t\t}\n\t\tif (parts.length > 0) return parts.join(\"\\n\");\n\t}\n\treturn \"Tool output unavailable.\";\n}\n"]}
1
+ {"version":3,"file":"anthropic-tool-references.js","sourceRoot":"","sources":["../../src/api/anthropic-tool-references.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tBetaContentBlockParam as ContentBlockParam,\n\tMessageCreateParamsStreaming,\n\tBetaMessageParam as MessageParam,\n} from \"@anthropic-ai/sdk/resources/beta/messages/messages.js\";\nimport { resolveToolNameMatch } from \"../utils/tool-name-match.ts\";\nimport { demotedToolCallText, demotedToolResultText } from \"../utils/unavailable-tool-text.ts\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\n/**\n * Anthropic validates that every tool referenced by the message history is\n * available in the same request — defined in `tools` or discovered through a\n * `tool_reference` block — and rejects the whole request otherwise\n * (\"Tool reference '<name>' not found in available tools\"). Sessions outlive\n * their tools: an MCP server can be absent after a resume, an extension can\n * stop registering a tool, or a payload hook can strip a definition while the\n * history still carries the call. Demote those references to plain text so\n * the turn can proceed; the matching tool_result is demoted in lockstep so no\n * orphan pairing error replaces the original one.\n *\n * Availability is decided by the request's `tools` array alone. A discovered\n * name never stands in for a missing definition: a `tool_reference` without a\n * definition is itself rejected, so it cannot keep a later `tool_use` alive.\n *\n * Native tool search results (`tool_search_tool_result`) replay verbatim on the\n * same model, and the wire path can hand their references back under a gateway\n * namespace (`mcp__<id>__<tool>`) that senpi never defined and that does not\n * survive across requests. Those references are folded back to the request's\n * own tool names; a reference that still does not resolve is dropped, and a\n * search pair left with no references is demoted to text.\n *\n * The same wire path can also recase the tool it namespaces (`memory` comes\n * back as `mcp__a4e6__Memory`, `lsp_symbols` as `mcp__a4e6__LspSymbols`), so\n * the suffix alone no longer matches the request's tool name byte for byte.\n * Names are therefore resolved by the shared lenient matcher\n * (`utils/tool-name-match.ts`, also used by the inbound tool-call resolver):\n * case and `_`/`-` folded away, an `mcp_`/`mcp__` prefix stripped on either\n * side, and a name resolves only when exactly one request tool matches.\n */\nfunction collectAvailableToolNames(tools: unknown): ReadonlySet<string> {\n\tconst defined = new Set<string>();\n\tif (Array.isArray(tools)) {\n\t\tfor (const tool of tools) {\n\t\t\tif (isRecord(tool) && typeof tool.name === \"string\") defined.add(tool.name);\n\t\t}\n\t}\n\treturn defined;\n}\n\nfunction isNativeToolSearchResultBlock(block: unknown): block is Record<string, unknown> & {\n\ttype: \"tool_search_tool_result\";\n\ttool_use_id: string;\n\tcontent: Record<string, unknown> & { tool_references: unknown[] };\n} {\n\treturn (\n\t\tisRecord(block) &&\n\t\tblock.type === \"tool_search_tool_result\" &&\n\t\ttypeof block.tool_use_id === \"string\" &&\n\t\tisRecord(block.content) &&\n\t\tArray.isArray(block.content.tool_references)\n\t);\n}\n\nexport function demoteUnavailableToolReferences(params: MessageCreateParamsStreaming): MessageCreateParamsStreaming {\n\tconst messages = params.messages;\n\tif (!Array.isArray(messages) || messages.length === 0) return params;\n\n\tconst available = collectAvailableToolNames(params.tools);\n\tconst resolve = (name: string): string | undefined => resolveToolNameMatch(name, available);\n\n\tconst demotedCallNames = new Map<string, string>();\n\tconst renamedCallNames = new Map<string, string>();\n\tfor (const message of messages) {\n\t\tif (message.role !== \"assistant\" || !Array.isArray(message.content)) continue;\n\t\tfor (const block of message.content) {\n\t\t\tif (!isRecord(block) || block.type !== \"tool_use\" || typeof block.name !== \"string\") continue;\n\t\t\tconst resolved = resolve(block.name);\n\t\t\tif (resolved === undefined) demotedCallNames.set(block.id, block.name);\n\t\t\telse if (resolved !== block.name) renamedCallNames.set(block.id, resolved);\n\t\t}\n\t}\n\n\tlet changed = false;\n\tconst availableToolNames = [...available];\n\tconst seenDemotedCallNames = new Set<string>();\n\tconst rewrittenMessages: MessageParam[] = [];\n\tfor (const message of messages) {\n\t\tif (!Array.isArray(message.content)) {\n\t\t\trewrittenMessages.push(message);\n\t\t\tcontinue;\n\t\t}\n\t\tlet messageChanged = false;\n\t\t// A native search pair whose every reference stopped resolving is demoted\n\t\t// as a unit: the result decides, and its `server_tool_use` follows.\n\t\tconst droppedSearchUseIds = new Set<string>();\n\t\tconst droppedSearchNames = new Map<string, string[]>();\n\t\tif (message.role === \"assistant\") {\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (!isNativeToolSearchResultBlock(block)) continue;\n\t\t\t\tconst names = block.content.tool_references\n\t\t\t\t\t.filter((item): item is Record<string, unknown> => isRecord(item) && item.type === \"tool_reference\")\n\t\t\t\t\t.map((item) => (typeof item.tool_name === \"string\" ? item.tool_name : \"\"));\n\t\t\t\tif (names.length > 0 && names.every((name) => resolve(name) === undefined)) {\n\t\t\t\t\tdroppedSearchUseIds.add(block.tool_use_id);\n\t\t\t\t\tdroppedSearchNames.set(block.tool_use_id, names);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst content: ContentBlockParam[] = [];\n\t\tfor (const block of message.content) {\n\t\t\tif (message.role === \"assistant\" && isRecord(block) && block.type === \"tool_use\") {\n\t\t\t\tconst demotedName = demotedCallNames.get(block.id);\n\t\t\t\tif (demotedName !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tconst firstOccurrence = !seenDemotedCallNames.has(demotedName);\n\t\t\t\t\tseenDemotedCallNames.add(demotedName);\n\t\t\t\t\tcontent.push({\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: demotedToolCallText(demotedName, availableToolNames, firstOccurrence),\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst renamedName = renamedCallNames.get(block.id);\n\t\t\t\tif (renamedName !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontent.push({ ...block, name: renamedName } as ContentBlockParam);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (message.role === \"assistant\" && isRecord(block) && block.type === \"server_tool_use\") {\n\t\t\t\tif (typeof block.id === \"string\" && droppedSearchUseIds.has(block.id)) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (message.role === \"assistant\" && isNativeToolSearchResultBlock(block)) {\n\t\t\t\tconst omitted = droppedSearchNames.get(block.tool_use_id);\n\t\t\t\tif (omitted !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontent.push({ type: \"text\", text: `Tool reference unavailable: ${[...new Set(omitted)].join(\", \")}` });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst rewritten = rewriteToolReferenceItems(block.content.tool_references, resolve);\n\t\t\t\tif (rewritten !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t...block,\n\t\t\t\t\t\tcontent: { ...block.content, tool_references: rewritten.kept },\n\t\t\t\t\t} as ContentBlockParam);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isRecord(block) && block.type === \"tool_result\") {\n\t\t\t\tconst demotedName = demotedCallNames.get(block.tool_use_id);\n\t\t\t\tif (demotedName !== undefined) {\n\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\tcontent.push({ type: \"text\", text: demotedToolResultText(demotedName, toolResultText(block.content)) });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(block.content)) {\n\t\t\t\t\tconst rewritten = rewriteToolReferenceItems(block.content, resolve);\n\t\t\t\t\tif (rewritten !== undefined) {\n\t\t\t\t\t\tmessageChanged = true;\n\t\t\t\t\t\tconst nextContent =\n\t\t\t\t\t\t\trewritten.kept.length > 0\n\t\t\t\t\t\t\t\t? rewritten.kept\n\t\t\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\t\ttext: `Tool reference unavailable: ${[...new Set(rewritten.omitted)].join(\", \")}`,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\tcontent.push({ ...block, content: nextContent } as ContentBlockParam);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontent.push(block);\n\t\t}\n\t\tif (content.length === 0) {\n\t\t\tchanged = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (messageChanged) {\n\t\t\tchanged = true;\n\t\t\trewrittenMessages.push({ ...message, content });\n\t\t\tcontinue;\n\t\t}\n\t\trewrittenMessages.push(message);\n\t}\n\n\tif (!changed) return params;\n\treturn { ...params, messages: rewrittenMessages };\n}\n\n/**\n * Folds every `tool_reference` item in `items` onto the request's own tool\n * name and drops the ones that still do not resolve. Returns undefined when\n * nothing changed so callers can keep the original block identity.\n */\nfunction rewriteToolReferenceItems(\n\titems: readonly unknown[],\n\tresolve: (name: string) => string | undefined,\n): { kept: unknown[]; omitted: string[] } | undefined {\n\tconst kept: unknown[] = [];\n\tconst omitted: string[] = [];\n\tlet rewritten = false;\n\tfor (const item of items) {\n\t\tif (!isRecord(item) || item.type !== \"tool_reference\" || typeof item.tool_name !== \"string\") {\n\t\t\tkept.push(item);\n\t\t\tcontinue;\n\t\t}\n\t\tconst resolved = resolve(item.tool_name);\n\t\tif (resolved === undefined) {\n\t\t\tomitted.push(item.tool_name);\n\t\t\trewritten = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (resolved !== item.tool_name) {\n\t\t\tkept.push({ ...item, tool_name: resolved });\n\t\t\trewritten = true;\n\t\t\tcontinue;\n\t\t}\n\t\tkept.push(item);\n\t}\n\treturn rewritten ? { kept, omitted } : undefined;\n}\n\nfunction toolResultText(content: unknown): string {\n\tif (typeof content === \"string\") return content;\n\tif (Array.isArray(content)) {\n\t\tconst parts: string[] = [];\n\t\tfor (const item of content) {\n\t\t\tif (!isRecord(item)) continue;\n\t\t\tif (item.type === \"text\" && typeof item.text === \"string\") parts.push(item.text);\n\t\t\telse if (typeof item.type === \"string\") parts.push(`[${item.type}]`);\n\t\t}\n\t\tif (parts.length > 0) return parts.join(\"\\n\");\n\t}\n\treturn \"Tool output unavailable.\";\n}\n"]}
@@ -715,6 +715,19 @@ function convertToolResultContent(content) {
715
715
  result.push({ text: EMPTY_TEXT_PLACEHOLDER });
716
716
  return result;
717
717
  }
718
+ // Bedrock Converse requires the conversation to alternate between user and assistant roles and answers
719
+ // "A conversation must alternate between user and assistant roles" otherwise. Adjacent senpi messages can share a
720
+ // role (the hidden environment-context user message right before the prompt, a user prompt after tool results, a
721
+ // user message around a skipped empty assistant), so a message whose role matches the previous wire message is
722
+ // folded into it with its blocks kept in order.
723
+ function appendMessage(result, message) {
724
+ const previous = result[result.length - 1];
725
+ if (previous?.role === message.role && previous.content && message.content) {
726
+ previous.content.push(...message.content);
727
+ return;
728
+ }
729
+ result.push(message);
730
+ }
718
731
  function convertMessages(context, model, cacheRetention, options = {}) {
719
732
  const result = [];
720
733
  const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId, {
@@ -747,7 +760,7 @@ function convertMessages(context, model, cacheRetention, options = {}) {
747
760
  if (content.length === 0)
748
761
  content.push({ text: EMPTY_TEXT_PLACEHOLDER });
749
762
  }
750
- result.push({
763
+ appendMessage(result, {
751
764
  role: ConversationRole.USER,
752
765
  content,
753
766
  });
@@ -827,7 +840,7 @@ function convertMessages(context, model, cacheRetention, options = {}) {
827
840
  if (contentBlocks.length === 0) {
828
841
  continue;
829
842
  }
830
- result.push({
843
+ appendMessage(result, {
831
844
  role: ConversationRole.ASSISTANT,
832
845
  content: contentBlocks,
833
846
  });
@@ -860,7 +873,7 @@ function convertMessages(context, model, cacheRetention, options = {}) {
860
873
  }
861
874
  // Skip the messages we've already processed
862
875
  i = j - 1;
863
- result.push({
876
+ appendMessage(result, {
864
877
  role: ConversationRole.USER,
865
878
  content: toolResults,
866
879
  });