@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.
@@ -146,16 +146,19 @@ function codexAppServerContent(event) {
146
146
  }
147
147
 
148
148
  // src/store/lifecycle.ts
149
- function environmentOutputStep(payload) {
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(payload) {
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(payload) {
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 turnEndedText(payload) {
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 detail = typeof payload.detail === "string" ? payload.detail.trim() : "";
222
- return detail || `Turn ${status}`;
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, payload) {
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 "turn_queued":
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 "turn_ended":
245
- return turnEndedText(payload);
250
+ case "message_ended":
251
+ return messageEndedText(payload);
246
252
  case "session_closed":
247
- return "Conversation closed";
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 Map();
738
- const turns = /* @__PURE__ */ new Map();
743
+ const pending = /* @__PURE__ */ new Set();
739
744
  let ready = false;
740
- const getTurn = (id) => {
741
- let turn = turns.get(id);
742
- if (!turn) {
743
- turn = {
744
- timing: { id, startedAt: null, completedAt: null },
745
- phase: ready ? "working" : "starting",
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
- turns.set(id, turn);
752
+ messages.set(id, message);
749
753
  }
750
- return turn;
754
+ return message;
751
755
  };
752
- const attach = (messageId, turnId) => {
753
- const message = messages.get(messageId);
754
- if (!message) return;
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 : null;
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 "message_received":
767
+ case "message":
776
768
  if (messageId && !messages.has(messageId)) {
777
- messages.set(messageId, { phase: "queued", turns: [] });
778
- if (turnId && turns.has(turnId)) attach(messageId, turnId);
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
- const requeued = typeof p.requeued_turn_id === "string" ? p.requeued_turn_id : null;
788
- pending.set(messageId, requeued);
789
- messages.get(messageId).phase = "queued";
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(turnId ?? null, "preparing");
785
+ setPending(recordMessageId, "preparing");
796
786
  break;
797
787
  case "environment_ready":
798
788
  ready = false;
799
- setPending(turnId ?? null, "starting");
789
+ setPending(recordMessageId, "starting");
800
790
  break;
801
- case "turn_started":
802
- if (turnId) {
803
- getTurn(turnId).timing.startedAt = record.created_at;
804
- for (const [id, turn] of pending) {
805
- if (turn === turnId) attach(id, turnId);
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 "turn_ended":
810
- if (turnId) {
811
- const turn = getTurn(turnId);
812
- turn.timing.completedAt = record.created_at;
813
- turn.phase = null;
814
- for (const id of turn.messages) {
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.keys()) messages.get(id).phase = null;
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 (record.turn_id) {
837
- const turn = getTurn(record.turn_id);
838
- if (turn.timing.completedAt == null) {
839
- turn.timing.startedAt ??= record.created_at;
840
- turn.phase = "working";
841
- for (const id of turn.messages) messages.get(id).phase = "working";
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 turnByMessageId = /* @__PURE__ */ new Map();
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 = (record.turn_id ? activeMessageByTurnId.get(record.turn_id) : void 0) ?? lastDeliveredMessageId;
876
- if (openIdx >= 0 && (turns[openIdx].turnId !== (record.turn_id ?? void 0) || turns[openIdx].messageId !== messageId)) {
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.turnId === (record.turn_id ?? void 0))
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 messageId = typeof record.payload.message_id === "string" ? record.payload.message_id : null;
916
- if (record.record_type === "message_received" && messageId) {
917
- if (typeof record.payload.turn_id === "string")
918
- turnByMessageId.set(messageId, record.payload.turn_id);
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 === "message_delivered" && messageId) {
939
- if (typeof record.payload.turn_id === "string") {
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
- if (typeof record.payload.requeued_turn_id === "string")
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 === "turn_ended" && typeof record.payload.turn_id === "string" && record.payload.status !== "completed")
953
- unansweredTurnIds.add(record.payload.turn_id);
954
- const text2 = lifecycleText(record.record_type, record.payload);
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(record.payload) : void 0;
959
- const lines = isOutput ? environmentOutputLines(record.payload) : void 0;
960
- const turnId = record.turn_id ?? void 0;
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.turnId === turnId) foldTarget = 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: record.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
- turnId,
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: record.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.session_message_id) {
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.turnId === (record.turn_id ?? void 0)) {
1055
+ if (open !== null && open.messageId === (record.message_id ?? void 0)) {
1090
1056
  return open;
1091
1057
  }
1092
- if (record.turn_id == null) {
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.session_message_id != null && userTurnByMessageId.has(record.session_message_id))
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, turnId] of turnByMessageId) {
1241
- const turn = userTurnByMessageId.get(messageId);
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 initialTurn = records.find(
1249
- (r) => r.kind === "platform" && r.record_type === "turn_started" && r.payload.turn_index === 0
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 initialDelivery = openingTurnId == null ? void 0 : records.find(
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 || initialTurn != null;
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: { phase: "preparing", turns: [] }
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 && !turn.progress.turns.length) {
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 turnOf(record) {
1397
- if (record.turn_id != null) return record.turn_id;
1398
- return typeof record.payload.turn_id === "string" ? record.payload.turn_id : null;
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 "turn_ended":
1421
- return turnEndedText(payload);
1381
+ case "message_ended":
1382
+ return messageEndedText(payload);
1422
1383
  case "session_closed":
1423
- return "Conversation closed";
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 "turn_started":
1398
+ case "message_started":
1438
1399
  inFlight = true;
1439
1400
  break;
1440
- case "turn_ended":
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 delivered = /* @__PURE__ */ new Map();
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.session_message_id != null) echoed.add(r.session_message_id);
1460
- if (r.source !== "lifecycle") continue;
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.message_id === "string" ? r.payload.message_id : null;
1424
+ const id = messageOf(r) ?? (typeof r.payload.id === "string" ? r.payload.id : null);
1467
1425
  if (!id) continue;
1468
- if (r.record_type === "message_received") {
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 === "message_delivered") {
1475
- delivered.set(
1476
- id,
1477
- typeof r.payload.turn_id === "string" ? r.payload.turn_id : ""
1478
- );
1479
- } else if (r.record_type === "message_requeued") delivered.delete(id);
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
- const turnId = delivered.get(id);
1484
- if (turnId === void 0 || echoed.has(id)) continue;
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 storyTurn = null;
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 turn = turnOf(record);
1543
- if (seen && turn !== storyTurn) {
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
- storyTurn = turn;
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 "turn_started":
1607
- case "turn_ended":
1608
- if (seen && turnOf(record) === storyTurn) done = true;
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
- if (entry.progress?.turns.length) {
1809
- return entry.progress.turns.reduce((ms2, turn) => {
1810
- if (turn.startedAt == null) return ms2;
1811
- const end = turn.completedAt == null ? now : Date.parse(turn.completedAt);
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 && !entry.progress.turns.some((turn) => turn.startedAt != null) ? null : entrySeconds(entry, now);
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.conversation.state === "closed";
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 inbox projection: message_received/delivered/requeued
2092
- // RECORDS drive it; the snapshot seeds the pending set at connect. Keyed by
2093
- // message id so a requeue can restore a message the delivered record
2094
- // removed from the pending view.
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
- summaryTurnId = null;
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 "snapshot": {
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.session_message_id != null) {
2212
- this.acknowledged.add(record.session_message_id);
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 === "turn_started" && record.turn_id !== this.summaryTurnId || record.record_type === "turn_ended" && record.turn_id === this.summaryTurnId)
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
- inboxChanged = this.applyMessageRecord(record) || inboxChanged;
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.message_id === "string") {
2222
- this.acknowledged.add(payload.message_id);
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 (next.conversationOver || frame.session.turn?.status !== "running") {
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.summaryTurnId = frame.turn_id;
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
  };