claude-sdk-proxy 3.1.1 → 3.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-sdk-proxy",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
4
4
  "description": "Anthropic Messages API proxy backed by Claude Agent SDK — use Claude Max with any API client",
5
5
  "type": "module",
6
6
  "main": "./src/proxy/server.ts",
@@ -221,7 +221,23 @@ function parseToolUse(text: string): { toolCalls: ToolCall[]; textBefore: string
221
221
  while ((m = xmlRegex.exec(text)) !== null) {
222
222
  if (firstIdx < 0) firstIdx = m.index
223
223
  try {
224
- const p = JSON.parse(m[1]!.trim())
224
+ const raw = m[1]!.trim()
225
+ let p: any
226
+ try { p = JSON.parse(raw) } catch {
227
+ // Repair: models often emit raw newlines in heredocs/multiline commands
228
+ // which are invalid inside JSON strings. Escape them only inside quoted values.
229
+ let fixed = "", inStr = false, esc = false
230
+ for (const ch of raw) {
231
+ if (esc) { fixed += ch; esc = false; continue }
232
+ if (ch === "\\") { esc = true; fixed += ch; continue }
233
+ if (ch === '"') { inStr = !inStr; fixed += ch; continue }
234
+ if (inStr && ch === "\n") { fixed += "\\n"; continue }
235
+ if (inStr && ch === "\r") { fixed += "\\r"; continue }
236
+ if (inStr && ch === "\t") { fixed += "\\t"; continue }
237
+ fixed += ch
238
+ }
239
+ p = JSON.parse(fixed)
240
+ }
225
241
  calls.push({
226
242
  id: generateId("toolu_"),
227
243
  name: String(p.name ?? ""),
@@ -950,6 +966,18 @@ export function createProxyServer(config: Partial<ProxyConfig> = {}) {
950
966
 
951
967
  if (hasTools) {
952
968
  const { toolCalls, textBefore } = parseToolUse(fullText)
969
+ // Debug: log when tools were expected but none parsed
970
+ if (toolCalls.length === 0 && fullText.length > 0) {
971
+ logDebug("tool_parse.no_match", {
972
+ reqId,
973
+ outputLen: fullText.length,
974
+ textPreview: fullText.slice(0, 500),
975
+ hasToolUseTag: fullText.includes("<tool_use>"),
976
+ hasFunctionCalls: fullText.includes("<function_calls>"),
977
+ hasInvoke: fullText.includes("<invoke"),
978
+ hasAntmlInvoke: fullText.includes("antml:invoke"),
979
+ })
980
+ }
953
981
  const content: any[] = []
954
982
  if (textBefore) content.push({ type: "text", text: textBefore })
955
983
  for (const tc of toolCalls) content.push({ type: "tool_use", id: tc.id, name: tc.name, input: tc.input })
@@ -1163,6 +1191,19 @@ export function createProxyServer(config: Partial<ProxyConfig> = {}) {
1163
1191
  traceStore.phase(reqId, "responding")
1164
1192
  const { toolCalls, textBefore } = parseToolUse(fullText)
1165
1193
 
1194
+ // Debug: log when tools were expected but none parsed
1195
+ if (toolCalls.length === 0 && fullText.length > 0) {
1196
+ logDebug("tool_parse.no_match_stream", {
1197
+ reqId,
1198
+ outputLen: fullText.length,
1199
+ textPreview: fullText.slice(0, 500),
1200
+ hasToolUseTag: fullText.includes("<tool_use>"),
1201
+ hasFunctionCalls: fullText.includes("<function_calls>"),
1202
+ hasInvoke: fullText.includes("<invoke"),
1203
+ hasAntmlInvoke: fullText.includes("antml:invoke"),
1204
+ })
1205
+ }
1206
+
1166
1207
  let blockIdx = 0
1167
1208
  const textContent = toolCalls.length === 0 ? (fullText || "...") : textBefore
1168
1209
  if (textContent) {