ragent-cli 1.11.16 → 1.11.18

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.
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "ragent-cli",
34
- version: "1.11.16",
34
+ version: "1.11.18",
35
35
  description: "CLI agent for rAgent Live \u2014 browser-first terminal control plane for AI coding agents",
36
36
  main: "dist/index.js",
37
37
  bin: {
@@ -4185,7 +4185,7 @@ var TranscriptWatcherManager = class {
4185
4185
  clearTranscriptState(sessionId) {
4186
4186
  this.sendSnapshotFn(sessionId, [], 0);
4187
4187
  if (this.sendV2SnapshotFn) {
4188
- this.sendV2SnapshotFn(sessionId, createEmptySnapshot(sessionId), 0);
4188
+ this.sendV2SnapshotFn(sessionId, createEmptySnapshot(sessionId), 0, true);
4189
4189
  }
4190
4190
  }
4191
4191
  /**
@@ -4271,7 +4271,7 @@ var TranscriptWatcherManager = class {
4271
4271
  }
4272
4272
  this.sendSnapshotFn(sessionId, [], 0);
4273
4273
  if (this.sendV2SnapshotFn) {
4274
- this.sendV2SnapshotFn(sessionId, createEmptySnapshot(sessionId), 0);
4274
+ this.sendV2SnapshotFn(sessionId, createEmptySnapshot(sessionId), 0, true);
4275
4275
  }
4276
4276
  newWatcher.replayFromStart();
4277
4277
  this.active.set(sessionId, { watcher: newWatcher, filePath: newPath, agentType: agentType ?? "" });
@@ -10857,6 +10857,9 @@ var import_node_string_decoder = require("string_decoder");
10857
10857
  var pty = __toESM(require("node-pty"));
10858
10858
  var log17 = createLogger("session-streamer");
10859
10859
  var BACKPRESSURE_RESUME_CHECK_MS = 100;
10860
+ var LIVENESS_CHECK_TICKS = 30;
10861
+ var DEAD_PIPE_STRIKES = 2;
10862
+ var REESTABLISH_BACKOFF_MS = 3e4;
10860
10863
  var STOP_DEBOUNCE_MS = 2e3;
10861
10864
  var MAX_CONCURRENT_STREAMS = 20;
10862
10865
  var MAX_SESSION_BUFFER_BYTES = 64 * 1024;
@@ -10979,11 +10982,19 @@ var SessionStreamer = class {
10979
10982
  lastSeenDroppedFrames = 0;
10980
10983
  /** Sessions whose viewers need a capture-pane resync after frame drops. */
10981
10984
  pendingDropResync = /* @__PURE__ */ new Set();
10982
- constructor(sendFn, onStreamStopped, sendResetFn, onFramesDropped) {
10985
+ /** Whether the relay socket is currently OPEN. Used to (a) gate resync delivery
10986
+ * so resync bytes aren't buffered-then-discarded under reconnect churn, and
10987
+ * (b) gate the liveness watchdog's re-establish so its capture burst lands. */
10988
+ isReadyFn;
10989
+ /** Tick counter for the resume timer; the pipe-liveness check runs every
10990
+ * LIVENESS_CHECK_TICKS ticks rather than on every 100ms tick. */
10991
+ livenessTick = 0;
10992
+ constructor(sendFn, onStreamStopped, sendResetFn, onFramesDropped, isReadyFn) {
10983
10993
  this.sendFn = sendFn;
10984
10994
  this.onStreamStopped = onStreamStopped;
10985
10995
  this.sendResetFn = sendResetFn;
10986
10996
  this.onFramesDropped = onFramesDropped;
10997
+ this.isReadyFn = isReadyFn ?? (() => true);
10987
10998
  this.cleanupStaleStreams();
10988
10999
  this.startBackpressureResumeTimer();
10989
11000
  }
@@ -11037,8 +11048,110 @@ var SessionStreamer = class {
11037
11048
  this.resyncStream(next.value);
11038
11049
  }
11039
11050
  }
11051
+ if (this.isReadyFn()) {
11052
+ for (const stream of this.active.values()) {
11053
+ if (stream.needsResync && !stream.stopped && stream.streamType === "tmux-pipe" && !stream.initializing) {
11054
+ stream.needsResync = false;
11055
+ this.resyncStream(stream.sessionId);
11056
+ break;
11057
+ }
11058
+ }
11059
+ }
11060
+ if (++this.livenessTick >= LIVENESS_CHECK_TICKS) {
11061
+ this.livenessTick = 0;
11062
+ if (this.isReadyFn()) this.checkPipeLiveness();
11063
+ }
11040
11064
  }, BACKPRESSURE_RESUME_CHECK_MS);
