pilotswarm 0.5.69 → 0.5.71
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/package.json +2 -2
- package/ui/core/src/controller.js +3 -1
- package/ui/core/src/history.js +3 -1
- package/ui/core/src/moa.js +2 -2
- package/ui/core/src/native-tasks.js +46 -2
- package/ui/core/src/reducer.js +14 -2
- package/ui/core/src/selectors.js +41 -16
- package/ui/core/src/session-sort.js +45 -0
- package/ui/core/src/state.js +3 -0
- package/ui/react/src/chat-call-line.js +17 -0
- package/ui/react/src/native-task-card.js +53 -23
- package/ui/react/src/web-app.js +100 -48
- package/web/dist/assets/{abnfDiagram-VRR7QNED-DCEEpIGf.js → abnfDiagram-VRR7QNED-C1caNr4U.js} +1 -1
- package/web/dist/assets/{arc-_RJw0dJ3.js → arc-BufdFJYK.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-Bze_PsYG.js → architectureDiagram-ZJ3FMSHR-C0vQblUK.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-CZaMO9H-.js → blockDiagram-677ZJIJ3-B6PYWI9u.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-Dzr58V1d.js → c4Diagram-LMCZKHZV-DFoaVeMm.js} +1 -1
- package/web/dist/assets/channel-CDzPwSI9.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-1IO7uTFx.js → chunk-2Q5K7J3B-CxFBb-8y.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-DKdpRP0l.js → chunk-32BRIVSS-DO1G8AVc.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-CxyhZOh5.js → chunk-5VM5RSS4-spE4bKSd.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-DWY7moCs.js → chunk-EX3LRPZG-Bc_eUP5F.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-By7oiTbd.js → chunk-JWPE2WC7-CGM2GAMX.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-CS-m-Pv3.js → chunk-MOJQB5TN-CHdrVT1z.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-Cxykz4-E.js → chunk-RYQCIY6F-BWJK7-AW.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-Dwl0aU9m.js → chunk-V7JOEXUC-D8tgpQwK.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-BERWiLR7.js → chunk-VR4S4FIN-BP86JfVu.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-CZ0e721z.js → chunk-XXDRQBXY-Ctq_LUrs.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-DdcABn-U.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-DdcABn-U.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-RLgoKX1J.js → cose-bilkent-JH36ORCC-DNvC4thT.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-CUvcvCFo.js → cynefin-VYW2F7L2-3zVj2GvI.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-D2ML_npa.js → cynefinDiagram-TSTJHNR4-DQbITfsJ.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-BHG6-avE.js → dagre-VKFMJZFB-BkGAcDyj.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-ynOb8q0h.js → diagram-FQU43EPY-D3lsgelm.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-BLHAADd0.js → diagram-G47NLZAW-C2rO9e3d.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-DwOMd4DW.js → diagram-NH7WQ7WH-DJBwtT8q.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-DZWnpqT9.js → diagram-OA4YK3LP-C-SAwEHZ.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-BMcvO3Pf.js → diagram-WEI45ONY-BpXMlmKz.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-CRX9h0gP.js → ebnfDiagram-CCIWWBDH-DwxUgEmE.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-Con2S52S.js → erDiagram-Q63AITRT-DNZMCmYz.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-wg4unvdR.js → flowDiagram-23GEKE2U-CUlGtnWV.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-CAg65Xfj.js → ganttDiagram-NO4QXBWP-DCj6zkyt.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-5u02gmfN.js → gitGraphDiagram-IHSO6WYX-Q6UP_dgi.js} +1 -1
- package/web/dist/assets/index-23y0f1it.js +24 -0
- package/web/dist/assets/index-BXR-II2L.css +1 -0
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-DW830L4J.js → infoDiagram-FWYZ7A6U-Cwn8XNQT.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-6Qr30UBj.js → ishikawaDiagram-FXEZZL3T-BaMvGBAo.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-BHCjdAv1.js → journeyDiagram-5HDEW3XC-DK95FJ0t.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-PGpXvhY7.js → kanban-definition-HUTT4EX6-C6W6RJLm.js} +1 -1
- package/web/dist/assets/{linear-CuN5Qi_I.js → linear-Un37Oyxr.js} +1 -1
- package/web/dist/assets/{mermaid.core-BEG_GOf7.js → mermaid.core-i8uqvLPa.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-Bbi2gb0f.js → mindmap-definition-LN4V7U3C-d_cgq1KW.js} +1 -1
- package/web/dist/assets/{papaparse.min-LDhOsJ62.js → papaparse.min-BiJEkQ8f.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-IZ3AowOt.js → pegDiagram-2B236MQR-5QRWM5AP.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-QnG3Rlhe.js → pieDiagram-ENE6RG2P-VIZXsjFV.js} +1 -1
- package/web/dist/assets/pilotswarm-D7Q48waO.js +109 -0
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-BIB7xhGf.js → quadrantDiagram-ABIIQ3AL-DMOx841b.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-CHbggWfd.js → railroadDiagram-RFXS5EU6-lTWzjdcd.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-CgNGJVBn.js → requirementDiagram-TGXJPOKE-BE5SpiNy.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-B1GfGCwn.js → sankeyDiagram-HTMAVEWB-C3LYWQTA.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-D-Bfj-RU.js → sequenceDiagram-DBY2YBRQ-BTCp88tQ.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-Ca-mVfMN.js → sizeCapture-X5ZJPWSS-Ppzj8n5l.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-5gIoJ_8A.js → stateDiagram-2N3HPSRC-Ch_iZ_cQ.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-BbJN7t8_.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-HxN6_3nA.js → swimlanes-5IMT3BWC-BX4-Oeby.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-0UA1VebA.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-CSkNwYwY.js → timeline-definition-FHXFAJF6-B1l-6l6u.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-o6qvAiNl.js → vennDiagram-L72KCM5P-Grpa6wFZ.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-HcUO3YZZ.js → wardleyDiagram-EHGQE667-B5bXnFlL.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-DmNJJPi3.js → xychartDiagram-FW5EYKEG-BkELQtgc.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/dist/assets/channel-CHAZ3LVB.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-CXv3ioyD.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-CXv3ioyD.js +0 -1
- package/web/dist/assets/index-BT3MiUPE.css +0 -1
- package/web/dist/assets/index-DzjecAo8.js +0 -24
- package/web/dist/assets/pilotswarm-BNTnbu9f.js +0 -109
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-C_IKdqOV.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-B8jNOz1z.js +0 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pilotswarm",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.71",
|
|
4
4
|
"description": "PilotSwarm application package: terminal UI, browser portal + Web API server, and MCP server — one install, three bins.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"ink": "^6.8.0",
|
|
82
82
|
"jose": "^6.2.2",
|
|
83
83
|
"mermaid": "^11.16.0",
|
|
84
|
-
"pilotswarm-sdk": "0.5.
|
|
84
|
+
"pilotswarm-sdk": "0.5.71",
|
|
85
85
|
"react": "^19.2.4",
|
|
86
86
|
"react-dom": "^19.2.4",
|
|
87
87
|
"ws": "^8.18.2"
|
|
@@ -7545,6 +7545,7 @@ export class PilotSwarmUiController {
|
|
|
7545
7545
|
force = true,
|
|
7546
7546
|
fullscreen = false,
|
|
7547
7547
|
pane = false,
|
|
7548
|
+
preserveSession = false,
|
|
7548
7549
|
} = {}) {
|
|
7549
7550
|
const resolvedSessionId = sessionId || this.getState().sessions.activeSessionId;
|
|
7550
7551
|
const name = String(filename || "").trim();
|
|
@@ -7558,7 +7559,7 @@ export class PilotSwarmUiController {
|
|
|
7558
7559
|
restoreArtifactId: this.getState().files.selectedArtifactId || null,
|
|
7559
7560
|
});
|
|
7560
7561
|
|
|
7561
|
-
if (this.getState().sessions.activeSessionId !== resolvedSessionId) {
|
|
7562
|
+
if (!preserveSession && this.getState().sessions.activeSessionId !== resolvedSessionId) {
|
|
7562
7563
|
await this.loadSession(resolvedSessionId).catch(() => null);
|
|
7563
7564
|
}
|
|
7564
7565
|
// The reader REPLACES the inspector, so it has no business changing
|
|
@@ -8996,6 +8997,7 @@ export class PilotSwarmUiController {
|
|
|
8996
8997
|
return;
|
|
8997
8998
|
}
|
|
8998
8999
|
|
|
9000
|
+
this.dispatch({ type: "sessions/used", sessionId });
|
|
8999
9001
|
const activePendingQuestion = activeSession?.pendingQuestion || null;
|
|
9000
9002
|
const answeringPendingQuestion = Boolean(activePendingQuestion?.question);
|
|
9001
9003
|
const promptEdit = this.getPromptEditSessionMatch(sessionId);
|
package/ui/core/src/history.js
CHANGED
|
@@ -3,7 +3,7 @@ import { isCanvasActionContent, parseCanvasActionContent } from "./canvas-action
|
|
|
3
3
|
import { formatCompactionActivityRuns } from "./context-usage.js";
|
|
4
4
|
import { canonicalSystemTitle } from "./system-titles.js";
|
|
5
5
|
import { matchesSessionError } from "./session-warning.js";
|
|
6
|
-
import { appendNativeTaskEvent } from "./native-tasks.js";
|
|
6
|
+
import { appendNativeTaskEvent, appendNativeTaskCall } from "./native-tasks.js";
|
|
7
7
|
import { buildSessionWarning } from "./session-errors.js";
|
|
8
8
|
import { appendChatCall, CHAT_CALL_EVENT_TYPES } from "./chat-activity.js";
|
|
9
9
|
|
|
@@ -21,6 +21,7 @@ export const HISTORY_EVENT_LIMIT_STEPS = [
|
|
|
21
21
|
// load transcript pages instead of raw-stream pages.
|
|
22
22
|
export const CHAT_HISTORY_EVENT_TYPES = [
|
|
23
23
|
...CHAT_CALL_EVENT_TYPES,
|
|
24
|
+
...CHAT_CALL_EVENT_TYPES.filter(type => type !== "session.agent_spawned").map(type => `native.${type}`),
|
|
24
25
|
"user.message",
|
|
25
26
|
"assistant.message",
|
|
26
27
|
// Needed to distinguish interim assistant output from the final answer
|
|
@@ -137,6 +138,7 @@ function appendSessionWarning(chat, event) {
|
|
|
137
138
|
|
|
138
139
|
const parentChatCallTypes = new Set(CHAT_CALL_EVENT_TYPES);
|
|
139
140
|
function appendParentChatCall(chat, event) {
|
|
141
|
+
if (appendNativeTaskCall(chat, event)) return;
|
|
140
142
|
if (!parentChatCallTypes.has(event?.eventType)) return;
|
|
141
143
|
const data = event?.data || {};
|
|
142
144
|
// Native tasks have their own abridged disclosure. Generic call cards
|
package/ui/core/src/moa.js
CHANGED
|
@@ -42,7 +42,7 @@ export function normalizeMoa(value) {
|
|
|
42
42
|
return safeLayout(dashboard, key, `MoA ${index + 1}`);
|
|
43
43
|
});
|
|
44
44
|
if (!dashboards.length) dashboards.push(safeLayout(null));
|
|
45
|
-
return { version: 3, activeDashboardId: dashboards.some(d => d.id === value.activeDashboardId) ? value.activeDashboardId : dashboards[0].id, dashboards };
|
|
45
|
+
return { version: 3, composerMode: value?.composerMode === "shared" ? "shared" : "per-chat", activeDashboardId: dashboards.some(d => d.id === value.activeDashboardId) ? value.activeDashboardId : dashboards[0].id, dashboards };
|
|
46
46
|
}
|
|
47
47
|
let source = value;
|
|
48
48
|
if (!(value?.version === 2 || (object(value) && Object.hasOwn(value, "tree")))) {
|
|
@@ -51,7 +51,7 @@ export function normalizeMoa(value) {
|
|
|
51
51
|
const active = Number.isInteger(value?.activeSlot) ? Math.max(0, Math.min(4, value.activeSlot)) : 0;
|
|
52
52
|
source = safeLayout(slots[active]).tree ? slots[active] : slots.find(slot => safeLayout(slot).tree);
|
|
53
53
|
}
|
|
54
|
-
return { version: 3, activeDashboardId: "moa-1", dashboards: [safeLayout(source)] };
|
|
54
|
+
return { version: 3, composerMode: value?.composerMode === "shared" ? "shared" : "per-chat", activeDashboardId: "moa-1", dashboards: [safeLayout(source)] };
|
|
55
55
|
}
|
|
56
56
|
export const activeMoaDashboard = value => value.dashboards.find(d => d.id === value.activeDashboardId) || value.dashboards[0];
|
|
57
57
|
export function updateMoaDashboard(value, dashboardId, patch) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { appendChatCall, CHAT_CALL_EVENT_TYPES } from "./chat-activity.js";
|
|
2
|
+
|
|
1
3
|
// Native Copilot work is local to one parent turn. Durable spawn_agent
|
|
2
4
|
// sessions deliberately do not enter this projection.
|
|
3
5
|
const TERMINAL = new Set(["completed", "failed", "cancelled", "interrupted"]);
|
|
@@ -61,6 +63,7 @@ function updateTask(chat, patch, event, { authoritative = false } = {}) {
|
|
|
61
63
|
if (previous?.inferredTerminal) next.error = patch.error || "";
|
|
62
64
|
}
|
|
63
65
|
next.title = clip(next.title, 160) || "Native task";
|
|
66
|
+
if (next.title === "Native task" && previous?.title) next.title = previous.title;
|
|
64
67
|
next.preview = clip(next.preview);
|
|
65
68
|
next.result = clip(next.result, 4000);
|
|
66
69
|
next.error = clip(next.error, 1000);
|
|
@@ -89,6 +92,47 @@ function updateTask(chat, patch, event, { authoritative = false } = {}) {
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
|
|
95
|
+
// Correlate by runtime identity, never by tool name or arrival time. External
|
|
96
|
+
// callbacks may arrive before the child event; move that existing disclosure
|
|
97
|
+
// under its owner as soon as the explicit relationship becomes available.
|
|
98
|
+
export function appendNativeTaskCall(chat, event) {
|
|
99
|
+
const type = (event?.eventType || "").replace(/^native\./, "");
|
|
100
|
+
if (!CHAT_CALL_EVENT_TYPES.includes(type) || type === "session.agent_spawned") return false;
|
|
101
|
+
const data = event.data || {};
|
|
102
|
+
const explicit = Boolean(data.nativeAgentId || data.parentToolCallId || event.eventType.startsWith("native."));
|
|
103
|
+
const keys = [data.toolCallId && `call:${data.toolCallId}`, data.requestId && `request:${data.requestId}`].filter(Boolean);
|
|
104
|
+
let match = explicit ? findTask(chat, { toolCallId: data.parentToolCallId, agentId: data.nativeAgentId }) : null;
|
|
105
|
+
const namespace = `${event.sessionId}:${data.durableSessionId || ""}`;
|
|
106
|
+
if (!match && !explicit) {
|
|
107
|
+
for (let g = chat.length - 1; g >= 0 && !match; g--) {
|
|
108
|
+
if (chat[g].kind !== "native-task-group") continue;
|
|
109
|
+
const t = chat[g].tasks.findIndex(task => task.calls?.some(call => call.namespace === namespace
|
|
110
|
+
&& call.callKeys.some(key => keys.includes(key))));
|
|
111
|
+
if (t >= 0) match = { groupIndex: g, taskIndex: t };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (!match && explicit && (data.parentToolCallId || data.nativeAgentId)) {
|
|
115
|
+
updateTask(chat, { toolCallId: data.parentToolCallId, agentId: data.nativeAgentId, status: "running" }, event);
|
|
116
|
+
match = findTask(chat, { toolCallId: data.parentToolCallId, agentId: data.nativeAgentId });
|
|
117
|
+
}
|
|
118
|
+
if (!match) return explicit; // Unknown child activity must not impersonate the parent.
|
|
119
|
+
const group = chat[match.groupIndex];
|
|
120
|
+
const task = group.tasks[match.taskIndex];
|
|
121
|
+
const calls = [...(task.calls || [])];
|
|
122
|
+
for (let i = chat.length - 1; i >= 0; i--) {
|
|
123
|
+
const call = chat[i];
|
|
124
|
+
if (call.kind !== "chat-call" || call.namespace !== namespace || !call.callKeys.some(key => keys.includes(key))) continue;
|
|
125
|
+
if (!calls.some(item => item.callKeys.some(key => call.callKeys.includes(key)))) calls.push(call);
|
|
126
|
+
chat.splice(i, 1);
|
|
127
|
+
}
|
|
128
|
+
appendChatCall(calls, { ...event, eventType: type });
|
|
129
|
+
const tasks = [...group.tasks];
|
|
130
|
+
tasks[match.taskIndex] = { ...task, calls, toolCalls: Math.max(task.toolCalls || 0, calls.length) };
|
|
131
|
+
// Removing a parent disclosure can shift the group's index.
|
|
132
|
+
chat[chat.indexOf(group)] = { ...group, tasks };
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
92
136
|
export function appendNativeTaskEvent(chat, event) {
|
|
93
137
|
const type = event?.eventType || "";
|
|
94
138
|
const data = event?.data || {};
|
|
@@ -119,7 +163,7 @@ export function appendNativeTaskEvent(chat, event) {
|
|
|
119
163
|
const previous = found ? chat[found.groupIndex].tasks[found.taskIndex] : null;
|
|
120
164
|
if (!previous && !lifecycle && !taskTool) return;
|
|
121
165
|
if (!previous || taskTool || type === "subagent.started") Object.assign(patch, {
|
|
122
|
-
title: args.description ||
|
|
166
|
+
title: args.description || args.name || (previous?.title !== "Native task" ? previous?.title : null) || data.agentDisplayName || "Native task",
|
|
123
167
|
profile: args.agent_type || data.agentName || data.agentType,
|
|
124
168
|
model: data.model,
|
|
125
169
|
});
|
|
@@ -145,7 +189,7 @@ export function appendNativeTaskEvent(chat, event) {
|
|
|
145
189
|
} else if (type === "native.tool.execution_start") {
|
|
146
190
|
const ids = previous.toolCallIds || [];
|
|
147
191
|
patch.toolCallIds = data.toolCallId && !ids.includes(data.toolCallId) ? [...ids, data.toolCallId].slice(-500) : ids;
|
|
148
|
-
patch.toolCalls = (previous.toolCalls || 0
|
|
192
|
+
patch.toolCalls = Math.max(previous.toolCalls || 0, patch.toolCallIds.length);
|
|
149
193
|
patch.preview = args.description || (data.toolName ? `Using ${data.toolName}` : "Working");
|
|
150
194
|
} else if (type === "native.assistant.message") {
|
|
151
195
|
// Do not expose reasoning, tool arguments, or the whole child transcript.
|
package/ui/core/src/reducer.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeSessionSortMode, normalizeSessionUsage, reconcileSessionSort } from "./session-sort.js";
|
|
1
2
|
import { normalizeMoa } from "./moa.js";
|
|
2
3
|
import { retainSessionWarnings } from "./session-errors.js";
|
|
3
4
|
import { buildSessionTree, isManuallyOrderableSession } from "./session-tree.js";
|
|
@@ -846,7 +847,7 @@ export function appReducer(state, action) {
|
|
|
846
847
|
if (sessionId && contentUpdate && state.sessions?.goneIds?.includes(sessionId)) return state;
|
|
847
848
|
const next = baseReducer(state, action);
|
|
848
849
|
if (next === state) return next;
|
|
849
|
-
return reconcileSessionView(state, next, action);
|
|
850
|
+
return reconcileSessionView(state, reconcileSessionSort(state, next, action), action);
|
|
850
851
|
}
|
|
851
852
|
|
|
852
853
|
// Per-session desktop views, kept in step with the ui slice.
|
|
@@ -1101,6 +1102,7 @@ function baseReducer(state, action) {
|
|
|
1101
1102
|
const nextActiveSessionId = hasActive
|
|
1102
1103
|
? normalizeStoredActiveSessionId(settings.activeSessionId)
|
|
1103
1104
|
: state.sessions.activeSessionId;
|
|
1105
|
+
const remoteUsage = normalizeSessionUsage(settings.sessionUsedAt);
|
|
1104
1106
|
const nextSessions = {
|
|
1105
1107
|
...state.sessions,
|
|
1106
1108
|
...(hasOwnerFilter
|
|
@@ -1117,6 +1119,9 @@ function baseReducer(state, action) {
|
|
|
1117
1119
|
: {}),
|
|
1118
1120
|
pinnedIds: nextPinnedIds,
|
|
1119
1121
|
manualOrder: nextManualOrder,
|
|
1122
|
+
sortMode: Object.hasOwn(settings, "sessionSortMode") ? normalizeSessionSortMode(settings.sessionSortMode) : state.sessions.sortMode,
|
|
1123
|
+
usedAt: Object.fromEntries([...new Set([...Object.keys(remoteUsage), ...Object.keys(state.sessions.usedAt || {})])]
|
|
1124
|
+
.map(id => [id, Math.max(remoteUsage[id] || 0, state.sessions.usedAt?.[id] || 0)])),
|
|
1120
1125
|
collapsedIds: nextCollapsedIds,
|
|
1121
1126
|
collapsedIdsExplicit: hasCollapsed ? true : state.sessions.collapsedIdsExplicit,
|
|
1122
1127
|
activeSessionId: nextActiveSessionId,
|
|
@@ -1891,8 +1896,15 @@ function baseReducer(state, action) {
|
|
|
1891
1896
|
};
|
|
1892
1897
|
}
|
|
1893
1898
|
|
|
1899
|
+
case "sessions/sortMode":
|
|
1900
|
+
return { ...state, sessions: { ...state.sessions, sortMode: normalizeSessionSortMode(action.mode) } };
|
|
1901
|
+
case "sessions/refreshSort":
|
|
1902
|
+
return { ...state, sessions: { ...state.sessions } };
|
|
1903
|
+
case "sessions/used":
|
|
1904
|
+
if (!action.sessionId) return state;
|
|
1905
|
+
return { ...state, sessions: { ...state.sessions, usedAt: { ...state.sessions.usedAt, [action.sessionId]: action.at || Date.now() } } };
|
|
1894
1906
|
case "sessions/selected": {
|
|
1895
|
-
state = { ...state, sessions: { ...state.sessions, listDeselected: false, localSelectionMade: true } };
|
|
1907
|
+
state = { ...state, sessions: { ...state.sessions, listDeselected: false, localSelectionMade: true, usedAt: action.sessionId ? { ...state.sessions.usedAt, [action.sessionId]: Date.now() } : state.sessions.usedAt } };
|
|
1896
1908
|
// Per-session chat scroll memory: stash both the outgoing offset
|
|
1897
1909
|
// and whether it follows the bottom. A paused reading position
|
|
1898
1910
|
// stays paused as new messages arrive and across session switches.
|
package/ui/core/src/selectors.js
CHANGED
|
@@ -638,11 +638,20 @@ function distinctHumanOwnerCount(state, stopAt = Infinity) {
|
|
|
638
638
|
return owners.size;
|
|
639
639
|
}
|
|
640
640
|
|
|
641
|
+
const sessionOwnerDecorationCache = new WeakMap();
|
|
641
642
|
function shouldDecorateSessionOwners(state) {
|
|
642
643
|
// One rule: the chip earns its place when the list holds more than one
|
|
643
644
|
// distinct human owner. Otherwise it is noise on every row — you are
|
|
644
645
|
// always "you".
|
|
645
|
-
|
|
646
|
+
const byId = state?.sessions?.byId;
|
|
647
|
+
if (!byId) return false;
|
|
648
|
+
const filter = state.sessions.ownerFilter;
|
|
649
|
+
const auth = state.auth;
|
|
650
|
+
const cached = sessionOwnerDecorationCache.get(byId);
|
|
651
|
+
if (cached && cached.filter === filter && cached.auth === auth) return cached.value;
|
|
652
|
+
const value = distinctHumanOwnerCount(state, 2) > 1;
|
|
653
|
+
sessionOwnerDecorationCache.set(byId, { filter, auth, value });
|
|
654
|
+
return value;
|
|
646
655
|
}
|
|
647
656
|
|
|
648
657
|
function groupMemberSessions(group, byId = {}) {
|
|
@@ -1293,6 +1302,7 @@ function matchesSearchQuery(value, query) {
|
|
|
1293
1302
|
}
|
|
1294
1303
|
|
|
1295
1304
|
const searchedSessionFlatCache = new WeakMap();
|
|
1305
|
+
const sessionSearchIndexCache = new WeakMap();
|
|
1296
1306
|
|
|
1297
1307
|
function sessionGroupTitle(session, byId) {
|
|
1298
1308
|
if (session?.isGroup) return session.title || "";
|
|
@@ -1320,20 +1330,17 @@ function sessionSearchOwner(session, byId) {
|
|
|
1320
1330
|
return null;
|
|
1321
1331
|
}
|
|
1322
1332
|
|
|
1323
|
-
function
|
|
1324
|
-
const existingFlat = Array.isArray(state.sessions?.flat) ? state.sessions.flat : [];
|
|
1325
|
-
if (!query) return existingFlat;
|
|
1326
|
-
const cached = searchedSessionFlatCache.get(existingFlat);
|
|
1327
|
-
if (cached?.query === query) return cached.result;
|
|
1328
|
-
|
|
1333
|
+
function sessionSearchIndex(state) {
|
|
1329
1334
|
const byId = state.sessions?.byId || {};
|
|
1330
|
-
const
|
|
1335
|
+
const deps = [state.sessions?.orderById, state.sessions?.pinnedIds, (state.sessions?.sortMode && state.sessions.sortMode !== "saved" ? state.sessions.sortSnapshot : state.sessions?.manualOrder)];
|
|
1336
|
+
const cached = sessionSearchIndexCache.get(byId);
|
|
1337
|
+
if (cached && sameRowDeps(cached.deps, deps)) return cached;
|
|
1331
1338
|
const expanded = buildSessionTree(
|
|
1332
1339
|
Object.values(byId),
|
|
1333
1340
|
new Set(),
|
|
1334
1341
|
state.sessions?.orderById,
|
|
1335
1342
|
state.sessions?.pinnedIds,
|
|
1336
|
-
state.sessions?.manualOrder,
|
|
1343
|
+
(state.sessions?.sortMode && state.sessions.sortMode !== "saved" ? state.sessions.sortSnapshot : state.sessions?.manualOrder),
|
|
1337
1344
|
);
|
|
1338
1345
|
const parentById = new Map();
|
|
1339
1346
|
const entryById = new Map();
|
|
@@ -1348,16 +1355,34 @@ function buildSearchedSessionFlat(state, query) {
|
|
|
1348
1355
|
baseIndex.set(entry.sessionId, index);
|
|
1349
1356
|
}
|
|
1350
1357
|
|
|
1358
|
+
const documents = new Map(expanded.map(entry => {
|
|
1359
|
+
const session = byId[entry.sessionId] || entry.standIn;
|
|
1360
|
+
return [entry.sessionId, buildSessionSearchDocument(session, {
|
|
1361
|
+
owner: sessionSearchOwner(session, byId), groupTitle: sessionGroupTitle(session, byId),
|
|
1362
|
+
})];
|
|
1363
|
+
}));
|
|
1364
|
+
const index = { deps, expanded, parentById, entryById, baseIndex, documents };
|
|
1365
|
+
sessionSearchIndexCache.set(byId, index);
|
|
1366
|
+
return index;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
function buildSearchedSessionFlat(state, query) {
|
|
1370
|
+
const existingFlat = Array.isArray(state.sessions?.flat) ? state.sessions.flat : [];
|
|
1371
|
+
if (!query) return existingFlat;
|
|
1372
|
+
const cached = searchedSessionFlatCache.get(existingFlat);
|
|
1373
|
+
const deps = [state.sessions?.byId, state.sessions?.ownerFilter, state.auth, state.sessions?.orderById,
|
|
1374
|
+
state.sessions?.pinnedIds, (state.sessions?.sortMode && state.sessions.sortMode !== "saved" ? state.sessions.sortSnapshot : state.sessions?.manualOrder), state.sessions?.filterExceptionId];
|
|
1375
|
+
if (cached?.query === query && sameRowDeps(cached.deps, deps)) return cached.result;
|
|
1376
|
+
|
|
1377
|
+
const byId = state.sessions?.byId || {};
|
|
1378
|
+
const parsed = parseSessionSearchQuery(query);
|
|
1379
|
+
const { expanded, parentById, entryById, baseIndex, documents } = sessionSearchIndex(state);
|
|
1380
|
+
|
|
1351
1381
|
const directScores = new Map();
|
|
1352
1382
|
for (const entry of expanded) {
|
|
1353
1383
|
const session = byId[entry.sessionId] || entry.standIn;
|
|
1354
1384
|
if (!session || !matchesOwnerFilter(session, state.sessions?.ownerFilter, state.auth || {}, byId)) continue;
|
|
1355
|
-
const
|
|
1356
|
-
const document = buildSessionSearchDocument(session, {
|
|
1357
|
-
owner,
|
|
1358
|
-
groupTitle: sessionGroupTitle(session, byId),
|
|
1359
|
-
});
|
|
1360
|
-
const score = scoreSessionSearchDocument(document, parsed);
|
|
1385
|
+
const score = scoreSessionSearchDocument(documents.get(entry.sessionId), parsed);
|
|
1361
1386
|
if (score > 0) directScores.set(entry.sessionId, score);
|
|
1362
1387
|
}
|
|
1363
1388
|
const matchedIds = new Set(directScores.keys());
|
|
@@ -1414,7 +1439,7 @@ function buildSearchedSessionFlat(state, query) {
|
|
|
1414
1439
|
// A user can type an unbounded number of distinct queries while the
|
|
1415
1440
|
// catalog array keeps the same identity. Keep only the latest result for
|
|
1416
1441
|
// that catalog rather than retaining every intermediate keystroke.
|
|
1417
|
-
searchedSessionFlatCache.set(existingFlat, { query, result });
|
|
1442
|
+
searchedSessionFlatCache.set(existingFlat, { query, deps, result });
|
|
1418
1443
|
return result;
|
|
1419
1444
|
}
|
|
1420
1445
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { buildSessionTree } from "./session-tree.js";
|
|
2
|
+
|
|
3
|
+
export const normalizeSessionSortMode = mode => ["used", "updated"].includes(mode) ? mode : "saved";
|
|
4
|
+
export function normalizeSessionUsage(value) {
|
|
5
|
+
return Object.fromEntries(Object.entries(value || {}).filter(([id, at]) => id && Number.isFinite(at) && at > 0));
|
|
6
|
+
}
|
|
7
|
+
export function snapshotSessionOrder(sessions, mode) {
|
|
8
|
+
const saved = buildSessionTree(Object.values(sessions.byId), new Set(), sessions.orderById, sessions.pinnedIds, sessions.manualOrder);
|
|
9
|
+
const rank = new Map(saved.map((row, index) => [row.sessionId, index]));
|
|
10
|
+
const timestamp = session => {
|
|
11
|
+
if (mode === "used") return sessions.usedAt?.[session.sessionId] || 0;
|
|
12
|
+
const raw = session.updatedAt ?? session.summaryUpdatedAt ?? session.latestSummaryUpdatedAt;
|
|
13
|
+
const value = typeof raw === "number" ? raw : Date.parse(raw || "");
|
|
14
|
+
return Number.isFinite(value) ? value : 0;
|
|
15
|
+
};
|
|
16
|
+
return Object.values(sessions.byId).sort((a, b) => timestamp(b) - timestamp(a)
|
|
17
|
+
|| (rank.get(a.sessionId) ?? Infinity) - (rank.get(b.sessionId) ?? Infinity))
|
|
18
|
+
.map(session => session.sessionId);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Live catalog and usage changes update row contents, never existing positions.
|
|
22
|
+
// A mode change or explicit refresh captures the next order. New rows append.
|
|
23
|
+
export function reconcileSessionSort(previous, next, action) {
|
|
24
|
+
const sessions = next.sessions;
|
|
25
|
+
if (!sessions) return next;
|
|
26
|
+
const mode = normalizeSessionSortMode(sessions.sortMode);
|
|
27
|
+
const modeChanged = mode !== normalizeSessionSortMode(previous.sessions?.sortMode);
|
|
28
|
+
const refresh = action.type === "sessions/refreshSort";
|
|
29
|
+
const rebuild = sessions.flat !== previous.sessions?.flat || sessions.byId !== previous.sessions?.byId || modeChanged || refresh;
|
|
30
|
+
if (!rebuild) return next;
|
|
31
|
+
let sortSnapshot = sessions.sortSnapshot;
|
|
32
|
+
if (mode !== "saved") {
|
|
33
|
+
if (!sessions.listingSeen) sortSnapshot = [];
|
|
34
|
+
else if (modeChanged || refresh || !sortSnapshot?.length) sortSnapshot = snapshotSessionOrder(sessions, mode);
|
|
35
|
+
else {
|
|
36
|
+
const known = new Set(sortSnapshot);
|
|
37
|
+
const added = Object.keys(sessions.byId).filter(id => !known.has(id));
|
|
38
|
+
if (added.length) sortSnapshot = [...sortSnapshot, ...added];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const flat = mode === "saved" && !modeChanged ? sessions.flat : buildSessionTree(
|
|
42
|
+
Object.values(sessions.byId), sessions.collapsedIds, sessions.orderById,
|
|
43
|
+
sessions.pinnedIds, mode === "saved" ? sessions.manualOrder : sortSnapshot);
|
|
44
|
+
return { ...next, sessions: { ...sessions, sortSnapshot, flat } };
|
|
45
|
+
}
|
package/ui/core/src/state.js
CHANGED
|
@@ -480,6 +480,9 @@ export function createInitialState({ mode = "local", branding = null, docs = nul
|
|
|
480
480
|
pinnedIds: normalizeStoredPinnedSessionIds(pinnedSessionIds),
|
|
481
481
|
// Explicit user placement (desktop drag); read by every surface.
|
|
482
482
|
manualOrder: normalizeStoredSessionOrder(sessionOrder),
|
|
483
|
+
sortMode: "saved",
|
|
484
|
+
sortSnapshot: [],
|
|
485
|
+
usedAt: {},
|
|
483
486
|
// Whether a listing of each kind has been processed. "This row is
|
|
484
487
|
// new" is only answerable against a previous listing OF THE SAME
|
|
485
488
|
// KIND — inferring it from byId being non-empty was wrong, because
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export function ChatCallLine({ line }) {
|
|
4
|
+
const [open, setOpen] = React.useState(false);
|
|
5
|
+
return React.createElement("details", {
|
|
6
|
+
className: "ps-system-notice ps-chat-call",
|
|
7
|
+
"data-call-id": line.callKey,
|
|
8
|
+
onToggle: event => setOpen(event.currentTarget.open),
|
|
9
|
+
},
|
|
10
|
+
React.createElement("summary", { className: "ps-system-notice-summary ps-chat-call-summary" },
|
|
11
|
+
React.createElement("span", { className: "ps-chat-call-tag" }, line.category || "Tool"),
|
|
12
|
+
React.createElement("span", { className: "ps-system-notice-summary-text" }, line.text),
|
|
13
|
+
line.status ? React.createElement("span", { className: `ps-chat-call-status${line.status === "Failed" ? " is-failed" : ""}` }, line.status) : null),
|
|
14
|
+
open ? React.createElement("div", { className: "ps-system-notice-body" },
|
|
15
|
+
line.time ? React.createElement("div", { className: "ps-chat-call-time" }, line.time) : null,
|
|
16
|
+
React.createElement("pre", { className: "ps-chat-call-payload" }, line.body)) : null);
|
|
17
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { ChatCallLine } from "./chat-call-line.js";
|
|
3
|
+
import { chatCallLine } from "../../core/src/chat-activity.js";
|
|
2
4
|
import { NATIVE_TASK_LABELS } from "../../core/src/native-tasks.js";
|
|
3
5
|
|
|
4
6
|
const TERMINAL = new Set(["completed", "failed", "cancelled", "interrupted"]);
|
|
@@ -31,29 +33,57 @@ export const NativeTaskCard = React.memo(function NativeTaskCard({ group, colors
|
|
|
31
33
|
React.createElement("span", { className: "ps-native-tasks-title" },
|
|
32
34
|
React.createElement("span", { className: "ps-native-task-branch", "aria-hidden": true }, "⑂"), "Native tasks"),
|
|
33
35
|
React.createElement("span", { className: "ps-native-tasks-count", "aria-live": "polite" }, countLabel)),
|
|
34
|
-
tasks.map(task => {
|
|
35
|
-
const color = colors[task.status] || colors.running;
|
|
36
|
-
const preview = task.error || task.result || task.preview || (task.status === "completed" ? "Task completed." : "");
|
|
37
|
-
const time = duration(task, now);
|
|
38
|
-
return React.createElement("details", { key: task.id, className: "ps-native-task", "data-task-id": task.id, "data-status": task.status,
|
|
39
|
-
style: { "--ps-native-task-accent": color } },
|
|
40
|
-
React.createElement("summary", null,
|
|
41
|
-
React.createElement("span", { className: "ps-native-task-mark", "aria-hidden": true }, MARKS[task.status] || "?"),
|
|
42
|
-
React.createElement("span", { className: "ps-native-task-main" },
|
|
43
|
-
React.createElement("span", { className: "ps-native-task-title" }, task.title),
|
|
44
|
-
preview ? React.createElement("span", { className: "ps-native-task-preview" }, preview.replace(/\s+/g, " ").slice(0, 240)) : null),
|
|
45
|
-
React.createElement("span", { className: "ps-native-task-meta" },
|
|
46
|
-
React.createElement("span", { className: "ps-native-task-status" }, task.telemetryStale ? "Reconnecting" : NATIVE_TASK_LABELS[task.status] || task.status),
|
|
47
|
-
React.createElement("span", null, [time, `${task.toolCalls || 0} calls`].filter(Boolean).join(" · "))),
|
|
48
|
-
React.createElement("span", { className: "ps-native-task-chevron", "aria-hidden": true }, "›")),
|
|
49
|
-
React.createElement("div", { className: "ps-native-task-detail" },
|
|
50
|
-
React.createElement("div", { className: "ps-native-task-scope" }, [task.profile, task.model, task.reasoningEffort, "Same worker"].filter(Boolean).join(" · ")),
|
|
51
|
-
task.result && !task.error ? React.createElement("div", { className: "ps-native-task-scope" }, "Result excerpt") : null,
|
|
52
|
-
React.createElement("p", { className: "ps-native-task-result" }, task.error || task.result || task.preview
|
|
53
|
-
|| (task.status === "completed" ? "Task completed. A result excerpt is not available in this recording." : "No result available yet.")),
|
|
54
|
-
task.recentActivity?.length ? React.createElement("ul", { className: "ps-native-task-activity" },
|
|
55
|
-
task.recentActivity.map((activity, i) => React.createElement("li", { key: i }, activity.message))) : null));
|
|
56
|
-
}),
|
|
36
|
+
tasks.map(task => React.createElement(NativeTask, { key: task.id, task, colors, now })),
|
|
57
37
|
group.tasks.length > 3 ? React.createElement("button", { type: "button", className: "ps-native-tasks-more", "aria-expanded": showAll,
|
|
58
38
|
onClick: () => setShowAll(value => !value) }, showAll ? "Show fewer tasks" : `Show ${group.tasks.length - 3} more tasks`) : null);
|
|
59
39
|
});
|
|
40
|
+
|
|
41
|
+
function NativeTask({ task, colors, now }) {
|
|
42
|
+
const failed = ["failed", "cancelled", "interrupted"].includes(task.status);
|
|
43
|
+
const [open, setOpen] = React.useState(!TERMINAL.has(task.status) || failed);
|
|
44
|
+
const inspected = React.useRef(false);
|
|
45
|
+
const viewport = React.useRef(null);
|
|
46
|
+
const follow = React.useRef(true);
|
|
47
|
+
const calls = React.useMemo(() => (task.calls || []).map(chatCallLine), [task.calls]);
|
|
48
|
+
React.useLayoutEffect(() => {
|
|
49
|
+
if (open && viewport.current && follow.current) viewport.current.scrollTop = viewport.current.scrollHeight;
|
|
50
|
+
}, [open, calls.length]);
|
|
51
|
+
React.useEffect(() => {
|
|
52
|
+
if (failed || !inspected.current) setOpen(!TERMINAL.has(task.status) || failed);
|
|
53
|
+
}, [task.status, failed]);
|
|
54
|
+
const preview = task.error || task.result || task.preview || (task.status === "completed" ? "Task completed." : "");
|
|
55
|
+
const time = duration(task, now);
|
|
56
|
+
const profile = task.profile === "swarm-explore" ? "Explore" : task.profile === "swarm-task" ? "Task" : "Native task";
|
|
57
|
+
return React.createElement("details", {
|
|
58
|
+
className: "ps-native-task", "data-task-id": task.id, "data-status": task.status, open,
|
|
59
|
+
onToggle: event => { if (event.target === event.currentTarget) setOpen(event.currentTarget.open); },
|
|
60
|
+
style: { "--ps-native-task-accent": colors[task.status] || colors.running },
|
|
61
|
+
},
|
|
62
|
+
React.createElement("summary", {
|
|
63
|
+
onClick: () => { inspected.current = true; },
|
|
64
|
+
onKeyDown: event => { if (["Enter", " "].includes(event.key)) inspected.current = true; },
|
|
65
|
+
},
|
|
66
|
+
React.createElement("span", { className: "ps-native-task-mark", "aria-hidden": true }, MARKS[task.status] || "?"),
|
|
67
|
+
React.createElement("span", { className: "ps-native-task-main" },
|
|
68
|
+
React.createElement("span", { className: "ps-native-task-title" },
|
|
69
|
+
React.createElement("span", { className: "ps-native-task-profile" }, profile),
|
|
70
|
+
task.title === "Native task" ? "Working on delegated request" : task.title),
|
|
71
|
+
preview ? React.createElement("span", { className: "ps-native-task-preview" }, preview.replace(/\s+/g, " ").slice(0, 240)) : null),
|
|
72
|
+
React.createElement("span", { className: "ps-native-task-meta" },
|
|
73
|
+
React.createElement("span", { className: "ps-native-task-status" }, task.telemetryStale ? "Reconnecting" : NATIVE_TASK_LABELS[task.status] || task.status),
|
|
74
|
+
React.createElement("span", null, [time, `${task.toolCalls || 0} ${task.toolCalls === 1 ? "call" : "calls"}`].filter(Boolean).join(" · "))),
|
|
75
|
+
React.createElement("span", { className: "ps-native-task-chevron", "aria-hidden": true }, "›")),
|
|
76
|
+
React.createElement("div", { className: "ps-native-task-detail", onClick: () => { inspected.current = true; } },
|
|
77
|
+
React.createElement("div", { className: "ps-native-task-scope" }, [task.model, task.reasoningEffort, "Same worker"].filter(Boolean).join(" · ")),
|
|
78
|
+
calls.length ? React.createElement("div", {
|
|
79
|
+
ref: viewport, className: "ps-native-task-calls", role: "region", "aria-label": `Tool calls for ${task.title}`, tabIndex: 0,
|
|
80
|
+
onScroll: event => { const node = event.currentTarget; follow.current = node.scrollHeight - node.scrollTop - node.clientHeight <= 24; },
|
|
81
|
+
onWheel: event => { inspected.current = true; if (event.deltaY < 0) follow.current = false; },
|
|
82
|
+
onTouchStart: () => { inspected.current = true; follow.current = false; },
|
|
83
|
+
onFocus: () => { inspected.current = true; },
|
|
84
|
+
}, calls.map(line => React.createElement(ChatCallLine, { key: line.callKey, line }))) :
|
|
85
|
+
React.createElement("p", { className: "ps-native-task-scope" }, task.toolCalls ? "Tool details are not available in this recording." : "Waiting for task activity…"),
|
|
86
|
+
task.result || task.error ? React.createElement("p", { className: "ps-native-task-result" }, task.error || task.result) : null,
|
|
87
|
+
task.recentActivity?.length && !task.calls?.length ? React.createElement("ul", { className: "ps-native-task-activity" },
|
|
88
|
+
task.recentActivity.map((activity, i) => React.createElement("li", { key: i }, activity.message))) : null));
|
|
89
|
+
}
|