@jsonstudio/llms 0.6.795 → 0.6.938
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/dist/bridge/routecodex-adapter.d.ts +74 -0
- package/dist/config-unified/enhanced-path-resolver.d.ts +5 -0
- package/dist/config-unified/unified-config.d.ts +26 -0
- package/dist/conversion/codec-registry.d.ts +10 -0
- package/dist/conversion/codecs/gemini-openai-codec.d.ts +16 -0
- package/dist/conversion/codecs/openai-openai-codec.d.ts +12 -0
- package/dist/conversion/codecs/responses-openai-codec.d.ts +12 -0
- package/dist/conversion/compat/profiles/chat-gemini.json +12 -0
- package/dist/conversion/config/config-manager.d.ts +212 -0
- package/dist/conversion/hub/config/types.d.ts +26 -0
- package/dist/conversion/hub/core/detour-registry.d.ts +9 -0
- package/dist/conversion/hub/core/hub-context.d.ts +21 -0
- package/dist/conversion/hub/core/index.d.ts +3 -0
- package/dist/conversion/hub/core/stage-driver.d.ts +30 -0
- package/dist/conversion/hub/format-adapters/anthropic-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/chat-format-adapter.d.ts +17 -0
- package/dist/conversion/hub/format-adapters/gemini-format-adapter.d.ts +16 -0
- package/dist/conversion/hub/format-adapters/index.d.ts +21 -0
- package/dist/conversion/hub/hub-feature.d.ts +1 -0
- package/dist/conversion/hub/node-support.d.ts +19 -0
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +11 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +7 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +71 -14
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +23 -1
- package/dist/conversion/hub/pipelines/inbound.d.ts +22 -0
- package/dist/conversion/hub/pipelines/outbound.d.ts +22 -0
- package/dist/conversion/hub/policy/policy-engine.d.ts +46 -0
- package/dist/conversion/hub/policy/policy-engine.js +176 -0
- package/dist/conversion/hub/policy/protocol-spec.d.ts +50 -0
- package/dist/conversion/hub/policy/protocol-spec.js +105 -0
- package/dist/conversion/hub/process/chat-process.d.ts +32 -0
- package/dist/conversion/hub/registry.d.ts +28 -0
- package/dist/conversion/hub/response/chat-response-utils.d.ts +6 -0
- package/dist/conversion/hub/response/provider-response.js +31 -0
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +7 -0
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +87 -1
- package/dist/conversion/hub/semantic-mappers/index.d.ts +4 -0
- package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +21 -0
- package/dist/conversion/hub/standardized-bridge.d.ts +12 -0
- package/dist/conversion/hub/types/chat-schema.d.ts +112 -0
- package/dist/conversion/hub/types/errors.d.ts +5 -0
- package/dist/conversion/hub/types/format-envelope.d.ts +7 -0
- package/dist/conversion/hub/types/index.d.ts +6 -0
- package/dist/conversion/hub/types/json.d.ts +9 -0
- package/dist/conversion/hub/types/node.d.ts +31 -0
- package/dist/conversion/responses/responses-openai-bridge.js +263 -10
- package/dist/conversion/schema-validator.d.ts +7 -0
- package/dist/conversion/shared/args-mapping.d.ts +18 -0
- package/dist/conversion/shared/chat-request-filters.d.ts +9 -0
- package/dist/conversion/shared/errors.d.ts +1 -1
- package/dist/conversion/shared/gemini-tool-utils.js +61 -0
- package/dist/conversion/shared/jsonish.d.ts +3 -0
- package/dist/conversion/shared/mcp-injection.d.ts +2 -0
- package/dist/conversion/shared/media.d.ts +1 -0
- package/dist/conversion/shared/openai-message-normalize.d.ts +1 -0
- package/dist/conversion/shared/payload-budget.d.ts +13 -0
- package/dist/conversion/shared/reasoning-mapping.d.ts +5 -0
- package/dist/conversion/shared/responses-request-adapter.d.ts +1 -28
- package/dist/conversion/shared/responses-request-adapter.js +1 -430
- package/dist/conversion/shared/snapshot-hooks.js +112 -4
- package/dist/conversion/shared/tool-governor.js +8 -2
- package/dist/conversion/shared/tool-harvester.d.ts +31 -0
- package/dist/conversion/shared/tool-mapping.js +10 -29
- package/dist/conversion/types.d.ts +33 -0
- package/dist/filters/builtin/add-fields-filter.d.ts +8 -0
- package/dist/filters/builtin/blacklist-filter.d.ts +8 -0
- package/dist/filters/builtin/whitelist-filter.d.ts +8 -0
- package/dist/filters/engine.d.ts +16 -0
- package/dist/filters/special/request-tool-choice-policy.d.ts +11 -0
- package/dist/filters/special/response-finish-invariants.d.ts +11 -0
- package/dist/filters/special/response-openai-to-responses-bridge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-blacklist.d.ts +12 -0
- package/dist/filters/special/response-tool-arguments-schema-converge.d.ts +13 -0
- package/dist/filters/special/response-tool-arguments-stringify.d.ts +9 -0
- package/dist/filters/special/response-tool-arguments-whitelist.d.ts +11 -0
- package/dist/filters/special/tool-filter-hooks.d.ts +19 -0
- package/dist/filters/special/tool-post-constraints.d.ts +31 -0
- package/dist/filters/types.d.ts +68 -0
- package/dist/filters/utils/fieldmap-loader.d.ts +2 -0
- package/dist/filters/utils/snapshot-writer.d.ts +10 -0
- package/dist/guidance/index.d.ts +3 -0
- package/dist/guidance/index.js +78 -83
- package/dist/http/sse-response.d.ts +22 -0
- package/dist/router/virtual-router/bootstrap.d.ts +6 -0
- package/dist/router/virtual-router/bootstrap.js +49 -5
- package/dist/router/virtual-router/classifier.d.ts +10 -0
- package/dist/router/virtual-router/engine-selection.js +147 -15
- package/dist/router/virtual-router/engine.js +177 -31
- package/dist/router/virtual-router/error-center.d.ts +10 -0
- package/dist/router/virtual-router/features.d.ts +3 -0
- package/dist/router/virtual-router/routing-instructions.d.ts +23 -1
- package/dist/router/virtual-router/routing-instructions.js +120 -30
- package/dist/router/virtual-router/types.d.ts +11 -0
- package/dist/servertool/engine.js +189 -16
- package/dist/servertool/handlers/apply-patch-guard.js +269 -0
- package/dist/servertool/handlers/exec-command-guard.js +558 -0
- package/dist/servertool/handlers/followup-message-trimmer.d.ts +16 -0
- package/dist/servertool/handlers/followup-message-trimmer.js +198 -0
- package/dist/servertool/handlers/followup-request-builder.d.ts +17 -0
- package/dist/servertool/handlers/followup-request-builder.js +122 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +252 -51
- package/dist/servertool/handlers/iflow-model-error-retry.js +12 -22
- package/dist/servertool/handlers/stop-message-auto.js +237 -75
- package/dist/servertool/handlers/vision.js +15 -27
- package/dist/servertool/handlers/web-search.js +17 -43
- package/dist/servertool/server-side-tools.d.ts +3 -0
- package/dist/servertool/server-side-tools.js +3 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/chat-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/event-generators/chat.d.ts +55 -0
- package/dist/sse/json-to-sse/event-generators/responses.d.ts +99 -0
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +2 -1
- package/dist/sse/json-to-sse/responses-json-to-sse-converter.d.ts +80 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +2 -2
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +1 -1
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.d.ts +40 -0
- package/dist/sse/shared/chat-serializer.d.ts +4 -0
- package/dist/sse/shared/constants.d.ts +272 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/base-serializer.d.ts +158 -0
- package/dist/sse/shared/serializers/chat-event-serializer.d.ts +82 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +1 -1
- package/dist/sse/shared/serializers/index.d.ts +2 -1
- package/dist/sse/shared/serializers/responses-event-serializer.d.ts +123 -0
- package/dist/sse/shared/serializers/types.d.ts +51 -0
- package/dist/sse/shared/utils.d.ts +254 -0
- package/dist/sse/shared/writer.d.ts +2 -2
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +1 -1
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +2 -1
- package/dist/sse/sse-to-json/parsers/sse-parser.d.ts +73 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
- package/dist/sse/types/chat-types.d.ts +1 -1
- package/dist/sse/types/responses-types.d.ts +1 -1
- package/dist/tools/apply-patch/execution-capturer.d.ts +13 -0
- package/dist/tools/apply-patch/execution-capturer.js +158 -0
- package/dist/tools/apply-patch/regression-capturer.d.ts +1 -0
- package/dist/tools/apply-patch/regression-capturer.js +5 -4
- package/dist/tools/apply-patch/structured.js +109 -13
- package/dist/tools/apply-patch/validator.js +261 -17
- package/dist/tools/tool-registry.d.ts +8 -0
- package/dist/tools/tool-registry.js +2 -1
- package/package.json +4 -4
- package/dist/conversion/compat/actions/apply-patch-format-fixer.js +0 -233
- package/dist/conversion/config/compat-profiles.json +0 -38
- package/dist/conversion/hub/response/server-side-tools.d.ts +0 -26
- package/dist/conversion/hub/response/server-side-tools.js +0 -383
- package/dist/conversion/shared/bridge-conversation-store.d.ts +0 -41
- package/dist/conversion/shared/bridge-conversation-store.js +0 -279
- package/dist/conversion/shared/bridge-request-adapter.d.ts +0 -28
- package/dist/conversion/shared/bridge-request-adapter.js +0 -430
- package/dist/conversion/shared/responses-id-utils.js +0 -42
- package/dist/conversion/shared/responses-instructions.js +0 -113
- package/dist/conversion/shared/responses-message-utils.d.ts +0 -15
- package/dist/conversion/shared/responses-message-utils.js +0 -206
- package/dist/conversion/shared/responses-metadata.js +0 -1
- package/dist/conversion/shared/responses-output-utils.d.ts +0 -7
- package/dist/conversion/shared/responses-output-utils.js +0 -108
- package/dist/conversion/shared/responses-types.d.ts +0 -33
- package/dist/conversion/shared/tool-normalizers.d.ts +0 -4
- package/dist/conversion/shared/tool-normalizers.js +0 -84
- package/dist/filters/special/request-streaming-to-nonstreaming.d.ts +0 -13
- package/dist/filters/special/request-streaming-to-nonstreaming.js +0 -39
- package/dist/filters/special/response-apply-patch-toon-decode.d.ts +0 -23
- package/dist/filters/special/response-apply-patch-toon-decode.js +0 -460
- package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +0 -10
- package/dist/filters/special/response-tool-arguments-toon-decode.js +0 -154
- package/dist/servertool/flow-types.d.ts +0 -40
- package/dist/servertool/flow-types.js +0 -1
- package/dist/servertool/orchestration-types.d.ts +0 -33
- package/dist/servertool/orchestration-types.js +0 -1
- package/dist/servertool/vision-tool.d.ts +0 -2
- package/dist/servertool/vision-tool.js +0 -185
- package/dist/tools/patch-args-normalizer.d.ts +0 -15
- package/dist/tools/patch-args-normalizer.js +0 -472
- package/dist/utils/toon.d.ts +0 -4
- package/dist/utils/toon.js +0 -75
- /package/dist/{conversion/compat/actions/apply-patch-format-fixer.d.ts → servertool/handlers/apply-patch-guard.d.ts} +0 -0
- /package/dist/{conversion/shared/responses-types.js → servertool/handlers/exec-command-guard.d.ts} +0 -0
|
@@ -86,7 +86,8 @@ const convertGitDiffToApplyPatch = (text) => {
|
|
|
86
86
|
current = null;
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
// Include rename-only diffs (no hunks) when we have a move target.
|
|
90
|
+
if (current.path && (current.lines.length || current.moveTo)) {
|
|
90
91
|
files.push(current);
|
|
91
92
|
}
|
|
92
93
|
current = null;
|
|
@@ -95,19 +96,30 @@ const convertGitDiffToApplyPatch = (text) => {
|
|
|
95
96
|
const v = String(value || '').trim();
|
|
96
97
|
if (!v)
|
|
97
98
|
return '';
|
|
98
|
-
const
|
|
99
|
-
|
|
99
|
+
const head = v.split('\t')[0] ?? v;
|
|
100
|
+
const m = String(head).trim().match(/^(?:a\/|b\/)?(.+)$/);
|
|
101
|
+
return (m && m[1] ? m[1] : String(head)).trim();
|
|
100
102
|
};
|
|
101
103
|
for (const line of lines) {
|
|
102
104
|
const diffMatch = line.match(/^diff --git\s+a\/(.+?)\s+b\/(.+)$/);
|
|
103
105
|
if (diffMatch) {
|
|
104
106
|
sawDiff = true;
|
|
105
107
|
flush();
|
|
106
|
-
current = {
|
|
108
|
+
current = {
|
|
109
|
+
path: extractPath(diffMatch[2]),
|
|
110
|
+
kind: 'update',
|
|
111
|
+
lines: [],
|
|
112
|
+
oldPath: extractPath(diffMatch[1]),
|
|
113
|
+
newPath: extractPath(diffMatch[2]),
|
|
114
|
+
};
|
|
107
115
|
continue;
|
|
108
116
|
}
|
|
109
117
|
if (!current)
|
|
110
118
|
continue;
|
|
119
|
+
if (line.startsWith('GIT binary patch') || line.startsWith('Binary files ')) {
|
|
120
|
+
current.binary = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
111
123
|
const delMatch = line.match(/^deleted file mode\s+/);
|
|
112
124
|
const newMatch = line.match(/^new file mode\s+/);
|
|
113
125
|
if (delMatch) {
|
|
@@ -118,7 +130,39 @@ const convertGitDiffToApplyPatch = (text) => {
|
|
|
118
130
|
current.kind = 'add';
|
|
119
131
|
continue;
|
|
120
132
|
}
|
|
121
|
-
if (line.startsWith('
|
|
133
|
+
if (line.startsWith('rename from ')) {
|
|
134
|
+
const p = extractPath(line.slice('rename from '.length));
|
|
135
|
+
if (p)
|
|
136
|
+
current.oldPath = p;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (line.startsWith('rename to ')) {
|
|
140
|
+
const p = extractPath(line.slice('rename to '.length));
|
|
141
|
+
if (p) {
|
|
142
|
+
current.newPath = p;
|
|
143
|
+
current.moveTo = p;
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (line.startsWith('--- ')) {
|
|
148
|
+
const p = extractPath(line.slice(4));
|
|
149
|
+
if (p) {
|
|
150
|
+
current.oldPath = p;
|
|
151
|
+
if (p === '/dev/null')
|
|
152
|
+
current.kind = 'add';
|
|
153
|
+
}
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (line.startsWith('+++ ')) {
|
|
157
|
+
const p = extractPath(line.slice(4));
|
|
158
|
+
if (p) {
|
|
159
|
+
current.newPath = p;
|
|
160
|
+
if (p === '/dev/null')
|
|
161
|
+
current.kind = 'delete';
|
|
162
|
+
}
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (line.startsWith('index ') || line.startsWith('similarity index ') || line.startsWith('dissimilarity index ')) {
|
|
122
166
|
continue;
|
|
123
167
|
}
|
|
124
168
|
if (line.startsWith('@@')) {
|
|
@@ -133,21 +177,35 @@ const convertGitDiffToApplyPatch = (text) => {
|
|
|
133
177
|
flush();
|
|
134
178
|
if (!sawDiff || files.length === 0)
|
|
135
179
|
return null;
|
|
180
|
+
if (files.some((f) => f.binary))
|
|
181
|
+
return null;
|
|
136
182
|
const out = ['*** Begin Patch'];
|
|
137
183
|
for (const file of files) {
|
|
184
|
+
const oldPath = typeof file.oldPath === 'string' ? file.oldPath : '';
|
|
185
|
+
const newPath = typeof file.newPath === 'string' ? file.newPath : '';
|
|
186
|
+
const resolvedPath = file.kind === 'add'
|
|
187
|
+
? (newPath && newPath !== '/dev/null' ? newPath : file.path)
|
|
188
|
+
: file.kind === 'delete'
|
|
189
|
+
? (oldPath && oldPath !== '/dev/null' ? oldPath : file.path)
|
|
190
|
+
: (file.moveTo && oldPath && oldPath !== '/dev/null'
|
|
191
|
+
? oldPath
|
|
192
|
+
: (newPath && newPath !== '/dev/null' ? newPath : file.path));
|
|
138
193
|
if (file.kind === 'delete') {
|
|
139
|
-
out.push(`*** Delete File: ${
|
|
194
|
+
out.push(`*** Delete File: ${resolvedPath}`);
|
|
140
195
|
continue;
|
|
141
196
|
}
|
|
142
197
|
if (file.kind === 'add') {
|
|
143
|
-
out.push(`*** Add File: ${
|
|
198
|
+
out.push(`*** Add File: ${resolvedPath}`);
|
|
144
199
|
for (const l of file.lines) {
|
|
145
200
|
if (l.startsWith('+'))
|
|
146
201
|
out.push(l);
|
|
147
202
|
}
|
|
148
203
|
continue;
|
|
149
204
|
}
|
|
150
|
-
out.push(`*** Update File: ${
|
|
205
|
+
out.push(`*** Update File: ${resolvedPath}`);
|
|
206
|
+
if (file.moveTo && file.moveTo !== resolvedPath) {
|
|
207
|
+
out.push(`*** Move to: ${file.moveTo}`);
|
|
208
|
+
}
|
|
151
209
|
out.push(...file.lines);
|
|
152
210
|
}
|
|
153
211
|
out.push('*** End Patch');
|
|
@@ -172,9 +230,18 @@ export const normalizeApplyPatchText = (raw) => {
|
|
|
172
230
|
const minusMatch = text.match(/^---\s+(.*)$/m);
|
|
173
231
|
const plusMatch = text.match(/^\+\+\+\s+(.*)$/m);
|
|
174
232
|
if (minusMatch && plusMatch) {
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
const
|
|
233
|
+
const rawMinus = (minusMatch[1] || '').split('\t')[0] || '';
|
|
234
|
+
const rawPlus = (plusMatch[1] || '').split('\t')[0] || '';
|
|
235
|
+
const normalizeHeaderPath = (value) => {
|
|
236
|
+
const v = String(value || '').trim();
|
|
237
|
+
if (!v)
|
|
238
|
+
return '';
|
|
239
|
+
const m = v.match(/^(?:a\/|b\/)?(.+)$/);
|
|
240
|
+
return (m && m[1] ? m[1] : v).trim();
|
|
241
|
+
};
|
|
242
|
+
const minusPath = normalizeHeaderPath(rawMinus);
|
|
243
|
+
const plusPath = normalizeHeaderPath(rawPlus);
|
|
244
|
+
const filePath = plusPath === '/dev/null' ? minusPath : plusPath;
|
|
178
245
|
if (filePath) {
|
|
179
246
|
const synthetic = `diff --git a/${filePath} b/${filePath}\n${text}`;
|
|
180
247
|
const converted = convertGitDiffToApplyPatch(synthetic);
|
|
@@ -304,6 +371,129 @@ const tryParseJson = (value) => {
|
|
|
304
371
|
return undefined;
|
|
305
372
|
}
|
|
306
373
|
};
|
|
374
|
+
const escapeUnescapedQuotesInJsonStrings = (input) => {
|
|
375
|
+
// Best-effort: when JSON is almost valid but contains unescaped `"` inside string values
|
|
376
|
+
// (e.g. JSX snippets like className="..."), escape quotes that are not followed by a
|
|
377
|
+
// valid JSON token delimiter. Deterministic; does not attempt to fix structural issues.
|
|
378
|
+
let out = '';
|
|
379
|
+
let inString = false;
|
|
380
|
+
let escaped = false;
|
|
381
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
382
|
+
const ch = input[i] ?? '';
|
|
383
|
+
if (!inString) {
|
|
384
|
+
if (ch === '"') {
|
|
385
|
+
inString = true;
|
|
386
|
+
escaped = false;
|
|
387
|
+
}
|
|
388
|
+
out += ch;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (escaped) {
|
|
392
|
+
out += ch;
|
|
393
|
+
escaped = false;
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (ch === '\\') {
|
|
397
|
+
out += ch;
|
|
398
|
+
escaped = true;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (ch === '"') {
|
|
402
|
+
let j = i + 1;
|
|
403
|
+
while (j < input.length && /\s/.test(input[j] ?? ''))
|
|
404
|
+
j += 1;
|
|
405
|
+
const next = j < input.length ? input[j] : '';
|
|
406
|
+
if (next === '' || next === ':' || next === ',' || next === '}' || next === ']') {
|
|
407
|
+
inString = false;
|
|
408
|
+
out += ch;
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
out += '\\"';
|
|
412
|
+
}
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
out += ch;
|
|
416
|
+
}
|
|
417
|
+
return out;
|
|
418
|
+
};
|
|
419
|
+
const balanceJsonContainers = (input) => {
|
|
420
|
+
// Best-effort bracket/brace balancing for JSON-like strings.
|
|
421
|
+
// Only operates outside string literals. When encountering a closing token that doesn't
|
|
422
|
+
// match the current stack top, inserts the missing closer(s) to recover.
|
|
423
|
+
let out = '';
|
|
424
|
+
let inString = false;
|
|
425
|
+
let escaped = false;
|
|
426
|
+
const stack = [];
|
|
427
|
+
const closeFor = (open) => (open === '{' ? '}' : ']');
|
|
428
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
429
|
+
const ch = input[i] ?? '';
|
|
430
|
+
if (inString) {
|
|
431
|
+
out += ch;
|
|
432
|
+
if (escaped) {
|
|
433
|
+
escaped = false;
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (ch === '\\') {
|
|
437
|
+
escaped = true;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (ch === '"') {
|
|
441
|
+
inString = false;
|
|
442
|
+
}
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (ch === '"') {
|
|
446
|
+
inString = true;
|
|
447
|
+
out += ch;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (ch === '{' || ch === '[') {
|
|
451
|
+
stack.push(ch);
|
|
452
|
+
out += ch;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (ch === '}' || ch === ']') {
|
|
456
|
+
const expectedOpen = ch === '}' ? '{' : '[';
|
|
457
|
+
while (stack.length && stack[stack.length - 1] !== expectedOpen) {
|
|
458
|
+
const open = stack.pop();
|
|
459
|
+
out += closeFor(open);
|
|
460
|
+
}
|
|
461
|
+
if (stack.length && stack[stack.length - 1] === expectedOpen) {
|
|
462
|
+
stack.pop();
|
|
463
|
+
}
|
|
464
|
+
out += ch;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
out += ch;
|
|
468
|
+
}
|
|
469
|
+
while (stack.length) {
|
|
470
|
+
const open = stack.pop();
|
|
471
|
+
out += closeFor(open);
|
|
472
|
+
}
|
|
473
|
+
return out;
|
|
474
|
+
};
|
|
475
|
+
const tryParseJsonLoose = (value) => {
|
|
476
|
+
const parsed = tryParseJson(value);
|
|
477
|
+
if (parsed !== undefined)
|
|
478
|
+
return parsed;
|
|
479
|
+
if (typeof value !== 'string')
|
|
480
|
+
return undefined;
|
|
481
|
+
const trimmed = value.trim();
|
|
482
|
+
if (!trimmed)
|
|
483
|
+
return undefined;
|
|
484
|
+
if (!(trimmed.startsWith('{') || trimmed.startsWith('[')))
|
|
485
|
+
return undefined;
|
|
486
|
+
let repaired = escapeUnescapedQuotesInJsonStrings(trimmed);
|
|
487
|
+
repaired = balanceJsonContainers(repaired);
|
|
488
|
+
if (!repaired || repaired === trimmed)
|
|
489
|
+
return undefined;
|
|
490
|
+
try {
|
|
491
|
+
return JSON.parse(repaired);
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
return undefined;
|
|
495
|
+
}
|
|
496
|
+
};
|
|
307
497
|
const coerceChangesArray = (value) => {
|
|
308
498
|
const parsed = tryParseJson(value);
|
|
309
499
|
if (!parsed)
|
|
@@ -378,29 +568,77 @@ export function validateApplyPatchArgs(argsString, rawArgs) {
|
|
|
378
568
|
// Raw patch text without JSON wrapper
|
|
379
569
|
if (!looksJsonContainer && looksLikePatch(rawTrimmed)) {
|
|
380
570
|
const patchText = normalizeApplyPatchText(rawTrimmed);
|
|
571
|
+
if (!patchText.includes('*** Begin Patch')) {
|
|
572
|
+
return { ok: false, reason: 'unsupported_patch_format' };
|
|
573
|
+
}
|
|
381
574
|
return { ok: true, normalizedArgs: toJson({ patch: patchText, input: patchText }) };
|
|
382
575
|
}
|
|
383
576
|
const extractFromRecord = (rec) => {
|
|
384
577
|
// Special case: argsString claims to be JSON but isn't parseable and raw text looks like a patch.
|
|
385
578
|
if (looksJsonContainer && Object.keys(rec).length === 0 && looksLikePatch(rawTrimmed)) {
|
|
386
|
-
|
|
579
|
+
const patchText = normalizeApplyPatchText(rawTrimmed);
|
|
580
|
+
if (!patchText.includes('*** Begin Patch'))
|
|
581
|
+
return { failureReason: 'unsupported_patch_format' };
|
|
582
|
+
return { patchText };
|
|
387
583
|
}
|
|
388
584
|
const patchField = asString(rec.patch);
|
|
389
585
|
if (patchField && looksLikePatch(patchField)) {
|
|
390
|
-
|
|
586
|
+
const patchText = normalizeApplyPatchText(patchField);
|
|
587
|
+
if (!patchText.includes('*** Begin Patch'))
|
|
588
|
+
return { failureReason: 'unsupported_patch_format' };
|
|
589
|
+
return { patchText };
|
|
391
590
|
}
|
|
392
591
|
const diffField = asString(rec.diff) ?? asString(rec.patchText) ?? asString(rec.body);
|
|
393
592
|
if (diffField && looksLikePatch(diffField)) {
|
|
394
|
-
|
|
593
|
+
const patchText = normalizeApplyPatchText(diffField);
|
|
594
|
+
if (!patchText.includes('*** Begin Patch'))
|
|
595
|
+
return { failureReason: 'unsupported_patch_format' };
|
|
596
|
+
return { patchText };
|
|
395
597
|
}
|
|
396
598
|
const inputField = asString(rec.input);
|
|
397
599
|
if (inputField && looksLikePatch(inputField)) {
|
|
398
|
-
|
|
600
|
+
const patchText = normalizeApplyPatchText(inputField);
|
|
601
|
+
if (!patchText.includes('*** Begin Patch'))
|
|
602
|
+
return { failureReason: 'unsupported_patch_format' };
|
|
603
|
+
return { patchText };
|
|
399
604
|
}
|
|
400
605
|
// Common shape: patch text stored under `instructions` (e.g. "*** Update File: ...").
|
|
401
606
|
const instructionsField = asString(rec.instructions);
|
|
402
607
|
if (instructionsField && looksLikePatch(instructionsField)) {
|
|
403
|
-
|
|
608
|
+
const patchText = normalizeApplyPatchText(instructionsField);
|
|
609
|
+
if (!patchText.includes('*** Begin Patch'))
|
|
610
|
+
return { failureReason: 'unsupported_patch_format' };
|
|
611
|
+
return { patchText };
|
|
612
|
+
}
|
|
613
|
+
// Common wrapper shape (seen in codex samples): { _raw: "{...json...}" }.
|
|
614
|
+
// `_raw` may contain either patch text or a JSON-encoded structured payload.
|
|
615
|
+
const rawEnvelope = asString(rec._raw);
|
|
616
|
+
if (rawEnvelope) {
|
|
617
|
+
const trimmed = rawEnvelope.trim();
|
|
618
|
+
if (looksLikePatch(trimmed)) {
|
|
619
|
+
const patchText = normalizeApplyPatchText(trimmed);
|
|
620
|
+
if (!patchText.includes('*** Begin Patch'))
|
|
621
|
+
return { failureReason: 'unsupported_patch_format' };
|
|
622
|
+
return { patchText };
|
|
623
|
+
}
|
|
624
|
+
const parsed = tryParseJsonLoose(trimmed);
|
|
625
|
+
if (parsed && isRecord(parsed)) {
|
|
626
|
+
return extractFromRecord(parsed);
|
|
627
|
+
}
|
|
628
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
629
|
+
const changesArray = parsed.filter((entry) => isRecord(entry));
|
|
630
|
+
if (changesArray.length && changesArray.some((c) => typeof c.kind === 'string')) {
|
|
631
|
+
const payload = { changes: changesArray };
|
|
632
|
+
try {
|
|
633
|
+
return { patchText: buildStructuredPatch(payload) };
|
|
634
|
+
}
|
|
635
|
+
catch (error) {
|
|
636
|
+
if (!(error instanceof StructuredApplyPatchError))
|
|
637
|
+
throw error;
|
|
638
|
+
return { failureReason: error.reason || 'structured_apply_patch_error' };
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
404
642
|
}
|
|
405
643
|
const payload = coerceStructuredPayload(rec);
|
|
406
644
|
if (payload) {
|
|
@@ -448,7 +686,13 @@ export function validateApplyPatchArgs(argsString, rawArgs) {
|
|
|
448
686
|
}
|
|
449
687
|
}
|
|
450
688
|
else if (typeof rawArgs === 'string' && looksLikePatch(rawArgs)) {
|
|
451
|
-
|
|
689
|
+
const normalized = normalizeApplyPatchText(rawArgs);
|
|
690
|
+
if (normalized.includes('*** Begin Patch')) {
|
|
691
|
+
patchText = normalized;
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
failureReason = 'unsupported_patch_format';
|
|
695
|
+
}
|
|
452
696
|
}
|
|
453
697
|
if (!patchText) {
|
|
454
698
|
if (failureReason)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ToolValidationResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
reason?: string;
|
|
4
|
+
warnings?: Array<Record<string, unknown>>;
|
|
5
|
+
normalizedArgs?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function getAllowedToolNames(): string[];
|
|
8
|
+
export declare function validateToolCall(name: string, argsString: string): ToolValidationResult;
|
|
@@ -116,7 +116,8 @@ export function validateToolCall(name, argsString) {
|
|
|
116
116
|
originalArgs: typeof argsString === 'string' ? argsString : String(argsString ?? ''),
|
|
117
117
|
normalizedArgs: typeof argsString === 'string' ? argsString : String(argsString ?? ''),
|
|
118
118
|
validationError: reason,
|
|
119
|
-
source: 'tool-registry.validateToolCall'
|
|
119
|
+
source: 'tool-registry.validateToolCall',
|
|
120
|
+
meta: { applyPatchToolMode: 'freeform' }
|
|
120
121
|
});
|
|
121
122
|
return { ok: false, reason };
|
|
122
123
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsonstudio/llms",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.938",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "node scripts/bump-version.mjs && tsc -p tsconfig.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
10
|
-
"build:dev": "node scripts/bump-version.mjs && tsc -p tsconfig.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
9
|
+
"build": "node scripts/clean-dist.mjs && node scripts/bump-version.mjs && tsc -p tsconfig.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
10
|
+
"build:dev": "node scripts/clean-dist.mjs && node scripts/bump-version.mjs && tsc -p tsconfig.json && node scripts/tools/copy-compat-profiles.mjs",
|
|
11
11
|
"lint": "eslint --no-eslintrc -c .eslintrc.json src --ext .ts --no-cache",
|
|
12
12
|
"lint:fix": "eslint --no-eslintrc -c .eslintrc.json src --ext .ts --no-cache --fix",
|
|
13
13
|
"postbuild": "node scripts/tests/run-matrix-ci.mjs",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"test:looprt:gemini": "npm run build && node scripts/tests/loop-rt-gemini.mjs",
|
|
26
26
|
"replay:responses:chat-sse": "node scripts/exp3-responses-sse-to-chat-sse.mjs",
|
|
27
27
|
"replay:responses:loop": "node scripts/exp4-responses-sse-loop.mjs",
|
|
28
|
-
"test:virtual-router": "npm run build:dev && node scripts/tests/virtual-router-dry-run.mjs && node scripts/tests/virtual-router-context.mjs",
|
|
28
|
+
"test:virtual-router": "npm run build:dev && node scripts/tests/virtual-router-prefer-autoclear.mjs && node scripts/tests/virtual-router-dry-run.mjs && node scripts/tests/virtual-router-context.mjs",
|
|
29
29
|
"test:virtual-router-health": "npm run build:dev && node scripts/tests/virtual-router-health.mjs"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import { registerBridgeAction } from '../../shared/bridge-actions.js';
|
|
2
|
-
import { ensureMessagesArray } from '../../shared/bridge-message-utils.js';
|
|
3
|
-
/**
|
|
4
|
-
* apply-patch-format-fixer.ts
|
|
5
|
-
*
|
|
6
|
-
* 专门处理 apply_patch 的格式错误修复,包括:
|
|
7
|
-
* 1. 修复缺失 '*** Begin Patch' 包装的 raw diff
|
|
8
|
-
* 2. 修复混用 git diff 头部的情况
|
|
9
|
-
* 3. 修复 hunk 中缺少前缀的代码行
|
|
10
|
-
*
|
|
11
|
-
* 注意:仅修复格式问题,不修复上下文匹配问题
|
|
12
|
-
*/
|
|
13
|
-
import { captureApplyPatchRegression } from '../../../tools/patch-regression-capturer.js';
|
|
14
|
-
/**
|
|
15
|
-
* 检测是否是裸 diff 格式(没有 Begin Patch 包装)
|
|
16
|
-
*/
|
|
17
|
-
function isBareDiff(text) {
|
|
18
|
-
const trimmed = text.trim();
|
|
19
|
-
// 检测典型的 git diff 标记
|
|
20
|
-
const hasGitDiffMarkers = trimmed.startsWith('diff --git') ||
|
|
21
|
-
/^---\s+a\//.test(trimmed) ||
|
|
22
|
-
/^\+\+\+\s+b\//.test(trimmed) ||
|
|
23
|
-
/^@@\s+-\d+,\d+\s+\+\d+,\d+\s+@@/.test(trimmed);
|
|
24
|
-
// 确保不是标准的 apply_patch 格式
|
|
25
|
-
const hasApplyPatchMarkers = trimmed.includes('*** Begin Patch') ||
|
|
26
|
-
trimmed.includes('*** Update File:') ||
|
|
27
|
-
trimmed.includes('*** Add File:');
|
|
28
|
-
return hasGitDiffMarkers && !hasApplyPatchMarkers;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* 转换裸 diff 为 apply_patch 格式
|
|
32
|
-
*/
|
|
33
|
-
function convertBareDiffToApplyPatch(diffText) {
|
|
34
|
-
const lines = diffText.replace(/\r\n/g, '\n').split('\n');
|
|
35
|
-
const patches = new Map();
|
|
36
|
-
let currentFile = '';
|
|
37
|
-
let currentHunk = [];
|
|
38
|
-
let inHunk = false;
|
|
39
|
-
for (const line of lines) {
|
|
40
|
-
// 检测文件头
|
|
41
|
-
const diffMatch = line.match(/^diff --git\s+a\/(.+?)\s+b\/(.+)$/);
|
|
42
|
-
if (diffMatch) {
|
|
43
|
-
// 保存上一个文件的 hunk
|
|
44
|
-
if (currentFile && currentHunk.length > 0) {
|
|
45
|
-
patches.set(currentFile, {
|
|
46
|
-
header: `*** Update File: ${diffMatch[2]}`,
|
|
47
|
-
hunk: [...currentHunk]
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
currentFile = diffMatch[2];
|
|
51
|
-
currentHunk = [];
|
|
52
|
-
inHunk = false;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
// 检测 hunk 头
|
|
56
|
-
if (/^@@\s+-\d+/.test(line)) {
|
|
57
|
-
currentHunk.push(line);
|
|
58
|
-
inHunk = true;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
// 在 hunk 中收集行
|
|
62
|
-
if (inHunk) {
|
|
63
|
-
// 确保每行都有正确的前缀
|
|
64
|
-
if (line.startsWith('+') || line.startsWith('-') || line.startsWith(' ')) {
|
|
65
|
-
currentHunk.push(line);
|
|
66
|
-
}
|
|
67
|
-
else if (line.trim() === '') {
|
|
68
|
-
currentHunk.push(' ');
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
// 行缺少前缀,作为上下文行处理
|
|
72
|
-
currentHunk.push(' ' + line);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
// 保存最后一个文件
|
|
77
|
-
if (currentFile && currentHunk.length > 0) {
|
|
78
|
-
patches.set(currentFile, {
|
|
79
|
-
header: `*** Update File: ${currentFile}`,
|
|
80
|
-
hunk: [...currentHunk]
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
if (patches.size === 0) {
|
|
84
|
-
// 如果无法解析,返回原始文本包装
|
|
85
|
-
return `*** Begin Patch\n${diffText}\n*** End Patch`;
|
|
86
|
-
}
|
|
87
|
-
// 构建 apply_patch 格式
|
|
88
|
-
const result = ['*** Begin Patch'];
|
|
89
|
-
for (const [file, { header, hunk }] of patches.entries()) {
|
|
90
|
-
result.push(header);
|
|
91
|
-
result.push(...hunk);
|
|
92
|
-
}
|
|
93
|
-
result.push('*** End Patch');
|
|
94
|
-
return result.join('\n');
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* 修复 hunk 中缺少前缀的行
|
|
98
|
-
*/
|
|
99
|
-
function fixHunkPrefixes(patchText) {
|
|
100
|
-
const lines = patchText.split('\n');
|
|
101
|
-
const result = [];
|
|
102
|
-
let inUpdateSection = false;
|
|
103
|
-
let afterHeader = false;
|
|
104
|
-
for (let i = 0; i < lines.length; i++) {
|
|
105
|
-
const line = lines[i];
|
|
106
|
-
if (line.startsWith('*** Begin Patch')) {
|
|
107
|
-
result.push(line);
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
if (line.startsWith('*** End Patch')) {
|
|
111
|
-
result.push(line);
|
|
112
|
-
inUpdateSection = false;
|
|
113
|
-
afterHeader = false;
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
if (line.startsWith('*** Update File:') || line.startsWith('*** Add File:') || line.startsWith('*** Delete File:')) {
|
|
117
|
-
result.push(line);
|
|
118
|
-
inUpdateSection = true;
|
|
119
|
-
afterHeader = true;
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
if (inUpdateSection) {
|
|
123
|
-
// 跳过紧跟在 header 后的空行
|
|
124
|
-
if (afterHeader && line.trim() === '') {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
afterHeader = false;
|
|
128
|
-
// 检查是否是 hunk 行
|
|
129
|
-
if (/^@@\s+-\d+,\d+\s+\+\d+,\d+\s+@@/.test(line)) {
|
|
130
|
-
result.push(line);
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
// 检查是否已经有正确前缀
|
|
134
|
-
if (/^[+-]\s/.test(line) || /^\s/.test(line)) {
|
|
135
|
-
result.push(line);
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
// 行缺少前缀,作为上下文行处理
|
|
139
|
-
if (line.trim() === '') {
|
|
140
|
-
result.push(' ');
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
result.push(' ' + line);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
result.push(line);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return result.join('\n');
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* 主要的修复函数
|
|
154
|
-
*/
|
|
155
|
-
function fixApplyPatchFormat(argsStr) {
|
|
156
|
-
try {
|
|
157
|
-
let args;
|
|
158
|
-
try {
|
|
159
|
-
args = JSON.parse(argsStr);
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
return { fixed: argsStr }; // 无法解析,返回原值
|
|
163
|
-
}
|
|
164
|
-
if (!args || typeof args !== 'object') {
|
|
165
|
-
return { fixed: argsStr };
|
|
166
|
-
}
|
|
167
|
-
let patch = args.patch || args.input || '';
|
|
168
|
-
if (typeof patch !== 'string') {
|
|
169
|
-
return { fixed: argsStr };
|
|
170
|
-
}
|
|
171
|
-
let modified = false;
|
|
172
|
-
let errorType;
|
|
173
|
-
// 修复 1: 裸 diff 缺少包装
|
|
174
|
-
if (isBareDiff(patch)) {
|
|
175
|
-
patch = convertBareDiffToApplyPatch(patch);
|
|
176
|
-
modified = true;
|
|
177
|
-
errorType = 'missing_begin_patch';
|
|
178
|
-
}
|
|
179
|
-
// 修复 2: hunk 中缺少前缀的行
|
|
180
|
-
const hasInvalidHunkLine = /"(.*?)".*Unexpected line found in update hunk/.test(patch);
|
|
181
|
-
if (hasInvalidHunkLine || !patch.match(/^@@/m)) {
|
|
182
|
-
const originalLength = patch.length;
|
|
183
|
-
patch = fixHunkPrefixes(patch);
|
|
184
|
-
if (patch.length !== originalLength) {
|
|
185
|
-
modified = true;
|
|
186
|
-
errorType = errorType || 'invalid_hunk_prefix';
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (modified) {
|
|
190
|
-
const newArgs = { ...args, patch, input: patch };
|
|
191
|
-
return { fixed: JSON.stringify(newArgs), errorType };
|
|
192
|
-
}
|
|
193
|
-
return { fixed: argsStr };
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
return { fixed: argsStr };
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Bridge action: 遍历消息,修复 apply_patch 格式问题
|
|
201
|
-
*/
|
|
202
|
-
const fixApplyPatchFormatAction = (ctx) => {
|
|
203
|
-
const messages = ensureMessagesArray(ctx.state);
|
|
204
|
-
for (const message of messages) {
|
|
205
|
-
if (message.role !== 'assistant')
|
|
206
|
-
continue;
|
|
207
|
-
if (!Array.isArray(message.tool_calls))
|
|
208
|
-
continue;
|
|
209
|
-
for (const toolCall of message.tool_calls) {
|
|
210
|
-
if (toolCall.type !== 'function')
|
|
211
|
-
continue;
|
|
212
|
-
const fn = toolCall.function;
|
|
213
|
-
if (!fn || fn.name !== 'apply_patch')
|
|
214
|
-
continue;
|
|
215
|
-
const rawArgs = fn.arguments;
|
|
216
|
-
if (typeof rawArgs !== 'string')
|
|
217
|
-
continue;
|
|
218
|
-
const { fixed, errorType } = fixApplyPatchFormat(rawArgs);
|
|
219
|
-
if (fixed !== rawArgs) {
|
|
220
|
-
// 保存可修复样本
|
|
221
|
-
captureApplyPatchRegression({
|
|
222
|
-
errorType: errorType || 'unknown_format_issue',
|
|
223
|
-
originalArgs: rawArgs,
|
|
224
|
-
fixerResult: fixed,
|
|
225
|
-
source: 'compat.fix-apply-patch-format'
|
|
226
|
-
});
|
|
227
|
-
fn.arguments = fixed;
|
|
228
|
-
toolCall._format_fixed = true;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
registerBridgeAction('compat.fix-apply-patch-format', fixApplyPatchFormatAction);
|