pilotswarm 0.5.21 → 0.5.23
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 -1
- package/tui/src/node-sdk-transport.js +22 -5
- package/ui/core/src/controller.js +1 -1
- package/ui/core/src/reducer.js +3 -2
- package/ui/core/src/selectors.js +142 -2
- package/ui/core/src/state.js +1 -1
- package/ui/react/src/web-app.js +541 -22
- package/web/dist/assets/abnfDiagram-VRR7QNED-BHkTpRkx.js +1 -0
- package/web/dist/assets/arc-BOgZyEWA.js +1 -0
- package/web/dist/assets/architectureDiagram-ZJ3FMSHR-DeE8fDMy.js +36 -0
- package/web/dist/assets/blockDiagram-677ZJIJ3-CzhdxVEu.js +132 -0
- package/web/dist/assets/c4Diagram-LMCZKHZV-Cc6p_IKP.js +10 -0
- package/web/dist/assets/channel-DhhWyKRW.js +1 -0
- package/web/dist/assets/chunk-2Q5K7J3B-BCo94LTM.js +1 -0
- package/web/dist/assets/chunk-32BRIVSS-DaklJ-6i.js +1 -0
- package/web/dist/assets/chunk-5VM5RSS4-BN_wg2pr.js +15 -0
- package/web/dist/assets/chunk-EX3LRPZG-DSFFLUoL.js +231 -0
- package/web/dist/assets/chunk-JWPE2WC7-DB4dAjza.js +1 -0
- package/web/dist/assets/chunk-MOJQB5TN-CRM5eGQS.js +88 -0
- package/web/dist/assets/chunk-RYQCIY6F-BFlkjazU.js +1 -0
- package/web/dist/assets/chunk-V7JOEXUC-Bnfh4AAv.js +206 -0
- package/web/dist/assets/chunk-VR4S4FIN-CQc4_qVM.js +1 -0
- package/web/dist/assets/chunk-XXDRQBXY-CZivyoDm.js +1 -0
- package/web/dist/assets/classDiagram-OUVF2IWQ-CQBxGpio.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-CQBxGpio.js +1 -0
- package/web/dist/assets/cose-bilkent-JH36ORCC-BUibhTD-.js +1 -0
- package/web/dist/assets/cynefin-VYW2F7L2-CRkjg5zF.js +166 -0
- package/web/dist/assets/cynefinDiagram-TSTJHNR4-BoeQn4Um.js +62 -0
- package/web/dist/assets/cytoscape.esm-D3_iZ_3b.js +321 -0
- package/web/dist/assets/dagre-VKFMJZFB-DGo7-PY5.js +4 -0
- package/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/web/dist/assets/diagram-FQU43EPY-DMXd2bdO.js +3 -0
- package/web/dist/assets/diagram-G47NLZAW-CkYHblMZ.js +24 -0
- package/web/dist/assets/diagram-NH7WQ7WH-C9iwEe4T.js +24 -0
- package/web/dist/assets/diagram-OA4YK3LP-DpYLrfwb.js +30 -0
- package/web/dist/assets/diagram-WEI45ONY-BvrBukHb.js +41 -0
- package/web/dist/assets/ebnfDiagram-CCIWWBDH-DGl1r3zR.js +1 -0
- package/web/dist/assets/erDiagram-Q63AITRT-CoP5DBSb.js +85 -0
- package/web/dist/assets/flowDiagram-23GEKE2U-DVHtyYyr.js +156 -0
- package/web/dist/assets/ganttDiagram-NO4QXBWP-_JyR78yC.js +292 -0
- package/web/dist/assets/gitGraphDiagram-IHSO6WYX-Bj7sh0Db.js +106 -0
- package/web/dist/assets/graph-DOmOIIwC.js +1 -0
- package/web/dist/assets/{index-BDMVG8w6.js → index-CB6NfSZb.js} +3 -3
- package/web/dist/assets/{index-CxkLM8_a.css → index-SmXlZ1eu.css} +1 -1
- package/web/dist/assets/infoDiagram-FWYZ7A6U-BrvMeRmA.js +2 -0
- package/web/dist/assets/init-Gi6I4Gst.js +1 -0
- package/web/dist/assets/ishikawaDiagram-FXEZZL3T-lNDujpEC.js +70 -0
- package/web/dist/assets/journeyDiagram-5HDEW3XC-yoD2QRni.js +139 -0
- package/web/dist/assets/kanban-definition-HUTT4EX6-DtyMlypg.js +89 -0
- package/web/dist/assets/katex-HP8lGamR.js +257 -0
- package/web/dist/assets/layout-D-LzfAck.js +1 -0
- package/web/dist/assets/linear-DXPbXF9v.js +1 -0
- package/web/dist/assets/map-DxJ2ADlA.js +1 -0
- package/web/dist/assets/mermaid.core-DCEXqVUL.js +308 -0
- package/web/dist/assets/mindmap-definition-LN4V7U3C-DpKHDJFg.js +96 -0
- package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/web/dist/assets/pegDiagram-2B236MQR-mxFI_8qh.js +1 -0
- package/web/dist/assets/pieDiagram-ENE6RG2P-sUWLrwMG.js +39 -0
- package/web/dist/assets/pilotswarm-CdEkcq2V.js +91 -0
- package/web/dist/assets/quadrantDiagram-ABIIQ3AL-Cph1z7ye.js +7 -0
- package/web/dist/assets/railroadDiagram-RFXS5EU6-BPKkt2va.js +1 -0
- package/web/dist/assets/requirementDiagram-TGXJPOKE-BB6DNHCy.js +84 -0
- package/web/dist/assets/sankeyDiagram-HTMAVEWB-BASvPQk2.js +40 -0
- package/web/dist/assets/sequenceDiagram-DBY2YBRQ-DMrNas01.js +162 -0
- package/web/dist/assets/sizeCapture-X5ZJPWSS-Ddq5yeZZ.js +1 -0
- package/web/dist/assets/stateDiagram-2N3HPSRC-DUb_IFST.js +1 -0
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-C3nsHf5u.js +1 -0
- package/web/dist/assets/swimlanes-5IMT3BWC-Cs9JsGaf.js +2 -0
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-C2xZA5V6.js +8 -0
- package/web/dist/assets/timeline-definition-FHXFAJF6-xuNF5fTm.js +120 -0
- package/web/dist/assets/vennDiagram-L72KCM5P-fsI1GFLy.js +34 -0
- package/web/dist/assets/wardleyDiagram-EHGQE667-DEc-kumC.js +78 -0
- package/web/dist/assets/xychartDiagram-FW5EYKEG-BCo6Qrfu.js +7 -0
- package/web/dist/index.html +3 -3
- package/web/dist/assets/pilotswarm-CjMx44so.js +0 -90
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pilotswarm",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.23",
|
|
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",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"hono": "^4.12.10",
|
|
79
79
|
"ink": "^6.8.0",
|
|
80
80
|
"jose": "^6.2.2",
|
|
81
|
+
"mermaid": "^11.16.0",
|
|
81
82
|
"pilotswarm-sdk": "^0.5.20",
|
|
82
83
|
"react": "^19.2.4",
|
|
83
84
|
"react-dom": "^19.2.4",
|
|
@@ -1690,8 +1690,13 @@ export class NodeSdkTransport {
|
|
|
1690
1690
|
const payload = JSON.parse(body || "{}");
|
|
1691
1691
|
const items = Array.isArray(payload?.items) ? payload.items : [];
|
|
1692
1692
|
resolve(items
|
|
1693
|
-
.map((item) =>
|
|
1694
|
-
|
|
1693
|
+
.map((item) => ({
|
|
1694
|
+
name: String(item?.metadata?.name || "").trim(),
|
|
1695
|
+
containers: (Array.isArray(item?.spec?.containers) ? item.spec.containers : [])
|
|
1696
|
+
.map((container) => String(container?.name || "").trim())
|
|
1697
|
+
.filter(Boolean),
|
|
1698
|
+
}))
|
|
1699
|
+
.filter((pod) => pod.name));
|
|
1695
1700
|
} catch (error) {
|
|
1696
1701
|
reject(error);
|
|
1697
1702
|
}
|
|
@@ -1709,6 +1714,9 @@ export class NodeSdkTransport {
|
|
|
1709
1714
|
timestamps: "true",
|
|
1710
1715
|
tailLines: String(options.tailLines ?? 500),
|
|
1711
1716
|
});
|
|
1717
|
+
// Multi-container pods (sidecars, init containers) reject log reads
|
|
1718
|
+
// that don't name a container (400 BadRequest).
|
|
1719
|
+
if (options.container) params.set("container", options.container);
|
|
1712
1720
|
const pathName = `/api/v1/namespaces/${encodeURIComponent(config.namespace)}/pods/${encodeURIComponent(podName)}/log?${params.toString()}`;
|
|
1713
1721
|
|
|
1714
1722
|
return new Promise((resolve, reject) => {
|
|
@@ -1810,9 +1818,9 @@ export class NodeSdkTransport {
|
|
|
1810
1818
|
this.logTailHandle = handle;
|
|
1811
1819
|
|
|
1812
1820
|
this.listPodsFromKubeApi(config, labelSelector)
|
|
1813
|
-
.then(async (
|
|
1821
|
+
.then(async (pods) => {
|
|
1814
1822
|
if (handle.stopped || this.logTailHandle !== handle) return;
|
|
1815
|
-
if (
|
|
1823
|
+
if (pods.length === 0) {
|
|
1816
1824
|
this.emitSyntheticLogMessage(
|
|
1817
1825
|
`No pods matched label selector ${JSON.stringify(labelSelector)} in namespace ${config.namespace}.`,
|
|
1818
1826
|
"warn",
|
|
@@ -1820,8 +1828,17 @@ export class NodeSdkTransport {
|
|
|
1820
1828
|
return;
|
|
1821
1829
|
}
|
|
1822
1830
|
|
|
1831
|
+
// Container selection per pod: explicit K8S_LOG_CONTAINER wins,
|
|
1832
|
+
// else the conventional "worker" container when present, else
|
|
1833
|
+
// the pod's first container. Single-container pods work either
|
|
1834
|
+
// way; multi-container pods 400 without an explicit name.
|
|
1835
|
+
const configuredContainer = String(process.env.K8S_LOG_CONTAINER || "").trim();
|
|
1823
1836
|
const results = await Promise.allSettled(
|
|
1824
|
-
|
|
1837
|
+
pods.map((pod) => {
|
|
1838
|
+
const container = configuredContainer
|
|
1839
|
+
|| (pod.containers.includes("worker") ? "worker" : pod.containers[0]);
|
|
1840
|
+
return this.streamPodLogsFromKubeApi(config, pod.name, handle, container ? { container } : {});
|
|
1841
|
+
}),
|
|
1825
1842
|
);
|
|
1826
1843
|
|
|
1827
1844
|
if (handle.stopped || this.logTailHandle !== handle) return;
|
|
@@ -3870,7 +3870,7 @@ export class PilotSwarmUiController {
|
|
|
3870
3870
|
}
|
|
3871
3871
|
|
|
3872
3872
|
setChatViewMode(mode) {
|
|
3873
|
-
if (mode !== "summary" && mode !== "transcript") return;
|
|
3873
|
+
if (mode !== "summary" && mode !== "transcript" && mode !== "rich") return;
|
|
3874
3874
|
if (this.getActiveSession()?.isGroup) return;
|
|
3875
3875
|
this.dispatch({ type: "ui/chatViewMode", mode });
|
|
3876
3876
|
}
|
package/ui/core/src/reducer.js
CHANGED
|
@@ -637,7 +637,8 @@ export function appReducer(state, action) {
|
|
|
637
637
|
const hasOwnerFilter = Object.prototype.hasOwnProperty.call(settings, "sessionOwnerFilter");
|
|
638
638
|
const hasLayout = Object.prototype.hasOwnProperty.call(settings, "layoutAdjustments");
|
|
639
639
|
const hasChatViewMode = Object.prototype.hasOwnProperty.call(settings, "chatViewMode")
|
|
640
|
-
&& (settings.chatViewMode === "summary" || settings.chatViewMode === "transcript"
|
|
640
|
+
&& (settings.chatViewMode === "summary" || settings.chatViewMode === "transcript"
|
|
641
|
+
|| settings.chatViewMode === "rich");
|
|
641
642
|
const hasPins = Object.prototype.hasOwnProperty.call(settings, "pinnedSessionIds");
|
|
642
643
|
const hasCollapsed = Object.prototype.hasOwnProperty.call(settings, "collapsedSessionIds");
|
|
643
644
|
// A pending/resolved deep-link intent outranks the profile's
|
|
@@ -701,7 +702,7 @@ export function appReducer(state, action) {
|
|
|
701
702
|
}
|
|
702
703
|
|
|
703
704
|
case "ui/chatViewMode":
|
|
704
|
-
if (action.mode !== "summary" && action.mode !== "transcript") {
|
|
705
|
+
if (action.mode !== "summary" && action.mode !== "transcript" && action.mode !== "rich") {
|
|
705
706
|
return state;
|
|
706
707
|
}
|
|
707
708
|
return {
|
package/ui/core/src/selectors.js
CHANGED
|
@@ -229,7 +229,15 @@ function shouldDecorateSessionOwners(state) {
|
|
|
229
229
|
// distinct human owner — otherwise the owner chip is pure noise on every
|
|
230
230
|
// row (you are always "you"). A narrowed owner filter is an explicit
|
|
231
231
|
// multi-user context, so honor that too.
|
|
232
|
-
|
|
232
|
+
// A narrowed filter counts as an explicit multi-user context ONLY when it
|
|
233
|
+
// names specific owners. The DEFAULT signed-in filter already sets
|
|
234
|
+
// `all: false` AND `includeShared: true` (see
|
|
235
|
+
// defaultOwnerFilterForPrincipal), so testing either of those tagged every
|
|
236
|
+
// row in a single-user deployment with a pointless "you are you" chip.
|
|
237
|
+
// Everything else falls through to counting DISTINCT owners below, which
|
|
238
|
+
// is what the chip actually communicates.
|
|
239
|
+
const ownerFilter = state.sessions?.ownerFilter;
|
|
240
|
+
if (Array.isArray(ownerFilter?.ownerKeys) && ownerFilter.ownerKeys.length > 0) return true;
|
|
233
241
|
const owners = new Set();
|
|
234
242
|
for (const session of Object.values(state.sessions?.byId || {})) {
|
|
235
243
|
if (!session || session.isSystem || session.isGroup) continue;
|
|
@@ -731,6 +739,37 @@ function buildSessionRowView(entry, session, state, totalDescendantCounts, visib
|
|
|
731
739
|
badgeRuns: [],
|
|
732
740
|
selectedMetaRuns: detailRuns,
|
|
733
741
|
detailRuns,
|
|
742
|
+
// Structured row description for renderers that draw real chrome
|
|
743
|
+
// instead of terminal runs (the portal's rich session list): status
|
|
744
|
+
// becomes a dot, depth becomes indentation, the owner/child/cron
|
|
745
|
+
// badges become chips. Purely additive — `runs`/`titleRuns` above
|
|
746
|
+
// stay the terminal's source of truth and the TUI ignores this.
|
|
747
|
+
chrome: {
|
|
748
|
+
kind: session?.isGroup ? "group"
|
|
749
|
+
: session?.isSystem ? "system"
|
|
750
|
+
: session?.serviceKind ? "service"
|
|
751
|
+
: "session",
|
|
752
|
+
title: hasRealTitle ? rawTitle : shortId,
|
|
753
|
+
untitled: !hasRealTitle,
|
|
754
|
+
statusColor: sessionStatusColor(session, mode),
|
|
755
|
+
accentColor: mainColor,
|
|
756
|
+
pinned: isPinned,
|
|
757
|
+
selectMode,
|
|
758
|
+
checked: isSelected,
|
|
759
|
+
owner: shouldDecorateSessionOwners(state) && !session?.isSystem && !session?.isGroup
|
|
760
|
+
? {
|
|
761
|
+
initials: effectiveOwner ? ownerInitials(effectiveOwner) : "?",
|
|
762
|
+
isMine: Boolean(ownerKeyForOwner(state?.auth?.principal)
|
|
763
|
+
&& ownerKeyForOwner(effectiveOwner) === ownerKeyForOwner(state?.auth?.principal)),
|
|
764
|
+
}
|
|
765
|
+
: null,
|
|
766
|
+
childBadge: collapseBadge ? { text: collapseBadge.text, color: collapseBadge.color } : null,
|
|
767
|
+
cron: Boolean(cronBadge),
|
|
768
|
+
age: relTime || null,
|
|
769
|
+
model: modelLabel || null,
|
|
770
|
+
shortId,
|
|
771
|
+
ctx: ctxRuns.length > 0 ? { text: ctxRuns[0].text, color: ctxRuns[0].color } : null,
|
|
772
|
+
},
|
|
734
773
|
};
|
|
735
774
|
}
|
|
736
775
|
|
|
@@ -1374,7 +1413,12 @@ function prefixRuns(text, color = "gray", options = {}) {
|
|
|
1374
1413
|
}];
|
|
1375
1414
|
}
|
|
1376
1415
|
|
|
1377
|
-
|
|
1416
|
+
// Header facts for one chat message — speaker label, label color, delivery
|
|
1417
|
+
// glyph, and timestamp — extracted from buildChatMessagePrefix so the rich
|
|
1418
|
+
// (block) chat renderer can consume the same labeling rules without the
|
|
1419
|
+
// terminal run/prefix shape. Returns { time, glyph, glyphColor, roleLabel,
|
|
1420
|
+
// roleColor }.
|
|
1421
|
+
function describeChatMessageHeader(message, options = {}) {
|
|
1378
1422
|
const time = formatTimestamp(message?.createdAt || message?.time);
|
|
1379
1423
|
// A user.message is labeled from the CURRENT VIEWER's perspective: "You"
|
|
1380
1424
|
// for the viewer's own messages, the sender's name for anyone else. When
|
|
@@ -1459,6 +1503,15 @@ function buildChatMessagePrefix(message, options = {}) {
|
|
|
1459
1503
|
: message?.role === "system"
|
|
1460
1504
|
? "yellow"
|
|
1461
1505
|
: "white";
|
|
1506
|
+
// `fromOtherPerson` lets renderers drop the speaker label for the
|
|
1507
|
+
// viewer's own messages and the agent's (a 1:1 chat needs no "You:" /
|
|
1508
|
+
// "Agent:" on every turn) while still naming OTHER humans in a shared
|
|
1509
|
+
// session, where attribution is the whole point.
|
|
1510
|
+
return { time, glyph, glyphColor, roleLabel, roleColor, fromOtherPerson };
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
function buildChatMessagePrefix(message, options = {}) {
|
|
1514
|
+
const { time, glyph, glyphColor, roleLabel, roleColor } = describeChatMessageHeader(message, options);
|
|
1462
1515
|
const prefix = time ? `[${time}] ` : "";
|
|
1463
1516
|
const runs = [...prefixRuns(prefix, "gray")];
|
|
1464
1517
|
if (glyph) {
|
|
@@ -2319,6 +2372,93 @@ export function selectChatLines(state, maxWidth = 80, options = {}) {
|
|
|
2319
2372
|
return lines.length > 0 ? lines : [{ text: "No messages yet.", color: "gray" }];
|
|
2320
2373
|
}
|
|
2321
2374
|
|
|
2375
|
+
// True when a chat message is plain user/assistant prose the rich (block)
|
|
2376
|
+
// renderer can format as markdown directly. Everything with terminal-era
|
|
2377
|
+
// special handling — splash art, thinking cards, chrome-less summaries,
|
|
2378
|
+
// system/PilotSwarm cards, asked-and-answered exchanges, embedded
|
|
2379
|
+
// [SYSTEM: …] notice segments — stays on the line renderer, which already
|
|
2380
|
+
// knows every edge case.
|
|
2381
|
+
function isRichRenderableChatMessage(message) {
|
|
2382
|
+
if (!message) return false;
|
|
2383
|
+
if (message.kind === "epoch-divider" || message.kind === "regen-refused") return false;
|
|
2384
|
+
if (message.splash || message.thinking || message.noChrome) return false;
|
|
2385
|
+
if (message.role !== "user" && message.role !== "assistant") return false;
|
|
2386
|
+
if (message.role === "user" && parseAskedAndAnsweredExchange(message?.text || "")) return false;
|
|
2387
|
+
const segments = splitSystemNoticeSegments(message?.text || "");
|
|
2388
|
+
if (segments.some((segment) => segment.kind === "system")) return false;
|
|
2389
|
+
return true;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
/**
|
|
2393
|
+
* Message-level view of the active chat for the rich (desktop-style) web
|
|
2394
|
+
* renderer. Returns an ordered array of blocks:
|
|
2395
|
+
*
|
|
2396
|
+
* { kind: "message", id, role, header, text, pendingPhase, attachments }
|
|
2397
|
+
* — plain user/assistant prose. `header` is describeChatMessageHeader()
|
|
2398
|
+
* output; `text` is the artifact-link-decorated markdown source the
|
|
2399
|
+
* renderer formats itself; `attachments` is { sessionId, attachments }
|
|
2400
|
+
* (the sentinel shape) or null.
|
|
2401
|
+
*
|
|
2402
|
+
* { kind: "lines", variant, lines }
|
|
2403
|
+
* — everything else (splash, thinking cards, system cards, dividers),
|
|
2404
|
+
* pre-rendered through the exact same line builders the terminal
|
|
2405
|
+
* transcript uses, so no edge case has two implementations.
|
|
2406
|
+
*
|
|
2407
|
+
* The TUI never calls this; selectChatLines remains the terminal path.
|
|
2408
|
+
*/
|
|
2409
|
+
export function selectChatBlocks(state, maxWidth = 80, options = {}) {
|
|
2410
|
+
const messages = selectActiveChat(state);
|
|
2411
|
+
if (!messages || messages.length === 0) return [];
|
|
2412
|
+
|
|
2413
|
+
const viewerKey = ownerKeyForOwner(state?.auth?.principal);
|
|
2414
|
+
const activeSessionId = state?.sessions?.activeSessionId;
|
|
2415
|
+
const activeSession = activeSessionId ? state?.sessions?.byId?.[activeSessionId] : null;
|
|
2416
|
+
const ownerKey = ownerKeyForOwner(activeSession?.owner);
|
|
2417
|
+
const sharedContext = Boolean(activeSession && (
|
|
2418
|
+
(activeSession.visibility && activeSession.visibility !== "private")
|
|
2419
|
+
|| (ownerKey && viewerKey && ownerKey !== viewerKey)
|
|
2420
|
+
));
|
|
2421
|
+
const buildOptions = {
|
|
2422
|
+
...(options?.tableMode ? { tableMode: options.tableMode } : {}),
|
|
2423
|
+
...(viewerKey ? { viewerKey } : {}),
|
|
2424
|
+
...(ownerKey ? { ownerKey } : {}),
|
|
2425
|
+
...(sharedContext ? { sharedContext: true } : {}),
|
|
2426
|
+
};
|
|
2427
|
+
|
|
2428
|
+
const blocks = [];
|
|
2429
|
+
for (const message of messages) {
|
|
2430
|
+
if (message?.kind === "epoch-divider") {
|
|
2431
|
+
blocks.push({ kind: "lines", variant: "divider", lines: [buildEpochDividerLine(message, maxWidth)] });
|
|
2432
|
+
continue;
|
|
2433
|
+
}
|
|
2434
|
+
if (message?.kind === "regen-refused") {
|
|
2435
|
+
blocks.push({ kind: "lines", variant: "divider", lines: [buildRegenRefusedLine(message, maxWidth)] });
|
|
2436
|
+
continue;
|
|
2437
|
+
}
|
|
2438
|
+
if (isRichRenderableChatMessage(message)) {
|
|
2439
|
+
const attachmentLines = buildAttachmentChipLines(message, { tableMode: "sentinel" });
|
|
2440
|
+
const attachmentSentinel = attachmentLines.find((line) => line?.kind === "imageAttachments") || null;
|
|
2441
|
+
blocks.push({
|
|
2442
|
+
kind: "message",
|
|
2443
|
+
id: message?.id ?? null,
|
|
2444
|
+
role: message.role,
|
|
2445
|
+
header: describeChatMessageHeader(message, buildOptions),
|
|
2446
|
+
text: decorateArtifactLinksForChat(message?.text || ""),
|
|
2447
|
+
pendingPhase: message?.pendingPhase || null,
|
|
2448
|
+
attachments: attachmentSentinel
|
|
2449
|
+
? { sessionId: attachmentSentinel.sessionId, attachments: attachmentSentinel.attachments }
|
|
2450
|
+
: null,
|
|
2451
|
+
});
|
|
2452
|
+
continue;
|
|
2453
|
+
}
|
|
2454
|
+
const lines = buildChatMessageLines(message, maxWidth, buildOptions);
|
|
2455
|
+
if (lines.length > 0) {
|
|
2456
|
+
blocks.push({ kind: "lines", variant: "event", lines });
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
return blocks;
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2322
2462
|
// A centered inline rule ("──── label ────") for transcript markers. The dash
|
|
2323
2463
|
// runs are CAPPED (not stretched to maxWidth): the web portal wraps by pixel
|
|
2324
2464
|
// width, and a maxWidth-long "─" run overflows a narrower pane and wraps the
|
package/ui/core/src/state.js
CHANGED
|
@@ -129,7 +129,7 @@ export function createInitialState({ mode = "local", branding = null, themeId =
|
|
|
129
129
|
inspectorTab: INSPECTOR_TABS[0],
|
|
130
130
|
sequenceExpandedTurns: [],
|
|
131
131
|
sequenceSelectedTurn: null,
|
|
132
|
-
chatViewMode: chatViewMode === "summary"
|
|
132
|
+
chatViewMode: (chatViewMode === "summary" || chatViewMode === "rich") ? chatViewMode : "transcript",
|
|
133
133
|
statsViewMode: "session",
|
|
134
134
|
prompt: "",
|
|
135
135
|
promptCursor: 0,
|