@jsonstudio/llms 0.6.1739 → 0.6.1890

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 (107) hide show
  1. package/dist/conversion/compat/actions/deepseek-web-request.d.ts +3 -0
  2. package/dist/conversion/compat/actions/deepseek-web-request.js +350 -0
  3. package/dist/conversion/compat/actions/deepseek-web-response.d.ts +3 -0
  4. package/dist/conversion/compat/actions/deepseek-web-response.js +886 -0
  5. package/dist/conversion/compat/actions/gemini-cli-request.js +3 -1
  6. package/dist/conversion/compat/profiles/chat-deepseek-web.json +18 -0
  7. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +166 -2
  8. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +169 -0
  9. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +6 -0
  10. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +12 -0
  11. package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -0
  12. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +4 -0
  13. package/dist/conversion/hub/pipeline/hub-pipeline.js +365 -144
  14. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +9 -0
  15. package/dist/conversion/hub/policy/policy-engine.d.ts +2 -0
  16. package/dist/conversion/hub/policy/policy-engine.js +8 -0
  17. package/dist/conversion/hub/process/chat-process.js +466 -16
  18. package/dist/conversion/hub/response/provider-response.js +0 -35
  19. package/dist/conversion/responses/responses-openai-bridge.d.ts +2 -0
  20. package/dist/conversion/responses/responses-openai-bridge.js +166 -8
  21. package/dist/conversion/shared/anthropic-message-utils.js +10 -1
  22. package/dist/conversion/shared/protocol-field-allowlists.d.ts +2 -2
  23. package/dist/conversion/shared/protocol-field-allowlists.js +4 -0
  24. package/dist/conversion/shared/tool-governor.js +102 -0
  25. package/dist/guidance/index.js +17 -0
  26. package/dist/router/virtual-router/bootstrap.js +46 -1
  27. package/dist/router/virtual-router/classifier.js +59 -4
  28. package/dist/router/virtual-router/engine/health/index.js +6 -6
  29. package/dist/router/virtual-router/engine/routing-state/store.js +16 -3
  30. package/dist/router/virtual-router/engine-logging.js +62 -24
  31. package/dist/router/virtual-router/engine-selection/route-utils.js +20 -20
  32. package/dist/router/virtual-router/engine-selection/tier-selection.js +2 -2
  33. package/dist/router/virtual-router/engine.d.ts +3 -1
  34. package/dist/router/virtual-router/engine.js +359 -39
  35. package/dist/router/virtual-router/features.js +2 -1
  36. package/dist/router/virtual-router/pre-command-file-resolver.d.ts +2 -0
  37. package/dist/router/virtual-router/pre-command-file-resolver.js +90 -0
  38. package/dist/router/virtual-router/provider-registry.js +3 -1
  39. package/dist/router/virtual-router/routing-instructions.d.ts +15 -1
  40. package/dist/router/virtual-router/routing-instructions.js +110 -151
  41. package/dist/router/virtual-router/routing-pre-command-actions.d.ts +3 -0
  42. package/dist/router/virtual-router/routing-pre-command-actions.js +26 -0
  43. package/dist/router/virtual-router/routing-pre-command-parser.d.ts +2 -0
  44. package/dist/router/virtual-router/routing-pre-command-parser.js +85 -0
  45. package/dist/router/virtual-router/routing-pre-command-state-codec.d.ts +3 -0
  46. package/dist/router/virtual-router/routing-pre-command-state-codec.js +24 -0
  47. package/dist/router/virtual-router/routing-stop-message-actions.d.ts +2 -0
  48. package/dist/router/virtual-router/routing-stop-message-actions.js +96 -0
  49. package/dist/router/virtual-router/routing-stop-message-parser.d.ts +3 -0
  50. package/dist/router/virtual-router/routing-stop-message-parser.js +142 -0
  51. package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +4 -0
  52. package/dist/router/virtual-router/routing-stop-message-state-codec.js +85 -0
  53. package/dist/router/virtual-router/sticky-session-store.js +206 -57
  54. package/dist/router/virtual-router/stop-message-stage-template-files.d.ts +12 -0
  55. package/dist/router/virtual-router/stop-message-stage-template-files.js +67 -0
  56. package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
  57. package/dist/router/virtual-router/stop-message-state-sync.js +5 -0
  58. package/dist/router/virtual-router/token-file-scanner.d.ts +9 -0
  59. package/dist/router/virtual-router/token-file-scanner.js +64 -3
  60. package/dist/router/virtual-router/tool-signals.d.ts +5 -0
  61. package/dist/router/virtual-router/tool-signals.js +42 -3
  62. package/dist/router/virtual-router/types.d.ts +19 -1
  63. package/dist/router/virtual-router/types.js +1 -0
  64. package/dist/servertool/clock/config.d.ts +1 -1
  65. package/dist/servertool/clock/config.js +27 -4
  66. package/dist/servertool/clock/state.js +41 -2
  67. package/dist/servertool/clock/task-store.d.ts +2 -2
  68. package/dist/servertool/clock/task-store.js +1 -1
  69. package/dist/servertool/clock/tasks.d.ts +3 -1
  70. package/dist/servertool/clock/tasks.js +209 -18
  71. package/dist/servertool/clock/types.d.ts +17 -0
  72. package/dist/servertool/continue-execution/log.d.ts +3 -0
  73. package/dist/servertool/continue-execution/log.js +13 -0
  74. package/dist/servertool/engine.js +414 -68
  75. package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +6 -6
  76. package/dist/servertool/handlers/clock-auto.js +54 -71
  77. package/dist/servertool/handlers/clock.js +121 -6
  78. package/dist/servertool/handlers/continue-execution.d.ts +1 -0
  79. package/dist/servertool/handlers/continue-execution.js +91 -0
  80. package/dist/servertool/handlers/followup-request-builder.js +13 -0
  81. package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -1
  82. package/dist/servertool/handlers/iflow-model-error-retry.js +1 -1
  83. package/dist/servertool/handlers/recursive-detection-guard.js +1 -1
  84. package/dist/servertool/handlers/stop-message-auto.js +386 -257
  85. package/dist/servertool/handlers/stop-message-stage-policy.d.ts +43 -0
  86. package/dist/servertool/handlers/stop-message-stage-policy.js +684 -0
  87. package/dist/servertool/handlers/vision.js +1 -1
  88. package/dist/servertool/log/progress-file.d.ts +14 -0
  89. package/dist/servertool/log/progress-file.js +88 -0
  90. package/dist/servertool/pre-command-hooks.d.ts +17 -0
  91. package/dist/servertool/pre-command-hooks.js +491 -0
  92. package/dist/servertool/registry.d.ts +23 -6
  93. package/dist/servertool/registry.js +66 -1
  94. package/dist/servertool/server-side-tools.d.ts +1 -0
  95. package/dist/servertool/server-side-tools.js +216 -14
  96. package/dist/servertool/stop-gateway-context.d.ts +14 -0
  97. package/dist/servertool/stop-gateway-context.js +167 -0
  98. package/dist/servertool/stop-message-compare-context.d.ts +24 -0
  99. package/dist/servertool/stop-message-compare-context.js +133 -0
  100. package/dist/servertool/types.d.ts +12 -0
  101. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
  102. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +36 -1
  103. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +3 -0
  104. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +3 -0
  105. package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +118 -1
  106. package/dist/tools/apply-patch/args-normalizer/default-actions.js +1 -1
  107. package/package.json +1 -1
