pilotswarm 0.5.1 → 0.5.3
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/mcp/dist/src/session-id.d.ts +13 -0
- package/mcp/dist/src/session-id.d.ts.map +1 -0
- package/mcp/dist/src/session-id.js +15 -0
- package/mcp/dist/src/session-id.js.map +1 -0
- package/mcp/dist/src/tools/artifacts.d.ts.map +1 -1
- package/mcp/dist/src/tools/artifacts.js +5 -4
- package/mcp/dist/src/tools/artifacts.js.map +1 -1
- package/mcp/dist/src/tools/observability.d.ts.map +1 -1
- package/mcp/dist/src/tools/observability.js +2 -1
- package/mcp/dist/src/tools/observability.js.map +1 -1
- package/mcp/dist/src/tools/sessions.d.ts.map +1 -1
- package/mcp/dist/src/tools/sessions.js +9 -8
- package/mcp/dist/src/tools/sessions.js.map +1 -1
- package/mcp/dist/src/tools/turn-control.d.ts.map +1 -1
- package/mcp/dist/src/tools/turn-control.js +5 -4
- package/mcp/dist/src/tools/turn-control.js.map +1 -1
- package/package.json +3 -3
- package/tui/plugins/.mcp.json +1 -7
- package/tui/src/app.js +12 -1
- package/tui/src/node-sdk-transport.js +17 -0
- package/ui/core/src/controller.js +62 -11
- package/ui/core/src/history.js +34 -0
- package/ui/core/src/reducer.js +87 -0
- package/ui/core/src/selectors.js +281 -174
- package/ui/core/src/state.js +15 -0
- package/ui/react/src/components.js +27 -8
- package/ui/react/src/web-app.js +83 -38
- package/web/dist/assets/index-BKai0rVp.js +24 -0
- package/web/dist/assets/index-BRbv6TsU.css +1 -0
- package/web/dist/assets/pilotswarm-PUVWBejV.js +90 -0
- package/web/dist/assets/{react-CRYBfNR4.js → react-BouHbnso.js} +1 -1
- package/web/dist/index.html +4 -4
- package/web/runtime.js +23 -2
- package/web/dist/assets/index-BmqU2WsR.js +0 -24
- package/web/dist/assets/index-D2-7fi56.css +0 -1
- package/web/dist/assets/pilotswarm-cIaYAE9e.js +0 -90
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
selectChatPaneChrome,
|
|
11
11
|
selectLiveActivityLines,
|
|
12
12
|
selectChatLines,
|
|
13
|
+
selectOutboxOverlayLines,
|
|
13
14
|
selectActivityPane,
|
|
14
15
|
selectArtifactUploadModal,
|
|
15
16
|
selectArtifactPickerModal,
|
|
@@ -317,6 +318,7 @@ const ChatPane = React.memo(function ChatPane({ controller, width, height, frame
|
|
|
317
318
|
activeSessionId,
|
|
318
319
|
activeSession: activeSessionId ? state.sessions.byId[activeSessionId] || null : null,
|
|
319
320
|
activeHistory: activeSessionId ? state.history.bySessionId.get(activeSessionId) || null : null,
|
|
321
|
+
activeOutbox: activeSessionId ? state.outbox?.bySessionId?.[activeSessionId] || null : null,
|
|
320
322
|
branding: state.branding,
|
|
321
323
|
connectionError: state.connection.error,
|
|
322
324
|
connectionMode: state.connection.mode,
|
|
@@ -347,11 +349,21 @@ const ChatPane = React.memo(function ChatPane({ controller, width, height, frame
|
|
|
347
349
|
history: {
|
|
348
350
|
bySessionId: historyMap,
|
|
349
351
|
},
|
|
352
|
+
// The queued-prompt overlay reads state.outbox; without it here the
|
|
353
|
+
// TUI ChatPane's synthetic selectorState always saw an empty outbox
|
|
354
|
+
// and rendered no "queued prompts: N" even though the item was
|
|
355
|
+
// durably queued in the real store.
|
|
356
|
+
outbox: {
|
|
357
|
+
bySessionId: chatView.activeSessionId && chatView.activeOutbox
|
|
358
|
+
? { [chatView.activeSessionId]: chatView.activeOutbox }
|
|
359
|
+
: {},
|
|
360
|
+
},
|
|
350
361
|
};
|
|
351
362
|
}, [
|
|
352
363
|
chatView.activeHistory,
|
|
353
364
|
chatView.activeSessionId,
|
|
354
365
|
chatView.activeSession,
|
|
366
|
+
chatView.activeOutbox,
|
|
355
367
|
chatView.branding,
|
|
356
368
|
chatView.chatViewMode,
|
|
357
369
|
chatView.connectionError,
|
|
@@ -383,13 +395,19 @@ const ChatPane = React.memo(function ChatPane({ controller, width, height, frame
|
|
|
383
395
|
() => (startupError ? [] : selectLiveActivityLines(selectorState, { spinnerFrame, maxWidth: contentWidth })),
|
|
384
396
|
[selectorState, startupError, spinnerFrame, contentWidth],
|
|
385
397
|
);
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
398
|
+
const outboxLines = React.useMemo(
|
|
399
|
+
() => (startupError ? [] : selectOutboxOverlayLines(selectorState, contentWidth)),
|
|
400
|
+
[selectorState, startupError, contentWidth],
|
|
401
|
+
);
|
|
402
|
+
// The queued-prompt overlay and the live "Working" strip are pinned in the
|
|
403
|
+
// bottom-sticky region (outbox above, strip at the very bottom), so they
|
|
404
|
+
// stay put at the foot of the pane instead of scrolling inline with the
|
|
405
|
+
// transcript — matching the portal. The transcript itself stays crisp.
|
|
406
|
+
const bottomStickyLines = React.useMemo(
|
|
407
|
+
() => (outboxLines.length > 0 || liveActivityLines.length > 0
|
|
408
|
+
? [...outboxLines, ...liveActivityLines]
|
|
409
|
+
: []),
|
|
410
|
+
[outboxLines, liveActivityLines],
|
|
393
411
|
);
|
|
394
412
|
|
|
395
413
|
return React.createElement(platform.Panel, {
|
|
@@ -399,7 +417,8 @@ const ChatPane = React.memo(function ChatPane({ controller, width, height, frame
|
|
|
399
417
|
focused: chatView.focused,
|
|
400
418
|
width,
|
|
401
419
|
height,
|
|
402
|
-
lines:
|
|
420
|
+
lines: elements,
|
|
421
|
+
bottomStickyLines,
|
|
403
422
|
scrollOffset: chatView.chatScroll,
|
|
404
423
|
scrollMode: "bottom",
|
|
405
424
|
paneId: "chat",
|
package/ui/react/src/web-app.js
CHANGED
|
@@ -431,13 +431,19 @@ function compactTitleRuns(title, maxWidth = 40) {
|
|
|
431
431
|
return [{ text: text.length > maxWidth ? `${text.slice(0, maxWidth - 1)}…` : text, color: "white", bold: true }];
|
|
432
432
|
}
|
|
433
433
|
const compactRuns = [];
|
|
434
|
+
// Gray runs are dropped on tight widths as decoration (ids, badges) —
|
|
435
|
+
// EXCEPT pure separators (" · "). Dropping those fuses adjacent values:
|
|
436
|
+
// "running · gpt-5.4 · ctx 33%" became "runninggpt-5.4ctx…".
|
|
437
|
+
const isSeparator = (run) => /^[\s·—-]+$/.test(String(run?.text || ""));
|
|
434
438
|
let remaining = Math.max(8, maxWidth);
|
|
435
439
|
for (const run of title) {
|
|
436
440
|
if (remaining <= 0) break;
|
|
437
441
|
const color = run?.color;
|
|
438
|
-
if (color === "gray" && compactRuns.length > 0) continue;
|
|
442
|
+
if (color === "gray" && compactRuns.length > 0 && !isSeparator(run)) continue;
|
|
439
443
|
const text = String(run?.text || "");
|
|
440
444
|
if (!text) continue;
|
|
445
|
+
// Don't start a separator we can't follow with content.
|
|
446
|
+
if (isSeparator(run) && remaining < text.length + 2) break;
|
|
441
447
|
const chunk = text.length > remaining && remaining > 1
|
|
442
448
|
? `${text.slice(0, remaining - 1)}…`
|
|
443
449
|
: text.slice(0, remaining);
|
|
@@ -445,6 +451,10 @@ function compactTitleRuns(title, maxWidth = 40) {
|
|
|
445
451
|
compactRuns.push({ ...run, text: chunk });
|
|
446
452
|
remaining -= chunk.length;
|
|
447
453
|
}
|
|
454
|
+
// Never end on a dangling separator.
|
|
455
|
+
while (compactRuns.length > 0 && isSeparator(compactRuns[compactRuns.length - 1])) {
|
|
456
|
+
compactRuns.pop();
|
|
457
|
+
}
|
|
448
458
|
return compactRuns.length > 0 ? compactRuns : title;
|
|
449
459
|
}
|
|
450
460
|
|
|
@@ -1715,6 +1725,7 @@ function PortalSequenceLines({ lines, theme, completionByTurn }) {
|
|
|
1715
1725
|
);
|
|
1716
1726
|
}
|
|
1717
1727
|
|
|
1728
|
+
|
|
1718
1729
|
function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, lines, stickyLines = [], bottomStickyLines = [], scrollOffset = 0, scrollMode = "top", paneKey, controller, className = "", panelClassName = "", topContent = null, bottomContent = null, structuredBlocks = false, stickyBottom = false, renderBody = null }) {
|
|
1719
1730
|
const themeId = useControllerSelector(controller, (state) => state.ui.themeId);
|
|
1720
1731
|
const theme = getTheme(themeId);
|
|
@@ -1736,11 +1747,26 @@ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, l
|
|
|
1736
1747
|
});
|
|
1737
1748
|
}, []);
|
|
1738
1749
|
|
|
1750
|
+
// Edge fades follow real overflow: fade the TOP only when content is
|
|
1751
|
+
// clipped above (scrolled down) and the BOTTOM only when clipped below
|
|
1752
|
+
// (scrolled up). With content that fits, neither fades — the first and
|
|
1753
|
+
// last lines stay crisp.
|
|
1754
|
+
const [scrollShadow, setScrollShadow] = React.useState({ up: false, down: false });
|
|
1755
|
+
const updateScrollShadow = React.useCallback((el) => {
|
|
1756
|
+
if (!el) return;
|
|
1757
|
+
const up = el.scrollHeight - el.scrollTop - el.clientHeight > 4;
|
|
1758
|
+
const down = el.scrollTop > 4;
|
|
1759
|
+
setScrollShadow((cur) => (cur.up === up && cur.down === down ? cur : { up, down }));
|
|
1760
|
+
}, []);
|
|
1739
1761
|
const handleBodyScroll = React.useCallback((event) => {
|
|
1740
1762
|
onScroll();
|
|
1763
|
+
updateScrollShadow(event.currentTarget);
|
|
1741
1764
|
if (!preserveHorizontalScroll || syncingHorizontalRef.current) return;
|
|
1742
1765
|
syncScrollLeft(event.currentTarget, stickyRef.current);
|
|
1743
|
-
}, [onScroll, preserveHorizontalScroll, syncScrollLeft]);
|
|
1766
|
+
}, [onScroll, preserveHorizontalScroll, syncScrollLeft, updateScrollShadow]);
|
|
1767
|
+
React.useEffect(() => {
|
|
1768
|
+
updateScrollShadow(ref.current);
|
|
1769
|
+
});
|
|
1744
1770
|
|
|
1745
1771
|
const handleStickyScroll = React.useCallback((event) => {
|
|
1746
1772
|
if (!preserveHorizontalScroll || syncingHorizontalRef.current) return;
|
|
@@ -1758,7 +1784,7 @@ function ScrollLinesPanel({ title, titleRight = null, color, focused, actions, l
|
|
|
1758
1784
|
normalizedSticky.map((line, index) => React.createElement(Line, { key: `sticky:${index}`, line, theme })),
|
|
1759
1785
|
)
|
|
1760
1786
|
: null,
|
|
1761
|
-
React.createElement("div", { ref, className: `ps-scroll-panel ${className}`.trim(), onScroll: handleBodyScroll, onWheel, onTouchStart, onTouchMove, onTouchEnd, onTouchCancel: onTouchEnd },
|
|
1787
|
+
React.createElement("div", { ref, className: `ps-scroll-panel ${className}${scrollShadow.down ? " is-scrolled-down" : ""}${scrollShadow.up ? " is-scrolled-up" : ""}`.trim(), onScroll: handleBodyScroll, onWheel, onTouchStart, onTouchMove, onTouchEnd, onTouchCancel: onTouchEnd },
|
|
1762
1788
|
typeof renderBody === "function"
|
|
1763
1789
|
? renderBody(normalizedLines, theme)
|
|
1764
1790
|
: structuredBlocks
|
|
@@ -1783,25 +1809,27 @@ function SessionRowContent({ row, theme, structured = false }) {
|
|
|
1783
1809
|
: row.text;
|
|
1784
1810
|
}
|
|
1785
1811
|
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1812
|
+
// Dense row: the title takes one line (clamped by CSS) and the context %
|
|
1813
|
+
// is pinned to the right. The full id · time · model · ctx detail unfolds
|
|
1814
|
+
// only under the selected row.
|
|
1815
|
+
const ctxRuns = Array.isArray(row.ctxRuns) ? row.ctxRuns : [];
|
|
1816
|
+
const detailRuns = Array.isArray(row.detailRuns)
|
|
1817
|
+
? row.detailRuns
|
|
1818
|
+
: (Array.isArray(row.selectedMetaRuns) ? row.selectedMetaRuns : []);
|
|
1819
|
+
const hasCtx = ctxRuns.length > 0;
|
|
1820
|
+
const hasDetail = row.active && detailRuns.length > 0;
|
|
1789
1821
|
|
|
1790
1822
|
return React.createElement(React.Fragment, null,
|
|
1791
|
-
React.createElement("div", { className: "ps-session-row-
|
|
1792
|
-
React.createElement(
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
? React.createElement("
|
|
1796
|
-
React.createElement(Runs, { runs:
|
|
1823
|
+
React.createElement("div", { className: "ps-session-row-line" },
|
|
1824
|
+
React.createElement("div", { className: "ps-session-row-title" },
|
|
1825
|
+
React.createElement(Runs, { runs: row.titleRuns, theme })),
|
|
1826
|
+
hasCtx
|
|
1827
|
+
? React.createElement("div", { className: "ps-session-row-ctx" },
|
|
1828
|
+
React.createElement(Runs, { runs: ctxRuns, theme }))
|
|
1797
1829
|
: null),
|
|
1798
|
-
|
|
1799
|
-
? React.createElement("div", { className: "ps-session-row-
|
|
1800
|
-
React.createElement(Runs, { runs:
|
|
1801
|
-
: null,
|
|
1802
|
-
hasSelectedMeta
|
|
1803
|
-
? React.createElement("div", { className: "ps-session-row-selected-meta" },
|
|
1804
|
-
React.createElement(Runs, { runs: row.selectedMetaRuns, theme }))
|
|
1830
|
+
hasDetail
|
|
1831
|
+
? React.createElement("div", { className: "ps-session-row-detail" },
|
|
1832
|
+
React.createElement(Runs, { runs: detailRuns, theme }))
|
|
1805
1833
|
: null);
|
|
1806
1834
|
}
|
|
1807
1835
|
|
|
@@ -2117,19 +2145,18 @@ function ChatPane({ controller, mobile = false, fullWidth = false, showComposer
|
|
|
2117
2145
|
() => selectLiveActivityLines(selectorState, { spinnerFrame, maxWidth: viewState.contentWidth }),
|
|
2118
2146
|
[selectorState, spinnerFrame, viewState.contentWidth],
|
|
2119
2147
|
);
|
|
2120
|
-
//
|
|
2121
|
-
//
|
|
2122
|
-
//
|
|
2123
|
-
const
|
|
2124
|
-
() => (liveActivityLines.length > 0
|
|
2125
|
-
? [...lines, [{ text: "", color: null }], ...liveActivityLines]
|
|
2126
|
-
: lines),
|
|
2127
|
-
[lines, liveActivityLines],
|
|
2128
|
-
);
|
|
2148
|
+
// The live-activity line is pinned in the bottom-sticky strip (with the
|
|
2149
|
+
// outbox overlay), NOT appended to the transcript — it must stay put while
|
|
2150
|
+
// chat content scrolls, and it drops the instant the turn ends.
|
|
2151
|
+
const pinnedActivityLines = liveActivityLines;
|
|
2129
2152
|
const outboxLines = React.useMemo(
|
|
2130
2153
|
() => selectOutboxOverlayLines(selectorState, viewState.contentWidth, { tableMode: "sentinel" }),
|
|
2131
2154
|
[selectorState, viewState.contentWidth],
|
|
2132
2155
|
);
|
|
2156
|
+
const stickyBottom = React.useMemo(
|
|
2157
|
+
() => (pinnedActivityLines.length > 0 ? [...outboxLines, ...pinnedActivityLines] : outboxLines),
|
|
2158
|
+
[outboxLines, pinnedActivityLines],
|
|
2159
|
+
);
|
|
2133
2160
|
const composer = showComposer && !viewState.activeSessionIsGroup && viewState.chatViewMode !== "summary"
|
|
2134
2161
|
? React.createElement("div", { className: "ps-chat-composer" },
|
|
2135
2162
|
React.createElement(PromptComposer, { controller, mobile, active: true }))
|
|
@@ -2144,8 +2171,8 @@ function ChatPane({ controller, mobile = false, fullWidth = false, showComposer
|
|
|
2144
2171
|
actions: null,
|
|
2145
2172
|
color: chrome.color,
|
|
2146
2173
|
focused: viewState.focused,
|
|
2147
|
-
lines
|
|
2148
|
-
bottomStickyLines:
|
|
2174
|
+
lines,
|
|
2175
|
+
bottomStickyLines: stickyBottom,
|
|
2149
2176
|
scrollOffset: viewState.scroll,
|
|
2150
2177
|
scrollMode: "bottom",
|
|
2151
2178
|
paneKey: "chat",
|
|
@@ -4105,11 +4132,27 @@ function AdminConsolePanel({ controller }) {
|
|
|
4105
4132
|
: null,
|
|
4106
4133
|
React.createElement("section", { className: "ps-admin-console__section" },
|
|
4107
4134
|
React.createElement("h3", null, "GitHub Copilot key"),
|
|
4108
|
-
|
|
4109
|
-
"
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4135
|
+
view.ghcpKey.storeAsSystem
|
|
4136
|
+
? React.createElement("p", { className: "ps-admin-console__hint" },
|
|
4137
|
+
"System-wide key for platform-managed (ownerless) system ",
|
|
4138
|
+
"sessions — agent tuners, repo cache managers, and other ",
|
|
4139
|
+
"sessions with no owner resolve this key for GitHub Copilot ",
|
|
4140
|
+
"models. Clearing it reverts them to the worker default.")
|
|
4141
|
+
: React.createElement("p", { className: "ps-admin-console__hint" },
|
|
4142
|
+
"Per-user override for the GitHub Copilot model provider token. ",
|
|
4143
|
+
"When set, this key is used instead of the worker's env-supplied ",
|
|
4144
|
+
"GITHUB_TOKEN for sessions you own. Clearing the key reverts to ",
|
|
4145
|
+
"the worker default."),
|
|
4146
|
+
view.isAdmin && view.systemGhcpKey.supported
|
|
4147
|
+
? React.createElement("label", { className: "ps-admin-console__system-toggle" },
|
|
4148
|
+
React.createElement("input", {
|
|
4149
|
+
type: "checkbox",
|
|
4150
|
+
checked: view.ghcpKey.storeAsSystem,
|
|
4151
|
+
disabled: view.ghcpKey.saving,
|
|
4152
|
+
onChange: (event) => controller.setAdminGhcpKeyStoreAsSystem(event.target.checked),
|
|
4153
|
+
}),
|
|
4154
|
+
"Store as System key")
|
|
4155
|
+
: null,
|
|
4113
4156
|
React.createElement("p", { className: `ps-admin-console__status${view.ghcpKey.error ? " is-error" : ""}` },
|
|
4114
4157
|
view.ghcpKey.error || view.ghcpKey.statusText),
|
|
4115
4158
|
view.ghcpKey.editing
|
|
@@ -4141,13 +4184,15 @@ function AdminConsolePanel({ controller }) {
|
|
|
4141
4184
|
type: "button",
|
|
4142
4185
|
className: "ps-primary-button",
|
|
4143
4186
|
onClick: onBeginEdit,
|
|
4144
|
-
}, view.ghcpKey.
|
|
4145
|
-
|
|
4187
|
+
}, view.ghcpKey.targetConfigured
|
|
4188
|
+
? (view.ghcpKey.storeAsSystem ? "Replace System key" : "Replace key")
|
|
4189
|
+
: (view.ghcpKey.storeAsSystem ? "Set System key" : "Set key")),
|
|
4190
|
+
view.ghcpKey.targetConfigured
|
|
4146
4191
|
? React.createElement("button", {
|
|
4147
4192
|
type: "button",
|
|
4148
4193
|
className: "ps-mini-button",
|
|
4149
4194
|
onClick: onClear,
|
|
4150
|
-
}, "Clear key")
|
|
4195
|
+
}, view.ghcpKey.storeAsSystem ? "Clear System key" : "Clear key")
|
|
4151
4196
|
: null,
|
|
4152
4197
|
React.createElement("button", {
|
|
4153
4198
|
type: "button",
|