orquesta-cli 0.2.45 → 0.2.47
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.
- package/dist/agents/planner/index.js +2 -1
- package/dist/cli.js +17 -16
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/commands/clear.d.ts +3 -0
- package/dist/core/commands/clear.js +22 -0
- package/dist/core/commands/compact.d.ts +3 -0
- package/dist/core/commands/compact.js +45 -0
- package/dist/core/commands/help.d.ts +3 -0
- package/dist/core/commands/help.js +50 -0
- package/dist/core/commands/index.d.ts +3 -0
- package/dist/core/commands/index.js +11 -0
- package/dist/core/commands/memory.d.ts +3 -0
- package/dist/core/commands/memory.js +40 -0
- package/dist/core/commands/registry.d.ts +11 -0
- package/dist/core/commands/registry.js +25 -0
- package/dist/core/commands/types.d.ts +10 -0
- package/dist/core/commands/types.js +2 -0
- package/dist/core/event-bus.d.ts +20 -0
- package/dist/core/event-bus.js +35 -0
- package/dist/core/git-context.d.ts +11 -0
- package/dist/core/git-context.js +62 -0
- package/dist/core/ignore-filter.d.ts +4 -0
- package/dist/core/ignore-filter.js +50 -0
- package/dist/core/llm/llm-client.d.ts +1 -0
- package/dist/core/llm/llm-client.js +118 -40
- package/dist/core/onboarding.d.ts +3 -0
- package/dist/core/onboarding.js +48 -0
- package/dist/core/slash-command-handler.js +8 -135
- package/dist/eval/eval-runner.d.ts +1 -0
- package/dist/eval/eval-runner.js +22 -0
- package/dist/orchestration/plan-executor.js +77 -71
- package/dist/prompts/shared/tool-usage.js +0 -1
- package/dist/prompts/system/plan-execute.js +50 -57
- package/dist/tools/llm/simple/file-tools.js +12 -1
- package/dist/tools/llm/simple/final-response-tool.js +7 -11
- package/dist/tools/registry.js +63 -10
- package/dist/ui/components/PlanExecuteApp.d.ts +1 -0
- package/dist/ui/components/PlanExecuteApp.js +59 -22
- package/package.json +8 -4
|
@@ -108,6 +108,7 @@ export class LLMClient {
|
|
|
108
108
|
modelName;
|
|
109
109
|
currentAbortController = null;
|
|
110
110
|
isInterrupted = false;
|
|
111
|
+
onStreamingContent = null;
|
|
111
112
|
static DEFAULT_MAX_RETRIES = 3;
|
|
112
113
|
constructor() {
|
|
113
114
|
const endpoint = configManager.getCurrentEndpoint();
|
|
@@ -183,11 +184,11 @@ export class LLMClient {
|
|
|
183
184
|
const modelId = options.model || this.model;
|
|
184
185
|
const processedMessages = options.messages ?
|
|
185
186
|
this.preprocessMessages(options.messages, modelId) : [];
|
|
186
|
-
logger.vars({ name: 'modelId', value: modelId }, { name: 'originalMessages', value: options.messages?.length || 0 }, { name: 'processedMessages', value: processedMessages.length }, { name: 'temperature', value: options.temperature ?? 0
|
|
187
|
+
logger.vars({ name: 'modelId', value: modelId }, { name: 'originalMessages', value: options.messages?.length || 0 }, { name: 'processedMessages', value: processedMessages.length }, { name: 'temperature', value: options.temperature ?? 0 });
|
|
187
188
|
const requestBody = {
|
|
188
189
|
model: modelId,
|
|
189
190
|
messages: processedMessages,
|
|
190
|
-
temperature: options.temperature ?? 0
|
|
191
|
+
temperature: options.temperature ?? 0,
|
|
191
192
|
max_tokens: options.max_tokens,
|
|
192
193
|
stream: false,
|
|
193
194
|
...(options.tools && {
|
|
@@ -210,13 +211,112 @@ export class LLMClient {
|
|
|
210
211
|
}
|
|
211
212
|
logger.startTimer('llm-api-call');
|
|
212
213
|
this.currentAbortController = new AbortController();
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
214
|
+
let response;
|
|
215
|
+
if (this.onStreamingContent) {
|
|
216
|
+
const streamRequestBody = { ...requestBody, stream: true };
|
|
217
|
+
const streamResp = await this.axiosInstance.post(url, streamRequestBody, {
|
|
218
|
+
responseType: 'stream',
|
|
219
|
+
signal: this.currentAbortController.signal,
|
|
220
|
+
headers: buildPerRequestHeaders(),
|
|
221
|
+
});
|
|
222
|
+
captureBatutaHeaders(streamResp.headers);
|
|
223
|
+
const stream = streamResp.data;
|
|
224
|
+
let buffer = '';
|
|
225
|
+
let contentAccum = '';
|
|
226
|
+
let reasoningAccum = '';
|
|
227
|
+
let role = 'assistant';
|
|
228
|
+
let finishReason = null;
|
|
229
|
+
const toolCallsMap = new Map();
|
|
230
|
+
let responseId = '';
|
|
231
|
+
let responseModel = '';
|
|
232
|
+
for await (const chunk of stream) {
|
|
233
|
+
if (this.isInterrupted) {
|
|
234
|
+
throw new Error('INTERRUPTED');
|
|
235
|
+
}
|
|
236
|
+
buffer += chunk.toString();
|
|
237
|
+
const lines = buffer.split('\n');
|
|
238
|
+
buffer = lines.pop() || '';
|
|
239
|
+
for (const line of lines) {
|
|
240
|
+
const trimmed = line.trim();
|
|
241
|
+
if (!trimmed || trimmed === 'data: [DONE]')
|
|
242
|
+
continue;
|
|
243
|
+
if (!trimmed.startsWith('data: '))
|
|
244
|
+
continue;
|
|
245
|
+
try {
|
|
246
|
+
const data = JSON.parse(trimmed.slice(6));
|
|
247
|
+
if (data.id)
|
|
248
|
+
responseId = data.id;
|
|
249
|
+
if (data.model)
|
|
250
|
+
responseModel = data.model;
|
|
251
|
+
const choice = data.choices?.[0];
|
|
252
|
+
if (!choice)
|
|
253
|
+
continue;
|
|
254
|
+
if (choice.finish_reason)
|
|
255
|
+
finishReason = choice.finish_reason;
|
|
256
|
+
const delta = choice.delta;
|
|
257
|
+
if (!delta)
|
|
258
|
+
continue;
|
|
259
|
+
if (delta.role)
|
|
260
|
+
role = delta.role;
|
|
261
|
+
if (delta.content) {
|
|
262
|
+
contentAccum += delta.content;
|
|
263
|
+
this.onStreamingContent(delta.content);
|
|
264
|
+
}
|
|
265
|
+
if (delta.reasoning) {
|
|
266
|
+
reasoningAccum += delta.reasoning;
|
|
267
|
+
}
|
|
268
|
+
if (delta.tool_calls) {
|
|
269
|
+
for (const tc of delta.tool_calls) {
|
|
270
|
+
const idx = tc.index ?? 0;
|
|
271
|
+
if (!toolCallsMap.has(idx)) {
|
|
272
|
+
toolCallsMap.set(idx, { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } });
|
|
273
|
+
}
|
|
274
|
+
const existing = toolCallsMap.get(idx);
|
|
275
|
+
if (tc.id)
|
|
276
|
+
existing.id = tc.id;
|
|
277
|
+
if (tc.function?.name)
|
|
278
|
+
existing.function.name += tc.function.name;
|
|
279
|
+
if (tc.function?.arguments)
|
|
280
|
+
existing.function.arguments += tc.function.arguments;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch { }
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
this.currentAbortController = null;
|
|
288
|
+
const toolCalls = Array.from(toolCallsMap.values())
|
|
289
|
+
.filter(tc => tc.id && tc.function.name)
|
|
290
|
+
.map(tc => ({ id: tc.id, type: 'function', function: { name: tc.function.name, arguments: tc.function.arguments } }));
|
|
291
|
+
const reassembledMessage = {
|
|
292
|
+
role: role,
|
|
293
|
+
content: contentAccum,
|
|
294
|
+
...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
|
|
295
|
+
...(reasoningAccum ? { reasoning: reasoningAccum } : {}),
|
|
296
|
+
};
|
|
297
|
+
response = {
|
|
298
|
+
data: {
|
|
299
|
+
id: responseId,
|
|
300
|
+
object: 'chat.completion',
|
|
301
|
+
created: Math.floor(Date.now() / 1000),
|
|
302
|
+
model: responseModel || modelId,
|
|
303
|
+
choices: [{ index: 0, message: reassembledMessage, finish_reason: finishReason }],
|
|
304
|
+
},
|
|
305
|
+
status: streamResp.status,
|
|
306
|
+
statusText: streamResp.statusText,
|
|
307
|
+
headers: streamResp.headers,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
const httpResp = await this.axiosInstance.post(url, requestBody, {
|
|
312
|
+
signal: this.currentAbortController.signal,
|
|
313
|
+
headers: buildPerRequestHeaders(),
|
|
314
|
+
});
|
|
315
|
+
this.currentAbortController = null;
|
|
316
|
+
response = { data: httpResp.data, status: httpResp.status, statusText: httpResp.statusText, headers: httpResp.headers };
|
|
317
|
+
captureBatutaHeaders(response.headers);
|
|
318
|
+
}
|
|
218
319
|
const elapsed = logger.endTimer('llm-api-call');
|
|
219
|
-
captureBatutaHeaders(response.headers);
|
|
220
320
|
logger.flow('API response received');
|
|
221
321
|
if (!response.data.choices || !Array.isArray(response.data.choices)) {
|
|
222
322
|
logger.error('Invalid response structure - missing choices array', response.data);
|
|
@@ -354,7 +454,7 @@ export class LLMClient {
|
|
|
354
454
|
const requestBody = {
|
|
355
455
|
model: modelId,
|
|
356
456
|
messages: processedMessages,
|
|
357
|
-
temperature: options.temperature ?? 0
|
|
457
|
+
temperature: options.temperature ?? 0,
|
|
358
458
|
max_tokens: options.max_tokens,
|
|
359
459
|
stream: true,
|
|
360
460
|
...(options.tools && {
|
|
@@ -495,9 +595,7 @@ export class LLMClient {
|
|
|
495
595
|
const toolCallHistory = [];
|
|
496
596
|
let iterations = 0;
|
|
497
597
|
let contextLengthRecoveryAttempted = false;
|
|
498
|
-
let noToolCallRetries = 0;
|
|
499
598
|
let finalResponseFailures = 0;
|
|
500
|
-
const MAX_NO_TOOL_CALL_RETRIES = 3;
|
|
501
599
|
const MAX_FINAL_RESPONSE_FAILURES = 3;
|
|
502
600
|
const recentToolSignatures = [];
|
|
503
601
|
const recentNormalizedSignatures = [];
|
|
@@ -524,7 +622,7 @@ export class LLMClient {
|
|
|
524
622
|
response = await this.chatCompletion({
|
|
525
623
|
messages: workingMessages,
|
|
526
624
|
tools,
|
|
527
|
-
tool_choice: '
|
|
625
|
+
tool_choice: 'auto',
|
|
528
626
|
...(roleModel ? { model: roleModel } : {}),
|
|
529
627
|
});
|
|
530
628
|
}
|
|
@@ -733,34 +831,14 @@ export class LLMClient {
|
|
|
733
831
|
continue;
|
|
734
832
|
}
|
|
735
833
|
else {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
message: { role: 'assistant', content: fallbackContent },
|
|
745
|
-
toolCalls: toolCallHistory,
|
|
746
|
-
allMessages: workingMessages,
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
|
-
const hasMalformedToolCall = assistantMessage.content &&
|
|
750
|
-
(/<tool_call>/i.test(assistantMessage.content) ||
|
|
751
|
-
/<arg_key>/i.test(assistantMessage.content) ||
|
|
752
|
-
/<arg_value>/i.test(assistantMessage.content) ||
|
|
753
|
-
/<\/tool_call>/i.test(assistantMessage.content) ||
|
|
754
|
-
/bash<arg_key>/i.test(assistantMessage.content));
|
|
755
|
-
const retryMessage = hasMalformedToolCall
|
|
756
|
-
? 'Your previous response contained a malformed tool call (XML tags in content). You MUST use the proper tool_calls API format. Use final_response tool to deliver your message to the user.'
|
|
757
|
-
: 'You must use tools for all actions. Use final_response tool to deliver your final message to the user after completing all tasks.';
|
|
758
|
-
workingMessages.push({
|
|
759
|
-
role: 'user',
|
|
760
|
-
content: retryMessage,
|
|
761
|
-
});
|
|
762
|
-
logger.debug('Enforcing tool call - added retry message');
|
|
763
|
-
continue;
|
|
834
|
+
const finalContent = assistantMessage.content || 'Task completed.';
|
|
835
|
+
const { emitAssistantResponse } = await import('../../tools/llm/simple/file-tools.js');
|
|
836
|
+
emitAssistantResponse(finalContent);
|
|
837
|
+
return {
|
|
838
|
+
message: { role: 'assistant', content: finalContent },
|
|
839
|
+
toolCalls: toolCallHistory,
|
|
840
|
+
allMessages: workingMessages,
|
|
841
|
+
};
|
|
764
842
|
}
|
|
765
843
|
}
|
|
766
844
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { configManager } from './config/config-manager.js';
|
|
3
|
+
import { scanProviders, toEndpointConfig } from './config/auto-detect.js';
|
|
4
|
+
import { CONFIG_FILE_PATH } from '../constants.js';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
export function shouldShowOnboarding() {
|
|
7
|
+
if (!fs.existsSync(CONFIG_FILE_PATH))
|
|
8
|
+
return true;
|
|
9
|
+
return !configManager.hasEndpoints();
|
|
10
|
+
}
|
|
11
|
+
export async function runOnboarding() {
|
|
12
|
+
console.log();
|
|
13
|
+
console.log(chalk.cyan(' ╔══════════════════════════════════════════╗'));
|
|
14
|
+
console.log(chalk.cyan(' ║') + chalk.bold(' Welcome to Orquesta CLI! 🎵 ') + chalk.cyan('║'));
|
|
15
|
+
console.log(chalk.cyan(' ╚══════════════════════════════════════════╝'));
|
|
16
|
+
console.log();
|
|
17
|
+
console.log(chalk.dim(' Scanning for LLM providers...'));
|
|
18
|
+
console.log();
|
|
19
|
+
const result = await scanProviders();
|
|
20
|
+
if (result.detected.length === 0) {
|
|
21
|
+
console.log(chalk.yellow(' No LLM providers detected.'));
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(chalk.dim(' To get started, do one of the following:'));
|
|
24
|
+
console.log(chalk.dim(' • Set an env var: OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.'));
|
|
25
|
+
console.log(chalk.dim(' • Start a local provider (Ollama on port 11434)'));
|
|
26
|
+
console.log(chalk.dim(' • Run: orquesta --add-provider <provider-id>'));
|
|
27
|
+
console.log();
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
let addedCount = 0;
|
|
31
|
+
for (const detected of result.detected) {
|
|
32
|
+
const endpoint = toEndpointConfig(detected);
|
|
33
|
+
await configManager.addEndpoint(endpoint);
|
|
34
|
+
addedCount++;
|
|
35
|
+
if (addedCount === 1 && endpoint.models.length > 0) {
|
|
36
|
+
await configManager.setCurrentEndpoint(endpoint.id);
|
|
37
|
+
await configManager.setCurrentModel(endpoint.models[0].id);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
console.log(chalk.green(` ✓ Auto-configured ${addedCount} provider(s):`));
|
|
41
|
+
for (const d of result.detected) {
|
|
42
|
+
const modelCount = d.discoveredModels.length;
|
|
43
|
+
console.log(chalk.white(` • ${d.provider.name}`) + chalk.dim(` (${modelCount} model${modelCount !== 1 ? 's' : ''})`));
|
|
44
|
+
}
|
|
45
|
+
console.log();
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=onboarding.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { commandRegistry } from './commands/index.js';
|
|
1
2
|
import { sessionManager } from './session/session-manager.js';
|
|
2
3
|
import { usageTracker } from './usage-tracker.js';
|
|
3
4
|
import { logger } from '../utils/logger.js';
|
|
@@ -6,72 +7,24 @@ import { readHookConfig, writeHookFiles, disableHooks } from '../orquesta/hook-i
|
|
|
6
7
|
import { checkForCliUpdate, runCliUpdate, setSkippedVersion } from '../utils/update-checker.js';
|
|
7
8
|
import { createRequire } from 'module';
|
|
8
9
|
import { configManager } from './config/config-manager.js';
|
|
9
|
-
import { getForcedTier, setForcedTier
|
|
10
|
+
import { getForcedTier, setForcedTier } from './routing-state.js';
|
|
10
11
|
import { auditLog } from '../orchestration/audit-log.js';
|
|
11
12
|
import { remotePhone } from '../orquesta/remote-phone.js';
|
|
12
13
|
export async function executeSlashCommand(command, context) {
|
|
13
14
|
const trimmedCommand = command.trim();
|
|
14
15
|
logger.enter('executeSlashCommand', { command: trimmedCommand });
|
|
16
|
+
const commandName = trimmedCommand.split(/\s/)[0];
|
|
17
|
+
const registryResult = await commandRegistry.execute(commandName, context, trimmedCommand);
|
|
18
|
+
if (registryResult) {
|
|
19
|
+
logger.exit('executeSlashCommand', { handled: true, command: commandName, source: 'registry' });
|
|
20
|
+
return registryResult;
|
|
21
|
+
}
|
|
15
22
|
if (trimmedCommand === '/exit' || trimmedCommand === '/quit') {
|
|
16
23
|
logger.flow('Exit command received');
|
|
17
24
|
context.exit();
|
|
18
25
|
logger.exit('executeSlashCommand', { handled: true, command: 'exit' });
|
|
19
26
|
return { handled: true, shouldContinue: false };
|
|
20
27
|
}
|
|
21
|
-
if (trimmedCommand === '/clear') {
|
|
22
|
-
logger.flow('Clear command - resetting messages and todos');
|
|
23
|
-
context.setMessages([]);
|
|
24
|
-
context.setTodos([]);
|
|
25
|
-
resetBatutaSession();
|
|
26
|
-
logger.exit('executeSlashCommand', { handled: true, command: 'clear' });
|
|
27
|
-
return {
|
|
28
|
-
handled: true,
|
|
29
|
-
shouldContinue: false,
|
|
30
|
-
updatedContext: {
|
|
31
|
-
messages: [],
|
|
32
|
-
todos: [],
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
if (trimmedCommand === '/compact') {
|
|
37
|
-
logger.flow('Compact command received');
|
|
38
|
-
if (context.onCompact) {
|
|
39
|
-
logger.flow('Executing compact callback');
|
|
40
|
-
const result = await context.onCompact();
|
|
41
|
-
logger.vars({ name: 'compactSuccess', value: result.success }, { name: 'originalCount', value: result.originalMessageCount }, { name: 'newCount', value: result.newMessageCount });
|
|
42
|
-
const compactMessage = result.success
|
|
43
|
-
? `✅ Conversation compacted successfully. (${result.originalMessageCount} → ${result.newMessageCount} messages)`
|
|
44
|
-
: `❌ Compact failed: ${result.error}`;
|
|
45
|
-
const baseMessages = (result.success && result.compactedMessages)
|
|
46
|
-
? result.compactedMessages
|
|
47
|
-
: context.messages;
|
|
48
|
-
const updatedMessages = [
|
|
49
|
-
...baseMessages,
|
|
50
|
-
{ role: 'assistant', content: compactMessage },
|
|
51
|
-
];
|
|
52
|
-
context.setMessages(updatedMessages);
|
|
53
|
-
return {
|
|
54
|
-
handled: true,
|
|
55
|
-
shouldContinue: false,
|
|
56
|
-
updatedContext: {
|
|
57
|
-
messages: updatedMessages,
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
const fallbackMessage = '/compact is only available in interactive mode.';
|
|
62
|
-
const updatedMessages = [
|
|
63
|
-
...context.messages,
|
|
64
|
-
{ role: 'assistant', content: fallbackMessage },
|
|
65
|
-
];
|
|
66
|
-
context.setMessages(updatedMessages);
|
|
67
|
-
return {
|
|
68
|
-
handled: true,
|
|
69
|
-
shouldContinue: false,
|
|
70
|
-
updatedContext: {
|
|
71
|
-
messages: updatedMessages,
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
28
|
if (trimmedCommand === '/settings') {
|
|
76
29
|
if (context.onShowSettings) {
|
|
77
30
|
context.onShowSettings();
|
|
@@ -524,41 +477,6 @@ ${executorLines}
|
|
|
524
477
|
context.setMessages(updatedMessages);
|
|
525
478
|
return { handled: true, shouldContinue: false, updatedContext: { messages: updatedMessages } };
|
|
526
479
|
}
|
|
527
|
-
if (trimmedCommand.startsWith('/memory')) {
|
|
528
|
-
const sub = trimmedCommand.slice(7).trim();
|
|
529
|
-
const { addMemory, removeMemory, clearMemory, listMemory } = await import('./memory.js');
|
|
530
|
-
const reply = (content) => {
|
|
531
|
-
const updatedMessages = [...context.messages, { role: 'assistant', content }];
|
|
532
|
-
context.setMessages(updatedMessages);
|
|
533
|
-
return { handled: true, shouldContinue: false, updatedContext: { messages: updatedMessages } };
|
|
534
|
-
};
|
|
535
|
-
if (sub.startsWith('add ')) {
|
|
536
|
-
const note = sub.slice(4).trim();
|
|
537
|
-
if (!note)
|
|
538
|
-
return reply('Usage: /memory add <note>');
|
|
539
|
-
addMemory(note);
|
|
540
|
-
return reply(`✓ Saved to memory: "${note}"`);
|
|
541
|
-
}
|
|
542
|
-
if (sub === 'list' || sub === '') {
|
|
543
|
-
const entries = listMemory();
|
|
544
|
-
if (entries.length === 0)
|
|
545
|
-
return reply('Memory is empty. Use `/memory add <note>` to save preferences.');
|
|
546
|
-
const list = entries.map((e, i) => ` ${i + 1}. ${e}`).join('\n');
|
|
547
|
-
return reply(`📝 User memory (${entries.length} entries):\n${list}\n\nCommands: /memory add <note> | remove <n> | clear`);
|
|
548
|
-
}
|
|
549
|
-
if (sub.startsWith('remove ')) {
|
|
550
|
-
const idx = parseInt(sub.slice(7).trim(), 10);
|
|
551
|
-
if (isNaN(idx))
|
|
552
|
-
return reply('Usage: /memory remove <number>');
|
|
553
|
-
const ok = removeMemory(idx);
|
|
554
|
-
return reply(ok ? `✓ Removed entry #${idx}` : `Entry #${idx} not found`);
|
|
555
|
-
}
|
|
556
|
-
if (sub === 'clear') {
|
|
557
|
-
clearMemory();
|
|
558
|
-
return reply('✓ Memory cleared');
|
|
559
|
-
}
|
|
560
|
-
return reply('Usage: /memory add <note> | list | remove <n> | clear');
|
|
561
|
-
}
|
|
562
480
|
if (trimmedCommand === '/update') {
|
|
563
481
|
logger.flow('Update command received');
|
|
564
482
|
const reply = (content) => {
|
|
@@ -661,51 +579,6 @@ ${executorLines}
|
|
|
661
579
|
return reply(`❌ Could not open the remote phone channel: ${e.message}`);
|
|
662
580
|
}
|
|
663
581
|
}
|
|
664
|
-
if (trimmedCommand === '/help') {
|
|
665
|
-
const helpMessage = `
|
|
666
|
-
Available commands:
|
|
667
|
-
/exit, /quit - Exit the application
|
|
668
|
-
/clear - Clear conversation and TODOs
|
|
669
|
-
/compact - Compact conversation to free up context
|
|
670
|
-
/memory - Persistent memory: /memory add <note> | list | remove <n> | clear
|
|
671
|
-
/settings - Open settings menu
|
|
672
|
-
/model - Switch between LLM models
|
|
673
|
-
/project - Switch between Orquesta projects
|
|
674
|
-
/tool - Enable/disable optional tools (Browser, Background)
|
|
675
|
-
/load - Load a saved session
|
|
676
|
-
/usage - Show token usage statistics
|
|
677
|
-
/cost - Estimated USD spend this process (by model)
|
|
678
|
-
/route - Pin Batuta Auto tier (fast/balanced/premium/auto)
|
|
679
|
-
/sync - Bidirectional sync with Orquesta dashboard (pull & push LLM configs)
|
|
680
|
-
/login - Sign in to Orquesta via browser (opens getorquesta.com)
|
|
681
|
-
/logout - Sign out of Orquesta (clears token, keeps local LLM configs)
|
|
682
|
-
/whoami - Show current Orquesta connection
|
|
683
|
-
/hook - Claude Code hook here: /hook status | enable | disable
|
|
684
|
-
/remote-phone - Drive this session from your phone: on | off | status
|
|
685
|
-
/update - Update orquesta-cli to the latest version
|
|
686
|
-
|
|
687
|
-
Keyboard shortcuts:
|
|
688
|
-
Ctrl+C - Exit
|
|
689
|
-
Ctrl+T - Toggle TODO details
|
|
690
|
-
ESC - Interrupt current execution
|
|
691
|
-
@ - File browser
|
|
692
|
-
/ - Command autocomplete
|
|
693
|
-
|
|
694
|
-
Note: All conversations are automatically saved.
|
|
695
|
-
`;
|
|
696
|
-
const updatedMessages = [
|
|
697
|
-
...context.messages,
|
|
698
|
-
{ role: 'assistant', content: helpMessage },
|
|
699
|
-
];
|
|
700
|
-
context.setMessages(updatedMessages);
|
|
701
|
-
return {
|
|
702
|
-
handled: true,
|
|
703
|
-
shouldContinue: false,
|
|
704
|
-
updatedContext: {
|
|
705
|
-
messages: updatedMessages,
|
|
706
|
-
},
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
582
|
if (trimmedCommand.startsWith('/load')) {
|
|
710
583
|
logger.flow('Load command received');
|
|
711
584
|
const parts = trimmedCommand.split(' ');
|
package/dist/eval/eval-runner.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createLLMClient } from '../core/llm/llm-client.js';
|
|
2
2
|
import { configManager } from '../core/config/config-manager.js';
|
|
3
3
|
import { PlanExecutor } from '../orchestration/plan-executor.js';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
4
6
|
function emitEvent(event) {
|
|
5
7
|
console.log(JSON.stringify(event));
|
|
6
8
|
}
|
|
@@ -15,8 +17,11 @@ export class EvalRunner {
|
|
|
15
17
|
filesModified = new Set();
|
|
16
18
|
lastResponse = '';
|
|
17
19
|
todos = [];
|
|
20
|
+
followUpFile;
|
|
18
21
|
constructor() {
|
|
19
22
|
this.planExecutor = new PlanExecutor();
|
|
23
|
+
const tmpDir = process.env['ORQUESTA_IPC_DIR'] || path.join(process.env['HOME'] || '/tmp', '.orquesta-cli');
|
|
24
|
+
this.followUpFile = path.join(tmpDir, 'follow-up.msg');
|
|
20
25
|
}
|
|
21
26
|
async run(input) {
|
|
22
27
|
this.startTime = Date.now();
|
|
@@ -126,6 +131,23 @@ export class EvalRunner {
|
|
|
126
131
|
},
|
|
127
132
|
setAskUserRequest: () => {
|
|
128
133
|
},
|
|
134
|
+
getPendingMessage: () => {
|
|
135
|
+
try {
|
|
136
|
+
if (fs.existsSync(this.followUpFile)) {
|
|
137
|
+
const msg = fs.readFileSync(this.followUpFile, 'utf-8').trim();
|
|
138
|
+
if (msg)
|
|
139
|
+
return msg;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch { }
|
|
143
|
+
return null;
|
|
144
|
+
},
|
|
145
|
+
clearPendingMessage: () => {
|
|
146
|
+
try {
|
|
147
|
+
fs.unlinkSync(this.followUpFile);
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
150
|
+
},
|
|
129
151
|
};
|
|
130
152
|
}
|
|
131
153
|
emitTodo(action, todo) {
|