@@ -0,0 +1,14 @@
1
+ export type ServerToolProgressFileEvent = {
2
+ requestId: string;
3
+ flowId: string;
4
+ tool: string;
5
+ stage: string;
6
+ result: string;
7
+ message: string;
8
+ step: number;
9
+ entryEndpoint?: string;
10
+ providerProtocol?: string;
11
+ };
12
+ export declare function appendServerToolProgressFileEvent(event: ServerToolProgressFileEvent): void;
13
+ export declare function resetServerToolProgressFileLoggerForTests(): void;
14
+ export declare function flushServerToolProgressFileLoggerForTests(): Promise<void>;
@@ -0,0 +1,88 @@
1
+ import * as os from 'node:os';
2
+ import * as path from 'node:path';
3
+ import { promises as fs } from 'node:fs';
4
+ const truthy = new Set(['1', 'true', 'yes', 'on']);
5
+ const falsy = new Set(['0', 'false', 'no', 'off']);
6
+ const DEFAULT_LOG_PATH = path.join(os.homedir(), '.routecodex', 'logs', 'servertool-events.jsonl');
7
+ let cachedEnabled = null;
8
+ let cachedLogPath = null;
9
+ let writeQueue = Promise.resolve();
10
+ const ensuredDirs = new Set();
11
+ function isDevMode() {
12
+ const nodeEnv = String(process.env.NODE_ENV || '').trim().toLowerCase();
13
+ if (nodeEnv === 'development') {
14
+ return true;
15
+ }
16
+ const buildMode = String(process.env.ROUTECODEX_BUILD_MODE ??
17
+ process.env.RCC_BUILD_MODE ??
18
+ process.env.BUILD_MODE ??
19
+ process.env.LLMSWITCH_BUILD_MODE ??
20
+ '')
21
+ .trim()
22
+ .toLowerCase();
23
+ return buildMode === 'dev' || buildMode === 'development';
24
+ }
25
+ function resolveEnabled() {
26
+ if (cachedEnabled !== null) {
27
+ return cachedEnabled;
28
+ }
29
+ const raw = String(process.env.ROUTECODEX_SERVERTOOL_FILE_LOG ??
30
+ process.env.RCC_SERVERTOOL_FILE_LOG ??
31
+ process.env.LLMSWITCH_SERVERTOOL_FILE_LOG ??
32
+ '')
33
+ .trim()
34
+ .toLowerCase();
35
+ if (truthy.has(raw)) {
36
+ cachedEnabled = true;
37
+ return true;
38
+ }
39
+ if (falsy.has(raw)) {
40
+ cachedEnabled = false;
41
+ return false;
42
+ }
43
+ cachedEnabled = isDevMode();
44
+ return cachedEnabled;
45
+ }
46
+ function resolveLogPath() {
47
+ if (cachedLogPath) {
48
+ return cachedLogPath;
49
+ }
50
+ const raw = String(process.env.ROUTECODEX_SERVERTOOL_FILE_LOG_PATH ??
51
+ process.env.RCC_SERVERTOOL_FILE_LOG_PATH ??
52
+ process.env.LLMSWITCH_SERVERTOOL_FILE_LOG_PATH ??
53
+ '').trim();
54
+ cachedLogPath = raw || DEFAULT_LOG_PATH;
55
+ return cachedLogPath;
56
+ }
57
+ async function ensureParentDir(logPath) {
58
+ const dir = path.dirname(logPath);
59
+ if (ensuredDirs.has(dir)) {
60
+ return;
61
+ }
62
+ await fs.mkdir(dir, { recursive: true });
63
+ ensuredDirs.add(dir);
64
+ }
65
+ export function appendServerToolProgressFileEvent(event) {
66
+ if (!resolveEnabled()) {
67
+ return;
68
+ }
69
+ const logPath = resolveLogPath();
70
+ const line = `${JSON.stringify({ ...event, ts: new Date().toISOString() })}\n`;
71
+ writeQueue = writeQueue
72
+ .then(async () => {
73
+ await ensureParentDir(logPath);
74
+ await fs.appendFile(logPath, line, 'utf8');
75
+ })
76
+ .catch(() => {
77
+ // best-effort file logging
78
+ });
79
+ }
80
+ export function resetServerToolProgressFileLoggerForTests() {
81
+ cachedEnabled = null;
82
+ cachedLogPath = null;
83
+ ensuredDirs.clear();
84
+ writeQueue = Promise.resolve();
85
+ }
86
+ export async function flushServerToolProgressFileLoggerForTests() {
87
+ await writeQueue;
88
+ }
@@ -0,0 +1,17 @@
1
+ import type { ServerToolAutoHookTraceEvent } from './types.js';
2
+ export interface PreCommandHookRunOptions {
3
+ requestId: string;
4
+ entryEndpoint: string;
5
+ providerProtocol: string;
6
+ toolName: string;
7
+ toolCallId: string;
8
+ toolArguments: string;
9
+ preCommandState?: unknown;
10
+ }
11
+ export interface PreCommandHookRunResult {
12
+ toolArguments: string;
13
+ changed: boolean;
14
+ traces: ServerToolAutoHookTraceEvent[];
15
+ }
16
+ export declare function runPreCommandHooks(options: PreCommandHookRunOptions): PreCommandHookRunResult;
17
+ export declare function resetPreCommandHooksCacheForTests(): void;
@@ -0,0 +1,491 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { isPreCommandScriptPathAllowed } from '../router/virtual-router/pre-command-file-resolver.js';
6
+ const DEFAULT_PRE_COMMAND_HOOKS_FILE = path.join(os.homedir(), '.routecodex', 'hooks', 'pre-command-hooks.json');
7
+ const DEFAULT_TIMEOUT_MS = 2000;
8
+ const DEFAULT_TOOLS = ['exec_command', 'shell', 'shell_command'];
9
+ let cachedConfig = null;
10
+ export function runPreCommandHooks(options) {
11
+ const runtimeRule = resolveRuntimePreCommandRule(options.preCommandState);
12
+ const config = runtimeRule
13
+ ? { enabled: true, hooks: [runtimeRule] }
14
+ : loadPreCommandHooksConfig();
15
+ if (!config.enabled || config.hooks.length === 0) {
16
+ return {
17
+ toolArguments: options.toolArguments,
18
+ changed: false,
19
+ traces: []
20
+ };
21
+ }
22
+ const traces = [];
23
+ let changed = false;
24
+ let currentArguments = options.toolArguments;
25
+ let currentParsedArgs = parseToolArgumentsObject(currentArguments);
26
+ let currentCommandText = extractCommandText(currentParsedArgs, currentArguments);
27
+ const normalizedTool = normalizeToolName(options.toolName);
28
+ for (const hook of config.hooks) {
29
+ const traceBase = {
30
+ hookId: hook.id,
31
+ phase: 'pre_command',
32
+ priority: hook.priority,
33
+ queue: 'A_optional',
34
+ queueIndex: 0,
35
+ queueTotal: config.hooks.length
36
+ };
37
+ if (!hook.toolNames.has(normalizedTool)) {
38
+ traces.push({ ...traceBase, result: 'miss', reason: 'tool_mismatch' });
39
+ continue;
40
+ }
41
+ if (hook.cmdRegex && !hook.cmdRegex.test(currentCommandText)) {
42
+ traces.push({ ...traceBase, result: 'miss', reason: 'cmd_regex_mismatch' });
43
+ continue;
44
+ }
45
+ try {
46
+ let matched = false;
47
+ if (hook.runtimeScriptPath && hook.runtimeScriptPath.trim()) {
48
+ const runtimeOutput = runRuntimeScriptHook(hook.runtimeScriptPath, {
49
+ requestId: options.requestId,
50
+ entryEndpoint: options.entryEndpoint,
51
+ providerProtocol: options.providerProtocol,
52
+ toolName: normalizedTool,
53
+ toolCallId: options.toolCallId,
54
+ arguments: currentParsedArgs ?? { args_raw: currentArguments },
55
+ command: currentCommandText,
56
+ hookId: hook.id
57
+ }, hook.timeoutMs);
58
+ if (typeof runtimeOutput === 'string' && runtimeOutput !== currentArguments) {
59
+ changed = true;
60
+ currentArguments = runtimeOutput;
61
+ currentParsedArgs = parseToolArgumentsObject(currentArguments);
62
+ currentCommandText = extractCommandText(currentParsedArgs, currentArguments);
63
+ }
64
+ matched = true;
65
+ }
66
+ if (hook.jqExpression && hook.jqExpression.trim()) {
67
+ const jqInput = currentParsedArgs ?? { args_raw: currentArguments };
68
+ const transformed = runJqTransform(hook.jqExpression, jqInput, hook.timeoutMs);
69
+ currentParsedArgs = transformed;
70
+ const nextArgs = JSON.stringify(transformed);
71
+ if (nextArgs !== currentArguments) {
72
+ changed = true;
73
+ currentArguments = nextArgs;
74
+ currentCommandText = extractCommandText(currentParsedArgs, currentArguments);
75
+ }
76
+ matched = true;
77
+ }
78
+ if (hook.shellCommand && hook.shellCommand.trim()) {
79
+ runShellCommandHook(hook.shellCommand, {
80
+ requestId: options.requestId,
81
+ entryEndpoint: options.entryEndpoint,
82
+ providerProtocol: options.providerProtocol,
83
+ toolName: normalizedTool,
84
+ toolCallId: options.toolCallId,
85
+ arguments: currentParsedArgs ?? { args_raw: currentArguments },
86
+ command: currentCommandText,
87
+ hookId: hook.id
88
+ }, hook.timeoutMs);
89
+ matched = true;
90
+ }
91
+ traces.push({
92
+ ...traceBase,
93
+ result: 'match',
94
+ reason: matched ? (changed ? 'applied' : 'matched') : 'no_action'
95
+ });
96
+ }
97
+ catch (error) {
98
+ const message = error instanceof Error ? error.message : String(error ?? 'unknown_error');
99
+ traces.push({
100
+ ...traceBase,
101
+ result: 'error',
102
+ reason: message
103
+ });
104
+ }
105
+ }
106
+ return {
107
+ toolArguments: currentArguments,
108
+ changed,
109
+ traces
110
+ };
111
+ }
112
+ export function resetPreCommandHooksCacheForTests() {
113
+ cachedConfig = null;
114
+ }
115
+ function resolveRuntimePreCommandRule(rawState) {
116
+ if (!rawState || typeof rawState !== 'object' || Array.isArray(rawState)) {
117
+ return null;
118
+ }
119
+ const record = rawState;
120
+ const scriptPath = readString(record.preCommandScriptPath ?? record.scriptPath);
121
+ if (!scriptPath) {
122
+ return null;
123
+ }
124
+ if (!isPreCommandScriptPathAllowed(scriptPath)) {
125
+ return null;
126
+ }
127
+ const timeoutMs = normalizeTimeoutMs(record.timeoutMs ?? record.timeout_ms ?? process.env.ROUTECODEX_PRE_COMMAND_TIMEOUT_MS);
128
+ return {
129
+ id: `runtime_precommand:${sanitizeHookId(path.basename(scriptPath) || 'script')}`,
130
+ toolNames: new Set(DEFAULT_TOOLS),
131
+ runtimeScriptPath: scriptPath,
132
+ timeoutMs,
133
+ priority: -1000,
134
+ order: -1
135
+ };
136
+ }
137
+ function resolvePreCommandHooksFilePath() {
138
+ const configured = process.env.ROUTECODEX_PRE_COMMAND_HOOKS_FILE ||
139
+ process.env.RCC_PRE_COMMAND_HOOKS_FILE ||
140
+ process.env.LLMSWITCH_PRE_COMMAND_HOOKS_FILE;
141
+ if (typeof configured === 'string' && configured.trim()) {
142
+ return path.resolve(configured.trim());
143
+ }
144
+ return DEFAULT_PRE_COMMAND_HOOKS_FILE;
145
+ }
146
+ function loadPreCommandHooksConfig() {
147
+ const filePath = resolvePreCommandHooksFilePath();
148
+ let stat;
149
+ try {
150
+ stat = fs.statSync(filePath);
151
+ }
152
+ catch {
153
+ cachedConfig = {
154
+ filePath,
155
+ mtimeMs: 0,
156
+ size: 0,
157
+ config: { enabled: false, hooks: [] }
158
+ };
159
+ return cachedConfig.config;
160
+ }
161
+ if (cachedConfig &&
162
+ cachedConfig.filePath === filePath &&
163
+ cachedConfig.mtimeMs === stat.mtimeMs &&
164
+ cachedConfig.size === stat.size) {
165
+ return cachedConfig.config;
166
+ }
167
+ try {
168
+ const content = fs.readFileSync(filePath, 'utf8');
169
+ const parsed = JSON.parse(content);
170
+ const config = normalizePreCommandHooksConfig(parsed);
171
+ cachedConfig = {
172
+ filePath,
173
+ mtimeMs: stat.mtimeMs,
174
+ size: stat.size,
175
+ config
176
+ };
177
+ return config;
178
+ }
179
+ catch {
180
+ cachedConfig = {
181
+ filePath,
182
+ mtimeMs: stat.mtimeMs,
183
+ size: stat.size,
184
+ config: { enabled: false, hooks: [] }
185
+ };
186
+ return cachedConfig.config;
187
+ }
188
+ }
189
+ function normalizePreCommandHooksConfig(raw) {
190
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
191
+ return { enabled: false, hooks: [] };
192
+ }
193
+ const record = raw;
194
+ const enabled = record.enabled !== false;
195
+ if (!enabled) {
196
+ return { enabled: false, hooks: [] };
197
+ }
198
+ const hooksRaw = Array.isArray(record.hooks) ? record.hooks : [];
199
+ const hooks = [];
200
+ for (let idx = 0; idx < hooksRaw.length; idx += 1) {
201
+ const normalized = normalizePreCommandHookRule(hooksRaw[idx], idx);
202
+ if (normalized) {
203
+ hooks.push(normalized);
204
+ }
205
+ }
206
+ hooks.sort((left, right) => {
207
+ if (left.priority !== right.priority) {
208
+ return left.priority - right.priority;
209
+ }
210
+ if (left.order !== right.order) {
211
+ return left.order - right.order;
212
+ }
213
+ return left.id.localeCompare(right.id);
214
+ });
215
+ return {
216
+ enabled: true,
217
+ hooks
218
+ };
219
+ }
220
+ function normalizePreCommandHookRule(raw, order) {
221
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
222
+ return null;
223
+ }
224
+ const record = raw;
225
+ if (record.enabled === false) {
226
+ return null;
227
+ }
228
+ const id = normalizeHookId(record.id, order);
229
+ const toolNames = normalizeToolSet(record.tool ?? record.tools);
230
+ const cmdRegex = parseRegex(record.cmdRegex ?? record.commandRegex ?? record.matchCommand);
231
+ const jqExpression = readString(record.jq ?? record.jqTransform ?? record.expression);
232
+ const shellCommand = readString(record.shell ?? record.command);
233
+ const hasAction = Boolean(jqExpression || shellCommand);
234
+ if (!hasAction) {
235
+ return null;
236
+ }
237
+ const timeoutMs = normalizeTimeoutMs(record.timeoutMs ?? record.timeout_ms);
238
+ const priority = normalizePriority(record.priority);
239
+ return {
240
+ id,
241
+ toolNames,
242
+ cmdRegex,
243
+ jqExpression,
244
+ shellCommand,
245
+ timeoutMs,
246
+ priority,
247
+ order
248
+ };
249
+ }
250
+ function sanitizeHookId(value) {
251
+ return value.replace(/[^a-zA-Z0-9_.-]+/g, '_');
252
+ }
253
+ function normalizeHookId(value, order) {
254
+ const text = readString(value);
255
+ if (!text) {
256
+ return `pre_command_hook_${order + 1}`;
257
+ }
258
+ return sanitizeHookId(text);
259
+ }
260
+ function normalizeToolName(value) {
261
+ return (value || '').trim().toLowerCase();
262
+ }
263
+ function normalizeToolSet(raw) {
264
+ const out = new Set();
265
+ const push = (value) => {
266
+ if (typeof value !== 'string') {
267
+ return;
268
+ }
269
+ const normalized = normalizeToolName(value);
270
+ if (normalized) {
271
+ out.add(normalized);
272
+ }
273
+ };
274
+ if (Array.isArray(raw)) {
275
+ for (const item of raw) {
276
+ push(item);
277
+ }
278
+ }
279
+ else {
280
+ push(raw);
281
+ }
282
+ if (out.size === 0) {
283
+ for (const tool of DEFAULT_TOOLS) {
284
+ out.add(tool);
285
+ }
286
+ }
287
+ return out;
288
+ }
289
+ function parseRegex(raw) {
290
+ if (typeof raw !== 'string' || !raw.trim()) {
291
+ return undefined;
292
+ }
293
+ const value = raw.trim();
294
+ const slashMatch = value.match(/^\/(.*)\/([a-z]*)$/i);
295
+ if (slashMatch) {
296
+ const pattern = slashMatch[1];
297
+ const flags = slashMatch[2] || 'i';
298
+ try {
299
+ return new RegExp(pattern, flags);
300
+ }
301
+ catch {
302
+ return undefined;
303
+ }
304
+ }
305
+ try {
306
+ return new RegExp(value, 'i');
307
+ }
308
+ catch {
309
+ return undefined;
310
+ }
311
+ }
312
+ function normalizeTimeoutMs(raw) {
313
+ const num = typeof raw === 'number' ? raw : typeof raw === 'string' ? Number.parseInt(raw, 10) : NaN;
314
+ if (!Number.isFinite(num) || num <= 0) {
315
+ return DEFAULT_TIMEOUT_MS;
316
+ }
317
+ return Math.floor(Math.min(num, 30_000));
318
+ }
319
+ function normalizePriority(raw) {
320
+ const num = typeof raw === 'number' ? raw : typeof raw === 'string' ? Number.parseInt(raw, 10) : NaN;
321
+ if (!Number.isFinite(num)) {
322
+ return 100;
323
+ }
324
+ return Math.floor(num);
325
+ }
326
+ function readString(raw) {
327
+ return typeof raw === 'string' ? raw.trim() : '';
328
+ }
329
+ function parseToolArgumentsObject(raw) {
330
+ if (typeof raw !== 'string' || !raw.trim()) {
331
+ return {};
332
+ }
333
+ try {
334
+ const parsed = JSON.parse(raw);
335
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
336
+ return null;
337
+ }
338
+ return parsed;
339
+ }
340
+ catch {
341
+ return null;
342
+ }
343
+ }
344
+ function extractCommandText(args, rawArgs) {
345
+ if (args && typeof args.cmd === 'string' && args.cmd.trim()) {
346
+ return args.cmd.trim();
347
+ }
348
+ if (args && typeof args.command === 'string' && args.command.trim()) {
349
+ return args.command.trim();
350
+ }
351
+ if (typeof rawArgs === 'string' && rawArgs.trim()) {
352
+ return rawArgs.trim();
353
+ }
354
+ return '';
355
+ }
356
+ function runJqTransform(expression, input, timeoutMs) {
357
+ const result = spawnSync('jq', ['-c', expression], {
358
+ input: JSON.stringify(input),
359
+ encoding: 'utf8',
360
+ timeout: timeoutMs,
361
+ maxBuffer: 1024 * 1024
362
+ });
363
+ if (result.error) {
364
+ const code = result.error.code;
365
+ if (code === 'ENOENT') {
366
+ throw new Error('jq_not_found');
367
+ }
368
+ throw new Error(`jq_error:${result.error.message}`);
369
+ }
370
+ if (typeof result.status === 'number' && result.status !== 0) {
371
+ const stderr = typeof result.stderr === 'string' ? result.stderr.trim() : '';
372
+ throw new Error(`jq_failed:${stderr || result.status}`);
373
+ }
374
+ const stdout = typeof result.stdout === 'string' ? result.stdout.trim() : '';
375
+ if (!stdout) {
376
+ throw new Error('jq_empty_output');
377
+ }
378
+ const lines = stdout
379
+ .split(/\r?\n/)
380
+ .map((line) => line.trim())
381
+ .filter((line) => line.length > 0);
382
+ const payload = lines.length > 0 ? lines[lines.length - 1] : stdout;
383
+ let parsed;
384
+ try {
385
+ parsed = JSON.parse(payload);
386
+ }
387
+ catch {
388
+ throw new Error('jq_invalid_json_output');
389
+ }
390
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
391
+ throw new Error('jq_non_object_output');
392
+ }
393
+ return parsed;
394
+ }
395
+ function runShellCommandHook(command, eventPayload, timeoutMs) {
396
+ const result = spawnSync(command, {
397
+ shell: true,
398
+ timeout: timeoutMs,
399
+ encoding: 'utf8',
400
+ input: JSON.stringify(eventPayload),
401
+ env: {
402
+ ...process.env,
403
+ ROUTECODEX_PRE_COMMAND_HOOK_EVENT: JSON.stringify(eventPayload)
404
+ }
405
+ });
406
+ if (result.error) {
407
+ throw new Error(`shell_hook_error:${result.error.message}`);
408
+ }
409
+ if (typeof result.status === 'number' && result.status !== 0) {
410
+ const stderr = typeof result.stderr === 'string' ? result.stderr.trim() : '';
411
+ throw new Error(`shell_hook_failed:${stderr || result.status}`);
412
+ }
413
+ }
414
+ function runRuntimeScriptHook(scriptPath, eventPayload, timeoutMs) {
415
+ const payloadText = JSON.stringify(eventPayload);
416
+ let result = spawnSync(scriptPath, [], {
417
+ timeout: timeoutMs,
418
+ encoding: 'utf8',
419
+ input: payloadText,
420
+ env: {
421
+ ...process.env,
422
+ ROUTECODEX_PRE_COMMAND_HOOK_EVENT: payloadText
423
+ }
424
+ });
425
+ if (result.error) {
426
+ const code = result.error.code;
427
+ if ((code === 'EACCES' || code === 'ENOEXEC') && process.platform !== 'win32') {
428
+ result = spawnSync('/bin/bash', [scriptPath], {
429
+ timeout: timeoutMs,
430
+ encoding: 'utf8',
431
+ input: payloadText,
432
+ env: {
433
+ ...process.env,
434
+ ROUTECODEX_PRE_COMMAND_HOOK_EVENT: payloadText
435
+ }
436
+ });
437
+ }
438
+ }
439
+ if (result.error) {
440
+ throw new Error(`runtime_precommand_error:${result.error.message}`);
441
+ }
442
+ if (typeof result.status === 'number' && result.status !== 0) {
443
+ const stderr = typeof result.stderr === 'string' ? result.stderr.trim() : '';
444
+ throw new Error(`runtime_precommand_failed:${stderr || result.status}`);
445
+ }
446
+ const stdout = typeof result.stdout === 'string' ? result.stdout.trim() : '';
447
+ if (!stdout) {
448
+ return undefined;
449
+ }
450
+ const lines = stdout
451
+ .split(/\r?\n/)
452
+ .map((line) => line.trim())
453
+ .filter((line) => line.length > 0);
454
+ const payload = lines.length > 0 ? lines[lines.length - 1] : stdout;
455
+ let parsed;
456
+ try {
457
+ parsed = JSON.parse(payload);
458
+ }
459
+ catch {
460
+ const fallback = payload.replace(/\\"/g, '"').trim();
461
+ const unwrapped = fallback.startsWith('"') && fallback.endsWith('"') && fallback.length > 1
462
+ ? fallback.slice(1, -1)
463
+ : fallback;
464
+ try {
465
+ parsed = JSON.parse(unwrapped);
466
+ }
467
+ catch {
468
+ throw new Error('runtime_precommand_invalid_json:' + payload.slice(0, 200));
469
+ }
470
+ }
471
+ if (typeof parsed === 'string' && parsed.trim()) {
472
+ return parsed;
473
+ }
474
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
475
+ return undefined;
476
+ }
477
+ const record = parsed;
478
+ if (typeof record.toolArguments === 'string' && record.toolArguments.trim()) {
479
+ return record.toolArguments;
480
+ }
481
+ if (Object.prototype.hasOwnProperty.call(record, 'arguments')) {
482
+ const argValue = record.arguments;
483
+ if (typeof argValue === 'string') {
484
+ return argValue;
485
+ }
486
+ if (argValue && typeof argValue === 'object' && !Array.isArray(argValue)) {
487
+ return JSON.stringify(argValue);
488
+ }
489
+ }
490
+ return JSON.stringify(record);
491
+ }
@@ -1,18 +1,35 @@
1
1
  import type { ServerToolHandler } from './types.js';
