rterm-backend 3.2.1 → 3.2.4

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 +214 -15
  2. package/package.json +2 -2
package/bin/gybackend.cjs CHANGED
@@ -295047,7 +295047,7 @@ var normalizeTerminalResizeTarget = (cols, rows) => {
295047
295047
  rows: Math.max(1, Math.floor(rows))
295048
295048
  };
295049
295049
  };
295050
- var TerminalService = class {
295050
+ var TerminalService = class _TerminalService {
295051
295051
  backends = /* @__PURE__ */ new Map();
295052
295052
  terminals = /* @__PURE__ */ new Map();
295053
295053
  terminalConfigs = /* @__PURE__ */ new Map();
@@ -295058,6 +295058,8 @@ var TerminalService = class {
295058
295058
  /** Buffered writes for terminals that aren't writable yet (prevents keystroke loss during reconnection). */
295059
295059
  pendingWrites = /* @__PURE__ */ new Map();
295060
295060
  pendingWriteTimers = /* @__PURE__ */ new Map();
295061
+ pendingWriteRetries = /* @__PURE__ */ new Map();
295062
+ static MAX_WRITE_RETRIES = 3;
295061
295063
  selectionByTerminal = /* @__PURE__ */ new Map();
295062
295064
  tasksByTerminal = /* @__PURE__ */ new Map();
295063
295065
  activeTaskByTerminal = /* @__PURE__ */ new Map();
@@ -295619,21 +295621,28 @@ var TerminalService = class {
295619
295621
  handleData(terminalId, data) {
295620
295622
  const sanitizedData = stripInternalControlMarkers(data);
295621
295623
  const tab = this.terminals.get(terminalId);
295622
- const recordingId = this.activeRecordings.get(terminalId);
295623
- if (recordingId && this.sessionRecorder && sanitizedData) {
295624
- try {
295625
- this.sessionRecorder.out(recordingId, sanitizedData);
295626
- } catch {
295627
- }
295628
- }
295629
- if (this.sessionLogger && tab) {
295630
- if (!this.sessionLogStarted.has(terminalId)) {
295631
- this.sessionLogStarted.add(terminalId);
295632
- const cfg = this.terminalConfigs.get(terminalId);
295633
- this.sessionLogger.start(terminalId, { title: tab.title || terminalId, type: tab.type });
295634
- void cfg;
295624
+ if (sanitizedData) {
295625
+ const recordingId = this.activeRecordings.get(terminalId);
295626
+ if (recordingId || this.sessionLogger && tab) {
295627
+ const captureData = sanitizedData;
295628
+ const captureTab = tab;
295629
+ const captureId = recordingId;
295630
+ setImmediate(() => {
295631
+ if (captureId && this.sessionRecorder) {
295632
+ try {
295633
+ this.sessionRecorder.out(captureId, captureData);
295634
+ } catch {
295635
+ }
295636
+ }
295637
+ if (this.sessionLogger && captureTab) {
295638
+ if (!this.sessionLogStarted.has(terminalId)) {
295639
+ this.sessionLogStarted.add(terminalId);
295640
+ this.sessionLogger.start(terminalId, { title: captureTab.title || terminalId, type: captureTab.type });
295641
+ }
295642
+ this.sessionLogger.write(terminalId, captureData);
295643
+ }
295644
+ });
295635
295645
  }
295636
- this.sessionLogger.write(terminalId, sanitizedData);
295637
295646
  }
295638
295647
  if (tab) {
295639
295648
  let shouldPublishTabsChanged = false;
@@ -295952,7 +295961,24 @@ ${promptPrefix}`;
295952
295961
  if (terminal && this.canWriteToTerminal(terminal)) {
295953
295962
  const backend = this.getBackend(terminal.type);
295954
295963
  backend.write(terminal.ptyId, data);
295964
+ this.pendingWriteRetries.delete(terminalId);
295955
295965
  } else if (terminal) {
295966
+ if (terminal.runtimeState === "exited") {
295967
+ this.pendingWrites.delete(terminalId);
295968
+ this.pendingWriteRetries.delete(terminalId);
295969
+ const timer = this.pendingWriteTimers.get(terminalId);
295970
+ if (timer) {
295971
+ clearTimeout(timer);
295972
+ this.pendingWriteTimers.delete(terminalId);
295973
+ }
295974
+ return;
295975
+ }
295976
+ const retries = this.pendingWriteRetries.get(terminalId) ?? 0;
295977
+ if (retries >= _TerminalService.MAX_WRITE_RETRIES) {
295978
+ this.pendingWrites.delete(terminalId);
295979
+ this.pendingWriteRetries.delete(terminalId);
295980
+ return;
295981
+ }
295956
295982
  const pending = this.pendingWrites.get(terminalId) ?? "";
295957
295983
  this.pendingWrites.set(terminalId, pending + data);
295958
295984
  if (!this.pendingWriteTimers.has(terminalId)) {
@@ -295962,8 +295988,11 @@ ${promptPrefix}`;
295962
295988
  const buffered = this.pendingWrites.get(terminalId);
295963
295989
  if (term && buffered && this.canWriteToTerminal(term)) {
295964
295990
  this.pendingWrites.delete(terminalId);
295991
+ this.pendingWriteRetries.delete(terminalId);
295965
295992
  const b = this.getBackend(term.type);
295966
295993
  b.write(term.ptyId, buffered);
295994
+ } else {
295995
+ this.pendingWriteRetries.set(terminalId, retries + 1);
295967
295996
  }
295968
295997
  }, 500);
295969
295998
  this.pendingWriteTimers.set(terminalId, timer);
@@ -296023,6 +296052,7 @@ ${promptPrefix}`;
296023
296052
  kill(terminalId) {
296024
296053
  this.pendingResizeByTerminal.delete(terminalId);
296025
296054
  this.pendingWrites.delete(terminalId);
296055
+ this.pendingWriteRetries.delete(terminalId);
296026
296056
  const writeTimer = this.pendingWriteTimers.get(terminalId);
296027
296057
  if (writeTimer) {
296028
296058
  clearTimeout(writeTimer);
@@ -367362,6 +367392,85 @@ var SINGLE_CALL_TOOL_BOUNDARY_NAMES = /* @__PURE__ */ new Set([
367362
367392
  "manage_template",
367363
367393
  "import_putty"
367364
367394
  ]);
367395
+ var PARALLEL_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
367396
+ "read_file",
367397
+ "read_terminal_tab",
367398
+ "read_command_output",
367399
+ "list_session_logs",
367400
+ "read_session_log",
367401
+ "search_session_logs",
367402
+ "get_metrics",
367403
+ "get_live_dashboard",
367404
+ "get_monitor_status",
367405
+ "get_cloud_inventory",
367406
+ "get_apm_summary",
367407
+ "get_dem_summary",
367408
+ "get_cost",
367409
+ "get_run_ledger",
367410
+ "list_gateway_methods",
367411
+ "manage_secret",
367412
+ // read operations (list/has) are safe; set/delete are rare
367413
+ "manage_oncall",
367414
+ // open_pages is read-only
367415
+ "manage_gitops",
367416
+ // export/drift/inSync are read-only
367417
+ "manage_playbook_version",
367418
+ // lint/history/diff are read-only
367419
+ "collect_facts",
367420
+ "run_fleet_command",
367421
+ // runs on different terminals — parallel-safe
367422
+ "synapse_health",
367423
+ "synapse_discover",
367424
+ "synapse_agents_summary",
367425
+ "synapse_reputation",
367426
+ "synapse_serve_status",
367427
+ "numbat_health",
367428
+ "numbat_findings_summary",
367429
+ "agentspan_health",
367430
+ "agentspan_list",
367431
+ "agentspan_status",
367432
+ "webintel_health",
367433
+ "web_search",
367434
+ "web_fetch",
367435
+ "web_find_similar",
367436
+ "web_watch_list",
367437
+ "patch_status",
367438
+ "list_requests",
367439
+ "request_status",
367440
+ "sop_search",
367441
+ "sop_get",
367442
+ "iam_user_info",
367443
+ "iam_user_groups",
367444
+ "iam_access_review",
367445
+ "fraudops_pipeline_status",
367446
+ "fraudops_str_status",
367447
+ "fraudops_decision_summary",
367448
+ "netdata_alert_summary",
367449
+ "netdata_correlate"
367450
+ ]);
367451
+ function canRunInParallel(toolCalls) {
367452
+ if (toolCalls.length <= 1) return false;
367453
+ for (const tc of toolCalls) {
367454
+ if (SINGLE_CALL_TOOL_BOUNDARY_NAMES.has(tc?.name)) return false;
367455
+ if (!PARALLEL_SAFE_TOOL_NAMES.has(tc?.name)) return false;
367456
+ }
367457
+ const terminalIds = /* @__PURE__ */ new Set();
367458
+ for (const tc of toolCalls) {
367459
+ const args = typeof tc?.args === "string" ? (() => {
367460
+ try {
367461
+ return JSON.parse(tc.args);
367462
+ } catch {
367463
+ return {};
367464
+ }
367465
+ })() : tc?.args || {};
367466
+ const tid = args.terminalId || args.target;
367467
+ if (tid) {
367468
+ if (terminalIds.has(tid)) return false;
367469
+ terminalIds.add(tid);
367470
+ }
367471
+ }
367472
+ return true;
367473
+ }
367365
367474
  function clipTextMiddle(input, maxChars) {
367366
367475
  if (maxChars <= 0) return "";
367367
367476
  if (input.length <= maxChars) return input;
@@ -368363,6 +368472,31 @@ var AgentService_v2 = class {
368363
368472
  if (!sessionId) throw new Error("No session ID in state");
368364
368473
  const sessionBinding = this.getSessionModelBinding(sessionId);
368365
368474
  const queue2 = Array.isArray(state.pendingToolCalls) ? state.pendingToolCalls : [];
368475
+ if (canRunInParallel(queue2)) {
368476
+ const parallelResults = await Promise.all(
368477
+ queue2.map(async (tc) => {
368478
+ const tm = this.createToolMessage(tc);
368479
+ const ec = this.createExecutionContext(
368480
+ sessionId,
368481
+ tm.additional_kwargs._gyshellMessageId,
368482
+ config2
368483
+ );
368484
+ let res = "";
368485
+ try {
368486
+ res = await this.executeToolByName(tc, ec);
368487
+ } catch (err) {
368488
+ res = `Parallel execution error for ${tc.name}: ${err.message}`;
368489
+ }
368490
+ tm.content = res;
368491
+ return tm;
368492
+ })
368493
+ );
368494
+ return {
368495
+ messages: [...state.messages, ...parallelResults],
368496
+ sessionId,
368497
+ pendingToolCalls: []
368498
+ };
368499
+ }
368366
368500
  const toolCall = queue2[0];
368367
368501
  if (!toolCall) return state;
368368
368502
  const toolMessage = this.createToolMessage(toolCall);
@@ -369215,6 +369349,71 @@ Actually, your intention might be different. Please re-read the description of t
369215
369349
  };
369216
369350
  });
369217
369351
  }
369352
+ /** v3.2.4: Execute a single tool by name (used by the parallel execution path).
369353
+ * This is a simplified dispatch that handles the common read-only/parallel-safe tools.
369354
+ * Tools not handled here fall back to the sequential switch/case path. */
369355
+ async executeToolByName(toolCall, executionContext) {
369356
+ const name = toolCall.name;
369357
+ const args = typeof toolCall.args === "string" ? (() => {
369358
+ try {
369359
+ return JSON.parse(toolCall.args);
369360
+ } catch {
369361
+ return {};
369362
+ }
369363
+ })() : toolCall.args || {};
369364
+ const ti = toolImplementations;
369365
+ switch (name) {
369366
+ case "read_terminal_tab":
369367
+ return ti.readTerminalTab(args, executionContext);
369368
+ case "read_command_output":
369369
+ return ti.readCommandOutput(args, executionContext);
369370
+ case "list_session_logs":
369371
+ return ti.listSessionLogs(args, executionContext);
369372
+ case "read_session_log":
369373
+ return ti.readSessionLog(args, executionContext);
369374
+ case "search_session_logs":
369375
+ return ti.searchSessionLogs(args, executionContext);
369376
+ case "get_metrics":
369377
+ return ti.getMetrics(args, executionContext);
369378
+ case "get_live_dashboard":
369379
+ return ti.getLiveDashboard(args, executionContext);
369380
+ case "get_monitor_status":
369381
+ return ti.getMonitorStatus(args, executionContext);
369382
+ case "get_cloud_inventory":
369383
+ return ti.getCloudInventory(args, executionContext);
369384
+ case "get_apm_summary":
369385
+ return ti.getApmSummary(args, executionContext);
369386
+ case "get_dem_summary":
369387
+ return ti.getDemSummary(args, executionContext);
369388
+ case "get_cost":
369389
+ return ti.getCost(args, executionContext);
369390
+ case "get_run_ledger":
369391
+ return ti.getRunLedger(args, executionContext);
369392
+ case "list_gateway_methods":
369393
+ return ti.listGatewayMethods(args, executionContext);
369394
+ case "collect_facts":
369395
+ return ti.collectFacts(args, executionContext);
369396
+ case "run_fleet_command":
369397
+ return ti.runFleetCommand(args, executionContext);
369398
+ case "manage_secret":
369399
+ return ti.manageSecret(args, executionContext);
369400
+ case "manage_oncall":
369401
+ return ti.manageOncall(args, executionContext);
369402
+ case "manage_gitops":
369403
+ return ti.manageGitops(args, executionContext);
369404
+ case "manage_playbook_version":
369405
+ return ti.managePlaybookVersion(args, executionContext);
369406
+ // Plugin tools — delegate to the pluginTools map
369407
+ default: {
369408
+ const pluginHandler = this.pluginTools.get(name);
369409
+ if (pluginHandler) {
369410
+ const result = await pluginHandler(args);
369411
+ return typeof result === "string" ? result : JSON.stringify(result);
369412
+ }
369413
+ return `Tool "${name}" is not supported in parallel execution mode.`;
369414
+ }
369415
+ }
369416
+ }
369218
369417
  createReadFileNode() {
369219
369418
  return RunnableLambda.from(async (state, config2) => {
369220
369419
  const sessionId = state.sessionId;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rterm-backend",
3
- "version": "3.2.1",
4
- "description": "Headless AI-native backend for RTerm / neuralOS — v3.2.1: terminal freeze fix (pending-write buffer), chat re-trigger fix (isThinking guard), stop button freeze fix (async await). 11 plugins, 55 plugin tools wired. Transports (SSH/serial/local), SQLite, and NATS libs install automatically.",
3
+ "version": "3.2.4",
4
+ "description": "Headless AI-native backend for RTerm / neuralOS — v3.2.4: parallel tool execution for compatible tools + per-model requestParams. 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",