@ellipsis-dev/sdk 0.28.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.
- package/README.md +25 -0
- package/dist/{chunk-YJC53TA7.js → chunk-G2AEYZW2.js} +5 -9
- package/dist/index.d.ts +1853 -757
- package/dist/index.js +1236 -365
- package/dist/store/index.d.ts +68 -23
- package/dist/store/index.js +637 -330
- package/dist/stream/index.d.ts +3 -3
- package/dist/stream/index.js +1 -1
- package/dist/{types-B-jIFh4_.d.ts → types-BQxZcD0_.d.ts} +6342 -6117
- package/package.json +1 -1
- package/schema/collection_frames.schema.json +1485 -933
- package/schema/frames.schema.json +3379 -4844
- package/schema/lifecycle.schema.json +182 -323
- package/schema/openapi.v1.json +18809 -12100
- package/schema/trigger_fields.schema.json +1 -1
package/dist/store/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SESSION_STREAM_PROTOCOL_VERSION
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-G2AEYZW2.js";
|
|
4
4
|
|
|
5
5
|
// src/store/claude.ts
|
|
6
6
|
function claudePayload(record) {
|
|
@@ -62,8 +62,9 @@ function codexAppServerContent(event) {
|
|
|
62
62
|
const item = event.params.item;
|
|
63
63
|
switch (item.type) {
|
|
64
64
|
case "agentMessage":
|
|
65
|
+
return { kind: "assistant", text: item.text, phase: item.phase };
|
|
65
66
|
case "plan":
|
|
66
|
-
return { kind: "assistant", text: item.text };
|
|
67
|
+
return { kind: "assistant", text: item.text, phase: "commentary" };
|
|
67
68
|
case "reasoning":
|
|
68
69
|
return {
|
|
69
70
|
kind: "thinking",
|
|
@@ -75,7 +76,7 @@ function codexAppServerContent(event) {
|
|
|
75
76
|
return {
|
|
76
77
|
kind: "tool",
|
|
77
78
|
name: "Bash",
|
|
78
|
-
input: { command: item.command },
|
|
79
|
+
input: { command: item.command, commandActions: item.commandActions },
|
|
79
80
|
summary: item.command,
|
|
80
81
|
result: item.aggregatedOutput?.trim() || "(no output)",
|
|
81
82
|
isError: item.status === "failed" || item.status === "declined" || typeof item.exitCode === "number" && item.exitCode !== 0
|
|
@@ -103,18 +104,136 @@ function codexAppServerContent(event) {
|
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
// src/store/progress.ts
|
|
108
|
+
function messageProgress(records) {
|
|
109
|
+
const messages = /* @__PURE__ */ new Map();
|
|
110
|
+
const pending = /* @__PURE__ */ new Map();
|
|
111
|
+
const turns = /* @__PURE__ */ new Map();
|
|
112
|
+
let ready = false;
|
|
113
|
+
const getTurn = (id) => {
|
|
114
|
+
let turn = turns.get(id);
|
|
115
|
+
if (!turn) {
|
|
116
|
+
turn = {
|
|
117
|
+
timing: { id, startedAt: null, completedAt: null },
|
|
118
|
+
phase: ready ? "working" : "starting",
|
|
119
|
+
messages: /* @__PURE__ */ new Set()
|
|
120
|
+
};
|
|
121
|
+
turns.set(id, turn);
|
|
122
|
+
}
|
|
123
|
+
return turn;
|
|
124
|
+
};
|
|
125
|
+
const attach = (messageId, turnId) => {
|
|
126
|
+
const message = messages.get(messageId);
|
|
127
|
+
if (!message) return;
|
|
128
|
+
const turn = getTurn(turnId);
|
|
129
|
+
if (!turn.messages.has(messageId)) {
|
|
130
|
+
turn.messages.add(messageId);
|
|
131
|
+
message.turns.push(turn.timing);
|
|
132
|
+
}
|
|
133
|
+
pending.delete(messageId);
|
|
134
|
+
message.phase = turn.phase;
|
|
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
|
+
};
|
|
142
|
+
for (const record of records) {
|
|
143
|
+
if (record.kind === "platform") {
|
|
144
|
+
const p = record.payload;
|
|
145
|
+
const messageId = typeof p.message_id === "string" ? p.message_id : null;
|
|
146
|
+
const turnId = typeof p.turn_id === "string" ? p.turn_id : record.turn_id;
|
|
147
|
+
switch (record.record_type) {
|
|
148
|
+
case "message_received":
|
|
149
|
+
if (messageId && !messages.has(messageId)) {
|
|
150
|
+
messages.set(messageId, { phase: "queued", turns: [] });
|
|
151
|
+
if (turnId && turns.has(turnId)) attach(messageId, turnId);
|
|
152
|
+
else pending.set(messageId, turnId ?? null);
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
case "message_delivered":
|
|
156
|
+
if (messageId && turnId) attach(messageId, turnId);
|
|
157
|
+
break;
|
|
158
|
+
case "message_requeued":
|
|
159
|
+
if (messageId && messages.has(messageId)) {
|
|
160
|
+
const requeued = typeof p.requeued_turn_id === "string" ? p.requeued_turn_id : null;
|
|
161
|
+
pending.set(messageId, requeued);
|
|
162
|
+
messages.get(messageId).phase = "queued";
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
case "environment_phase":
|
|
166
|
+
case "environment_output":
|
|
167
|
+
ready = false;
|
|
168
|
+
setPending(turnId ?? null, "preparing");
|
|
169
|
+
break;
|
|
170
|
+
case "environment_ready":
|
|
171
|
+
ready = false;
|
|
172
|
+
setPending(turnId ?? null, "starting");
|
|
173
|
+
break;
|
|
174
|
+
case "turn_started":
|
|
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
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
case "turn_ended":
|
|
183
|
+
if (turnId) {
|
|
184
|
+
const turn = getTurn(turnId);
|
|
185
|
+
turn.timing.completedAt = record.created_at;
|
|
186
|
+
turn.phase = null;
|
|
187
|
+
for (const id of turn.messages) {
|
|
188
|
+
if (!pending.has(id)) messages.get(id).phase = null;
|
|
189
|
+
}
|
|
190
|
+
for (const [id, pendingTurn] of pending) {
|
|
191
|
+
if (pendingTurn === turnId) {
|
|
192
|
+
pending.delete(id);
|
|
193
|
+
messages.get(id).phase = null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
case "session_closed":
|
|
199
|
+
ready = false;
|
|
200
|
+
for (const id of pending.keys()) messages.get(id).phase = null;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const claude = claudePayload(record);
|
|
206
|
+
const agentStarted = record.kind === "codex_app_server" && record.payload.method === "turn/started" || record.kind === "codex" && record.payload.type === "turn.started" || claude != null && (claude.kind === "assistant" || claude.kind === "user" || claude.kind === "system" && claude.subtype === "init");
|
|
207
|
+
if (agentStarted) {
|
|
208
|
+
ready = true;
|
|
209
|
+
if (record.turn_id) {
|
|
210
|
+
const turn = getTurn(record.turn_id);
|
|
211
|
+
if (turn.timing.completedAt == null) {
|
|
212
|
+
turn.timing.startedAt ??= record.created_at;
|
|
213
|
+
turn.phase = "working";
|
|
214
|
+
for (const id of turn.messages) messages.get(id).phase = "working";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (record.session_message_id && record.turn_id) {
|
|
219
|
+
attach(record.session_message_id, record.turn_id);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return messages;
|
|
223
|
+
}
|
|
224
|
+
|
|
106
225
|
// src/store/lifecycle.ts
|
|
107
|
-
function
|
|
226
|
+
function environmentOutputStep(payload) {
|
|
108
227
|
if (typeof payload.step === "string" && payload.step) return payload.step;
|
|
109
228
|
return typeof payload.phase === "string" ? payload.phase : "setup";
|
|
110
229
|
}
|
|
111
|
-
function
|
|
230
|
+
function environmentOutputLines(payload) {
|
|
112
231
|
return Array.isArray(payload.lines) ? payload.lines.filter(
|
|
113
232
|
(l) => typeof l === "string" && l.trim().length > 0
|
|
114
233
|
) : [];
|
|
115
234
|
}
|
|
116
|
-
function
|
|
117
|
-
const lines =
|
|
235
|
+
function environmentOutputLine(payload) {
|
|
236
|
+
const lines = environmentOutputLines(payload);
|
|
118
237
|
return lines.length ? lines[lines.length - 1].trim() : null;
|
|
119
238
|
}
|
|
120
239
|
function cacheTierLabel(tier) {
|
|
@@ -129,7 +248,7 @@ function cacheTierLabel(tier) {
|
|
|
129
248
|
return null;
|
|
130
249
|
}
|
|
131
250
|
}
|
|
132
|
-
function
|
|
251
|
+
function environmentPhaseLabel(phase) {
|
|
133
252
|
switch (phase) {
|
|
134
253
|
case "image":
|
|
135
254
|
return "Preparing image";
|
|
@@ -151,8 +270,8 @@ function durationLabel(ms) {
|
|
|
151
270
|
if (typeof ms !== "number" || !isFinite(ms) || ms < 0) return null;
|
|
152
271
|
return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${Math.round(ms)}ms`;
|
|
153
272
|
}
|
|
154
|
-
function
|
|
155
|
-
const label =
|
|
273
|
+
function environmentPhaseText(payload) {
|
|
274
|
+
const label = environmentPhaseLabel(payload.phase);
|
|
156
275
|
const detail = payload.detail && typeof payload.detail === "object" ? payload.detail : {};
|
|
157
276
|
switch (payload.status) {
|
|
158
277
|
case "started":
|
|
@@ -173,56 +292,42 @@ function sandboxPhaseText(payload) {
|
|
|
173
292
|
return label;
|
|
174
293
|
}
|
|
175
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
|
+
}
|
|
176
301
|
function lifecycleText(recordType, payload) {
|
|
177
302
|
switch (recordType) {
|
|
178
|
-
case "
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const attempt = typeof payload.attempt === "number" ? payload.attempt : 0;
|
|
184
|
-
if (attempt > 0) return "Retrying\u2026";
|
|
185
|
-
const wakeIndex = typeof payload.wake_index === "number" ? payload.wake_index : 0;
|
|
186
|
-
return wakeIndex > 0 ? "Waking the session\u2026" : "Session starting\u2026";
|
|
303
|
+
case "environment_phase":
|
|
304
|
+
return environmentPhaseText(payload);
|
|
305
|
+
case "environment_output": {
|
|
306
|
+
const last = environmentOutputLine(payload);
|
|
307
|
+
return last ? `${environmentOutputStep(payload)}, ${last}` : null;
|
|
187
308
|
}
|
|
188
|
-
case "
|
|
189
|
-
return typeof payload.reason === "string" && payload.reason ? `Retrying, ${payload.reason}` : "Retrying after a transient error\u2026";
|
|
190
|
-
case "sandbox_starting":
|
|
191
|
-
return "Starting sandbox\u2026";
|
|
192
|
-
case "sandbox_phase":
|
|
193
|
-
return sandboxPhaseText(payload);
|
|
194
|
-
case "sandbox_output": {
|
|
195
|
-
const last = sandboxOutputLine(payload);
|
|
196
|
-
return last ? `${sandboxOutputStep(payload)}, ${last}` : null;
|
|
197
|
-
}
|
|
198
|
-
case "sandbox_ready": {
|
|
309
|
+
case "environment_ready": {
|
|
199
310
|
const repos = Array.isArray(payload.repositories) ? payload.repositories.filter(
|
|
200
311
|
(r) => typeof r === "string"
|
|
201
312
|
) : [];
|
|
202
|
-
const parts = ["
|
|
313
|
+
const parts = ["Environment ready"];
|
|
203
314
|
if (repos.length) parts.push(repos.join(", "));
|
|
204
|
-
const
|
|
205
|
-
if (
|
|
315
|
+
const duration = durationLabel(payload.duration_ms);
|
|
316
|
+
if (duration) parts.push(duration);
|
|
206
317
|
return parts.join(", ");
|
|
207
318
|
}
|
|
208
|
-
case "
|
|
209
|
-
return
|
|
210
|
-
case "session_idle":
|
|
211
|
-
return "Idle \u2014 your next message wakes it";
|
|
319
|
+
case "turn_ended":
|
|
320
|
+
return turnEndedText(payload);
|
|
212
321
|
case "session_closed":
|
|
213
322
|
return "Conversation closed";
|
|
214
|
-
case "session_cancelled": {
|
|
215
|
-
const reason = typeof payload.reason === "string" ? payload.reason : null;
|
|
216
|
-
return reason ? `Session cancelled, ${reason}` : "Session cancelled";
|
|
217
|
-
}
|
|
218
323
|
default:
|
|
219
324
|
return null;
|
|
220
325
|
}
|
|
221
326
|
}
|
|
222
327
|
|
|
223
328
|
// src/store/transcript.ts
|
|
224
|
-
function oneLine(
|
|
225
|
-
const collapsed =
|
|
329
|
+
function oneLine(text2, max) {
|
|
330
|
+
const collapsed = text2.replace(/\s+/g, " ").trim();
|
|
226
331
|
return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 3)}...`;
|
|
227
332
|
}
|
|
228
333
|
function summarizeToolInput(name, input) {
|
|
@@ -377,15 +482,15 @@ function codexEventToItems(event, keyBase) {
|
|
|
377
482
|
const item = event.item;
|
|
378
483
|
switch (item.type) {
|
|
379
484
|
case "agent_message": {
|
|
380
|
-
const
|
|
381
|
-
if (
|
|
485
|
+
const text2 = (item.text ?? "").trim();
|
|
486
|
+
if (text2) push({ kind: "assistant", text: text2, spaceBefore: true });
|
|
382
487
|
break;
|
|
383
488
|
}
|
|
384
489
|
case "reasoning": {
|
|
385
490
|
const r = item;
|
|
386
|
-
const
|
|
387
|
-
if (
|
|
388
|
-
push({ kind: "thinking", gutter: "\u273B", text, spaceBefore: true });
|
|
491
|
+
const text2 = (r.text ?? r.summary ?? "").trim();
|
|
492
|
+
if (text2)
|
|
493
|
+
push({ kind: "thinking", gutter: "\u273B", text: text2, spaceBefore: true });
|
|
389
494
|
break;
|
|
390
495
|
}
|
|
391
496
|
case "command_execution": {
|
|
@@ -517,15 +622,15 @@ function recordToItems(record, keyBase, options = {}) {
|
|
|
517
622
|
] : [];
|
|
518
623
|
}
|
|
519
624
|
case "platform": {
|
|
520
|
-
const
|
|
521
|
-
return
|
|
625
|
+
const text2 = lifecycleText(record.record_type, record.payload);
|
|
626
|
+
return text2 ? [{ key: keyBase, kind: "notice", text: text2, spaceBefore: true }] : [];
|
|
522
627
|
}
|
|
523
628
|
default:
|
|
524
629
|
return [];
|
|
525
630
|
}
|
|
526
631
|
}
|
|
527
632
|
function isConnectVisibleRecord(record) {
|
|
528
|
-
return record.source !== "lifecycle" || record.record_type === "
|
|
633
|
+
return record.source !== "lifecycle" || record.record_type === "environment_ready";
|
|
529
634
|
}
|
|
530
635
|
function pendingToolCalls(items) {
|
|
531
636
|
let pending = [];
|
|
@@ -589,9 +694,9 @@ function collapseToolRuns(items, { working = false } = {}) {
|
|
|
589
694
|
flush(true);
|
|
590
695
|
return out;
|
|
591
696
|
}
|
|
592
|
-
function clampLines(
|
|
593
|
-
const lines =
|
|
594
|
-
if (lines.length <= maxLines) return { body:
|
|
697
|
+
function clampLines(text2, maxLines) {
|
|
698
|
+
const lines = text2.split("\n");
|
|
699
|
+
if (lines.length <= maxLines) return { body: text2, more: 0 };
|
|
595
700
|
return {
|
|
596
701
|
body: lines.slice(0, maxLines).join("\n"),
|
|
597
702
|
more: lines.length - maxLines
|
|
@@ -614,12 +719,8 @@ function foldCosts(events) {
|
|
|
614
719
|
}
|
|
615
720
|
function statusActivityText(status) {
|
|
616
721
|
switch (status) {
|
|
617
|
-
case "
|
|
618
|
-
return "Waiting
|
|
619
|
-
case "starting":
|
|
620
|
-
return "Starting sandbox";
|
|
621
|
-
case "retrying":
|
|
622
|
-
return "Retrying after a transient error";
|
|
722
|
+
case "pending":
|
|
723
|
+
return "Waiting to start";
|
|
623
724
|
default:
|
|
624
725
|
return null;
|
|
625
726
|
}
|
|
@@ -627,26 +728,34 @@ function statusActivityText(status) {
|
|
|
627
728
|
|
|
628
729
|
// src/store/chatTurns.ts
|
|
629
730
|
var isInitEvent = (data) => data.kind === "system" && data.subtype === "init";
|
|
630
|
-
var
|
|
631
|
-
"
|
|
632
|
-
"
|
|
633
|
-
"
|
|
634
|
-
"sandbox_ready"
|
|
731
|
+
var ENVIRONMENT_RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
732
|
+
"environment_phase",
|
|
733
|
+
"environment_output",
|
|
734
|
+
"environment_ready"
|
|
635
735
|
]);
|
|
636
|
-
function groupRecordsToChatTurns(records) {
|
|
736
|
+
function groupRecordsToChatTurns(records, session) {
|
|
637
737
|
const turns = [];
|
|
638
738
|
const toolNodeByUseId = /* @__PURE__ */ new Map();
|
|
639
739
|
const userTurnByMessageId = /* @__PURE__ */ new Map();
|
|
640
|
-
const
|
|
740
|
+
const turnByMessageId = /* @__PURE__ */ new Map();
|
|
741
|
+
const unansweredTurnIds = /* @__PURE__ */ new Set();
|
|
742
|
+
const activeMessageByTurnId = /* @__PURE__ */ new Map();
|
|
743
|
+
let lastDeliveredMessageId;
|
|
641
744
|
let seq = 0;
|
|
642
745
|
const nextKey = (base) => `${base}:${seq++}`;
|
|
643
746
|
let openIdx = -1;
|
|
644
747
|
let initCount = 0;
|
|
645
748
|
let pendingResume = false;
|
|
646
|
-
const
|
|
749
|
+
const openSessionTurn = (record) => {
|
|
750
|
+
const messageId = (record.turn_id ? activeMessageByTurnId.get(record.turn_id) : void 0) ?? lastDeliveredMessageId;
|
|
751
|
+
if (openIdx >= 0 && (turns[openIdx].turnId !== (record.turn_id ?? void 0) || turns[openIdx].messageId !== messageId)) {
|
|
752
|
+
openIdx = -1;
|
|
753
|
+
}
|
|
647
754
|
if (openIdx < 0) {
|
|
648
755
|
turns.push({
|
|
649
756
|
key: nextKey(record.id),
|
|
757
|
+
messageId,
|
|
758
|
+
turnId: record.turn_id ?? void 0,
|
|
650
759
|
role: "assistant",
|
|
651
760
|
nodes: [],
|
|
652
761
|
startedAt: record.created_at,
|
|
@@ -666,12 +775,15 @@ function groupRecordsToChatTurns(records) {
|
|
|
666
775
|
if (record.kind === "platform") {
|
|
667
776
|
const messageId = typeof record.payload.message_id === "string" ? record.payload.message_id : null;
|
|
668
777
|
if (record.record_type === "message_received" && messageId) {
|
|
778
|
+
if (typeof record.payload.turn_id === "string")
|
|
779
|
+
turnByMessageId.set(messageId, record.payload.turn_id);
|
|
669
780
|
const body = typeof record.payload.body === "string" ? record.payload.body : "";
|
|
670
781
|
if (!body.trim() || userTurnByMessageId.has(messageId)) continue;
|
|
671
782
|
const turn2 = {
|
|
672
783
|
resumed: false,
|
|
673
784
|
isError: false,
|
|
674
785
|
key: `msg:${messageId}`,
|
|
786
|
+
messageId,
|
|
675
787
|
role: "user",
|
|
676
788
|
nodes: [{ key: `msg:${messageId}`, kind: "user", text: body.trim() }],
|
|
677
789
|
startedAt: record.created_at,
|
|
@@ -685,46 +797,51 @@ function groupRecordsToChatTurns(records) {
|
|
|
685
797
|
continue;
|
|
686
798
|
}
|
|
687
799
|
if (record.record_type === "message_delivered" && messageId) {
|
|
688
|
-
if (typeof record.payload.turn_id === "string")
|
|
689
|
-
|
|
800
|
+
if (typeof record.payload.turn_id === "string") {
|
|
801
|
+
turnByMessageId.set(messageId, record.payload.turn_id);
|
|
802
|
+
activeMessageByTurnId.set(record.payload.turn_id, messageId);
|
|
803
|
+
lastDeliveredMessageId = messageId;
|
|
804
|
+
}
|
|
690
805
|
continue;
|
|
691
806
|
}
|
|
692
|
-
if (record.record_type === "
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
if (userTurn) userTurn.isError = true;
|
|
807
|
+
if (record.record_type === "message_requeued" && messageId) {
|
|
808
|
+
if (typeof record.payload.requeued_turn_id === "string")
|
|
809
|
+
turnByMessageId.set(messageId, record.payload.requeued_turn_id);
|
|
810
|
+
else turnByMessageId.delete(messageId);
|
|
697
811
|
continue;
|
|
698
812
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
const
|
|
704
|
-
const
|
|
813
|
+
if (record.record_type === "turn_ended" && typeof record.payload.turn_id === "string" && record.payload.status !== "completed")
|
|
814
|
+
unansweredTurnIds.add(record.payload.turn_id);
|
|
815
|
+
const text2 = lifecycleText(record.record_type, record.payload);
|
|
816
|
+
if (!text2) continue;
|
|
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;
|
|
705
822
|
let foldTarget = null;
|
|
706
|
-
if (
|
|
823
|
+
if (isEnvironment) {
|
|
707
824
|
for (let i = turns.length - 1; i >= 0; i--) {
|
|
708
825
|
const candidate = turns[i];
|
|
709
826
|
if (candidate.role !== "lifecycle") break;
|
|
710
827
|
if (candidate.nodes.some(
|
|
711
|
-
(n) => n.kind === "lifecycle" &&
|
|
828
|
+
(n) => n.kind === "lifecycle" && ENVIRONMENT_RECORD_TYPES.has(n.recordType)
|
|
712
829
|
)) {
|
|
713
|
-
foldTarget = candidate;
|
|
830
|
+
if (candidate.turnId === turnId) foldTarget = candidate;
|
|
714
831
|
break;
|
|
715
832
|
}
|
|
716
833
|
}
|
|
717
834
|
}
|
|
718
835
|
if (foldTarget) {
|
|
719
836
|
const last = foldTarget.nodes[foldTarget.nodes.length - 1];
|
|
720
|
-
if (
|
|
721
|
-
last.text =
|
|
837
|
+
if (isOutput && last && last.kind === "lifecycle" && last.recordType === "environment_output" && last.step === step) {
|
|
838
|
+
last.text = text2;
|
|
722
839
|
last.lines = [...last.lines ?? [], ...lines ?? []];
|
|
723
840
|
} else {
|
|
724
841
|
foldTarget.nodes.push({
|
|
725
842
|
key: nextKey(record.id),
|
|
726
843
|
kind: "lifecycle",
|
|
727
|
-
text,
|
|
844
|
+
text: text2,
|
|
728
845
|
recordType: record.record_type,
|
|
729
846
|
step,
|
|
730
847
|
lines,
|
|
@@ -736,12 +853,13 @@ function groupRecordsToChatTurns(records) {
|
|
|
736
853
|
}
|
|
737
854
|
turns.push({
|
|
738
855
|
key: nextKey(record.id),
|
|
856
|
+
turnId,
|
|
739
857
|
role: "lifecycle",
|
|
740
858
|
nodes: [
|
|
741
859
|
{
|
|
742
860
|
key: nextKey(record.id),
|
|
743
861
|
kind: "lifecycle",
|
|
744
|
-
text,
|
|
862
|
+
text: text2,
|
|
745
863
|
recordType: record.record_type,
|
|
746
864
|
step,
|
|
747
865
|
lines,
|
|
@@ -758,9 +876,14 @@ function groupRecordsToChatTurns(records) {
|
|
|
758
876
|
});
|
|
759
877
|
continue;
|
|
760
878
|
}
|
|
879
|
+
if (record.session_message_id) {
|
|
880
|
+
lastDeliveredMessageId = record.session_message_id;
|
|
881
|
+
if (record.turn_id)
|
|
882
|
+
activeMessageByTurnId.set(record.turn_id, record.session_message_id);
|
|
883
|
+
}
|
|
761
884
|
if (record.kind === "codex") {
|
|
762
885
|
codexEventIntoTurns(record, record.payload, {
|
|
763
|
-
|
|
886
|
+
openSessionTurn,
|
|
764
887
|
closeTurn: (completedAt, isError) => {
|
|
765
888
|
if (openIdx >= 0) {
|
|
766
889
|
turns[openIdx].completedAt = completedAt;
|
|
@@ -782,7 +905,7 @@ function groupRecordsToChatTurns(records) {
|
|
|
782
905
|
openIdx = -1;
|
|
783
906
|
}
|
|
784
907
|
} else if (content.kind === "tool") {
|
|
785
|
-
|
|
908
|
+
openSessionTurn(record).nodes.push({
|
|
786
909
|
key: nextKey(record.id),
|
|
787
910
|
kind: "tool",
|
|
788
911
|
name: content.name,
|
|
@@ -794,12 +917,14 @@ function groupRecordsToChatTurns(records) {
|
|
|
794
917
|
completedAt: record.created_at
|
|
795
918
|
});
|
|
796
919
|
} else if (content.text.trim()) {
|
|
797
|
-
const turn2 =
|
|
920
|
+
const turn2 = openSessionTurn(record);
|
|
798
921
|
if (content.kind === "error") turn2.isError = true;
|
|
799
922
|
turn2.nodes.push({
|
|
800
923
|
key: nextKey(record.id),
|
|
801
924
|
kind: content.kind === "error" ? "assistant" : content.kind,
|
|
802
|
-
text: content.text.trim()
|
|
925
|
+
text: content.text.trim(),
|
|
926
|
+
createdAt: record.created_at,
|
|
927
|
+
...content.kind === "assistant" && content.phase != null ? { phase: content.phase } : {}
|
|
803
928
|
});
|
|
804
929
|
}
|
|
805
930
|
continue;
|
|
@@ -856,6 +981,9 @@ function groupRecordsToChatTurns(records) {
|
|
|
856
981
|
const node = toolNodeByUseId.get(useId);
|
|
857
982
|
if (node) {
|
|
858
983
|
node.result = body;
|
|
984
|
+
if (record.kind === "claude_code" || record.kind === "claude_sdk") {
|
|
985
|
+
node.resultDetails = record.payload.tool_use_result;
|
|
986
|
+
}
|
|
859
987
|
node.isError = !!block.is_error;
|
|
860
988
|
node.completedAt = record.created_at;
|
|
861
989
|
} else {
|
|
@@ -870,14 +998,14 @@ function groupRecordsToChatTurns(records) {
|
|
|
870
998
|
startedAt: null,
|
|
871
999
|
completedAt: record.created_at
|
|
872
1000
|
};
|
|
873
|
-
|
|
1001
|
+
openSessionTurn(record).nodes.push(orphan);
|
|
874
1002
|
toolNodeByUseId.set(useId, orphan);
|
|
875
1003
|
}
|
|
876
1004
|
}
|
|
877
1005
|
continue;
|
|
878
1006
|
}
|
|
879
1007
|
if (data.kind !== "assistant") continue;
|
|
880
|
-
const turn =
|
|
1008
|
+
const turn = openSessionTurn(record);
|
|
881
1009
|
if (data.usage && typeof data.usage.output_tokens === "number") {
|
|
882
1010
|
turn.tokens = (turn.tokens ?? 0) + data.usage.output_tokens;
|
|
883
1011
|
}
|
|
@@ -906,11 +1034,60 @@ function groupRecordsToChatTurns(records) {
|
|
|
906
1034
|
turn.nodes.push({
|
|
907
1035
|
key: nextKey(record.id),
|
|
908
1036
|
kind: "assistant",
|
|
909
|
-
text: block.text.trim()
|
|
1037
|
+
text: block.text.trim(),
|
|
1038
|
+
createdAt: record.created_at
|
|
910
1039
|
});
|
|
911
1040
|
}
|
|
912
1041
|
}
|
|
913
1042
|
}
|
|
1043
|
+
for (const [messageId, turnId] of turnByMessageId) {
|
|
1044
|
+
const turn = userTurnByMessageId.get(messageId);
|
|
1045
|
+
if (turn) turn.isError = unansweredTurnIds.has(turnId);
|
|
1046
|
+
}
|
|
1047
|
+
for (const [messageId, progress] of messageProgress(records)) {
|
|
1048
|
+
const turn = userTurnByMessageId.get(messageId);
|
|
1049
|
+
if (turn) turn.progress = progress;
|
|
1050
|
+
}
|
|
1051
|
+
const initialTurn = records.find(
|
|
1052
|
+
(r) => r.kind === "platform" && r.record_type === "turn_started" && r.payload.turn_index === 0
|
|
1053
|
+
);
|
|
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)
|
|
1057
|
+
);
|
|
1058
|
+
const initialDelivery = openingTurnId == null ? void 0 : records.find(
|
|
1059
|
+
(r) => r.kind === "platform" && r.record_type === "message_delivered" && r.payload.turn_id === openingTurnId
|
|
1060
|
+
);
|
|
1061
|
+
const initialId = (initialReceived ?? initialDelivery)?.payload.message_id;
|
|
1062
|
+
const opening = initialId ? userTurnByMessageId.get(initialId) : void 0;
|
|
1063
|
+
const includesStart = records.length === 0 || initialTurn != null;
|
|
1064
|
+
const prompt = (session?.claude_code ?? session?.codex)?.prompt;
|
|
1065
|
+
if (prompt?.trim() && session && (opening || includesStart)) {
|
|
1066
|
+
const first = opening ?? {
|
|
1067
|
+
key: "prompt",
|
|
1068
|
+
role: "user",
|
|
1069
|
+
nodes: [{ key: "prompt", kind: "user", text: prompt }],
|
|
1070
|
+
startedAt: session.created_at,
|
|
1071
|
+
completedAt: session.created_at,
|
|
1072
|
+
durationMs: null,
|
|
1073
|
+
costUsd: null,
|
|
1074
|
+
tokens: null,
|
|
1075
|
+
resumed: false,
|
|
1076
|
+
isError: false,
|
|
1077
|
+
progress: { phase: "preparing", turns: [] }
|
|
1078
|
+
};
|
|
1079
|
+
if (opening) turns.splice(turns.indexOf(opening), 1);
|
|
1080
|
+
first.key = "prompt";
|
|
1081
|
+
first.startedAt = session.created_at;
|
|
1082
|
+
turns.unshift(first);
|
|
1083
|
+
if (!opening) {
|
|
1084
|
+
for (const turn of turns) {
|
|
1085
|
+
if (turn !== first && turn.role === "user" && turn.progress && !turn.progress.turns.length) {
|
|
1086
|
+
turn.progress.phase = turn.progress.phase === null ? null : "queued";
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
914
1091
|
return turns;
|
|
915
1092
|
}
|
|
916
1093
|
function codexEventIntoTurns(record, event, ops) {
|
|
@@ -919,36 +1096,38 @@ function codexEventIntoTurns(record, event, ops) {
|
|
|
919
1096
|
return;
|
|
920
1097
|
}
|
|
921
1098
|
if (event.type === "error") {
|
|
922
|
-
const turn2 = ops.
|
|
1099
|
+
const turn2 = ops.openSessionTurn(record);
|
|
923
1100
|
turn2.nodes.push({
|
|
924
1101
|
key: ops.nextKey(record.id),
|
|
925
1102
|
kind: "assistant",
|
|
926
|
-
text: event.message?.trim() || "error"
|
|
1103
|
+
text: event.message?.trim() || "error",
|
|
1104
|
+
createdAt: record.created_at
|
|
927
1105
|
});
|
|
928
1106
|
return;
|
|
929
1107
|
}
|
|
930
1108
|
if (event.type !== "item.completed") return;
|
|
931
1109
|
const item = event.item;
|
|
932
|
-
const turn = () => ops.
|
|
1110
|
+
const turn = () => ops.openSessionTurn(record);
|
|
933
1111
|
switch (item.type) {
|
|
934
1112
|
case "agent_message": {
|
|
935
|
-
const
|
|
936
|
-
if (
|
|
1113
|
+
const text2 = (item.text ?? "").trim();
|
|
1114
|
+
if (text2)
|
|
937
1115
|
turn().nodes.push({
|
|
938
1116
|
key: ops.nextKey(record.id),
|
|
939
1117
|
kind: "assistant",
|
|
940
|
-
text
|
|
1118
|
+
text: text2,
|
|
1119
|
+
createdAt: record.created_at
|
|
941
1120
|
});
|
|
942
1121
|
break;
|
|
943
1122
|
}
|
|
944
1123
|
case "reasoning": {
|
|
945
1124
|
const r = item;
|
|
946
|
-
const
|
|
947
|
-
if (
|
|
1125
|
+
const text2 = (r.text ?? r.summary ?? "").trim();
|
|
1126
|
+
if (text2)
|
|
948
1127
|
turn().nodes.push({
|
|
949
1128
|
key: ops.nextKey(record.id),
|
|
950
1129
|
kind: "thinking",
|
|
951
|
-
text
|
|
1130
|
+
text: text2
|
|
952
1131
|
});
|
|
953
1132
|
break;
|
|
954
1133
|
}
|
|
@@ -1017,6 +1196,10 @@ function codexEventIntoTurns(record, event, ops) {
|
|
|
1017
1196
|
function recordSlice(records) {
|
|
1018
1197
|
return records;
|
|
1019
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
|
+
}
|
|
1020
1203
|
function humanDuration(seconds) {
|
|
1021
1204
|
const clamped = Math.max(0, seconds);
|
|
1022
1205
|
if (clamped === 0) return "0s";
|
|
@@ -1035,26 +1218,12 @@ function humanDuration(seconds) {
|
|
|
1035
1218
|
if (s > 0 || bits.length === 0) bits.push(`${s}s`);
|
|
1036
1219
|
return bits.join(" ");
|
|
1037
1220
|
}
|
|
1038
|
-
var SESSION_WAKING = "Session waking";
|
|
1039
1221
|
function sessionLogText(recordType, payload) {
|
|
1040
|
-
const p = payload;
|
|
1041
1222
|
switch (recordType) {
|
|
1042
|
-
case "
|
|
1043
|
-
return
|
|
1044
|
-
case "
|
|
1045
|
-
|
|
1046
|
-
const attempt = typeof p.attempt === "number" ? p.attempt : 0;
|
|
1047
|
-
if (attempt > 0) return "Restarting the sandbox after a transient error\u2026";
|
|
1048
|
-
return wake > 0 ? SESSION_WAKING : null;
|
|
1049
|
-
}
|
|
1050
|
-
case "session_retrying":
|
|
1051
|
-
return typeof p.reason === "string" && p.reason ? `Retrying, ${p.reason}` : "Retrying after a transient error\u2026";
|
|
1052
|
-
case "session_resumed":
|
|
1053
|
-
return "Session awake";
|
|
1054
|
-
case "session_cancelled": {
|
|
1055
|
-
const reason = typeof p.reason === "string" && p.reason ? `, ${p.reason}` : "";
|
|
1056
|
-
return `Session cancelled${reason}`;
|
|
1057
|
-
}
|
|
1223
|
+
case "turn_ended":
|
|
1224
|
+
return turnEndedText(payload);
|
|
1225
|
+
case "session_closed":
|
|
1226
|
+
return "Conversation closed";
|
|
1058
1227
|
default:
|
|
1059
1228
|
return null;
|
|
1060
1229
|
}
|
|
@@ -1065,14 +1234,20 @@ function awaitingAgentPhase(records) {
|
|
|
1065
1234
|
for (const r of records) {
|
|
1066
1235
|
if (r.source !== "lifecycle") {
|
|
1067
1236
|
sawAgent = true;
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
switch (r.record_type) {
|
|
1240
|
+
case "turn_started":
|
|
1241
|
+
inFlight = true;
|
|
1242
|
+
break;
|
|
1243
|
+
case "turn_ended":
|
|
1073
1244
|
inFlight = false;
|
|
1245
|
+
break;
|
|
1246
|
+
case "environment_phase":
|
|
1247
|
+
case "environment_output":
|
|
1248
|
+
case "environment_ready":
|
|
1074
1249
|
sawAgent = false;
|
|
1075
|
-
|
|
1250
|
+
break;
|
|
1076
1251
|
}
|
|
1077
1252
|
}
|
|
1078
1253
|
if (!inFlight) return null;
|
|
@@ -1081,14 +1256,14 @@ function awaitingAgentPhase(records) {
|
|
|
1081
1256
|
function deliveredUnechoedSends(records) {
|
|
1082
1257
|
const received = /* @__PURE__ */ new Map();
|
|
1083
1258
|
const delivered = /* @__PURE__ */ new Map();
|
|
1084
|
-
const
|
|
1259
|
+
const unanswered = /* @__PURE__ */ new Set();
|
|
1085
1260
|
const echoed = /* @__PURE__ */ new Set();
|
|
1086
1261
|
for (const r of records) {
|
|
1087
1262
|
if (r.session_message_id != null) echoed.add(r.session_message_id);
|
|
1088
1263
|
if (r.source !== "lifecycle") continue;
|
|
1089
|
-
if (r.record_type === "
|
|
1090
|
-
if (typeof r.payload.turn_id === "string")
|
|
1091
|
-
|
|
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);
|
|
1092
1267
|
continue;
|
|
1093
1268
|
}
|
|
1094
1269
|
const id = typeof r.payload.message_id === "string" ? r.payload.message_id : null;
|
|
@@ -1110,11 +1285,11 @@ function deliveredUnechoedSends(records) {
|
|
|
1110
1285
|
for (const [id, body] of received) {
|
|
1111
1286
|
const turnId = delivered.get(id);
|
|
1112
1287
|
if (turnId === void 0 || echoed.has(id)) continue;
|
|
1113
|
-
out.push({ id, body, cancelled:
|
|
1288
|
+
out.push({ id, body, cancelled: unanswered.has(turnId) });
|
|
1114
1289
|
}
|
|
1115
1290
|
return out;
|
|
1116
1291
|
}
|
|
1117
|
-
var
|
|
1292
|
+
var PREPARING_ENVIRONMENT = "Preparing environment";
|
|
1118
1293
|
function msLabel(ms) {
|
|
1119
1294
|
if (typeof ms !== "number" || !isFinite(ms) || ms < 0) return null;
|
|
1120
1295
|
return humanDuration(ms / 1e3);
|
|
@@ -1152,64 +1327,38 @@ function stepLabel(phase, step) {
|
|
|
1152
1327
|
if (phase === "image") return imageStepLabel(step);
|
|
1153
1328
|
return step;
|
|
1154
1329
|
}
|
|
1155
|
-
return
|
|
1330
|
+
return environmentPhaseLabel(phase);
|
|
1156
1331
|
}
|
|
1157
|
-
function
|
|
1332
|
+
function deriveEnvironmentState(records, minFeedSeq) {
|
|
1158
1333
|
let seen = false;
|
|
1159
|
-
let headline = STARTING_SANDBOX;
|
|
1160
1334
|
let done = false;
|
|
1161
|
-
let sandboxDone = false;
|
|
1162
1335
|
let readySeconds = null;
|
|
1163
|
-
let configName = null;
|
|
1164
|
-
let configCommitSha = null;
|
|
1165
1336
|
let log = [];
|
|
1166
1337
|
let open = /* @__PURE__ */ new Map();
|
|
1167
|
-
|
|
1168
|
-
|
|
1338
|
+
let storyTurn = null;
|
|
1339
|
+
const push = (record, kind, text2) => {
|
|
1340
|
+
const entry = { key: `${record.feed_seq}:${log.length}`, kind, text: text2 };
|
|
1169
1341
|
log.push(entry);
|
|
1170
1342
|
return entry;
|
|
1171
1343
|
};
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
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;
|
|
1176
1354
|
};
|
|
1177
1355
|
for (const record of records) {
|
|
1178
1356
|
if (record.feed_seq <= minFeedSeq || record.source !== "lifecycle")
|
|
1179
1357
|
continue;
|
|
1180
1358
|
const p = record.payload;
|
|
1181
1359
|
switch (record.record_type) {
|
|
1182
|
-
case "
|
|
1183
|
-
|
|
1184
|
-
headline = "Session scheduled\u2026";
|
|
1185
|
-
done = false;
|
|
1186
|
-
configName = typeof p.config_name === "string" && p.config_name ? p.config_name : null;
|
|
1187
|
-
configCommitSha = typeof p.config_commit_sha === "string" && p.config_commit_sha ? p.config_commit_sha : null;
|
|
1188
|
-
break;
|
|
1189
|
-
}
|
|
1190
|
-
case "session_starting":
|
|
1191
|
-
case "session_retrying": {
|
|
1192
|
-
seen = true;
|
|
1193
|
-
const text = lifecycleText(record.record_type, p);
|
|
1194
|
-
headline = !text || text === "Session starting\u2026" ? STARTING_SANDBOX : text;
|
|
1195
|
-
done = false;
|
|
1196
|
-
readySeconds = null;
|
|
1197
|
-
reset();
|
|
1198
|
-
break;
|
|
1199
|
-
}
|
|
1200
|
-
case "session_resumed":
|
|
1201
|
-
case "session_idle": {
|
|
1202
|
-
seen = true;
|
|
1203
|
-
done = true;
|
|
1204
|
-
break;
|
|
1205
|
-
}
|
|
1206
|
-
case "sandbox_starting": {
|
|
1207
|
-
seen = true;
|
|
1208
|
-
reset();
|
|
1209
|
-
break;
|
|
1210
|
-
}
|
|
1211
|
-
case "sandbox_phase": {
|
|
1212
|
-
seen = true;
|
|
1360
|
+
case "environment_phase": {
|
|
1361
|
+
begin(record);
|
|
1213
1362
|
const phase = typeof p.phase === "string" && p.phase ? p.phase : "setup";
|
|
1214
1363
|
const step = typeof p.step === "string" && p.step ? p.step : null;
|
|
1215
1364
|
const key = step ? `${phase}:${step}` : phase;
|
|
@@ -1220,7 +1369,7 @@ function deriveSandboxState(records, minFeedSeq) {
|
|
|
1220
1369
|
const dur = msLabel(p.duration_ms);
|
|
1221
1370
|
const failed = p.status === "failed";
|
|
1222
1371
|
const base = failed ? `${label} failed` : label;
|
|
1223
|
-
const
|
|
1372
|
+
const text2 = [
|
|
1224
1373
|
base,
|
|
1225
1374
|
...tier ? [tier] : [],
|
|
1226
1375
|
...dur ? [dur] : []
|
|
@@ -1228,80 +1377,60 @@ function deriveSandboxState(records, minFeedSeq) {
|
|
|
1228
1377
|
const line = open.get(key);
|
|
1229
1378
|
if (line) {
|
|
1230
1379
|
line.kind = failed ? "failed" : "done";
|
|
1231
|
-
line.text =
|
|
1380
|
+
line.text = text2;
|
|
1232
1381
|
open.delete(key);
|
|
1233
1382
|
} else {
|
|
1234
|
-
push(record, failed ? "failed" : "done",
|
|
1383
|
+
push(record, failed ? "failed" : "done", text2);
|
|
1235
1384
|
}
|
|
1236
1385
|
} else if (!open.has(key)) {
|
|
1237
1386
|
open.set(key, push(record, "step", `${label}\u2026`));
|
|
1238
1387
|
}
|
|
1239
1388
|
break;
|
|
1240
1389
|
}
|
|
1241
|
-
case "
|
|
1242
|
-
|
|
1243
|
-
for (const l of
|
|
1390
|
+
case "environment_output": {
|
|
1391
|
+
begin(record);
|
|
1392
|
+
for (const l of environmentOutputLines(p)) push(record, "output", l);
|
|
1244
1393
|
break;
|
|
1245
1394
|
}
|
|
1246
|
-
case "
|
|
1247
|
-
|
|
1395
|
+
case "environment_ready": {
|
|
1396
|
+
begin(record);
|
|
1248
1397
|
for (const [, line] of open) line.kind = "done";
|
|
1249
1398
|
open = /* @__PURE__ */ new Map();
|
|
1250
|
-
const
|
|
1251
|
-
const totalSeconds = timings.reduce(
|
|
1252
|
-
(acc, v) => typeof v === "number" && isFinite(v) ? acc + v : acc,
|
|
1253
|
-
0
|
|
1254
|
-
);
|
|
1255
|
-
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;
|
|
1256
1400
|
push(
|
|
1257
1401
|
record,
|
|
1258
1402
|
"done",
|
|
1259
|
-
|
|
1260
|
-
"Sandbox ready",
|
|
1261
|
-
...tier ? [tier] : [],
|
|
1262
|
-
...totalSeconds > 0 ? [humanDuration(totalSeconds)] : []
|
|
1263
|
-
].join(", ")
|
|
1403
|
+
seconds == null ? "Environment ready" : `Environment ready, ${humanDuration(seconds)}`
|
|
1264
1404
|
);
|
|
1265
|
-
|
|
1266
|
-
readySeconds = totalSeconds > 0 ? totalSeconds : null;
|
|
1405
|
+
readySeconds = seconds;
|
|
1267
1406
|
done = true;
|
|
1268
1407
|
break;
|
|
1269
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;
|
|
1270
1416
|
default:
|
|
1271
1417
|
break;
|
|
1272
1418
|
}
|
|
1273
1419
|
}
|
|
1274
1420
|
const openLines = [...open.values()];
|
|
1275
1421
|
if (openLines.length) openLines[openLines.length - 1].loading = true;
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
sandboxDone,
|
|
1289
|
-
configName,
|
|
1290
|
-
configCommitSha,
|
|
1291
|
-
log: full
|
|
1292
|
-
} : null;
|
|
1293
|
-
}
|
|
1294
|
-
function startupSettled(sandbox, status) {
|
|
1295
|
-
return (sandbox?.done ?? false) && statusActivityText(status) == null;
|
|
1296
|
-
}
|
|
1297
|
-
function startupHeadline(sandbox, status) {
|
|
1298
|
-
const activity = statusActivityText(status);
|
|
1299
|
-
const text = !sandbox || sandbox.done ? activity ?? STARTING_SANDBOX : sandbox.headline;
|
|
1300
|
-
return `${text.replace(/…$/, "")}\u2026`;
|
|
1301
|
-
}
|
|
1302
|
-
function sandboxSummary(sandbox) {
|
|
1303
|
-
const seconds = sandbox?.readySeconds ?? null;
|
|
1304
|
-
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";
|
|
1305
1434
|
}
|
|
1306
1435
|
function lastLines(log, max) {
|
|
1307
1436
|
return log.length <= max ? [...log] : log.slice(log.length - max);
|
|
@@ -1317,42 +1446,21 @@ function gutterFor(item) {
|
|
|
1317
1446
|
if (item.kind === "system" || item.kind === "notice") return "\u2726";
|
|
1318
1447
|
return item.gutter ?? "";
|
|
1319
1448
|
}
|
|
1320
|
-
var
|
|
1321
|
-
"
|
|
1322
|
-
"
|
|
1323
|
-
"
|
|
1324
|
-
"sandbox_ready"
|
|
1449
|
+
var ENVIRONMENT_RECORD_TYPES2 = /* @__PURE__ */ new Set([
|
|
1450
|
+
"environment_phase",
|
|
1451
|
+
"environment_output",
|
|
1452
|
+
"environment_ready"
|
|
1325
1453
|
]);
|
|
1326
1454
|
function chatTurnsToItems(turns) {
|
|
1327
1455
|
const items = [];
|
|
1328
|
-
let wakeAt = -1;
|
|
1329
|
-
const settleWake = () => {
|
|
1330
|
-
if (wakeAt < 0) return;
|
|
1331
|
-
const { loading: _, ...settled } = items[wakeAt];
|
|
1332
|
-
items[wakeAt] = { ...settled, text: "Session awake" };
|
|
1333
|
-
wakeAt = -1;
|
|
1334
|
-
};
|
|
1335
1456
|
for (const turn of turns) {
|
|
1336
|
-
if (turn.role === "assistant") settleWake();
|
|
1337
1457
|
if (turn.role === "lifecycle") {
|
|
1338
1458
|
for (const node of turn.nodes) {
|
|
1339
1459
|
if (node.kind !== "lifecycle") continue;
|
|
1340
|
-
if (
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
}
|
|
1345
|
-
const text = sessionLogText(node.recordType, node.payload ?? {});
|
|
1346
|
-
if (!text) continue;
|
|
1347
|
-
const waking = text === SESSION_WAKING;
|
|
1348
|
-
items.push({
|
|
1349
|
-
key: node.key,
|
|
1350
|
-
kind: "notice",
|
|
1351
|
-
text,
|
|
1352
|
-
spaceBefore: true,
|
|
1353
|
-
...waking ? { loading: true } : {}
|
|
1354
|
-
});
|
|
1355
|
-
wakeAt = waking ? items.length - 1 : -1;
|
|
1460
|
+
if (ENVIRONMENT_RECORD_TYPES2.has(node.recordType)) continue;
|
|
1461
|
+
const text2 = sessionLogText(node.recordType, node.payload ?? {});
|
|
1462
|
+
if (!text2) continue;
|
|
1463
|
+
items.push({ key: node.key, kind: "notice", text: text2, spaceBefore: true });
|
|
1356
1464
|
}
|
|
1357
1465
|
continue;
|
|
1358
1466
|
}
|
|
@@ -1363,6 +1471,7 @@ function chatTurnsToItems(turns) {
|
|
|
1363
1471
|
key: node.key,
|
|
1364
1472
|
kind: "user",
|
|
1365
1473
|
text: node.text,
|
|
1474
|
+
createdAt: turn.startedAt,
|
|
1366
1475
|
spaceBefore: true,
|
|
1367
1476
|
...turn.isError ? { isError: true } : {}
|
|
1368
1477
|
});
|
|
@@ -1376,6 +1485,7 @@ function chatTurnsToItems(turns) {
|
|
|
1376
1485
|
key: node.key,
|
|
1377
1486
|
kind: "assistant",
|
|
1378
1487
|
text: node.text,
|
|
1488
|
+
createdAt: node.createdAt,
|
|
1379
1489
|
spaceBefore: true
|
|
1380
1490
|
});
|
|
1381
1491
|
} else if (node.kind === "thinking") {
|
|
@@ -1430,14 +1540,19 @@ function isToolActivity(item) {
|
|
|
1430
1540
|
return item.kind === "tool" || item.kind === "tool_result" || isToolFold(item);
|
|
1431
1541
|
}
|
|
1432
1542
|
function layOutItems(items) {
|
|
1433
|
-
|
|
1543
|
+
const isActivity = (item) => item.kind === "thinking" || isToolActivity(item);
|
|
1544
|
+
return items.map((item, index) => ({
|
|
1545
|
+
item,
|
|
1546
|
+
nested: false,
|
|
1547
|
+
attach: index > 0 && isActivity(item) && isActivity(items[index - 1])
|
|
1548
|
+
}));
|
|
1434
1549
|
}
|
|
1435
1550
|
function foldGutter(nested) {
|
|
1436
1551
|
return nested ? BRANCH_GLYPH : "\u25CF";
|
|
1437
1552
|
}
|
|
1438
|
-
function tailLines(
|
|
1439
|
-
const lines =
|
|
1440
|
-
if (lines.length <= maxLines) return { body:
|
|
1553
|
+
function tailLines(text2, maxLines) {
|
|
1554
|
+
const lines = text2.split("\n");
|
|
1555
|
+
if (lines.length <= maxLines) return { body: text2, hidden: 0 };
|
|
1441
1556
|
return {
|
|
1442
1557
|
body: lines.slice(lines.length - maxLines).join("\n"),
|
|
1443
1558
|
hidden: lines.length - maxLines
|
|
@@ -1462,24 +1577,44 @@ function toolRunMembers(items) {
|
|
|
1462
1577
|
function splitLedger(turns) {
|
|
1463
1578
|
const preamble = [];
|
|
1464
1579
|
const entries = [];
|
|
1580
|
+
const entryByTurn = /* @__PURE__ */ new Map();
|
|
1581
|
+
const entryByMessage = /* @__PURE__ */ new Map();
|
|
1465
1582
|
for (const turn of turns) {
|
|
1466
1583
|
if (turn.role === "user") {
|
|
1467
1584
|
const node = turn.nodes.find((n) => n.kind === "user");
|
|
1468
|
-
|
|
1585
|
+
const entry = {
|
|
1469
1586
|
key: turn.key,
|
|
1470
1587
|
prompt: node?.text ?? "",
|
|
1588
|
+
createdAt: turn.startedAt,
|
|
1471
1589
|
cancelled: turn.isError,
|
|
1472
|
-
turns: []
|
|
1473
|
-
|
|
1590
|
+
turns: [],
|
|
1591
|
+
...turn.progress ? { progress: turn.progress } : {}
|
|
1592
|
+
};
|
|
1593
|
+
entries.push(entry);
|
|
1594
|
+
entryByTurn.set(turn, entry);
|
|
1595
|
+
if (turn.messageId) entryByMessage.set(turn.messageId, entry);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
let last;
|
|
1599
|
+
for (const turn of turns) {
|
|
1600
|
+
if (turn.role === "user") {
|
|
1601
|
+
last = entryByTurn.get(turn);
|
|
1474
1602
|
continue;
|
|
1475
1603
|
}
|
|
1476
|
-
const
|
|
1477
|
-
if (
|
|
1604
|
+
const owner = turn.messageId ? entryByMessage.get(turn.messageId) : last;
|
|
1605
|
+
if (owner) owner.turns.push(turn);
|
|
1478
1606
|
else preamble.push(turn);
|
|
1479
1607
|
}
|
|
1480
1608
|
return { preamble, entries };
|
|
1481
1609
|
}
|
|
1482
1610
|
function entrySeconds(entry, now) {
|
|
1611
|
+
if (entry.progress?.turns.length) {
|
|
1612
|
+
return entry.progress.turns.reduce((ms2, turn) => {
|
|
1613
|
+
if (turn.startedAt == null) return ms2;
|
|
1614
|
+
const end = turn.completedAt == null ? now : Date.parse(turn.completedAt);
|
|
1615
|
+
return ms2 + Math.max(0, end - Date.parse(turn.startedAt));
|
|
1616
|
+
}, 0) / 1e3;
|
|
1617
|
+
}
|
|
1483
1618
|
let ms = 0;
|
|
1484
1619
|
for (const turn of entry.turns) {
|
|
1485
1620
|
if (turn.role !== "assistant" || turn.startedAt == null) continue;
|
|
@@ -1491,12 +1626,15 @@ function entrySeconds(entry, now) {
|
|
|
1491
1626
|
}
|
|
1492
1627
|
return ms / 1e3;
|
|
1493
1628
|
}
|
|
1494
|
-
function
|
|
1629
|
+
function isWorkFold(item) {
|
|
1630
|
+
return item.key.startsWith("work:");
|
|
1631
|
+
}
|
|
1632
|
+
function foldToolTurn(raw, members) {
|
|
1495
1633
|
const items = [];
|
|
1496
1634
|
let run = [];
|
|
1497
1635
|
const flush = () => {
|
|
1498
1636
|
if (run.length === 0) return;
|
|
1499
|
-
const fold = foldRun(run,
|
|
1637
|
+
const fold = foldRun(run, false);
|
|
1500
1638
|
members.set(fold.key, run);
|
|
1501
1639
|
items.push(fold);
|
|
1502
1640
|
run = [];
|
|
@@ -1511,77 +1649,217 @@ function foldToolTurn(raw, live, members) {
|
|
|
1511
1649
|
flush();
|
|
1512
1650
|
return items;
|
|
1513
1651
|
}
|
|
1652
|
+
function finalReplyKeys(entry, live) {
|
|
1653
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1654
|
+
for (const turn of entry.turns) {
|
|
1655
|
+
if (turn.role !== "assistant") continue;
|
|
1656
|
+
for (const node of turn.nodes) {
|
|
1657
|
+
if (node.kind === "assistant" && node.phase === "final_answer") {
|
|
1658
|
+
keys.add(node.key);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
if (entry.cancelled || turn.isError) continue;
|
|
1662
|
+
if (live && turn.completedAt == null && turn.durationMs == null) continue;
|
|
1663
|
+
for (let i = turn.nodes.length - 1; i >= 0; i--) {
|
|
1664
|
+
const node = turn.nodes[i];
|
|
1665
|
+
if (node.kind !== "assistant" || node.phase === "commentary") break;
|
|
1666
|
+
keys.add(node.key);
|
|
1667
|
+
const previous = turn.nodes[i - 1];
|
|
1668
|
+
if (node.createdAt == null || previous?.kind !== "assistant" || previous.createdAt !== node.createdAt)
|
|
1669
|
+
break;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
return keys;
|
|
1673
|
+
}
|
|
1674
|
+
function reasoningSummaryTitle(summary) {
|
|
1675
|
+
const line = summary.trim().split(/\r?\n/, 1)[0] ?? "";
|
|
1676
|
+
const title = line.replace(/^#{1,6}\s+/, "").replace(/^[*_`]+|[*_`]+$/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").trim();
|
|
1677
|
+
return title.length > 120 ? `${title.slice(0, 119).trimEnd()}\u2026` : title;
|
|
1678
|
+
}
|
|
1514
1679
|
function entryBody(entry, {
|
|
1515
1680
|
live,
|
|
1516
1681
|
now,
|
|
1517
|
-
liveText = ""
|
|
1682
|
+
liveText = "",
|
|
1683
|
+
liveSummary = ""
|
|
1518
1684
|
}) {
|
|
1685
|
+
live = live && (entry.progress === void 0 || entry.progress.phase !== null);
|
|
1519
1686
|
const raw = chatTurnsToItems(entry.turns);
|
|
1687
|
+
const replies = finalReplyKeys(entry, live);
|
|
1520
1688
|
const members = /* @__PURE__ */ new Map();
|
|
1521
|
-
const
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
kind: "thinking",
|
|
1542
|
-
text: `Thought for ${humanDuration(entrySeconds(entry, now))}`,
|
|
1543
|
-
summary: true,
|
|
1689
|
+
const items = [];
|
|
1690
|
+
const work = [];
|
|
1691
|
+
const phase = entry.progress?.phase;
|
|
1692
|
+
const seconds = entry.progress && !entry.progress.turns.some((turn) => turn.startedAt != null) ? null : entrySeconds(entry, now);
|
|
1693
|
+
const key = `work:${entry.key}`;
|
|
1694
|
+
const header = {
|
|
1695
|
+
key,
|
|
1696
|
+
kind: "summary",
|
|
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)}`,
|
|
1698
|
+
spaceBefore: true,
|
|
1699
|
+
...entry.cancelled || entry.turns.some((turn) => turn.isError) ? { isError: true } : {},
|
|
1700
|
+
...live ? { loading: true } : {}
|
|
1701
|
+
};
|
|
1702
|
+
if (live && (phase === void 0 || phase === "working")) {
|
|
1703
|
+
const visible = raw.map(({ loading: _, ...item }) => item);
|
|
1704
|
+
if (liveText !== "") {
|
|
1705
|
+
visible.push({
|
|
1706
|
+
key: `live:${entry.key}`,
|
|
1707
|
+
kind: "assistant",
|
|
1708
|
+
text: liveText,
|
|
1544
1709
|
spaceBefore: true
|
|
1545
1710
|
});
|
|
1546
1711
|
}
|
|
1712
|
+
return {
|
|
1713
|
+
items: [header, ...foldToolTurn(visible, members)],
|
|
1714
|
+
members,
|
|
1715
|
+
seconds
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
const addWork = (item) => {
|
|
1719
|
+
if (work.length === 0) items.push(header);
|
|
1720
|
+
work.push(item);
|
|
1721
|
+
};
|
|
1722
|
+
for (const item of raw) {
|
|
1723
|
+
const { loading: _, ...settled } = item;
|
|
1724
|
+
if (replies.has(item.key) || !["assistant", "thinking", "tool", "tool_result"].includes(item.kind)) {
|
|
1725
|
+
items.push(settled);
|
|
1726
|
+
} else {
|
|
1727
|
+
addWork(settled);
|
|
1728
|
+
}
|
|
1547
1729
|
}
|
|
1548
1730
|
if (live && liveText !== "") {
|
|
1549
|
-
|
|
1731
|
+
addWork({
|
|
1550
1732
|
key: `live:${entry.key}`,
|
|
1551
1733
|
kind: "assistant",
|
|
1552
1734
|
text: liveText,
|
|
1553
1735
|
spaceBefore: true
|
|
1554
1736
|
});
|
|
1555
1737
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
}
|
|
1561
|
-
return { items, members };
|
|
1738
|
+
if (work.length > 0) {
|
|
1739
|
+
members.set(key, foldToolTurn(work, members));
|
|
1740
|
+
} else if (live) {
|
|
1741
|
+
items.unshift(header);
|
|
1742
|
+
}
|
|
1743
|
+
return { items, members, seconds };
|
|
1562
1744
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1745
|
+
|
|
1746
|
+
// src/store/activity.ts
|
|
1747
|
+
var text = (value) => typeof value === "string" ? value : "";
|
|
1748
|
+
function toolActivities(item) {
|
|
1749
|
+
const input = item.tool?.input ?? {};
|
|
1750
|
+
const name = item.tool?.name ?? item.text;
|
|
1751
|
+
const actions = input.commandActions;
|
|
1752
|
+
if (Array.isArray(actions) && actions.length) {
|
|
1753
|
+
const parsed = actions.flatMap((value) => {
|
|
1754
|
+
if (!value || typeof value !== "object") return [];
|
|
1755
|
+
const action = value;
|
|
1756
|
+
switch (action.type) {
|
|
1757
|
+
case "read":
|
|
1758
|
+
return [
|
|
1759
|
+
{
|
|
1760
|
+
kind: "read",
|
|
1761
|
+
label: "Read",
|
|
1762
|
+
detail: text(action.path) || text(action.name)
|
|
1763
|
+
}
|
|
1764
|
+
];
|
|
1765
|
+
case "search": {
|
|
1766
|
+
const query = text(action.query);
|
|
1767
|
+
const path2 = text(action.path);
|
|
1768
|
+
return [
|
|
1769
|
+
{
|
|
1770
|
+
kind: "search",
|
|
1771
|
+
label: "Searched",
|
|
1772
|
+
detail: (query ? `for ${query}` : "code") + (path2 ? ` in ${path2}` : "")
|
|
1773
|
+
}
|
|
1774
|
+
];
|
|
1775
|
+
}
|
|
1776
|
+
case "listFiles":
|
|
1777
|
+
return [
|
|
1778
|
+
{
|
|
1779
|
+
kind: "list",
|
|
1780
|
+
label: "Listed",
|
|
1781
|
+
detail: text(action.path) || "files"
|
|
1782
|
+
}
|
|
1783
|
+
];
|
|
1784
|
+
default:
|
|
1785
|
+
return [
|
|
1786
|
+
{
|
|
1787
|
+
kind: "command",
|
|
1788
|
+
label: "Ran",
|
|
1789
|
+
detail: text(action.command) || text(input.command)
|
|
1790
|
+
}
|
|
1791
|
+
];
|
|
1792
|
+
}
|
|
1793
|
+
});
|
|
1794
|
+
if (parsed.length) return parsed;
|
|
1795
|
+
}
|
|
1796
|
+
const path = text(input.file_path) || text(input.path) || text(input.notebook_path);
|
|
1797
|
+
switch (name.toLowerCase()) {
|
|
1798
|
+
case "read":
|
|
1799
|
+
return [{ kind: "read", label: "Read", detail: path }];
|
|
1800
|
+
case "edit":
|
|
1801
|
+
case "write":
|
|
1802
|
+
case "multiedit":
|
|
1803
|
+
if (Array.isArray(input.changes)) {
|
|
1804
|
+
return input.changes.map((change) => ({
|
|
1805
|
+
kind: "edit",
|
|
1806
|
+
label: "Edited",
|
|
1807
|
+
detail: text(change?.path)
|
|
1808
|
+
}));
|
|
1809
|
+
}
|
|
1810
|
+
return [
|
|
1811
|
+
{
|
|
1812
|
+
kind: "edit",
|
|
1813
|
+
label: name.toLowerCase() === "write" ? "Wrote" : "Edited",
|
|
1814
|
+
detail: path
|
|
1815
|
+
}
|
|
1816
|
+
];
|
|
1817
|
+
case "grep":
|
|
1818
|
+
case "glob":
|
|
1819
|
+
return [
|
|
1820
|
+
{
|
|
1821
|
+
kind: "search",
|
|
1822
|
+
label: "Searched",
|
|
1823
|
+
detail: `for ${text(input.pattern)}` + (path ? ` in ${path}` : "")
|
|
1824
|
+
}
|
|
1825
|
+
];
|
|
1826
|
+
case "bash":
|
|
1827
|
+
return [{ kind: "command", label: "Ran", detail: text(input.command) }];
|
|
1828
|
+
case "websearch":
|
|
1829
|
+
return [
|
|
1830
|
+
{ kind: "web", label: "Searched the web", detail: text(input.query) }
|
|
1831
|
+
];
|
|
1832
|
+
case "webfetch":
|
|
1833
|
+
return [{ kind: "web", label: "Opened", detail: text(input.url) }];
|
|
1834
|
+
default:
|
|
1835
|
+
return [
|
|
1836
|
+
{
|
|
1837
|
+
kind: "tool",
|
|
1838
|
+
label: name.replace(/^mcp__/, "").replace("__", ":"),
|
|
1839
|
+
detail: item.detail ?? ""
|
|
1840
|
+
}
|
|
1841
|
+
];
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
function activitySummary(activities) {
|
|
1845
|
+
const labels = {
|
|
1846
|
+
read: "read files",
|
|
1847
|
+
search: "searched code",
|
|
1848
|
+
list: "listed files",
|
|
1849
|
+
edit: "edited files",
|
|
1850
|
+
command: "ran commands",
|
|
1851
|
+
web: "browsed the web",
|
|
1852
|
+
tool: "used tools"
|
|
1853
|
+
};
|
|
1854
|
+
const summary = [
|
|
1855
|
+
...new Set(activities.map((activity) => labels[activity.kind]))
|
|
1856
|
+
].join(", ");
|
|
1857
|
+
return summary ? summary[0].toUpperCase() + summary.slice(1) : "Activity";
|
|
1571
1858
|
}
|
|
1572
1859
|
|
|
1573
1860
|
// src/store/index.ts
|
|
1574
|
-
var TERMINAL_SESSION_STATUSES = /* @__PURE__ */ new Set([
|
|
1575
|
-
"completed",
|
|
1576
|
-
"error",
|
|
1577
|
-
"cancelled",
|
|
1578
|
-
"stopped"
|
|
1579
|
-
]);
|
|
1580
1861
|
function isConversationOver(session) {
|
|
1581
|
-
|
|
1582
|
-
return session.session_state === "closed";
|
|
1583
|
-
}
|
|
1584
|
-
return TERMINAL_SESSION_STATUSES.has(session.status);
|
|
1862
|
+
return session.conversation.state === "closed";
|
|
1585
1863
|
}
|
|
1586
1864
|
function seedTranscriptStore(store, seed) {
|
|
1587
1865
|
store.ingest({
|
|
@@ -1602,6 +1880,7 @@ var EMPTY_SNAPSHOT = {
|
|
|
1602
1880
|
acknowledgedMessageIds: /* @__PURE__ */ new Set(),
|
|
1603
1881
|
historyTruncated: false,
|
|
1604
1882
|
liveText: "",
|
|
1883
|
+
liveSummary: "",
|
|
1605
1884
|
liveOutputTokens: null,
|
|
1606
1885
|
lastEventAt: null,
|
|
1607
1886
|
conversationOver: false
|
|
@@ -1625,7 +1904,9 @@ var SessionTranscriptStore = class {
|
|
|
1625
1904
|
cursorSeq = 0;
|
|
1626
1905
|
// Derived-turn cache, keyed on the records array identity.
|
|
1627
1906
|
turnsFor = null;
|
|
1907
|
+
turnsSession = null;
|
|
1628
1908
|
turnsCache = [];
|
|
1909
|
+
summaryTurnId = null;
|
|
1629
1910
|
get cursor() {
|
|
1630
1911
|
return this.cursorSeq;
|
|
1631
1912
|
}
|
|
@@ -1640,9 +1921,10 @@ var SessionTranscriptStore = class {
|
|
|
1640
1921
|
// records_append batch) — what a chat-style transcript renders.
|
|
1641
1922
|
chatTurns = () => {
|
|
1642
1923
|
const records = this.snapshot.records;
|
|
1643
|
-
if (this.turnsFor !== records) {
|
|
1644
|
-
this.turnsCache = groupRecordsToChatTurns(records);
|
|
1924
|
+
if (this.turnsFor !== records || this.turnsSession !== this.snapshot.session) {
|
|
1925
|
+
this.turnsCache = groupRecordsToChatTurns(records, this.snapshot.session);
|
|
1645
1926
|
this.turnsFor = records;
|
|
1927
|
+
this.turnsSession = this.snapshot.session;
|
|
1646
1928
|
}
|
|
1647
1929
|
return this.turnsCache;
|
|
1648
1930
|
};
|
|
@@ -1668,7 +1950,8 @@ var SessionTranscriptStore = class {
|
|
|
1668
1950
|
sender_attribution_id: typeof payload.sender_attribution_id === "string" ? payload.sender_attribution_id : null,
|
|
1669
1951
|
created_at: record.created_at,
|
|
1670
1952
|
delivered_at: null,
|
|
1671
|
-
|
|
1953
|
+
images: [],
|
|
1954
|
+
turn_id: typeof payload.turn_id === "string" ? payload.turn_id : null
|
|
1672
1955
|
});
|
|
1673
1956
|
return true;
|
|
1674
1957
|
}
|
|
@@ -1679,7 +1962,7 @@ var SessionTranscriptStore = class {
|
|
|
1679
1962
|
...existing,
|
|
1680
1963
|
status: "delivered",
|
|
1681
1964
|
delivered_at: record.created_at,
|
|
1682
|
-
|
|
1965
|
+
turn_id: typeof payload.turn_id === "string" ? payload.turn_id : existing.turn_id
|
|
1683
1966
|
});
|
|
1684
1967
|
return true;
|
|
1685
1968
|
}
|
|
@@ -1690,7 +1973,7 @@ var SessionTranscriptStore = class {
|
|
|
1690
1973
|
...existing,
|
|
1691
1974
|
status: "pending",
|
|
1692
1975
|
delivered_at: null,
|
|
1693
|
-
|
|
1976
|
+
turn_id: typeof payload.requeued_turn_id === "string" ? payload.requeued_turn_id : null
|
|
1694
1977
|
});
|
|
1695
1978
|
return true;
|
|
1696
1979
|
}
|
|
@@ -1709,6 +1992,7 @@ var SessionTranscriptStore = class {
|
|
|
1709
1992
|
const frame = rawFrame;
|
|
1710
1993
|
switch (frame.type) {
|
|
1711
1994
|
case "snapshot": {
|
|
1995
|
+
next.liveSummary = "";
|
|
1712
1996
|
next.session = frame.session;
|
|
1713
1997
|
for (const message of frame.messages) {
|
|
1714
1998
|
this.inbox.set(message.id, message);
|
|
@@ -1733,6 +2017,8 @@ var SessionTranscriptStore = class {
|
|
|
1733
2017
|
acknowledgedNew = true;
|
|
1734
2018
|
}
|
|
1735
2019
|
if (record.source === "lifecycle") {
|
|
2020
|
+
if (record.record_type === "turn_started" && record.turn_id !== this.summaryTurnId || record.record_type === "turn_ended" && record.turn_id === this.summaryTurnId)
|
|
2021
|
+
next.liveSummary = "";
|
|
1736
2022
|
inboxChanged = this.applyMessageRecord(record) || inboxChanged;
|
|
1737
2023
|
if (record.record_type === "message_received" || record.record_type === "message_delivered") {
|
|
1738
2024
|
const payload = record.payload;
|
|
@@ -1754,17 +2040,34 @@ var SessionTranscriptStore = class {
|
|
|
1754
2040
|
case "session": {
|
|
1755
2041
|
next.session = frame.session;
|
|
1756
2042
|
next.conversationOver = isConversationOver(frame.session);
|
|
2043
|
+
if (next.conversationOver || frame.session.turn?.status !== "running") {
|
|
2044
|
+
next.liveSummary = "";
|
|
2045
|
+
}
|
|
1757
2046
|
break;
|
|
1758
2047
|
}
|
|
1759
2048
|
case "delta": {
|
|
2049
|
+
if (frame.kind === "thinking") {
|
|
2050
|
+
if (next.conversationOver) break;
|
|
2051
|
+
this.summaryTurnId = frame.turn_id;
|
|
2052
|
+
if (frame.text != null) next.liveSummary = frame.text;
|
|
2053
|
+
break;
|
|
2054
|
+
}
|
|
1760
2055
|
if (frame.kind !== "text") break;
|
|
1761
|
-
if (frame.text != null)
|
|
2056
|
+
if (frame.text != null) {
|
|
2057
|
+
next.liveText = prev.liveText + frame.text;
|
|
2058
|
+
next.liveSummary = "";
|
|
2059
|
+
}
|
|
1762
2060
|
if (frame.output_tokens != null)
|
|
1763
2061
|
next.liveOutputTokens = frame.output_tokens;
|
|
1764
2062
|
break;
|
|
1765
2063
|
}
|
|
1766
2064
|
case "done": {
|
|
1767
2065
|
next.conversationOver = true;
|
|
2066
|
+
next.liveSummary = "";
|
|
2067
|
+
break;
|
|
2068
|
+
}
|
|
2069
|
+
case "error": {
|
|
2070
|
+
next.liveSummary = "";
|
|
1768
2071
|
break;
|
|
1769
2072
|
}
|
|
1770
2073
|
default:
|
|
@@ -1777,9 +2080,9 @@ var SessionTranscriptStore = class {
|
|
|
1777
2080
|
export {
|
|
1778
2081
|
BRANCH_GLYPH,
|
|
1779
2082
|
LIVE_GLYPH,
|
|
1780
|
-
SESSION_WAKING,
|
|
1781
2083
|
SessionTranscriptStore,
|
|
1782
2084
|
USER_BAR,
|
|
2085
|
+
activitySummary,
|
|
1783
2086
|
awaitingAgentPhase,
|
|
1784
2087
|
cacheTierLabel,
|
|
1785
2088
|
chatTurnsToItems,
|
|
@@ -1790,10 +2093,15 @@ export {
|
|
|
1790
2093
|
codexMcpToolName,
|
|
1791
2094
|
collapseToolRuns,
|
|
1792
2095
|
deliveredUnechoedSends,
|
|
1793
|
-
|
|
2096
|
+
deriveEnvironmentState,
|
|
1794
2097
|
emptySessionTranscriptSnapshot,
|
|
1795
2098
|
entryBody,
|
|
1796
2099
|
entrySeconds,
|
|
2100
|
+
environmentOutputLine,
|
|
2101
|
+
environmentOutputLines,
|
|
2102
|
+
environmentOutputStep,
|
|
2103
|
+
environmentPhaseLabel,
|
|
2104
|
+
environmentSummary,
|
|
1797
2105
|
eventToItems,
|
|
1798
2106
|
foldCosts,
|
|
1799
2107
|
foldGutter,
|
|
@@ -1808,19 +2116,16 @@ export {
|
|
|
1808
2116
|
isFoldable,
|
|
1809
2117
|
isToolActivity,
|
|
1810
2118
|
isToolFold,
|
|
2119
|
+
isWorkFold,
|
|
1811
2120
|
lastLines,
|
|
1812
2121
|
layOutItems,
|
|
1813
2122
|
lifecycleText,
|
|
1814
2123
|
oneLine,
|
|
1815
2124
|
pendingToolCalls,
|
|
2125
|
+
reasoningSummaryTitle,
|
|
1816
2126
|
recordSlice,
|
|
1817
2127
|
recordToItems,
|
|
1818
2128
|
resultCostUsd,
|
|
1819
|
-
sandboxOutputLine,
|
|
1820
|
-
sandboxOutputLines,
|
|
1821
|
-
sandboxOutputStep,
|
|
1822
|
-
sandboxPhaseLabel,
|
|
1823
|
-
sandboxSummary,
|
|
1824
2129
|
seedTranscriptStore,
|
|
1825
2130
|
sessionLogText,
|
|
1826
2131
|
splitLedger,
|
|
@@ -1829,6 +2134,8 @@ export {
|
|
|
1829
2134
|
statusActivityText,
|
|
1830
2135
|
summarizeToolInput,
|
|
1831
2136
|
tailLines,
|
|
2137
|
+
toolActivities,
|
|
1832
2138
|
toolResultText,
|
|
1833
|
-
toolRunMembers
|
|
2139
|
+
toolRunMembers,
|
|
2140
|
+
turnEndedText
|
|
1834
2141
|
};
|