@ellipsis-dev/sdk 0.28.0 → 0.30.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-K252XHLI.js} +3 -3
- package/dist/index.d.ts +1746 -590
- package/dist/index.js +1226 -221
- package/dist/store/index.d.ts +51 -4
- package/dist/store/index.js +516 -121
- package/dist/stream/index.d.ts +2 -2
- package/dist/stream/index.js +1 -1
- package/dist/{types-B-jIFh4_.d.ts → types-DrPhhfOb.d.ts} +4994 -2554
- package/package.json +1 -1
- package/schema/collection_frames.schema.json +1466 -931
- package/schema/frames.schema.json +1707 -972
- package/schema/openapi.v1.json +14194 -4044
- 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-K252XHLI.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,6 +104,117 @@ 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 Set();
|
|
111
|
+
const turns = /* @__PURE__ */ new Map();
|
|
112
|
+
let starting = "queued";
|
|
113
|
+
let ready = false;
|
|
114
|
+
const getTurn = (id) => {
|
|
115
|
+
let turn = turns.get(id);
|
|
116
|
+
if (!turn) {
|
|
117
|
+
turn = {
|
|
118
|
+
timing: { id, startedAt: null, completedAt: null },
|
|
119
|
+
phase: ready ? "working" : "starting",
|
|
120
|
+
messages: /* @__PURE__ */ new Set()
|
|
121
|
+
};
|
|
122
|
+
turns.set(id, turn);
|
|
123
|
+
}
|
|
124
|
+
return turn;
|
|
125
|
+
};
|
|
126
|
+
const updatePending = () => {
|
|
127
|
+
for (const id of pending) messages.get(id).phase = starting;
|
|
128
|
+
};
|
|
129
|
+
const attach = (messageId, turnId) => {
|
|
130
|
+
const message = messages.get(messageId);
|
|
131
|
+
if (!message) return;
|
|
132
|
+
const turn = getTurn(turnId);
|
|
133
|
+
if (!turn.messages.has(messageId)) {
|
|
134
|
+
turn.messages.add(messageId);
|
|
135
|
+
message.turns.push(turn.timing);
|
|
136
|
+
}
|
|
137
|
+
pending.delete(messageId);
|
|
138
|
+
message.phase = turn.phase;
|
|
139
|
+
starting = "queued";
|
|
140
|
+
updatePending();
|
|
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: starting, turns: [] });
|
|
151
|
+
pending.add(messageId);
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
case "message_delivered":
|
|
155
|
+
if (messageId && turnId) attach(messageId, turnId);
|
|
156
|
+
break;
|
|
157
|
+
case "message_requeued":
|
|
158
|
+
if (messageId && messages.has(messageId)) {
|
|
159
|
+
pending.add(messageId);
|
|
160
|
+
messages.get(messageId).phase = "queued";
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
case "session_starting":
|
|
164
|
+
case "session_retrying":
|
|
165
|
+
ready = false;
|
|
166
|
+
starting = typeof p.wake_index === "number" && p.wake_index > 0 ? "waking" : "starting";
|
|
167
|
+
updatePending();
|
|
168
|
+
break;
|
|
169
|
+
case "session_resumed":
|
|
170
|
+
starting = "starting";
|
|
171
|
+
updatePending();
|
|
172
|
+
break;
|
|
173
|
+
case "turn_started":
|
|
174
|
+
if (turnId) getTurn(turnId).timing.startedAt = record.created_at;
|
|
175
|
+
break;
|
|
176
|
+
case "turn_completed":
|
|
177
|
+
case "turn_failed":
|
|
178
|
+
if (turnId) {
|
|
179
|
+
const turn = getTurn(turnId);
|
|
180
|
+
turn.timing.completedAt = record.created_at;
|
|
181
|
+
turn.phase = null;
|
|
182
|
+
for (const id of turn.messages) {
|
|
183
|
+
if (!pending.has(id)) messages.get(id).phase = null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
starting = "queued";
|
|
187
|
+
break;
|
|
188
|
+
case "session_idle":
|
|
189
|
+
case "session_closed":
|
|
190
|
+
case "session_cancelled":
|
|
191
|
+
ready = false;
|
|
192
|
+
starting = "queued";
|
|
193
|
+
for (const id of pending) messages.get(id).phase = null;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const claude = claudePayload(record);
|
|
199
|
+
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");
|
|
200
|
+
if (agentStarted) {
|
|
201
|
+
ready = true;
|
|
202
|
+
if (record.turn_id) {
|
|
203
|
+
const turn = getTurn(record.turn_id);
|
|
204
|
+
if (turn.timing.completedAt == null) {
|
|
205
|
+
turn.timing.startedAt ??= record.created_at;
|
|
206
|
+
turn.phase = "working";
|
|
207
|
+
for (const id of turn.messages) messages.get(id).phase = "working";
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (record.session_message_id && record.turn_id) {
|
|
212
|
+
attach(record.session_message_id, record.turn_id);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return messages;
|
|
216
|
+
}
|
|
217
|
+
|
|
106
218
|
// src/store/lifecycle.ts
|
|
107
219
|
function sandboxOutputStep(payload) {
|
|
108
220
|
if (typeof payload.step === "string" && payload.step) return payload.step;
|
|
@@ -221,8 +333,8 @@ function lifecycleText(recordType, payload) {
|
|
|
221
333
|
}
|
|
222
334
|
|
|
223
335
|
// src/store/transcript.ts
|
|
224
|
-
function oneLine(
|
|
225
|
-
const collapsed =
|
|
336
|
+
function oneLine(text2, max) {
|
|
337
|
+
const collapsed = text2.replace(/\s+/g, " ").trim();
|
|
226
338
|
return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 3)}...`;
|
|
227
339
|
}
|
|
228
340
|
function summarizeToolInput(name, input) {
|
|
@@ -377,15 +489,15 @@ function codexEventToItems(event, keyBase) {
|
|
|
377
489
|
const item = event.item;
|
|
378
490
|
switch (item.type) {
|
|
379
491
|
case "agent_message": {
|
|
380
|
-
const
|
|
381
|
-
if (
|
|
492
|
+
const text2 = (item.text ?? "").trim();
|
|
493
|
+
if (text2) push({ kind: "assistant", text: text2, spaceBefore: true });
|
|
382
494
|
break;
|
|
383
495
|
}
|
|
384
496
|
case "reasoning": {
|
|
385
497
|
const r = item;
|
|
386
|
-
const
|
|
387
|
-
if (
|
|
388
|
-
push({ kind: "thinking", gutter: "\u273B", text, spaceBefore: true });
|
|
498
|
+
const text2 = (r.text ?? r.summary ?? "").trim();
|
|
499
|
+
if (text2)
|
|
500
|
+
push({ kind: "thinking", gutter: "\u273B", text: text2, spaceBefore: true });
|
|
389
501
|
break;
|
|
390
502
|
}
|
|
391
503
|
case "command_execution": {
|
|
@@ -517,8 +629,8 @@ function recordToItems(record, keyBase, options = {}) {
|
|
|
517
629
|
] : [];
|
|
518
630
|
}
|
|
519
631
|
case "platform": {
|
|
520
|
-
const
|
|
521
|
-
return
|
|
632
|
+
const text2 = lifecycleText(record.record_type, record.payload);
|
|
633
|
+
return text2 ? [{ key: keyBase, kind: "notice", text: text2, spaceBefore: true }] : [];
|
|
522
634
|
}
|
|
523
635
|
default:
|
|
524
636
|
return [];
|
|
@@ -589,9 +701,9 @@ function collapseToolRuns(items, { working = false } = {}) {
|
|
|
589
701
|
flush(true);
|
|
590
702
|
return out;
|
|
591
703
|
}
|
|
592
|
-
function clampLines(
|
|
593
|
-
const lines =
|
|
594
|
-
if (lines.length <= maxLines) return { body:
|
|
704
|
+
function clampLines(text2, maxLines) {
|
|
705
|
+
const lines = text2.split("\n");
|
|
706
|
+
if (lines.length <= maxLines) return { body: text2, more: 0 };
|
|
595
707
|
return {
|
|
596
708
|
body: lines.slice(0, maxLines).join("\n"),
|
|
597
709
|
more: lines.length - maxLines
|
|
@@ -633,20 +745,29 @@ var SANDBOX_RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
|
633
745
|
"sandbox_output",
|
|
634
746
|
"sandbox_ready"
|
|
635
747
|
]);
|
|
636
|
-
function groupRecordsToChatTurns(records) {
|
|
748
|
+
function groupRecordsToChatTurns(records, session) {
|
|
637
749
|
const turns = [];
|
|
638
750
|
const toolNodeByUseId = /* @__PURE__ */ new Map();
|
|
639
751
|
const userTurnByMessageId = /* @__PURE__ */ new Map();
|
|
640
|
-
const
|
|
752
|
+
const deliveredTurnByMessageId = /* @__PURE__ */ new Map();
|
|
753
|
+
const failedTurnIds = /* @__PURE__ */ new Set();
|
|
754
|
+
const activeMessageByTurnId = /* @__PURE__ */ new Map();
|
|
755
|
+
let lastDeliveredMessageId;
|
|
641
756
|
let seq = 0;
|
|
642
757
|
const nextKey = (base) => `${base}:${seq++}`;
|
|
643
758
|
let openIdx = -1;
|
|
644
759
|
let initCount = 0;
|
|
645
760
|
let pendingResume = false;
|
|
646
|
-
const
|
|
761
|
+
const openSessionTurn = (record) => {
|
|
762
|
+
const messageId = (record.turn_id ? activeMessageByTurnId.get(record.turn_id) : void 0) ?? lastDeliveredMessageId;
|
|
763
|
+
if (openIdx >= 0 && (turns[openIdx].turnId !== (record.turn_id ?? void 0) || turns[openIdx].messageId !== messageId)) {
|
|
764
|
+
openIdx = -1;
|
|
765
|
+
}
|
|
647
766
|
if (openIdx < 0) {
|
|
648
767
|
turns.push({
|
|
649
768
|
key: nextKey(record.id),
|
|
769
|
+
messageId,
|
|
770
|
+
turnId: record.turn_id ?? void 0,
|
|
650
771
|
role: "assistant",
|
|
651
772
|
nodes: [],
|
|
652
773
|
startedAt: record.created_at,
|
|
@@ -672,6 +793,7 @@ function groupRecordsToChatTurns(records) {
|
|
|
672
793
|
resumed: false,
|
|
673
794
|
isError: false,
|
|
674
795
|
key: `msg:${messageId}`,
|
|
796
|
+
messageId,
|
|
675
797
|
role: "user",
|
|
676
798
|
nodes: [{ key: `msg:${messageId}`, kind: "user", text: body.trim() }],
|
|
677
799
|
startedAt: record.created_at,
|
|
@@ -685,19 +807,24 @@ function groupRecordsToChatTurns(records) {
|
|
|
685
807
|
continue;
|
|
686
808
|
}
|
|
687
809
|
if (record.record_type === "message_delivered" && messageId) {
|
|
688
|
-
if (typeof record.payload.turn_id === "string")
|
|
689
|
-
|
|
810
|
+
if (typeof record.payload.turn_id === "string") {
|
|
811
|
+
deliveredTurnByMessageId.set(messageId, record.payload.turn_id);
|
|
812
|
+
activeMessageByTurnId.set(record.payload.turn_id, messageId);
|
|
813
|
+
lastDeliveredMessageId = messageId;
|
|
814
|
+
}
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
if (record.record_type === "message_requeued" && messageId) {
|
|
818
|
+
deliveredTurnByMessageId.delete(messageId);
|
|
690
819
|
continue;
|
|
691
820
|
}
|
|
692
821
|
if (record.record_type === "turn_failed") {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const userTurn = owner ? userTurnByMessageId.get(owner) : void 0;
|
|
696
|
-
if (userTurn) userTurn.isError = true;
|
|
822
|
+
if (typeof record.payload.turn_id === "string")
|
|
823
|
+
failedTurnIds.add(record.payload.turn_id);
|
|
697
824
|
continue;
|
|
698
825
|
}
|
|
699
|
-
const
|
|
700
|
-
if (!
|
|
826
|
+
const text2 = lifecycleText(record.record_type, record.payload);
|
|
827
|
+
if (!text2) continue;
|
|
701
828
|
const isSandbox = SANDBOX_RECORD_TYPES.has(record.record_type);
|
|
702
829
|
const isSandboxOutput = record.record_type === "sandbox_output";
|
|
703
830
|
const step = isSandboxOutput ? sandboxOutputStep(record.payload) : void 0;
|
|
@@ -718,13 +845,13 @@ function groupRecordsToChatTurns(records) {
|
|
|
718
845
|
if (foldTarget) {
|
|
719
846
|
const last = foldTarget.nodes[foldTarget.nodes.length - 1];
|
|
720
847
|
if (isSandboxOutput && last && last.kind === "lifecycle" && last.recordType === "sandbox_output" && last.step === step) {
|
|
721
|
-
last.text =
|
|
848
|
+
last.text = text2;
|
|
722
849
|
last.lines = [...last.lines ?? [], ...lines ?? []];
|
|
723
850
|
} else {
|
|
724
851
|
foldTarget.nodes.push({
|
|
725
852
|
key: nextKey(record.id),
|
|
726
853
|
kind: "lifecycle",
|
|
727
|
-
text,
|
|
854
|
+
text: text2,
|
|
728
855
|
recordType: record.record_type,
|
|
729
856
|
step,
|
|
730
857
|
lines,
|
|
@@ -741,7 +868,7 @@ function groupRecordsToChatTurns(records) {
|
|
|
741
868
|
{
|
|
742
869
|
key: nextKey(record.id),
|
|
743
870
|
kind: "lifecycle",
|
|
744
|
-
text,
|
|
871
|
+
text: text2,
|
|
745
872
|
recordType: record.record_type,
|
|
746
873
|
step,
|
|
747
874
|
lines,
|
|
@@ -758,9 +885,14 @@ function groupRecordsToChatTurns(records) {
|
|
|
758
885
|
});
|
|
759
886
|
continue;
|
|
760
887
|
}
|
|
888
|
+
if (record.session_message_id) {
|
|
889
|
+
lastDeliveredMessageId = record.session_message_id;
|
|
890
|
+
if (record.turn_id)
|
|
891
|
+
activeMessageByTurnId.set(record.turn_id, record.session_message_id);
|
|
892
|
+
}
|
|
761
893
|
if (record.kind === "codex") {
|
|
762
894
|
codexEventIntoTurns(record, record.payload, {
|
|
763
|
-
|
|
895
|
+
openSessionTurn,
|
|
764
896
|
closeTurn: (completedAt, isError) => {
|
|
765
897
|
if (openIdx >= 0) {
|
|
766
898
|
turns[openIdx].completedAt = completedAt;
|
|
@@ -782,7 +914,7 @@ function groupRecordsToChatTurns(records) {
|
|
|
782
914
|
openIdx = -1;
|
|
783
915
|
}
|
|
784
916
|
} else if (content.kind === "tool") {
|
|
785
|
-
|
|
917
|
+
openSessionTurn(record).nodes.push({
|
|
786
918
|
key: nextKey(record.id),
|
|
787
919
|
kind: "tool",
|
|
788
920
|
name: content.name,
|
|
@@ -794,12 +926,14 @@ function groupRecordsToChatTurns(records) {
|
|
|
794
926
|
completedAt: record.created_at
|
|
795
927
|
});
|
|
796
928
|
} else if (content.text.trim()) {
|
|
797
|
-
const turn2 =
|
|
929
|
+
const turn2 = openSessionTurn(record);
|
|
798
930
|
if (content.kind === "error") turn2.isError = true;
|
|
799
931
|
turn2.nodes.push({
|
|
800
932
|
key: nextKey(record.id),
|
|
801
933
|
kind: content.kind === "error" ? "assistant" : content.kind,
|
|
802
|
-
text: content.text.trim()
|
|
934
|
+
text: content.text.trim(),
|
|
935
|
+
createdAt: record.created_at,
|
|
936
|
+
...content.kind === "assistant" && content.phase != null ? { phase: content.phase } : {}
|
|
803
937
|
});
|
|
804
938
|
}
|
|
805
939
|
continue;
|
|
@@ -856,6 +990,9 @@ function groupRecordsToChatTurns(records) {
|
|
|
856
990
|
const node = toolNodeByUseId.get(useId);
|
|
857
991
|
if (node) {
|
|
858
992
|
node.result = body;
|
|
993
|
+
if (record.kind === "claude_code" || record.kind === "claude_sdk") {
|
|
994
|
+
node.resultDetails = record.payload.tool_use_result;
|
|
995
|
+
}
|
|
859
996
|
node.isError = !!block.is_error;
|
|
860
997
|
node.completedAt = record.created_at;
|
|
861
998
|
} else {
|
|
@@ -870,14 +1007,14 @@ function groupRecordsToChatTurns(records) {
|
|
|
870
1007
|
startedAt: null,
|
|
871
1008
|
completedAt: record.created_at
|
|
872
1009
|
};
|
|
873
|
-
|
|
1010
|
+
openSessionTurn(record).nodes.push(orphan);
|
|
874
1011
|
toolNodeByUseId.set(useId, orphan);
|
|
875
1012
|
}
|
|
876
1013
|
}
|
|
877
1014
|
continue;
|
|
878
1015
|
}
|
|
879
1016
|
if (data.kind !== "assistant") continue;
|
|
880
|
-
const turn =
|
|
1017
|
+
const turn = openSessionTurn(record);
|
|
881
1018
|
if (data.usage && typeof data.usage.output_tokens === "number") {
|
|
882
1019
|
turn.tokens = (turn.tokens ?? 0) + data.usage.output_tokens;
|
|
883
1020
|
}
|
|
@@ -906,11 +1043,60 @@ function groupRecordsToChatTurns(records) {
|
|
|
906
1043
|
turn.nodes.push({
|
|
907
1044
|
key: nextKey(record.id),
|
|
908
1045
|
kind: "assistant",
|
|
909
|
-
text: block.text.trim()
|
|
1046
|
+
text: block.text.trim(),
|
|
1047
|
+
createdAt: record.created_at
|
|
910
1048
|
});
|
|
911
1049
|
}
|
|
912
1050
|
}
|
|
913
1051
|
}
|
|
1052
|
+
for (const [messageId, turnId] of deliveredTurnByMessageId) {
|
|
1053
|
+
const turn = userTurnByMessageId.get(messageId);
|
|
1054
|
+
if (turn) turn.isError = failedTurnIds.has(turnId);
|
|
1055
|
+
}
|
|
1056
|
+
for (const [messageId, progress] of messageProgress(records)) {
|
|
1057
|
+
const turn = userTurnByMessageId.get(messageId);
|
|
1058
|
+
if (turn) turn.progress = progress;
|
|
1059
|
+
}
|
|
1060
|
+
const initialTurn = records.find(
|
|
1061
|
+
(r) => r.kind === "platform" && r.record_type === "turn_started" && r.payload.turn_index === 0
|
|
1062
|
+
);
|
|
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)
|
|
1065
|
+
);
|
|
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)
|
|
1068
|
+
);
|
|
1069
|
+
const initialMessage = initialReceived ?? initialDelivery;
|
|
1070
|
+
const initialId = initialMessage?.kind === "platform" && typeof initialMessage.payload.message_id === "string" ? initialMessage.payload.message_id : void 0;
|
|
1071
|
+
const opening = initialId ? userTurnByMessageId.get(initialId) : void 0;
|
|
1072
|
+
const includesStart = records.length === 0 || records.some((r) => r.record_type === "session_scheduled");
|
|
1073
|
+
const prompt = (session?.claude_code ?? session?.codex)?.prompt;
|
|
1074
|
+
if (prompt?.trim() && session && (opening || includesStart)) {
|
|
1075
|
+
const first = opening ?? {
|
|
1076
|
+
key: "prompt",
|
|
1077
|
+
role: "user",
|
|
1078
|
+
nodes: [{ key: "prompt", kind: "user", text: prompt }],
|
|
1079
|
+
startedAt: session.lifecycle.timestamps.created_at,
|
|
1080
|
+
completedAt: session.lifecycle.timestamps.created_at,
|
|
1081
|
+
durationMs: null,
|
|
1082
|
+
costUsd: null,
|
|
1083
|
+
tokens: null,
|
|
1084
|
+
resumed: false,
|
|
1085
|
+
isError: false,
|
|
1086
|
+
progress: { phase: "starting", turns: [] }
|
|
1087
|
+
};
|
|
1088
|
+
if (opening) turns.splice(turns.indexOf(opening), 1);
|
|
1089
|
+
first.key = "prompt";
|
|
1090
|
+
first.startedAt = session.lifecycle.timestamps.created_at;
|
|
1091
|
+
turns.unshift(first);
|
|
1092
|
+
if (!opening) {
|
|
1093
|
+
for (const turn of turns) {
|
|
1094
|
+
if (turn !== first && turn.role === "user" && turn.progress && !turn.progress.turns.length) {
|
|
1095
|
+
turn.progress.phase = turn.progress.phase === null ? null : "queued";
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
914
1100
|
return turns;
|
|
915
1101
|
}
|
|
916
1102
|
function codexEventIntoTurns(record, event, ops) {
|
|
@@ -919,36 +1105,38 @@ function codexEventIntoTurns(record, event, ops) {
|
|
|
919
1105
|
return;
|
|
920
1106
|
}
|
|
921
1107
|
if (event.type === "error") {
|
|
922
|
-
const turn2 = ops.
|
|
1108
|
+
const turn2 = ops.openSessionTurn(record);
|
|
923
1109
|
turn2.nodes.push({
|
|
924
1110
|
key: ops.nextKey(record.id),
|
|
925
1111
|
kind: "assistant",
|
|
926
|
-
text: event.message?.trim() || "error"
|
|
1112
|
+
text: event.message?.trim() || "error",
|
|
1113
|
+
createdAt: record.created_at
|
|
927
1114
|
});
|
|
928
1115
|
return;
|
|
929
1116
|
}
|
|
930
1117
|
if (event.type !== "item.completed") return;
|
|
931
1118
|
const item = event.item;
|
|
932
|
-
const turn = () => ops.
|
|
1119
|
+
const turn = () => ops.openSessionTurn(record);
|
|
933
1120
|
switch (item.type) {
|
|
934
1121
|
case "agent_message": {
|
|
935
|
-
const
|
|
936
|
-
if (
|
|
1122
|
+
const text2 = (item.text ?? "").trim();
|
|
1123
|
+
if (text2)
|
|
937
1124
|
turn().nodes.push({
|
|
938
1125
|
key: ops.nextKey(record.id),
|
|
939
1126
|
kind: "assistant",
|
|
940
|
-
text
|
|
1127
|
+
text: text2,
|
|
1128
|
+
createdAt: record.created_at
|
|
941
1129
|
});
|
|
942
1130
|
break;
|
|
943
1131
|
}
|
|
944
1132
|
case "reasoning": {
|
|
945
1133
|
const r = item;
|
|
946
|
-
const
|
|
947
|
-
if (
|
|
1134
|
+
const text2 = (r.text ?? r.summary ?? "").trim();
|
|
1135
|
+
if (text2)
|
|
948
1136
|
turn().nodes.push({
|
|
949
1137
|
key: ops.nextKey(record.id),
|
|
950
1138
|
kind: "thinking",
|
|
951
|
-
text
|
|
1139
|
+
text: text2
|
|
952
1140
|
});
|
|
953
1141
|
break;
|
|
954
1142
|
}
|
|
@@ -1164,8 +1352,8 @@ function deriveSandboxState(records, minFeedSeq) {
|
|
|
1164
1352
|
let configCommitSha = null;
|
|
1165
1353
|
let log = [];
|
|
1166
1354
|
let open = /* @__PURE__ */ new Map();
|
|
1167
|
-
const push = (record, kind,
|
|
1168
|
-
const entry = { key: `${record.feed_seq}:${log.length}`, kind, text };
|
|
1355
|
+
const push = (record, kind, text2) => {
|
|
1356
|
+
const entry = { key: `${record.feed_seq}:${log.length}`, kind, text: text2 };
|
|
1169
1357
|
log.push(entry);
|
|
1170
1358
|
return entry;
|
|
1171
1359
|
};
|
|
@@ -1190,8 +1378,8 @@ function deriveSandboxState(records, minFeedSeq) {
|
|
|
1190
1378
|
case "session_starting":
|
|
1191
1379
|
case "session_retrying": {
|
|
1192
1380
|
seen = true;
|
|
1193
|
-
const
|
|
1194
|
-
headline = !
|
|
1381
|
+
const text2 = lifecycleText(record.record_type, p);
|
|
1382
|
+
headline = !text2 || text2 === "Session starting\u2026" ? STARTING_SANDBOX : text2;
|
|
1195
1383
|
done = false;
|
|
1196
1384
|
readySeconds = null;
|
|
1197
1385
|
reset();
|
|
@@ -1220,7 +1408,7 @@ function deriveSandboxState(records, minFeedSeq) {
|
|
|
1220
1408
|
const dur = msLabel(p.duration_ms);
|
|
1221
1409
|
const failed = p.status === "failed";
|
|
1222
1410
|
const base = failed ? `${label} failed` : label;
|
|
1223
|
-
const
|
|
1411
|
+
const text2 = [
|
|
1224
1412
|
base,
|
|
1225
1413
|
...tier ? [tier] : [],
|
|
1226
1414
|
...dur ? [dur] : []
|
|
@@ -1228,10 +1416,10 @@ function deriveSandboxState(records, minFeedSeq) {
|
|
|
1228
1416
|
const line = open.get(key);
|
|
1229
1417
|
if (line) {
|
|
1230
1418
|
line.kind = failed ? "failed" : "done";
|
|
1231
|
-
line.text =
|
|
1419
|
+
line.text = text2;
|
|
1232
1420
|
open.delete(key);
|
|
1233
1421
|
} else {
|
|
1234
|
-
push(record, failed ? "failed" : "done",
|
|
1422
|
+
push(record, failed ? "failed" : "done", text2);
|
|
1235
1423
|
}
|
|
1236
1424
|
} else if (!open.has(key)) {
|
|
1237
1425
|
open.set(key, push(record, "step", `${label}\u2026`));
|
|
@@ -1296,8 +1484,8 @@ function startupSettled(sandbox, status) {
|
|
|
1296
1484
|
}
|
|
1297
1485
|
function startupHeadline(sandbox, status) {
|
|
1298
1486
|
const activity = statusActivityText(status);
|
|
1299
|
-
const
|
|
1300
|
-
return `${
|
|
1487
|
+
const text2 = !sandbox || sandbox.done ? activity ?? STARTING_SANDBOX : sandbox.headline;
|
|
1488
|
+
return `${text2.replace(/…$/, "")}\u2026`;
|
|
1301
1489
|
}
|
|
1302
1490
|
function sandboxSummary(sandbox) {
|
|
1303
1491
|
const seconds = sandbox?.readySeconds ?? null;
|
|
@@ -1342,13 +1530,13 @@ function chatTurnsToItems(turns) {
|
|
|
1342
1530
|
settleWake();
|
|
1343
1531
|
continue;
|
|
1344
1532
|
}
|
|
1345
|
-
const
|
|
1346
|
-
if (!
|
|
1347
|
-
const waking =
|
|
1533
|
+
const text2 = sessionLogText(node.recordType, node.payload ?? {});
|
|
1534
|
+
if (!text2) continue;
|
|
1535
|
+
const waking = text2 === SESSION_WAKING;
|
|
1348
1536
|
items.push({
|
|
1349
1537
|
key: node.key,
|
|
1350
1538
|
kind: "notice",
|
|
1351
|
-
text,
|
|
1539
|
+
text: text2,
|
|
1352
1540
|
spaceBefore: true,
|
|
1353
1541
|
...waking ? { loading: true } : {}
|
|
1354
1542
|
});
|
|
@@ -1363,6 +1551,7 @@ function chatTurnsToItems(turns) {
|
|
|
1363
1551
|
key: node.key,
|
|
1364
1552
|
kind: "user",
|
|
1365
1553
|
text: node.text,
|
|
1554
|
+
createdAt: turn.startedAt,
|
|
1366
1555
|
spaceBefore: true,
|
|
1367
1556
|
...turn.isError ? { isError: true } : {}
|
|
1368
1557
|
});
|
|
@@ -1376,6 +1565,7 @@ function chatTurnsToItems(turns) {
|
|
|
1376
1565
|
key: node.key,
|
|
1377
1566
|
kind: "assistant",
|
|
1378
1567
|
text: node.text,
|
|
1568
|
+
createdAt: node.createdAt,
|
|
1379
1569
|
spaceBefore: true
|
|
1380
1570
|
});
|
|
1381
1571
|
} else if (node.kind === "thinking") {
|
|
@@ -1430,14 +1620,19 @@ function isToolActivity(item) {
|
|
|
1430
1620
|
return item.kind === "tool" || item.kind === "tool_result" || isToolFold(item);
|
|
1431
1621
|
}
|
|
1432
1622
|
function layOutItems(items) {
|
|
1433
|
-
|
|
1623
|
+
const isActivity = (item) => item.kind === "thinking" || isToolActivity(item);
|
|
1624
|
+
return items.map((item, index) => ({
|
|
1625
|
+
item,
|
|
1626
|
+
nested: false,
|
|
1627
|
+
attach: index > 0 && isActivity(item) && isActivity(items[index - 1])
|
|
1628
|
+
}));
|
|
1434
1629
|
}
|
|
1435
1630
|
function foldGutter(nested) {
|
|
1436
1631
|
return nested ? BRANCH_GLYPH : "\u25CF";
|
|
1437
1632
|
}
|
|
1438
|
-
function tailLines(
|
|
1439
|
-
const lines =
|
|
1440
|
-
if (lines.length <= maxLines) return { body:
|
|
1633
|
+
function tailLines(text2, maxLines) {
|
|
1634
|
+
const lines = text2.split("\n");
|
|
1635
|
+
if (lines.length <= maxLines) return { body: text2, hidden: 0 };
|
|
1441
1636
|
return {
|
|
1442
1637
|
body: lines.slice(lines.length - maxLines).join("\n"),
|
|
1443
1638
|
hidden: lines.length - maxLines
|
|
@@ -1462,24 +1657,44 @@ function toolRunMembers(items) {
|
|
|
1462
1657
|
function splitLedger(turns) {
|
|
1463
1658
|
const preamble = [];
|
|
1464
1659
|
const entries = [];
|
|
1660
|
+
const entryByTurn = /* @__PURE__ */ new Map();
|
|
1661
|
+
const entryByMessage = /* @__PURE__ */ new Map();
|
|
1465
1662
|
for (const turn of turns) {
|
|
1466
1663
|
if (turn.role === "user") {
|
|
1467
1664
|
const node = turn.nodes.find((n) => n.kind === "user");
|
|
1468
|
-
|
|
1665
|
+
const entry = {
|
|
1469
1666
|
key: turn.key,
|
|
1470
1667
|
prompt: node?.text ?? "",
|
|
1668
|
+
createdAt: turn.startedAt,
|
|
1471
1669
|
cancelled: turn.isError,
|
|
1472
|
-
turns: []
|
|
1473
|
-
|
|
1670
|
+
turns: [],
|
|
1671
|
+
...turn.progress ? { progress: turn.progress } : {}
|
|
1672
|
+
};
|
|
1673
|
+
entries.push(entry);
|
|
1674
|
+
entryByTurn.set(turn, entry);
|
|
1675
|
+
if (turn.messageId) entryByMessage.set(turn.messageId, entry);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
let last;
|
|
1679
|
+
for (const turn of turns) {
|
|
1680
|
+
if (turn.role === "user") {
|
|
1681
|
+
last = entryByTurn.get(turn);
|
|
1474
1682
|
continue;
|
|
1475
1683
|
}
|
|
1476
|
-
const
|
|
1477
|
-
if (
|
|
1684
|
+
const owner = turn.messageId ? entryByMessage.get(turn.messageId) : last;
|
|
1685
|
+
if (owner) owner.turns.push(turn);
|
|
1478
1686
|
else preamble.push(turn);
|
|
1479
1687
|
}
|
|
1480
1688
|
return { preamble, entries };
|
|
1481
1689
|
}
|
|
1482
1690
|
function entrySeconds(entry, now) {
|
|
1691
|
+
if (entry.progress?.turns.length) {
|
|
1692
|
+
return entry.progress.turns.reduce((ms2, turn) => {
|
|
1693
|
+
if (turn.startedAt == null) return ms2;
|
|
1694
|
+
const end = turn.completedAt == null ? now : Date.parse(turn.completedAt);
|
|
1695
|
+
return ms2 + Math.max(0, end - Date.parse(turn.startedAt));
|
|
1696
|
+
}, 0) / 1e3;
|
|
1697
|
+
}
|
|
1483
1698
|
let ms = 0;
|
|
1484
1699
|
for (const turn of entry.turns) {
|
|
1485
1700
|
if (turn.role !== "assistant" || turn.startedAt == null) continue;
|
|
@@ -1491,12 +1706,15 @@ function entrySeconds(entry, now) {
|
|
|
1491
1706
|
}
|
|
1492
1707
|
return ms / 1e3;
|
|
1493
1708
|
}
|
|
1494
|
-
function
|
|
1709
|
+
function isWorkFold(item) {
|
|
1710
|
+
return item.key.startsWith("work:");
|
|
1711
|
+
}
|
|
1712
|
+
function foldToolTurn(raw, members) {
|
|
1495
1713
|
const items = [];
|
|
1496
1714
|
let run = [];
|
|
1497
1715
|
const flush = () => {
|
|
1498
1716
|
if (run.length === 0) return;
|
|
1499
|
-
const fold = foldRun(run,
|
|
1717
|
+
const fold = foldRun(run, false);
|
|
1500
1718
|
members.set(fold.key, run);
|
|
1501
1719
|
items.push(fold);
|
|
1502
1720
|
run = [];
|
|
@@ -1511,77 +1729,224 @@ function foldToolTurn(raw, live, members) {
|
|
|
1511
1729
|
flush();
|
|
1512
1730
|
return items;
|
|
1513
1731
|
}
|
|
1732
|
+
function finalReplyKeys(entry, live) {
|
|
1733
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1734
|
+
for (const turn of entry.turns) {
|
|
1735
|
+
if (turn.role !== "assistant") continue;
|
|
1736
|
+
for (const node of turn.nodes) {
|
|
1737
|
+
if (node.kind === "assistant" && node.phase === "final_answer") {
|
|
1738
|
+
keys.add(node.key);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
if (entry.cancelled || turn.isError) continue;
|
|
1742
|
+
if (live && turn.completedAt == null && turn.durationMs == null) continue;
|
|
1743
|
+
for (let i = turn.nodes.length - 1; i >= 0; i--) {
|
|
1744
|
+
const node = turn.nodes[i];
|
|
1745
|
+
if (node.kind !== "assistant" || node.phase === "commentary") break;
|
|
1746
|
+
keys.add(node.key);
|
|
1747
|
+
const previous = turn.nodes[i - 1];
|
|
1748
|
+
if (node.createdAt == null || previous?.kind !== "assistant" || previous.createdAt !== node.createdAt)
|
|
1749
|
+
break;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
return keys;
|
|
1753
|
+
}
|
|
1754
|
+
function reasoningSummaryTitle(summary) {
|
|
1755
|
+
const line = summary.trim().split(/\r?\n/, 1)[0] ?? "";
|
|
1756
|
+
const title = line.replace(/^#{1,6}\s+/, "").replace(/^[*_`]+|[*_`]+$/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").trim();
|
|
1757
|
+
return title.length > 120 ? `${title.slice(0, 119).trimEnd()}\u2026` : title;
|
|
1758
|
+
}
|
|
1514
1759
|
function entryBody(entry, {
|
|
1515
1760
|
live,
|
|
1516
1761
|
now,
|
|
1517
|
-
liveText = ""
|
|
1762
|
+
liveText = "",
|
|
1763
|
+
liveSummary = ""
|
|
1518
1764
|
}) {
|
|
1765
|
+
live = live && (entry.progress === void 0 || entry.progress.phase !== null);
|
|
1519
1766
|
const raw = chatTurnsToItems(entry.turns);
|
|
1767
|
+
const replies = finalReplyKeys(entry, live);
|
|
1520
1768
|
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
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1769
|
+
const items = [];
|
|
1770
|
+
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
|
+
const phase = entry.progress?.phase;
|
|
1779
|
+
const seconds = entry.progress && !entry.progress.turns.some((turn) => turn.startedAt != null) ? null : entrySeconds(entry, now);
|
|
1780
|
+
const key = `work:${entry.key}`;
|
|
1781
|
+
const header = {
|
|
1782
|
+
key,
|
|
1783
|
+
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)}`,
|
|
1785
|
+
spaceBefore: true,
|
|
1786
|
+
...entry.cancelled || entry.turns.some((turn) => turn.isError) ? { isError: true } : {},
|
|
1787
|
+
...live ? { loading: true } : {}
|
|
1788
|
+
};
|
|
1789
|
+
if (live && (phase === void 0 || phase === "working")) {
|
|
1790
|
+
const visible = raw.filter((item) => !wakeKeys.has(item.key)).map(({ loading: _, ...item }) => item);
|
|
1791
|
+
if (liveText !== "") {
|
|
1792
|
+
visible.push({
|
|
1793
|
+
key: `live:${entry.key}`,
|
|
1794
|
+
kind: "assistant",
|
|
1795
|
+
text: liveText,
|
|
1544
1796
|
spaceBefore: true
|
|
1545
1797
|
});
|
|
1546
1798
|
}
|
|
1799
|
+
return {
|
|
1800
|
+
items: [header, ...foldToolTurn(visible, members)],
|
|
1801
|
+
members,
|
|
1802
|
+
seconds
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
const addWork = (item) => {
|
|
1806
|
+
if (work.length === 0) items.push(header);
|
|
1807
|
+
work.push(item);
|
|
1808
|
+
};
|
|
1809
|
+
for (const item of raw) {
|
|
1810
|
+
const { loading: _, ...settled } = item;
|
|
1811
|
+
if (!wakeKeys.has(item.key) && (replies.has(item.key) || !["assistant", "thinking", "tool", "tool_result"].includes(item.kind))) {
|
|
1812
|
+
items.push(settled);
|
|
1813
|
+
} else {
|
|
1814
|
+
addWork(settled);
|
|
1815
|
+
}
|
|
1547
1816
|
}
|
|
1548
1817
|
if (live && liveText !== "") {
|
|
1549
|
-
|
|
1818
|
+
addWork({
|
|
1550
1819
|
key: `live:${entry.key}`,
|
|
1551
1820
|
kind: "assistant",
|
|
1552
1821
|
text: liveText,
|
|
1553
1822
|
spaceBefore: true
|
|
1554
1823
|
});
|
|
1555
1824
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
}
|
|
1561
|
-
return { items, members };
|
|
1825
|
+
if (work.length > 0) {
|
|
1826
|
+
members.set(key, foldToolTurn(work, members));
|
|
1827
|
+
} else if (live) {
|
|
1828
|
+
items.unshift(header);
|
|
1829
|
+
}
|
|
1830
|
+
return { items, members, seconds };
|
|
1562
1831
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1832
|
+
|
|
1833
|
+
// src/store/activity.ts
|
|
1834
|
+
var text = (value) => typeof value === "string" ? value : "";
|
|
1835
|
+
function toolActivities(item) {
|
|
1836
|
+
const input = item.tool?.input ?? {};
|
|
1837
|
+
const name = item.tool?.name ?? item.text;
|
|
1838
|
+
const actions = input.commandActions;
|
|
1839
|
+
if (Array.isArray(actions) && actions.length) {
|
|
1840
|
+
const parsed = actions.flatMap((value) => {
|
|
1841
|
+
if (!value || typeof value !== "object") return [];
|
|
1842
|
+
const action = value;
|
|
1843
|
+
switch (action.type) {
|
|
1844
|
+
case "read":
|
|
1845
|
+
return [
|
|
1846
|
+
{
|
|
1847
|
+
kind: "read",
|
|
1848
|
+
label: "Read",
|
|
1849
|
+
detail: text(action.path) || text(action.name)
|
|
1850
|
+
}
|
|
1851
|
+
];
|
|
1852
|
+
case "search": {
|
|
1853
|
+
const query = text(action.query);
|
|
1854
|
+
const path2 = text(action.path);
|
|
1855
|
+
return [
|
|
1856
|
+
{
|
|
1857
|
+
kind: "search",
|
|
1858
|
+
label: "Searched",
|
|
1859
|
+
detail: (query ? `for ${query}` : "code") + (path2 ? ` in ${path2}` : "")
|
|
1860
|
+
}
|
|
1861
|
+
];
|
|
1862
|
+
}
|
|
1863
|
+
case "listFiles":
|
|
1864
|
+
return [
|
|
1865
|
+
{
|
|
1866
|
+
kind: "list",
|
|
1867
|
+
label: "Listed",
|
|
1868
|
+
detail: text(action.path) || "files"
|
|
1869
|
+
}
|
|
1870
|
+
];
|
|
1871
|
+
default:
|
|
1872
|
+
return [
|
|
1873
|
+
{
|
|
1874
|
+
kind: "command",
|
|
1875
|
+
label: "Ran",
|
|
1876
|
+
detail: text(action.command) || text(input.command)
|
|
1877
|
+
}
|
|
1878
|
+
];
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
if (parsed.length) return parsed;
|
|
1882
|
+
}
|
|
1883
|
+
const path = text(input.file_path) || text(input.path) || text(input.notebook_path);
|
|
1884
|
+
switch (name.toLowerCase()) {
|
|
1885
|
+
case "read":
|
|
1886
|
+
return [{ kind: "read", label: "Read", detail: path }];
|
|
1887
|
+
case "edit":
|
|
1888
|
+
case "write":
|
|
1889
|
+
case "multiedit":
|
|
1890
|
+
if (Array.isArray(input.changes)) {
|
|
1891
|
+
return input.changes.map((change) => ({
|
|
1892
|
+
kind: "edit",
|
|
1893
|
+
label: "Edited",
|
|
1894
|
+
detail: text(change?.path)
|
|
1895
|
+
}));
|
|
1896
|
+
}
|
|
1897
|
+
return [
|
|
1898
|
+
{
|
|
1899
|
+
kind: "edit",
|
|
1900
|
+
label: name.toLowerCase() === "write" ? "Wrote" : "Edited",
|
|
1901
|
+
detail: path
|
|
1902
|
+
}
|
|
1903
|
+
];
|
|
1904
|
+
case "grep":
|
|
1905
|
+
case "glob":
|
|
1906
|
+
return [
|
|
1907
|
+
{
|
|
1908
|
+
kind: "search",
|
|
1909
|
+
label: "Searched",
|
|
1910
|
+
detail: `for ${text(input.pattern)}` + (path ? ` in ${path}` : "")
|
|
1911
|
+
}
|
|
1912
|
+
];
|
|
1913
|
+
case "bash":
|
|
1914
|
+
return [{ kind: "command", label: "Ran", detail: text(input.command) }];
|
|
1915
|
+
case "websearch":
|
|
1916
|
+
return [
|
|
1917
|
+
{ kind: "web", label: "Searched the web", detail: text(input.query) }
|
|
1918
|
+
];
|
|
1919
|
+
case "webfetch":
|
|
1920
|
+
return [{ kind: "web", label: "Opened", detail: text(input.url) }];
|
|
1921
|
+
default:
|
|
1922
|
+
return [
|
|
1923
|
+
{
|
|
1924
|
+
kind: "tool",
|
|
1925
|
+
label: name.replace(/^mcp__/, "").replace("__", ":"),
|
|
1926
|
+
detail: item.detail ?? ""
|
|
1927
|
+
}
|
|
1928
|
+
];
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
function activitySummary(activities) {
|
|
1932
|
+
const labels = {
|
|
1933
|
+
read: "read files",
|
|
1934
|
+
search: "searched code",
|
|
1935
|
+
list: "listed files",
|
|
1936
|
+
edit: "edited files",
|
|
1937
|
+
command: "ran commands",
|
|
1938
|
+
web: "browsed the web",
|
|
1939
|
+
tool: "used tools"
|
|
1940
|
+
};
|
|
1941
|
+
const summary = [
|
|
1942
|
+
...new Set(activities.map((activity) => labels[activity.kind]))
|
|
1943
|
+
].join(", ");
|
|
1944
|
+
return summary ? summary[0].toUpperCase() + summary.slice(1) : "Activity";
|
|
1571
1945
|
}
|
|
1572
1946
|
|
|
1573
1947
|
// src/store/index.ts
|
|
1574
|
-
var TERMINAL_SESSION_STATUSES = /* @__PURE__ */ new Set([
|
|
1575
|
-
"completed",
|
|
1576
|
-
"error",
|
|
1577
|
-
"cancelled",
|
|
1578
|
-
"stopped"
|
|
1579
|
-
]);
|
|
1580
1948
|
function isConversationOver(session) {
|
|
1581
|
-
|
|
1582
|
-
return session.session_state === "closed";
|
|
1583
|
-
}
|
|
1584
|
-
return TERMINAL_SESSION_STATUSES.has(session.status);
|
|
1949
|
+
return session.lifecycle.conversation === "closed";
|
|
1585
1950
|
}
|
|
1586
1951
|
function seedTranscriptStore(store, seed) {
|
|
1587
1952
|
store.ingest({
|
|
@@ -1602,6 +1967,7 @@ var EMPTY_SNAPSHOT = {
|
|
|
1602
1967
|
acknowledgedMessageIds: /* @__PURE__ */ new Set(),
|
|
1603
1968
|
historyTruncated: false,
|
|
1604
1969
|
liveText: "",
|
|
1970
|
+
liveSummary: "",
|
|
1605
1971
|
liveOutputTokens: null,
|
|
1606
1972
|
lastEventAt: null,
|
|
1607
1973
|
conversationOver: false
|
|
@@ -1625,7 +1991,10 @@ var SessionTranscriptStore = class {
|
|
|
1625
1991
|
cursorSeq = 0;
|
|
1626
1992
|
// Derived-turn cache, keyed on the records array identity.
|
|
1627
1993
|
turnsFor = null;
|
|
1994
|
+
turnsSession = null;
|
|
1628
1995
|
turnsCache = [];
|
|
1996
|
+
summaryTurnId = null;
|
|
1997
|
+
summaryExecutionId = null;
|
|
1629
1998
|
get cursor() {
|
|
1630
1999
|
return this.cursorSeq;
|
|
1631
2000
|
}
|
|
@@ -1640,9 +2009,10 @@ var SessionTranscriptStore = class {
|
|
|
1640
2009
|
// records_append batch) — what a chat-style transcript renders.
|
|
1641
2010
|
chatTurns = () => {
|
|
1642
2011
|
const records = this.snapshot.records;
|
|
1643
|
-
if (this.turnsFor !== records) {
|
|
1644
|
-
this.turnsCache = groupRecordsToChatTurns(records);
|
|
2012
|
+
if (this.turnsFor !== records || this.turnsSession !== this.snapshot.session) {
|
|
2013
|
+
this.turnsCache = groupRecordsToChatTurns(records, this.snapshot.session);
|
|
1645
2014
|
this.turnsFor = records;
|
|
2015
|
+
this.turnsSession = this.snapshot.session;
|
|
1646
2016
|
}
|
|
1647
2017
|
return this.turnsCache;
|
|
1648
2018
|
};
|
|
@@ -1709,6 +2079,7 @@ var SessionTranscriptStore = class {
|
|
|
1709
2079
|
const frame = rawFrame;
|
|
1710
2080
|
switch (frame.type) {
|
|
1711
2081
|
case "snapshot": {
|
|
2082
|
+
next.liveSummary = "";
|
|
1712
2083
|
next.session = frame.session;
|
|
1713
2084
|
for (const message of frame.messages) {
|
|
1714
2085
|
this.inbox.set(message.id, message);
|
|
@@ -1733,6 +2104,8 @@ var SessionTranscriptStore = class {
|
|
|
1733
2104
|
acknowledgedNew = true;
|
|
1734
2105
|
}
|
|
1735
2106
|
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)
|
|
2108
|
+
next.liveSummary = "";
|
|
1736
2109
|
inboxChanged = this.applyMessageRecord(record) || inboxChanged;
|
|
1737
2110
|
if (record.record_type === "message_received" || record.record_type === "message_delivered") {
|
|
1738
2111
|
const payload = record.payload;
|
|
@@ -1754,17 +2127,35 @@ var SessionTranscriptStore = class {
|
|
|
1754
2127
|
case "session": {
|
|
1755
2128
|
next.session = frame.session;
|
|
1756
2129
|
next.conversationOver = isConversationOver(frame.session);
|
|
2130
|
+
if (next.conversationOver || frame.session.lifecycle.status === "idle") {
|
|
2131
|
+
next.liveSummary = "";
|
|
2132
|
+
}
|
|
1757
2133
|
break;
|
|
1758
2134
|
}
|
|
1759
2135
|
case "delta": {
|
|
2136
|
+
if (frame.kind === "thinking") {
|
|
2137
|
+
if (next.conversationOver) break;
|
|
2138
|
+
this.summaryTurnId = frame.turn_id;
|
|
2139
|
+
this.summaryExecutionId = frame.session_execution_id;
|
|
2140
|
+
if (frame.text != null) next.liveSummary = frame.text;
|
|
2141
|
+
break;
|
|
2142
|
+
}
|
|
1760
2143
|
if (frame.kind !== "text") break;
|
|
1761
|
-
if (frame.text != null)
|
|
2144
|
+
if (frame.text != null) {
|
|
2145
|
+
next.liveText = prev.liveText + frame.text;
|
|
2146
|
+
next.liveSummary = "";
|
|
2147
|
+
}
|
|
1762
2148
|
if (frame.output_tokens != null)
|
|
1763
2149
|
next.liveOutputTokens = frame.output_tokens;
|
|
1764
2150
|
break;
|
|
1765
2151
|
}
|
|
1766
2152
|
case "done": {
|
|
1767
2153
|
next.conversationOver = true;
|
|
2154
|
+
next.liveSummary = "";
|
|
2155
|
+
break;
|
|
2156
|
+
}
|
|
2157
|
+
case "error": {
|
|
2158
|
+
next.liveSummary = "";
|
|
1768
2159
|
break;
|
|
1769
2160
|
}
|
|
1770
2161
|
default:
|
|
@@ -1780,6 +2171,7 @@ export {
|
|
|
1780
2171
|
SESSION_WAKING,
|
|
1781
2172
|
SessionTranscriptStore,
|
|
1782
2173
|
USER_BAR,
|
|
2174
|
+
activitySummary,
|
|
1783
2175
|
awaitingAgentPhase,
|
|
1784
2176
|
cacheTierLabel,
|
|
1785
2177
|
chatTurnsToItems,
|
|
@@ -1808,11 +2200,13 @@ export {
|
|
|
1808
2200
|
isFoldable,
|
|
1809
2201
|
isToolActivity,
|
|
1810
2202
|
isToolFold,
|
|
2203
|
+
isWorkFold,
|
|
1811
2204
|
lastLines,
|
|
1812
2205
|
layOutItems,
|
|
1813
2206
|
lifecycleText,
|
|
1814
2207
|
oneLine,
|
|
1815
2208
|
pendingToolCalls,
|
|
2209
|
+
reasoningSummaryTitle,
|
|
1816
2210
|
recordSlice,
|
|
1817
2211
|
recordToItems,
|
|
1818
2212
|
resultCostUsd,
|
|
@@ -1829,6 +2223,7 @@ export {
|
|
|
1829
2223
|
statusActivityText,
|
|
1830
2224
|
summarizeToolInput,
|
|
1831
2225
|
tailLines,
|
|
2226
|
+
toolActivities,
|
|
1832
2227
|
toolResultText,
|
|
1833
2228
|
toolRunMembers
|
|
1834
2229
|
};
|