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.
Files changed (36) hide show
  1. package/mcp/dist/src/session-id.d.ts +13 -0
  2. package/mcp/dist/src/session-id.d.ts.map +1 -0
  3. package/mcp/dist/src/session-id.js +15 -0
  4. package/mcp/dist/src/session-id.js.map +1 -0
  5. package/mcp/dist/src/tools/artifacts.d.ts.map +1 -1
  6. package/mcp/dist/src/tools/artifacts.js +5 -4
  7. package/mcp/dist/src/tools/artifacts.js.map +1 -1
  8. package/mcp/dist/src/tools/observability.d.ts.map +1 -1
  9. package/mcp/dist/src/tools/observability.js +2 -1
  10. package/mcp/dist/src/tools/observability.js.map +1 -1
  11. package/mcp/dist/src/tools/sessions.d.ts.map +1 -1
  12. package/mcp/dist/src/tools/sessions.js +9 -8
  13. package/mcp/dist/src/tools/sessions.js.map +1 -1
  14. package/mcp/dist/src/tools/turn-control.d.ts.map +1 -1
  15. package/mcp/dist/src/tools/turn-control.js +5 -4
  16. package/mcp/dist/src/tools/turn-control.js.map +1 -1
  17. package/package.json +3 -3
  18. package/tui/plugins/.mcp.json +1 -7
  19. package/tui/src/app.js +12 -1
  20. package/tui/src/node-sdk-transport.js +17 -0
  21. package/ui/core/src/controller.js +62 -11
  22. package/ui/core/src/history.js +34 -0
  23. package/ui/core/src/reducer.js +87 -0
  24. package/ui/core/src/selectors.js +281 -174
  25. package/ui/core/src/state.js +15 -0
  26. package/ui/react/src/components.js +27 -8
  27. package/ui/react/src/web-app.js +83 -38
  28. package/web/dist/assets/index-BKai0rVp.js +24 -0
  29. package/web/dist/assets/index-BRbv6TsU.css +1 -0
  30. package/web/dist/assets/pilotswarm-PUVWBejV.js +90 -0
  31. package/web/dist/assets/{react-CRYBfNR4.js → react-BouHbnso.js} +1 -1
  32. package/web/dist/index.html +4 -4
  33. package/web/runtime.js +23 -2
  34. package/web/dist/assets/index-BmqU2WsR.js +0 -24
  35. package/web/dist/assets/index-D2-7fi56.css +0 -1
  36. package/web/dist/assets/pilotswarm-cIaYAE9e.js +0 -90
