neuralos 3.2.2 → 3.2.5

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.
Files changed (2) hide show
  1. package/bin/gybackend.cjs +220 -3
  2. package/package.json +2 -2
package/bin/gybackend.cjs CHANGED
@@ -296885,6 +296885,13 @@ ${res.stderr}` : "")
296885
296885
  task2.endTime = Date.now();
296886
296886
  task2.exitCode = options?.exitCode;
296887
296887
  task2.endOffset = task2.startOffset + task2.output.length;
296888
+ if (task2.capturedOutput !== void 0 && task2.capturedOutput.length < (task2.output || "").length) {
296889
+ task2.captureStatus = "partial";
296890
+ } else if ((task2.output || "").length > MAX_BUFFER_SIZE) {
296891
+ task2.captureStatus = "display-truncated";
296892
+ } else {
296893
+ task2.captureStatus = "complete";
296894
+ }
296888
296895
  this.stopCommandTrackingWatcher(taskId);
296889
296896
  this.activeTaskByTerminal.delete(terminalId);
296890
296897
  this.pendingTaskFinishByTerminal.delete(terminalId);
@@ -296899,7 +296906,8 @@ ${res.stderr}` : "")
296899
296906
  callback({
296900
296907
  stdoutDelta: task2.output,
296901
296908
  exitCode: options?.exitCode,
296902
- history_command_match_id: taskId
296909
+ history_command_match_id: taskId,
296910
+ captureStatus: task2.captureStatus
296903
296911
  });
296904
296912
  }
296905
296913
  }
@@ -349593,10 +349601,11 @@ async function runCommand(args, context2, options) {
349593
349601
  });
349594
349602
  finalResult = `The command has been running for over 120s and has been switched to nowait mode (running in the background). You can use read_command_output to check its progress. history_command_match_id=${historyCommandMatchId}, terminalId=${bestMatch.id}`;
