@jsonstudio/llms 0.6.749 → 0.6.795

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 (41) hide show
  1. package/dist/conversion/compat/actions/apply-patch-fixer.d.ts +1 -0
  2. package/dist/conversion/compat/actions/apply-patch-fixer.js +30 -0
  3. package/dist/conversion/compat/actions/apply-patch-format-fixer.d.ts +1 -0
  4. package/dist/conversion/compat/actions/apply-patch-format-fixer.js +233 -0
  5. package/dist/conversion/compat/actions/index.d.ts +2 -0
  6. package/dist/conversion/compat/actions/index.js +2 -0
  7. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +6 -0
  8. package/dist/conversion/hub/pipeline/hub-pipeline.js +35 -0
  9. package/dist/conversion/shared/bridge-message-utils.d.ts +1 -0
  10. package/dist/conversion/shared/bridge-message-utils.js +7 -0
  11. package/dist/conversion/shared/bridge-policies.js +8 -8
  12. package/dist/conversion/shared/errors.d.ts +1 -1
  13. package/dist/conversion/shared/errors.js +3 -0
  14. package/dist/conversion/shared/tool-governor.js +18 -23
  15. package/dist/filters/special/response-tool-arguments-stringify.js +3 -22
  16. package/dist/router/virtual-router/engine.d.ts +5 -0
  17. package/dist/router/virtual-router/engine.js +21 -0
  18. package/dist/servertool/engine.js +74 -3
  19. package/dist/servertool/server-side-tools.js +19 -8
  20. package/dist/tools/apply-patch/regression-capturer.d.ts +12 -0
  21. package/dist/tools/apply-patch/regression-capturer.js +112 -0
  22. package/dist/tools/apply-patch/structured.d.ts +20 -0
  23. package/dist/tools/apply-patch/structured.js +441 -0
  24. package/dist/tools/apply-patch/validator.d.ts +8 -0
  25. package/dist/tools/apply-patch/validator.js +466 -0
  26. package/dist/tools/apply-patch-structured.d.ts +1 -20
  27. package/dist/tools/apply-patch-structured.js +1 -277
  28. package/dist/tools/args-json.d.ts +1 -0
  29. package/dist/tools/args-json.js +175 -0
  30. package/dist/tools/exec-command/normalize.d.ts +17 -0
  31. package/dist/tools/exec-command/normalize.js +112 -0
  32. package/dist/tools/exec-command/regression-capturer.d.ts +11 -0
  33. package/dist/tools/exec-command/regression-capturer.js +144 -0
  34. package/dist/tools/exec-command/validator.d.ts +6 -0
  35. package/dist/tools/exec-command/validator.js +22 -0
  36. package/dist/tools/patch-args-normalizer.d.ts +15 -0
  37. package/dist/tools/patch-args-normalizer.js +472 -0
  38. package/dist/tools/patch-regression-capturer.d.ts +1 -0
  39. package/dist/tools/patch-regression-capturer.js +1 -0
  40. package/dist/tools/tool-registry.js +36 -541
  41. package/package.json +1 -1
@@ -3,7 +3,64 @@ import { ProviderProtocolError } from '../conversion/shared/errors.js';
3
3
  import { createHash } from 'node:crypto';
4
4
  import { loadRoutingInstructionStateSync, saveRoutingInstructionStateSync } from '../router/virtual-router/sticky-session-store.js';
5
5
  import { deserializeRoutingInstructionState, serializeRoutingInstructionState } from '../router/virtual-router/routing-instructions.js';
