ragent-cli 1.11.9 → 1.11.10

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.9",
34
+ version: "1.11.10",
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: {
@@ -662,8 +662,30 @@ var ClaudeCodeParserV2 = class {
662
662
  return "";
663
663
  }
664
664
  };
665
+ function mapCodexRole(role) {
666
+ const r = role?.toLowerCase();
667
+ if (r === "user") return "user";
668
+ if (r === "assistant") return "assistant";
669
+ return null;
670
+ }
671
+ function extractCodexText(content) {
672
+ if (!Array.isArray(content)) return "";
673
+ return content.filter(
674
+ (c) => (c.type === "input_text" || c.type === "output_text" || c.type === "text") && typeof c.text === "string"
675
+ ).map((c) => c.text).join("\n").trim();
676
+ }
677
+ function parseCodexArguments(args) {
678
+ if (typeof args !== "string" || !args) return void 0;
679
+ try {
680
+ return JSON.parse(args);
681
+ } catch {
682
+ return args;
683
+ }
684
+ }
665
685
  var CodexCliParserV2 = class {
666
686
  name = "codex-cli-v2";
687
+ pendingTools = /* @__PURE__ */ new Map();
688
+ turnCounter = 0;
667
689
  parseLine(line) {
668
690
  let obj;
669
691
  try {
@@ -672,21 +694,62 @@ var CodexCliParserV2 = class {
672
694
  debugTranscriptParse("codex", line, error);
673
695
  return [];
674
696
  }
697
+ if (obj.response_item) {
698
+ return this.parseLegacy(obj);
699
+ }
700
+ if (obj.type === "response_item" && obj.payload) {
701
+ return this.parseNewEnvelope(obj.payload, obj.timestamp);
702
+ }
703
+ return [];
704
+ }
705
+ parseLegacy(obj) {
675
706
  const item = obj.response_item;
676
- if (!item) return [];
677
- if (item.type === "response.output_item.done" && item.item?.type === "message") {
678
- const textParts = (item.item.content ?? []).filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text);
679
- const text = textParts.join("\n").trim();
707
+ if (item.type !== "response.output_item.done") return [];
708
+ if (item.item?.type !== "message") return [];
709
+ const text = extractCodexText(item.item.content);
710
+ if (!text) return [];
711
+ const turnId = item.id ?? this.genTurnId(obj.timestamp);
712
+ const timestamp = obj.timestamp ?? now();
713
+ const blockId = genBlockId("txt");
714
+ return [
715
+ {
716
+ op: "upsert_turn",
717
+ turn: {
718
+ turnId,
719
+ role: "assistant",
720
+ startedAt: timestamp,
721
+ updatedAt: timestamp,
722
+ isComplete: false
723
+ }
724
+ },
725
+ {
726
+ op: "insert_block",
727
+ turnId,
728
+ block: makeTextBlock(blockId, text, true)
729
+ },
730
+ {
731
+ op: "complete_turn",
732
+ turnId,
733
+ endedAt: timestamp,
734
+ updatedAt: timestamp
735
+ }
736
+ ];
737
+ }
738
+ parseNewEnvelope(payload, lineTimestamp) {
739
+ const timestamp = lineTimestamp ?? now();
740
+ if (payload.type === "message") {
741
+ const role = mapCodexRole(payload.role);
742
+ if (!role) return [];
743
+ const text = extractCodexText(payload.content);
680
744
  if (!text) return [];
681
- const turnId = item.id ?? `codex-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
682
- const timestamp = obj.timestamp ?? now();
745
+ const turnId = this.genTurnId(lineTimestamp);
683
746
  const blockId = genBlockId("txt");
684
747
  return [
685
748
  {
686
749
  op: "upsert_turn",
687
750
  turn: {
688
751
  turnId,
689
- role: "assistant",
752
+ role,
690
753
  startedAt: timestamp,
691
754
  updatedAt: timestamp,
692
755
  isComplete: false
@@ -705,13 +768,140 @@ var CodexCliParserV2 = class {
705
768
  }
706
769
  ];
707
770
  }
771
+ if (payload.type === "function_call") {
772
+ if (!payload.name) return [];
773
+ const callId = payload.call_id ?? genBlockId("call");
774
+ const blockId = genBlockId("tool");
775
+ const turnId = this.genTurnId(lineTimestamp);
776
+ this.pendingTools.set(callId, {
777
+ blockId,
778
+ turnId,
779
+ toolName: payload.name
780
+ });
781
+ this.capPendingTools();
782
+ const input = parseCodexArguments(payload.arguments);
783
+ return [
784
+ {
785
+ op: "upsert_turn",
786
+ turn: {
787
+ turnId,
788
+ role: "assistant",
789
+ startedAt: timestamp,
790
+ updatedAt: timestamp,
791
+ isComplete: false
792
+ }
793
+ },
794
+ {
795
+ op: "insert_block",
796
+ turnId,
797
+ block: makeToolBlock(
798
+ blockId,
799
+ callId,
800
+ payload.name,
801
+ "running",
802
+ input
803
+ )
804
+ }
805
+ ];
806
+ }
807
+ if (payload.type === "function_call_output") {
808
+ if (!payload.call_id) return [];
809
+ const pending = this.pendingTools.get(payload.call_id);
810
+ const output = payload.output ?? "";
811
+ if (pending) {
812
+ this.pendingTools.delete(payload.call_id);
813
+ const ops2 = [
814
+ {
815
+ op: "set_tool_state",
816
+ turnId: pending.turnId,
817
+ blockId: pending.blockId,
818
+ state: "completed",
819
+ isComplete: true
820
+ }
821
+ ];
822
+ if (output) {
823
+ ops2.push({
824
+ op: "append_tool_output",
825
+ turnId: pending.turnId,
826
+ blockId: pending.blockId,
827
+ text: output
828
+ });
829
+ }
830
+ ops2.push({
831
+ op: "complete_turn",
832
+ turnId: pending.turnId,
833
+ endedAt: timestamp,
834
+ updatedAt: timestamp
835
+ });
836
+ return ops2;
837
+ }
838
+ const turnId = this.genTurnId(lineTimestamp);
839
+ const blockId = genBlockId("tool");
840
+ const ops = [
841
+ {
842
+ op: "upsert_turn",
843
+ turn: {
844
+ turnId,
845
+ role: "user",
846
+ startedAt: timestamp,
847
+ updatedAt: timestamp,
848
+ isComplete: false
849
+ }
850
+ },
851
+ {
852
+ op: "insert_block",
853
+ turnId,
854
+ block: makeToolBlock(
855
+ blockId,
856
+ payload.call_id,
857
+ "tool",
858
+ "completed",
859
+ void 0,
860
+ true
861
+ )
862
+ }
863
+ ];
864
+ if (output) {
865
+ ops.push({
866
+ op: "append_tool_output",
867
+ turnId,
868
+ blockId,
869
+ text: output
870
+ });
871
+ }
872
+ ops.push({
873
+ op: "complete_turn",
874
+ turnId,
875
+ endedAt: timestamp,
876
+ updatedAt: timestamp
877
+ });
878
+ return ops;
879
+ }
708
880
  return [];
709
881
  }
882
+ genTurnId(timestamp) {
883
+ this.turnCounter += 1;
884
+ const ts = timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
885
+ return `codex-${ts}-${this.turnCounter.toString(36)}`;
886
+ }
887
+ capPendingTools() {
888
+ if (this.pendingTools.size <= 500) return;
889
+ const keys = [...this.pendingTools.keys()];
890
+ for (let i = 0; i < 250; i += 1) {
891
+ this.pendingTools.delete(keys[i]);
892
+ }
893
+ }
710
894
  };
711
895
  function isGeminiHistoryItem(value) {
712
896
  if (!value || typeof value !== "object") return false;
713
897
  const item = value;
714
- return Boolean(item.role && (item.parts || item.content || item.message));
898
+ if (typeof item.role === "string" && (item.parts !== void 0 || item.content !== void 0 || item.message !== void 0)) {
899
+ return true;
900
+ }
901
+ if (typeof item.type === "string" && (item.content !== void 0 || Array.isArray(item.toolCalls))) {
902
+ return true;
903
+ }
904
+ return false;
715
905
  }
716
906
  function findGeminiHistory(value, depth = 0) {
717
907
  if (depth > 6 || !value || typeof value !== "object") return null;
@@ -736,20 +926,76 @@ function findGeminiHistory(value, depth = 0) {
736
926
  }
737
927
  return null;
738
928
  }
739
- function geminiPartsFromItem(item) {
929
+ function geminiRoleFromLegacy(role) {
930
+ const normalized = role?.toLowerCase();
931
+ if (normalized === "user") return "user";
932
+ if (normalized === "model" || normalized === "assistant") return "assistant";
933
+ return null;
934
+ }
935
+ function geminiRoleFromType(type) {
936
+ if (type === "user") return "user";
937
+ if (type === "gemini" || type === "model" || type === "assistant") {
938
+ return "assistant";
939
+ }
940
+ return null;
941
+ }
942
+ function geminiPartsFromLegacyItem(item) {
740
943
  if (Array.isArray(item.parts)) return item.parts;
741
944
  const source = item.content ?? item.message;
742
945
  if (typeof source === "string") return [{ text: source }];
743
- if (source && typeof source === "object" && Array.isArray(source.parts)) {
744
- return source.parts;
946
+ if (source && !Array.isArray(source) && typeof source === "object") {
947
+ const objSource = source;
948
+ if (Array.isArray(objSource.parts)) return objSource.parts;
745
949
  }
746
950
  return [];
747
951
  }
748
- function geminiRole(role) {
749
- const normalized = role?.toLowerCase();
750
- if (normalized === "user") return "user";
751
- if (normalized === "model" || normalized === "assistant") return "assistant";
752
- return null;
952
+ function geminiTextFromNewItem(item) {
953
+ const c = item.content;
954
+ if (typeof c === "string") return c.trim();
955
+ if (Array.isArray(c)) {
956
+ return c.map((p) => p && typeof p.text === "string" ? p.text : "").filter(Boolean).join("\n").trim();
957
+ }
958
+ return "";
959
+ }
960
+ function mapGeminiToolStatus(status) {
961
+ switch (status?.toLowerCase()) {
962
+ case "success":
963
+ case "completed":
964
+ case "ok":
965
+ return "completed";
966
+ case "error":
967
+ case "failed":
968
+ case "failure":
969
+ return "error";
970
+ case "cancelled":
971
+ case "canceled":
972
+ return "cancelled";
973
+ case "pending":
974
+ return "pending";
975
+ case "running":
976
+ case "in_progress":
977
+ return "running";
978
+ default:
979
+ return status ? "completed" : "running";
980
+ }
981
+ }
982
+ function extractGeminiToolOutput(tc) {
983
+ if (Array.isArray(tc.result)) {
984
+ for (const r of tc.result) {
985
+ const output = r?.functionResponse?.response?.output;
986
+ if (typeof output === "string" && output) return output;
987
+ if (output !== void 0 && output !== null) {
988
+ try {
989
+ return JSON.stringify(output, null, 2);
990
+ } catch {
991
+ }
992
+ }
993
+ }
994
+ }
995
+ if (typeof tc.resultDisplay === "string" && tc.resultDisplay) {
996
+ return tc.resultDisplay;
997
+ }
998
+ return "";
753
999
  }
754
1000
  var GeminiCliParserV2 = class {
755
1001
  name = "gemini-cli-v2";
@@ -766,41 +1012,134 @@ var GeminiCliParserV2 = class {
766
1012
  }
767
1013
  const history = findGeminiHistory(root);
768
1014
  if (!history) return [];
1015
+ const pendingLegacyCalls = /* @__PURE__ */ new Map();
769
1016
  const ops = [];
770
1017
  history.forEach((item, index) => {
771
- const role = geminiRole(item.role);
772
- if (!role) return;
773
- const parts = geminiPartsFromItem(item);
774
- const timestamp = item.timestamp ?? item.createdAt ?? item.createTime ?? now();
775
- const turnId = item.id ?? `gemini-${index}`;
776
- const blocks = [];
777
- for (const part of parts) {
778
- const text = typeof part.text === "string" ? part.text.trim() : "";
779
- if (text) {
1018
+ const itemOps = this.parseHistoryItem(item, index, pendingLegacyCalls);
1019
+ ops.push(...itemOps);
1020
+ });
1021
+ return ops;
1022
+ }
1023
+ parseHistoryItem(item, index, pendingLegacyCalls) {
1024
+ const timestamp = item.timestamp ?? item.createdAt ?? item.createTime ?? now();
1025
+ const fallbackTurnId = `gemini-${index}`;
1026
+ const turnId = item.id ?? fallbackTurnId;
1027
+ if (typeof item.type === "string") {
1028
+ return this.parseNewSchemaItem(item, turnId, timestamp);
1029
+ }
1030
+ return this.parseLegacySchemaItem(item, turnId, timestamp, pendingLegacyCalls);
1031
+ }
1032
+ parseNewSchemaItem(item, turnId, timestamp) {
1033
+ const role = geminiRoleFromType(item.type);
1034
+ if (!role) return [];
1035
+ const text = geminiTextFromNewItem(item);
1036
+ const blocks = [];
1037
+ if (text) {
1038
+ blocks.push({
1039
+ op: "insert_block",
1040
+ turnId,
1041
+ block: makeTextBlock(genBlockId("txt"), text, true)
1042
+ });
1043
+ }
1044
+ if (role === "assistant" && Array.isArray(item.toolCalls)) {
1045
+ for (const tc of item.toolCalls) {
1046
+ if (!tc.name) continue;
1047
+ const callId = tc.id ?? genBlockId("call");
1048
+ const blockId = genBlockId("tool");
1049
+ const state = mapGeminiToolStatus(tc.status);
1050
+ const isComplete = state === "completed" || state === "error" || state === "cancelled";
1051
+ blocks.push({
1052
+ op: "insert_block",
1053
+ turnId,
1054
+ block: makeToolBlock(blockId, callId, tc.name, state, tc.args, isComplete)
1055
+ });
1056
+ const output = extractGeminiToolOutput(tc);
1057
+ if (output) {
780
1058
  blocks.push({
781
- op: "insert_block",
1059
+ op: "append_tool_output",
782
1060
  turnId,
783
- block: makeTextBlock(genBlockId("txt"), text, true)
1061
+ blockId,
1062
+ text: output
784
1063
  });
785
1064
  }
786
- if (part.functionCall?.name) {
787
- const callId = genBlockId("call");
788
- blocks.push({
789
- op: "insert_block",
790
- turnId,
791
- block: makeToolBlock(
792
- genBlockId("tool"),
793
- callId,
794
- part.functionCall.name,
795
- "running",
796
- part.functionCall.args
797
- )
798
- });
1065
+ }
1066
+ }
1067
+ if (blocks.length === 0) return [];
1068
+ return [
1069
+ {
1070
+ op: "upsert_turn",
1071
+ turn: {
1072
+ turnId,
1073
+ role,
1074
+ startedAt: timestamp,
1075
+ updatedAt: timestamp,
1076
+ isComplete: false
799
1077
  }
800
- if (part.functionResponse?.name) {
801
- const output = part.functionResponse.response;
1078
+ },
1079
+ ...blocks,
1080
+ {
1081
+ op: "complete_turn",
1082
+ turnId,
1083
+ endedAt: timestamp,
1084
+ updatedAt: timestamp
1085
+ }
1086
+ ];
1087
+ }
1088
+ parseLegacySchemaItem(item, turnId, timestamp, pendingLegacyCalls) {
1089
+ const role = geminiRoleFromLegacy(item.role);
1090
+ if (!role) return [];
1091
+ const parts = geminiPartsFromLegacyItem(item);
1092
+ const currentTurnBlocks = [];
1093
+ const priorTurnUpdates = [];
1094
+ for (const part of parts) {
1095
+ const text = typeof part.text === "string" ? part.text.trim() : "";
1096
+ if (text) {
1097
+ currentTurnBlocks.push({
1098
+ op: "insert_block",
1099
+ turnId,
1100
+ block: makeTextBlock(genBlockId("txt"), text, true)
1101
+ });
1102
+ }
1103
+ if (part.functionCall?.name) {
1104
+ const callId = genBlockId("call");
1105
+ const blockId = genBlockId("tool");
1106
+ pendingLegacyCalls.set(part.functionCall.name, { turnId, blockId });
1107
+ currentTurnBlocks.push({
1108
+ op: "insert_block",
1109
+ turnId,
1110
+ block: makeToolBlock(
1111
+ blockId,
1112
+ callId,
1113
+ part.functionCall.name,
1114
+ "running",
1115
+ part.functionCall.args
1116
+ )
1117
+ });
1118
+ }
1119
+ if (part.functionResponse?.name) {
1120
+ const output = part.functionResponse.response;
1121
+ const outputText = output === void 0 || output === null ? "" : typeof output === "string" ? output : JSON.stringify(output, null, 2);
1122
+ const existing = pendingLegacyCalls.get(part.functionResponse.name);
1123
+ if (existing) {
1124
+ pendingLegacyCalls.delete(part.functionResponse.name);
1125
+ priorTurnUpdates.push({
1126
+ op: "set_tool_state",
1127
+ turnId: existing.turnId,
1128
+ blockId: existing.blockId,
1129
+ state: "completed",
1130
+ isComplete: true
1131
+ });
1132
+ if (outputText) {
1133
+ priorTurnUpdates.push({
1134
+ op: "append_tool_output",
1135
+ turnId: existing.turnId,
1136
+ blockId: existing.blockId,
1137
+ text: outputText
1138
+ });
1139
+ }
1140
+ } else {
802
1141
  const blockId = genBlockId("tool");
803
- blocks.push({
1142
+ currentTurnBlocks.push({
804
1143
  op: "insert_block",
805
1144
  turnId,
806
1145
  block: makeToolBlock(
@@ -812,17 +1151,20 @@ var GeminiCliParserV2 = class {
812
1151
  true
813
1152
  )
814
1153
  });
815
- if (output !== void 0) {
816
- blocks.push({
1154
+ if (outputText) {
1155
+ currentTurnBlocks.push({
817
1156
  op: "append_tool_output",
818
1157
  turnId,
819
1158
  blockId,
820
- text: typeof output === "string" ? output : JSON.stringify(output, null, 2)
1159
+ text: outputText
821
1160
  });
822
1161
  }
823
1162
  }
824
1163
  }
825
- if (blocks.length === 0) return;
1164
+ }
1165
+ const ops = [];
1166
+ ops.push(...priorTurnUpdates);
1167
+ if (currentTurnBlocks.length > 0) {
826
1168
  ops.push({
827
1169
  op: "upsert_turn",
828
1170
  turn: {
@@ -833,14 +1175,14 @@ var GeminiCliParserV2 = class {
833
1175
  isComplete: false
834
1176
  }
835
1177
  });
836
- ops.push(...blocks);
1178
+ ops.push(...currentTurnBlocks);
837
1179
  ops.push({
838
1180
  op: "complete_turn",
839
1181
  turnId,
840
1182
  endedAt: timestamp,
841
1183
  updatedAt: timestamp
842
1184
  });
843
- });
1185
+ }
844
1186
  return ops;
845
1187
  }
846
1188
  };
@@ -1158,8 +1500,17 @@ var ClaudeCodeParser = class {
1158
1500
  return "";
1159
1501
  }
1160
1502
  };
1503
+ function extractCodexLineText(content) {
1504
+ if (!Array.isArray(content)) return "";
1505
+ return content.filter(
1506
+ (c) => (c.type === "input_text" || c.type === "output_text" || c.type === "text") && typeof c.text === "string"
1507
+ ).map((c) => c.text).join("\n").trim();
1508
+ }
1161
1509
  var CodexCliParser = class {
1162
1510
  name = "codex-cli";
1511
+ /** call_id → tool name, populated by function_call and consumed by function_call_output. */
1512
+ pendingTools = /* @__PURE__ */ new Map();
1513
+ turnCounter = 0;
1163
1514
  parseLine(line) {
1164
1515
  let obj;
1165
1516
  try {
@@ -1167,26 +1518,106 @@ var CodexCliParser = class {
1167
1518
  } catch {
1168
1519
  return null;
1169
1520
  }
1521
+ if (obj.response_item) {
1522
+ return this.parseLegacy(obj);
1523
+ }
1524
+ if (obj.type === "response_item" && obj.payload) {
1525
+ return this.parseNewEnvelope(obj.payload, obj.timestamp);
1526
+ }
1527
+ return null;
1528
+ }
1529
+ parseLegacy(obj) {
1170
1530
  const item = obj.response_item;
1171
- if (!item) return null;
1172
- if (item.type === "response.output_item.done" && item.item?.type === "message") {
1173
- const textParts = (item.item.content ?? []).filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text);
1174
- const text = textParts.join("\n").trim();
1531
+ if (item.type !== "response.output_item.done") return null;
1532
+ if (item.item?.type !== "message") return null;
1533
+ const text = extractCodexLineText(item.item.content);
1534
+ if (!text) return null;
1535
+ return {
1536
+ turnId: item.id ?? this.genTurnId(obj.timestamp),
1537
+ role: "assistant",
1538
+ content: text,
1539
+ timestamp: obj.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
1540
+ };
1541
+ }
1542
+ parseNewEnvelope(payload, lineTimestamp) {
1543
+ const timestamp = lineTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
1544
+ if (payload.type === "message") {
1545
+ const role = payload.role === "user" ? "user" : payload.role === "assistant" ? "assistant" : null;
1546
+ if (!role) return null;
1547
+ const text = extractCodexLineText(payload.content);
1175
1548
  if (!text) return null;
1176
1549
  return {
1177
- turnId: item.id ?? `codex-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
1178
- role: "assistant",
1550
+ turnId: this.genTurnId(lineTimestamp),
1551
+ role,
1179
1552
  content: text,
1180
- timestamp: obj.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
1553
+ timestamp
1554
+ };
1555
+ }
1556
+ if (payload.type === "function_call") {
1557
+ if (!payload.name) return null;
1558
+ if (payload.call_id) {
1559
+ this.pendingTools.set(payload.call_id, payload.name);
1560
+ this.capPendingTools();
1561
+ }
1562
+ let input;
1563
+ if (typeof payload.arguments === "string" && payload.arguments) {
1564
+ try {
1565
+ input = JSON.parse(payload.arguments);
1566
+ } catch {
1567
+ input = void 0;
1568
+ }
1569
+ }
1570
+ return {
1571
+ turnId: this.genTurnId(lineTimestamp),
1572
+ role: "assistant",
1573
+ content: "",
1574
+ tools: [{ name: payload.name, input, status: "started" }],
1575
+ timestamp
1576
+ };
1577
+ }
1578
+ if (payload.type === "function_call_output") {
1579
+ if (!payload.call_id) return null;
1580
+ const toolName = this.pendingTools.get(payload.call_id) ?? "tool";
1581
+ this.pendingTools.delete(payload.call_id);
1582
+ return {
1583
+ turnId: this.genTurnId(lineTimestamp),
1584
+ role: "user",
1585
+ content: "",
1586
+ tools: [
1587
+ {
1588
+ name: toolName,
1589
+ status: "completed",
1590
+ result: payload.output || void 0
1591
+ }
1592
+ ],
1593
+ timestamp
1181
1594
  };
1182
1595
  }
1183
1596
  return null;
1184
1597
  }
1598
+ genTurnId(timestamp) {
1599
+ this.turnCounter += 1;
1600
+ const ts = timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
1601
+ return `codex-${ts}-${this.turnCounter.toString(36)}`;
1602
+ }
1603
+ capPendingTools() {
1604
+ if (this.pendingTools.size <= 500) return;
1605
+ const keys = [...this.pendingTools.keys()];
1606
+ for (let i = 0; i < 250; i += 1) {
1607
+ this.pendingTools.delete(keys[i]);
1608
+ }
1609
+ }
1185
1610
  };