349595
349603
  } else {
349604
+ const captureNote = result.captureStatus === "partial" ? "\n[Note: Some output may have been lost during capture. Use read_command_output to retrieve the full output if needed.]" : result.captureStatus === "display-truncated" ? "\n[Note: Output was truncated for display. Use read_command_output to retrieve the full output if needed.]" : "";
349596
349605
  finalResult = `The command has finished executing. The following is the output (history_command_match_id=${historyCommandMatchId}):
349597
349606
  <terminal_content>
349598
349607
  ${truncatedOutput}
349599
- </terminal_content>`;
349608
+ </terminal_content>${captureNote}`;
349600
349609
  }
349601
349610
  context2.sendEvent(sessionId, {
349602
349611
  messageId,
@@ -367392,6 +367401,114 @@ var SINGLE_CALL_TOOL_BOUNDARY_NAMES = /* @__PURE__ */ new Set([
367392
367401
  "manage_template",
367393
367402
  "import_putty"
367394
367403
  ]);
367404
+ var PARALLEL_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
367405
+ "read_file",
367406
+ "read_terminal_tab",
367407
+ "read_command_output",
367408
+ "list_session_logs",
367409
+ "read_session_log",
367410
+ "search_session_logs",
367411
+ "get_metrics",
367412
+ "get_live_dashboard",
367413
+ "get_monitor_status",
367414
+ "get_cloud_inventory",
367415
+ "get_apm_summary",
367416
+ "get_dem_summary",
367417
+ "get_cost",
367418
+ "get_run_ledger",
367419
+ "list_gateway_methods",
367420
+ "manage_secret",
367421
+ // read operations (list/has) are safe; set/delete are rare
367422
+ "manage_oncall",
367423
+ // open_pages is read-only
367424
+ "manage_gitops",
367425
+ // export/drift/inSync are read-only
367426
+ "manage_playbook_version",
367427
+ // lint/history/diff are read-only
367428
+ "collect_facts",
367429
+ "run_fleet_command",
367430
+ // runs on different terminals — parallel-safe
367431
+ "synapse_health",
367432
+ "synapse_discover",
367433
+ "synapse_agents_summary",
367434
+ "synapse_reputation",
367435
+ "synapse_serve_status",
367436
+ "numbat_health",
367437
+ "numbat_findings_summary",
367438
+ "agentspan_health",
367439
+ "agentspan_list",
367440
+ "agentspan_status",
367441
+ "webintel_health",
367442
+ "web_search",
367443
+ "web_fetch",
367444
+ "web_find_similar",
367445
+ "web_watch_list",
367446
+ "patch_status",
367447
+ "list_requests",
367448
+ "request_status",
367449
+ "sop_search",
367450
+ "sop_get",
367451
+ "iam_user_info",
367452
+ "iam_user_groups",
367453
+ "iam_access_review",
367454
+ "fraudops_pipeline_status",
367455
+ "fraudops_str_status",
367456
+ "fraudops_decision_summary",
367457
+ "netdata_alert_summary",
367458
+ "netdata_correlate"
367459
+ ]);
367460
+ function canRunInParallel(toolCalls) {
367461
+ if (toolCalls.length <= 1) return false;
367462
+ for (const tc of toolCalls) {
367463
+ if (SINGLE_CALL_TOOL_BOUNDARY_NAMES.has(tc?.name)) return false;
367464
+ if (!PARALLEL_SAFE_TOOL_NAMES.has(tc?.name)) return false;
367465
+ }
367466
+ const terminalIds = /* @__PURE__ */ new Set();
367467
+ for (const tc of toolCalls) {
367468
+ const args = typeof tc?.args === "string" ? (() => {
367469
+ try {
367470
+ return JSON.parse(tc.args);
367471
+ } catch {
367472
+ return {};
367473
+ }
367474
+ })() : tc?.args || {};
367475
+ const tid = args.terminalId || args.target;
367476
+ if (tid) {
367477
+ if (terminalIds.has(tid)) return false;
367478
+ terminalIds.add(tid);
367479
+ }
367480
+ }
367481
+ return true;
367482
+ }
367483
+ function reconcileToolCalls(toolCalls) {
367484
+ if (!Array.isArray(toolCalls) || toolCalls.length === 0) return [];
367485
+ const seen = /* @__PURE__ */ new Set();
367486
+ const result = [];
367487
+ for (const tc of toolCalls) {
367488
+ if (!tc || typeof tc !== "object") continue;
367489
+ if (!tc.name || typeof tc.name !== "string") continue;
367490
+ let args = tc.args;
367491
+ if (args == null) {
367492
+ args = {};
367493
+ } else if (typeof args === "string") {
367494
+ try {
367495
+ args = JSON.parse(args);
367496
+ } catch {
367497
+ args = {};
367498
+ }
367499
+ }
367500
+ const id = tc.id || "";
367501
+ if (id) {
367502
+ if (seen.has(id)) continue;
367503
+ seen.add(id);
367504
+ }
367505
+ const dedupKey = `${tc.name}::${JSON.stringify(args)}`;
367506
+ if (seen.has(dedupKey)) continue;
367507
+ seen.add(dedupKey);
367508
+ result.push({ ...tc, args });
367509
+ }
367510
+ return result;
367511
+ }
367395
367512
  function clipTextMiddle(input, maxChars) {
367396
367513
  if (maxChars <= 0) return "";
367397
367514
  if (input.length <= maxChars) return input;
@@ -368358,7 +368475,9 @@ var AgentService_v2 = class {
368358
368475
  if (!AIMessage.isInstance(lastMessage)) {
368359
368476
  return { messages, sessionId, pendingToolCalls };
368360
368477
  }
368361
- const toolCalls = Array.isArray(lastMessage.tool_calls) ? lastMessage.tool_calls : [];
368478
+ const toolCalls = reconcileToolCalls(
368479
+ Array.isArray(lastMessage.tool_calls) ? lastMessage.tool_calls : []
368480
+ );
368362
368481
  if (!toolCalls || toolCalls.length === 0) {
368363
368482
  this.cleanupModelToolCallMetadata(lastMessage, []);
368364
368483
  return { messages, sessionId, pendingToolCalls };
@@ -368393,6 +368512,31 @@ var AgentService_v2 = class {
368393
368512
  if (!sessionId) throw new Error("No session ID in state");
368394
368513
  const sessionBinding = this.getSessionModelBinding(sessionId);
368395
368514
  const queue2 = Array.isArray(state.pendingToolCalls) ? state.pendingToolCalls : [];
368515
+ if (canRunInParallel(queue2)) {
368516
+ const parallelResults = await Promise.all(
368517
+ queue2.map(async (tc) => {
368518
+ const tm = this.createToolMessage(tc);
368519
+ const ec = this.createExecutionContext(
368520
+ sessionId,
368521
+ tm.additional_kwargs._gyshellMessageId,
368522
+ config2
368523
+ );
368524
+ let res = "";
368525
+ try {
368526
+ res = await this.executeToolByName(tc, ec);
368527
+ } catch (err) {
368528
+ res = `Parallel execution error for ${tc.name}: ${err.message}`;
368529
+ }
368530
+ tm.content = res;
368531
+ return tm;
368532
+ })
368533
+ );
368534
+ return {
368535
+ messages: [...state.messages, ...parallelResults],
368536
+ sessionId,
368537
+ pendingToolCalls: []
368538
+ };
368539
+ }
368396
368540
  const toolCall = queue2[0];
368397
368541
  if (!toolCall) return state;
368398
368542
  const toolMessage = this.createToolMessage(toolCall);
@@ -369245,6 +369389,71 @@ Actually, your intention might be different. Please re-read the description of t
369245
369389
  };
369246
369390
  });
369247
369391
  }
369392
+ /** v3.2.4: Execute a single tool by name (used by the parallel execution path).
369393
+ * This is a simplified dispatch that handles the common read-only/parallel-safe tools.
369394
+ * Tools not handled here fall back to the sequential switch/case path. */
369395
+ async executeToolByName(toolCall, executionContext) {
369396
+ const name = toolCall.name;
369397
+ const args = typeof toolCall.args === "string" ? (() => {
369398
+ try {
369399
+ return JSON.parse(toolCall.args);
369400
+ } catch {
369401
+ return {};
369402
+ }
369403
+ })() : toolCall.args || {};
369404
+ const ti = toolImplementations;
369405
+ switch (name) {
369406
+ case "read_terminal_tab":
369407
+ return ti.readTerminalTab(args, executionContext);
369408
+ case "read_command_output":
369409
+ return ti.readCommandOutput(args, executionContext);
369410
+ case "list_session_logs":
369411
+ return ti.listSessionLogs(args, executionContext);
369412
+ case "read_session_log":
369413
+ return ti.readSessionLog(args, executionContext);
369414
+ case "search_session_logs":
369415
+ return ti.searchSessionLogs(args, executionContext);
369416
+ case "get_metrics":
369417
+ return ti.getMetrics(args, executionContext);
369418
+ case "get_live_dashboard":
369419
+ return ti.getLiveDashboard(args, executionContext);
369420
+ case "get_monitor_status":
369421
+ return ti.getMonitorStatus(args, executionContext);
369422
+ case "get_cloud_inventory":
369423
+ return ti.getCloudInventory(args, executionContext);
369424
+ case "get_apm_summary":
369425
+ return ti.getApmSummary(args, executionContext);
369426
+ case "get_dem_summary":
369427
+ return ti.getDemSummary(args, executionContext);
369428
+ case "get_cost":
369429
+ return ti.getCost(args, executionContext);
369430
+ case "get_run_ledger":
369431
+ return ti.getRunLedger(args, executionContext);
369432
+ case "list_gateway_methods":
369433
+ return ti.listGatewayMethods(args, executionContext);
369434
+ case "collect_facts":
369435
+ return ti.collectFacts(args, executionContext);
369436
+ case "run_fleet_command":
369437
+ return ti.runFleetCommand(args, executionContext);
369438
+ case "manage_secret":
369439
+ return ti.manageSecret(args, executionContext);
369440
+ case "manage_oncall":
369441
+ return ti.manageOncall(args, executionContext);
369442
+ case "manage_gitops":
369443
+ return ti.manageGitops(args, executionContext);
369444
+ case "manage_playbook_version":
369445
+ return ti.managePlaybookVersion(args, executionContext);
369446
+ // Plugin tools — delegate to the pluginTools map
369447
+ default: {
369448
+ const pluginHandler = this.pluginTools.get(name);
369449
+ if (pluginHandler) {
369450
+ const result = await pluginHandler(args);
369451
+ return typeof result === "string" ? result : JSON.stringify(result);
369452
+ }
369453
+ return `Tool "${name}" is not supported in parallel execution mode.`;
369454
+ }
369455
+ }
369456
+ }
369248
369457
  createReadFileNode() {
369249
369458
  return RunnableLambda.from(async (state, config2) => {
369250
369459
  const sessionId = state.sessionId;
@@ -370593,12 +370802,20 @@ ${reminder}`;
370593
370802
  lastCheckpointOffset: 0,
370594
370803
  lastProfileMaxTokens: this.getEffectiveMaxTokensForSession(sessionId)
370595
370804
  };