6
+ function parseTimeoutMs(raw, fallback) {
7
+ const n = typeof raw === 'string' ? Number(raw.trim()) : typeof raw === 'number' ? raw : NaN;
8
+ if (!Number.isFinite(n) || n <= 0) {
9
+ return fallback;
10
+ }
11
+ return Math.floor(n);
12
+ }
13
+ function resolveServerToolTimeoutMs() {
14
+ return parseTimeoutMs(process.env.ROUTECODEX_SERVERTOOL_TIMEOUT_MS ||
15
+ process.env.RCC_SERVERTOOL_TIMEOUT_MS ||
16
+ process.env.LLMSWITCH_SERVERTOOL_TIMEOUT_MS, 500_000);
17
+ }
18
+ function resolveServerToolFollowupTimeoutMs(fallback) {
19
+ return parseTimeoutMs(process.env.ROUTECODEX_SERVERTOOL_FOLLOWUP_TIMEOUT_MS ||
20
+ process.env.RCC_SERVERTOOL_FOLLOWUP_TIMEOUT_MS ||
21
+ process.env.LLMSWITCH_SERVERTOOL_FOLLOWUP_TIMEOUT_MS, fallback);
22
+ }
23
+ function withTimeout(promise, timeoutMs, buildError) {
24
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
25
+ return promise;
26
+ }
27
+ let timer;
28
+ return new Promise((resolve, reject) => {
29
+ timer = setTimeout(() => reject(buildError()), timeoutMs);
30
+ promise.then(resolve, reject).finally(() => {
31
+ if (timer) {
32
+ clearTimeout(timer);
33
+ timer = undefined;
34
+ }
35
+ });
36
+ });
37
+ }
38
+ function isServerToolTimeoutError(error) {
39
+ return Boolean(error &&
40
+ typeof error === 'object' &&
41
+ typeof error.code === 'string' &&
42
+ error.code === 'SERVERTOOL_TIMEOUT');
43
+ }
44
+ function createServerToolTimeoutError(options) {
45
+ const err = new ProviderProtocolError(`[servertool] ${options.phase} timeout after ${options.timeoutMs}ms` +
46
+ (options.flowId ? ` flow=${options.flowId}` : ''), {
47
+ code: 'SERVERTOOL_TIMEOUT',
48
+ category: 'INTERNAL_ERROR',
49
+ details: {
50
+ requestId: options.requestId,
51
+ phase: options.phase,
52
+ flowId: options.flowId,
53
+ timeoutMs: options.timeoutMs,
54
+ attempt: options.attempt,
55
+ maxAttempts: options.maxAttempts
56
+ }
57
+ });
58
+ err.status = 504;
59
+ return err;
60
+ }
6
61
  export async function runServerToolOrchestration(options) {
62
+ const serverToolTimeoutMs = resolveServerToolTimeoutMs();
63
+ const followupTimeoutMs = resolveServerToolFollowupTimeoutMs(serverToolTimeoutMs);
7
64
  const engineOptions = {
8
65
  chatResponse: options.chat,
9
66
  adapterContext: options.adapterContext,
@@ -13,7 +70,11 @@ export async function runServerToolOrchestration(options) {
13
70
  providerInvoker: options.providerInvoker,
14
71
  reenterPipeline: options.reenterPipeline
15
72
  };
16
- const engineResult = await runServerSideToolEngine(engineOptions);
73
+ const engineResult = await withTimeout(runServerSideToolEngine(engineOptions), serverToolTimeoutMs, () => createServerToolTimeoutError({
74
+ requestId: options.requestId,
75
+ phase: 'engine',
76
+ timeoutMs: serverToolTimeoutMs
77
+ }));
17
78
  if (engineResult.mode === 'passthrough' || !engineResult.execution) {
18
79
  return {
19
80
  chat: engineResult.finalChatResponse,
@@ -70,12 +131,19 @@ export async function runServerToolOrchestration(options) {
70
131
  if (isStopMessageFlow) {
71
132
  reservation = reserveStopMessageUsage(options.adapterContext);
72
133
  }
73
- followup = await options.reenterPipeline({
134
+ followup = await withTimeout(options.reenterPipeline({
74
135
  entryEndpoint: followupEntryEndpoint,
75
136
  requestId: followupRequestId,
76
137
  body: engineResult.execution.followup.payload,
77
138
  metadata
78
- });
139
+ }), followupTimeoutMs, () => createServerToolTimeoutError({
140
+ requestId: options.requestId,
141
+ phase: 'followup',
142
+ timeoutMs: followupTimeoutMs,
143
+ flowId: engineResult.execution.flowId,
144
+ attempt,
145
+ maxAttempts
146
+ }));
79
147
  lastError = undefined;
80
148
  break;
81
149
  }
@@ -84,6 +152,9 @@ export async function runServerToolOrchestration(options) {
84
152
  rollbackStopMessageUsage(reservation);
85
153
  reservation = null;
86
154
  }
155
+ if (isServerToolTimeoutError(error)) {
156
+ throw error;
157
+ }
87
158
  lastError = error;
88
159
  if (attempt >= maxAttempts) {
89
160
  const wrapped = new ProviderProtocolError(`[servertool] Followup failed for flow ${engineResult.execution.flowId ?? 'unknown'} ` +
@@ -1,4 +1,5 @@
1
1
  import { getServerToolHandler, listAutoServerToolHandlers } from './registry.js';
2
+ import { ProviderProtocolError } from '../conversion/shared/errors.js';
2
3
  import './handlers/web-search.js';
3
4
  import './handlers/vision.js';
4
5
  import './handlers/iflow-model-error-retry.js';
@@ -49,14 +50,24 @@ async function runHandler(handler, ctx) {
49
50
  return await handler(ctx);
50
51
  }
51
52
  catch (error) {
52
- try {
53
- // eslint-disable-next-line no-console
54
- console.error('[servertool] handler failed', error);
55
- }
56
- catch {
57
- /* logging best-effort */
58
- }
59
- return null;
53
+ const toolName = ctx && ctx.toolCall && typeof ctx.toolCall.name === 'string' && ctx.toolCall.name.trim()
54
+ ? ctx.toolCall.name.trim()
55
+ : 'auto';
56
+ const message = error instanceof Error ? error.message : String(error ?? 'unknown');
57
+ const wrapped = new ProviderProtocolError(`[servertool] handler failed: ${toolName}: ${message}`, {
58
+ code: 'SERVERTOOL_HANDLER_FAILED',
59
+ category: 'INTERNAL_ERROR',
60
+ details: {
61
+ toolName,
62
+ requestId: ctx.options?.requestId,
63
+ entryEndpoint: ctx.options?.entryEndpoint,
64
+ providerProtocol: ctx.options?.providerProtocol,
65
+ error: message
66
+ }
67
+ });
68
+ wrapped.status = 500;
69
+ wrapped.cause = error;
70
+ throw wrapped;
60
71
  }
61
72
  }
62
73
  export function extractToolCalls(chatResponse) {
@@ -0,0 +1,12 @@
1
+ interface RegressionSample {
2
+ id: string;
3
+ timestamp: string;
4
+ errorType: string;
5
+ originalArgs: string;
6
+ normalizedArgs?: string;
7
+ fixerResult?: string;
8
+ validationError?: string;
9
+ source?: string;
10
+ }
11
+ export declare function captureApplyPatchRegression(sample: Omit<RegressionSample, 'id' | 'timestamp'>): void;
12
+ export {};
@@ -0,0 +1,112 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import { createHash } from 'crypto';
5
+ /**
6
+ * Captures apply_patch payload regressions into a golden samples structure.
7
+ *
8
+ * Trigger: apply_patch validation failure OR fixer failure
9
+ * Condition: non-context errors only (format/json/prefix/etc.)
10
+ *
11
+ * Default destination:
12
+ * ~/.routecodex/golden_samples/ci-regression/apply_patch/<error-type>/
13
+ *
14
+ * Optional repo destination (explicit opt-in):
15
+ * ROUTECODEX_APPLY_PATCH_REGRESSION_TO_REPO=1
16
+ * → <repoRoot>/samples/ci-goldens/_regressions/apply_patch/<error-type>/
17
+ *
18
+ * Explicit override:
19
+ * ROUTECODEX_APPLY_PATCH_REGRESSION_DIR=/abs/path
20
+ */
21
+ const MAX_SAMPLES_PER_TYPE = 50;
22
+ function detectRepoRootFromCwd() {
23
+ try {
24
+ let dir = process.cwd();
25
+ for (let i = 0; i < 12; i += 1) {
26
+ const marker = path.join(dir, 'samples', 'ci-goldens');
27
+ const pkg = path.join(dir, 'package.json');
28
+ if (fs.existsSync(marker) && fs.existsSync(pkg)) {
29
+ try {
30
+ const raw = fs.readFileSync(pkg, 'utf-8');
31
+ const json = JSON.parse(raw);
32
+ if (json && typeof json === 'object' && String(json.name || '') === 'routecodex') {
33
+ return dir;
34
+ }
35
+ }
36
+ catch {
37
+ // ignore parse errors
38
+ }
39
+ }
40
+ const parent = path.dirname(dir);
41
+ if (parent === dir)
42
+ break;
43
+ dir = parent;
44
+ }
45
+ }
46
+ catch {
47
+ // ignore
48
+ }
49
+ return null;
50
+ }
51
+ function resolveSamplesRoot() {
52
+ const explicit = String(process?.env?.ROUTECODEX_APPLY_PATCH_REGRESSION_DIR || '').trim();
53
+ if (explicit) {
54
+ return path.resolve(explicit);
55
+ }
56
+ const toRepo = String(process?.env?.ROUTECODEX_APPLY_PATCH_REGRESSION_TO_REPO || '').trim() === '1';
57
+ if (toRepo) {
58
+ const repoRoot = detectRepoRootFromCwd();
59
+ if (repoRoot) {
60
+ return path.join(repoRoot, 'samples', 'ci-goldens', '_regressions', 'apply_patch');
61
+ }
62
+ }
63
+ return path.join(os.homedir(), '.routecodex', 'golden_samples', 'ci-regression', 'apply_patch');
64
+ }
65
+ function isContextError(error) {
66
+ if (!error)
67
+ return false;
68
+ const lower = error.toLowerCase();
69
+ return (lower.includes('failed to find context') ||
70
+ lower.includes('failed to find expected lines') ||
71
+ lower.includes('file not found') ||
72
+ lower.includes('no such file'));
73
+ }
74
+ function stableSampleId(sample) {
75
+ const key = `${String(sample.errorType || 'unknown')}:${String(sample.originalArgs ?? '')}:` +
76
+ `${String(sample.normalizedArgs ?? '')}:${String(sample.fixerResult ?? '')}:${String(sample.validationError ?? '')}`;
77
+ return createHash('sha1').update(key).digest('hex').slice(0, 16);
78
+ }
79
+ export function captureApplyPatchRegression(sample) {
80
+ try {
81
+ if (isContextError(sample.validationError))
82
+ return;
83
+ const root = resolveSamplesRoot();
84
+ const safeType = String(sample.errorType || 'unknown').replace(/[^a-z0-9-]/gi, '_');
85
+ const typeDir = path.join(root, safeType);
86
+ if (!fs.existsSync(typeDir)) {
87
+ fs.mkdirSync(typeDir, { recursive: true });
88
+ }
89
+ // Check limit (best-effort; keep runtime safe)
90
+ try {
91
+ const existing = fs.readdirSync(typeDir).filter((f) => f.endsWith('.json'));
92
+ if (existing.length >= MAX_SAMPLES_PER_TYPE)
93
+ return;
94
+ }
95
+ catch {
96
+ // ignore
97
+ }
98
+ const id = `sample_${stableSampleId(sample)}`;
99
+ const file = path.join(typeDir, `${id}.json`);
100
+ if (fs.existsSync(file))
101
+ return;
102
+ const fullSample = {
103
+ id,
104
+ timestamp: new Date().toISOString(),
105
+ ...sample
106
+ };
107
+ fs.writeFileSync(file, JSON.stringify(fullSample, null, 2), 'utf-8');
108
+ }
109
+ catch {
110
+ // Silently fail to avoid disrupting runtime
111
+ }
112
+ }
@@ -0,0 +1,20 @@
1
+ export type StructuredApplyPatchKind = 'insert_after' | 'insert_before' | 'replace' | 'delete' | 'create_file' | 'delete_file';
2
+ export interface StructuredApplyPatchChange {
3
+ file?: string;
4
+ kind: StructuredApplyPatchKind | string;
5
+ anchor?: string;
6
+ target?: string;
7
+ lines?: string[] | string;
8
+ use_anchor_indent?: boolean;
9
+ }
10
+ export interface StructuredApplyPatchPayload extends Record<string, unknown> {
11
+ instructions?: string;
12
+ file?: string;
13
+ changes: StructuredApplyPatchChange[];
14
+ }
15
+ export declare class StructuredApplyPatchError extends Error {
16
+ reason: string;
17
+ constructor(reason: string, message: string);
18
+ }
19
+ export declare function buildStructuredPatch(payload: StructuredApplyPatchPayload): string;
20
+ export declare function isStructuredApplyPatchPayload(candidate: unknown): candidate is StructuredApplyPatchPayload;