orquesta-cli 0.2.15 → 0.2.16
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.
|
@@ -6,6 +6,37 @@ import { LLMError, TokenLimitError, RateLimitError, ContextLengthError, } from '
|
|
|
6
6
|
import { logger, isLLMLogEnabled } from '../../utils/logger.js';
|
|
7
7
|
import { usageTracker } from '../usage-tracker.js';
|
|
8
8
|
import { getForcedTier, getBatutaSessionId, setLastBatutaRoute } from '../routing-state.js';
|
|
9
|
+
function coerceSyntheticToolCall(content) {
|
|
10
|
+
const trimmed = content.trim();
|
|
11
|
+
if (!trimmed || !trimmed.includes('"tool"'))
|
|
12
|
+
return null;
|
|
13
|
+
const cleaned = trimmed
|
|
14
|
+
.replace(/^```(?:json)?\s*\n?/i, '')
|
|
15
|
+
.replace(/\n?```\s*$/i, '')
|
|
16
|
+
.trim();
|
|
17
|
+
const start = cleaned.indexOf('{');
|
|
18
|
+
const end = cleaned.lastIndexOf('}');
|
|
19
|
+
if (start === -1 || end === -1 || end <= start)
|
|
20
|
+
return null;
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(cleaned.slice(start, end + 1));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
if (!parsed || typeof parsed !== 'object')
|
|
29
|
+
return null;
|
|
30
|
+
const obj = parsed;
|
|
31
|
+
if (typeof obj.tool !== 'string' || !obj.tool)
|
|
32
|
+
return null;
|
|
33
|
+
const args = obj.arguments && typeof obj.arguments === 'object' ? obj.arguments : {};
|
|
34
|
+
return {
|
|
35
|
+
id: `call_synth_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
36
|
+
type: 'function',
|
|
37
|
+
function: { name: obj.tool, arguments: JSON.stringify(args) },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
9
40
|
function buildPerRequestHeaders() {
|
|
10
41
|
const headers = {
|
|
11
42
|
'X-Batuta-Session-ID': getBatutaSessionId(),
|
|
@@ -500,6 +531,17 @@ export class LLMClient {
|
|
|
500
531
|
throw new Error('Cannot find choice in response.');
|
|
501
532
|
}
|
|
502
533
|
const assistantMessage = choice.message;
|
|
534
|
+
if ((!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) &&
|
|
535
|
+
typeof assistantMessage.content === 'string') {
|
|
536
|
+
const coerced = coerceSyntheticToolCall(assistantMessage.content);
|
|
537
|
+
if (coerced) {
|
|
538
|
+
assistantMessage.tool_calls = [coerced];
|
|
539
|
+
assistantMessage.content = '';
|
|
540
|
+
logger.flow('Recovered synthetic tool call from plain-text content', {
|
|
541
|
+
tool: coerced.function.name,
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
}
|
|
503
545
|
workingMessages.push(assistantMessage);
|
|
504
546
|
if (assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0) {
|
|
505
547
|
if (assistantMessage.tool_calls.length > 1) {
|
|
@@ -23,25 +23,26 @@ async function executeBash(command, cwd, timeout = 30000, explicitEnv) {
|
|
|
23
23
|
stderr += data.toString();
|
|
24
24
|
});
|
|
25
25
|
child.on('close', (code) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
resolve({
|
|
28
|
+
stdout: stdout.trim(),
|
|
29
|
+
stderr: stderr.trim(),
|
|
30
|
+
exitCode: code ?? (killed ? 124 : 0),
|
|
31
|
+
timedOut: killed,
|
|
32
|
+
});
|
|
33
33
|
});
|
|
34
34
|
child.on('error', (error) => {
|
|
35
|
+
clearTimeout(timer);
|
|
35
36
|
reject(error);
|
|
36
37
|
});
|
|
37
38
|
const timer = setTimeout(() => {
|
|
38
39
|
killed = true;
|
|
39
40
|
child.kill('SIGTERM');
|
|
40
|
-
|
|
41
|
+
setTimeout(() => { try {
|
|
42
|
+
child.kill('SIGKILL');
|
|
43
|
+
}
|
|
44
|
+
catch { } }, 2000).unref?.();
|
|
41
45
|
}, timeout);
|
|
42
|
-
child.on('close', () => {
|
|
43
|
-
clearTimeout(timer);
|
|
44
|
-
});
|
|
45
46
|
});
|
|
46
47
|
}
|
|
47
48
|
const BASH_TOOL_DEFINITION = {
|
|
@@ -149,6 +150,14 @@ export const bashTool = {
|
|
|
149
150
|
if (output.length > MAX_OUTPUT_LENGTH) {
|
|
150
151
|
output = output.slice(0, MAX_OUTPUT_LENGTH) + '\n\n... [output truncated]';
|
|
151
152
|
}
|
|
153
|
+
if (execResult.timedOut) {
|
|
154
|
+
const partial = output ? `\n\nPartial output before timeout:\n${output}` : ' (no output captured)';
|
|
155
|
+
logger.exit('bashTool.execute', { timedOut: true, outputLength: output.length });
|
|
156
|
+
return {
|
|
157
|
+
success: false,
|
|
158
|
+
error: `Command timed out after ${timeout}ms.${partial}`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
152
161
|
if (execResult.exitCode !== 0) {
|
|
153
162
|
output += `\n\n[Exit code: ${execResult.exitCode}]`;
|
|
154
163
|
}
|