@ellipsis-dev/sdk 0.30.0 → 0.31.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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SESSION_STREAM_PROTOCOL_VERSION
3
- } from "../chunk-K252XHLI.js";
3
+ } from "../chunk-G2AEYZW2.js";
4
4
 
5
5
  // src/store/claude.ts
6
6
  function claudePayload(record) {
@@ -107,9 +107,8 @@ function codexAppServerContent(event) {
107
107
  // src/store/progress.ts
108
108
  function messageProgress(records) {
109
109
  const messages = /* @__PURE__ */ new Map();
110
- const pending = /* @__PURE__ */ new Set();
110
+ const pending = /* @__PURE__ */ new Map();
111
111
  const turns = /* @__PURE__ */ new Map();
112
- let starting = "queued";
113
112
  let ready = false;
114
113
  const getTurn = (id) => {
115
114
  let turn = turns.get(id);
@@ -123,9 +122,6 @@ function messageProgress(records) {
123
122
  }
124
123
  return turn;
125
124
  };
126
- const updatePending = () => {
127
- for (const id of pending) messages.get(id).phase = starting;
128
- };
129
125
  const attach = (messageId, turnId) => {
130
126
  const message = messages.get(messageId);
131
127
  if (!message) return;
@@ -136,8 +132,12 @@ function messageProgress(records) {
136
132
  }
137
133
  pending.delete(messageId);
138
134
  message.phase = turn.phase;
139
- starting = "queued";
140
- updatePending();
135
+ };
136
+ const setPending = (turnId, phase) => {
137
+ for (const [id, turn] of pending) {
138
+ if (turnId == null || turn == null || turn === turnId)
139
+ messages.get(id).phase = phase;
140
+ }
141
141
  };
142
142
  for (const record of records) {
143
143
  if (record.kind === "platform") {
@@ -147,8 +147,9 @@ function messageProgress(records) {
147
147
  switch (record.record_type) {
148
148
  case "message_received":
149
149
  if (messageId && !messages.has(messageId)) {
150
- messages.set(messageId, { phase: starting, turns: [] });
151
- pending.add(messageId);
150
+ messages.set(messageId, { phase: "queued", turns: [] });
151
+ if (turnId && turns.has(turnId)) attach(messageId, turnId);
152
+ else pending.set(messageId, turnId ?? null);
152
153
  }
153
154
  break;
154
155
  case "message_delivered":
@@ -156,25 +157,29 @@ function messageProgress(records) {
156
157
  break;
157
158
  case "message_requeued":
158
159
  if (messageId && messages.has(messageId)) {
159
- pending.add(messageId);
160
+ const requeued = typeof p.requeued_turn_id === "string" ? p.requeued_turn_id : null;
161
+ pending.set(messageId, requeued);
160
162
  messages.get(messageId).phase = "queued";
161
163
  }
162
164
  break;
163
- case "session_starting":
164
- case "session_retrying":
165
+ case "environment_phase":
166
+ case "environment_output":
165
167
  ready = false;
166
- starting = typeof p.wake_index === "number" && p.wake_index > 0 ? "waking" : "starting";
167
- updatePending();
168
+ setPending(turnId ?? null, "preparing");
168
169
  break;
169
- case "session_resumed":
170
- starting = "starting";
171
- updatePending();
170
+ case "environment_ready":
171
+ ready = false;
172
+ setPending(turnId ?? null, "starting");
172
173
  break;
173
174
  case "turn_started":
174
- if (turnId) getTurn(turnId).timing.startedAt = record.created_at;
175
+ if (turnId) {
176
+ getTurn(turnId).timing.startedAt = record.created_at;
177
+ for (const [id, turn] of pending) {
178
+ if (turn === turnId) attach(id, turnId);
179
+ }
180
+ }
175
181
  break;
176
- case "turn_completed":
177
- case "turn_failed":
182
+ case "turn_ended":
178
183
  if (turnId) {
179
184
  const turn = getTurn(turnId);
180
185
  turn.timing.completedAt = record.created_at;
@@ -182,15 +187,17 @@ function messageProgress(records) {
182
187
  for (const id of turn.messages) {
183
188
  if (!pending.has(id)) messages.get(id).phase = null;
184
189
  }
190
+ for (const [id, pendingTurn] of pending) {
191
+ if (pendingTurn === turnId) {
192
+ pending.delete(id);
193
+ messages.get(id).phase = null;
194
+ }
195
+ }
185
196
  }
186
- starting = "queued";
187
197
  break;
188
- case "session_idle":
189
198
  case "session_closed":
190
- case "session_cancelled":
191
199
  ready = false;
192
- starting = "queued";
193
- for (const id of pending) messages.get(id).phase = null;
200
+ for (const id of pending.keys()) messages.get(id).phase = null;
194
201
  break;
195
202
  }
196
203
  continue;
@@ -216,17 +223,17 @@ function messageProgress(records) {
216
223
  }
217
224
 
218
225
  // src/store/lifecycle.ts
219
- function sandboxOutputStep(payload) {
226
+ function environmentOutputStep(payload) {
220
227
  if (typeof payload.step === "string" && payload.step) return payload.step;
221
228
  return typeof payload.phase === "string" ? payload.phase : "setup";
222
229
  }
223
- function sandboxOutputLines(payload) {
230
+ function environmentOutputLines(payload) {
224
231
  return Array.isArray(payload.lines) ? payload.lines.filter(
225
232
  (l) => typeof l === "string" && l.trim().length > 0
226
233
  ) : [];
227
234
  }
228
- function sandboxOutputLine(payload) {
229
- const lines = sandboxOutputLines(payload);
235
+ function environmentOutputLine(payload) {
236
+ const lines = environmentOutputLines(payload);
230
237
  return lines.length ? lines[lines.length - 1].trim() : null;
231
238
  }
232
239
  function cacheTierLabel(tier) {
@@ -241,7 +248,7 @@ function cacheTierLabel(tier) {
241
248
  return null;
242
249
  }
243
250
  }
244
- function sandboxPhaseLabel(phase) {
251
+ function environmentPhaseLabel(phase) {
245
252
  switch (phase) {
246
253
  case "image":
247
254
  return "Preparing image";
@@ -263,8 +270,8 @@ function durationLabel(ms) {
263
270
  if (typeof ms !== "number" || !isFinite(ms) || ms < 0) return null;
264
271
  return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${Math.round(ms)}ms`;
265
272
  }
266
- function sandboxPhaseText(payload) {
267
- const label = sandboxPhaseLabel(payload.phase);
273
+ function environmentPhaseText(payload) {
274
+ const label = environmentPhaseLabel(payload.phase);
268
275
  const detail = payload.detail && typeof payload.detail === "object" ? payload.detail : {};
269
276
  switch (payload.status) {
270
277
  case "started":
@@ -285,48 +292,34 @@ function sandboxPhaseText(payload) {
285
292
  return label;
286
293
  }
287
294
  }
295
+ function turnEndedText(payload) {
296
+ const status = typeof payload.status === "string" ? payload.status : "";
297
+ if (!status || status === "completed") return null;
298
+ const detail = typeof payload.detail === "string" ? payload.detail.trim() : "";
299
+ return detail || `Turn ${status}`;
300
+ }
288
301
  function lifecycleText(recordType, payload) {
289
302
  switch (recordType) {
290
- case "session_scheduled": {
291
- const config = typeof payload.config_name === "string" ? payload.config_name : null;
292
- return config ? `Session scheduled, ${config}` : "Session scheduled";
293
- }
294
- case "session_starting": {
295
- const attempt = typeof payload.attempt === "number" ? payload.attempt : 0;
296
- if (attempt > 0) return "Retrying\u2026";
297
- const wakeIndex = typeof payload.wake_index === "number" ? payload.wake_index : 0;
298
- return wakeIndex > 0 ? "Waking the session\u2026" : "Session starting\u2026";
299
- }
300
- case "session_retrying":
301
- return typeof payload.reason === "string" && payload.reason ? `Retrying, ${payload.reason}` : "Retrying after a transient error\u2026";
302
- case "sandbox_starting":
303
- return "Starting sandbox\u2026";
304
- case "sandbox_phase":
305
- return sandboxPhaseText(payload);
306
- case "sandbox_output": {
307
- const last = sandboxOutputLine(payload);
308
- return last ? `${sandboxOutputStep(payload)}, ${last}` : null;
303
+ case "environment_phase":
304
+ return environmentPhaseText(payload);
305
+ case "environment_output": {
306
+ const last = environmentOutputLine(payload);
307
+ return last ? `${environmentOutputStep(payload)}, ${last}` : null;
309
308
  }
310
- case "sandbox_ready": {
309
+ case "environment_ready": {
311
310
  const repos = Array.isArray(payload.repositories) ? payload.repositories.filter(
312
311
  (r) => typeof r === "string"
313
312
  ) : [];
314
- const parts = ["Sandbox ready"];
313
+ const parts = ["Environment ready"];
315
314
  if (repos.length) parts.push(repos.join(", "));
316
- const tier = cacheTierLabel(payload.cache_tier);
317
- if (tier) parts.push(tier);
315
+ const duration = durationLabel(payload.duration_ms);
316
+ if (duration) parts.push(duration);
318
317
  return parts.join(", ");
319
318
  }
320
- case "session_resumed":
321
- return "Resumed the conversation";
322
- case "session_idle":
323
- return "Idle \u2014 your next message wakes it";
319
+ case "turn_ended":
320
+ return turnEndedText(payload);
324
321
  case "session_closed":
325
322
  return "Conversation closed";
326
- case "session_cancelled": {
327
- const reason = typeof payload.reason === "string" ? payload.reason : null;
328
- return reason ? `Session cancelled, ${reason}` : "Session cancelled";
329
- }
330
323
  default:
331
324
  return null;
332
325
  }
@@ -637,7 +630,7 @@ function recordToItems(record, keyBase, options = {}) {
637
630
  }
638
631
  }
639
632
  function isConnectVisibleRecord(record) {
640
- return record.source !== "lifecycle" || record.record_type === "sandbox_ready";
633
+ return record.source !== "lifecycle" || record.record_type === "environment_ready";
641
634
  }
642
635
  function pendingToolCalls(items) {
643
636
  let pending = [];
@@ -726,12 +719,8 @@ function foldCosts(events) {
726
719
  }
727
720
  function statusActivityText(status) {
728
721
  switch (status) {
729
- case "scheduled":
730
- return "Waiting for a worker";
731
- case "starting":
732
- return "Starting sandbox";
733
- case "retrying":
734
- return "Retrying after a transient error";
722
+ case "pending":
723
+ return "Waiting to start";
735
724
  default:
736
725
  return null;
737
726
  }
@@ -739,18 +728,17 @@ function statusActivityText(status) {
739
728
 
740
729
  // src/store/chatTurns.ts
741
730
  var isInitEvent = (data) => data.kind === "system" && data.subtype === "init";
742
- var SANDBOX_RECORD_TYPES = /* @__PURE__ */ new Set([
743
- "sandbox_starting",
744
- "sandbox_phase",
745
- "sandbox_output",
746
- "sandbox_ready"
731
+ var ENVIRONMENT_RECORD_TYPES = /* @__PURE__ */ new Set([
732
+ "environment_phase",
733
+ "environment_output",
734
+ "environment_ready"
747
735
  ]);
748
736
  function groupRecordsToChatTurns(records, session) {
749
737
  const turns = [];
750
738
  const toolNodeByUseId = /* @__PURE__ */ new Map();
751
739
  const userTurnByMessageId = /* @__PURE__ */ new Map();
752
- const deliveredTurnByMessageId = /* @__PURE__ */ new Map();
753
- const failedTurnIds = /* @__PURE__ */ new Set();
740
+ const turnByMessageId = /* @__PURE__ */ new Map();
741
+ const unansweredTurnIds = /* @__PURE__ */ new Set();
754
742
  const activeMessageByTurnId = /* @__PURE__ */ new Map();
755
743
  let lastDeliveredMessageId;
756
744
  let seq = 0;
@@ -787,6 +775,8 @@ function groupRecordsToChatTurns(records, session) {
787
775
  if (record.kind === "platform") {
788
776
  const messageId = typeof record.payload.message_id === "string" ? record.payload.message_id : null;
789
777
  if (record.record_type === "message_received" && messageId) {
778
+ if (typeof record.payload.turn_id === "string")
779
+ turnByMessageId.set(messageId, record.payload.turn_id);
790
780
  const body = typeof record.payload.body === "string" ? record.payload.body : "";
791
781
  if (!body.trim() || userTurnByMessageId.has(messageId)) continue;
792
782
  const turn2 = {
@@ -808,43 +798,43 @@ function groupRecordsToChatTurns(records, session) {
808
798
  }
809
799
  if (record.record_type === "message_delivered" && messageId) {
810
800
  if (typeof record.payload.turn_id === "string") {
811
- deliveredTurnByMessageId.set(messageId, record.payload.turn_id);
801
+ turnByMessageId.set(messageId, record.payload.turn_id);
812
802
  activeMessageByTurnId.set(record.payload.turn_id, messageId);
813
803
  lastDeliveredMessageId = messageId;
814
804
  }
815
805
  continue;
816
806
  }
817
807
  if (record.record_type === "message_requeued" && messageId) {
818
- deliveredTurnByMessageId.delete(messageId);
819
- continue;
820
- }
821
- if (record.record_type === "turn_failed") {
822
- if (typeof record.payload.turn_id === "string")
823
- failedTurnIds.add(record.payload.turn_id);
808
+ if (typeof record.payload.requeued_turn_id === "string")
809
+ turnByMessageId.set(messageId, record.payload.requeued_turn_id);
810
+ else turnByMessageId.delete(messageId);
824
811
  continue;
825
812
  }
813
+ if (record.record_type === "turn_ended" && typeof record.payload.turn_id === "string" && record.payload.status !== "completed")
814
+ unansweredTurnIds.add(record.payload.turn_id);
826
815
  const text2 = lifecycleText(record.record_type, record.payload);
827
816
  if (!text2) continue;
828
- const isSandbox = SANDBOX_RECORD_TYPES.has(record.record_type);
829
- const isSandboxOutput = record.record_type === "sandbox_output";
830
- const step = isSandboxOutput ? sandboxOutputStep(record.payload) : void 0;
831
- const lines = isSandboxOutput ? sandboxOutputLines(record.payload) : void 0;
817
+ const isEnvironment = ENVIRONMENT_RECORD_TYPES.has(record.record_type);
818
+ const isOutput = record.record_type === "environment_output";
819
+ const step = isOutput ? environmentOutputStep(record.payload) : void 0;
820
+ const lines = isOutput ? environmentOutputLines(record.payload) : void 0;
821
+ const turnId = record.turn_id ?? void 0;
832
822
  let foldTarget = null;
833
- if (isSandbox && record.record_type !== "sandbox_starting") {
823
+ if (isEnvironment) {
834
824
  for (let i = turns.length - 1; i >= 0; i--) {
835
825
  const candidate = turns[i];
836
826
  if (candidate.role !== "lifecycle") break;
837
827
  if (candidate.nodes.some(
838
- (n) => n.kind === "lifecycle" && SANDBOX_RECORD_TYPES.has(n.recordType)
828
+ (n) => n.kind === "lifecycle" && ENVIRONMENT_RECORD_TYPES.has(n.recordType)
839
829
  )) {
840
- foldTarget = candidate;
830
+ if (candidate.turnId === turnId) foldTarget = candidate;
841
831
  break;
842
832
  }
843
833
  }
844
834
  }
845
835
  if (foldTarget) {
846
836
  const last = foldTarget.nodes[foldTarget.nodes.length - 1];
847
- if (isSandboxOutput && last && last.kind === "lifecycle" && last.recordType === "sandbox_output" && last.step === step) {
837
+ if (isOutput && last && last.kind === "lifecycle" && last.recordType === "environment_output" && last.step === step) {
848
838
  last.text = text2;
849
839
  last.lines = [...last.lines ?? [], ...lines ?? []];
850
840
  } else {
@@ -863,6 +853,7 @@ function groupRecordsToChatTurns(records, session) {
863
853
  }
864
854
  turns.push({
865
855
  key: nextKey(record.id),
856
+ turnId,
866
857
  role: "lifecycle",
867
858
  nodes: [
868
859
  {
@@ -1049,9 +1040,9 @@ function groupRecordsToChatTurns(records, session) {
1049
1040
  }
1050
1041
  }
1051
1042
  }
1052
- for (const [messageId, turnId] of deliveredTurnByMessageId) {
1043
+ for (const [messageId, turnId] of turnByMessageId) {
1053
1044
  const turn = userTurnByMessageId.get(messageId);
1054
- if (turn) turn.isError = failedTurnIds.has(turnId);
1045
+ if (turn) turn.isError = unansweredTurnIds.has(turnId);
1055
1046
  }
1056
1047
  for (const [messageId, progress] of messageProgress(records)) {
1057
1048
  const turn = userTurnByMessageId.get(messageId);
@@ -1060,34 +1051,34 @@ function groupRecordsToChatTurns(records, session) {
1060
1051
  const initialTurn = records.find(
1061
1052
  (r) => r.kind === "platform" && r.record_type === "turn_started" && r.payload.turn_index === 0
1062
1053
  );
1063
- const initialDelivery = initialTurn && records.find(
1064
- (r) => r.kind === "platform" && r.record_type === "message_delivered" && r.payload.turn_id === (initialTurn.turn_id ?? initialTurn.payload.turn_id)
1054
+ const openingTurnId = initialTurn?.turn_id ?? initialTurn?.payload.turn_id ?? (session?.turn?.index === 0 ? session.turn.id : void 0);
1055
+ const initialReceived = openingTurnId == null ? void 0 : records.find(
1056
+ (r) => r.kind === "platform" && r.record_type === "message_received" && (r.turn_id === openingTurnId || r.payload.turn_id === openingTurnId)
1065
1057
  );
1066
- const initialReceived = initialTurn && records.find(
1067
- (r) => r.kind === "platform" && r.record_type === "message_received" && r.turn_id === (initialTurn.turn_id ?? initialTurn.payload.turn_id)
1058
+ const initialDelivery = openingTurnId == null ? void 0 : records.find(
1059
+ (r) => r.kind === "platform" && r.record_type === "message_delivered" && r.payload.turn_id === openingTurnId
1068
1060
  );
1069
- const initialMessage = initialReceived ?? initialDelivery;
1070
- const initialId = initialMessage?.kind === "platform" && typeof initialMessage.payload.message_id === "string" ? initialMessage.payload.message_id : void 0;
1061
+ const initialId = (initialReceived ?? initialDelivery)?.payload.message_id;
1071
1062
  const opening = initialId ? userTurnByMessageId.get(initialId) : void 0;
1072
- const includesStart = records.length === 0 || records.some((r) => r.record_type === "session_scheduled");
1063
+ const includesStart = records.length === 0 || initialTurn != null;
1073
1064
  const prompt = (session?.claude_code ?? session?.codex)?.prompt;
1074
1065
  if (prompt?.trim() && session && (opening || includesStart)) {
1075
1066
  const first = opening ?? {
1076
1067
  key: "prompt",
1077
1068
  role: "user",
1078
1069
  nodes: [{ key: "prompt", kind: "user", text: prompt }],
1079
- startedAt: session.lifecycle.timestamps.created_at,
1080
- completedAt: session.lifecycle.timestamps.created_at,
1070
+ startedAt: session.created_at,
1071
+ completedAt: session.created_at,
1081
1072
  durationMs: null,
1082
1073
  costUsd: null,
1083
1074
  tokens: null,
1084
1075
  resumed: false,
1085
1076
  isError: false,
1086
- progress: { phase: "starting", turns: [] }
1077
+ progress: { phase: "preparing", turns: [] }
1087
1078
  };
1088
1079
  if (opening) turns.splice(turns.indexOf(opening), 1);
1089
1080
  first.key = "prompt";
1090
- first.startedAt = session.lifecycle.timestamps.created_at;
1081
+ first.startedAt = session.created_at;
1091
1082
  turns.unshift(first);
1092
1083
  if (!opening) {
1093
1084
  for (const turn of turns) {
@@ -1205,6 +1196,10 @@ function codexEventIntoTurns(record, event, ops) {
1205
1196
  function recordSlice(records) {
1206
1197
  return records;
1207
1198
  }
1199
+ function turnOf(record) {
1200
+ if (record.turn_id != null) return record.turn_id;
1201
+ return typeof record.payload.turn_id === "string" ? record.payload.turn_id : null;
1202
+ }
1208
1203
  function humanDuration(seconds) {
1209
1204
  const clamped = Math.max(0, seconds);
1210
1205
  if (clamped === 0) return "0s";
@@ -1223,26 +1218,12 @@ function humanDuration(seconds) {
1223
1218
  if (s > 0 || bits.length === 0) bits.push(`${s}s`);
1224
1219
  return bits.join(" ");
1225
1220
  }
1226
- var SESSION_WAKING = "Session waking";
1227
1221
  function sessionLogText(recordType, payload) {
1228
- const p = payload;
1229
1222
  switch (recordType) {
1230
- case "session_idle":
1231
- return "Session asleep";
1232
- case "session_starting": {
1233
- const wake = typeof p.wake_index === "number" ? p.wake_index : 0;
1234
- const attempt = typeof p.attempt === "number" ? p.attempt : 0;
1235
- if (attempt > 0) return "Restarting the sandbox after a transient error\u2026";
1236
- return wake > 0 ? SESSION_WAKING : null;
1237
- }
1238
- case "session_retrying":
1239
- return typeof p.reason === "string" && p.reason ? `Retrying, ${p.reason}` : "Retrying after a transient error\u2026";
1240
- case "session_resumed":
1241
- return "Session awake";
1242
- case "session_cancelled": {
1243
- const reason = typeof p.reason === "string" && p.reason ? `, ${p.reason}` : "";
1244
- return `Session cancelled${reason}`;
1245
- }
1223
+ case "turn_ended":
1224
+ return turnEndedText(payload);
1225
+ case "session_closed":
1226
+ return "Conversation closed";
1246
1227
  default:
1247
1228
  return null;
1248
1229
  }
@@ -1253,14 +1234,20 @@ function awaitingAgentPhase(records) {
1253
1234
  for (const r of records) {
1254
1235
  if (r.source !== "lifecycle") {
1255
1236
  sawAgent = true;
1256
- } else {
1257
- if (r.record_type === "turn_started") inFlight = true;
1258
- else if (r.record_type === "turn_completed" || r.record_type === "turn_failed") {
1259
- inFlight = false;
1260
- } else if (r.record_type === "session_starting" || r.record_type === "session_retrying") {
1237
+ continue;
1238
+ }
1239
+ switch (r.record_type) {
1240
+ case "turn_started":
1241
+ inFlight = true;
1242
+ break;
1243
+ case "turn_ended":
1261
1244
  inFlight = false;
1245
+ break;
1246
+ case "environment_phase":
1247
+ case "environment_output":
1248
+ case "environment_ready":
1262
1249
  sawAgent = false;
1263
- }
1250
+ break;
1264
1251
  }
1265
1252
  }
1266
1253
  if (!inFlight) return null;
@@ -1269,14 +1256,14 @@ function awaitingAgentPhase(records) {
1269
1256
  function deliveredUnechoedSends(records) {
1270
1257
  const received = /* @__PURE__ */ new Map();
1271
1258
  const delivered = /* @__PURE__ */ new Map();
1272
- const failedTurns = /* @__PURE__ */ new Set();
1259
+ const unanswered = /* @__PURE__ */ new Set();
1273
1260
  const echoed = /* @__PURE__ */ new Set();
1274
1261
  for (const r of records) {
1275
1262
  if (r.session_message_id != null) echoed.add(r.session_message_id);
1276
1263
  if (r.source !== "lifecycle") continue;
1277
- if (r.record_type === "turn_failed") {
1278
- if (typeof r.payload.turn_id === "string")
1279
- failedTurns.add(r.payload.turn_id);
1264
+ if (r.record_type === "turn_ended") {
1265
+ if (typeof r.payload.turn_id === "string" && r.payload.status !== "completed")
1266
+ unanswered.add(r.payload.turn_id);
1280
1267
  continue;
1281
1268
  }
1282
1269
  const id = typeof r.payload.message_id === "string" ? r.payload.message_id : null;
@@ -1298,11 +1285,11 @@ function deliveredUnechoedSends(records) {
1298
1285
  for (const [id, body] of received) {
1299
1286
  const turnId = delivered.get(id);
1300
1287
  if (turnId === void 0 || echoed.has(id)) continue;
1301
- out.push({ id, body, cancelled: failedTurns.has(turnId) });
1288
+ out.push({ id, body, cancelled: unanswered.has(turnId) });
1302
1289
  }
1303
1290
  return out;
1304
1291
  }
1305
- var STARTING_SANDBOX = "Starting sandbox\u2026";
1292
+ var PREPARING_ENVIRONMENT = "Preparing environment";
1306
1293
  function msLabel(ms) {
1307
1294
  if (typeof ms !== "number" || !isFinite(ms) || ms < 0) return null;
1308
1295
  return humanDuration(ms / 1e3);
@@ -1340,64 +1327,38 @@ function stepLabel(phase, step) {
1340
1327
  if (phase === "image") return imageStepLabel(step);
1341
1328
  return step;
1342
1329
  }
1343
- return sandboxPhaseLabel(phase);
1330
+ return environmentPhaseLabel(phase);
1344
1331
  }
1345
- function deriveSandboxState(records, minFeedSeq) {
1332
+ function deriveEnvironmentState(records, minFeedSeq) {
1346
1333
  let seen = false;
1347
- let headline = STARTING_SANDBOX;
1348
1334
  let done = false;
1349
- let sandboxDone = false;
1350
1335
  let readySeconds = null;
1351
- let configName = null;
1352
- let configCommitSha = null;
1353
1336
  let log = [];
1354
1337
  let open = /* @__PURE__ */ new Map();
1338
+ let storyTurn = null;
1355
1339
  const push = (record, kind, text2) => {
1356
1340
  const entry = { key: `${record.feed_seq}:${log.length}`, kind, text: text2 };
1357
1341
  log.push(entry);
1358
1342
  return entry;
1359
1343
  };
1360
- const reset = () => {
1361
- log = [];
1362
- open = /* @__PURE__ */ new Map();
1363
- sandboxDone = false;
1344
+ const begin = (record) => {
1345
+ const turn = turnOf(record);
1346
+ if (seen && turn !== storyTurn) {
1347
+ log = [];
1348
+ open = /* @__PURE__ */ new Map();
1349
+ done = false;
1350
+ readySeconds = null;
1351
+ }
1352
+ seen = true;
1353
+ storyTurn = turn;
1364
1354
  };
1365
1355
  for (const record of records) {
1366
1356
  if (record.feed_seq <= minFeedSeq || record.source !== "lifecycle")
1367
1357
  continue;
1368
1358
  const p = record.payload;
1369
1359
  switch (record.record_type) {
1370
- case "session_scheduled": {
1371
- seen = true;
1372
- headline = "Session scheduled\u2026";
1373
- done = false;
1374
- configName = typeof p.config_name === "string" && p.config_name ? p.config_name : null;
1375
- configCommitSha = typeof p.config_commit_sha === "string" && p.config_commit_sha ? p.config_commit_sha : null;
1376
- break;
1377
- }
1378
- case "session_starting":
1379
- case "session_retrying": {
1380
- seen = true;
1381
- const text2 = lifecycleText(record.record_type, p);
1382
- headline = !text2 || text2 === "Session starting\u2026" ? STARTING_SANDBOX : text2;
1383
- done = false;
1384
- readySeconds = null;
1385
- reset();
1386
- break;
1387
- }
1388
- case "session_resumed":
1389
- case "session_idle": {
1390
- seen = true;
1391
- done = true;
1392
- break;
1393
- }
1394
- case "sandbox_starting": {
1395
- seen = true;
1396
- reset();
1397
- break;
1398
- }
1399
- case "sandbox_phase": {
1400
- seen = true;
1360
+ case "environment_phase": {
1361
+ begin(record);
1401
1362
  const phase = typeof p.phase === "string" && p.phase ? p.phase : "setup";
1402
1363
  const step = typeof p.step === "string" && p.step ? p.step : null;
1403
1364
  const key = step ? `${phase}:${step}` : phase;
@@ -1426,70 +1387,50 @@ function deriveSandboxState(records, minFeedSeq) {
1426
1387
  }
1427
1388
  break;
1428
1389
  }
1429
- case "sandbox_output": {
1430
- seen = true;
1431
- for (const l of sandboxOutputLines(p)) push(record, "output", l);
1390
+ case "environment_output": {
1391
+ begin(record);
1392
+ for (const l of environmentOutputLines(p)) push(record, "output", l);
1432
1393
  break;
1433
1394
  }
1434
- case "sandbox_ready": {
1435
- seen = true;
1395
+ case "environment_ready": {
1396
+ begin(record);
1436
1397
  for (const [, line] of open) line.kind = "done";
1437
1398
  open = /* @__PURE__ */ new Map();
1438
- const timings = p.phase_timings && typeof p.phase_timings === "object" ? Object.values(p.phase_timings) : [];
1439
- const totalSeconds = timings.reduce(
1440
- (acc, v) => typeof v === "number" && isFinite(v) ? acc + v : acc,
1441
- 0
1442
- );
1443
- const tier = cacheTierLabel(p.cache_tier);
1399
+ const seconds = typeof p.duration_ms === "number" && isFinite(p.duration_ms) && p.duration_ms > 0 ? p.duration_ms / 1e3 : null;
1444
1400
  push(
1445
1401
  record,
1446
1402
  "done",
1447
- [
1448
- "Sandbox ready",
1449
- ...tier ? [tier] : [],
1450
- ...totalSeconds > 0 ? [humanDuration(totalSeconds)] : []
1451
- ].join(", ")
1403
+ seconds == null ? "Environment ready" : `Environment ready, ${humanDuration(seconds)}`
1452
1404
  );
1453
- sandboxDone = true;
1454
- readySeconds = totalSeconds > 0 ? totalSeconds : null;
1405
+ readySeconds = seconds;
1455
1406
  done = true;
1456
1407
  break;
1457
1408
  }
1409
+ case "turn_started":
1410
+ case "turn_ended":
1411
+ if (seen && turnOf(record) === storyTurn) done = true;
1412
+ break;
1413
+ case "session_closed":
1414
+ if (seen) done = true;
1415
+ break;
1458
1416
  default:
1459
1417
  break;
1460
1418
  }
1461
1419
  }
1462
1420
  const openLines = [...open.values()];
1463
1421
  if (openLines.length) openLines[openLines.length - 1].loading = true;
1464
- const full = configName ? [
1465
- {
1466
- key: "config",
1467
- kind: "done",
1468
- text: `Using ${configName}${configCommitSha ? ` @ ${configCommitSha.slice(0, 7)}` : ""}`
1469
- },
1470
- ...log
1471
- ] : log;
1472
- return seen ? {
1473
- headline,
1474
- done,
1475
- readySeconds,
1476
- sandboxDone,
1477
- configName,
1478
- configCommitSha,
1479
- log: full
1480
- } : null;
1481
- }
1482
- function startupSettled(sandbox, status) {
1483
- return (sandbox?.done ?? false) && statusActivityText(status) == null;
1484
- }
1485
- function startupHeadline(sandbox, status) {
1486
- const activity = statusActivityText(status);
1487
- const text2 = !sandbox || sandbox.done ? activity ?? STARTING_SANDBOX : sandbox.headline;
1488
- return `${text2.replace(/…$/, "")}\u2026`;
1489
- }
1490
- function sandboxSummary(sandbox) {
1491
- const seconds = sandbox?.readySeconds ?? null;
1492
- return seconds ? `Sandbox ready in ${humanDuration(seconds)}` : "Sandbox started";
1422
+ return seen ? { done, readySeconds, log } : null;
1423
+ }
1424
+ function startupSettled(environment, status) {
1425
+ return (environment?.done ?? false) && statusActivityText(status) == null;
1426
+ }
1427
+ function startupHeadline(environment, status) {
1428
+ const text2 = !environment || environment.done ? statusActivityText(status) ?? PREPARING_ENVIRONMENT : PREPARING_ENVIRONMENT;
1429
+ return `${text2}\u2026`;
1430
+ }
1431
+ function environmentSummary(environment) {
1432
+ const seconds = environment?.readySeconds ?? null;
1433
+ return seconds ? `Environment ready in ${humanDuration(seconds)}` : "Environment ready";
1493
1434
  }
1494
1435
  function lastLines(log, max) {
1495
1436
  return log.length <= max ? [...log] : log.slice(log.length - max);
@@ -1505,42 +1446,21 @@ function gutterFor(item) {
1505
1446
  if (item.kind === "system" || item.kind === "notice") return "\u2726";
1506
1447
  return item.gutter ?? "";
1507
1448
  }
1508
- var SANDBOX_RECORD_TYPES2 = /* @__PURE__ */ new Set([
1509
- "sandbox_starting",
1510
- "sandbox_phase",
1511
- "sandbox_output",
1512
- "sandbox_ready"
1449
+ var ENVIRONMENT_RECORD_TYPES2 = /* @__PURE__ */ new Set([
1450
+ "environment_phase",
1451
+ "environment_output",
1452
+ "environment_ready"
1513
1453
  ]);
1514
1454
  function chatTurnsToItems(turns) {
1515
1455
  const items = [];
1516
- let wakeAt = -1;
1517
- const settleWake = () => {
1518
- if (wakeAt < 0) return;
1519
- const { loading: _, ...settled } = items[wakeAt];
1520
- items[wakeAt] = { ...settled, text: "Session awake" };
1521
- wakeAt = -1;
1522
- };
1523
1456
  for (const turn of turns) {
1524
- if (turn.role === "assistant") settleWake();
1525
1457
  if (turn.role === "lifecycle") {
1526
1458
  for (const node of turn.nodes) {
1527
1459
  if (node.kind !== "lifecycle") continue;
1528
- if (SANDBOX_RECORD_TYPES2.has(node.recordType)) continue;
1529
- if (node.recordType === "session_resumed") {
1530
- settleWake();
1531
- continue;
1532
- }
1460
+ if (ENVIRONMENT_RECORD_TYPES2.has(node.recordType)) continue;
1533
1461
  const text2 = sessionLogText(node.recordType, node.payload ?? {});
1534
1462
  if (!text2) continue;
1535
- const waking = text2 === SESSION_WAKING;
1536
- items.push({
1537
- key: node.key,
1538
- kind: "notice",
1539
- text: text2,
1540
- spaceBefore: true,
1541
- ...waking ? { loading: true } : {}
1542
- });
1543
- wakeAt = waking ? items.length - 1 : -1;
1463
+ items.push({ key: node.key, kind: "notice", text: text2, spaceBefore: true });
1544
1464
  }
1545
1465
  continue;
1546
1466
  }
@@ -1768,26 +1688,19 @@ function entryBody(entry, {
1768
1688
  const members = /* @__PURE__ */ new Map();
1769
1689
  const items = [];
1770
1690
  const work = [];
1771
- const wakeKeys = new Set(
1772
- entry.turns.flatMap(
1773
- (turn) => turn.nodes.flatMap(
1774
- (node) => node.kind === "lifecycle" && (node.recordType === "session_resumed" || node.recordType === "session_starting" && typeof node.payload?.wake_index === "number" && node.payload.wake_index > 0) ? [node.key] : []
1775
- )
1776
- )
1777
- );
1778
1691
  const phase = entry.progress?.phase;
1779
1692
  const seconds = entry.progress && !entry.progress.turns.some((turn) => turn.startedAt != null) ? null : entrySeconds(entry, now);
1780
1693
  const key = `work:${entry.key}`;
1781
1694
  const header = {
1782
1695
  key,
1783
1696
  kind: "summary",
1784
- text: live ? phase === "waking" ? "Waking session" : phase === "starting" ? "Starting agent" : phase === "queued" ? "Queued" : reasoningSummaryTitle(liveSummary) || "Working" : seconds == null ? "Session startup" : `Worked for ${humanDuration(seconds)}`,
1697
+ text: live ? phase === "preparing" ? "Preparing environment" : phase === "starting" ? "Starting agent" : phase === "queued" ? "Queued" : reasoningSummaryTitle(liveSummary) || "Working" : seconds == null ? "Session startup" : `Worked for ${humanDuration(seconds)}`,
1785
1698
  spaceBefore: true,
1786
1699
  ...entry.cancelled || entry.turns.some((turn) => turn.isError) ? { isError: true } : {},
1787
1700
  ...live ? { loading: true } : {}
1788
1701
  };
1789
1702
  if (live && (phase === void 0 || phase === "working")) {
1790
- const visible = raw.filter((item) => !wakeKeys.has(item.key)).map(({ loading: _, ...item }) => item);
1703
+ const visible = raw.map(({ loading: _, ...item }) => item);
1791
1704
  if (liveText !== "") {
1792
1705
  visible.push({
1793
1706
  key: `live:${entry.key}`,
@@ -1808,7 +1721,7 @@ function entryBody(entry, {
1808
1721
  };
1809
1722
  for (const item of raw) {
1810
1723
  const { loading: _, ...settled } = item;
1811
- if (!wakeKeys.has(item.key) && (replies.has(item.key) || !["assistant", "thinking", "tool", "tool_result"].includes(item.kind))) {
1724
+ if (replies.has(item.key) || !["assistant", "thinking", "tool", "tool_result"].includes(item.kind)) {
1812
1725
  items.push(settled);
1813
1726
  } else {
1814
1727
  addWork(settled);
@@ -1946,7 +1859,7 @@ function activitySummary(activities) {
1946
1859
 
1947
1860
  // src/store/index.ts
1948
1861
  function isConversationOver(session) {
1949
- return session.lifecycle.conversation === "closed";
1862
+ return session.conversation.state === "closed";
1950
1863
  }
1951
1864
  function seedTranscriptStore(store, seed) {
1952
1865
  store.ingest({
@@ -1994,7 +1907,6 @@ var SessionTranscriptStore = class {
1994
1907
  turnsSession = null;
1995
1908
  turnsCache = [];
1996
1909
  summaryTurnId = null;
1997
- summaryExecutionId = null;
1998
1910
  get cursor() {
1999
1911
  return this.cursorSeq;
2000
1912
  }
@@ -2038,7 +1950,8 @@ var SessionTranscriptStore = class {
2038
1950
  sender_attribution_id: typeof payload.sender_attribution_id === "string" ? payload.sender_attribution_id : null,
2039
1951
  created_at: record.created_at,
2040
1952
  delivered_at: null,
2041
- delivered_turn_id: null
1953
+ images: [],
1954
+ turn_id: typeof payload.turn_id === "string" ? payload.turn_id : null
2042
1955
  });
2043
1956
  return true;
2044
1957
  }
@@ -2049,7 +1962,7 @@ var SessionTranscriptStore = class {
2049
1962
  ...existing,
2050
1963
  status: "delivered",
2051
1964
  delivered_at: record.created_at,
2052
- delivered_turn_id: typeof payload.turn_id === "string" ? payload.turn_id : null
1965
+ turn_id: typeof payload.turn_id === "string" ? payload.turn_id : existing.turn_id
2053
1966
  });
2054
1967
  return true;
2055
1968
  }
@@ -2060,7 +1973,7 @@ var SessionTranscriptStore = class {
2060
1973
  ...existing,
2061
1974
  status: "pending",
2062
1975
  delivered_at: null,
2063
- delivered_turn_id: null
1976
+ turn_id: typeof payload.requeued_turn_id === "string" ? payload.requeued_turn_id : null
2064
1977
  });
2065
1978
  return true;
2066
1979
  }
@@ -2104,7 +2017,7 @@ var SessionTranscriptStore = class {
2104
2017
  acknowledgedNew = true;
2105
2018
  }
2106
2019
  if (record.source === "lifecycle") {
2107
- if (record.record_type === "turn_started" && (record.turn_id !== this.summaryTurnId || record.session_execution_id !== this.summaryExecutionId) || (record.record_type === "turn_completed" || record.record_type === "turn_failed") && record.turn_id === this.summaryTurnId && record.session_execution_id === this.summaryExecutionId)
2020
+ if (record.record_type === "turn_started" && record.turn_id !== this.summaryTurnId || record.record_type === "turn_ended" && record.turn_id === this.summaryTurnId)
2108
2021
  next.liveSummary = "";
2109
2022
  inboxChanged = this.applyMessageRecord(record) || inboxChanged;
2110
2023
  if (record.record_type === "message_received" || record.record_type === "message_delivered") {
@@ -2127,7 +2040,7 @@ var SessionTranscriptStore = class {
2127
2040
  case "session": {
2128
2041
  next.session = frame.session;
2129
2042
  next.conversationOver = isConversationOver(frame.session);
2130
- if (next.conversationOver || frame.session.lifecycle.status === "idle") {
2043
+ if (next.conversationOver || frame.session.turn?.status !== "running") {
2131
2044
  next.liveSummary = "";
2132
2045
  }
2133
2046
  break;
@@ -2136,7 +2049,6 @@ var SessionTranscriptStore = class {
2136
2049
  if (frame.kind === "thinking") {
2137
2050
  if (next.conversationOver) break;
2138
2051
  this.summaryTurnId = frame.turn_id;
2139
- this.summaryExecutionId = frame.session_execution_id;
2140
2052
  if (frame.text != null) next.liveSummary = frame.text;
2141
2053
  break;
2142
2054
  }
@@ -2168,7 +2080,6 @@ var SessionTranscriptStore = class {
2168
2080
  export {
2169
2081
  BRANCH_GLYPH,
2170
2082
  LIVE_GLYPH,
2171
- SESSION_WAKING,
2172
2083
  SessionTranscriptStore,
2173
2084
  USER_BAR,
2174
2085
  activitySummary,
@@ -2182,10 +2093,15 @@ export {
2182
2093
  codexMcpToolName,
2183
2094
  collapseToolRuns,
2184
2095
  deliveredUnechoedSends,
2185
- deriveSandboxState,
2096
+ deriveEnvironmentState,
2186
2097
  emptySessionTranscriptSnapshot,
2187
2098
  entryBody,
2188
2099
  entrySeconds,
2100
+ environmentOutputLine,
2101
+ environmentOutputLines,
2102
+ environmentOutputStep,
2103
+ environmentPhaseLabel,
2104
+ environmentSummary,
2189
2105
  eventToItems,
2190
2106
  foldCosts,
2191
2107
  foldGutter,
@@ -2210,11 +2126,6 @@ export {
2210
2126
  recordSlice,
2211
2127
  recordToItems,
2212
2128
  resultCostUsd,
2213
- sandboxOutputLine,
2214
- sandboxOutputLines,
2215
- sandboxOutputStep,
2216
- sandboxPhaseLabel,
2217
- sandboxSummary,
2218
2129
  seedTranscriptStore,
2219
2130
  sessionLogText,
2220
2131
  splitLedger,
@@ -2225,5 +2136,6 @@ export {
2225
2136
  tailLines,
2226
2137
  toolActivities,
2227
2138
  toolResultText,
2228
- toolRunMembers
2139
+ toolRunMembers,
2140
+ turnEndedText
2229
2141
  };