@@ -2160,6 +2160,7 @@ export class PilotSwarmUiController {
2160
2160
  try {
2161
2161
  const profile = await this.transport.getCurrentUserProfile();
2162
2162
  this.dispatch({ type: "admin/profile/loaded", profile });
2163
+ await this.refreshSystemGhcpKeyStatus(profile);
2163
2164
  return profile || null;
2164
2165
  } catch (error) {
2165
2166
  this.dispatch({ type: "admin/profile/loadFailed", error: error?.message || String(error) });
@@ -2167,6 +2168,35 @@ export class PilotSwarmUiController {
2167
2168
  }
2168
2169
  }
2169
2170
 
2171
+ /**
2172
+ * Load the SYSTEM user's Copilot key status (admins only). Quietly a
2173
+ * no-op when the caller is not admin or the transport predates the
2174
+ * feature, so the Admin Console degrades to the per-user-only view.
2175
+ */
2176
+ async refreshSystemGhcpKeyStatus(profile = null) {
2177
+ const effective = profile || this.getState().admin?.profile || null;
2178
+ if (!effective?.isAdmin) return null;
2179
+ if (typeof this.transport.getSystemGitHubCopilotKeyStatus !== "function") return null;
2180
+ this.dispatch({ type: "admin/systemGhcpKey/loading" });
2181
+ try {
2182
+ const status = await this.transport.getSystemGitHubCopilotKeyStatus();
2183
+ this.dispatch({ type: "admin/systemGhcpKey/loaded", status });
2184
+ return status || null;
2185
+ } catch (error) {
2186
+ this.dispatch({ type: "admin/systemGhcpKey/loadFailed", error: error?.message || String(error) });
2187
+ return null;
2188
+ }
2189
+ }
2190
+
2191
+ /**
2192
+ * Admin-only: switch the key editor between "your key" and
2193
+ * "System key" (the key ownerless system sessions run on).
2194
+ */
2195
+ setAdminGhcpKeyStoreAsSystem(value) {
2196
+ if (value && !this.getState().admin?.profile?.isAdmin) return;
2197
+ this.dispatch({ type: "admin/ghcpKey/setSystemTarget", value: Boolean(value) });
2198
+ }
2199
+
2170
2200
  beginAdminEditGhcpKey() {
2171
2201
  this.dispatch({ type: "admin/ghcpKey/beginEdit", draft: "" });
2172
2202
  }
@@ -2218,8 +2248,12 @@ export class PilotSwarmUiController {
2218
2248
  * use `clearAdminGhcpKey()` for that.
2219
2249
  */
2220
2250
  async saveAdminGhcpKey() {
2221
- if (typeof this.transport.setCurrentUserGitHubCopilotKey !== "function") {
2222
- this.dispatch({ type: "admin/ghcpKey/saveFailed", error: "Admin Console is not available on this transport." });
2251
+ const storeAsSystem = Boolean(this.getState().admin.ghcpKey.storeAsSystem);
2252
+ const setter = storeAsSystem ? "setSystemGitHubCopilotKey" : "setCurrentUserGitHubCopilotKey";
2253
+ if (typeof this.transport[setter] !== "function") {
2254
+ this.dispatch({ type: "admin/ghcpKey/saveFailed", error: storeAsSystem
2255
+ ? "This deployment does not support System keys yet."
2256
+ : "Admin Console is not available on this transport." });
2223
2257
  return;
2224
2258
  }
2225
2259
  const draft = String(this.getState().admin.ghcpKey.draft || "").trim();
@@ -2229,8 +2263,13 @@ export class PilotSwarmUiController {
2229
2263
  }
2230
2264
  this.dispatch({ type: "admin/ghcpKey/saving" });
2231
2265
  try {
2232
- const profile = await this.transport.setCurrentUserGitHubCopilotKey({ key: draft });
2233
- this.dispatch({ type: "admin/ghcpKey/saved", profile });
2266
+ if (storeAsSystem) {
2267
+ const status = await this.transport.setSystemGitHubCopilotKey({ key: draft });
2268
+ this.dispatch({ type: "admin/systemGhcpKey/saved", status });
2269
+ } else {
2270
+ const profile = await this.transport.setCurrentUserGitHubCopilotKey({ key: draft });
2271
+ this.dispatch({ type: "admin/ghcpKey/saved", profile });
2272
+ }
2234
2273
  } catch (error) {
2235
2274
  this.dispatch({ type: "admin/ghcpKey/saveFailed", error: error?.message || String(error) });
2236
2275
  }
@@ -2241,14 +2280,23 @@ export class PilotSwarmUiController {
2241
2280
  * worker's env-supplied default token.
2242
2281
  */
2243
2282
  async clearAdminGhcpKey() {
2244
- if (typeof this.transport.setCurrentUserGitHubCopilotKey !== "function") {
2245
- this.dispatch({ type: "admin/ghcpKey/saveFailed", error: "Admin Console is not available on this transport." });
2283
+ const storeAsSystem = Boolean(this.getState().admin.ghcpKey.storeAsSystem);
2284
+ const setter = storeAsSystem ? "setSystemGitHubCopilotKey" : "setCurrentUserGitHubCopilotKey";
2285
+ if (typeof this.transport[setter] !== "function") {
2286
+ this.dispatch({ type: "admin/ghcpKey/saveFailed", error: storeAsSystem
2287
+ ? "This deployment does not support System keys yet."
2288
+ : "Admin Console is not available on this transport." });
2246
2289
  return;
2247
2290
  }
2248
2291
  this.dispatch({ type: "admin/ghcpKey/saving" });
2249
2292
  try {
2250
- const profile = await this.transport.setCurrentUserGitHubCopilotKey({ key: null });
2251
- this.dispatch({ type: "admin/ghcpKey/saved", profile });
2293
+ if (storeAsSystem) {
2294
+ const status = await this.transport.setSystemGitHubCopilotKey({ key: null });
2295
+ this.dispatch({ type: "admin/systemGhcpKey/saved", status });
2296
+ } else {
2297
+ const profile = await this.transport.setCurrentUserGitHubCopilotKey({ key: null });
2298
+ this.dispatch({ type: "admin/ghcpKey/saved", profile });
2299
+ }
2252
2300
  } catch (error) {
2253
2301
  this.dispatch({ type: "admin/ghcpKey/saveFailed", error: error?.message || String(error) });
2254
2302
  }
@@ -5172,11 +5220,14 @@ export class PilotSwarmUiController {
5172
5220
 
5173
5221
  const contentWidth = Math.max(20, layout.leftWidth - 4);
5174
5222
  const contentHeight = Math.max(1, layout.chatPaneHeight - 2);
5175
- const lines = [
5176
- ...selectChatLines(state, contentWidth),
5223
+ const lines = selectChatLines(state, contentWidth);
5224
+ // The live "Working" strip and the queued-prompt overlay are both
5225
+ // pinned in the bottom-sticky region (matching the ChatPane render),
5226
+ // so the transcript scroll math must reserve them here — not inline.
5227
+ const bottomStickyLines = [
5228
+ ...selectOutboxOverlayLines(state, contentWidth),
5177
5229
  ...selectLiveActivityLines(state),
5178
5230
  ];
5179
- const bottomStickyLines = selectOutboxOverlayLines(state, contentWidth);
5180
5231
  const bottomStickyHeight = Math.min(
5181
5232
  Math.max(0, Math.floor(contentHeight * 0.34)),
5182
5233
  countWrappedRenderableLines(bottomStickyLines, contentWidth),
@@ -360,12 +360,16 @@ function buildChatMessage(event, role) {
360
360
 
361
361
  const text = extractVisibleChatText(rawText, role);
362
362
  if (!hasVisibleMessageText(text)) return null;
363
+ const clientMessageIds = Array.isArray(event?.data?.clientMessageIds)
364
+ ? event.data.clientMessageIds.filter((id) => typeof id === "string" && id)
365
+ : [];
363
366
  return {
364
367
  id: `${event.sessionId}:${event.seq}`,
365
368
  role: deriveChatRole(event, role, text),
366
369
  text,
367
370
  time: formatTimestamp(event.createdAt),
368
371
  createdAt: event.createdAt instanceof Date ? event.createdAt.getTime() : new Date(event.createdAt).getTime(),
372
+ ...(clientMessageIds.length > 0 ? { clientMessageIds } : {}),
369
373
  };
370
374
  }
371
375
 
@@ -873,13 +877,43 @@ export function appendEventToHistory(history, event) {
873
877
  loadedEventLimit,
874
878
  loadedEventCount: Math.max(Number(history?.loadedEventCount || 0), nextEvents.length),
875
879
  hasOlderEvents: Boolean(history?.hasOlderEvents),
880
+ // clientMessageIds whose turn was user-stopped mid-flight. Carried
881
+ // across appends so a prompt stays flagged even as the transcript
882
+ // clamps/reloads.
883
+ stoppedMessageIds: Array.isArray(history?.stoppedMessageIds) ? history.stoppedMessageIds : [],
876
884
  };
877
885
 
886
+ // A stopped turn leaves a durable session.turn_stopped carrying the
887
+ // interrupted prompt's clientMessageIds. Record them and retroactively
888
+ // flag any matching transcript message (the user.message usually lands
889
+ // first, at turn start; the stop lands at turn end).
890
+ if (event.eventType === "session.turn_stopped") {
891
+ const ids = Array.isArray(event?.data?.clientMessageIds)
892
+ ? event.data.clientMessageIds.filter((id) => typeof id === "string" && id)
893
+ : [];
894
+ if (ids.length > 0) {
895
+ const merged = new Set([...next.stoppedMessageIds, ...ids]);
896
+ next.stoppedMessageIds = Array.from(merged);
897
+ next.chat = next.chat.map((m) => (
898
+ Array.isArray(m.clientMessageIds) && m.clientMessageIds.some((id) => merged.has(id))
899
+ ? { ...m, stopped: true }
900
+ : m
901
+ ));
902
+ }
903
+ return next;
904
+ }
905
+
878
906
  if (event.eventType === "user.message") {
879
907
  next.activity.push(...buildEmbeddedSystemNoticeActivityItems(event, "user"));
880
908
  next.activity = clampHistoryItems(next.activity, loadedEventLimit);
881
909
  const message = buildChatMessage(event, "user");
882
910
  if (!message) return next;
911
+ // Prospective flag: covers a bulk load where the stop event arrived
912
+ // before this message in the reduce order.
913
+ if (Array.isArray(message.clientMessageIds)
914
+ && message.clientMessageIds.some((id) => next.stoppedMessageIds.includes(id))) {
915
+ message.stopped = true;
916
+ }
883
917
  next.chat = reconcileOptimisticMessage(next.chat, message);
884
918
  next.chat.push(message);
885
919
  next.chat = clampHistoryItems(dedupeChatMessages(next.chat), loadedEventLimit);
@@ -1488,6 +1488,7 @@ export function appReducer(state, action) {
1488
1488
  saving: false,
1489
1489
  error: null,
1490
1490
  lastSavedAt: state.admin.ghcpKey.lastSavedAt,
1491
+ storeAsSystem: Boolean(state.admin.ghcpKey.storeAsSystem),
1491
1492
  },
1492
1493
  },
1493
1494
  };
@@ -1524,6 +1525,7 @@ export function appReducer(state, action) {
1524
1525
  saving: false,
1525
1526
  error: null,
1526
1527
  lastSavedAt: state.admin.ghcpKey.lastSavedAt,
1528
+ storeAsSystem: Boolean(state.admin.ghcpKey.storeAsSystem),
1527
1529
  },
1528
1530
  },
1529
1531
  };
@@ -1567,6 +1569,91 @@ export function appReducer(state, action) {
1567
1569
  saving: false,
1568
1570
  error: null,
1569
1571
  lastSavedAt: Date.now(),
1572
+ storeAsSystem: Boolean(state.admin.ghcpKey.storeAsSystem),
1573
+ },
1574
+ },
1575
+ };
1576
+ }
1577
+ case "admin/ghcpKey/setSystemTarget": {
1578
+ const storeAsSystem = Boolean(action.value);
1579
+ if (Boolean(state.admin.ghcpKey.storeAsSystem) === storeAsSystem) return state;
1580
+ return {
1581
+ ...state,
1582
+ admin: {
1583
+ ...state.admin,
1584
+ ghcpKey: {
1585
+ ...state.admin.ghcpKey,
1586
+ storeAsSystem,
1587
+ error: null,
1588
+ },
1589
+ },
1590
+ };
1591
+ }
1592
+ case "admin/systemGhcpKey/loading": {
1593
+ return {
1594
+ ...state,
1595
+ admin: {
1596
+ ...state.admin,
1597
+ systemGhcpKey: {
1598
+ ...state.admin.systemGhcpKey,
1599
+ loading: true,
1600
+ error: null,
1601
+ },
1602
+ },
1603
+ };
1604
+ }
1605
+ case "admin/systemGhcpKey/loaded": {
1606
+ const status = action.status || {};
1607
+ return {
1608
+ ...state,
1609
+ admin: {
1610
+ ...state.admin,
1611
+ systemGhcpKey: {
1612
+ supported: true,
1613
+ loading: false,
1614
+ configured: Boolean(status.configured),
1615
+ changedBy: status.changedBy || null,
1616
+ changedAt: status.changedAt || null,
1617
+ error: null,
1618
+ },
1619
+ },
1620
+ };
1621
+ }
1622
+ case "admin/systemGhcpKey/loadFailed": {
1623
+ return {
1624
+ ...state,
1625
+ admin: {
1626
+ ...state.admin,
1627
+ systemGhcpKey: {
1628
+ ...state.admin.systemGhcpKey,
1629
+ loading: false,
1630
+ error: action.error ? String(action.error) : "Failed to load System key status",
1631
+ },
1632
+ },
1633
+ };
1634
+ }
1635
+ case "admin/systemGhcpKey/saved": {
1636
+ const status = action.status || {};
1637
+ return {
1638
+ ...state,
1639
+ admin: {
1640
+ ...state.admin,
1641
+ ghcpKey: {
1642
+ editing: false,
1643
+ draft: "",
1644
+ cursorIndex: 0,
1645
+ saving: false,
1646
+ error: null,
1647
+ lastSavedAt: Date.now(),
1648
+ storeAsSystem: Boolean(state.admin.ghcpKey.storeAsSystem),
1649
+ },
1650
+ systemGhcpKey: {
1651
+ supported: true,
1652
+ loading: false,
1653
+ configured: Boolean(status.configured),
1654
+ changedBy: status.changedBy || null,
1655
+ changedAt: status.changedAt || null,
1656
+ error: null,
1570
1657
  },
1571
1658
  },
1572
1659
  };