@jsonstudio/llms 0.6.473 → 0.6.567

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 (47) hide show
  1. package/dist/conversion/compat/actions/claude-thinking-tools.d.ts +15 -0
  2. package/dist/conversion/compat/actions/claude-thinking-tools.js +72 -0
  3. package/dist/conversion/compat/profiles/chat-gemini.json +15 -14
  4. package/dist/conversion/compat/profiles/chat-glm.json +194 -194
  5. package/dist/conversion/compat/profiles/chat-iflow.json +199 -199
  6. package/dist/conversion/compat/profiles/chat-lmstudio.json +43 -43
  7. package/dist/conversion/compat/profiles/chat-qwen.json +20 -20
  8. package/dist/conversion/compat/profiles/responses-c4m.json +42 -42
  9. package/dist/conversion/compat/profiles/responses-output2choices-test.json +12 -0
  10. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
  11. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +2 -0
  12. package/dist/conversion/hub/pipeline/hub-pipeline.js +15 -0
  13. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +15 -0
  14. package/dist/conversion/hub/process/chat-process.js +44 -17
  15. package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +8 -0
  16. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +13 -8
  17. package/dist/conversion/hub/tool-session-compat.d.ts +26 -0
  18. package/dist/conversion/hub/tool-session-compat.js +299 -0
  19. package/dist/conversion/responses/responses-openai-bridge.d.ts +0 -1
  20. package/dist/conversion/responses/responses-openai-bridge.js +0 -71
  21. package/dist/conversion/shared/gemini-tool-utils.js +8 -0
  22. package/dist/conversion/shared/responses-output-builder.js +6 -68
  23. package/dist/conversion/shared/tool-governor.js +75 -4
  24. package/dist/conversion/shared/tool-mapping.js +14 -8
  25. package/dist/filters/special/request-toolcalls-stringify.js +5 -55
  26. package/dist/filters/special/request-tools-normalize.js +0 -19
  27. package/dist/guidance/index.js +25 -9
  28. package/dist/router/virtual-router/engine-health.d.ts +11 -0
  29. package/dist/router/virtual-router/engine-health.js +210 -0
  30. package/dist/router/virtual-router/engine-logging.d.ts +19 -0
  31. package/dist/router/virtual-router/engine-logging.js +165 -0
  32. package/dist/router/virtual-router/engine-selection.d.ts +32 -0
  33. package/dist/router/virtual-router/engine-selection.js +649 -0
  34. package/dist/router/virtual-router/engine.d.ts +4 -13
  35. package/dist/router/virtual-router/engine.js +64 -535
  36. package/dist/router/virtual-router/message-utils.js +22 -0
  37. package/dist/router/virtual-router/routing-instructions.d.ts +6 -1
  38. package/dist/router/virtual-router/routing-instructions.js +119 -3
  39. package/dist/servertool/handlers/gemini-empty-reply-continue.d.ts +1 -0
  40. package/dist/servertool/handlers/gemini-empty-reply-continue.js +120 -0
  41. package/dist/servertool/handlers/stop-message-auto.d.ts +1 -0
  42. package/dist/servertool/handlers/stop-message-auto.js +147 -0
  43. package/dist/servertool/handlers/vision.js +105 -7
  44. package/dist/servertool/server-side-tools.d.ts +2 -0
  45. package/dist/servertool/server-side-tools.js +2 -0
  46. package/dist/tools/tool-registry.js +195 -4
  47. package/package.json +1 -1
@@ -43,6 +43,164 @@ const toJson = (value) => {
43
43
  return '{}';
44
44
  }
45
45
  };
