pilotswarm 0.5.7 → 0.5.9
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 +41 -2
- package/ui/core/src/selectors.js +11 -0
- package/ui/react/src/web-app.js +13 -1
- package/web/dist/assets/{index-oq-VaEBa.js → index-CaPYGyHb.js} +2 -2
- package/web/dist/assets/{index-BRbv6TsU.css → index-oldX95Tp.css} +1 -1
- package/web/dist/assets/{pilotswarm-LkrSYzZ1.js → pilotswarm-DRs6o-lA.js} +50 -50
- package/web/dist/assets/{react-BoMupMMQ.js → react-C9iQPS2h.js} +1 -1
- package/web/dist/index.html +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pilotswarm",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9",
|
|
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",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"hono": "^4.12.10",
|
|
78
78
|
"ink": "^6.8.0",
|
|
79
79
|
"jose": "^6.2.2",
|
|
80
|
-
"pilotswarm-sdk": "^0.5.
|
|
80
|
+
"pilotswarm-sdk": "^0.5.9",
|
|
81
81
|
"react": "^19.2.4",
|
|
82
82
|
"react-dom": "^19.2.4",
|
|
83
83
|
"ws": "^8.18.2"
|
|
@@ -467,6 +467,12 @@ function ownerDisplayName(owner, fallback = "unknown user") {
|
|
|
467
467
|
return String(owner?.displayName || owner?.email || "").trim() || fallback;
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
+
// Owner key of the first-class System user. System-agent children inherit this
|
|
471
|
+
// owner; they belong to the static "System" filter entry, so we never mint a
|
|
472
|
+
// second, duplicate "System" owner bucket for them. Computed via the join
|
|
473
|
+
// helper so it stays byte-identical to real owner keys.
|
|
474
|
+
const SYSTEM_OWNER_KEY = ownerKeyForPrincipal({ provider: "system", subject: "system" });
|
|
475
|
+
|
|
470
476
|
function buildSessionOwnerFilterItems(state) {
|
|
471
477
|
const principal = state.auth?.principal || null;
|
|
472
478
|
const principalKey = ownerKeyForPrincipal(principal);
|
|
@@ -506,7 +512,9 @@ function buildSessionOwnerFilterItems(state) {
|
|
|
506
512
|
for (const session of Object.values(state.sessions?.byId || {})) {
|
|
507
513
|
const owner = session?.owner;
|
|
508
514
|
const key = ownerKeyForPrincipal(owner);
|
|
509
|
-
|
|
515
|
+
// Skip the current user (covered by "Me") and the System user (covered
|
|
516
|
+
// by the static "System" entry — never a duplicate owner bucket).
|
|
517
|
+
if (!key || key === principalKey || key === SYSTEM_OWNER_KEY || ownersByKey.has(key)) continue;
|
|
510
518
|
ownersByKey.set(key, owner);
|
|
511
519
|
}
|
|
512
520
|
|
|
@@ -527,7 +535,7 @@ function buildSessionOwnerFilterItems(state) {
|
|
|
527
535
|
return items;
|
|
528
536
|
}
|
|
529
537
|
|
|
530
|
-
function defaultOwnerFilterForPrincipal(principal) {
|
|
538
|
+
export function defaultOwnerFilterForPrincipal(principal) {
|
|
531
539
|
return ownerKeyForPrincipal(principal)
|
|
532
540
|
? {
|
|
533
541
|
all: false,
|
|
@@ -1574,6 +1582,36 @@ export class PilotSwarmUiController {
|
|
|
1574
1582
|
});
|
|
1575
1583
|
}
|
|
1576
1584
|
|
|
1585
|
+
// Keep the open Session Filter modal's entry list in sync with the live
|
|
1586
|
+
// session set. The item list is snapshotted into modal.items at open time
|
|
1587
|
+
// (it must be — the shared keyboard-nav handler indexes into it), but the
|
|
1588
|
+
// periodic catalog refresh mutates owners underneath it: a user whose first
|
|
1589
|
+
// session arrives after the modal opened would otherwise never see their
|
|
1590
|
+
// owner bucket without reopening. Rebuild in place, preserving the
|
|
1591
|
+
// highlighted row by its stable id, and only when the shape actually
|
|
1592
|
+
// changed (so we don't churn the modal on every 4s refresh).
|
|
1593
|
+
refreshOpenSessionOwnerFilterModal() {
|
|
1594
|
+
const state = this.getState();
|
|
1595
|
+
const modal = state.ui.modal;
|
|
1596
|
+
if (!modal || modal.type !== "sessionOwnerFilter") return;
|
|
1597
|
+
const nextItems = buildSessionOwnerFilterItems(state);
|
|
1598
|
+
const prevItems = Array.isArray(modal.items) ? modal.items : [];
|
|
1599
|
+
const sameShape = prevItems.length === nextItems.length
|
|
1600
|
+
&& prevItems.every((item, i) => item?.id === nextItems[i]?.id);
|
|
1601
|
+
if (sameShape) return;
|
|
1602
|
+
const prevSelectedId = prevItems[Math.max(0, Number(modal.selectedIndex) || 0)]?.id;
|
|
1603
|
+
const remappedIndex = prevSelectedId != null
|
|
1604
|
+
? nextItems.findIndex((item) => item.id === prevSelectedId)
|
|
1605
|
+
: -1;
|
|
1606
|
+
const nextSelectedIndex = remappedIndex >= 0
|
|
1607
|
+
? remappedIndex
|
|
1608
|
+
: Math.max(0, Math.min(Number(modal.selectedIndex) || 0, Math.max(0, nextItems.length - 1)));
|
|
1609
|
+
this.dispatch({
|
|
1610
|
+
type: "ui/modal",
|
|
1611
|
+
modal: { ...modal, items: nextItems, selectedIndex: nextSelectedIndex },
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1577
1615
|
setFilesFilter(patch = {}) {
|
|
1578
1616
|
this.dispatch({
|
|
1579
1617
|
type: "files/filter",
|
|
@@ -1743,6 +1781,7 @@ export class PilotSwarmUiController {
|
|
|
1743
1781
|
});
|
|
1744
1782
|
}
|
|
1745
1783
|
this.dispatch({ type: "sessions/loaded", sessions });
|
|
1784
|
+
this.refreshOpenSessionOwnerFilterModal();
|
|
1746
1785
|
const selected = this.getState().sessions.activeSessionId;
|
|
1747
1786
|
const syncedIds = new Set();
|
|
1748
1787
|
if (selected) {
|
package/ui/core/src/selectors.js
CHANGED
|
@@ -198,6 +198,13 @@ function ownerDisplayName(owner, fallback = "unknown user") {
|
|
|
198
198
|
return String(owner?.displayName || owner?.email || "").trim() || fallback;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
// The owner key of the first-class System user. Sessions a system agent spawns
|
|
202
|
+
// on a user's behalf inherit this owner (they are NOT is_system, so they stay
|
|
203
|
+
// deletable) — but they belong to the same "System" bucket as the real system
|
|
204
|
+
// agents, so the single System filter entry covers both. Computed via the same
|
|
205
|
+
// join helper so it stays byte-identical to real owner keys.
|
|
206
|
+
const SYSTEM_OWNER_KEY = ownerKeyForOwner({ provider: "system", subject: "system" });
|
|
207
|
+
|
|
201
208
|
function initialsFromText(value) {
|
|
202
209
|
const text = String(value || "").trim();
|
|
203
210
|
if (!text) return "";
|
|
@@ -335,6 +342,10 @@ function matchesOwnerFilterDirect(session, ownerFilter = {}, auth = {}, ownerOve
|
|
|
335
342
|
if (session?.isSystem) return ownerFilter.includeSystem === true;
|
|
336
343
|
const ownerKey = ownerKeyForOwner(ownerOverride !== undefined ? ownerOverride : session?.owner);
|
|
337
344
|
if (!ownerKey) return ownerFilter.includeUnowned === true;
|
|
345
|
+
// Sessions OWNED BY the System user (children a system agent spawned) share
|
|
346
|
+
// the "System" bucket with the real is_system agents — one entry, one
|
|
347
|
+
// meaning. Gate them on includeSystem, never on Me / owner keys.
|
|
348
|
+
if (ownerKey === SYSTEM_OWNER_KEY) return ownerFilter.includeSystem === true;
|
|
338
349
|
const currentUserKey = ownerKeyForOwner(auth?.principal);
|
|
339
350
|
if (ownerFilter.includeMe && currentUserKey && ownerKey === currentUserKey) return true;
|
|
340
351
|
return Array.isArray(ownerFilter.ownerKeys) && ownerFilter.ownerKeys.includes(ownerKey);
|
package/ui/react/src/web-app.js
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
selectStatusBar,
|
|
41
41
|
selectThemePickerModal,
|
|
42
42
|
selectConfirmModal,
|
|
43
|
+
defaultOwnerFilterForPrincipal,
|
|
43
44
|
normalizeStoredLayoutAdjustments,
|
|
44
45
|
normalizeStoredPinnedSessionIds,
|
|
45
46
|
} from "pilotswarm/ui-core";
|
|
@@ -204,7 +205,13 @@ function profileSettingsFromViewState(state) {
|
|
|
204
205
|
function buildDefaultProfileSettingsFromState(state) {
|
|
205
206
|
return normalizeProfileSettings({
|
|
206
207
|
themeId: state?.ui?.themeId,
|
|
207
|
-
|
|
208
|
+
// Derive the owner-filter default from the RESOLVED principal, never a
|
|
209
|
+
// snapshot of state.sessions.ownerFilter — at mount that snapshot is
|
|
210
|
+
// still {all:true} (principal not yet applied), and using it as the
|
|
211
|
+
// "no persisted filter" fallback would drift an authenticated user's
|
|
212
|
+
// saved filter to All (or, mid-resolve, hide their own sessions). This
|
|
213
|
+
// guarantees the fallback is always Me+System for a signed-in user.
|
|
214
|
+
sessionOwnerFilter: defaultOwnerFilterForPrincipal(state?.auth?.principal ?? null),
|
|
208
215
|
layoutAdjustments: state?.ui?.layout,
|
|
209
216
|
pinnedSessionIds: state?.sessions?.pinnedIds,
|
|
210
217
|
collapsedSessionIds: state?.sessions?.collapsedIds,
|
|
@@ -4333,6 +4340,11 @@ export function PilotSwarmWebApp({ controller }) {
|
|
|
4333
4340
|
try {
|
|
4334
4341
|
const profile = await transport.getCurrentUserProfile();
|
|
4335
4342
|
if (!active) return;
|
|
4343
|
+
// Recompute the fallback defaults from CURRENT state each poll:
|
|
4344
|
+
// the principal is resolved by now (it was likely null at mount),
|
|
4345
|
+
// so the "no persisted filter" fallback becomes the correct
|
|
4346
|
+
// principal-scoped default (Me+System) instead of {all:true}.
|
|
4347
|
+
defaultProfileSettingsRef.current = buildDefaultProfileSettingsFromState(controller.getState());
|
|
4336
4348
|
const settings = materializeProfileSettings(
|
|
4337
4349
|
profile?.profileSettings,
|
|
4338
4350
|
defaultProfileSettingsRef.current,
|