@ellipsis-dev/sdk 0.36.0 → 0.37.0
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.d.ts +56 -175
- package/dist/index.js +38 -82
- package/dist/store/index.d.ts +11 -25
- package/dist/store/index.js +151 -284
- package/dist/stream/index.d.ts +2 -3
- package/dist/stream/index.js +11 -22
- package/dist/{types-CbePyY-S.d.ts → types-BXbnHOQe.d.ts} +636 -1414
- package/package.json +1 -1
- package/schema/collection_frames.schema.json +87 -211
- package/schema/frames.schema.json +2886 -3653
- package/schema/lifecycle.schema.json +389 -150
- package/schema/openapi.v1.json +650 -1619
package/dist/store/index.js
CHANGED
|
@@ -146,16 +146,19 @@ function codexAppServerContent(event) {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
// src/store/lifecycle.ts
|
|
149
|
-
function environmentOutputStep(
|
|
149
|
+
function environmentOutputStep(source) {
|
|
150
|
+
const payload = source;
|
|
150
151
|
if (typeof payload.step === "string" && payload.step) return payload.step;
|
|
151
152
|
return typeof payload.phase === "string" ? payload.phase : "setup";
|
|
152
153
|
}
|
|
153
|
-
function environmentOutputLines(
|
|
154
|
+
function environmentOutputLines(source) {
|
|
155
|
+
const payload = source;
|
|
154
156
|
return Array.isArray(payload.lines) ? payload.lines.filter(
|
|
155
157
|
(l) => typeof l === "string" && l.trim().length > 0
|
|
156
158
|
) : [];
|
|
157
159
|
}
|
|
158
|
-
function environmentOutputLine(
|
|
160
|
+
function environmentOutputLine(source) {
|
|
161
|
+
const payload = source;
|
|
159
162
|
const lines = environmentOutputLines(payload);
|
|
160
163
|
return lines.length ? lines[lines.length - 1].trim() : null;
|
|
161
164
|
}
|
|
@@ -215,13 +218,16 @@ function environmentPhaseText(payload) {
|
|
|
215
218
|
return label;
|
|
216
219
|
}
|
|
217
220
|
}
|
|
218
|
-
function
|
|
221
|
+
function messageEndedText(source) {
|
|
222
|
+
const payload = source;
|
|
219
223
|
const status = typeof payload.status === "string" ? payload.status : "";
|
|
220
224
|
if (!status || status === "completed") return null;
|
|
221
|
-
const
|
|
222
|
-
|
|
225
|
+
const error = payload.error && typeof payload.error === "object" ? payload.error : {};
|
|
226
|
+
const detail = typeof error.detail === "string" ? error.detail.trim() : "";
|
|
227
|
+
return detail || `Message ${status}`;
|
|
223
228
|
}
|
|
224
|
-
function lifecycleText(recordType,
|
|
229
|
+
function lifecycleText(recordType, source) {
|
|
230
|
+
const payload = source;
|
|
225
231
|
switch (recordType) {
|
|
226
232
|
case "environment_phase":
|
|
227
233
|
return environmentPhaseText(payload);
|
|
@@ -239,12 +245,12 @@ function lifecycleText(recordType, payload) {
|
|
|
239
245
|
if (duration) parts.push(duration);
|
|
240
246
|
return parts.join(", ");
|
|
241
247
|
}
|
|
242
|
-
case "
|
|
248
|
+
case "message_queued":
|
|
243
249
|
return typeof payload.limit === "number" ? `Waiting because ${payload.limit} sessions are already alive on this account` : null;
|
|
244
|
-
case "
|
|
245
|
-
return
|
|
250
|
+
case "message_ended":
|
|
251
|
+
return messageEndedText(payload);
|
|
246
252
|
case "session_closed":
|
|
247
|
-
return "
|
|
253
|
+
return "Session closed";
|
|
248
254
|
default:
|
|
249
255
|
return null;
|
|
250
256
|
}
|
|
@@ -734,97 +740,74 @@ function geminiStreamContent(event) {
|
|
|
734
740
|
// src/store/progress.ts
|
|
735
741
|
function messageProgress(records) {
|
|
736
742
|
const messages = /* @__PURE__ */ new Map();
|
|
737
|
-
const pending = /* @__PURE__ */ new
|
|
738
|
-
const turns = /* @__PURE__ */ new Map();
|
|
743
|
+
const pending = /* @__PURE__ */ new Set();
|
|
739
744
|
let ready = false;
|
|
740
|
-
const
|
|
741
|
-
let
|
|
742
|
-
if (!
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
messages: /* @__PURE__ */ new Set()
|
|
745
|
+
const get = (id) => {
|
|
746
|
+
let message = messages.get(id);
|
|
747
|
+
if (!message) {
|
|
748
|
+
message = {
|
|
749
|
+
phase: "queued",
|
|
750
|
+
timing: { startedAt: null, completedAt: null }
|
|
747
751
|
};
|
|
748
|
-
|
|
752
|
+
messages.set(id, message);
|
|
749
753
|
}
|
|
750
|
-
return
|
|
754
|
+
return message;
|
|
751
755
|
};
|
|
752
|
-
const
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
const turn = getTurn(turnId);
|
|
756
|
-
if (!turn.messages.has(messageId)) {
|
|
757
|
-
turn.messages.add(messageId);
|
|
758
|
-
message.turns.push(turn.timing);
|
|
759
|
-
}
|
|
760
|
-
pending.delete(messageId);
|
|
761
|
-
message.phase = turn.phase;
|
|
762
|
-
};
|
|
763
|
-
const setPending = (turnId, phase) => {
|
|
764
|
-
for (const [id, turn] of pending) {
|
|
765
|
-
if (turnId == null || turn == null || turn === turnId)
|
|
766
|
-
messages.get(id).phase = phase;
|
|
756
|
+
const setPending = (messageId, phase) => {
|
|
757
|
+
for (const id of pending) {
|
|
758
|
+
if (messageId == null || messageId === id) get(id).phase = phase;
|
|
767
759
|
}
|
|
768
760
|
};
|
|
769
761
|
for (const record of records) {
|
|
762
|
+
const recordMessageId = record.message_id ?? null;
|
|
770
763
|
if (record.kind === "platform") {
|
|
771
|
-
const p = record.payload;
|
|
772
|
-
const messageId = typeof p.message_id === "string" ? p.message_id :
|
|
773
|
-
const turnId = typeof p.turn_id === "string" ? p.turn_id : record.turn_id;
|
|
764
|
+
const p = { ...record.payload };
|
|
765
|
+
const messageId = typeof p.message_id === "string" ? p.message_id : typeof p.id === "string" && record.record_type === "message" ? p.id : recordMessageId;
|
|
774
766
|
switch (record.record_type) {
|
|
775
|
-
case "
|
|
767
|
+
case "message":
|
|
776
768
|
if (messageId && !messages.has(messageId)) {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
else pending.set(messageId, turnId ?? null);
|
|
769
|
+
get(messageId);
|
|
770
|
+
pending.add(messageId);
|
|
780
771
|
}
|
|
781
772
|
break;
|
|
782
|
-
case "message_delivered":
|
|
783
|
-
if (messageId && turnId) attach(messageId, turnId);
|
|
784
|
-
break;
|
|
785
773
|
case "message_requeued":
|
|
786
774
|
if (messageId && messages.has(messageId)) {
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
775
|
+
pending.add(messageId);
|
|
776
|
+
const message = get(messageId);
|
|
777
|
+
message.phase = "queued";
|
|
778
|
+
message.timing.startedAt = null;
|
|
779
|
+
message.timing.completedAt = null;
|
|
790
780
|
}
|
|
791
781
|
break;
|
|
792
782
|
case "environment_phase":
|
|
793
783
|
case "environment_output":
|
|
794
784
|
ready = false;
|
|
795
|
-
setPending(
|
|
785
|
+
setPending(recordMessageId, "preparing");
|
|
796
786
|
break;
|
|
797
787
|
case "environment_ready":
|
|
798
788
|
ready = false;
|
|
799
|
-
setPending(
|
|
789
|
+
setPending(recordMessageId, "starting");
|
|
800
790
|
break;
|
|
801
|
-
case "
|
|
802
|
-
if (
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
791
|
+
case "message_started":
|
|
792
|
+
if (messageId) {
|
|
793
|
+
const message = get(messageId);
|
|
794
|
+
message.timing.startedAt = record.created_at;
|
|
795
|
+
message.phase = ready ? "working" : "starting";
|
|
796
|
+
pending.delete(messageId);
|
|
807
797
|
}
|
|
808
798
|
break;
|
|
809
|
-
case "
|
|
810
|
-
if (
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
if (!pending.has(id)) messages.get(id).phase = null;
|
|
816
|
-
}
|
|
817
|
-
for (const [id, pendingTurn] of pending) {
|
|
818
|
-
if (pendingTurn === turnId) {
|
|
819
|
-
pending.delete(id);
|
|
820
|
-
messages.get(id).phase = null;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
799
|
+
case "message_ended":
|
|
800
|
+
if (messageId) {
|
|
801
|
+
const message = get(messageId);
|
|
802
|
+
message.timing.completedAt = record.created_at;
|
|
803
|
+
message.phase = null;
|
|
804
|
+
pending.delete(messageId);
|
|
823
805
|
}
|
|
824
806
|
break;
|
|
825
807
|
case "session_closed":
|
|
826
808
|
ready = false;
|
|
827
|
-
for (const id of pending
|
|
809
|
+
for (const id of pending) get(id).phase = null;
|
|
810
|
+
pending.clear();
|
|
828
811
|
break;
|
|
829
812
|
}
|
|
830
813
|
continue;
|
|
@@ -833,18 +816,15 @@ function messageProgress(records) {
|
|
|
833
816
|
const agentStarted = record.kind === "codex_app_server" && record.payload.method === "turn/started" || record.kind === "codex" && record.payload.type === "turn.started" || record.kind === "gemini" && record.payload.type === "init" || claude != null && (claude.kind === "assistant" || claude.kind === "user" || claude.kind === "system" && claude.subtype === "init");
|
|
834
817
|
if (agentStarted) {
|
|
835
818
|
ready = true;
|
|
836
|
-
if (
|
|
837
|
-
const
|
|
838
|
-
if (
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
819
|
+
if (recordMessageId) {
|
|
820
|
+
const message = get(recordMessageId);
|
|
821
|
+
if (message.timing.completedAt == null) {
|
|
822
|
+
message.timing.startedAt ??= record.created_at;
|
|
823
|
+
message.phase = "working";
|
|
824
|
+
pending.delete(recordMessageId);
|
|
842
825
|
}
|
|
843
826
|
}
|
|
844
827
|
}
|
|
845
|
-
if (record.session_message_id && record.turn_id) {
|
|
846
|
-
attach(record.session_message_id, record.turn_id);
|
|
847
|
-
}
|
|
848
828
|
}
|
|
849
829
|
return messages;
|
|
850
830
|
}
|
|
@@ -862,9 +842,7 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
862
842
|
const openGeminiTools = /* @__PURE__ */ new Map();
|
|
863
843
|
const codexSubagentThreads2 = /* @__PURE__ */ new Set();
|
|
864
844
|
const userTurnByMessageId = /* @__PURE__ */ new Map();
|
|
865
|
-
const
|
|
866
|
-
const unansweredTurnIds = /* @__PURE__ */ new Set();
|
|
867
|
-
const activeMessageByTurnId = /* @__PURE__ */ new Map();
|
|
845
|
+
const unansweredMessageIds = /* @__PURE__ */ new Set();
|
|
868
846
|
let lastDeliveredMessageId;
|
|
869
847
|
let seq = 0;
|
|
870
848
|
const nextKey = (base) => `${base}:${seq++}`;
|
|
@@ -872,15 +850,14 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
872
850
|
let initCount = 0;
|
|
873
851
|
let pendingResume = false;
|
|
874
852
|
const openSessionTurn = (record) => {
|
|
875
|
-
const messageId =
|
|
876
|
-
if (openIdx >= 0 &&
|
|
853
|
+
const messageId = record.message_id ?? lastDeliveredMessageId;
|
|
854
|
+
if (openIdx >= 0 && turns[openIdx].messageId !== messageId) {
|
|
877
855
|
openIdx = -1;
|
|
878
856
|
}
|
|
879
857
|
if (openIdx < 0) {
|
|
880
858
|
turns.push({
|
|
881
859
|
key: nextKey(record.id),
|
|
882
860
|
messageId,
|
|
883
|
-
turnId: record.turn_id ?? void 0,
|
|
884
861
|
role: "assistant",
|
|
885
862
|
nodes: [],
|
|
886
863
|
startedAt: record.created_at,
|
|
@@ -899,7 +876,7 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
899
876
|
const subagentTurn = (record) => {
|
|
900
877
|
for (let i = turns.length - 1; i >= 0; i--) {
|
|
901
878
|
const turn = turns[i];
|
|
902
|
-
if (turn.role === "assistant" && turn.
|
|
879
|
+
if (turn.role === "assistant" && turn.messageId === (record.message_id ?? void 0))
|
|
903
880
|
return turn;
|
|
904
881
|
}
|
|
905
882
|
return null;
|
|
@@ -912,11 +889,10 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
912
889
|
};
|
|
913
890
|
for (const record of records) {
|
|
914
891
|
if (record.kind === "platform") {
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
const body = typeof record.payload.body === "string" ? record.payload.body : "";
|
|
892
|
+
const payload = { ...record.payload };
|
|
893
|
+
const messageId = typeof payload.message_id === "string" ? payload.message_id : record.record_type === "message" && typeof payload.id === "string" ? payload.id : null;
|
|
894
|
+
if (record.record_type === "message" && messageId) {
|
|
895
|
+
const body = typeof payload.body === "string" ? payload.body : "";
|
|
920
896
|
if (!body.trim() || userTurnByMessageId.has(messageId)) continue;
|
|
921
897
|
const turn2 = {
|
|
922
898
|
resumed: false,
|
|
@@ -935,29 +911,23 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
935
911
|
turns.push(turn2);
|
|
936
912
|
continue;
|
|
937
913
|
}
|
|
938
|
-
if (record.record_type === "
|
|
939
|
-
|
|
940
|
-
turnByMessageId.set(messageId, record.payload.turn_id);
|
|
941
|
-
activeMessageByTurnId.set(record.payload.turn_id, messageId);
|
|
942
|
-
lastDeliveredMessageId = messageId;
|
|
943
|
-
}
|
|
914
|
+
if (record.record_type === "message_started" && messageId) {
|
|
915
|
+
lastDeliveredMessageId = messageId;
|
|
944
916
|
continue;
|
|
945
917
|
}
|
|
946
918
|
if (record.record_type === "message_requeued" && messageId) {
|
|
947
|
-
|
|
948
|
-
turnByMessageId.set(messageId, record.payload.requeued_turn_id);
|
|
949
|
-
else turnByMessageId.delete(messageId);
|
|
919
|
+
unansweredMessageIds.delete(messageId);
|
|
950
920
|
continue;
|
|
951
921
|
}
|
|
952
|
-
if (record.record_type === "
|
|
953
|
-
|
|
954
|
-
const text2 = lifecycleText(record.record_type,
|
|
922
|
+
if (record.record_type === "message_ended" && messageId && payload.status !== "completed")
|
|
923
|
+
unansweredMessageIds.add(messageId);
|
|
924
|
+
const text2 = lifecycleText(record.record_type, payload);
|
|
955
925
|
if (!text2) continue;
|
|
956
926
|
const isEnvironment = ENVIRONMENT_RECORD_TYPES.has(record.record_type);
|
|
957
927
|
const isOutput = record.record_type === "environment_output";
|
|
958
|
-
const step = isOutput ? environmentOutputStep(
|
|
959
|
-
const lines = isOutput ? environmentOutputLines(
|
|
960
|
-
const
|
|
928
|
+
const step = isOutput ? environmentOutputStep(payload) : void 0;
|
|
929
|
+
const lines = isOutput ? environmentOutputLines(payload) : void 0;
|
|
930
|
+
const messageKey = record.message_id ?? void 0;
|
|
961
931
|
let foldTarget = null;
|
|
962
932
|
if (isEnvironment) {
|
|
963
933
|
for (let i = turns.length - 1; i >= 0; i--) {
|
|
@@ -966,7 +936,7 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
966
936
|
if (candidate.nodes.some(
|
|
967
937
|
(n) => n.kind === "lifecycle" && ENVIRONMENT_RECORD_TYPES.has(n.recordType)
|
|
968
938
|
)) {
|
|
969
|
-
if (candidate.
|
|
939
|
+
if (candidate.messageId === messageKey) foldTarget = candidate;
|
|
970
940
|
break;
|
|
971
941
|
}
|
|
972
942
|
}
|
|
@@ -984,7 +954,7 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
984
954
|
recordType: record.record_type,
|
|
985
955
|
step,
|
|
986
956
|
lines,
|
|
987
|
-
payload
|
|
957
|
+
payload
|
|
988
958
|
});
|
|
989
959
|
}
|
|
990
960
|
foldTarget.completedAt = record.created_at;
|
|
@@ -992,7 +962,7 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
992
962
|
}
|
|
993
963
|
turns.push({
|
|
994
964
|
key: nextKey(record.id),
|
|
995
|
-
|
|
965
|
+
messageId: messageKey,
|
|
996
966
|
role: "lifecycle",
|
|
997
967
|
nodes: [
|
|
998
968
|
{
|
|
@@ -1002,7 +972,7 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
1002
972
|
recordType: record.record_type,
|
|
1003
973
|
step,
|
|
1004
974
|
lines,
|
|
1005
|
-
payload
|
|
975
|
+
payload
|
|
1006
976
|
}
|
|
1007
977
|
],
|
|
1008
978
|
startedAt: record.created_at,
|
|
@@ -1015,11 +985,7 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
1015
985
|
});
|
|
1016
986
|
continue;
|
|
1017
987
|
}
|
|
1018
|
-
if (record.
|
|
1019
|
-
lastDeliveredMessageId = record.session_message_id;
|
|
1020
|
-
if (record.turn_id)
|
|
1021
|
-
activeMessageByTurnId.set(record.turn_id, record.session_message_id);
|
|
1022
|
-
}
|
|
988
|
+
if (record.message_id) lastDeliveredMessageId = record.message_id;
|
|
1023
989
|
if (record.kind === "codex") {
|
|
1024
990
|
codexEventIntoTurns(record, record.payload, {
|
|
1025
991
|
openSessionTurn,
|
|
@@ -1086,10 +1052,10 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
1086
1052
|
const turnFor = () => {
|
|
1087
1053
|
if (!late) return openSessionTurn(record);
|
|
1088
1054
|
const open = openIdx >= 0 ? turns[openIdx] : null;
|
|
1089
|
-
if (open !== null && open.
|
|
1055
|
+
if (open !== null && open.messageId === (record.message_id ?? void 0)) {
|
|
1090
1056
|
return open;
|
|
1091
1057
|
}
|
|
1092
|
-
if (record.
|
|
1058
|
+
if (record.message_id == null) {
|
|
1093
1059
|
return open ?? newestAssistantTurn() ?? openSessionTurn(record);
|
|
1094
1060
|
}
|
|
1095
1061
|
return subagentTurn(record) ?? openSessionTurn(record);
|
|
@@ -1152,7 +1118,7 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
1152
1118
|
if (typeof content === "string") {
|
|
1153
1119
|
if (content.trim()) {
|
|
1154
1120
|
openIdx = -1;
|
|
1155
|
-
if (record.
|
|
1121
|
+
if (record.message_id != null && userTurnByMessageId.has(record.message_id))
|
|
1156
1122
|
continue;
|
|
1157
1123
|
turns.push({
|
|
1158
1124
|
resumed: false,
|
|
@@ -1237,27 +1203,19 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
1237
1203
|
}
|
|
1238
1204
|
}
|
|
1239
1205
|
}
|
|
1240
|
-
for (const [messageId,
|
|
1241
|
-
|
|
1242
|
-
if (turn) turn.isError = unansweredTurnIds.has(turnId);
|
|
1206
|
+
for (const [messageId, turn] of userTurnByMessageId) {
|
|
1207
|
+
turn.isError = unansweredMessageIds.has(messageId);
|
|
1243
1208
|
}
|
|
1244
1209
|
for (const [messageId, progress] of messageProgress(records)) {
|
|
1245
1210
|
const turn = userTurnByMessageId.get(messageId);
|
|
1246
1211
|
if (turn) turn.progress = progress;
|
|
1247
1212
|
}
|
|
1248
|
-
const
|
|
1249
|
-
(r) => r.kind === "platform" && r.record_type === "
|
|
1250
|
-
);
|
|
1251
|
-
const openingTurnId = initialTurn?.turn_id ?? initialTurn?.payload.turn_id ?? (session?.turn?.index === 0 ? session.turn.id : void 0);
|
|
1252
|
-
const initialReceived = openingTurnId == null ? void 0 : records.find(
|
|
1253
|
-
(r) => r.kind === "platform" && r.record_type === "message_received" && (r.turn_id === openingTurnId || r.payload.turn_id === openingTurnId)
|
|
1213
|
+
const initialMessage = records.find(
|
|
1214
|
+
(r) => r.kind === "platform" && r.record_type === "message" && r.payload.index === 0
|
|
1254
1215
|
);
|
|
1255
|
-
const
|
|
1256
|
-
(r) => r.kind === "platform" && r.record_type === "message_delivered" && r.payload.turn_id === openingTurnId
|
|
1257
|
-
);
|
|
1258
|
-
const initialId = (initialReceived ?? initialDelivery)?.payload.message_id;
|
|
1216
|
+
const initialId = initialMessage?.payload.id;
|
|
1259
1217
|
const opening = initialId ? userTurnByMessageId.get(initialId) : void 0;
|
|
1260
|
-
const includesStart = records.length === 0 ||
|
|
1218
|
+
const includesStart = records.length === 0 || initialMessage != null;
|
|
1261
1219
|
const prompt = (session?.claude_code ?? session?.codex)?.prompt;
|
|
1262
1220
|
if (prompt?.trim() && session && (opening || includesStart)) {
|
|
1263
1221
|
const first = opening ?? {
|
|
@@ -1271,7 +1229,10 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
1271
1229
|
tokens: null,
|
|
1272
1230
|
resumed: false,
|
|
1273
1231
|
isError: false,
|
|
1274
|
-
progress: {
|
|
1232
|
+
progress: {
|
|
1233
|
+
phase: "preparing",
|
|
1234
|
+
timing: { startedAt: null, completedAt: null }
|
|
1235
|
+
}
|
|
1275
1236
|
};
|
|
1276
1237
|
if (opening) turns.splice(turns.indexOf(opening), 1);
|
|
1277
1238
|
first.key = "prompt";
|
|
@@ -1279,7 +1240,7 @@ function groupRecordsToChatTurns(records, session) {
|
|
|
1279
1240
|
turns.unshift(first);
|
|
1280
1241
|
if (!opening) {
|
|
1281
1242
|
for (const turn of turns) {
|
|
1282
|
-
if (turn !== first && turn.role === "user" && turn.progress &&
|
|
1243
|
+
if (turn !== first && turn.role === "user" && turn.progress && turn.progress.timing.startedAt == null) {
|
|
1283
1244
|
turn.progress.phase = turn.progress.phase === null ? null : "queued";
|
|
1284
1245
|
}
|
|
1285
1246
|
}
|
|
@@ -1393,9 +1354,9 @@ function codexEventIntoTurns(record, event, ops) {
|
|
|
1393
1354
|
function recordSlice(records) {
|
|
1394
1355
|
return records;
|
|
1395
1356
|
}
|
|
1396
|
-
function
|
|
1397
|
-
if (record.
|
|
1398
|
-
return typeof record.payload.
|
|
1357
|
+
function messageOf(record) {
|
|
1358
|
+
if (record.message_id != null) return record.message_id;
|
|
1359
|
+
return typeof record.payload.message_id === "string" ? record.payload.message_id : null;
|
|
1399
1360
|
}
|
|
1400
1361
|
function humanDuration(seconds) {
|
|
1401
1362
|
const clamped = Math.max(0, seconds);
|
|
@@ -1417,10 +1378,10 @@ function humanDuration(seconds) {
|
|
|
1417
1378
|
}
|
|
1418
1379
|
function sessionLogText(recordType, payload) {
|
|
1419
1380
|
switch (recordType) {
|
|
1420
|
-
case "
|
|
1421
|
-
return
|
|
1381
|
+
case "message_ended":
|
|
1382
|
+
return messageEndedText(payload);
|
|
1422
1383
|
case "session_closed":
|
|
1423
|
-
return "
|
|
1384
|
+
return "Session closed";
|
|
1424
1385
|
default:
|
|
1425
1386
|
return null;
|
|
1426
1387
|
}
|
|
@@ -1434,10 +1395,10 @@ function awaitingAgentPhase(records) {
|
|
|
1434
1395
|
continue;
|
|
1435
1396
|
}
|
|
1436
1397
|
switch (r.record_type) {
|
|
1437
|
-
case "
|
|
1398
|
+
case "message_started":
|
|
1438
1399
|
inFlight = true;
|
|
1439
1400
|
break;
|
|
1440
|
-
case "
|
|
1401
|
+
case "message_ended":
|
|
1441
1402
|
inFlight = false;
|
|
1442
1403
|
break;
|
|
1443
1404
|
case "environment_phase":
|
|
@@ -1452,37 +1413,34 @@ function awaitingAgentPhase(records) {
|
|
|
1452
1413
|
}
|
|
1453
1414
|
function deliveredUnechoedSends(records) {
|
|
1454
1415
|
const received = /* @__PURE__ */ new Map();
|
|
1455
|
-
const
|
|
1416
|
+
const started = /* @__PURE__ */ new Set();
|
|
1456
1417
|
const unanswered = /* @__PURE__ */ new Set();
|
|
1457
1418
|
const echoed = /* @__PURE__ */ new Set();
|
|
1458
1419
|
for (const r of records) {
|
|
1459
|
-
if (r.
|
|
1460
|
-
|
|
1461
|
-
if (r.record_type === "turn_ended") {
|
|
1462
|
-
if (typeof r.payload.turn_id === "string" && r.payload.status !== "completed")
|
|
1463
|
-
unanswered.add(r.payload.turn_id);
|
|
1420
|
+
if (r.source !== "lifecycle") {
|
|
1421
|
+
if (r.message_id != null) echoed.add(r.message_id);
|
|
1464
1422
|
continue;
|
|
1465
1423
|
}
|
|
1466
|
-
const id = typeof r.payload.
|
|
1424
|
+
const id = messageOf(r) ?? (typeof r.payload.id === "string" ? r.payload.id : null);
|
|
1467
1425
|
if (!id) continue;
|
|
1468
|
-
if (r.record_type === "
|
|
1426
|
+
if (r.record_type === "message") {
|
|
1469
1427
|
if (!received.has(id))
|
|
1470
1428
|
received.set(
|
|
1471
1429
|
id,
|
|
1472
1430
|
typeof r.payload.body === "string" ? r.payload.body : ""
|
|
1473
1431
|
);
|
|
1474
|
-
} else if (r.record_type === "
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1432
|
+
} else if (r.record_type === "message_started") {
|
|
1433
|
+
started.add(id);
|
|
1434
|
+
} else if (r.record_type === "message_requeued") {
|
|
1435
|
+
started.delete(id);
|
|
1436
|
+
} else if (r.record_type === "message_ended") {
|
|
1437
|
+
if (r.payload.status !== "completed") unanswered.add(id);
|
|
1438
|
+
}
|
|
1480
1439
|
}
|
|
1481
1440
|
const out = [];
|
|
1482
1441
|
for (const [id, body] of received) {
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
out.push({ id, body, cancelled: unanswered.has(turnId) });
|
|
1442
|
+
if (!started.has(id) || echoed.has(id)) continue;
|
|
1443
|
+
out.push({ id, body, cancelled: unanswered.has(id) });
|
|
1486
1444
|
}
|
|
1487
1445
|
return out;
|
|
1488
1446
|
}
|
|
@@ -1532,22 +1490,22 @@ function deriveEnvironmentState(records, minFeedSeq) {
|
|
|
1532
1490
|
let readySeconds = null;
|
|
1533
1491
|
let log = [];
|
|
1534
1492
|
let open = /* @__PURE__ */ new Map();
|
|
1535
|
-
let
|
|
1493
|
+
let storyMessage = null;
|
|
1536
1494
|
const push = (record, kind, text2) => {
|
|
1537
1495
|
const entry = { key: `${record.feed_seq}:${log.length}`, kind, text: text2 };
|
|
1538
1496
|
log.push(entry);
|
|
1539
1497
|
return entry;
|
|
1540
1498
|
};
|
|
1541
1499
|
const begin = (record) => {
|
|
1542
|
-
const
|
|
1543
|
-
if (seen &&
|
|
1500
|
+
const message = messageOf(record);
|
|
1501
|
+
if (seen && message !== storyMessage) {
|
|
1544
1502
|
log = [];
|
|
1545
1503
|
open = /* @__PURE__ */ new Map();
|
|
1546
1504
|
done = false;
|
|
1547
1505
|
readySeconds = null;
|
|
1548
1506
|
}
|
|
1549
1507
|
seen = true;
|
|
1550
|
-
|
|
1508
|
+
storyMessage = message;
|
|
1551
1509
|
};
|
|
1552
1510
|
for (const record of records) {
|
|
1553
1511
|
if (record.feed_seq <= minFeedSeq || record.source !== "lifecycle")
|
|
@@ -1603,9 +1561,9 @@ function deriveEnvironmentState(records, minFeedSeq) {
|
|
|
1603
1561
|
done = true;
|
|
1604
1562
|
break;
|
|
1605
1563
|
}
|
|
1606
|
-
case "
|
|
1607
|
-
case "
|
|
1608
|
-
if (seen &&
|
|
1564
|
+
case "message_started":
|
|
1565
|
+
case "message_ended":
|
|
1566
|
+
if (seen && messageOf(record) === storyMessage) done = true;
|
|
1609
1567
|
break;
|
|
1610
1568
|
case "session_closed":
|
|
1611
1569
|
if (seen) done = true;
|
|
@@ -1805,12 +1763,10 @@ function splitLedger(turns) {
|
|
|
1805
1763
|
return { preamble, entries };
|
|
1806
1764
|
}
|
|
1807
1765
|
function entrySeconds(entry, now) {
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
return ms2 + Math.max(0, end - Date.parse(turn.startedAt));
|
|
1813
|
-
}, 0) / 1e3;
|
|
1766
|
+
const timing = entry.progress?.timing;
|
|
1767
|
+
if (timing?.startedAt != null) {
|
|
1768
|
+
const end = timing.completedAt == null ? now : Date.parse(timing.completedAt);
|
|
1769
|
+
return Math.max(0, end - Date.parse(timing.startedAt)) / 1e3;
|
|
1814
1770
|
}
|
|
1815
1771
|
let ms = 0;
|
|
1816
1772
|
for (const turn of entry.turns) {
|
|
@@ -1886,7 +1842,7 @@ function entryBody(entry, {
|
|
|
1886
1842
|
const items = [];
|
|
1887
1843
|
const work = [];
|
|
1888
1844
|
const phase = entry.progress?.phase;
|
|
1889
|
-
const seconds = entry.progress &&
|
|
1845
|
+
const seconds = entry.progress && entry.progress.timing.startedAt == null ? null : entrySeconds(entry, now);
|
|
1890
1846
|
const key = `work:${entry.key}`;
|
|
1891
1847
|
const header = {
|
|
1892
1848
|
key,
|
|
@@ -2056,25 +2012,17 @@ function activitySummary(activities) {
|
|
|
2056
2012
|
|
|
2057
2013
|
// src/store/index.ts
|
|
2058
2014
|
function isConversationOver(session) {
|
|
2059
|
-
return session.
|
|
2015
|
+
return session.status === "closed";
|
|
2060
2016
|
}
|
|
2061
2017
|
function seedTranscriptStore(store, seed) {
|
|
2062
|
-
store.ingest({
|
|
2063
|
-
type: "snapshot",
|
|
2064
|
-
earliest_feed_seq: seed.earliestFeedSeq ?? null,
|
|
2065
|
-
session: seed.session,
|
|
2066
|
-
messages: [...seed.messages ?? []]
|
|
2067
|
-
});
|
|
2018
|
+
store.ingest({ type: "session", session: seed.session });
|
|
2068
2019
|
const ordered = [...seed.records].sort((a, b) => a.feed_seq - b.feed_seq);
|
|
2069
|
-
if (ordered.length)
|
|
2070
|
-
store.ingest({ type: "records_append", records: ordered });
|
|
2020
|
+
if (ordered.length) store.ingest({ type: "records", records: ordered });
|
|
2071
2021
|
}
|
|
2072
2022
|
var EMPTY_SNAPSHOT = {
|
|
2073
2023
|
session: null,
|
|
2074
2024
|
records: [],
|
|
2075
|
-
messages: [],
|
|
2076
2025
|
acknowledgedMessageIds: /* @__PURE__ */ new Set(),
|
|
2077
|
-
historyTruncated: false,
|
|
2078
2026
|
liveText: "",
|
|
2079
2027
|
liveSummary: "",
|
|
2080
2028
|
liveOutputTokens: null,
|
|
@@ -2088,21 +2036,16 @@ var SessionTranscriptStore = class {
|
|
|
2088
2036
|
snapshot = EMPTY_SNAPSHOT;
|
|
2089
2037
|
listeners = /* @__PURE__ */ new Set();
|
|
2090
2038
|
acknowledged = /* @__PURE__ */ new Set();
|
|
2091
|
-
// The
|
|
2092
|
-
//
|
|
2093
|
-
//
|
|
2094
|
-
//
|
|
2095
|
-
inbox = /* @__PURE__ */ new Map();
|
|
2096
|
-
// The resume cursor: the highest feed_seq received via records_append. Only
|
|
2097
|
-
// records advance it — never messages/session frames. streamSession
|
|
2098
|
-
// tracks its own copy; this one seeds a NEW streamSession call (e.g. a
|
|
2099
|
-
// page-level reconnect after unmount) from already-held state.
|
|
2039
|
+
// The resume cursor: the highest feed_seq received via records frames. Only
|
|
2040
|
+
// records advance it — never session frames. streamSession tracks its own
|
|
2041
|
+
// copy; this one seeds a NEW streamSession call (e.g. a page-level
|
|
2042
|
+
// reconnect after unmount) from already-held state.
|
|
2100
2043
|
cursorSeq = 0;
|
|
2101
2044
|
// Derived-turn cache, keyed on the records array identity.
|
|
2102
2045
|
turnsFor = null;
|
|
2103
2046
|
turnsSession = null;
|
|
2104
2047
|
turnsCache = [];
|
|
2105
|
-
|
|
2048
|
+
summaryMessageId = null;
|
|
2106
2049
|
get cursor() {
|
|
2107
2050
|
return this.cursorSeq;
|
|
2108
2051
|
}
|
|
@@ -2124,59 +2067,6 @@ var SessionTranscriptStore = class {
|
|
|
2124
2067
|
}
|
|
2125
2068
|
return this.turnsCache;
|
|
2126
2069
|
};
|
|
2127
|
-
// The current pending set, in feed order — what queued chips render.
|
|
2128
|
-
pendingMessages = () => [...this.inbox.values()].filter((m) => m.status === "pending").sort((a, b) => (a.feed_seq ?? 0) - (b.feed_seq ?? 0));
|
|
2129
|
-
// Fold one lifecycle record into the inbox projection; returns whether the
|
|
2130
|
-
// pending view changed. Unknown fields/types are ignored.
|
|
2131
|
-
applyMessageRecord = (record) => {
|
|
2132
|
-
if (record.kind !== "platform") return false;
|
|
2133
|
-
const payload = record.payload;
|
|
2134
|
-
const messageId = typeof payload.message_id === "string" ? payload.message_id : null;
|
|
2135
|
-
switch (record.record_type) {
|
|
2136
|
-
case "message_received": {
|
|
2137
|
-
if (messageId == null || this.inbox.has(messageId)) return false;
|
|
2138
|
-
this.inbox.set(messageId, {
|
|
2139
|
-
id: messageId,
|
|
2140
|
-
session_id: record.session_id,
|
|
2141
|
-
feed_seq: record.feed_seq,
|
|
2142
|
-
status: "pending",
|
|
2143
|
-
body: typeof payload.body === "string" ? payload.body : "",
|
|
2144
|
-
author: typeof payload.author === "string" ? payload.author : null,
|
|
2145
|
-
sender_attribution_type: typeof payload.sender_attribution_type === "string" ? payload.sender_attribution_type : null,
|
|
2146
|
-
sender_attribution_id: typeof payload.sender_attribution_id === "string" ? payload.sender_attribution_id : null,
|
|
2147
|
-
created_at: record.created_at,
|
|
2148
|
-
delivered_at: null,
|
|
2149
|
-
images: [],
|
|
2150
|
-
turn_id: typeof payload.turn_id === "string" ? payload.turn_id : null
|
|
2151
|
-
});
|
|
2152
|
-
return true;
|
|
2153
|
-
}
|
|
2154
|
-
case "message_delivered": {
|
|
2155
|
-
const existing = messageId != null ? this.inbox.get(messageId) : null;
|
|
2156
|
-
if (!existing || existing.status !== "pending") return false;
|
|
2157
|
-
this.inbox.set(messageId, {
|
|
2158
|
-
...existing,
|
|
2159
|
-
status: "delivered",
|
|
2160
|
-
delivered_at: record.created_at,
|
|
2161
|
-
turn_id: typeof payload.turn_id === "string" ? payload.turn_id : existing.turn_id
|
|
2162
|
-
});
|
|
2163
|
-
return true;
|
|
2164
|
-
}
|
|
2165
|
-
case "message_requeued": {
|
|
2166
|
-
const existing = messageId != null ? this.inbox.get(messageId) : null;
|
|
2167
|
-
if (!existing || existing.status === "pending") return false;
|
|
2168
|
-
this.inbox.set(messageId, {
|
|
2169
|
-
...existing,
|
|
2170
|
-
status: "pending",
|
|
2171
|
-
delivered_at: null,
|
|
2172
|
-
turn_id: typeof payload.requeued_turn_id === "string" ? payload.requeued_turn_id : null
|
|
2173
|
-
});
|
|
2174
|
-
return true;
|
|
2175
|
-
}
|
|
2176
|
-
default:
|
|
2177
|
-
return false;
|
|
2178
|
-
}
|
|
2179
|
-
};
|
|
2180
2070
|
// Ingest one frame from the stream client. Unknown frame types stamp
|
|
2181
2071
|
// liveness and change nothing else.
|
|
2182
2072
|
ingest = (rawFrame) => {
|
|
@@ -2187,45 +2077,29 @@ var SessionTranscriptStore = class {
|
|
|
2187
2077
|
};
|
|
2188
2078
|
const frame = rawFrame;
|
|
2189
2079
|
switch (frame.type) {
|
|
2190
|
-
case "
|
|
2191
|
-
next.liveSummary = "";
|
|
2192
|
-
next.session = frame.session;
|
|
2193
|
-
for (const message of frame.messages) {
|
|
2194
|
-
this.inbox.set(message.id, message);
|
|
2195
|
-
this.acknowledged.add(message.id);
|
|
2196
|
-
}
|
|
2197
|
-
next.messages = this.pendingMessages();
|
|
2198
|
-
next.acknowledgedMessageIds = new Set(this.acknowledged);
|
|
2199
|
-
next.conversationOver = isConversationOver(frame.session);
|
|
2200
|
-
next.historyTruncated = frame.earliest_feed_seq != null && this.cursorSeq < frame.earliest_feed_seq - 1;
|
|
2201
|
-
break;
|
|
2202
|
-
}
|
|
2203
|
-
case "records_append": {
|
|
2080
|
+
case "records": {
|
|
2204
2081
|
const fresh = frame.records.filter((r) => r.feed_seq > this.cursorSeq);
|
|
2205
2082
|
if (fresh.length === 0) break;
|
|
2206
2083
|
this.cursorSeq = fresh[fresh.length - 1].feed_seq;
|
|
2207
2084
|
next.records = [...prev.records, ...fresh];
|
|
2208
2085
|
let acknowledgedNew = false;
|
|
2209
|
-
let inboxChanged = false;
|
|
2210
2086
|
for (const record of fresh) {
|
|
2211
|
-
if (record.
|
|
2212
|
-
this.acknowledged.add(record.
|
|
2087
|
+
if (record.message_id != null) {
|
|
2088
|
+
this.acknowledged.add(record.message_id);
|
|
2213
2089
|
acknowledgedNew = true;
|
|
2214
2090
|
}
|
|
2215
2091
|
if (record.source === "lifecycle") {
|
|
2216
|
-
if (record.record_type === "
|
|
2092
|
+
if (record.record_type === "message_started" && record.message_id !== this.summaryMessageId || record.record_type === "message_ended" && record.message_id === this.summaryMessageId)
|
|
2217
2093
|
next.liveSummary = "";
|
|
2218
|
-
|
|
2219
|
-
if (record.record_type === "message_received" || record.record_type === "message_delivered") {
|
|
2094
|
+
if (record.record_type === "message") {
|
|
2220
2095
|
const payload = record.payload;
|
|
2221
|
-
if (typeof payload.
|
|
2222
|
-
this.acknowledged.add(payload.
|
|
2096
|
+
if (typeof payload.id === "string") {
|
|
2097
|
+
this.acknowledged.add(payload.id);
|
|
2223
2098
|
acknowledgedNew = true;
|
|
2224
2099
|
}
|
|
2225
2100
|
}
|
|
2226
2101
|
}
|
|
2227
2102
|
}
|
|
2228
|
-
if (inboxChanged) next.messages = this.pendingMessages();
|
|
2229
2103
|
if (acknowledgedNew) {
|
|
2230
2104
|
next.acknowledgedMessageIds = new Set(this.acknowledged);
|
|
2231
2105
|
}
|
|
@@ -2236,15 +2110,13 @@ var SessionTranscriptStore = class {
|
|
|
2236
2110
|
case "session": {
|
|
2237
2111
|
next.session = frame.session;
|
|
2238
2112
|
next.conversationOver = isConversationOver(frame.session);
|
|
2239
|
-
if (
|
|
2240
|
-
next.liveSummary = "";
|
|
2241
|
-
}
|
|
2113
|
+
if (frame.session.status !== "running") next.liveSummary = "";
|
|
2242
2114
|
break;
|
|
2243
2115
|
}
|
|
2244
2116
|
case "delta": {
|
|
2245
2117
|
if (frame.kind === "thinking") {
|
|
2246
2118
|
if (next.conversationOver) break;
|
|
2247
|
-
this.
|
|
2119
|
+
this.summaryMessageId = frame.message_id;
|
|
2248
2120
|
if (frame.text != null) next.liveSummary = frame.text;
|
|
2249
2121
|
break;
|
|
2250
2122
|
}
|
|
@@ -2257,11 +2129,6 @@ var SessionTranscriptStore = class {
|
|
|
2257
2129
|
next.liveOutputTokens = frame.output_tokens;
|
|
2258
2130
|
break;
|
|
2259
2131
|
}
|
|
2260
|
-
case "done": {
|
|
2261
|
-
next.conversationOver = true;
|
|
2262
|
-
next.liveSummary = "";
|
|
2263
|
-
break;
|
|
2264
|
-
}
|
|
2265
2132
|
case "error": {
|
|
2266
2133
|
next.liveSummary = "";
|
|
2267
2134
|
break;
|
|
@@ -2318,6 +2185,7 @@ export {
|
|
|
2318
2185
|
lastLines,
|
|
2319
2186
|
layOutItems,
|
|
2320
2187
|
lifecycleText,
|
|
2188
|
+
messageEndedText,
|
|
2321
2189
|
oneLine,
|
|
2322
2190
|
pendingToolCalls,
|
|
2323
2191
|
reasoningSummaryTitle,
|
|
@@ -2334,6 +2202,5 @@ export {
|
|
|
2334
2202
|
tailLines,
|
|
2335
2203
|
toolActivities,
|
|
2336
2204
|
toolResultText,
|
|
2337
|
-
toolRunMembers
|
|
2338
|
-
turnEndedText
|
|
2205
|
+
toolRunMembers
|
|
2339
2206
|
};
|