@vacbo/opencode-anthropic-fix 0.0.44 → 0.1.1
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/README.md +19 -0
- package/dist/bun-proxy.mjs +282 -55
- package/dist/opencode-anthropic-auth-cli.mjs +194 -55
- package/dist/opencode-anthropic-auth-plugin.js +1816 -594
- package/package.json +1 -1
- package/src/__tests__/billing-edge-cases.test.ts +84 -0
- package/src/__tests__/bun-proxy.parallel.test.ts +460 -0
- package/src/__tests__/debug-gating.test.ts +76 -0
- package/src/__tests__/decomposition-smoke.test.ts +92 -0
- package/src/__tests__/fingerprint-regression.test.ts +1 -1
- package/src/__tests__/helpers/conversation-history.smoke.test.ts +338 -0
- package/src/__tests__/helpers/conversation-history.ts +376 -0
- package/src/__tests__/helpers/deferred.smoke.test.ts +161 -0
- package/src/__tests__/helpers/deferred.ts +122 -0
- package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +166 -0
- package/src/__tests__/helpers/in-memory-storage.ts +152 -0
- package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +92 -0
- package/src/__tests__/helpers/mock-bun-proxy.ts +229 -0
- package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +337 -0
- package/src/__tests__/helpers/plugin-fetch-harness.ts +401 -0
- package/src/__tests__/helpers/sse.smoke.test.ts +243 -0
- package/src/__tests__/helpers/sse.ts +288 -0
- package/src/__tests__/index.parallel.test.ts +711 -0
- package/src/__tests__/sanitization-regex.test.ts +65 -0
- package/src/__tests__/state-bounds.test.ts +110 -0
- package/src/account-identity.test.ts +213 -0
- package/src/account-identity.ts +108 -0
- package/src/accounts.dedup.test.ts +696 -0
- package/src/accounts.test.ts +2 -1
- package/src/accounts.ts +485 -191
- package/src/bun-fetch.test.ts +379 -0
- package/src/bun-fetch.ts +447 -174
- package/src/bun-proxy.ts +289 -57
- package/src/circuit-breaker.test.ts +274 -0
- package/src/circuit-breaker.ts +235 -0
- package/src/cli.test.ts +1 -0
- package/src/cli.ts +37 -18
- package/src/commands/router.ts +25 -5
- package/src/env.ts +1 -0
- package/src/headers/billing.ts +31 -13
- package/src/index.ts +224 -247
- package/src/oauth.ts +7 -1
- package/src/parent-pid-watcher.test.ts +219 -0
- package/src/parent-pid-watcher.ts +99 -0
- package/src/plugin-helpers.ts +112 -0
- package/src/refresh-helpers.ts +169 -0
- package/src/refresh-lock.test.ts +36 -9
- package/src/refresh-lock.ts +2 -2
- package/src/request/body.history.test.ts +398 -0
- package/src/request/body.ts +200 -13
- package/src/request/metadata.ts +6 -2
- package/src/response/index.ts +1 -1
- package/src/response/mcp.ts +60 -31
- package/src/response/streaming.test.ts +382 -0
- package/src/response/streaming.ts +403 -76
- package/src/storage.test.ts +127 -104
- package/src/storage.ts +152 -62
- package/src/system-prompt/builder.ts +33 -3
- package/src/system-prompt/sanitize.ts +12 -2
- package/src/token-refresh.test.ts +84 -1
- package/src/token-refresh.ts +14 -8
package/src/request/body.ts
CHANGED
|
@@ -2,39 +2,222 @@
|
|
|
2
2
|
// Request body transformation
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
+
import { CLAUDE_CODE_IDENTITY_STRING, KNOWN_IDENTITY_STRINGS } from "../constants.js";
|
|
5
6
|
import { buildSystemPromptBlocks } from "../system-prompt/builder.js";
|
|
6
7
|
import { normalizeSystemTextBlocks } from "../system-prompt/normalize.js";
|
|
7
8
|
import { normalizeThinkingBlock } from "../thinking.js";
|
|
8
9
|
import type { RuntimeContext, SignatureConfig } from "../types.js";
|
|
9
10
|
import { buildRequestMetadata } from "./metadata.js";
|
|
10
11
|
|
|
12
|
+
const TOOL_PREFIX = "mcp_";
|
|
13
|
+
|
|
14
|
+
function getBodyType(body: unknown): string {
|
|
15
|
+
if (body === null) return "null";
|
|
16
|
+
return typeof body;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getInvalidBodyError(body: unknown): TypeError {
|
|
20
|
+
return new TypeError(
|
|
21
|
+
`opencode-anthropic-auth: expected string body, got ${getBodyType(body)}. This plugin does not support stream bodies. Please file a bug with the OpenCode version.`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function validateBodyType(body: unknown, throwOnInvalid = false): body is string {
|
|
26
|
+
if (body === undefined || body === null) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (typeof body === "string") {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (throwOnInvalid) {
|
|
35
|
+
throw getInvalidBodyError(body);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function cloneBodyForRetry(body: string): string {
|
|
42
|
+
validateBodyType(body, true);
|
|
43
|
+
return body;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function detectDoublePrefix(name: string): boolean {
|
|
47
|
+
return name.startsWith(`${TOOL_PREFIX}${TOOL_PREFIX}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function extractToolNamesFromBody(body: string): string[] {
|
|
51
|
+
const parsed = JSON.parse(body) as {
|
|
52
|
+
tools?: Array<{ name?: unknown }>;
|
|
53
|
+
messages?: Array<{ content?: unknown }>;
|
|
54
|
+
};
|
|
55
|
+
const names: string[] = [];
|
|
56
|
+
|
|
57
|
+
if (Array.isArray(parsed.tools)) {
|
|
58
|
+
for (const tool of parsed.tools) {
|
|
59
|
+
if (typeof tool?.name === "string") {
|
|
60
|
+
names.push(tool.name);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (Array.isArray(parsed.messages)) {
|
|
66
|
+
for (const message of parsed.messages) {
|
|
67
|
+
if (!Array.isArray(message?.content)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const block of message.content) {
|
|
72
|
+
if (
|
|
73
|
+
block &&
|
|
74
|
+
typeof block === "object" &&
|
|
75
|
+
"type" in block &&
|
|
76
|
+
block.type === "tool_use" &&
|
|
77
|
+
"name" in block &&
|
|
78
|
+
typeof block.name === "string"
|
|
79
|
+
) {
|
|
80
|
+
names.push(block.name);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return names;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function prefixToolDefinitionName(name: unknown): unknown {
|
|
90
|
+
if (typeof name !== "string") {
|
|
91
|
+
return name;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (detectDoublePrefix(name)) {
|
|
95
|
+
throw new TypeError(`Double tool prefix detected: ${TOOL_PREFIX}${TOOL_PREFIX}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return `${TOOL_PREFIX}${name}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function prefixToolUseName(
|
|
102
|
+
name: unknown,
|
|
103
|
+
literalToolNames: ReadonlySet<string>,
|
|
104
|
+
debugLog?: (...args: unknown[]) => void,
|
|
105
|
+
): unknown {
|
|
106
|
+
if (typeof name !== "string") {
|
|
107
|
+
return name;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (detectDoublePrefix(name)) {
|
|
111
|
+
throw new TypeError(`Double tool prefix detected in tool_use block: ${name}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!name.startsWith(TOOL_PREFIX)) {
|
|
115
|
+
return `${TOOL_PREFIX}${name}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (literalToolNames.has(name)) {
|
|
119
|
+
return `${TOOL_PREFIX}${name}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
debugLog?.("prevented double-prefix drift for tool_use block", { name });
|
|
123
|
+
return name;
|
|
124
|
+
}
|
|
125
|
+
|
|
11
126
|
export function transformRequestBody(
|
|
12
127
|
body: string | undefined,
|
|
13
128
|
signature: SignatureConfig,
|
|
14
129
|
runtime: RuntimeContext,
|
|
130
|
+
relocateThirdPartyPrompts = true,
|
|
131
|
+
debugLog?: (...args: unknown[]) => void,
|
|
15
132
|
): string | undefined {
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
const TOOL_PREFIX = "mcp_";
|
|
133
|
+
if (body === undefined || body === null) return body;
|
|
134
|
+
validateBodyType(body, true);
|
|
19
135
|
|
|
20
136
|
try {
|
|
21
|
-
const parsed = JSON.parse(body)
|
|
137
|
+
const parsed = JSON.parse(body) as Record<string, unknown> & {
|
|
138
|
+
tools?: Array<Record<string, unknown>>;
|
|
139
|
+
messages?: Array<Record<string, unknown>>;
|
|
140
|
+
thinking?: unknown;
|
|
141
|
+
model?: string;
|
|
142
|
+
metadata?: Record<string, unknown>;
|
|
143
|
+
system?: unknown[] | undefined;
|
|
144
|
+
};
|
|
145
|
+
const parsedMessages = Array.isArray(parsed.messages) ? parsed.messages : [];
|
|
146
|
+
const literalToolNames = new Set<string>(
|
|
147
|
+
Array.isArray(parsed.tools)
|
|
148
|
+
? parsed.tools
|
|
149
|
+
.map((tool: Record<string, unknown>) => tool.name)
|
|
150
|
+
.filter((name: unknown): name is string => typeof name === "string")
|
|
151
|
+
: [],
|
|
152
|
+
);
|
|
153
|
+
|
|
22
154
|
if (Object.hasOwn(parsed, "betas")) {
|
|
23
155
|
delete parsed.betas;
|
|
24
156
|
}
|
|
25
157
|
// Normalize thinking block for adaptive (Opus 4.6) vs manual (older models).
|
|
26
158
|
if (Object.hasOwn(parsed, "thinking")) {
|
|
27
|
-
parsed.thinking = normalizeThinkingBlock(parsed.thinking, parsed.model || "");
|
|
159
|
+
parsed.thinking = normalizeThinkingBlock(parsed.thinking as unknown, parsed.model || "");
|
|
28
160
|
}
|
|
29
|
-
const hasThinking =
|
|
161
|
+
const hasThinking =
|
|
162
|
+
parsed.thinking &&
|
|
163
|
+
typeof parsed.thinking === "object" &&
|
|
164
|
+
(parsed.thinking as { type?: string }).type === "enabled";
|
|
30
165
|
if (hasThinking) {
|
|
31
166
|
delete parsed.temperature;
|
|
32
167
|
} else if (!Object.hasOwn(parsed, "temperature")) {
|
|
33
168
|
parsed.temperature = 1;
|
|
34
169
|
}
|
|
35
170
|
|
|
36
|
-
// Sanitize system prompt and
|
|
37
|
-
|
|
171
|
+
// Sanitize system prompt and inject Claude Code identity/billing blocks.
|
|
172
|
+
const allSystemBlocks = buildSystemPromptBlocks(
|
|
173
|
+
normalizeSystemTextBlocks(parsed.system),
|
|
174
|
+
signature,
|
|
175
|
+
parsedMessages,
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
if (signature.enabled && relocateThirdPartyPrompts) {
|
|
179
|
+
// Keep CC blocks in system. Move blocks with third-party identifiers
|
|
180
|
+
// into messages to avoid system prompt content detection.
|
|
181
|
+
const THIRD_PARTY_MARKERS =
|
|
182
|
+
/sisyphus|ohmyclaude|oh\s*my\s*claude|morph[_ ]|\.sisyphus\/|ultrawork|autopilot mode|\bohmy\b|SwarmMode|\bomc\b|\bomo\b/i;
|
|
183
|
+
|
|
184
|
+
const ccBlocks: typeof allSystemBlocks = [];
|
|
185
|
+
const extraBlocks: typeof allSystemBlocks = [];
|
|
186
|
+
for (const block of allSystemBlocks) {
|
|
187
|
+
const isBilling = block.text.startsWith("x-anthropic-billing-header:");
|
|
188
|
+
const isIdentity = block.text === CLAUDE_CODE_IDENTITY_STRING || KNOWN_IDENTITY_STRINGS.has(block.text);
|
|
189
|
+
const hasThirdParty = THIRD_PARTY_MARKERS.test(block.text);
|
|
190
|
+
|
|
191
|
+
if (isBilling || isIdentity || !hasThirdParty) {
|
|
192
|
+
ccBlocks.push(block);
|
|
193
|
+
} else {
|
|
194
|
+
extraBlocks.push(block);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
parsed.system = ccBlocks;
|
|
198
|
+
|
|
199
|
+
// Inject extra blocks as <system-instructions> in the first user message
|
|
200
|
+
if (extraBlocks.length > 0 && Array.isArray(parsed.messages) && parsed.messages.length > 0) {
|
|
201
|
+
const extraText = extraBlocks.map((b) => b.text).join("\n\n");
|
|
202
|
+
const wrapped = `<system-instructions>\n${extraText}\n</system-instructions>`;
|
|
203
|
+
const firstMsg = parsed.messages[0];
|
|
204
|
+
if (firstMsg && firstMsg.role === "user") {
|
|
205
|
+
if (typeof firstMsg.content === "string") {
|
|
206
|
+
firstMsg.content = `${wrapped}\n\n${firstMsg.content}`;
|
|
207
|
+
} else if (Array.isArray(firstMsg.content)) {
|
|
208
|
+
firstMsg.content.unshift({ type: "text", text: wrapped });
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
// No user message first — prepend a new user message
|
|
212
|
+
parsed.messages.unshift({
|
|
213
|
+
role: "user",
|
|
214
|
+
content: wrapped,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
parsed.system = allSystemBlocks;
|
|
220
|
+
}
|
|
38
221
|
|
|
39
222
|
if (signature.enabled) {
|
|
40
223
|
const currentMetadata =
|
|
@@ -55,7 +238,7 @@ export function transformRequestBody(
|
|
|
55
238
|
if (parsed.tools && Array.isArray(parsed.tools)) {
|
|
56
239
|
parsed.tools = parsed.tools.map((tool: Record<string, unknown>) => ({
|
|
57
240
|
...tool,
|
|
58
|
-
name: tool.name
|
|
241
|
+
name: prefixToolDefinitionName(tool.name),
|
|
59
242
|
}));
|
|
60
243
|
}
|
|
61
244
|
// Add prefix to tool_use blocks in messages
|
|
@@ -66,7 +249,7 @@ export function transformRequestBody(
|
|
|
66
249
|
if (block.type === "tool_use" && block.name) {
|
|
67
250
|
return {
|
|
68
251
|
...block,
|
|
69
|
-
name:
|
|
252
|
+
name: prefixToolUseName(block.name, literalToolNames, debugLog),
|
|
70
253
|
};
|
|
71
254
|
}
|
|
72
255
|
return block;
|
|
@@ -76,8 +259,12 @@ export function transformRequestBody(
|
|
|
76
259
|
});
|
|
77
260
|
}
|
|
78
261
|
return JSON.stringify(parsed);
|
|
79
|
-
} catch {
|
|
80
|
-
|
|
81
|
-
|
|
262
|
+
} catch (err) {
|
|
263
|
+
if (err instanceof SyntaxError) {
|
|
264
|
+
debugLog?.("body parse failed:", err.message);
|
|
265
|
+
return body;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
throw err;
|
|
82
269
|
}
|
|
83
270
|
}
|
package/src/request/metadata.ts
CHANGED
|
@@ -38,7 +38,10 @@ export function extractFileIds(parsed: unknown): string[] {
|
|
|
38
38
|
return ids;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export function parseRequestBodyMetadata(
|
|
41
|
+
export function parseRequestBodyMetadata(
|
|
42
|
+
body: string | undefined,
|
|
43
|
+
debugLog?: (...args: unknown[]) => void,
|
|
44
|
+
): RequestBodyMetadata {
|
|
42
45
|
if (!body || typeof body !== "string") {
|
|
43
46
|
return { model: "", tools: [], messages: [], hasFileReferences: false };
|
|
44
47
|
}
|
|
@@ -50,7 +53,8 @@ export function parseRequestBodyMetadata(body: string | undefined): RequestBodyM
|
|
|
50
53
|
const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
|
|
51
54
|
const hasFileReferences = extractFileIds(parsed).length > 0;
|
|
52
55
|
return { model, tools, messages, hasFileReferences };
|
|
53
|
-
} catch {
|
|
56
|
+
} catch (err) {
|
|
57
|
+
debugLog?.("extractFileIds failed:", (err as Error).message);
|
|
54
58
|
return { model: "", tools: [], messages: [], hasFileReferences: false };
|
|
55
59
|
}
|
|
56
60
|
}
|
package/src/response/index.ts
CHANGED
package/src/response/mcp.ts
CHANGED
|
@@ -21,6 +21,45 @@ export function stripMcpPrefixFromSSE(text: string): string {
|
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
function stripMcpPrefixFromToolUseBlock(block: unknown): boolean {
|
|
25
|
+
if (!block || typeof block !== "object") return false;
|
|
26
|
+
|
|
27
|
+
const parsedBlock = block as Record<string, unknown>;
|
|
28
|
+
if (parsedBlock.type !== "tool_use" || typeof parsedBlock.name !== "string") {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!parsedBlock.name.startsWith("mcp_")) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
parsedBlock.name = parsedBlock.name.slice(4);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stripMcpPrefixFromContentBlocks(content: unknown): boolean {
|
|
41
|
+
if (!Array.isArray(content)) return false;
|
|
42
|
+
|
|
43
|
+
let modified = false;
|
|
44
|
+
for (const block of content) {
|
|
45
|
+
modified = stripMcpPrefixFromToolUseBlock(block) || modified;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return modified;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function stripMcpPrefixFromMessages(messages: unknown): boolean {
|
|
52
|
+
if (!Array.isArray(messages)) return false;
|
|
53
|
+
|
|
54
|
+
let modified = false;
|
|
55
|
+
for (const message of messages) {
|
|
56
|
+
if (!message || typeof message !== "object") continue;
|
|
57
|
+
modified = stripMcpPrefixFromContentBlocks((message as Record<string, unknown>).content) || modified;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return modified;
|
|
61
|
+
}
|
|
62
|
+
|
|
24
63
|
/**
|
|
25
64
|
* Mutate a parsed SSE event object, removing `mcp_` prefix from tool_use
|
|
26
65
|
* name fields. Returns true if any modification was made.
|
|
@@ -32,42 +71,32 @@ export function stripMcpPrefixFromParsedEvent(parsed: unknown): boolean {
|
|
|
32
71
|
let modified = false;
|
|
33
72
|
|
|
34
73
|
// content_block_start: { content_block: { type: "tool_use", name: "mcp_..." } }
|
|
35
|
-
|
|
36
|
-
p.content_block &&
|
|
37
|
-
typeof p.content_block === "object" &&
|
|
38
|
-
(p.content_block as Record<string, unknown>).type === "tool_use" &&
|
|
39
|
-
typeof (p.content_block as Record<string, unknown>).name === "string" &&
|
|
40
|
-
((p.content_block as Record<string, unknown>).name as string).startsWith("mcp_")
|
|
41
|
-
) {
|
|
42
|
-
(p.content_block as Record<string, unknown>).name = (
|
|
43
|
-
(p.content_block as Record<string, unknown>).name as string
|
|
44
|
-
).slice(4);
|
|
45
|
-
modified = true;
|
|
46
|
-
}
|
|
74
|
+
modified = stripMcpPrefixFromToolUseBlock(p.content_block) || modified;
|
|
47
75
|
|
|
48
76
|
// message_start: { message: { content: [{ type: "tool_use", name: "mcp_..." }] } }
|
|
49
|
-
if (p.message &&
|
|
50
|
-
|
|
51
|
-
if (!block || typeof block !== "object") continue;
|
|
52
|
-
const b = block as Record<string, unknown>;
|
|
53
|
-
if (b.type === "tool_use" && typeof b.name === "string" && b.name.startsWith("mcp_")) {
|
|
54
|
-
b.name = b.name.slice(4);
|
|
55
|
-
modified = true;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
77
|
+
if (p.message && typeof p.message === "object") {
|
|
78
|
+
modified = stripMcpPrefixFromContentBlocks((p.message as Record<string, unknown>).content) || modified;
|
|
58
79
|
}
|
|
59
80
|
|
|
60
81
|
// Top-level content array (non-streaming responses forwarded through SSE)
|
|
61
|
-
|
|
62
|
-
for (const block of p.content) {
|
|
63
|
-
if (!block || typeof block !== "object") continue;
|
|
64
|
-
const b = block as Record<string, unknown>;
|
|
65
|
-
if (b.type === "tool_use" && typeof b.name === "string" && b.name.startsWith("mcp_")) {
|
|
66
|
-
b.name = b.name.slice(4);
|
|
67
|
-
modified = true;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
82
|
+
modified = stripMcpPrefixFromContentBlocks(p.content) || modified;
|
|
71
83
|
|
|
72
84
|
return modified;
|
|
73
85
|
}
|
|
86
|
+
|
|
87
|
+
export function stripMcpPrefixFromJsonBody(body: string): string {
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(body) as Record<string, unknown>;
|
|
90
|
+
let modified = false;
|
|
91
|
+
|
|
92
|
+
modified = stripMcpPrefixFromContentBlocks(parsed.content) || modified;
|
|
93
|
+
modified = stripMcpPrefixFromMessages(parsed.messages) || modified;
|
|
94
|
+
if (parsed.message && typeof parsed.message === "object") {
|
|
95
|
+
modified = stripMcpPrefixFromContentBlocks((parsed.message as Record<string, unknown>).content) || modified;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return modified ? JSON.stringify(parsed) : body;
|
|
99
|
+
} catch {
|
|
100
|
+
return body;
|
|
101
|
+
}
|
|
102
|
+
}
|