1186
1611
  function isGeminiHistoryItem2(value) {
1187
1612
  if (!value || typeof value !== "object") return false;
1188
1613
  const item = value;
1189
- return Boolean(item.role && (item.parts || item.content || item.message));
1614
+ if (typeof item.role === "string" && (item.parts !== void 0 || item.content !== void 0 || item.message !== void 0)) {
1615
+ return true;
1616
+ }
1617
+ if (typeof item.type === "string" && (item.content !== void 0 || Array.isArray(item.toolCalls))) {
1618
+ return true;
1619
+ }
1620
+ return false;
1190
1621
  }
1191
1622
  function findGeminiHistory2(value, depth = 0) {
1192
1623
  if (depth > 6 || !value || typeof value !== "object") return null;
@@ -1209,15 +1640,63 @@ function findGeminiHistory2(value, depth = 0) {
1209
1640
  }
1210
1641
  return null;
1211
1642
  }
1212
- function geminiPartsFromItem2(item) {
1643
+ function geminiPartsFromLegacyItem2(item) {
1213
1644
  if (Array.isArray(item.parts)) return item.parts;
1214
1645
  const source = item.content ?? item.message;
1215
1646
  if (typeof source === "string") return [{ text: source }];
1216
- if (source && typeof source === "object" && Array.isArray(source.parts)) {
1217
- return source.parts;
1647
+ if (source && !Array.isArray(source) && typeof source === "object") {
1648
+ const objSource = source;
1649
+ if (Array.isArray(objSource.parts)) return objSource.parts;
1218
1650
  }
1219
1651
  return [];
1220
1652
  }
1653
+ function geminiTextFromNewItem2(item) {
1654
+ const c = item.content;
1655
+ if (typeof c === "string") return c.trim();
1656
+ if (Array.isArray(c)) {
1657
+ return c.map((p) => p && typeof p.text === "string" ? p.text : "").filter(Boolean).join("\n").trim();
1658
+ }
1659
+ return "";
1660
+ }
1661
+ function geminiToolStatusToEvent(status) {
1662
+ switch (status?.toLowerCase()) {
1663
+ case "success":
1664
+ case "completed":
1665
+ case "ok":
1666
+ return "completed";
1667
+ case "error":
1668
+ case "failed":
1669
+ case "failure":
1670
+ return "error";
1671
+ case "cancelled":
1672
+ case "canceled":
1673
+ return "completed";
1674
+ case "pending":
1675
+ case "running":
1676
+ case "in_progress":
1677
+ return "started";
1678
+ default:
1679
+ return status ? "completed" : "started";
1680
+ }
1681
+ }
1682
+ function geminiToolResult(tc) {
1683
+ if (Array.isArray(tc.result)) {
1684
+ for (const r of tc.result) {
1685
+ const output = r?.functionResponse?.response?.output;
1686
+ if (typeof output === "string" && output) return output;
1687
+ if (output !== void 0 && output !== null) {
1688
+ try {
1689
+ return JSON.stringify(output, null, 2);
1690
+ } catch {
1691
+ }
1692
+ }
1693
+ }
1694
+ }
1695
+ if (typeof tc.resultDisplay === "string" && tc.resultDisplay) {
1696
+ return tc.resultDisplay;
1697
+ }
1698
+ return void 0;
1699
+ }
1221
1700
  var GeminiCliParser = class {
1222
1701
  name = "gemini-cli";
1223
1702
  parseLine(line) {
@@ -1235,43 +1714,79 @@ var GeminiCliParser = class {
1235
1714
  if (!history) return [];
1236
1715
  const turns = [];
1237
1716
  history.forEach((item, index) => {
1238
- const normalizedRole = item.role?.toLowerCase();
1239
- const role = normalizedRole === "model" || normalizedRole === "assistant" ? "assistant" : normalizedRole === "user" ? "user" : null;
1240
- if (!role) return;
1241
- const textBlocks = [];
1242
- const tools = [];
1243
- for (const part of geminiPartsFromItem2(item)) {
1244
- if (typeof part.text === "string" && part.text.trim()) {
1245
- textBlocks.push(part.text.trim());
1246
- }
1247
- if (part.functionCall?.name) {
1248
- tools.push({
1249
- name: part.functionCall.name,
1250
- input: part.functionCall.args,
1251
- status: "started"
1252
- });
1253
- }
1254
- if (part.functionResponse?.name) {
1255
- const response = part.functionResponse.response;
1256
- tools.push({
1257
- name: part.functionResponse.name,
1258
- status: "completed",
1259
- result: response === void 0 ? void 0 : typeof response === "string" ? response : JSON.stringify(response, null, 2)
1260
- });
1261
- }
1262
- }
1263
- const content = textBlocks.join("\n").trim();
1264
- if (!content && tools.length === 0) return;
1265
- turns.push({
1266
- turnId: item.id ?? `gemini-${index}`,
1267
- role,
1268
- content,
1269
- tools: tools.length > 0 ? tools : void 0,
1270
- timestamp: item.timestamp ?? item.createdAt ?? item.createTime ?? (/* @__PURE__ */ new Date()).toISOString()
1271
- });
1717
+ const parsed = this.parseHistoryItem(item, index);
1718
+ if (parsed) turns.push(parsed);
1272
1719
  });
1273
1720
  return turns;
1274
1721
  }
1722
+ parseHistoryItem(item, index) {
1723
+ if (typeof item.type === "string") {
1724
+ return this.parseNewSchemaItem(item, index);
1725
+ }
1726
+ return this.parseLegacySchemaItem(item, index);
1727
+ }
1728
+ parseNewSchemaItem(item, index) {
1729
+ const t = item.type;
1730
+ const role = t === "user" ? "user" : t === "gemini" || t === "model" || t === "assistant" ? "assistant" : null;
1731
+ if (!role) return null;
1732
+ const content = geminiTextFromNewItem2(item);
1733
+ const tools = [];
1734
+ if (role === "assistant" && Array.isArray(item.toolCalls)) {
1735
+ for (const tc of item.toolCalls) {
1736
+ if (!tc.name) continue;
1737
+ tools.push({
1738
+ name: tc.name,
1739
+ input: tc.args,
1740
+ status: geminiToolStatusToEvent(tc.status),
1741
+ result: geminiToolResult(tc)
1742
+ });
1743
+ }
1744
+ }
1745
+ if (!content && tools.length === 0) return null;
1746
+ return {
1747
+ turnId: item.id ?? `gemini-${index}`,
1748
+ role,
1749
+ content,
1750
+ tools: tools.length > 0 ? tools : void 0,
1751
+ timestamp: item.timestamp ?? item.createdAt ?? item.createTime ?? (/* @__PURE__ */ new Date()).toISOString()
1752
+ };
1753
+ }
1754
+ parseLegacySchemaItem(item, index) {
1755
+ const normalizedRole = item.role?.toLowerCase();
1756
+ const role = normalizedRole === "model" || normalizedRole === "assistant" ? "assistant" : normalizedRole === "user" ? "user" : null;
1757
+ if (!role) return null;
1758
+ const textBlocks = [];
1759
+ const tools = [];
1760
+ for (const part of geminiPartsFromLegacyItem2(item)) {
1761
+ if (typeof part.text === "string" && part.text.trim()) {
1762
+ textBlocks.push(part.text.trim());
1763
+ }
1764
+ if (part.functionCall?.name) {
1765
+ tools.push({
1766
+ name: part.functionCall.name,
1767
+ input: part.functionCall.args,
1768
+ status: "started"
1769
+ });
1770
+ }
1771
+ if (part.functionResponse?.name) {
1772
+ const response = part.functionResponse.response;
1773
+ tools.push({
1774
+ name: part.functionResponse.name,
1775
+ status: "completed",
1776
+ result: response === void 0 ? void 0 : typeof response === "string" ? response : JSON.stringify(response, null, 2)
1777
+ });
1778
+ }
1779
+ }
1780
+ const content = textBlocks.join("\n").trim();
1781
+ if (!content && tools.length === 0) return null;
1782
+ return {
1783
+ turnId: item.id ?? `gemini-${index}`,
1784
+ role,
1785
+ content,
1786
+ tools: tools.length > 0 ? tools : void 0,
1787
+ timestamp: item.timestamp ?? item.createdAt ?? item.createTime ?? (/* @__PURE__ */ new Date()).toISOString()
1788
+ };
1789
+ }
1275
1790
  };
1276
1791
  function parseProcessPid(sessionId) {
1277
1792
  if (!sessionId.startsWith("process:")) return null;
@@ -2930,9 +3445,20 @@ var BACKPRESSURE_RESUME_CHECK_MS = 100;
2930
3445
  var STOP_DEBOUNCE_MS = 2e3;
2931
3446
  var MAX_CONCURRENT_STREAMS = 20;
2932
3447
  var MAX_SESSION_BUFFER_BYTES = 64 * 1024;
2933
- var ALT_SCREEN_MODE_RE = /\x1b\[\?(?=[0-9;]*(?:47|1047|1048|1049)(?:;|[hl]))[0-9;]*[hl]/g;
3448
+ var PRIVATE_MODE_SEQUENCE_RE = /\x1b\[\?([0-9;]*)([hl])/g;
3449
+ var ALT_SCREEN_PARAMS = /* @__PURE__ */ new Set(["47", "1047", "1048", "1049"]);
2934
3450
  function stripAlternateScreenModeSwitches(data) {
2935
- return data.replace(ALT_SCREEN_MODE_RE, "");
3451
+ return data.replace(
3452
+ PRIVATE_MODE_SEQUENCE_RE,
3453
+ (full, params, action) => {
3454
+ if (!params) return full;
3455
+ const parts = params.split(";");
3456
+ const filtered = parts.filter((p) => !ALT_SCREEN_PARAMS.has(p));
3457
+ if (filtered.length === parts.length) return full;
3458
+ if (filtered.length === 0) return "";
3459
+ return `\x1B[?${filtered.join(";")}${action}`;
3460
+ }
3461
+ );
2936
3462
  }
2937
3463
  var AlternateScreenModeStripper = class {
2938
3464
  pending = "";
@@ -3846,6 +4372,7 @@ var ANSI_KEY_SEQUENCES = /* @__PURE__ */ new Map([
3846
4372
  ["\x1B[5~", "PageUp"],
3847
4373
  ["\x1B[6~", "PageDown"]
3848
4374
  ]);
4375
+ var ANSI_KEY_ENTRIES = Array.from(ANSI_KEY_SEQUENCES.entries());
3849
4376
  function controlKeyName(charCode) {
3850
4377
  if (charCode >= 1 && charCode <= 26) {
3851
4378
  return `C-${String.fromCharCode(charCode + 96)}`;
@@ -3884,8 +4411,12 @@ function encodeTmuxInput(data) {
3884
4411
  literal = "";
3885
4412
  };
3886
4413
  for (let i = 0; i < data.length; i++) {
3887
- const remaining = data.slice(i);
3888
- const matchedSequence = Array.from(ANSI_KEY_SEQUENCES.entries()).find(([sequence]) => remaining.startsWith(sequence));
4414
+ let matchedSequence;
4415
+ if (data.charCodeAt(i) === 27) {
4416
+ matchedSequence = ANSI_KEY_ENTRIES.find(
4417
+ ([sequence]) => data.startsWith(sequence, i)
4418
+ );
4419
+ }
3889
4420
  if (matchedSequence) {
3890
4421
  flushLiteral();
3891
4422
  const [sequence, key] = matchedSequence;
@@ -3973,12 +4504,30 @@ async function sendInputToTmux(sessionId, data) {
3973
4504
  return;
3974
4505
  }
3975
4506
  try {
3976
- for (const segment of encodeTmuxInput(data)) {
3977
- if (segment.kind === "literal") {
3978
- await execTmuxSendKeys(["send-keys", "-t", target, "-l", "--", segment.value]);
3979
- } else {
3980
- await execTmuxSendKeys(["send-keys", "-t", target, "--", segment.value]);
4507
+ const segments = encodeTmuxInput(data);
4508
+ let i = 0;
4509
+ while (i < segments.length) {
4510
+ const seg = segments[i];
4511
+ if (seg.kind === "literal") {
4512
+ await execTmuxSendKeys([
4513
+ "send-keys",
4514
+ "-t",
4515
+ target,
4516
+ "-l",
4517
+ "--",
4518
+ seg.value
4519
+ ]);
4520
+ i += 1;
4521
+ continue;
3981
4522
  }
4523
+ const keys = [seg.value];
4524
+ let j = i + 1;
4525
+ while (j < segments.length && segments[j].kind === "key") {
4526
+ keys.push(segments[j].value);
4527
+ j += 1;
4528
+ }
4529
+ await execTmuxSendKeys(["send-keys", "-t", target, "--", ...keys]);
4530
+ i = j;
3982
4531
  }
3983
4532
  } catch (error) {
3984
4533
  const message = error instanceof Error ? error.message : String(error);
@@ -4227,11 +4776,15 @@ var ConnectionManager = class {
4227
4776
  * the field.
4228
4777
  */
4229
4778
  getRxSeqForViewer(viewerId) {
4230
- const key = typeof viewerId === "string" && viewerId.length > 0 ? viewerId : "_legacy";
4779
+ const isValid = typeof viewerId === "string" && viewerId.length > 0;
4780
+ const key = isValid ? viewerId : "_legacy";
4231
4781
  let tracker = this.rxSeqByViewer.get(key);
4232
4782
  if (!tracker) {
4233
4783
  tracker = new SequenceTracker();
4234
4784
  this.rxSeqByViewer.set(key, tracker);
4785
+ if (!isValid) {
4786
+ log11.warn("rxSeq fell back to _legacy bucket \u2014 portal sent empty viewerId", { typeofViewerId: typeof viewerId });
4787
+ }
4235
4788
  }
4236
4789
  return tracker;
4237
4790
  }
@@ -5066,6 +5619,7 @@ async function installLatestCliFromNpm() {
5066
5619
  // src/control-dispatcher.ts
5067
5620
  var log13 = createLogger("control");
5068
5621
  var TMUX_RESYNC_DEBOUNCE_MS = 150;
5622
+ var TMUX_INPUT_COALESCE_MS = 4;
5069
5623
  function collectProcessTreePids(rootPid, visited = /* @__PURE__ */ new Set()) {
5070
5624
  if (visited.has(rootPid)) return [];
5071
5625
  visited.add(rootPid);
@@ -5108,6 +5662,7 @@ var ControlDispatcher = class {
5108
5662
  options;
5109
5663
  _approvalEnforcer = null;
5110
5664
  tmuxResizeState = /* @__PURE__ */ new Map();
5665
+ tmuxInputBuffers = /* @__PURE__ */ new Map();
5111
5666
  /** Set to true when a reconnect was requested (restart-agent, disconnect). */
5112
5667
  reconnectRequested = false;
5113
5668
  /** Set to false to stop the agent. */
@@ -5359,14 +5914,32 @@ var ControlDispatcher = class {
5359
5914
  if (!sessionId || sessionId.startsWith("pty:")) {
5360
5915
  this.shell.write(data);
5361
5916
  } else if (sessionId.startsWith("tmux:")) {
5362
- sendInputToTmux(sessionId, data).catch(() => {
5363
- });
5917
+ this.queueTmuxInput(sessionId, data);
5364
5918
  } else if (sessionId.startsWith("screen:") || sessionId.startsWith("zellij:")) {
5365
5919
  this.streamer.writeInput(sessionId, data);
5366
5920
  }
5367
5921
  }
5922
+ /** Coalesce per-keystroke input into one `tmux send-keys` spawn. */
5923
+ queueTmuxInput(sessionId, data) {
5924
+ const existing = this.tmuxInputBuffers.get(sessionId);
5925
+ if (existing) {
5926
+ existing.data += data;
5927
+ return;
5928
+ }
5929
+ const buffer = { data, timer: null };
5930
+ buffer.timer = setTimeout(() => this.flushTmuxInput(sessionId), TMUX_INPUT_COALESCE_MS);
5931
+ this.tmuxInputBuffers.set(sessionId, buffer);
5932
+ }
5933
+ flushTmuxInput(sessionId) {
5934
+ const buffer = this.tmuxInputBuffers.get(sessionId);
5935
+ if (!buffer) return;
5936
+ this.tmuxInputBuffers.delete(sessionId);
5937
+ if (buffer.timer) clearTimeout(buffer.timer);
5938
+ sendInputToTmux(sessionId, buffer.data).catch(() => {
5939
+ });
5940
+ }
5368
5941
  /** Handle resize routing. */
5369
- handleResize(cols, rows, sessionId) {
5942
+ handleResize(cols, rows, sessionId, viewerId) {
5370
5943
  if (!sessionId || sessionId.startsWith("pty:")) {
5371
5944
  this.shell.resize(cols, rows);
5372
5945
  } else if (sessionId.startsWith("tmux:")) {
@@ -5388,7 +5961,7 @@ var ControlDispatcher = class {
5388
5961
  const latest = this.tmuxResizeState.get(sessionId);
5389
5962
  if (latest === current) {
5390
5963
  latest.timer = null;
5391
- this.streamer.resyncStream(sessionId);
5964
+ this.streamer.resyncStream(sessionId, viewerId);
5392
5965
  }
5393
5966
  }, TMUX_RESYNC_DEBOUNCE_MS);
5394
5967
  }).catch((error) => {
@@ -6352,7 +6925,8 @@ async function runAgent(rawOptions) {
6352
6925
  return;
6353
6926
  }
6354
6927
  const sid = typeof payload.sessionId === "string" ? payload.sessionId.trim() : "";
6355
- dispatcher.handleResize(payload.cols, payload.rows, sid);
6928
+ const resizeViewerId = typeof payload.viewerId === "string" ? payload.viewerId : void 0;
6929
+ dispatcher.handleResize(payload.cols, payload.rows, sid, resizeViewerId);
6356
6930
  } else if (payload.type === "control" && typeof payload.action === "string") {
6357
6931
  let controlPayload = payload;
6358
6932
  if (typeof payload.enc === "string" && typeof payload.iv === "string" && conn.sessionKey) {
package/dist/sbom.json CHANGED
@@ -1166,8 +1166,8 @@
1166
1166
  {
1167
1167
  "type": "library",
1168
1168
  "name": "ragent-cli",
1169
- "version": "1.11.9",
1170
- "bom-ref": "ragent-live|ragent-cli@1.11.9",
1169
+ "version": "1.11.10",
1170
+ "bom-ref": "ragent-live|ragent-cli@1.11.10",
1171
1171
  "author": "Intellimetrics",
1172
1172
  "description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
1173
1173
  "licenses": [
@@ -1178,7 +1178,7 @@
1178
1178
  }
1179
1179
  }
1180
1180
  ],
1181
- "purl": "pkg:npm/ragent-cli@1.11.9",
1181
+ "purl": "pkg:npm/ragent-cli@1.11.10",
1182
1182
  "externalReferences": [
1183
1183
  {
1184
1184
  "url": "https://github.com/chadlindell/ragent-live/issues",
@@ -1303,7 +1303,7 @@
1303
1303
  "ragent-live|@emnapi/wasi-threads@1.2.1",
1304
1304
  "ragent-live|@pkgjs/parseargs@0.11.0",
1305
1305
  "ragent-live|@tybys/wasm-util@0.10.1",
1306
- "ragent-live|ragent-cli@1.11.9"
1306
+ "ragent-live|ragent-cli@1.11.10"
1307
1307
  ]
1308
1308
  },
1309
1309
  {
@@ -1436,7 +1436,7 @@
1436
1436
  ]
1437
1437
  },
1438
1438
  {
1439
- "ref": "ragent-live|ragent-cli@1.11.9",
1439
+ "ref": "ragent-live|ragent-cli@1.11.10",
1440
1440
  "dependsOn": [
1441
1441
  "ragent-live|@azure/web-pubsub-client@1.0.4",
1442
1442
  "ragent-live|commander@14.0.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ragent-cli",
3
- "version": "1.11.9",
3
+ "version": "1.11.10",
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": {