2
- /**
3
- * ServerToolRegistry:全局注册表,按 tool name 管理 ServerTool handler。
4
- * - key 统一使用小写名称(如 "web_search");
5
- * - 不关注具体协议(Chat / Responses),只负责分发到 handler。
6
- */
7
2
  type TriggerMode = 'tool_call' | 'auto';
8
- interface ServerToolHandlerEntry {
3
+ type AutoHookPhase = 'pre' | 'default' | 'post';
4
+ interface ServerToolAutoHookSpec {
5
+ id: string;
6
+ phase: AutoHookPhase;
7
+ priority: number;
8
+ order: number;
9
+ }
10
+ export interface ServerToolHandlerEntry {
9
11
  name: string;
10
12
  trigger: TriggerMode;
11
13
  handler: ServerToolHandler;
14
+ autoHook?: ServerToolAutoHookSpec;
15
+ }
16
+ export interface ServerToolAutoHookDescriptor {
17
+ id: string;
18
+ phase: AutoHookPhase;
19
+ priority: number;
20
+ order: number;
21
+ handler: ServerToolHandler;
12
22
  }
13
23
  export declare function registerServerToolHandler(name: string, handler: ServerToolHandler, options?: {
14
24
  trigger?: TriggerMode;
25
+ priority?: number;
26
+ phase?: AutoHookPhase | string;
27
+ hook?: {
28
+ priority?: number;
29
+ phase?: AutoHookPhase | string;
30
+ };
15
31
  }): void;
16
32
  export declare function getServerToolHandler(name: string): ServerToolHandlerEntry | undefined;
17
33
  export declare function listAutoServerToolHandlers(): ServerToolHandlerEntry[];
34
+ export declare function listAutoServerToolHooks(): ServerToolAutoHookDescriptor[];
18
35
  export {};