46
+ const APPLY_PATCH_SECTION_RE = /^\*\*\*\s+(Add|Update|Delete|Copy|Move)\s+File:\s*(.+)$/;
47
+ const sanitizeRelativePath = (value) => {
48
+ if (!value)
49
+ return null;
50
+ const trimmed = value.trim().replace(/\\/g, '/');
51
+ if (!trimmed)
52
+ return null;
53
+ if (trimmed.startsWith('/'))
54
+ return null;
55
+ if (/^[a-zA-Z]:/.test(trimmed))
56
+ return null;
57
+ if (trimmed.includes('\0'))
58
+ return null;
59
+ if (trimmed.includes('..')) {
60
+ const segments = trimmed.split('/');
61
+ if (segments.some((seg) => seg === '..')) {
62
+ return null;
63
+ }
64
+ }
65
+ return trimmed.replace(/\/{2,}/g, '/');
66
+ };
67
+ const collectExplicitPaths = (record) => {
68
+ const bucket = [];
69
+ const invalid = [];
70
+ const pushPath = (candidate) => {
71
+ const str = asString(candidate);
72
+ const normalized = sanitizeRelativePath(str);
73
+ if (normalized)
74
+ bucket.push(normalized);
75
+ else if (typeof candidate === 'string')
76
+ invalid.push(candidate);
77
+ };
78
+ const entries = [
79
+ record.paths,
80
+ record.path,
81
+ record.files,
82
+ record.file,
83
+ record.targets,
84
+ record.target_path
85
+ ];
86
+ for (const entry of entries) {
87
+ if (!entry)
88
+ continue;
89
+ if (typeof entry === 'string') {
90
+ pushPath(entry);
91
+ }
92
+ else if (Array.isArray(entry)) {
93
+ entry.forEach((item) => {
94
+ if (typeof item === 'string')
95
+ pushPath(item);
96
+ else if (isRecord(item))
97
+ pushPath(item.path);
98
+ });
99
+ }
100
+ else if (isRecord(entry)) {
101
+ pushPath(entry.path ?? entry.value);
102
+ }
103
+ }
104
+ return { paths: bucket, invalid };
105
+ };
106
+ const looksLikePatchBody = (text) => {
107
+ const trimmed = text.trim();
108
+ if (!trimmed)
109
+ return false;
110
+ const hunkRe = /^@@/m;
111
+ const diffLineRe = /^[ +-]/m;
112
+ return hunkRe.test(trimmed) || diffLineRe.test(trimmed);
113
+ };
114
+ const applyPathOverrides = (lines, overridePaths) => {
115
+ if (!overridePaths.length)
116
+ return;
117
+ const sections = [];
118
+ for (let i = 0; i < lines.length; i += 1) {
119
+ const match = lines[i].match(APPLY_PATCH_SECTION_RE);
120
+ if (match) {
121
+ sections.push({ index: i, action: match[1] });
122
+ }
123
+ }
124
+ if (!sections.length) {
125
+ const beginIdx = lines.findIndex((line) => line.trim() === '*** Begin Patch');
126
+ if (beginIdx >= 0) {
127
+ lines.splice(beginIdx + 1, 0, `*** Update File: ${overridePaths[0]}`);
128
+ }
129
+ return;
130
+ }
131
+ const limit = Math.min(sections.length, overridePaths.length);
132
+ for (let i = 0; i < limit; i += 1) {
133
+ lines[sections[i].index] = `*** ${sections[i].action} File: ${overridePaths[i]}`;
134
+ }
135
+ };
136
+ const normalizeApplyPatchInput = (rawPatch, options) => {
137
+ if (typeof rawPatch !== 'string') {
138
+ return null;
139
+ }
140
+ const text = rawPatch.replace(/\r\n/g, '\n');
141
+ const beginIdx = text.toLowerCase().indexOf('*** begin patch');
142
+ const endIdx = text.toLowerCase().lastIndexOf('*** end patch');
143
+ let candidate = '';
144
+ if (beginIdx >= 0 && endIdx >= 0 && endIdx > beginIdx) {
145
+ candidate = text.slice(beginIdx, endIdx + '*** End Patch'.length);
146
+ }
147
+ else if (options?.overridePaths && options.overridePaths.length) {
148
+ if (!looksLikePatchBody(text)) {
149
+ return null;
150
+ }
151
+ const headerPath = options.overridePaths[0];
152
+ candidate = ['*** Begin Patch', `*** Update File: ${headerPath}`, text.trim(), '*** End Patch'].join('\n');
153
+ }
154
+ else {
155
+ return null;
156
+ }
157
+ const lines = candidate.split(/\r?\n/);
158
+ if (!lines.length) {
159
+ return null;
160
+ }
161
+ let firstIdx = 0;
162
+ while (firstIdx < lines.length && lines[firstIdx].trim().length === 0) {
163
+ firstIdx += 1;
164
+ }
165
+ if (firstIdx >= lines.length) {
166
+ return null;
167
+ }
168
+ const firstLineRaw = lines[firstIdx];
169
+ const firstLineTrimmed = firstLineRaw.trim();
170
+ if (!firstLineTrimmed.toLowerCase().startsWith('*** begin patch')) {
171
+ return null;
172
+ }
173
+ if (firstLineTrimmed !== '*** Begin Patch') {
174
+ lines[firstIdx] = '*** Begin Patch';
175
+ }
176
+ let lastIdx = lines.length - 1;
177
+ while (lastIdx > firstIdx && lines[lastIdx].trim().length === 0) {
178
+ lastIdx -= 1;
179
+ }
180
+ if (lastIdx > firstIdx) {
181
+ const lastLineTrimmed = lines[lastIdx].trim();
182
+ if (lastLineTrimmed.toLowerCase().startsWith('*** end patch') && lastLineTrimmed !== '*** End Patch') {
183
+ lines[lastIdx] = '*** End Patch';
184
+ }
185
+ }
186
+ if (options?.overridePaths && options.overridePaths.length) {
187
+ applyPathOverrides(lines, options.overridePaths);
188
+ }
189
+ const normalized = lines.join('\n');
190
+ if (!normalized.includes('*** Begin Patch') || !normalized.includes('*** End Patch')) {
191
+ return null;
192
+ }
193
+ return normalized;
194
+ };
195
+ const logApplyPatchValidatorError = (message) => {
196
+ try {
197
+ // eslint-disable-next-line no-console
198
+ console.error(`\x1b[31m[apply_patch][validator] ${message}\x1b[0m`);
199
+ }
200
+ catch {
201
+ /* ignore */
202
+ }
203
+ };
46
204
  const detectForbiddenWrite = (script) => {
47
205
  const normalized = script.toLowerCase();
48
206
  if (!normalized) {
@@ -95,12 +253,45 @@ export function validateToolCall(name, argsString) {
95
253
  const rawArgs = tryParseJson(typeof argsString === 'string' ? argsString : '{}');
96
254
  switch (normalizedName) {
97
255
  case 'apply_patch': {
98
- const input = asString(rawArgs.input);
99
- const patch = asString(rawArgs.patch) ?? input;
100
- if (!patch) {
256
+ const record = rawArgs;
257
+ const inputField = asString(record.input);
258
+ const patchField = asString(record.patch);
259
+ const rawStr = typeof argsString === 'string' ? argsString : '';
260
+ let patchRaw = null;
261
+ if (patchField) {
262
+ patchRaw = patchField;
263
+ }
264
+ else if (inputField) {
265
+ patchRaw = inputField;
266
+ }
267
+ else if (rawStr && /\*\*\*\s+Begin Patch/i.test(rawStr)) {
268
+ patchRaw = rawStr;
269
+ }
270
+ if (!patchRaw) {
271
+ logApplyPatchValidatorError('missing apply_patch patch/input payload');
101
272
  return { ok: false, reason: 'missing_input' };
102
273
  }
103
- return { ok: true, normalizedArgs: toJson({ input: patch, patch }) };
274
+ const { paths: explicitPaths, invalid } = collectExplicitPaths(record);
275
+ if (invalid.length) {
276
+ invalid.forEach((value) => logApplyPatchValidatorError(`invalid path argument: ${value}`));
277
+ }
278
+ const normalizedPatch = normalizeApplyPatchInput(patchRaw, { overridePaths: explicitPaths });
279
+ if (!normalizedPatch) {
280
+ logApplyPatchValidatorError('patch text missing *** Begin Patch/*** End Patch or failed to auto-wrap with provided paths');
281
+ return { ok: false, reason: 'invalid_patch_header' };
282
+ }
283
+ // Canonical, parameterized shape; keep input for backwards compatibility.
284
+ const payload = {
285
+ patch: normalizedPatch,
286
+ input: normalizedPatch
287
+ };
288
+ if (explicitPaths.length) {
289
+ payload.paths = explicitPaths;
290
+ }
291
+ return {
292
+ ok: true,
293
+ normalizedArgs: toJson(payload)
294
+ };
104
295
  }
105
296
  case 'shell': {
106
297
  const rawCommand = rawArgs.command;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonstudio/llms",
3
- "version": "0.6.473",
3
+ "version": "0.6.567",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",