@jsonstudio/llms 0.6.1643 → 0.6.1739
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/conversion/compat/actions/harvest-tool-calls-from-text.d.ts +10 -0
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +121 -0
- package/dist/conversion/compat/actions/iflow-kimi-cli-defaults.d.ts +10 -0
- package/dist/conversion/compat/actions/iflow-kimi-cli-defaults.js +80 -0
- package/dist/conversion/compat/actions/iflow-kimi-history-media-placeholder.d.ts +7 -0
- package/dist/conversion/compat/actions/iflow-kimi-history-media-placeholder.js +161 -0
- package/dist/conversion/compat/actions/iflow-kimi-thinking-reasoning-fill.d.ts +12 -0
- package/dist/conversion/compat/actions/iflow-kimi-thinking-reasoning-fill.js +67 -0
- package/dist/conversion/compat/actions/iflow-response-body-unwrap.d.ts +9 -0
- package/dist/conversion/compat/actions/iflow-response-body-unwrap.js +140 -0
- package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.d.ts +10 -0
- package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.js +59 -0
- package/dist/conversion/compat/actions/lmstudio-responses-input-stringify.d.ts +14 -0
- package/dist/conversion/compat/actions/lmstudio-responses-input-stringify.js +125 -0
- package/dist/conversion/compat/actions/normalize-tool-call-ids.d.ts +11 -0
- package/dist/conversion/compat/actions/normalize-tool-call-ids.js +140 -0
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.d.ts +2 -0
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +152 -0
- package/dist/conversion/compat/antigravity-session-signature.d.ts +1 -1
- package/dist/conversion/compat/antigravity-session-signature.js +5 -4
- package/dist/conversion/compat/profiles/chat-iflow.json +6 -0
- package/dist/conversion/compat/profiles/chat-lmstudio.json +7 -1
- package/dist/conversion/hub/operation-table/operation-table-runner.js +1 -1
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +19 -2
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +101 -5
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.d.ts +2 -0
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +63 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +18 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +1 -1
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +8 -5
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +5 -1
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +113 -0
- package/dist/conversion/hub/pipeline/target-utils.js +3 -0
- package/dist/conversion/hub/response/provider-response.js +27 -1
- package/dist/conversion/responses/responses-openai-bridge.js +32 -6
- package/dist/conversion/shared/anthropic-message-utils.js +20 -5
- package/dist/conversion/shared/bridge-id-utils.d.ts +2 -0
- package/dist/conversion/shared/bridge-id-utils.js +52 -15
- package/dist/conversion/shared/responses-conversation-store.js +40 -5
- package/dist/conversion/shared/responses-output-builder.js +23 -7
- package/dist/conversion/shared/responses-tool-utils.d.ts +1 -0
- package/dist/conversion/shared/responses-tool-utils.js +30 -13
- package/dist/conversion/shared/text-markup-normalizer.d.ts +1 -0
- package/dist/conversion/shared/text-markup-normalizer.js +269 -1
- package/dist/router/virtual-router/bootstrap.js +31 -7
- package/dist/router/virtual-router/classifier.js +1 -1
- package/dist/router/virtual-router/engine/antigravity/alias-lease.d.ts +33 -0
- package/dist/router/virtual-router/engine/antigravity/alias-lease.js +247 -0
- package/dist/router/virtual-router/engine/health/index.d.ts +23 -0
- package/dist/router/virtual-router/engine/health/index.js +720 -0
- package/dist/router/virtual-router/engine/provider-key/parse.d.ts +6 -0
- package/dist/router/virtual-router/engine/provider-key/parse.js +43 -0
- package/dist/router/virtual-router/engine/routing-pools/index.d.ts +13 -0
- package/dist/router/virtual-router/engine/routing-pools/index.js +225 -0
- package/dist/router/virtual-router/engine/routing-state/keys.d.ts +3 -0
- package/dist/router/virtual-router/engine/routing-state/keys.js +30 -0
- package/dist/router/virtual-router/engine/routing-state/metadata.d.ts +6 -0
- package/dist/router/virtual-router/engine/routing-state/metadata.js +132 -0
- package/dist/router/virtual-router/engine/routing-state/store.d.ts +11 -0
- package/dist/router/virtual-router/engine/routing-state/store.js +107 -0
- package/dist/router/virtual-router/engine-health.d.ts +1 -23
- package/dist/router/virtual-router/engine-health.js +1 -720
- package/dist/router/virtual-router/engine-selection/route-utils.js +57 -0
- package/dist/router/virtual-router/engine-selection/tier-selection-select.js +8 -48
- package/dist/router/virtual-router/engine-selection/tier-selection.js +34 -17
- package/dist/router/virtual-router/engine-selection.d.ts +1 -13
- package/dist/router/virtual-router/engine-selection.js +1 -225
- package/dist/router/virtual-router/engine.d.ts +2 -23
- package/dist/router/virtual-router/engine.js +130 -603
- package/dist/router/virtual-router/message-utils.js +15 -5
- package/dist/servertool/engine.js +4 -4
- package/dist/servertool/handlers/followup-request-builder.js +46 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +48 -47
- package/dist/servertool/handlers/stop-message-auto.js +64 -7
- package/dist/servertool/handlers/vision.js +10 -0
- package/dist/servertool/types.d.ts +3 -0
- package/dist/sse/sse-to-json/builders/response-builder.js +6 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +32 -2
- package/dist/sse/sse-to-json/parsers/sse-parser.js +34 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.js +33 -1
- package/dist/tools/apply-patch/args-normalizer/default-actions.d.ts +2 -0
- package/dist/tools/apply-patch/args-normalizer/default-actions.js +12 -0
- package/dist/tools/apply-patch/args-normalizer/extract-patch.d.ts +2 -0
- package/dist/tools/apply-patch/args-normalizer/extract-patch.js +15 -0
- package/dist/tools/apply-patch/args-normalizer/index.d.ts +2 -0
- package/dist/tools/apply-patch/args-normalizer/index.js +164 -0
- package/dist/tools/apply-patch/args-normalizer/structured-builders.d.ts +7 -0
- package/dist/tools/apply-patch/args-normalizer/structured-builders.js +85 -0
- package/dist/tools/apply-patch/args-normalizer/types.d.ts +54 -0
- package/dist/tools/apply-patch/args-normalizer/types.js +1 -0
- package/dist/tools/apply-patch/patch-text/looks-like-patch.js +1 -0
- package/dist/tools/apply-patch/patch-text/normalize.js +104 -5
- package/dist/tools/apply-patch/structured/coercion.js +28 -4
- package/dist/tools/apply-patch/validator.js +7 -146
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { normalizeApplyPatchText, looksLikePatch } from '../patch-text/normalize.js';
|
|
2
|
+
export function extractNormalizedPatch(value) {
|
|
3
|
+
if (!value) {
|
|
4
|
+
return {};
|
|
5
|
+
}
|
|
6
|
+
const normalized = normalizeApplyPatchText(value);
|
|
7
|
+
const patchLike = looksLikePatch(value) || looksLikePatch(normalized) || normalized.includes('*** Begin Patch');
|
|
8
|
+
if (!patchLike) {
|
|
9
|
+
return {};
|
|
10
|
+
}
|
|
11
|
+
if (!normalized.includes('*** Begin Patch')) {
|
|
12
|
+
return { failureReason: 'unsupported_patch_format' };
|
|
13
|
+
}
|
|
14
|
+
return { patchText: normalized };
|
|
15
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { coerceStructuredPayload } from '../structured/coercion.js';
|
|
2
|
+
import { tryParseJsonLoose } from '../json/parse-loose.js';
|
|
3
|
+
import { looksLikePatch } from '../patch-text/normalize.js';
|
|
4
|
+
import { asString, isRecord } from '../validation/shared.js';
|
|
5
|
+
import { DEFAULT_APPLY_PATCH_NORMALIZE_ACTIONS } from './default-actions.js';
|
|
6
|
+
import { extractNormalizedPatch } from './extract-patch.js';
|
|
7
|
+
import { buildPatchFromChangesArray, buildPatchFromStructuredPayload, extractConflictPatchAsStructuredReplace, resolvePathAlias } from './structured-builders.js';
|
|
8
|
+
function getActionList(options) {
|
|
9
|
+
if (Array.isArray(options?.actions) && options?.actions.length > 0) {
|
|
10
|
+
return options.actions;
|
|
11
|
+
}
|
|
12
|
+
return DEFAULT_APPLY_PATCH_NORMALIZE_ACTIONS;
|
|
13
|
+
}
|
|
14
|
+
function runRecordActions(record, state, actions, runNested, isFinalPass) {
|
|
15
|
+
for (const action of actions) {
|
|
16
|
+
if (action.action === 'record_text_fields') {
|
|
17
|
+
for (const field of action.fields) {
|
|
18
|
+
const value = asString(record[field]);
|
|
19
|
+
const extracted = extractNormalizedPatch(value);
|
|
20
|
+
if (extracted.patchText || extracted.failureReason) {
|
|
21
|
+
return extracted;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (action.action === 'record_conflict_patch') {
|
|
27
|
+
const patchFieldValue = asString(record[action.patchField]);
|
|
28
|
+
const pathAlias = resolvePathAlias(record, action.fileFields);
|
|
29
|
+
const extracted = extractConflictPatchAsStructuredReplace(pathAlias, patchFieldValue ?? undefined);
|
|
30
|
+
if (extracted.patchText || extracted.failureReason) {
|
|
31
|
+
return extracted;
|
|
32
|
+
}
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (action.action === 'record_raw_envelope') {
|
|
36
|
+
const rawEnvelope = asString(record[action.field]);
|
|
37
|
+
if (!rawEnvelope) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const fromEnvelope = extractNormalizedPatch(rawEnvelope.trim());
|
|
41
|
+
if (fromEnvelope.patchText || fromEnvelope.failureReason) {
|
|
42
|
+
return fromEnvelope;
|
|
43
|
+
}
|
|
44
|
+
if (action.parseJson === false) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const maxDepth = typeof action.maxDepth === 'number' ? action.maxDepth : 2;
|
|
48
|
+
if (state.depth >= maxDepth) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const parsed = tryParseJsonLoose(rawEnvelope.trim());
|
|
52
|
+
if (isRecord(parsed) || Array.isArray(parsed)) {
|
|
53
|
+
const nested = runNested(rawEnvelope.trim(), parsed, state.depth + 1);
|
|
54
|
+
if (nested.patchText || nested.failureReason) {
|
|
55
|
+
return nested;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (action.action === 'record_structured_payload') {
|
|
61
|
+
const payload = coerceStructuredPayload(record);
|
|
62
|
+
const extracted = buildPatchFromStructuredPayload(payload);
|
|
63
|
+
if (extracted.patchText || extracted.failureReason) {
|
|
64
|
+
return extracted;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (action.action === 'invalid_json_guard' && isFinalPass) {
|
|
69
|
+
if (state.looksJsonContainer &&
|
|
70
|
+
isRecord(state.rawArgs) &&
|
|
71
|
+
Object.keys(state.rawArgs).length === 0 &&
|
|
72
|
+
state.rawTrimmed.length > 0 &&
|
|
73
|
+
!looksLikePatch(state.rawTrimmed)) {
|
|
74
|
+
return { failureReason: 'invalid_json' };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
function runActions(state, actions, runNested, isFinalPass) {
|
|
81
|
+
for (const action of actions) {
|
|
82
|
+
if (action.action === 'raw_non_json_patch') {
|
|
83
|
+
if (!state.looksJsonContainer) {
|
|
84
|
+
const extracted = extractNormalizedPatch(state.rawTrimmed);
|
|
85
|
+
if (extracted.patchText || extracted.failureReason) {
|
|
86
|
+
return extracted;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (action.action === 'json_container_patch_fallback') {
|
|
92
|
+
if (state.looksJsonContainer && isRecord(state.rawArgs) && Object.keys(state.rawArgs).length === 0) {
|
|
93
|
+
const extracted = extractNormalizedPatch(state.rawTrimmed);
|
|
94
|
+
if (extracted.patchText || extracted.failureReason) {
|
|
95
|
+
return extracted;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (action.action === 'array_structured_payload') {
|
|
101
|
+
if (!Array.isArray(state.rawArgs) || state.rawArgs.length === 0) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const firstRecord = state.rawArgs.find((entry) => isRecord(entry));
|
|
105
|
+
if (firstRecord && Array.isArray(firstRecord.changes)) {
|
|
106
|
+
const extracted = runRecordActions(firstRecord, state, actions, runNested, isFinalPass);
|
|
107
|
+
if (extracted.patchText || extracted.failureReason) {
|
|
108
|
+
return extracted;
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const changesArray = state.rawArgs.filter((entry) => isRecord(entry));
|
|
113
|
+
const extracted = buildPatchFromChangesArray(changesArray);
|
|
114
|
+
if (extracted.patchText || extracted.failureReason) {
|
|
115
|
+
return extracted;
|
|
116
|
+
}
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (action.action === 'raw_string_patch') {
|
|
120
|
+
if (typeof state.rawArgs === 'string') {
|
|
121
|
+
const extracted = extractNormalizedPatch(state.rawArgs);
|
|
122
|
+
if (extracted.patchText || extracted.failureReason) {
|
|
123
|
+
return extracted;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (isRecord(state.rawArgs)) {
|
|
129
|
+
const extracted = runRecordActions(state.rawArgs, state, [action], runNested, isFinalPass);
|
|
130
|
+
if (extracted.patchText || extracted.failureReason) {
|
|
131
|
+
return extracted;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
function normalizeState(argsString, rawArgs, depth) {
|
|
138
|
+
const rawTrimmed = typeof argsString === 'string' ? argsString.trim() : '';
|
|
139
|
+
return {
|
|
140
|
+
argsString,
|
|
141
|
+
rawArgs,
|
|
142
|
+
rawTrimmed,
|
|
143
|
+
looksJsonContainer: rawTrimmed.startsWith('{') || rawTrimmed.startsWith('['),
|
|
144
|
+
depth
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function resolveExtraction(argsString, rawArgs, actions, depth, isFinalPass) {
|
|
148
|
+
const state = normalizeState(argsString, rawArgs, depth);
|
|
149
|
+
const runNested = (nextArgsString, nextRawArgs, nextDepth) => {
|
|
150
|
+
return resolveExtraction(nextArgsString, nextRawArgs, actions, nextDepth, false);
|
|
151
|
+
};
|
|
152
|
+
return runActions(state, actions, runNested, isFinalPass);
|
|
153
|
+
}
|
|
154
|
+
export function normalizeApplyPatchArgs(argsString, rawArgs, options) {
|
|
155
|
+
const actions = getActionList(options);
|
|
156
|
+
const extracted = resolveExtraction(argsString, rawArgs, actions, 0, true);
|
|
157
|
+
if (extracted.patchText) {
|
|
158
|
+
return { ok: true, patchText: extracted.patchText };
|
|
159
|
+
}
|
|
160
|
+
if (extracted.failureReason) {
|
|
161
|
+
return { ok: false, reason: extracted.failureReason };
|
|
162
|
+
}
|
|
163
|
+
return { ok: false, reason: 'missing_changes' };
|
|
164
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type StructuredApplyPatchPayload } from '../structured.js';
|
|
2
|
+
import { type UnknownRecord } from '../validation/shared.js';
|
|
3
|
+
import type { ApplyPatchExtraction } from './types.js';
|
|
4
|
+
export declare function resolvePathAlias(record: UnknownRecord, fileFields: string[]): string | undefined;
|
|
5
|
+
export declare function buildPatchFromChangesArray(changesArray: UnknownRecord[]): ApplyPatchExtraction;
|
|
6
|
+
export declare function buildPatchFromStructuredPayload(payload: StructuredApplyPatchPayload | undefined): ApplyPatchExtraction;
|
|
7
|
+
export declare function extractConflictPatchAsStructuredReplace(filePath: string | undefined, conflictText: string | undefined): ApplyPatchExtraction;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { buildStructuredPatch, StructuredApplyPatchError } from '../structured.js';
|
|
2
|
+
import { asString } from '../validation/shared.js';
|
|
3
|
+
export function resolvePathAlias(record, fileFields) {
|
|
4
|
+
for (const field of fileFields) {
|
|
5
|
+
const value = asString(record[field]);
|
|
6
|
+
if (value) {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
function safeBuildStructuredPatch(payload) {
|
|
13
|
+
try {
|
|
14
|
+
return { patchText: buildStructuredPatch(payload) };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (!(error instanceof StructuredApplyPatchError)) {
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
return { failureReason: error.reason || 'structured_apply_patch_error' };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function buildPatchFromChangesArray(changesArray) {
|
|
24
|
+
if (!changesArray.length || !changesArray.some((change) => typeof change.kind === 'string')) {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
const payload = {
|
|
28
|
+
changes: changesArray
|
|
29
|
+
};
|
|
30
|
+
return safeBuildStructuredPatch(payload);
|
|
31
|
+
}
|
|
32
|
+
export function buildPatchFromStructuredPayload(payload) {
|
|
33
|
+
if (!payload) {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
return safeBuildStructuredPatch(payload);
|
|
37
|
+
}
|
|
38
|
+
export function extractConflictPatchAsStructuredReplace(filePath, conflictText) {
|
|
39
|
+
const safeFilePath = typeof filePath === 'string' ? filePath.trim() : '';
|
|
40
|
+
const safeText = typeof conflictText === 'string' ? conflictText : '';
|
|
41
|
+
if (!safeFilePath || !safeText) {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
const lines = safeText.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
45
|
+
let start = -1;
|
|
46
|
+
let middle = -1;
|
|
47
|
+
let end = -1;
|
|
48
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
49
|
+
const line = lines[index] ?? '';
|
|
50
|
+
if (start < 0) {
|
|
51
|
+
if (line.startsWith('<<<<<<<')) {
|
|
52
|
+
start = index;
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (middle < 0) {
|
|
57
|
+
if (line.startsWith('=======')) {
|
|
58
|
+
middle = index;
|
|
59
|
+
}
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (line.startsWith('>>>>>>>')) {
|
|
63
|
+
end = index;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (start < 0 || middle <= start || end <= middle) {
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
const original = lines.slice(start + 1, middle).join('\n');
|
|
71
|
+
const updated = lines.slice(middle + 1, end).join('\n');
|
|
72
|
+
if (!original.trim() || !updated.trim()) {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
return safeBuildStructuredPatch({
|
|
76
|
+
file: safeFilePath,
|
|
77
|
+
changes: [
|
|
78
|
+
{
|
|
79
|
+
kind: 'replace',
|
|
80
|
+
target: original,
|
|
81
|
+
lines: updated
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { UnknownRecord } from '../validation/shared.js';
|
|
2
|
+
export type ApplyPatchExtraction = {
|
|
3
|
+
patchText?: string;
|
|
4
|
+
failureReason?: string;
|
|
5
|
+
};
|
|
6
|
+
export type ApplyPatchNormalizeResult = {
|
|
7
|
+
ok: true;
|
|
8
|
+
patchText: string;
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
reason: string;
|
|
12
|
+
};
|
|
13
|
+
export type ApplyPatchNormalizeAction = {
|
|
14
|
+
action: 'raw_non_json_patch';
|
|
15
|
+
} | {
|
|
16
|
+
action: 'json_container_patch_fallback';
|
|
17
|
+
} | {
|
|
18
|
+
action: 'record_text_fields';
|
|
19
|
+
fields: string[];
|
|
20
|
+
} | {
|
|
21
|
+
action: 'record_conflict_patch';
|
|
22
|
+
patchField: string;
|
|
23
|
+
fileFields: string[];
|
|
24
|
+
} | {
|
|
25
|
+
action: 'record_raw_envelope';
|
|
26
|
+
field: string;
|
|
27
|
+
parseJson?: boolean;
|
|
28
|
+
maxDepth?: number;
|
|
29
|
+
} | {
|
|
30
|
+
action: 'record_structured_payload';
|
|
31
|
+
} | {
|
|
32
|
+
action: 'array_structured_payload';
|
|
33
|
+
} | {
|
|
34
|
+
action: 'raw_string_patch';
|
|
35
|
+
} | {
|
|
36
|
+
action: 'invalid_json_guard';
|
|
37
|
+
};
|
|
38
|
+
export type ApplyPatchNormalizeOptions = {
|
|
39
|
+
actions?: ApplyPatchNormalizeAction[];
|
|
40
|
+
};
|
|
41
|
+
export type ApplyPatchNormalizeState = {
|
|
42
|
+
argsString: string;
|
|
43
|
+
rawArgs: unknown;
|
|
44
|
+
rawTrimmed: string;
|
|
45
|
+
looksJsonContainer: boolean;
|
|
46
|
+
depth: number;
|
|
47
|
+
};
|
|
48
|
+
export type ApplyPatchActionContext = {
|
|
49
|
+
state: ApplyPatchNormalizeState;
|
|
50
|
+
runNested: (argsString: string, rawArgs: unknown, depth: number) => ApplyPatchExtraction;
|
|
51
|
+
};
|
|
52
|
+
export type ApplyPatchRecordActionContext = ApplyPatchActionContext & {
|
|
53
|
+
record: UnknownRecord;
|
|
54
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -7,6 +7,7 @@ export const looksLikePatch = (text) => {
|
|
|
7
7
|
return (t.includes('*** Begin Patch') ||
|
|
8
8
|
t.includes('*** Update File:') ||
|
|
9
9
|
t.includes('*** Add File:') ||
|
|
10
|
+
t.includes('*** Create File:') ||
|
|
10
11
|
t.includes('*** Delete File:') ||
|
|
11
12
|
t.includes('diff --git') ||
|
|
12
13
|
/^(?:@@|\+\+\+\s|---\s)/m.test(t));
|
|
@@ -53,6 +53,77 @@ const convertUnifiedDiffToApplyPatchIfPossible = (text) => {
|
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
|
+
const convertStarHeaderDiffToApplyPatchIfPossible = (text) => {
|
|
57
|
+
try {
|
|
58
|
+
if (!text || text.includes('*** Begin Patch') || text.includes('diff --git'))
|
|
59
|
+
return null;
|
|
60
|
+
const lines = text.replace(/\r\n/g, '\n').split('\n');
|
|
61
|
+
if (lines.length < 3)
|
|
62
|
+
return null;
|
|
63
|
+
const first = String(lines[0] ?? '').trim();
|
|
64
|
+
const second = String(lines[1] ?? '').trim();
|
|
65
|
+
if (!first.startsWith('*** ') || !second.startsWith('--- '))
|
|
66
|
+
return null;
|
|
67
|
+
if (first.startsWith('*** Add File:') || first.startsWith('*** Update File:') || first.startsWith('*** Delete File:')) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (!lines.some((line) => line.startsWith('@@')))
|
|
71
|
+
return null;
|
|
72
|
+
const normalizeHeaderPath = (value) => {
|
|
73
|
+
const v = String(value || '').trim().replace(/\s+\*\*\*$/g, '').trim();
|
|
74
|
+
if (!v)
|
|
75
|
+
return '';
|
|
76
|
+
const m = v.match(/^(?:a\/|b\/)?(.+)$/);
|
|
77
|
+
return (m && m[1] ? m[1] : v).trim();
|
|
78
|
+
};
|
|
79
|
+
const oldPath = normalizeHeaderPath(first.slice(4));
|
|
80
|
+
const newPath = normalizeHeaderPath(second.slice(4));
|
|
81
|
+
const body = lines.slice(2);
|
|
82
|
+
const out = ['*** Begin Patch'];
|
|
83
|
+
if (oldPath === '/dev/null' && newPath && newPath !== '/dev/null') {
|
|
84
|
+
out.push(`*** Add File: ${newPath}`);
|
|
85
|
+
for (const line of body) {
|
|
86
|
+
if (line.startsWith('+')) {
|
|
87
|
+
out.push(line);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
out.push('*** End Patch');
|
|
91
|
+
return out.join('\n');
|
|
92
|
+
}
|
|
93
|
+
if (newPath === '/dev/null' && oldPath && oldPath !== '/dev/null') {
|
|
94
|
+
out.push(`*** Delete File: ${oldPath}`);
|
|
95
|
+
out.push('*** End Patch');
|
|
96
|
+
return out.join('\n');
|
|
97
|
+
}
|
|
98
|
+
const filePath = newPath || oldPath;
|
|
99
|
+
if (!filePath)
|
|
100
|
+
return null;
|
|
101
|
+
out.push(`*** Update File: ${filePath}`);
|
|
102
|
+
for (const line of body) {
|
|
103
|
+
out.push(line);
|
|
104
|
+
}
|
|
105
|
+
out.push('*** End Patch');
|
|
106
|
+
return out.join('\n');
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const normalizeApplyPatchFileHeader = (line) => {
|
|
113
|
+
const addMatch = line.match(/^\*\*\* Add File:\s*(.+?)(?:\s+\*\*\*)?\s*$/);
|
|
114
|
+
if (addMatch && addMatch[1]) {
|
|
115
|
+
return `*** Add File: ${addMatch[1].trim()}`;
|
|
116
|
+
}
|
|
117
|
+
const updateMatch = line.match(/^\*\*\* Update File:\s*(.+?)(?:\s+\*\*\*)?\s*$/);
|
|
118
|
+
if (updateMatch && updateMatch[1]) {
|
|
119
|
+
return `*** Update File: ${updateMatch[1].trim()}`;
|
|
120
|
+
}
|
|
121
|
+
const deleteMatch = line.match(/^\*\*\* Delete File:\s*(.+?)(?:\s+\*\*\*)?\s*$/);
|
|
122
|
+
if (deleteMatch && deleteMatch[1]) {
|
|
123
|
+
return `*** Delete File: ${deleteMatch[1].trim()}`;
|
|
124
|
+
}
|
|
125
|
+
return line;
|
|
126
|
+
};
|
|
56
127
|
const stripCodeFences = (text) => {
|
|
57
128
|
const trimmed = text.trim();
|
|
58
129
|
// Only treat the entire payload as fenced when it *starts* with a code fence.
|
|
@@ -152,7 +223,12 @@ export const normalizeApplyPatchText = (raw) => {
|
|
|
152
223
|
if (converted)
|
|
153
224
|
text = converted;
|
|
154
225
|
}
|
|
155
|
-
else if (!text.includes('*** Begin Patch') &&
|
|
226
|
+
else if (!text.includes('*** Begin Patch') && !text.includes('diff --git')) {
|
|
227
|
+
const converted = convertStarHeaderDiffToApplyPatchIfPossible(text);
|
|
228
|
+
if (converted)
|
|
229
|
+
text = converted;
|
|
230
|
+
}
|
|
231
|
+
if (!text.includes('*** Begin Patch') &&
|
|
156
232
|
!text.includes('diff --git') &&
|
|
157
233
|
text.includes('***************') &&
|
|
158
234
|
/^\*\*\*\s+\S+/m.test(text) &&
|
|
@@ -172,9 +248,11 @@ export const normalizeApplyPatchText = (raw) => {
|
|
|
172
248
|
if (converted)
|
|
173
249
|
text = converted;
|
|
174
250
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
251
|
+
text = text.replace(/\*\*\* Create File:/g, '*** Add File:');
|
|
252
|
+
text = text
|
|
253
|
+
.split('\n')
|
|
254
|
+
.map((line) => normalizeApplyPatchFileHeader(line))
|
|
255
|
+
.join('\n');
|
|
178
256
|
let hasBegin = text.includes('*** Begin Patch');
|
|
179
257
|
const hasEnd = text.includes('*** End Patch');
|
|
180
258
|
if (hasBegin && !hasEnd) {
|
|
@@ -216,29 +294,41 @@ export const normalizeApplyPatchText = (raw) => {
|
|
|
216
294
|
const lines = text.split('\n');
|
|
217
295
|
const output = [];
|
|
218
296
|
let inUpdateSection = false;
|
|
297
|
+
let inAddSection = false;
|
|
219
298
|
let afterUpdateHeader = false;
|
|
220
299
|
for (const line of lines) {
|
|
221
300
|
if (line.startsWith('*** Begin Patch')) {
|
|
222
301
|
output.push(line);
|
|
223
302
|
inUpdateSection = false;
|
|
303
|
+
inAddSection = false;
|
|
224
304
|
afterUpdateHeader = false;
|
|
225
305
|
continue;
|
|
226
306
|
}
|
|
227
307
|
if (line.startsWith('*** End Patch')) {
|
|
228
308
|
output.push(line);
|
|
229
309
|
inUpdateSection = false;
|
|
310
|
+
inAddSection = false;
|
|
230
311
|
afterUpdateHeader = false;
|
|
231
312
|
continue;
|
|
232
313
|
}
|
|
233
314
|
if (line.startsWith('*** Update File:')) {
|
|
234
315
|
output.push(line);
|
|
235
316
|
inUpdateSection = true;
|
|
317
|
+
inAddSection = false;
|
|
236
318
|
afterUpdateHeader = true;
|
|
237
319
|
continue;
|
|
238
320
|
}
|
|
239
|
-
if (line.startsWith('*** Add File:')
|
|
321
|
+
if (line.startsWith('*** Add File:')) {
|
|
240
322
|
output.push(line);
|
|
241
323
|
inUpdateSection = false;
|
|
324
|
+
inAddSection = true;
|
|
325
|
+
afterUpdateHeader = false;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (line.startsWith('*** Delete File:')) {
|
|
329
|
+
output.push(line);
|
|
330
|
+
inUpdateSection = false;
|
|
331
|
+
inAddSection = false;
|
|
242
332
|
afterUpdateHeader = false;
|
|
243
333
|
continue;
|
|
244
334
|
}
|
|
@@ -255,6 +345,15 @@ export const normalizeApplyPatchText = (raw) => {
|
|
|
255
345
|
}
|
|
256
346
|
continue;
|
|
257
347
|
}
|
|
348
|
+
if (inAddSection) {
|
|
349
|
+
if (line.startsWith('+')) {
|
|
350
|
+
output.push(line);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
output.push(`+${line}`);
|
|
354
|
+
}
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
258
357
|
output.push(line);
|
|
259
358
|
}
|
|
260
359
|
return output.join('\n');
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import { isStructuredApplyPatchPayload } from '../structured.js';
|
|
2
2
|
import { tryParseJson } from '../json/parse-loose.js';
|
|
3
3
|
import { asString } from '../validation/shared.js';
|
|
4
|
+
const resolveTopLevelFile = (record) => {
|
|
5
|
+
const direct = asString(record.file) ??
|
|
6
|
+
asString(record.path) ??
|
|
7
|
+
asString(record.filepath) ??
|
|
8
|
+
asString(record.filename);
|
|
9
|
+
if (direct)
|
|
10
|
+
return direct;
|
|
11
|
+
const targetAlias = asString(record.target);
|
|
12
|
+
if (!targetAlias)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (targetAlias.includes('\n') || targetAlias.includes('\r'))
|
|
15
|
+
return undefined;
|
|
16
|
+
if (!/[./\\]/.test(targetAlias))
|
|
17
|
+
return undefined;
|
|
18
|
+
return targetAlias;
|
|
19
|
+
};
|
|
4
20
|
const buildSingleChangePayload = (record) => {
|
|
5
21
|
const kindRaw = asString(record.kind);
|
|
6
22
|
if (!kindRaw)
|
|
@@ -14,7 +30,11 @@ const buildSingleChangePayload = (record) => {
|
|
|
14
30
|
if (typeof record.use_anchor_indent === 'boolean') {
|
|
15
31
|
change.use_anchor_indent = record.use_anchor_indent;
|
|
16
32
|
}
|
|
17
|
-
const changeFile = asString(record.file)
|
|
33
|
+
const changeFile = asString(record.file) ??
|
|
34
|
+
asString(record.path) ??
|
|
35
|
+
asString(record.filepath) ??
|
|
36
|
+
asString(record.filename) ??
|
|
37
|
+
resolveTopLevelFile(record);
|
|
18
38
|
if (changeFile) {
|
|
19
39
|
change.file = changeFile;
|
|
20
40
|
}
|
|
@@ -44,7 +64,11 @@ const coerceChangesArray = (value) => {
|
|
|
44
64
|
return undefined;
|
|
45
65
|
};
|
|
46
66
|
export const coerceStructuredPayload = (record) => {
|
|
67
|
+
const topLevelFile = resolveTopLevelFile(record);
|
|
47
68
|
if (isStructuredApplyPatchPayload(record)) {
|
|
69
|
+
if (topLevelFile && !asString(record.file)) {
|
|
70
|
+
return { ...record, file: topLevelFile };
|
|
71
|
+
}
|
|
48
72
|
return record;
|
|
49
73
|
}
|
|
50
74
|
if (Array.isArray(record.changes) && record.changes.length === 0) {
|
|
@@ -55,7 +79,7 @@ export const coerceStructuredPayload = (record) => {
|
|
|
55
79
|
const changesFromInstructions = coerceChangesArray(record.instructions);
|
|
56
80
|
if (changesFromInstructions) {
|
|
57
81
|
return {
|
|
58
|
-
...(
|
|
82
|
+
...(topLevelFile ? { file: topLevelFile } : {}),
|
|
59
83
|
changes: changesFromInstructions
|
|
60
84
|
};
|
|
61
85
|
}
|
|
@@ -63,14 +87,14 @@ export const coerceStructuredPayload = (record) => {
|
|
|
63
87
|
const changesFromString = coerceChangesArray(record.changes);
|
|
64
88
|
if (changesFromString) {
|
|
65
89
|
return {
|
|
66
|
-
...(
|
|
90
|
+
...(topLevelFile ? { file: topLevelFile } : {}),
|
|
67
91
|
changes: changesFromString
|
|
68
92
|
};
|
|
69
93
|
}
|
|
70
94
|
const editsFromString = coerceChangesArray(record.edits ?? record.operations ?? record.ops);
|
|
71
95
|
if (editsFromString) {
|
|
72
96
|
return {
|
|
73
|
-
...(
|
|
97
|
+
...(topLevelFile ? { file: topLevelFile } : {}),
|
|
74
98
|
changes: editsFromString
|
|
75
99
|
};
|
|
76
100
|
}
|