omnikey-cli 1.0.19 → 1.0.21
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.
|
@@ -26,14 +26,16 @@ ${hasTaskInstructions
|
|
|
26
26
|
- If a request needs BOTH machine data AND web search: emit a \`<shell_script>\` first → wait for \`TERMINAL OUTPUT:\` → then call the web tool with concrete values. Never use placeholders like "my IP" in a web query.
|
|
27
27
|
|
|
28
28
|
**Incoming message tags:**
|
|
29
|
-
- \`TERMINAL OUTPUT:\` — stdout/stderr from a prior script.
|
|
29
|
+
- \`TERMINAL OUTPUT:\` — stdout/stderr from a prior script. Analyze it immediately and respond with EITHER a follow-up \`<shell_script>\` (if more data is needed) OR a \`<final_answer>\` (if you have enough to conclude). You MUST pick one — never respond with plain text.
|
|
30
30
|
- \`COMMAND ERROR:\` — script failed. Diagnose and emit a corrected \`<shell_script>\` or explain in \`<final_answer>\`.
|
|
31
31
|
- No prefix — direct user message; treat as the primary request.
|
|
32
32
|
|
|
33
33
|
**Response format — every response must be exactly one of:**
|
|
34
|
-
1. \`<shell_script>...</shell_script>\` — to run commands.
|
|
34
|
+
1. \`<shell_script>...</shell_script>\` — to run commands and gather more data.
|
|
35
35
|
2. A \`web_search\` or \`web_fetch\` tool call — to fetch web context (use native tool calling, not XML tags).
|
|
36
|
-
3. \`<final_answer>...</final_answer>\` —
|
|
36
|
+
3. \`<final_answer>...</final_answer>\` — your conclusion once you have enough information.
|
|
37
|
+
|
|
38
|
+
**Critical rule:** After receiving \`TERMINAL OUTPUT:\` you MUST immediately produce either \`<shell_script>\` or \`<final_answer>\`. Never output raw text, markdown, or any other format. If the terminal output contains enough information to answer the user's request, output \`<final_answer>\` right away.
|
|
37
39
|
|
|
38
40
|
No plain text, reasoning, or other tags outside these blocks. Never wrap in additional XML/JSON.
|
|
39
41
|
|
|
@@ -112,7 +112,7 @@ async function runToolLoop(initialResult, session, sessionId, send, log, tools,
|
|
|
112
112
|
session.history.push(result.assistantMessage);
|
|
113
113
|
session.history.push({
|
|
114
114
|
role: 'user',
|
|
115
|
-
content: 'You have reached the maximum number of tool calls.
|
|
115
|
+
content: 'You have reached the maximum number of tool calls. Do NOT make any further tool calls or web searches. You MUST now provide a final answer directly. If you still need to gather information from the system, generate a `<shell_scripts>` block instead of making tool calls.',
|
|
116
116
|
});
|
|
117
117
|
result = await ai_client_1.aiClient.complete(aiModel, session.history, {
|
|
118
118
|
tools: undefined,
|
|
@@ -254,7 +254,12 @@ async function authenticateFromAuthHeader(authHeader, log) {
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
function createUserContent(content, hasStoredPrompt) {
|
|
257
|
-
return hasStoredPrompt
|
|
257
|
+
return hasStoredPrompt
|
|
258
|
+
? content
|
|
259
|
+
.toLowerCase()
|
|
260
|
+
.replace(/@omniagent/g, '')
|
|
261
|
+
.trim()
|
|
262
|
+
: content;
|
|
258
263
|
}
|
|
259
264
|
async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
260
265
|
const { sessionState: session, hasStoredPrompt } = await getOrCreateSession(sessionId, subscription, clientMessage.platform, log);
|
|
@@ -290,9 +295,14 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
|
290
295
|
isError: isErrorFlag,
|
|
291
296
|
rawContentLength: (clientMessage.content || '').length,
|
|
292
297
|
userContentLength: userContent.length,
|
|
298
|
+
isRecursiveCall: clientMessage.is_web_call,
|
|
293
299
|
});
|
|
294
300
|
const isAssistance = isTerminalOutput || isErrorFlag;
|
|
295
301
|
if (!clientMessage?.is_web_call) {
|
|
302
|
+
// Terminal output and command errors are always user-role messages — they
|
|
303
|
+
// represent environment feedback that the agent must reason about next.
|
|
304
|
+
// Pushing them as 'assistant' would create two consecutive assistant turns
|
|
305
|
+
// which breaks most LLM APIs and prevents the model from processing the output.
|
|
296
306
|
session.history.push({
|
|
297
307
|
role: 'user',
|
|
298
308
|
content: isAssistance
|
|
@@ -306,7 +316,7 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
|
306
316
|
const tools = isFinalTurn ? undefined : buildAvailableTools();
|
|
307
317
|
const recordUsage = async (result) => {
|
|
308
318
|
const usage = result.usage;
|
|
309
|
-
if (!usage || !subscription.id)
|
|
319
|
+
if (!usage || !subscription.id || config_1.config.isSelfHosted)
|
|
310
320
|
return;
|
|
311
321
|
try {
|
|
312
322
|
await subscriptionUsage_1.SubscriptionUsage.create({
|
|
@@ -359,12 +369,58 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
|
359
369
|
subscriptionId: subscription.id,
|
|
360
370
|
turn: session.turns,
|
|
361
371
|
});
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
372
|
+
const toolLoopResult = await runToolLoop(result, session, sessionId, send, log, buildAvailableTools(), recordUsage);
|
|
373
|
+
const toolLoopContent = toolLoopResult.content.trim();
|
|
374
|
+
const toolLoopHasShell = toolLoopContent.includes('<shell_script>');
|
|
375
|
+
const toolLoopHasFinal = toolLoopContent.includes('<final_answer>');
|
|
376
|
+
const webToolFailed = session.history.some((msg) => msg.role === 'tool' && typeof msg.content === 'string' && msg.content.startsWith('Error'));
|
|
377
|
+
if (toolLoopHasShell || (toolLoopHasFinal && !webToolFailed)) {
|
|
378
|
+
// The tool loop already produced a shell script — use it directly.
|
|
379
|
+
// This avoids a redundant AI call and handles the case where the model
|
|
380
|
+
// emits a <shell_script> immediately after its web tool calls.
|
|
381
|
+
log.info('Tool loop produced shell script; processing inline', { sessionId });
|
|
382
|
+
content = toolLoopContent;
|
|
383
|
+
result = toolLoopResult;
|
|
384
|
+
// Fall through to the <shell_script> handling below.
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
// The tool loop returned either plain text or a <final_answer>.
|
|
388
|
+
// We always make one more AI turn here so the model has a chance to
|
|
389
|
+
// correct itself — specifically when web tools failed (404 / error) the
|
|
390
|
+
// model tends to wrap a "please run this manually" message in
|
|
391
|
+
// <final_answer>. The directive below tells it to use <shell_script> as
|
|
392
|
+
// a fallback instead of asking the user to run commands.
|
|
393
|
+
if (toolLoopResult.assistantMessage) {
|
|
394
|
+
session.history.push(toolLoopResult.assistantMessage);
|
|
395
|
+
}
|
|
396
|
+
session.history.push({
|
|
397
|
+
role: 'user',
|
|
398
|
+
content: webToolFailed
|
|
399
|
+
? [
|
|
400
|
+
'IMPORTANT: The web search tool failed and is unavailable. Do NOT attempt any further web calls or ask the user to run commands manually.',
|
|
401
|
+
'You MUST retrieve any needed data by generating a <shell_script> that runs terminal commands (curl, grep, cat, etc.).',
|
|
402
|
+
'The shell script output will be returned to you automatically.',
|
|
403
|
+
'',
|
|
404
|
+
'Respond with exactly one of:',
|
|
405
|
+
'- <shell_script>...</shell_script> — to fetch or retrieve data via terminal commands',
|
|
406
|
+
'- <final_answer>...</final_answer> — only if you already have enough information',
|
|
407
|
+
'No plain text. No web tool calls. No other format.',
|
|
408
|
+
].join('\n')
|
|
409
|
+
: [
|
|
410
|
+
'Web research is complete. The results are in the conversation above.',
|
|
411
|
+
'',
|
|
412
|
+
'Now respond with exactly one of:',
|
|
413
|
+
'- <shell_script>...</shell_script> — to run terminal commands (output will be returned to you automatically)',
|
|
414
|
+
'- <final_answer>...</final_answer> — only if you genuinely have enough information',
|
|
415
|
+
'No plain text. No other format.',
|
|
416
|
+
].join('\n'),
|
|
417
|
+
});
|
|
418
|
+
await runAgentTurn(sessionId, subscription, {
|
|
419
|
+
sender: 'agent',
|
|
420
|
+
session_id: sessionId,
|
|
421
|
+
content: '',
|
|
422
|
+
is_web_call: true,
|
|
423
|
+
}, send, logger_1.logger);
|
|
368
424
|
return;
|
|
369
425
|
}
|
|
370
426
|
}
|
|
@@ -410,6 +466,31 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
|
410
466
|
});
|
|
411
467
|
sessionMessages.delete(sessionId);
|
|
412
468
|
}
|
|
469
|
+
else if (content) {
|
|
470
|
+
// Fallback: the LLM returned content without any recognized tag and it
|
|
471
|
+
// is not the final turn (e.g. plain-text conclusion after terminal
|
|
472
|
+
// output). Treat it as a final answer so the client is never left
|
|
473
|
+
// hanging.
|
|
474
|
+
log.info('Agent returned untagged content on a non-final turn; treating as final answer', {
|
|
475
|
+
sessionId,
|
|
476
|
+
subscriptionId: subscription.id,
|
|
477
|
+
turn: session.turns,
|
|
478
|
+
});
|
|
479
|
+
session.history.push({ role: 'assistant', content });
|
|
480
|
+
send({
|
|
481
|
+
session_id: sessionId,
|
|
482
|
+
sender: 'agent',
|
|
483
|
+
content: `<final_answer>\n${content}\n</final_answer>`,
|
|
484
|
+
});
|
|
485
|
+
sessionMessages.delete(sessionId);
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
log.warn('Agent returned empty content with no recognized tags; sending error', {
|
|
489
|
+
sessionId,
|
|
490
|
+
});
|
|
491
|
+
sendFinalAnswer(send, sessionId, 'The agent returned an empty response. Please try again.', true);
|
|
492
|
+
sessionMessages.delete(sessionId);
|
|
493
|
+
}
|
|
413
494
|
}
|
|
414
495
|
catch (err) {
|
|
415
496
|
log.error('Agent LLM call failed', { error: err });
|
|
@@ -118,7 +118,7 @@ async function enhanceText(logger, text, cmd, subscription) {
|
|
|
118
118
|
const { rawResponse, usage, model } = result;
|
|
119
119
|
// Record token usage for this subscription and model, if usage
|
|
120
120
|
// data is available and we know which subscription made the call.
|
|
121
|
-
if (usage && subscription.id) {
|
|
121
|
+
if (usage && subscription.id && !config_1.config.isSelfHosted) {
|
|
122
122
|
try {
|
|
123
123
|
await subscriptionUsage_1.SubscriptionUsage.create({
|
|
124
124
|
subscriptionId: subscription.id,
|
|
@@ -197,7 +197,7 @@ async function streamEnhanceResponse(res, text, cmd) {
|
|
|
197
197
|
return;
|
|
198
198
|
}
|
|
199
199
|
const { usage, model } = result;
|
|
200
|
-
if (usage && subscription.id) {
|
|
200
|
+
if (usage && subscription.id && !config_1.config.isSelfHosted) {
|
|
201
201
|
try {
|
|
202
202
|
await subscriptionUsage_1.SubscriptionUsage.create({
|
|
203
203
|
subscriptionId: subscription.id,
|
package/backend-dist/index.js
CHANGED
|
@@ -64,8 +64,8 @@ app.get('/macos/appcast', (req, res) => {
|
|
|
64
64
|
const appcastUrl = `${baseUrl}/macos/appcast`;
|
|
65
65
|
// These should match the values embedded into the macOS app
|
|
66
66
|
// Info.plist in macOS/build_release_dmg.sh.
|
|
67
|
-
const bundleVersion = '
|
|
68
|
-
const shortVersion = '1.0.
|
|
67
|
+
const bundleVersion = '17';
|
|
68
|
+
const shortVersion = '1.0.16';
|
|
69
69
|
const xml = `<?xml version="1.0" encoding="utf-8"?>
|
|
70
70
|
<rss version="2.0"
|
|
71
71
|
xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"registry": "https://registry.npmjs.org/"
|
|
6
6
|
},
|
|
7
|
-
"version": "1.0.
|
|
7
|
+
"version": "1.0.21",
|
|
8
8
|
"description": "CLI for onboarding users to Omnikey AI and configuring OPENAI_API_KEY. Use Yarn for install/build.",
|
|
9
9
|
"engines": {
|
|
10
10
|
"node": ">=14.0.0",
|