omnikey-cli 1.0.19 → 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. Use it to continue reasoning or emit a follow-up.
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>\` — when done.
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
 
@@ -123,6 +123,9 @@ async function runToolLoop(initialResult, session, sessionId, send, log, tools,
123
123
  log.info('Finished reasoning and tool calls: ', {
124
124
  reason: result.finish_reason,
125
125
  });
126
+ if (result.assistantMessage) {
127
+ session.history.push(result.assistantMessage);
128
+ }
126
129
  return result;
127
130
  }
128
131
  function buildAvailableTools() {
@@ -254,7 +257,12 @@ async function authenticateFromAuthHeader(authHeader, log) {
254
257
  }
255
258
  }
256
259
  function createUserContent(content, hasStoredPrompt) {
257
- return hasStoredPrompt ? content.replace(/@omniAgent/g, '').trim() : content;
260
+ return hasStoredPrompt
261
+ ? content
262
+ .toLowerCase()
263
+ .replace(/@omniagent/g, '')
264
+ .trim()
265
+ : content;
258
266
  }
259
267
  async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
260
268
  const { sessionState: session, hasStoredPrompt } = await getOrCreateSession(sessionId, subscription, clientMessage.platform, log);
@@ -293,6 +301,10 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
293
301
  });
294
302
  const isAssistance = isTerminalOutput || isErrorFlag;
295
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.
296
308
  session.history.push({
297
309
  role: 'user',
298
310
  content: isAssistance
@@ -306,7 +318,7 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
306
318
  const tools = isFinalTurn ? undefined : buildAvailableTools();
307
319
  const recordUsage = async (result) => {
308
320
  const usage = result.usage;
309
- if (!usage || !subscription.id)
321
+ if (!usage || !subscription.id || config_1.config.isSelfHosted)
310
322
  return;
311
323
  try {
312
324
  await subscriptionUsage_1.SubscriptionUsage.create({
@@ -367,6 +379,13 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
367
379
  sessionMessages.delete(sessionId);
368
380
  return;
369
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;
370
389
  }
371
390
  // Ensure that a proper <final_answer> block is produced for the
372
391
  // desktop clients once we reach the final turn. If the model did
@@ -410,6 +429,31 @@ async function runAgentTurn(sessionId, subscription, clientMessage, send, log) {
410
429
  });
411
430
  sessionMessages.delete(sessionId);
412
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
+ }
413
457
  }
414
458
  catch (err) {
415
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/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "registry": "https://registry.npmjs.org/"
6
6
  },
7
- "version": "1.0.19",
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",