11041
11065
  }
11066
+ /**
11067
+ * Re-establish a tmux-pipe stream whose `pane_pid` indicates a program swap
11068
+ * (tmux-resurrect restore, pane reuse, exec) or whose pipe-pane is alive in
11069
+ * tmux's view (`pane_pipe=1`) but no longer feeds our FIFO.
11070
+ *
11071
+ * Why this exists: `pipe-pane` is set up exactly once at stream start. tmux
11072
+ * reports `pipe=1` afterwards even when it has stopped delivering bytes (e.g.
11073
+ * the pane was recreated by a restore racing the connector restart). The
11074
+ * `cat` reader blocks forever rather than exiting, so the normal exit cleanup
11075
+ * never fires, and `resyncStream` deliberately does NOT restart pipe-pane —
11076
+ * so without this the pane is frozen until the stream is torn down by hand.
11077
+ *
11078
+ * Batched into one `tmux list-panes -a` call so liveness costs one fork.
11079
+ */
11080
+ checkPipeLiveness() {
11081
+ const tmuxStreams = [...this.active.values()].filter(
11082
+ (s) => s.streamType === "tmux-pipe" && !s.stopped && !s.initializing
11083
+ );
11084
+ if (tmuxStreams.length === 0) return;
11085
+ const panes = this.readAllPaneInfo();
11086
+ if (!panes) return;
11087
+ const now2 = Date.now();
11088
+ const dead = [];
11089
+ for (const stream of tmuxStreams) {
11090
+ const info = panes.get(stream.paneTarget);
11091
+ if (!info) continue;
11092
+ const backedOff = stream.lastReestablishAt !== void 0 && now2 - stream.lastReestablishAt < REESTABLISH_BACKOFF_MS;
11093
+ if (stream.panePid && info.panePid !== stream.panePid) {
11094
+ if (!backedOff) {
11095
+ dead.push({
11096
+ sessionId: stream.sessionId,
11097
+ reason: "pane program swapped",
11098
+ detail: { oldPid: stream.panePid, newPid: info.panePid }
11099
+ });
11100
+ }
11101
+ continue;
11102
+ }
11103
+ const bytesNow = stream.bytesReceived ?? 0;
11104
+ const paneMoved = info.fingerprint !== stream.lastLivenessFingerprint;
11105
+ const fifoSilent = bytesNow === (stream.lastLivenessBytes ?? 0);
11106
+ if (paneMoved && fifoSilent) {
11107
+ stream.deadPipeStrikes = (stream.deadPipeStrikes ?? 0) + 1;
11108
+ if (stream.deadPipeStrikes >= DEAD_PIPE_STRIKES && !backedOff) {
11109
+ dead.push({
11110
+ sessionId: stream.sessionId,
11111
+ reason: "pipe-pane silent while pane active",
11112
+ detail: { strikes: stream.deadPipeStrikes }
11113
+ });
11114
+ }
11115
+ } else {
11116
+ stream.deadPipeStrikes = 0;
11117
+ }
11118
+ stream.lastLivenessBytes = bytesNow;
11119
+ stream.lastLivenessFingerprint = info.fingerprint;
11120
+ }
11121
+ if (dead.length === 0) return;
11122
+ const [first, ...rest] = dead;
11123
+ log17.warn(`${first.reason}; re-establishing stream`, { sessionId: first.sessionId, ...first.detail });
11124
+ if (rest.length > 0) {
11125
+ log17.info("more dead streams queued for re-establish (one per tick)", { remaining: rest.length });
11126
+ }
11127
+ this.reestablishTmuxStream(first.sessionId);
11128
+ }
11129
+ /**
11130
+ * Tear down a tmux-pipe stream's pipe-pane/FIFO/cat and start a fresh one for
11131
+ * the same sessionId, preserving its viewer set. Sends a new stream-reset +
11132
+ * capture so the portal hard-repaints the pane. Uses a LEAN re-establish (no
11133
+ * 5000-line scrollback re-send) to keep the post-reconnect burst small.
11134
+ */
11135
+ reestablishTmuxStream(sessionId) {
11136
+ const old = this.active.get(sessionId);
11137
+ if (!old || old.streamType !== "tmux-pipe") return;
11138
+ const viewerIds = new Set(old.viewerIds);
11139
+ this.cleanupStream(old);
11140
+ this.active.delete(sessionId);
11141
+ this.disconnectBuffers.delete(sessionId);
11142
+ this.disconnectBufferBytes.delete(sessionId);
11143
+ const restarted = this.startTmuxStream(sessionId, "", { skipScrollback: true });
11144
+ if (restarted) {
11145
+ const fresh = this.active.get(sessionId);
11146
+ if (fresh) {
11147
+ for (const v of viewerIds) fresh.viewerIds.add(v);
11148
+ fresh.lastReestablishAt = Date.now();
11149
+ }
11150
+ } else {
11151
+ log17.warn("failed to re-establish tmux stream", { sessionId });
11152
+ this.onStreamStopped?.(sessionId);
11153
+ }
11154
+ }
11042
11155
  /**
11043
11156
  * Pause a PTY-attach stream when WS queue pressure is high. Only applies
11044
11157
  * to `pty-attach` streams — tmux-pipe (FIFO cat) and process-trace
@@ -11266,6 +11379,34 @@ var SessionStreamer = class {
11266
11379
  this.disconnectBufferBytes.delete(sessionId);
11267
11380
  return result;
11268
11381
  }
11382
+ /**
11383
+ * #556: recover active streams after a relay reconnect.
11384
+ *
11385
+ * For tmux-pipe streams the disconnect-buffered deltas are stale AND
11386
+ * redundant: the portal requests a capture-pane resync on reconnect, and the
11387
+ * capture reflects everything tmux applied to the pane during the disconnect.
11388
+ * Replaying the buffered deltas instead (a) paints stale bytes over the fresh
11389
+ * capture → interleaved garble, and (b) re-floods the relay immediately after
11390
+ * a reconnect, helping trigger Azure's next close — the reconnect churn loop
11391
+ * behind the terminal artifacting. So we DISCARD the buffer and resync.
11392
+ *
11393
+ * pty-attach / process-trace streams have no capture-pane resync, so their
11394
+ * buffered output IS replayed via `replay`.
11395
+ */
11396
+ recoverStreamsAfterReconnect(replay) {
11397
+ for (const stream of this.active.values()) {
11398
+ if (stream.stopped) continue;
11399
+ if (stream.streamType === "tmux-pipe") {
11400
+ this.drainBuffer(stream.sessionId);
11401
+ stream.needsResync = true;
11402
+ this.resyncStream(stream.sessionId);
11403
+ } else {
11404
+ for (const chunk of this.drainBuffer(stream.sessionId)) {
11405
+ replay(stream.sessionId, chunk);
11406
+ }
11407
+ }
11408
+ }
11409
+ }
11269
11410
  /**
11270
11411
  * Re-send capture-pane data for an already-active tmux stream.
11271
11412
  * Used when a viewer missed the initial burst (e.g. joinGroup race),
@@ -11302,6 +11443,13 @@ var SessionStreamer = class {
11302
11443
  stream.initBuffer = [];
11303
11444
  return;
11304
11445
  }
11446
+ if (!this.isReadyFn()) {
11447
+ stream.needsResync = true;
11448
+ stream.initializing = false;
11449
+ stream.initBuffer = [];
11450
+ return;
11451
+ }
11452
+ stream.needsResync = false;
11305
11453
  const send = (data) => {
11306
11454
  if (targetViewerId) {
11307
11455
  this.sendFn(sessionId, data, targetViewerId);
@@ -11357,6 +11505,57 @@ var SessionStreamer = class {
11357
11505
  return false;
11358
11506
  }
11359
11507
  }
11508
+ /** Build a tmux-safe env (TMUX/TMUX_PANE stripped) for liveness queries. */
11509
+ tmuxEnv() {
11510
+ const env = { ...process.env };
11511
+ delete env.TMUX;
11512
+ delete env.TMUX_PANE;
11513
+ return env;
11514
+ }
11515
+ /** Pane pid + a cheap activity fingerprint (cursor + history) for one pane. */
11516
+ readPaneInfo(paneTarget, cleanEnv) {
11517
+ try {
11518
+ const out = (0, import_node_child_process2.execFileSync)(
11519
+ "tmux",
11520
+ ["display-message", "-t", paneTarget, "-p", "#{pane_pid}|#{history_size}:#{cursor_x}:#{cursor_y}:#{pane_in_mode}"],
11521
+ { env: cleanEnv, timeout: 5e3, encoding: "utf-8" }
11522
+ ).trim();
11523
+ const sep2 = out.indexOf("|");
11524
+ if (sep2 < 0) return null;
11525
+ return { panePid: out.slice(0, sep2), fingerprint: out.slice(sep2 + 1) };
11526
+ } catch {
11527
+ return null;
11528
+ }
11529
+ }
11530
+ /**
11531
+ * One batched tmux call → pane pid + activity fingerprint for every pane,
11532
+ * keyed by `session:window.pane` (matching parsePaneTarget). Returns null on
11533
+ * tmux failure so the watchdog skips this tick rather than acting on no data.
11534
+ */
11535
+ readAllPaneInfo() {
11536
+ try {
11537
+ const out = (0, import_node_child_process2.execFileSync)(
11538
+ "tmux",
11539
+ [
11540
+ "list-panes",
11541
+ "-a",
11542
+ "-F",
11543
+ "#{session_name}:#{window_index}.#{pane_index}|#{pane_pid}|#{history_size}:#{cursor_x}:#{cursor_y}:#{pane_in_mode}"
11544
+ ],
11545
+ { env: this.tmuxEnv(), timeout: 5e3, encoding: "utf-8" }
11546
+ );
11547
+ const map = /* @__PURE__ */ new Map();
11548
+ for (const line of out.split("\n")) {
11549
+ if (!line) continue;
11550
+ const parts = line.split("|");
11551
+ if (parts.length < 3) continue;
11552
+ map.set(parts[0], { panePid: parts[1], fingerprint: parts[2] });
11553
+ }
11554
+ return map;
11555
+ } catch {
11556
+ return null;
11557
+ }
11558
+ }
11360
11559
  /**
11361
11560
  * Capture and send the visible pane content. Converts bare \n to \r\n for
11362
11561
  * xterm.js and strips the trailing \r\n to prevent an extra scroll that
@@ -11408,7 +11607,7 @@ var SessionStreamer = class {
11408
11607
  // ---------------------------------------------------------------------------
11409
11608
  // tmux streaming (pipe-pane with cursor sync)
11410
11609
  // ---------------------------------------------------------------------------
11411
- startTmuxStream(sessionId, viewerKey = "") {
11610
+ startTmuxStream(sessionId, viewerKey = "", opts) {
11412
11611
  const paneTarget = parsePaneTarget(sessionId);
11413
11612
  if (!paneTarget) return false;
11414
11613
  try {
@@ -11458,10 +11657,19 @@ var SessionStreamer = class {
11458
11657
  log17.warn("failed pipe-pane", { sessionId, error: message });
11459
11658
  return false;
11460
11659
  }
11660
+ const paneInfo = this.readPaneInfo(paneTarget, cleanEnv);
11661
+ if (paneInfo) {
11662
+ stream.panePid = paneInfo.panePid;
11663
+ stream.lastLivenessFingerprint = paneInfo.fingerprint;
11664
+ }
11665
+ stream.bytesReceived = 0;
11666
+ stream.lastLivenessBytes = 0;
11667
+ stream.deadPipeStrikes = 0;
11461
11668
  const catProc = (0, import_node_child_process2.spawn)("cat", [fifoPath], { env: cleanEnv, stdio: ["ignore", "pipe", "ignore"] });
11462
11669
  stream.catProc = catProc;
11463
11670
  catProc.stdout.on("data", (chunk) => {
11464
11671
  if (stream.stopped) return;
11672
+ stream.bytesReceived = (stream.bytesReceived ?? 0) + chunk.length;
11465
11673
  const data = stream.utf8Decoder.write(chunk);
11466
11674
  if (!data) return;
11467
11675
  if (stream.initializing) {
@@ -11490,17 +11698,19 @@ var SessionStreamer = class {
11490
11698
  this.sendResetFn?.(sessionId, "full");
11491
11699
  const alternateScreen = this.detectAlternateScreen(paneTarget, cleanEnv);
11492
11700
  stream.alternateScreen = alternateScreen;
11493
- this.sendFn(sessionId, "\x1B[3J");
11494
- try {
11495
- const scrollback = (0, import_node_child_process2.execFileSync)(
11496
- "tmux",
11497
- ["capture-pane", "-t", paneTarget, "-p", "-e", "-S", "-5000", "-E", "-1"],
11498
- { env: cleanEnv, timeout: 1e4, encoding: "utf-8" }
11499
- );
11500
- if (scrollback && scrollback.length > 0) {
11501
- this.sendFn(sessionId, scrollback.replace(/\n/g, "\r\n"));
11701
+ if (!opts?.skipScrollback) {
11702
+ this.sendFn(sessionId, "\x1B[3J");
11703
+ try {
11704
+ const scrollback = (0, import_node_child_process2.execFileSync)(
11705
+ "tmux",
11706
+ ["capture-pane", "-t", paneTarget, "-p", "-e", "-S", "-5000", "-E", "-1"],
11707
+ { env: cleanEnv, timeout: 1e4, encoding: "utf-8" }
11708
+ );
11709
+ if (scrollback && scrollback.length > 0) {
11710
+ this.sendFn(sessionId, scrollback.replace(/\n/g, "\r\n"));
11711
+ }
11712
+ } catch {
11502
11713
  }
11503
- } catch {
11504
11714
  }
11505
11715
  this.sendFn(sessionId, "\x1B[?1049l\x1B[0m\x1B[2J\x1B[H");
11506
11716
  const sendBroadcast = (data) => this.sendFn(sessionId, data);
@@ -14517,7 +14727,11 @@ async function runAgent(rawOptions) {
14517
14727
  },
14518
14728
  () => {
14519
14729
  transcriptWatcher.resyncMarkdownAfterDrop();
14520
- }
14730
+ },
14731
+ // #556 follow-up: lets the streamer gate resync delivery + the pipe-liveness
14732
+ // watchdog on a confirmed-OPEN socket, so resync bytes aren't buffered then
14733
+ // discarded under reconnect churn and re-establish captures actually land.
14734
+ () => conn.isReady()
14521
14735
  );
14522
14736
  const conn = new ConnectionManager(sessionStreamer, outputBuffer);
14523
14737
  const sendOutput = (chunk) => {
@@ -14593,8 +14807,11 @@ async function runAgent(rawOptions) {
14593
14807
  sendToGroup(conn.activeSocket, conn.activeGroups.privateGroup, payload);
14594
14808
  }
14595
14809
  },
14596
- // V2: send full snapshot
14597
- (sessionId, snapshot, seq) => {
14810
+ // V2: send full snapshot. `reset` marks an explicit clear (new conversation
14811
+ // / transcript file rotation on pane reuse) so the portal's snapshot guard
14812
+ // lets an empty snapshot wipe stale content instead of rejecting it. See the
14813
+ // `reset` field on TranscriptSnapshotMessage (#538) and shouldApplyTranscriptSnapshot.
14814
+ (sessionId, snapshot, seq, reset) => {
14598
14815
  if (!conn.isReady()) return;
14599
14816
  const payload = {
14600
14817
  type: "markdown",
@@ -14602,7 +14819,8 @@ async function runAgent(rawOptions) {
14602
14819
  schemaVersion: 2,
14603
14820
  sessionId,
14604
14821
  seq,
14605
- snapshot
14822
+ snapshot,
14823
+ ...reset ? { reset: true } : {}
14606
14824
  };
14607
14825
  if (conn.sessionKey) {
14608
14826
  const contentStr = JSON.stringify(payload);
@@ -14697,6 +14915,7 @@ async function runAgent(rawOptions) {
14697
14915
  await new Promise((resolve) => {
14698
14916
  const ws = new import_ws5.default(negotiated.url, "json.webpubsub.azure.v1");
14699
14917
  conn.setConnection(ws, groups, negotiated.sessionKey ?? null);
14918
+ let connectedAt = 0;
14700
14919
  if (negotiated.sessionKey) {
14701
14920
  const enforcer = new ApprovalEnforcer({
14702
14921
  secret: negotiated.sessionKey,
@@ -14717,6 +14936,7 @@ async function runAgent(rawOptions) {
14717
14936
  }
14718
14937
  ws.on("open", async () => {
14719
14938
  log25.info("connector connected to relay");
14939
+ connectedAt = Date.now();
14720
14940
  conn.resetReconnectDelay();
14721
14941
  ws.send(JSON.stringify({ type: "joinGroup", group: groups.privateGroup }));
14722
14942
  ws.send(JSON.stringify({ type: "joinGroup", group: groups.registryGroup }));
@@ -14749,19 +14969,16 @@ async function runAgent(rawOptions) {
14749
14969
  }
14750
14970
  }
14751
14971
  });
14752
- for (const [sessionId] of sessionStreamer.activeStreams()) {
14753
- const buffered = sessionStreamer.drainBuffer(sessionId);
14754
- for (const chunk of buffered) {
14755
- for (const outputChunk of splitTransportChunks(chunk)) {
14756
- if (conn.sessionKey) {
14757
- const { enc, iv, seq } = encryptPayload(outputChunk, conn.sessionKey, conn.txSeq.next());
14758
- sendToGroup(ws, groups.privateGroup, { type: "output", enc, iv, seq, sessionId });
14759
- } else {
14760
- sendToGroup(ws, groups.privateGroup, { type: "output", data: outputChunk, sessionId });
14761
- }
14972
+ sessionStreamer.recoverStreamsAfterReconnect((sessionId, chunk) => {
14973
+ for (const outputChunk of splitTransportChunks(chunk)) {
14974
+ if (conn.sessionKey) {
14975
+ const { enc, iv, seq } = encryptPayload(outputChunk, conn.sessionKey, conn.txSeq.next());
14976
+ sendToGroup(ws, groups.privateGroup, { type: "output", enc, iv, seq, sessionId });
14977
+ } else {
14978
+ sendToGroup(ws, groups.privateGroup, { type: "output", data: outputChunk, sessionId });
14762
14979
  }
14763
14980
  }
14764
- }
14981
+ });
14765
14982
  await inventory.syncInventory(ws, groups, true);
14766
14983
  conn.startTimers(
14767
14984
  () => inventory.announceToRegistry(ws, groups.registryGroup, "heartbeat"),
@@ -14886,6 +15103,7 @@ async function runAgent(rawOptions) {
14886
15103
  const fields = {};
14887
15104
  if (code) fields.code = code;
14888
15105
  if (reason.length > 0) fields.reason = reason.toString();
15106
+ if (connectedAt > 0) fields.uptimeMs = Date.now() - connectedAt;
14889
15107
  log25.info("relay disconnected; output will be buffered until reconnect", fields);
14890
15108
  conn.cleanup();
14891
15109
  resolve();
package/dist/sbom.json CHANGED
@@ -1299,8 +1299,8 @@
1299
1299
  {
1300
1300
  "type": "library",
1301
1301
  "name": "ragent-cli",
1302
- "version": "1.11.16",
1303
- "bom-ref": "ragent-live|ragent-cli@1.11.16",
1302
+ "version": "1.11.18",
1303
+ "bom-ref": "ragent-live|ragent-cli@1.11.18",
1304
1304
  "author": "Intellimetrics",
1305
1305
  "description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
1306
1306
  "licenses": [
@@ -1311,7 +1311,7 @@
1311
1311
  }
1312
1312
  }
1313
1313
  ],
1314
- "purl": "pkg:npm/ragent-cli@1.11.16",
1314
+ "purl": "pkg:npm/ragent-cli@1.11.18",
1315
1315
  "externalReferences": [
1316
1316
  {
1317
1317
  "url": "https://github.com/chadlindell/ragent-live/issues",
@@ -1436,7 +1436,7 @@
1436
1436
  "ragent-live|@emnapi/wasi-threads@1.2.1",
1437
1437
  "ragent-live|@pkgjs/parseargs@0.11.0",
1438
1438
  "ragent-live|@tybys/wasm-util@0.10.1",
1439
- "ragent-live|ragent-cli@1.11.16"
1439
+ "ragent-live|ragent-cli@1.11.18"
1440
1440
  ]
1441
1441
  },
1442
1442
  {
@@ -1584,7 +1584,7 @@
1584
1584
  ]
1585
1585
  },
1586
1586
  {
1587
- "ref": "ragent-live|ragent-cli@1.11.16",
1587
+ "ref": "ragent-live|ragent-cli@1.11.18",
1588
1588
  "dependsOn": [
1589
1589
  "ragent-live|@azure/web-pubsub-client@1.0.4",
1590
1590
  "ragent-live|@openai/codex-sdk@0.130.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ragent-cli",
3
- "version": "1.11.16",
3
+ "version": "1.11.18",
4
4
  "description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
5
5
  "main": "dist/index.js",
6
6
  "bin": {