omnikey-cli 1.0.18 → 1.0.20
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
|
|
|
@@ -105,9 +105,27 @@ async function runToolLoop(initialResult, session, sessionId, send, log, tools,
|
|
|
105
105
|
});
|
|
106
106
|
await onUsage(result);
|
|
107
107
|
}
|
|
108
|
+
// If we exhausted the iteration cap and the model still wants to call tools,
|
|
109
|
+
// force a final text response by calling again without tools.
|
|
110
|
+
if (result.finish_reason === 'tool_calls') {
|
|
111
|
+
log.warn('Tool loop hit MAX_TOOL_ITERATIONS; forcing final conclusion', { sessionId });
|
|
112
|
+
session.history.push(result.assistantMessage);
|
|
113
|
+
session.history.push({
|
|
114
|
+
role: 'user',
|
|
115
|
+
content: 'You have reached the maximum number of tool calls. Based on all the information gathered so far, provide a single, final, concise answer. Do not call any more tools.',
|
|
116
|
+
});
|
|
117
|
+
result = await ai_client_1.aiClient.complete(aiModel, session.history, {
|
|
118
|
+
tools: undefined,
|
|
119
|
+
temperature: 0.2,
|
|
120
|
+
});
|
|
121
|
+
await onUsage(result);
|
|
122
|
+
}
|
|
108
123
|
log.info('Finished reasoning and tool calls: ', {
|
|
109
124
|
reason: result.finish_reason,
|
|
110
125
|
});
|
|
126
|
+
if (result.assistantMessage) {
|
|
127
|
+
session.history.push(result.assistantMessage);
|
|
128
|
+
}
|
|
111
129
|
return result;
|
|
112
130
|
}
|
|
113
131
|
function buildAvailableTools() {
|
|
@@ -239,7 +257,12 @@ async function authenticateFromAuthHeader(authHeader, log) {
|
|
|
239
257
|
}
|
|
240
258
|
}
|
|
241
259
|
function createUserContent(content, hasStoredPrompt) {
|
|
242
|
-
return hasStoredPrompt
|
|
260
|
+
return hasStoredPrompt
|
|
261
|
+
? content
|
|
262
|
+
.toLowerCase()
|
|
263
|
+
.replace(/@omniagent/g, '')
|
|
264
|
+
.trim()
|
|
265
|
+
: content;
|
|
243
266
|
}
|
|
244
267
|
async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
245
268
|
const { sessionState: session, hasStoredPrompt } = await getOrCreateSession(sessionId, subscription, clientMessage.platform, log);
|
|
@@ -278,6 +301,10 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
|
278
301
|
});
|
|
279
302
|
const isAssistance = isTerminalOutput || isErrorFlag;
|
|
280
303
|
if (!clientMessage?.is_web_call) {
|
|
304
|
+
// Terminal output and command errors are always user-role messages — they
|
|
305
|
+
// represent environment feedback that the agent must reason about next.
|
|
306
|
+
// Pushing them as 'assistant' would create two consecutive assistant turns
|
|
307
|
+
// which breaks most LLM APIs and prevents the model from processing the output.
|
|
281
308
|
session.history.push({
|
|
282
309
|
role: 'user',
|
|
283
310
|
content: isAssistance
|
|
@@ -291,7 +318,7 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
|
291
318
|
const tools = isFinalTurn ? undefined : buildAvailableTools();
|
|
292
319
|
const recordUsage = async (result) => {
|
|
293
320
|
const usage = result.usage;
|
|
294
|
-
if (!usage || !subscription.id)
|
|
321
|
+
if (!usage || !subscription.id || config_1.config.isSelfHosted)
|
|
295
322
|
return;
|
|
296
323
|
try {
|
|
297
324
|
await subscriptionUsage_1.SubscriptionUsage.create({
|
|
@@ -352,6 +379,13 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
|
352
379
|
sessionMessages.delete(sessionId);
|
|
353
380
|
return;
|
|
354
381
|
}
|
|
382
|
+
await runAgentTurn(sessionId, subscription, {
|
|
383
|
+
sender: 'agent',
|
|
384
|
+
session_id: sessionId,
|
|
385
|
+
content: '',
|
|
386
|
+
is_web_call: true,
|
|
387
|
+
}, send, logger_1.logger);
|
|
388
|
+
return;
|
|
355
389
|
}
|
|
356
390
|
// Ensure that a proper <final_answer> block is produced for the
|
|
357
391
|
// desktop clients once we reach the final turn. If the model did
|
|
@@ -395,6 +429,31 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
|
|
|
395
429
|
});
|
|
396
430
|
sessionMessages.delete(sessionId);
|
|
397
431
|
}
|
|
432
|
+
else if (content) {
|
|
433
|
+
// Fallback: the LLM returned content without any recognized tag and it
|
|
434
|
+
// is not the final turn (e.g. plain-text conclusion after terminal
|
|
435
|
+
// output). Treat it as a final answer so the client is never left
|
|
436
|
+
// hanging.
|
|
437
|
+
log.info('Agent returned untagged content on a non-final turn; treating as final answer', {
|
|
438
|
+
sessionId,
|
|
439
|
+
subscriptionId: subscription.id,
|
|
440
|
+
turn: session.turns,
|
|
441
|
+
});
|
|
442
|
+
session.history.push({ role: 'assistant', content });
|
|
443
|
+
send({
|
|
444
|
+
session_id: sessionId,
|
|
445
|
+
sender: 'agent',
|
|
446
|
+
content: `<final_answer>\n${content}\n</final_answer>`,
|
|
447
|
+
});
|
|
448
|
+
sessionMessages.delete(sessionId);
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
log.warn('Agent returned empty content with no recognized tags; sending error', {
|
|
452
|
+
sessionId,
|
|
453
|
+
});
|
|
454
|
+
sendFinalAnswer(send, sessionId, 'The agent returned an empty response. Please try again.', true);
|
|
455
|
+
sessionMessages.delete(sessionId);
|
|
456
|
+
}
|
|
398
457
|
}
|
|
399
458
|
catch (err) {
|
|
400
459
|
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 = '16';
|
|
68
|
+
const shortVersion = '1.0.15';
|
|
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"
|
|
@@ -93,7 +93,7 @@ app.get('/macos/appcast', (req, res) => {
|
|
|
93
93
|
// ── Windows distribution endpoints ───────────────────────────────────────────
|
|
94
94
|
// These should match the values in windows/OmniKey.Windows.csproj
|
|
95
95
|
// <Version> and windows/build_release_zip.ps1 $APP_VERSION.
|
|
96
|
-
const WIN_VERSION = '1.
|
|
96
|
+
const WIN_VERSION = '1.3';
|
|
97
97
|
const WIN_ZIP_FILENAME = 'OmniKeyAI-windows-win-x64.zip';
|
|
98
98
|
const WIN_ZIP_PATH = path_1.default.join(process.cwd(), 'windows', WIN_ZIP_FILENAME);
|
|
99
99
|
// Serves the pre-built ZIP produced by windows/build_release_zip.ps1.
|
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.20",
|
|
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",
|