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
- if (!killed) {
27
- resolve({
28
- stdout: stdout.trim(),
29
- stderr: stderr.trim(),
30
- exitCode: code ?? 0,
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
- reject(new Error(`Command timed out after ${timeout}ms`));
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-cli",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
4
4
  "description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",