370805
+ const previousFrontier = session.lastCheckpointOffset || 0;
370806
+ const newFrontier = messages.length;
370596
370807
  this.updateSessionFromMessages(
370597
370808
  session,
370598
370809
  messages,
370599
370810
  this.getEffectiveMaxTokensForSession(sessionId)
370600
370811
  );
370812
+ session.lastCheckpointOffset = newFrontier;
370601
370813
  this.chatHistoryService.saveSession(session);
370814
+ if (newFrontier !== previousFrontier) {
370815
+ console.log(
370816
+ `[AgentService_v2] Reading frontier updated: ${previousFrontier} -> ${newFrontier} messages (session ${sessionId}).`
370817
+ );
370818
+ }
370602
370819
  } catch (error40) {
370603
370820
  console.warn(
370604
370821
  "[AgentService_v2] Failed to save session from checkpoint:",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "neuralos",
3
- "version": "3.2.2",
4
- "description": "Headless AI-native backend for RTerm / neuralOS — v3.2.2: 5 edge case fixes (session-busy race, heavy output batching, stop-during-approval, stop debounce, write-retry limit). 11 plugins, 55 plugin tools wired. Transports (SSH/serial/local), SQLite, and NATS libs install automatically.",
3
+ "version": "3.2.5",
4
+ "description": "Headless AI-native backend for RTerm / neuralOS — v3.2.5: captureStatus (complete/partial/truncated), reconcileToolCalls (streamed multi-tool validation), reading frontier for emergency recovery. 11 plugins, 55 plugin tools wired. Transports (SSH/serial/local), SQLite, and NATS libs install automatically.",
5
5
  "main": "bin/gybackend.cjs",
6
6
  "bin": { "gybackend": "bin/gybackend.cjs" },
7
7
  "license": "MIT",