ragent-cli 1.11.8 → 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 +744 -126
- package/dist/sbom.json +5 -5
- package/package.json +1 -1
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.
|
|
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 (
|
|
677
|
-
if (item.
|
|
678
|
-
|
|
679
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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"
|
|
744
|
-
|
|
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
|
|
749
|
-
const
|
|
750
|
-
if (
|
|
751
|
-
if (
|
|
752
|
-
|
|
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
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
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: "
|
|
1059
|
+
op: "append_tool_output",
|
|
782
1060
|
turnId,
|
|
783
|
-
|
|
1061
|
+
blockId,
|
|
1062
|
+
text: output
|
|
784
1063
|
});
|
|
785
1064
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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
|
-
|
|
801
|
-
|
|
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
|
-
|
|
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 (
|
|
816
|
-
|
|
1154
|
+
if (outputText) {
|
|
1155
|
+
currentTurnBlocks.push({
|
|
817
1156
|
op: "append_tool_output",
|
|
818
1157
|
turnId,
|
|
819
1158
|
blockId,
|
|
820
|
-
text:
|
|
1159
|
+
text: outputText
|
|
821
1160
|
});
|
|
822
1161
|
}
|
|
823
1162
|
}
|
|
824
1163
|
}
|
|
825
|
-
|
|
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(...
|
|
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 (
|
|
1172
|
-
if (item.
|
|
1173
|
-
|
|
1174
|
-
|
|
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:
|
|
1178
|
-
role
|
|
1550
|
+
turnId: this.genTurnId(lineTimestamp),
|
|
1551
|
+
role,
|
|
1179
1552
|
content: text,
|
|
1180
|
-
timestamp
|
|
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
|
-
|
|
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
|
|
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"
|
|
1217
|
-
|
|
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
|
|
1239
|
-
|
|
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,10 +3445,39 @@ 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
|
|
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(
|
|
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
|
}
|
|
3463
|
+
var AlternateScreenModeStripper = class {
|
|
3464
|
+
pending = "";
|
|
3465
|
+
write(data) {
|
|
3466
|
+
const combined = this.pending + data;
|
|
3467
|
+
this.pending = "";
|
|
3468
|
+
const match = combined.match(/\x1b(?:\[(?:\?(?:[0-9;]*)?)?)?$/);
|
|
3469
|
+
if (!match || match.index === void 0) {
|
|
3470
|
+
return stripAlternateScreenModeSwitches(combined);
|
|
3471
|
+
}
|
|
3472
|
+
this.pending = combined.slice(match.index);
|
|
3473
|
+
return stripAlternateScreenModeSwitches(combined.slice(0, match.index));
|
|
3474
|
+
}
|
|
3475
|
+
end() {
|
|
3476
|
+
const pending = this.pending;
|
|
3477
|
+
this.pending = "";
|
|
3478
|
+
return stripAlternateScreenModeSwitches(pending);
|
|
3479
|
+
}
|
|
3480
|
+
};
|
|
2937
3481
|
function parsePaneTarget(sessionId) {
|
|
2938
3482
|
if (!sessionId.startsWith("tmux:")) return null;
|
|
2939
3483
|
const rest = sessionId.slice("tmux:".length);
|
|
@@ -3172,6 +3716,11 @@ var SessionStreamer = class {
|
|
|
3172
3716
|
isStreaming(sessionId) {
|
|
3173
3717
|
return this.active.has(sessionId);
|
|
3174
3718
|
}
|
|
3719
|
+
hasViewer(sessionId, viewerId) {
|
|
3720
|
+
const viewerKey = viewerId?.trim() || "";
|
|
3721
|
+
if (!viewerKey) return false;
|
|
3722
|
+
return this.active.get(sessionId)?.viewerIds.has(viewerKey) ?? false;
|
|
3723
|
+
}
|
|
3175
3724
|
/**
|
|
3176
3725
|
* Stop all active streams immediately (no debounce).
|
|
3177
3726
|
*/
|
|
@@ -3253,11 +3802,19 @@ var SessionStreamer = class {
|
|
|
3253
3802
|
* Used when a viewer missed the initial burst (e.g. joinGroup race)
|
|
3254
3803
|
* or reconnected ("Retry stream"). Does NOT restart pipe-pane.
|
|
3255
3804
|
*/
|
|
3256
|
-
resyncStream(sessionId) {
|
|
3805
|
+
resyncStream(sessionId, viewerId) {
|
|
3257
3806
|
const stream = this.active.get(sessionId);
|
|
3258
3807
|
if (!stream || stream.stopped || stream.streamType !== "tmux-pipe") return false;
|
|
3259
3808
|
const { paneTarget, cleanEnv } = stream;
|
|
3260
3809
|
if (!paneTarget || !cleanEnv) return false;
|
|
3810
|
+
const targetViewerId = viewerId?.trim() || void 0;
|
|
3811
|
+
const send = (data) => {
|
|
3812
|
+
if (targetViewerId) {
|
|
3813
|
+
this.sendFn(sessionId, data, targetViewerId);
|
|
3814
|
+
} else {
|
|
3815
|
+
this.sendFn(sessionId, data);
|
|
3816
|
+
}
|
|
3817
|
+
};
|
|
3261
3818
|
stream.initializing = true;
|
|
3262
3819
|
try {
|
|
3263
3820
|
let alternateScreen = false;
|
|
@@ -3277,11 +3834,11 @@ var SessionStreamer = class {
|
|
|
3277
3834
|
{ env: cleanEnv, timeout: 1e4, encoding: "utf-8" }
|
|
3278
3835
|
);
|
|
3279
3836
|
if (scrollback && scrollback.length > 0) {
|
|
3280
|
-
|
|
3837
|
+
send(scrollback.replace(/\n/g, "\r\n"));
|
|
3281
3838
|
}
|
|
3282
3839
|
} catch {
|
|
3283
3840
|
}
|
|
3284
|
-
|
|
3841
|
+
send("\x1B[?1049l\x1B[0m\x1B[2J\x1B[H");
|
|
3285
3842
|
try {
|
|
3286
3843
|
const captureArgs = alternateScreen ? ["capture-pane", "-a", "-t", paneTarget, "-p", "-e"] : ["capture-pane", "-t", paneTarget, "-p", "-e"];
|
|
3287
3844
|
const initial = (0, import_node_child_process2.execFileSync)(
|
|
@@ -3290,7 +3847,7 @@ var SessionStreamer = class {
|
|
|
3290
3847
|
{ env: cleanEnv, timeout: 5e3, encoding: "utf-8" }
|
|
3291
3848
|
);
|
|
3292
3849
|
if (initial) {
|
|
3293
|
-
|
|
3850
|
+
send(stripAlternateScreenModeSwitches(initial.replace(/\n/g, "\r\n").replace(/\r\n$/, "")));
|
|
3294
3851
|
}
|
|
3295
3852
|
} catch {
|
|
3296
3853
|
}
|
|
@@ -3305,7 +3862,7 @@ var SessionStreamer = class {
|
|
|
3305
3862
|
const x = parseInt(parts[0], 10);
|
|
3306
3863
|
const y = parseInt(parts[1], 10);
|
|
3307
3864
|
if (!isNaN(x) && !isNaN(y)) {
|
|
3308
|
-
|
|
3865
|
+
send(`\x1B[${y + 1};${x + 1}H`);
|
|
3309
3866
|
}
|
|
3310
3867
|
}
|
|
3311
3868
|
} catch {
|
|
@@ -3359,7 +3916,8 @@ var SessionStreamer = class {
|
|
|
3359
3916
|
cleanEnv,
|
|
3360
3917
|
ptyProc: null,
|
|
3361
3918
|
straceProc: null,
|
|
3362
|
-
utf8Decoder: new import_node_string_decoder.StringDecoder("utf8")
|
|
3919
|
+
utf8Decoder: new import_node_string_decoder.StringDecoder("utf8"),
|
|
3920
|
+
altScreenStripper: new AlternateScreenModeStripper()
|
|
3363
3921
|
};
|
|
3364
3922
|
this.active.set(sessionId, stream);
|
|
3365
3923
|
try {
|
|
@@ -3383,13 +3941,19 @@ var SessionStreamer = class {
|
|
|
3383
3941
|
if (stream.initializing) {
|
|
3384
3942
|
stream.initBuffer.push(data);
|
|
3385
3943
|
} else {
|
|
3386
|
-
|
|
3944
|
+
const filtered = stream.altScreenStripper.write(data);
|
|
3945
|
+
if (filtered) this.sendFn(sessionId, filtered);
|
|
3387
3946
|
}
|
|
3388
3947
|
});
|
|
3389
3948
|
catProc.on("exit", () => {
|
|
3390
3949
|
if (!stream.stopped) {
|
|
3391
3950
|
const remaining = stream.utf8Decoder.end();
|
|
3392
|
-
if (remaining)
|
|
3951
|
+
if (remaining) {
|
|
3952
|
+
const filtered = stream.altScreenStripper.write(remaining);
|
|
3953
|
+
if (filtered) this.sendFn(sessionId, filtered);
|
|
3954
|
+
}
|
|
3955
|
+
const pending = stream.altScreenStripper.end();
|
|
3956
|
+
if (pending) this.sendFn(sessionId, pending);
|
|
3393
3957
|
this.cleanupStream(stream);
|
|
3394
3958
|
this.active.delete(sessionId);
|
|
3395
3959
|
this.pendingStops.delete(sessionId);
|
|
@@ -3485,7 +4049,8 @@ var SessionStreamer = class {
|
|
|
3485
4049
|
initBuffer: [],
|
|
3486
4050
|
ptyProc: proc,
|
|
3487
4051
|
straceProc: null,
|
|
3488
|
-
utf8Decoder: new import_node_string_decoder.StringDecoder("utf8")
|
|
4052
|
+
utf8Decoder: new import_node_string_decoder.StringDecoder("utf8"),
|
|
4053
|
+
altScreenStripper: new AlternateScreenModeStripper()
|
|
3489
4054
|
};
|
|
3490
4055
|
this.active.set(sessionId, stream);
|
|
3491
4056
|
proc.onData((data) => {
|
|
@@ -3538,7 +4103,8 @@ var SessionStreamer = class {
|
|
|
3538
4103
|
initBuffer: [],
|
|
3539
4104
|
ptyProc: proc,
|
|
3540
4105
|
straceProc: null,
|
|
3541
|
-
utf8Decoder: new import_node_string_decoder.StringDecoder("utf8")
|
|
4106
|
+
utf8Decoder: new import_node_string_decoder.StringDecoder("utf8"),
|
|
4107
|
+
altScreenStripper: new AlternateScreenModeStripper()
|
|
3542
4108
|
};
|
|
3543
4109
|
this.active.set(sessionId, stream);
|
|
3544
4110
|
proc.onData((data) => {
|
|
@@ -3603,7 +4169,8 @@ var SessionStreamer = class {
|
|
|
3603
4169
|
initBuffer: [],
|
|
3604
4170
|
ptyProc: null,
|
|
3605
4171
|
straceProc,
|
|
3606
|
-
utf8Decoder: new import_node_string_decoder.StringDecoder("utf8")
|
|
4172
|
+
utf8Decoder: new import_node_string_decoder.StringDecoder("utf8"),
|
|
4173
|
+
altScreenStripper: new AlternateScreenModeStripper()
|
|
3607
4174
|
};
|
|
3608
4175
|
this.active.set(sessionId, stream);
|
|
3609
4176
|
const writeRegex = /^write\(([12]),\s*"((?:[^"\\]|\\.)*)"/;
|
|
@@ -3805,6 +4372,7 @@ var ANSI_KEY_SEQUENCES = /* @__PURE__ */ new Map([
|
|
|
3805
4372
|
["\x1B[5~", "PageUp"],
|
|
3806
4373
|
["\x1B[6~", "PageDown"]
|
|
3807
4374
|
]);
|
|
4375
|
+
var ANSI_KEY_ENTRIES = Array.from(ANSI_KEY_SEQUENCES.entries());
|
|
3808
4376
|
function controlKeyName(charCode) {
|
|
3809
4377
|
if (charCode >= 1 && charCode <= 26) {
|
|
3810
4378
|
return `C-${String.fromCharCode(charCode + 96)}`;
|
|
@@ -3843,8 +4411,12 @@ function encodeTmuxInput(data) {
|
|
|
3843
4411
|
literal = "";
|
|
3844
4412
|
};
|
|
3845
4413
|
for (let i = 0; i < data.length; i++) {
|
|
3846
|
-
|
|
3847
|
-
|
|
4414
|
+
let matchedSequence;
|
|
4415
|
+
if (data.charCodeAt(i) === 27) {
|
|
4416
|
+
matchedSequence = ANSI_KEY_ENTRIES.find(
|
|
4417
|
+
([sequence]) => data.startsWith(sequence, i)
|
|
4418
|
+
);
|
|
4419
|
+
}
|
|
3848
4420
|
if (matchedSequence) {
|
|
3849
4421
|
flushLiteral();
|
|
3850
4422
|
const [sequence, key] = matchedSequence;
|
|
@@ -3932,12 +4504,30 @@ async function sendInputToTmux(sessionId, data) {
|
|
|
3932
4504
|
return;
|
|
3933
4505
|
}
|
|
3934
4506
|
try {
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
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;
|
|
3940
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;
|
|
3941
4531
|
}
|
|
3942
4532
|
} catch (error) {
|
|
3943
4533
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -4186,11 +4776,15 @@ var ConnectionManager = class {
|
|
|
4186
4776
|
* the field.
|
|
4187
4777
|
*/
|
|
4188
4778
|
getRxSeqForViewer(viewerId) {
|
|
4189
|
-
const
|
|
4779
|
+
const isValid = typeof viewerId === "string" && viewerId.length > 0;
|
|
4780
|
+
const key = isValid ? viewerId : "_legacy";
|
|
4190
4781
|
let tracker = this.rxSeqByViewer.get(key);
|
|
4191
4782
|
if (!tracker) {
|
|
4192
4783
|
tracker = new SequenceTracker();
|
|
4193
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
|
+
}
|
|
4194
4788
|
}
|
|
4195
4789
|
return tracker;
|
|
4196
4790
|
}
|
|
@@ -5025,6 +5619,7 @@ async function installLatestCliFromNpm() {
|
|
|
5025
5619
|
// src/control-dispatcher.ts
|
|
5026
5620
|
var log13 = createLogger("control");
|
|
5027
5621
|
var TMUX_RESYNC_DEBOUNCE_MS = 150;
|
|
5622
|
+
var TMUX_INPUT_COALESCE_MS = 4;
|
|
5028
5623
|
function collectProcessTreePids(rootPid, visited = /* @__PURE__ */ new Set()) {
|
|
5029
5624
|
if (visited.has(rootPid)) return [];
|
|
5030
5625
|
visited.add(rootPid);
|
|
@@ -5067,6 +5662,7 @@ var ControlDispatcher = class {
|
|
|
5067
5662
|
options;
|
|
5068
5663
|
_approvalEnforcer = null;
|
|
5069
5664
|
tmuxResizeState = /* @__PURE__ */ new Map();
|
|
5665
|
+
tmuxInputBuffers = /* @__PURE__ */ new Map();
|
|
5070
5666
|
/** Set to true when a reconnect was requested (restart-agent, disconnect). */
|
|
5071
5667
|
reconnectRequested = false;
|
|
5072
5668
|
/** Set to false to stop the agent. */
|
|
@@ -5318,14 +5914,32 @@ var ControlDispatcher = class {
|
|
|
5318
5914
|
if (!sessionId || sessionId.startsWith("pty:")) {
|
|
5319
5915
|
this.shell.write(data);
|
|
5320
5916
|
} else if (sessionId.startsWith("tmux:")) {
|
|
5321
|
-
|
|
5322
|
-
});
|
|
5917
|
+
this.queueTmuxInput(sessionId, data);
|
|
5323
5918
|
} else if (sessionId.startsWith("screen:") || sessionId.startsWith("zellij:")) {
|
|
5324
5919
|
this.streamer.writeInput(sessionId, data);
|
|
5325
5920
|
}
|
|
5326
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
|
+
}
|
|
5327
5941
|
/** Handle resize routing. */
|
|
5328
|
-
handleResize(cols, rows, sessionId) {
|
|
5942
|
+
handleResize(cols, rows, sessionId, viewerId) {
|
|
5329
5943
|
if (!sessionId || sessionId.startsWith("pty:")) {
|
|
5330
5944
|
this.shell.resize(cols, rows);
|
|
5331
5945
|
} else if (sessionId.startsWith("tmux:")) {
|
|
@@ -5347,7 +5961,7 @@ var ControlDispatcher = class {
|
|
|
5347
5961
|
const latest = this.tmuxResizeState.get(sessionId);
|
|
5348
5962
|
if (latest === current) {
|
|
5349
5963
|
latest.timer = null;
|
|
5350
|
-
this.streamer.resyncStream(sessionId);
|
|
5964
|
+
this.streamer.resyncStream(sessionId, viewerId);
|
|
5351
5965
|
}
|
|
5352
5966
|
}, TMUX_RESYNC_DEBOUNCE_MS);
|
|
5353
5967
|
}).catch((error) => {
|
|
@@ -5558,13 +6172,15 @@ var ControlDispatcher = class {
|
|
|
5558
6172
|
if (!sessionId) return;
|
|
5559
6173
|
const ws = this.connection.activeSocket;
|
|
5560
6174
|
const group = this.connection.activeGroups.privateGroup;
|
|
6175
|
+
const viewerKey = viewerId?.trim() || "";
|
|
5561
6176
|
if (sessionId.startsWith("tmux:") || sessionId.startsWith("screen:") || sessionId.startsWith("zellij:") || sessionId.startsWith("process:")) {
|
|
5562
6177
|
const alreadyStreaming = this.streamer.isStreaming(sessionId);
|
|
6178
|
+
const viewerAlreadyAttached = alreadyStreaming && this.streamer.hasViewer(sessionId, viewerKey);
|
|
5563
6179
|
const started = this.streamer.startStream(sessionId, viewerId ?? void 0);
|
|
5564
6180
|
if (ws && ws.readyState === import_ws4.default.OPEN && group) {
|
|
5565
6181
|
if (started) {
|
|
5566
|
-
if (alreadyStreaming) {
|
|
5567
|
-
this.streamer.resyncStream(sessionId);
|
|
6182
|
+
if (alreadyStreaming && !viewerAlreadyAttached) {
|
|
6183
|
+
this.streamer.resyncStream(sessionId, viewerKey);
|
|
5568
6184
|
}
|
|
5569
6185
|
const dims = this.queryPaneDimensions(sessionId);
|
|
5570
6186
|
sendToGroup(ws, group, { type: "stream-started", sessionId, ...dims });
|
|
@@ -6033,7 +6649,7 @@ async function runAgent(rawOptions) {
|
|
|
6033
6649
|
const outputBuffer = new OutputBuffer();
|
|
6034
6650
|
const ptySessionId = `pty:${options.hostId}`;
|
|
6035
6651
|
const sessionStreamer = new SessionStreamer(
|
|
6036
|
-
(sessionId, data) => {
|
|
6652
|
+
(sessionId, data, viewerId) => {
|
|
6037
6653
|
conn.requestInventorySync();
|
|
6038
6654
|
if (!conn.isReady()) {
|
|
6039
6655
|
sessionStreamer.bufferOutput(sessionId, data);
|
|
@@ -6041,11 +6657,12 @@ async function runAgent(rawOptions) {
|
|
|
6041
6657
|
}
|
|
6042
6658
|
const redactedData = redactStreamingChunk(sessionId, data);
|
|
6043
6659
|
for (const chunk of splitTransportChunks(redactedData)) {
|
|
6660
|
+
const outputPayload = viewerId ? { type: "output", sessionId, viewerId } : { type: "output", sessionId };
|
|
6044
6661
|
if (conn.sessionKey) {
|
|
6045
6662
|
const { enc, iv, seq } = encryptPayload(chunk, conn.sessionKey, conn.txSeq.next());
|
|
6046
|
-
sendToGroup(conn.activeSocket, conn.activeGroups.privateGroup, {
|
|
6663
|
+
sendToGroup(conn.activeSocket, conn.activeGroups.privateGroup, { ...outputPayload, enc, iv, seq });
|
|
6047
6664
|
} else {
|
|
6048
|
-
sendToGroup(conn.activeSocket, conn.activeGroups.privateGroup, {
|
|
6665
|
+
sendToGroup(conn.activeSocket, conn.activeGroups.privateGroup, { ...outputPayload, data: chunk });
|
|
6049
6666
|
}
|
|
6050
6667
|
}
|
|
6051
6668
|
},
|
|
@@ -6308,7 +6925,8 @@ async function runAgent(rawOptions) {
|
|
|
6308
6925
|
return;
|
|
6309
6926
|
}
|
|
6310
6927
|
const sid = typeof payload.sessionId === "string" ? payload.sessionId.trim() : "";
|
|
6311
|
-
|
|
6928
|
+
const resizeViewerId = typeof payload.viewerId === "string" ? payload.viewerId : void 0;
|
|
6929
|
+
dispatcher.handleResize(payload.cols, payload.rows, sid, resizeViewerId);
|
|
6312
6930
|
} else if (payload.type === "control" && typeof payload.action === "string") {
|
|
6313
6931
|
let controlPayload = payload;
|
|
6314
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.
|
|
1170
|
-
"bom-ref": "ragent-live|ragent-cli@1.11.
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|