pilotswarm 0.5.60 → 0.5.62
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 +3 -2
- package/mcp/dist/src/resources/models.d.ts.map +1 -1
- package/mcp/dist/src/resources/models.js +6 -1
- package/mcp/dist/src/resources/models.js.map +1 -1
- package/mcp/dist/src/tools/models.d.ts.map +1 -1
- package/mcp/dist/src/tools/models.js +11 -1
- package/mcp/dist/src/tools/models.js.map +1 -1
- package/mcp/dist/src/tools/sessions.d.ts.map +1 -1
- package/mcp/dist/src/tools/sessions.js +8 -2
- package/mcp/dist/src/tools/sessions.js.map +1 -1
- package/package.json +2 -2
- package/tui/src/node-sdk-transport.js +1 -1
- package/ui/core/src/chat-activity.js +94 -0
- package/ui/core/src/controller.js +50 -5
- package/ui/core/src/history.js +15 -0
- package/ui/core/src/moa.js +32 -11
- package/ui/core/src/reducer.js +63 -51
- package/ui/core/src/selectors.js +25 -5
- package/ui/core/src/session-errors.js +10 -2
- package/ui/core/src/system-titles.js +2 -0
- package/ui/react/src/web-app.js +155 -77
- package/web/dist/assets/{abnfDiagram-VRR7QNED-CXEHJkTQ.js → abnfDiagram-VRR7QNED-DAiG41W-.js} +1 -1
- package/web/dist/assets/{arc-lInHlEVm.js → arc-CN_GY3DG.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-vrocx6re.js → architectureDiagram-ZJ3FMSHR-DD9qiBOj.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-B1KA5QIB.js → blockDiagram-677ZJIJ3-CZg2R8i6.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-Du9xJMjz.js → c4Diagram-LMCZKHZV-DuZxUZYd.js} +1 -1
- package/web/dist/assets/channel-B50-C7oJ.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-BuHucQOQ.js → chunk-2Q5K7J3B-BX7lDvmf.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-T-AE5Yz0.js → chunk-32BRIVSS-COupwzAw.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-B_kTsNzt.js → chunk-5VM5RSS4-C_IyERib.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-CtqgceBQ.js → chunk-EX3LRPZG-CyfIuYKn.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-C_Fv9QUd.js → chunk-JWPE2WC7-Cj5lvEfN.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-D5GeF8ZZ.js → chunk-MOJQB5TN-l907LpMA.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-Cg63wDst.js → chunk-RYQCIY6F-PLru1fOZ.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-Bz6G3lev.js → chunk-V7JOEXUC-C6GCxiE5.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-DKs_hvUD.js → chunk-VR4S4FIN-BGelrIxQ.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-B9AtUlbn.js → chunk-XXDRQBXY-BmJstjEv.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-CIJlKS0m.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-CIJlKS0m.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-rAG6oS0i.js → cose-bilkent-JH36ORCC-CRabxOOK.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-CUy2C8UT.js → cynefin-VYW2F7L2-CminxBoG.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-C1W8blXH.js → cynefinDiagram-TSTJHNR4-yBazsQf4.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-DC0XqwcC.js → dagre-VKFMJZFB-DcZuZV2n.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-COAY9zCP.js → diagram-FQU43EPY-CHoFzsw6.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-DLPr8eWG.js → diagram-G47NLZAW-B8leqMRl.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-DoNgwoDz.js → diagram-NH7WQ7WH-DNoVxpa3.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-D3-5TINE.js → diagram-OA4YK3LP-gC3Q2X6-.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-DplCkBJC.js → diagram-WEI45ONY-CABqyjDl.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-dvcVh7yr.js → ebnfDiagram-CCIWWBDH-cSguXaDC.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-Cde8cTY6.js → erDiagram-Q63AITRT-CoWX37V3.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-MGMNsh2E.js → flowDiagram-23GEKE2U-I9DHYjhG.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-BiKw7AfS.js → ganttDiagram-NO4QXBWP-DaFnfYTt.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-vC7fsylq.js → gitGraphDiagram-IHSO6WYX-CwiXVM_O.js} +1 -1
- package/web/dist/assets/index-CHlrgp0s.css +1 -0
- package/web/dist/assets/index-bf7RCrAf.js +24 -0
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-Du1uirB0.js → infoDiagram-FWYZ7A6U-DtzIrIpW.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-Dk--Qshr.js → ishikawaDiagram-FXEZZL3T-B9nk829n.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-iDE4zf_N.js → journeyDiagram-5HDEW3XC-xLaPm-ow.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-D7WI8iRR.js → kanban-definition-HUTT4EX6-BnDqLYLC.js} +1 -1
- package/web/dist/assets/{linear-B1_kxCBK.js → linear-Djc7nVo8.js} +1 -1
- package/web/dist/assets/{mermaid.core-DNmC5O4Y.js → mermaid.core-B5elhUtV.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-DIbLKLMA.js → mindmap-definition-LN4V7U3C-N-B7EJGs.js} +1 -1
- package/web/dist/assets/{papaparse.min-BdWinjY-.js → papaparse.min-BqM-LJkq.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-CkxxR07G.js → pegDiagram-2B236MQR-BIollSMB.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-Dxvp1mn7.js → pieDiagram-ENE6RG2P-DkZY6iCd.js} +1 -1
- package/web/dist/assets/pilotswarm-Di5s6c69.js +105 -0
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-CrL0hjZW.js → quadrantDiagram-ABIIQ3AL-iUDmQIyL.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-CmVmFBPa.js → railroadDiagram-RFXS5EU6-BSCGJgV7.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-DJc3NRXb.js → requirementDiagram-TGXJPOKE-9yqcGRUA.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-BJNqWqBb.js → sankeyDiagram-HTMAVEWB-SPh1plT9.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-C0wddECV.js → sequenceDiagram-DBY2YBRQ-C0nv5x71.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-Cw0dqPWS.js → sizeCapture-X5ZJPWSS-DMXfUt1o.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-C5793RHZ.js → stateDiagram-2N3HPSRC-vjhtquua.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-CYIeYmPN.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-Bhpm77Zu.js → swimlanes-5IMT3BWC-Bqewmkt5.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-BeKhH9C9.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-CwbE4NsH.js → timeline-definition-FHXFAJF6-DYHsdXUL.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-DZnJEL_w.js → vennDiagram-L72KCM5P-Cb7cOHSa.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-DwTgBDev.js → wardleyDiagram-EHGQE667-BHpO1byL.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-BiPjN3Qc.js → xychartDiagram-FW5EYKEG-DHDvGPtH.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/runtime.js +1 -0
- package/web/dist/assets/channel-6wPrR6sC.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-vvXyIfbC.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-vvXyIfbC.js +0 -1
- package/web/dist/assets/index-D9Ab1VTY.css +0 -1
- package/web/dist/assets/index-DDAyy127.js +0 -24
- package/web/dist/assets/pilotswarm-493nqGmy.js +0 -98
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-qGaRayHj.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-BXjNM_Zw.js +0 -8
package/ui/core/src/moa.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Personal dashboards contain references and geometry only, never session data.
|
|
2
|
+
export const MOA_MAX_DASHBOARDS = 5;
|
|
2
3
|
export const MOA_MAX_PANELS = 16;
|
|
3
4
|
export const MOA_BREAKPOINT = 920;
|
|
4
5
|
const object = (v) => v && typeof v === "object" && !Array.isArray(v);
|
|
@@ -24,17 +25,37 @@ export function normalizeMoaLayout(value) {
|
|
|
24
25
|
return { name: typeof value.name === "string" ? value.name.trim().slice(0, 64) || "Untitled MoA" : "Untitled MoA", tree: value.tree == null ? null : walk(value.tree) };
|
|
25
26
|
}
|
|
26
27
|
export function normalizeMoa(value) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const safeLayout = (layout, dashboardId = "moa-1", name = "MoA 1") => {
|
|
29
|
+
let tree = null;
|
|
30
|
+
try { tree = normalizeMoaLayout(layout).tree; } catch {}
|
|
31
|
+
const leaves = moaLeaves(tree);
|
|
32
|
+
return { id: dashboardId, name: typeof layout?.name === "string" ? layout.name.trim().slice(0, 64) || name : name, tree,
|
|
33
|
+
...(Number.isFinite(layout?.aspectRatio) && layout.aspectRatio >= .2 && layout.aspectRatio <= 8 ? { aspectRatio: layout.aspectRatio } : {}),
|
|
34
|
+
focusedPanelId: leaves.some(p => p.id === layout?.focusedPanelId) ? layout.focusedPanelId : leaves[0]?.id || null };
|
|
35
|
+
};
|
|
36
|
+
if (value?.version === 3 && Array.isArray(value.dashboards)) {
|
|
37
|
+
const used = new Set();
|
|
38
|
+
const dashboards = value.dashboards.slice(0, MOA_MAX_DASHBOARDS).map((dashboard, index) => {
|
|
39
|
+
let key = id(dashboard?.id) && !used.has(dashboard.id) ? dashboard.id : `moa-${index + 1}`;
|
|
40
|
+
while (used.has(key)) key += "-copy";
|
|
41
|
+
used.add(key);
|
|
42
|
+
return safeLayout(dashboard, key, `MoA ${index + 1}`);
|
|
43
|
+
});
|
|
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 };
|
|
31
46
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
let source = value;
|
|
48
|
+
if (!(value?.version === 2 || (object(value) && Object.hasOwn(value, "tree")))) {
|
|
49
|
+
// Match the old single-workspace migration; do not resurrect discarded tabs.
|
|
50
|
+
const slots = Array.isArray(value?.slots) ? value.slots.slice(0, 5) : [];
|
|
51
|
+
const active = Number.isInteger(value?.activeSlot) ? Math.max(0, Math.min(4, value.activeSlot)) : 0;
|
|
52
|
+
source = safeLayout(slots[active]).tree ? slots[active] : slots.find(slot => safeLayout(slot).tree);
|
|
53
|
+
}
|
|
54
|
+
return { version: 3, activeDashboardId: "moa-1", dashboards: [safeLayout(source)] };
|
|
55
|
+
}
|
|
56
|
+
export const activeMoaDashboard = value => value.dashboards.find(d => d.id === value.activeDashboardId) || value.dashboards[0];
|
|
57
|
+
export function updateMoaDashboard(value, dashboardId, patch) {
|
|
58
|
+
return normalizeMoa({ ...value, dashboards: value.dashboards.map(d => d.id === dashboardId ? { ...d, ...patch } : d) });
|
|
38
59
|
}
|
|
39
60
|
export function replaceMoaNode(tree, nodeId, next) {
|
|
40
61
|
if (!tree) return null;
|
package/ui/core/src/reducer.js
CHANGED
|
@@ -319,63 +319,67 @@ function isSameOrOlderSessionUpdate(previousSession, nextSession) {
|
|
|
319
319
|
return previousAt > 0 && nextAt > 0 && nextAt <= previousAt;
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
// "running" is far worse than a brief wrong status.
|
|
322
|
+
// Completion can come from orchestration metadata without another custom
|
|
323
|
+
// status write. Allow it at the same version, but never at an older version.
|
|
325
324
|
const TERMINAL_SESSION_STATUSES = new Set(["completed", "failed", "cancelled", "error"]);
|
|
326
325
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
//
|
|
345
|
-
// "Genuinely newer" is decided by the server's monotonic statusVersion when
|
|
346
|
-
// both sides carry one — wall-clock updatedAt is only the fallback. The
|
|
347
|
-
// timestamp heuristic alone can WEDGE a finished turn as "running" forever:
|
|
348
|
-
// client-side event merges (turn_completed, context usage) inflate the held
|
|
349
|
-
// session's updatedAt past the server row's idle-write timestamp, so every
|
|
350
|
-
// subsequent idle poll compares as stale and only a terminal status could
|
|
351
|
-
// ever land. Observed live (2026-07-21, local portal): server row idle at
|
|
352
|
-
// statusVersion 5 while the client showed "Working.." indefinitely.
|
|
353
|
-
function shouldPreserveRunningStatus(previousSession, nextSession) {
|
|
354
|
-
const nextStatus = String(nextSession?.status ?? "").toLowerCase();
|
|
355
|
-
if (previousSession?.status !== "running"
|
|
356
|
-
|| nextStatus === "running"
|
|
357
|
-
|| TERMINAL_SESSION_STATUSES.has(nextStatus)) {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
const previousVersion = Number(previousSession?.statusVersion);
|
|
361
|
-
const nextVersion = Number(nextSession?.statusVersion);
|
|
362
|
-
if (Number.isFinite(previousVersion) && previousVersion > 0
|
|
363
|
-
&& Number.isFinite(nextVersion) && nextVersion > 0) {
|
|
364
|
-
// Monotonic server counter: a higher version is authoritative and
|
|
365
|
-
// must land; a same-or-lower version is provably stale and is held
|
|
366
|
-
// regardless of what the timestamps claim.
|
|
367
|
-
return nextVersion <= previousVersion;
|
|
326
|
+
function sessionStatusVersion(session) {
|
|
327
|
+
const value = session?.statusVersion;
|
|
328
|
+
if (value == null || value === "" || typeof value === "boolean") return null;
|
|
329
|
+
const version = Number(value);
|
|
330
|
+
return Number.isSafeInteger(version) && version > 0 ? version : null;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// List polls and detail requests can arrive out of order. Compare the server
|
|
334
|
+
// counter in both directions: an old running row must not revive an idle
|
|
335
|
+
// session, and an old idle row must not hide a running one. Keep the version
|
|
336
|
+
// with its status; accepting an older counter would let the next stale row win.
|
|
337
|
+
function shouldPreserveSessionStatus(previousSession, nextSession) {
|
|
338
|
+
const previousVersion = sessionStatusVersion(previousSession);
|
|
339
|
+
const nextVersion = sessionStatusVersion(nextSession);
|
|
340
|
+
if (previousVersion != null && nextVersion != null) {
|
|
341
|
+
if (nextVersion < previousVersion) return true;
|
|
342
|
+
if (nextVersion > previousVersion) return false;
|
|
368
343
|
}
|
|
369
|
-
|
|
344
|
+
const previousStatus = previousSession?.status;
|
|
345
|
+
const nextStatus = nextSession?.status;
|
|
346
|
+
if (!previousStatus || !nextStatus || previousStatus === nextStatus
|
|
347
|
+
|| TERMINAL_SESSION_STATUSES.has(nextStatus)) return false;
|
|
348
|
+
if (previousVersion != null && nextVersion != null) return true;
|
|
349
|
+
// Legacy rows and live event patches may lack a counter. Do not guess
|
|
350
|
+
// their order without timestamps. Preserve the terminal-status escape
|
|
351
|
+
// hatch, since updatedAt also changes for non-status events.
|
|
352
|
+
if (previousStatus === "running") return isSameOrOlderSessionUpdate(previousSession, nextSession);
|
|
353
|
+
// An unversioned new turn may share its timestamp with the preceding idle
|
|
354
|
+
// row. Equal timestamps alone are not evidence that a start is stale.
|
|
355
|
+
const previousAt = sessionUpdateTimestampMs(previousSession);
|
|
356
|
+
const nextAt = sessionUpdateTimestampMs(nextSession);
|
|
357
|
+
return nextStatus === "running" && previousAt > 0 && nextAt > 0 && nextAt < previousAt;
|
|
370
358
|
}
|
|
371
359
|
|
|
360
|
+
// These values describe the same run as status. Mixing an old question or
|
|
361
|
+
// pause reason with a newer status can change composer routing and controls.
|
|
362
|
+
const SESSION_STATUS_FIELDS = new Set([
|
|
363
|
+
"status", "statusVersion", "updatedAt", "orchestrationStatus",
|
|
364
|
+
"pendingQuestion", "waitReason", "pauseState", "error", "result",
|
|
365
|
+
]);
|
|
366
|
+
|
|
372
367
|
function mergeDefinedSessionFields(previousSession = {}, nextSession = {}) {
|
|
373
368
|
let merged = previousSession || {};
|
|
374
|
-
const
|
|
369
|
+
const preserveStatus = shouldPreserveSessionStatus(previousSession, nextSession);
|
|
370
|
+
const previousVersion = sessionStatusVersion(previousSession);
|
|
375
371
|
for (const [key, value] of Object.entries(nextSession || {})) {
|
|
376
372
|
if (value === undefined) continue;
|
|
377
|
-
if (
|
|
378
|
-
|
|
373
|
+
if (preserveStatus && SESSION_STATUS_FIELDS.has(key)) continue;
|
|
374
|
+
// Repeated snapshots of one server version cannot roll its timestamp
|
|
375
|
+
// back and then admit an older unversioned running update.
|
|
376
|
+
if (key === "updatedAt" && previousVersion != null
|
|
377
|
+
&& previousVersion === sessionStatusVersion(nextSession)
|
|
378
|
+
&& sessionUpdateTimestampMs(nextSession) < sessionUpdateTimestampMs(previousSession)) continue;
|
|
379
|
+
// An absent/invalid counter must not erase the last known server version.
|
|
380
|
+
if (key === "statusVersion" && previousVersion != null
|
|
381
|
+
&& sessionStatusVersion(nextSession) == null) continue;
|
|
382
|
+
if (key === "pendingQuestion" && isAnsweredPendingQuestion(previousSession, value, nextSession)) {
|
|
379
383
|
if (merged === previousSession) {
|
|
380
384
|
merged = { ...(previousSession || {}) };
|
|
381
385
|
}
|
|
@@ -551,10 +555,18 @@ function normalizedPendingQuestionText(pendingQuestion) {
|
|
|
551
555
|
return String(pendingQuestion?.question || "").trim();
|
|
552
556
|
}
|
|
553
557
|
|
|
554
|
-
function isAnsweredPendingQuestion(previousSession, pendingQuestion) {
|
|
555
|
-
const answeredQuestion = normalizedPendingQuestionText(previousSession?.answeredPendingQuestion);
|
|
558
|
+
function isAnsweredPendingQuestion(previousSession, pendingQuestion, nextSession) {
|
|
556
559
|
const incomingQuestion = normalizedPendingQuestionText(pendingQuestion);
|
|
557
|
-
|
|
560
|
+
if (!incomingQuestion) return false;
|
|
561
|
+
const incomingTime = sessionUpdateTimestampMs({ updatedAt: pendingQuestion?.askedAt })
|
|
562
|
+
|| sessionUpdateTimestampMs(nextSession);
|
|
563
|
+
return [previousSession?.answeredPendingQuestion, previousSession?.resolvedInputQuestion].some(answered => {
|
|
564
|
+
if (normalizedPendingQuestionText(answered) !== incomingQuestion) return false;
|
|
565
|
+
const answeredAt = sessionUpdateTimestampMs({ updatedAt: answered?.answeredAt });
|
|
566
|
+
// Preserve the answer fence across stale detail polls, but let a new
|
|
567
|
+
// instance of the same question through when it was asked later.
|
|
568
|
+
return !incomingTime || !answeredAt || incomingTime <= answeredAt;
|
|
569
|
+
});
|
|
558
570
|
}
|
|
559
571
|
|
|
560
572
|
function pickDefaultActiveSessionId(sessions = []) {
|
package/ui/core/src/selectors.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { INSPECTOR_TABS, FOCUS_REGIONS } from "./commands.js";
|
|
2
|
+
import { chatCallLine, firstCallLine } from "./chat-activity.js";
|
|
2
3
|
import { canvasKey as canvasSlotKey, parseCanvasKey } from "./state.js";
|
|
3
4
|
import { isManuallyOrderableSession } from "./session-tree.js";
|
|
4
5
|
import {
|
|
@@ -28,7 +29,7 @@ import {
|
|
|
28
29
|
getContextHeaderBadge,
|
|
29
30
|
} from "./context-usage.js";
|
|
30
31
|
import { canonicalSystemTitle } from "./system-titles.js";
|
|
31
|
-
import { withSessionWarnings } from "./session-errors.js";
|
|
32
|
+
import { withSessionWarnings, isActivityOnlySessionError } from "./session-errors.js";
|
|
32
33
|
import {
|
|
33
34
|
BUDGET_PERIODS,
|
|
34
35
|
BUDGET_SERIES_DAYS,
|
|
@@ -1645,7 +1646,7 @@ function chatAlreadyContainsPendingQuestion(chat, question) {
|
|
|
1645
1646
|
|
|
1646
1647
|
function buildSessionErrorMessage(session, events = []) {
|
|
1647
1648
|
const errorText = String(session?.error || "").trim();
|
|
1648
|
-
if (!errorText) return null;
|
|
1649
|
+
if (!errorText || isActivityOnlySessionError(errorText)) return null;
|
|
1649
1650
|
|
|
1650
1651
|
const errorKind = getSessionErrorVisualKind(session);
|
|
1651
1652
|
if (!errorKind) return null;
|
|
@@ -2251,6 +2252,7 @@ function chatMessageSpacingKind(message) {
|
|
|
2251
2252
|
|
|
2252
2253
|
function shouldInsertChatSpacer(currentMessage, nextMessage) {
|
|
2253
2254
|
if (!currentMessage || !nextMessage) return false;
|
|
2255
|
+
if (currentMessage.kind === "chat-call" || nextMessage.kind === "chat-call") return false;
|
|
2254
2256
|
return chatMessageSpacingKind(currentMessage) !== chatMessageSpacingKind(nextMessage);
|
|
2255
2257
|
}
|
|
2256
2258
|
|
|
@@ -2287,7 +2289,7 @@ function buildCollapsedSystemNoticeLine(text, timestamp = "") {
|
|
|
2287
2289
|
};
|
|
2288
2290
|
}
|
|
2289
2291
|
|
|
2290
|
-
function buildSystemNoticeLine({ title, summary, body, timestamp = "", color = "gray" }) {
|
|
2292
|
+
function buildSystemNoticeLine({ title, summary, body, timestamp = "", color = "gray", callPreview, callKey }) {
|
|
2291
2293
|
const normalizedTitle = String(title || "System").trim() || "System";
|
|
2292
2294
|
const normalizedSummary = String(summary || "").replace(/\s+/g, " ").trim();
|
|
2293
2295
|
return {
|
|
@@ -2296,6 +2298,7 @@ function buildSystemNoticeLine({ title, summary, body, timestamp = "", color = "
|
|
|
2296
2298
|
summary: normalizedSummary,
|
|
2297
2299
|
body: decorateArtifactLinksForChat(body || normalizedSummary),
|
|
2298
2300
|
color,
|
|
2301
|
+
...(callPreview !== undefined ? { callPreview: `${normalizedTitle} — ${firstCallLine(callPreview)}`, callKey } : {}),
|
|
2299
2302
|
};
|
|
2300
2303
|
}
|
|
2301
2304
|
|
|
@@ -2401,6 +2404,8 @@ function buildSystemNoticeCardLines(systemText, message, maxWidth) {
|
|
|
2401
2404
|
].filter((line, index) => index === 2 || line).join("\n");
|
|
2402
2405
|
return [buildSystemNoticeLine({
|
|
2403
2406
|
title: `Sub-agent Response — ${shortId}`,
|
|
2407
|
+
callPreview: subAgent.result || subAgent.status,
|
|
2408
|
+
callKey: `${message.id}:agent:${shortId}`,
|
|
2404
2409
|
summary: subAgent.status || "completed",
|
|
2405
2410
|
timestamp,
|
|
2406
2411
|
body,
|
|
@@ -2420,6 +2425,8 @@ function buildSystemNoticeCardLines(systemText, message, maxWidth) {
|
|
|
2420
2425
|
.join(" · ");
|
|
2421
2426
|
return [buildSystemNoticeLine({
|
|
2422
2427
|
title: `Sub-agent Responses (${subAgent.agents.length})`,
|
|
2428
|
+
callPreview: subAgent.agents.map(agent => firstCallLine(agent.result) || agent.status).join(" · "),
|
|
2429
|
+
callKey: `${message.id}:agents`,
|
|
2423
2430
|
summary,
|
|
2424
2431
|
timestamp,
|
|
2425
2432
|
body: sections.join("\n\n---\n\n"),
|
|
@@ -2505,6 +2512,17 @@ function buildChatMessageLines(message, maxWidth, options = {}) {
|
|
|
2505
2512
|
}
|
|
2506
2513
|
|
|
2507
2514
|
function buildChatMessageLinesUncached(message, maxWidth, options = {}) {
|
|
2515
|
+
if (message?.kind === "chat-call") {
|
|
2516
|
+
return options.tableMode === "sentinel" ? [chatCallLine(message)] : [];
|
|
2517
|
+
}
|
|
2518
|
+
if (options.tableMode === "sentinel" && (message?.sender?.kind === "agent" || message?.agentCallPreview !== undefined)) {
|
|
2519
|
+
const label = message.cardTitle || message.sender?.display || "Agent message";
|
|
2520
|
+
return [{
|
|
2521
|
+
kind: "chatCall", callKey: message.id, category: "Agent",
|
|
2522
|
+
text: `${label} — ${firstCallLine(message.agentCallPreview ?? message.text)}`,
|
|
2523
|
+
body: message.text, time: message.time,
|
|
2524
|
+
}];
|
|
2525
|
+
}
|
|
2508
2526
|
if (message?.splash) {
|
|
2509
2527
|
// Swap in the narrow-viewport variant when the main art would
|
|
2510
2528
|
// overflow the pane (mobile portal, narrow terminals). When the
|
|
@@ -2574,7 +2592,9 @@ function buildChatMessageLinesUncached(message, maxWidth, options = {}) {
|
|
|
2574
2592
|
const askedAndAnswered = parseAskedAndAnsweredExchange(message?.text || "");
|
|
2575
2593
|
if (askedAndAnswered) {
|
|
2576
2594
|
return [
|
|
2577
|
-
|
|
2595
|
+
// Older runtimes inserted this placeholder for late answers.
|
|
2596
|
+
// Keep the user's answer, without displaying a fabricated card.
|
|
2597
|
+
...(askedAndAnswered.question === "a question" ? [] : buildMessageCardLines({
|
|
2578
2598
|
title: "Question",
|
|
2579
2599
|
timestamp: formatTimestamp(message?.createdAt || message?.time),
|
|
2580
2600
|
body: askedAndAnswered.question,
|
|
@@ -2582,7 +2602,7 @@ function buildChatMessageLinesUncached(message, maxWidth, options = {}) {
|
|
|
2582
2602
|
titleColor: USER_CHAT_COLOR,
|
|
2583
2603
|
borderColor: USER_CHAT_COLOR,
|
|
2584
2604
|
tableMode: options.tableMode,
|
|
2585
|
-
}),
|
|
2605
|
+
})),
|
|
2586
2606
|
...buildChatMessageLines({
|
|
2587
2607
|
...message,
|
|
2588
2608
|
text: askedAndAnswered.answer,
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// Copilot emits this diagnostic for an accepted turn without visible text.
|
|
2
|
+
// Keep its durable event and Activity entry, but do not turn it into a chat
|
|
3
|
+
// warning. Match only this message and the runtime's known retry wrapper.
|
|
4
|
+
export function isActivityOnlySessionError(text) {
|
|
5
|
+
return /^(?:Execution failed:\s*)?No response was returned\. Send your message again to retry\.(?: \(retry \d+\/\d+ in \d+s\))?$/.test(String(text || "").trim());
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
export function shouldKeepSessionWarning(previousSession, nextSession) {
|
|
2
9
|
if (!String(previousSession?.error || "").trim()) return false;
|
|
3
10
|
if (nextSession?.error !== undefined) return false;
|
|
@@ -26,7 +33,7 @@ const sameError = (statusText, eventText) => Boolean(eventText) && statusText.in
|
|
|
26
33
|
|
|
27
34
|
export function buildSessionWarning(event) {
|
|
28
35
|
const text = errorText(event);
|
|
29
|
-
if (!text) return null;
|
|
36
|
+
if (!text || isActivityOnlySessionError(text)) return null;
|
|
30
37
|
const failed = event?.data?.fatal === true;
|
|
31
38
|
return {
|
|
32
39
|
id: `${event.sessionId}:${event.seq}:warning`,
|
|
@@ -79,8 +86,9 @@ export function retainSessionWarnings(previous, next, events = [], now = Date.no
|
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
export function withSessionWarnings(chat, session, events = []) {
|
|
82
|
-
const messages =
|
|
89
|
+
const messages = chat.filter(message => message.kind !== "session-warning" || !isActivityOnlySessionError(message.text));
|
|
83
90
|
for (const warning of session?.chatWarnings || []) {
|
|
91
|
+
if (isActivityOnlySessionError(warning.text)) continue;
|
|
84
92
|
// Status often wins the race against its durable event. Match only
|
|
85
93
|
// that boundary, never a later failure after a new user/agent message.
|
|
86
94
|
const following = events.filter(e => Number(e.seq) > warning.afterSeq);
|
|
@@ -5,12 +5,14 @@ export function canonicalSystemTitle(session, brandingTitle = "PilotSwarm") {
|
|
|
5
5
|
if (agentId === "sweeper") return "Sweeper Agent";
|
|
6
6
|
if (agentId === "resourcemgr") return "Resource Manager Agent";
|
|
7
7
|
if (agentId === "facts-manager") return "Facts Manager";
|
|
8
|
+
if (agentId === "agent-manager") return "Agent Smith";
|
|
8
9
|
|
|
9
10
|
const rawTitle = String(session?.title || "").trim();
|
|
10
11
|
if (/^pilotswarm(?: agent)?$/i.test(rawTitle)) return brandedRootTitle;
|
|
11
12
|
if (/^sweeper agent$/i.test(rawTitle) || /^sweeper$/i.test(rawTitle)) return "Sweeper Agent";
|
|
12
13
|
if (/^resource manager agent$/i.test(rawTitle) || /^resourcemgr$/i.test(rawTitle)) return "Resource Manager Agent";
|
|
13
14
|
if (/^facts manager$/i.test(rawTitle) || /^facts-manager$/i.test(rawTitle)) return "Facts Manager";
|
|
15
|
+
if (/^agent manager$/i.test(rawTitle)) return "Agent Smith";
|
|
14
16
|
return rawTitle || "System Agent";
|
|
15
17
|
}
|
|
16
18
|
|