pilotswarm 0.5.46 → 0.5.48
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/README.md +1 -0
- package/mcp/dist/src/tools/providers.d.ts.map +1 -1
- package/mcp/dist/src/tools/providers.js +27 -3
- package/mcp/dist/src/tools/providers.js.map +1 -1
- package/package.json +2 -2
- package/tui/src/app.js +7 -11
- package/tui/src/node-sdk-transport.js +21 -0
- package/ui/core/src/commands.js +1 -0
- package/ui/core/src/controller.js +331 -68
- package/ui/core/src/history.js +5 -0
- package/ui/core/src/index.js +1 -0
- package/ui/core/src/reducer.js +93 -4
- package/ui/core/src/selectors.js +175 -15
- package/ui/core/src/state.js +31 -0
- package/ui/core/src/table-layout.js +155 -0
- package/ui/react/src/components.js +1 -0
- package/ui/react/src/web-app.js +1035 -323
- package/web/dist/assets/{abnfDiagram-VRR7QNED-GXM5pEfT.js → abnfDiagram-VRR7QNED-6rYy4Wj8.js} +1 -1
- package/web/dist/assets/{arc-D52-xt6J.js → arc-CrRZ1Za7.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-Bxh49GyJ.js → architectureDiagram-ZJ3FMSHR-CPe2QuQL.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-D8LSE94K.js → blockDiagram-677ZJIJ3-DfT6vWOg.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-Dcs1NVIx.js → c4Diagram-LMCZKHZV-BO-VXBT8.js} +1 -1
- package/web/dist/assets/channel-HtOTPdHg.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-Dy3zu7gI.js → chunk-2Q5K7J3B-nq8p0xwC.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-5v78VX3T.js → chunk-32BRIVSS-DstJjLIA.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-DUJMCfdj.js → chunk-5VM5RSS4-Bq0MPH_Q.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-DYyT1So9.js → chunk-EX3LRPZG-BPofXJWl.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-Buzzx5R2.js → chunk-JWPE2WC7-BBr7v6mo.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-B8fOWRD7.js → chunk-MOJQB5TN-DA7ca6Jr.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-Bp04YO26.js → chunk-RYQCIY6F-fdXZe4nw.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-Gw6h8gPE.js → chunk-V7JOEXUC-BSATEdk2.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-ASf4dPE-.js → chunk-VR4S4FIN-RR27eQST.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-C4B-MNNk.js → chunk-XXDRQBXY-Bh8VimxP.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-C2P3dws0.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-C2P3dws0.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-C0S415ey.js → cose-bilkent-JH36ORCC-eP239MQJ.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-LWUlL1XB.js → cynefin-VYW2F7L2-Sb8l4Vm6.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-ClnNDuqy.js → cynefinDiagram-TSTJHNR4-BrWMkhw9.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-CRMTDGPv.js → dagre-VKFMJZFB-DT5kNKEx.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-Cjt1nFI8.js → diagram-FQU43EPY-Dy0Bxyod.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-DynrWO-8.js → diagram-G47NLZAW-R_yWa2-6.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-CkEgxg4T.js → diagram-NH7WQ7WH-BV2cGmgX.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-DzB43wzH.js → diagram-OA4YK3LP-auayiDmq.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-DqxH3-sW.js → diagram-WEI45ONY-CE7bHIYE.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-5NxDrqIa.js → ebnfDiagram-CCIWWBDH-C3SMk6Uu.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-OfiKzPj4.js → erDiagram-Q63AITRT-CnTs6X2D.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-DsqfPM3s.js → flowDiagram-23GEKE2U-DobYm7mq.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-N48fLign.js → ganttDiagram-NO4QXBWP-AkTQtAsR.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-rn1cXRYh.js → gitGraphDiagram-IHSO6WYX-CpLWsqgx.js} +1 -1
- package/web/dist/assets/{index-BHp6FirC.js → index-BCzNnCl0.js} +6 -6
- package/web/dist/assets/index-BUp5hFdX.css +1 -0
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-CjJ3TLmy.js → infoDiagram-FWYZ7A6U-ByzTSgj_.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-DvCylaIQ.js → ishikawaDiagram-FXEZZL3T-CaL9Wi8i.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-B_Ethynx.js → journeyDiagram-5HDEW3XC-BillueZJ.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-BlQuhDqn.js → kanban-definition-HUTT4EX6-DI_IRVWQ.js} +1 -1
- package/web/dist/assets/{linear-DIwvxXrN.js → linear-Cabaq-RD.js} +1 -1
- package/web/dist/assets/{mermaid.core-CfAsl9Pc.js → mermaid.core-D8KnBjxP.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-BxJep4HE.js → mindmap-definition-LN4V7U3C-Bl7FpPwk.js} +1 -1
- package/web/dist/assets/{papaparse.min-BjO79nbw.js → papaparse.min-CcjjKk0h.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-yceXoFi0.js → pegDiagram-2B236MQR-CmsG2owD.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-BNBzpT5B.js → pieDiagram-ENE6RG2P-CLA_mcHl.js} +1 -1
- package/web/dist/assets/pilotswarm-DMCBXi5A.js +97 -0
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-DCg1DTax.js → quadrantDiagram-ABIIQ3AL-hT9bnnUG.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-DQdy8xST.js → railroadDiagram-RFXS5EU6-CNC0kFW6.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-N8PYSqS8.js → requirementDiagram-TGXJPOKE-C-nSthhK.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-DK9WhfTA.js → sankeyDiagram-HTMAVEWB-C5JdV5bI.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-DEpALkWi.js → sequenceDiagram-DBY2YBRQ-BMKBxKk9.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-CKOEPe9x.js → sizeCapture-X5ZJPWSS-CbwNw-TR.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-Dwwyy0hH.js → stateDiagram-2N3HPSRC-ChvGrUa_.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-DLbEwVA2.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-hDoJpKdQ.js → swimlanes-5IMT3BWC-D9Uzkbi7.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-DMzhdO6y.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-CPXnQ7LH.js → timeline-definition-FHXFAJF6-ctFEfgUp.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-CYsOwGTT.js → vennDiagram-L72KCM5P-DGrJhlyF.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-DKh__oEb.js → wardleyDiagram-EHGQE667-BGqtZ8dQ.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-Bws7l8k9.js → xychartDiagram-FW5EYKEG-DAXXs9xC.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/runtime.js +22 -0
- package/web/dist/assets/channel-DE0-pG_Z.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-B0o6by6m.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-B0o6by6m.js +0 -1
- package/web/dist/assets/index-DEymVbi5.css +0 -1
- package/web/dist/assets/pilotswarm-DewDyJhu.js +0 -97
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-CRKYt8KU.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-DtnCdOQI.js +0 -8
|
@@ -579,7 +579,7 @@ function buildSessionOwnerFilterItems(state) {
|
|
|
579
579
|
id: "all",
|
|
580
580
|
kind: "all",
|
|
581
581
|
label: "All",
|
|
582
|
-
description: "
|
|
582
|
+
description: "Every session you can see — your own, ones shared with you, and system sessions.",
|
|
583
583
|
},
|
|
584
584
|
{
|
|
585
585
|
id: "system",
|
|
@@ -893,7 +893,127 @@ export function agentPickerInitialIndex(items = []) {
|
|
|
893
893
|
return firstClosed >= 0 ? firstClosed : 0;
|
|
894
894
|
}
|
|
895
895
|
|
|
896
|
-
|
|
896
|
+
/** How the picker may be ordered. `used` is per-person; the rest are stable. */
|
|
897
|
+
export const AGENT_PICKER_SORTS = ["used", "name", "package"];
|
|
898
|
+
export const AGENT_PICKER_DEFAULT_SORT = "used";
|
|
899
|
+
|
|
900
|
+
/** Profile-settings key holding this person's per-agent start counts. */
|
|
901
|
+
export const AGENT_USAGE_SETTING = "agentPickerUsage";
|
|
902
|
+
|
|
903
|
+
/** Everything a query is matched against, lowercased once per agent. */
|
|
904
|
+
function agentHaystack(agent) {
|
|
905
|
+
return [
|
|
906
|
+
agent?.label, agent?.agentName, agent?.title, agent?.description,
|
|
907
|
+
agent?.packageTitle, agent?.packageName,
|
|
908
|
+
...(Array.isArray(agent?.tools) ? agent.tools : []),
|
|
909
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Lexical match: every whitespace-separated term must appear somewhere in the
|
|
914
|
+
* agent's text. AND rather than OR, because "rcakit pg" should narrow to one
|
|
915
|
+
* agent rather than widen to everything mentioning either word.
|
|
916
|
+
*
|
|
917
|
+
* The score only ranks matches against each other — a name hit outranks a
|
|
918
|
+
* description hit, and a prefix outranks a mid-word hit, so typing "rca" puts
|
|
919
|
+
* the agents CALLED rcakit above the ones that merely mention it.
|
|
920
|
+
*/
|
|
921
|
+
export function scoreAgentMatch(agent, query) {
|
|
922
|
+
const terms = String(query || "").toLowerCase().split(/\s+/).filter(Boolean);
|
|
923
|
+
if (terms.length === 0) return 0;
|
|
924
|
+
const hay = agentHaystack(agent);
|
|
925
|
+
const name = `${agent?.label || ""} ${agent?.agentName || ""}`.toLowerCase();
|
|
926
|
+
let score = 0;
|
|
927
|
+
for (const term of terms) {
|
|
928
|
+
if (!hay.includes(term)) return -1; // every term must land
|
|
929
|
+
if (name.startsWith(term)) score += 6;
|
|
930
|
+
else if (new RegExp(`\\b${term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`).test(name)) score += 4;
|
|
931
|
+
else if (name.includes(term)) score += 3;
|
|
932
|
+
else score += 1;
|
|
933
|
+
}
|
|
934
|
+
return score;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
function agentUsageCount(usage, agent) {
|
|
938
|
+
if (!usage || !agent?.agentName) return 0;
|
|
939
|
+
return Number(usage[String(agent.agentName)]) || 0;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function compareAgentsForSort(sort, usage) {
|
|
943
|
+
const byName = (a, b) => String(a.label || a.agentName || "")
|
|
944
|
+
.localeCompare(String(b.label || b.agentName || ""), undefined, { sensitivity: "base" });
|
|
945
|
+
if (sort === "name") return byName;
|
|
946
|
+
if (sort === "package") {
|
|
947
|
+
return (a, b) => String(a.packageTitle || a.packageName || "")
|
|
948
|
+
.localeCompare(String(b.packageTitle || b.packageName || ""), undefined, { sensitivity: "base" })
|
|
949
|
+
|| byName(a, b);
|
|
950
|
+
}
|
|
951
|
+
// "used": most-started first, then alphabetical so the long tail of
|
|
952
|
+
// never-started agents is not in arbitrary order.
|
|
953
|
+
return (a, b) => agentUsageCount(usage, b) - agentUsageCount(usage, a) || byName(a, b);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* The picker list: ONE ROW PER AGENT.
|
|
958
|
+
*
|
|
959
|
+
* The old shape gave each package a header row and then repeated its agents
|
|
960
|
+
* beneath. Nearly every package holds exactly one agent, so that spent two
|
|
961
|
+
* rows saying the same thing twice and pushed the list past a screen. The
|
|
962
|
+
* package is still shown — as trailing metadata on the row it belongs to,
|
|
963
|
+
* and as a sort key — but it no longer costs a row of its own.
|
|
964
|
+
*/
|
|
965
|
+
export function buildAgentPickerItems(catalog = [], opts = {}) {
|
|
966
|
+
const {
|
|
967
|
+
query = "",
|
|
968
|
+
sort = AGENT_PICKER_DEFAULT_SORT,
|
|
969
|
+
usage = null,
|
|
970
|
+
genericItem = null,
|
|
971
|
+
} = opts || {};
|
|
972
|
+
|
|
973
|
+
// Generic sits ABOVE everything and never filters out on an empty query:
|
|
974
|
+
// it is not an agent, it is the absence of one, and it is the most common
|
|
975
|
+
// pick. A typed query does filter it, so search can reach past it.
|
|
976
|
+
const items = [];
|
|
977
|
+
const terms = String(query || "").trim();
|
|
978
|
+
if (genericItem && (!terms || scoreAgentMatch({ label: genericItem.label, description: genericItem.description }, terms) >= 0)) {
|
|
979
|
+
items.push({ ...genericItem, depth: 0 });
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// Composition still decides whether an agent can be STARTED — `startedBy`
|
|
983
|
+
// resolving inside its own set is what makes it called-only. The flat list
|
|
984
|
+
// drops the nesting, not that derivation, so each set still goes through
|
|
985
|
+
// orderAgentsByComposition; only its ordering is discarded here.
|
|
986
|
+
const raw = catalog.filter((agent) => agent && agent.kind !== "section");
|
|
987
|
+
const sets = new Map();
|
|
988
|
+
for (const agent of raw) {
|
|
989
|
+
const key = agent.builtin ? "builtin" : agentPickerPackageKey(agent);
|
|
990
|
+
if (!sets.has(key)) sets.set(key, []);
|
|
991
|
+
sets.get(key).push(agent);
|
|
992
|
+
}
|
|
993
|
+
const agents = [];
|
|
994
|
+
for (const set of sets.values()) {
|
|
995
|
+
for (const agent of orderAgentsByComposition(set)) agents.push({ ...agent, depth: 0 });
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
const scored = terms
|
|
999
|
+
? agents.map((agent) => ({ agent, score: scoreAgentMatch(agent, terms) })).filter((row) => row.score >= 0)
|
|
1000
|
+
: agents.map((agent) => ({ agent, score: 0 }));
|
|
1001
|
+
|
|
1002
|
+
const compare = compareAgentsForSort(sort, usage);
|
|
1003
|
+
scored.sort((a, b) => (terms ? b.score - a.score : 0) || compare(a.agent, b.agent));
|
|
1004
|
+
|
|
1005
|
+
for (const { agent } of scored) {
|
|
1006
|
+
items.push({
|
|
1007
|
+
...agent,
|
|
1008
|
+
depth: 0,
|
|
1009
|
+
usageCount: agentUsageCount(usage, agent),
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
return items;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/** @deprecated the grouped shape; kept only for the TUI's collapse tests. */
|
|
1016
|
+
export function buildGroupedAgentPickerItems(catalog = [], collapsedKeys = [], genericItem = null) {
|
|
897
1017
|
const collapsed = new Set(collapsedKeys);
|
|
898
1018
|
const items = [];
|
|
899
1019
|
|
|
@@ -3051,6 +3171,8 @@ export class PilotSwarmUiController {
|
|
|
3051
3171
|
* admin view) and refresh the user profile so the UI is current.
|
|
3052
3172
|
*/
|
|
3053
3173
|
async openAdminConsole() {
|
|
3174
|
+
// One MODE at a time — see openBudget.
|
|
3175
|
+
if (this.getState().ui?.budgetOpen) this.closeBudget();
|
|
3054
3176
|
this.dispatch({ type: "admin/visibility", visible: true });
|
|
3055
3177
|
await this.refreshAdminProfile().catch(() => {});
|
|
3056
3178
|
void this.refreshAdminModelProviders().catch(() => {});
|
|
@@ -3062,6 +3184,24 @@ export class PilotSwarmUiController {
|
|
|
3062
3184
|
this.dispatch({ type: "admin/visibility", visible: false });
|
|
3063
3185
|
}
|
|
3064
3186
|
|
|
3187
|
+
/**
|
|
3188
|
+
* The Workspace mode: sessions, chat and the panels. Closes whichever
|
|
3189
|
+
* other mode is up. A real destination, so the toolbar can offer "back
|
|
3190
|
+
* to the workspace" as a button rather than "close whatever is open".
|
|
3191
|
+
*/
|
|
3192
|
+
openWorkspace() {
|
|
3193
|
+
if (this.getState().admin?.visible) this.closeAdminConsole();
|
|
3194
|
+
if (this.getState().ui?.budgetOpen) this.closeBudget();
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
/** Which mode is on screen: "workspace" | "budget" | "admin". */
|
|
3198
|
+
currentMode() {
|
|
3199
|
+
const state = this.getState();
|
|
3200
|
+
if (state.admin?.visible) return "admin";
|
|
3201
|
+
if (state.ui?.budgetOpen) return "budget";
|
|
3202
|
+
return "workspace";
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3065
3205
|
/**
|
|
3066
3206
|
* Land on a freshly created session. Selecting it is not enough: the
|
|
3067
3207
|
* Admin Console REPLACES the workspace, so creating from there left the
|
|
@@ -3163,6 +3303,15 @@ export class PilotSwarmUiController {
|
|
|
3163
3303
|
}
|
|
3164
3304
|
}
|
|
3165
3305
|
|
|
3306
|
+
/**
|
|
3307
|
+
* Drop a failed provider mutation's error. Called when the sheet that
|
|
3308
|
+
* raised it closes — the failure was that sheet's, and a banner outliving
|
|
3309
|
+
* it describes a change the person already abandoned.
|
|
3310
|
+
*/
|
|
3311
|
+
dismissAdminModelProviderMutationError() {
|
|
3312
|
+
this.dispatch({ type: "admin/modelProviders/mutationDismissed" });
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3166
3315
|
async _runAdminModelProviderMutation(pending, run) {
|
|
3167
3316
|
this.dispatch({ type: "admin/modelProviders/mutationPending", pending });
|
|
3168
3317
|
try {
|
|
@@ -3188,6 +3337,28 @@ export class PilotSwarmUiController {
|
|
|
3188
3337
|
}));
|
|
3189
3338
|
}
|
|
3190
3339
|
|
|
3340
|
+
/**
|
|
3341
|
+
* Rotate a provider's key in place.
|
|
3342
|
+
*
|
|
3343
|
+
* Two ops, one per scope, the same split deleteAdminProvider uses: a
|
|
3344
|
+
* personal provider answers to its owner, a shared one to an admin. Both
|
|
3345
|
+
* change ONLY the key — the name, type, base URL, defaults, system-session
|
|
3346
|
+
* routing and usage history all stay, which is the whole reason this
|
|
3347
|
+
* exists rather than delete-and-recreate.
|
|
3348
|
+
*/
|
|
3349
|
+
async updateAdminProviderCredential({ name, credentials, shared = false } = {}) {
|
|
3350
|
+
const op = shared ? "updateSharedProviderCredential" : "updateMyProviderCredential";
|
|
3351
|
+
if (typeof this.transport[op] !== "function") {
|
|
3352
|
+
const error = shared
|
|
3353
|
+
? "Shared provider credential updates are not available on this transport."
|
|
3354
|
+
: "Provider credential updates are not available on this transport.";
|
|
3355
|
+
this.dispatch({ type: "admin/modelProviders/mutationFailed", error });
|
|
3356
|
+
return { ok: false, result: null, error };
|
|
3357
|
+
}
|
|
3358
|
+
return this._runAdminModelProviderMutation("updateCredential", () =>
|
|
3359
|
+
this.transport[op]({ name, credentials }));
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3191
3362
|
async deleteAdminProvider(name, { shared = false } = {}) {
|
|
3192
3363
|
const op = shared ? "deleteProvider" : "deleteMyProvider";
|
|
3193
3364
|
if (typeof this.transport[op] !== "function") {
|
|
@@ -3265,13 +3436,25 @@ export class PilotSwarmUiController {
|
|
|
3265
3436
|
name = `${base}-${suffix}`;
|
|
3266
3437
|
suffix += 1;
|
|
3267
3438
|
}
|
|
3268
|
-
this.dispatch({ type: "admin/modelProviders/createBegin", name, typeId, shared });
|
|
3439
|
+
this.dispatch({ type: "admin/modelProviders/createBegin", name, typeId, shared, mode: "create" });
|
|
3269
3440
|
}
|
|
3270
3441
|
|
|
3271
3442
|
beginAdminCreateGithubProvider() {
|
|
3272
3443
|
this.beginAdminCreateProvider({ shared: false });
|
|
3273
3444
|
}
|
|
3274
3445
|
|
|
3446
|
+
beginAdminUpdateProviderCredential(provider) {
|
|
3447
|
+
if (!provider?.name || provider.class === "shared") return;
|
|
3448
|
+
this.dispatch({
|
|
3449
|
+
type: "admin/modelProviders/createBegin",
|
|
3450
|
+
name: provider.name,
|
|
3451
|
+
typeId: provider.typeId,
|
|
3452
|
+
shared: false,
|
|
3453
|
+
mode: "update",
|
|
3454
|
+
stage: "credential",
|
|
3455
|
+
});
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3275
3458
|
cycleAdminProviderCreateType() {
|
|
3276
3459
|
const providers = selectAdminConsole(this.getState()).modelProviders;
|
|
3277
3460
|
const create = this.getState().admin?.modelProviders?.create;
|
|
@@ -3342,7 +3525,9 @@ export class PilotSwarmUiController {
|
|
|
3342
3525
|
};
|
|
3343
3526
|
// Clear the credential from shared state before any network await.
|
|
3344
3527
|
this.dispatch({ type: "admin/modelProviders/createSaving" });
|
|
3345
|
-
const outcome =
|
|
3528
|
+
const outcome = create.mode === "update"
|
|
3529
|
+
? await this.updateAdminProviderCredential({ name: input.name, credentials: input.credentials })
|
|
3530
|
+
: await this.createAdminProvider(input);
|
|
3346
3531
|
this.dispatch(outcome?.ok
|
|
3347
3532
|
? { type: "admin/modelProviders/createEnd" }
|
|
3348
3533
|
: { type: "admin/modelProviders/createFailed", error: outcome?.error });
|
|
@@ -3929,6 +4114,10 @@ export class PilotSwarmUiController {
|
|
|
3929
4114
|
* row it names is in it.
|
|
3930
4115
|
*/
|
|
3931
4116
|
async openBudget({ provider = undefined } = {}) {
|
|
4117
|
+
// One MODE at a time: Workspace, Budget or the Admin Console. The two
|
|
4118
|
+
// flags used to be independent, so Budget could open behind an open
|
|
4119
|
+
// Admin Console and the toolbar showed both as active.
|
|
4120
|
+
if (this.getState().admin?.visible) this.closeAdminConsole();
|
|
3932
4121
|
this.dispatch({
|
|
3933
4122
|
type: "ui/budgetOpen",
|
|
3934
4123
|
open: true,
|
|
@@ -3950,6 +4139,45 @@ export class PilotSwarmUiController {
|
|
|
3950
4139
|
this.dispatch({ type: "ui/budgetOpen", open: false });
|
|
3951
4140
|
}
|
|
3952
4141
|
|
|
4142
|
+
/** Which tab of the Budget screen: "providers" or "summary". */
|
|
4143
|
+
setBudgetTab(tab) {
|
|
4144
|
+
this.dispatch({ type: "budget/tab", tab });
|
|
4145
|
+
if (tab === "summary") this.loadUsageSummary().catch(() => {});
|
|
4146
|
+
}
|
|
4147
|
+
|
|
4148
|
+
/**
|
|
4149
|
+
* Change the Cluster summary's window or provider filter and re-read.
|
|
4150
|
+
* `providers` is the exact list to ask for (empty = all); `preset` is
|
|
4151
|
+
* what the picker shows ("all" | "shared" | "users" | "custom").
|
|
4152
|
+
*/
|
|
4153
|
+
async setUsageSummaryFilter({ days, providers, preset } = {}) {
|
|
4154
|
+
this.dispatch({ type: "budget/summary/filter", days, providers, preset });
|
|
4155
|
+
await this.loadUsageSummary();
|
|
4156
|
+
}
|
|
4157
|
+
|
|
4158
|
+
/**
|
|
4159
|
+
* Read the Cluster summary for the current filter. One request; the
|
|
4160
|
+
* database builds the windows, the series and the model pivot over the
|
|
4161
|
+
* same rows, so the three can never disagree.
|
|
4162
|
+
*/
|
|
4163
|
+
async loadUsageSummary() {
|
|
4164
|
+
if (typeof this.transport.getProviderUsageSummary !== "function") {
|
|
4165
|
+
this.dispatch({ type: "budget/summary/failed", error: "The cluster summary is not available on this transport." });
|
|
4166
|
+
return;
|
|
4167
|
+
}
|
|
4168
|
+
const filter = this.getState().budget?.summary || {};
|
|
4169
|
+
this.dispatch({ type: "budget/summary/loading" });
|
|
4170
|
+
try {
|
|
4171
|
+
const data = await this.transport.getProviderUsageSummary({
|
|
4172
|
+
days: filter.days || 14,
|
|
4173
|
+
...(Array.isArray(filter.providers) && filter.providers.length ? { providers: filter.providers } : {}),
|
|
4174
|
+
});
|
|
4175
|
+
this.dispatch({ type: "budget/summary/loaded", data, fetchedAt: Date.now() });
|
|
4176
|
+
} catch (error) {
|
|
4177
|
+
this.dispatch({ type: "budget/summary/failed", error: budgetRefusalMessage(error) });
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
|
|
3953
4181
|
/**
|
|
3954
4182
|
* The "show overall usage" tick.
|
|
3955
4183
|
*
|
|
@@ -5648,6 +5876,10 @@ export class PilotSwarmUiController {
|
|
|
5648
5876
|
try {
|
|
5649
5877
|
const requestOptions = this.applyActiveGroupDefault(options);
|
|
5650
5878
|
const created = await this.transport.createSessionForAgent(agentName, requestOptions);
|
|
5879
|
+
// Count the start for this person's "most used" ordering. Deliberately
|
|
5880
|
+
// not awaited: the picker's sort order must never delay opening the
|
|
5881
|
+
// session that was just created, and a lost count only reorders a list.
|
|
5882
|
+
this._recordAgentPickerUse(agentName);
|
|
5651
5883
|
await this.placeCreatedSessionInGroup(created, requestOptions.groupId ?? null);
|
|
5652
5884
|
await this.refreshSessions();
|
|
5653
5885
|
await this.loadSession(created.sessionId);
|
|
@@ -6143,18 +6375,14 @@ export class PilotSwarmUiController {
|
|
|
6143
6375
|
}
|
|
6144
6376
|
: null;
|
|
6145
6377
|
|
|
6146
|
-
//
|
|
6147
|
-
//
|
|
6148
|
-
//
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
AGENT_PICKER_MINE_KEY,
|
|
6155
|
-
...new Set(catalog.filter((item) => !item.builtin).map((item) => agentPickerPackageKey(item))),
|
|
6156
|
-
];
|
|
6157
|
-
const items = buildAgentPickerItems(catalog, collapsed, genericItem);
|
|
6378
|
+
// Per-person start counts, so "most used" means most used BY YOU.
|
|
6379
|
+
// Best-effort: a profile that cannot be read just sorts alphabetically
|
|
6380
|
+
// rather than failing the picker open.
|
|
6381
|
+
const usage = await this._loadAgentPickerUsage();
|
|
6382
|
+
const sort = AGENT_PICKER_SORTS.includes(this._agentPickerSort)
|
|
6383
|
+
? this._agentPickerSort
|
|
6384
|
+
: AGENT_PICKER_DEFAULT_SORT;
|
|
6385
|
+
const items = buildAgentPickerItems(catalog, { query: "", sort, usage, genericItem });
|
|
6158
6386
|
|
|
6159
6387
|
this.dispatch({
|
|
6160
6388
|
type: "ui/modal",
|
|
@@ -6163,8 +6391,10 @@ export class PilotSwarmUiController {
|
|
|
6163
6391
|
title: "Select agent for new session",
|
|
6164
6392
|
catalog,
|
|
6165
6393
|
genericItem,
|
|
6166
|
-
collapsed,
|
|
6167
6394
|
items,
|
|
6395
|
+
query: "",
|
|
6396
|
+
sort,
|
|
6397
|
+
usage,
|
|
6168
6398
|
// Land somewhere Enter does something useful. First choice is a
|
|
6169
6399
|
// real agent row; with none visible (no generic, no baked
|
|
6170
6400
|
// agents, every package closed) fall back to the first CLOSED
|
|
@@ -7715,61 +7945,96 @@ export class PilotSwarmUiController {
|
|
|
7715
7945
|
* cursor shifts every index below it, and re-using the old number would
|
|
7716
7946
|
* silently move the highlight onto a different agent.
|
|
7717
7947
|
*/
|
|
7718
|
-
|
|
7948
|
+
/**
|
|
7949
|
+
* This person's per-agent start counts, from their profile settings.
|
|
7950
|
+
* Best-effort by design: a profile that cannot be read sorts the picker
|
|
7951
|
+
* alphabetically instead of refusing to open.
|
|
7952
|
+
*/
|
|
7953
|
+
async _loadAgentPickerUsage() {
|
|
7954
|
+
try {
|
|
7955
|
+
if (typeof this.transport.getCurrentUserProfile !== "function") return null;
|
|
7956
|
+
const profile = await this.transport.getCurrentUserProfile();
|
|
7957
|
+
const raw = profile?.profileSettings?.[AGENT_USAGE_SETTING];
|
|
7958
|
+
if (!raw || typeof raw !== "object") return {};
|
|
7959
|
+
const usage = {};
|
|
7960
|
+
for (const [name, count] of Object.entries(raw)) {
|
|
7961
|
+
const n = Number(count);
|
|
7962
|
+
if (Number.isFinite(n) && n > 0) usage[name] = n;
|
|
7963
|
+
}
|
|
7964
|
+
return usage;
|
|
7965
|
+
} catch {
|
|
7966
|
+
return null;
|
|
7967
|
+
}
|
|
7968
|
+
}
|
|
7969
|
+
|
|
7970
|
+
/**
|
|
7971
|
+
* Count one start. Read-modify-write against the whole settings document,
|
|
7972
|
+
* because that is the shape the API takes — so a concurrent write from
|
|
7973
|
+
* another tab can lose a count. A miscount only reorders a picker, which
|
|
7974
|
+
* is not worth a locking protocol.
|
|
7975
|
+
*/
|
|
7976
|
+
async _recordAgentPickerUse(agentName) {
|
|
7977
|
+
const name = String(agentName || "").trim();
|
|
7978
|
+
if (!name) return;
|
|
7979
|
+
try {
|
|
7980
|
+
if (typeof this.transport.getCurrentUserProfile !== "function"
|
|
7981
|
+
|| typeof this.transport.setCurrentUserProfileSettings !== "function") return;
|
|
7982
|
+
const profile = await this.transport.getCurrentUserProfile();
|
|
7983
|
+
const settings = { ...(profile?.profileSettings || {}) };
|
|
7984
|
+
const usage = { ...(settings[AGENT_USAGE_SETTING] || {}) };
|
|
7985
|
+
usage[name] = (Number(usage[name]) || 0) + 1;
|
|
7986
|
+
settings[AGENT_USAGE_SETTING] = usage;
|
|
7987
|
+
// Into state before the network: the portal's own profile save
|
|
7988
|
+
// fires on the very activeSessionId change this create causes,
|
|
7989
|
+
// and it writes the whole document from state. If state does not
|
|
7990
|
+
// have the new count by then, that save erases it.
|
|
7991
|
+
this.dispatch({ type: "ui/agentPickerUsage", usage });
|
|
7992
|
+
await this.transport.setCurrentUserProfileSettings({ settings });
|
|
7993
|
+
} catch {
|
|
7994
|
+
// Never let bookkeeping fail a session create.
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
|
|
7998
|
+
/** Re-run the list against a new query or sort, keeping the selection. */
|
|
7999
|
+
_refreshAgentPicker(patch) {
|
|
7719
8000
|
const modal = this.getState().ui.modal;
|
|
7720
|
-
if (!modal || modal.type !== "sessionAgentPicker"
|
|
7721
|
-
const
|
|
7722
|
-
const
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
8001
|
+
if (!modal || modal.type !== "sessionAgentPicker") return;
|
|
8002
|
+
const next = { ...modal, ...patch };
|
|
8003
|
+
const items = buildAgentPickerItems(next.catalog || [], {
|
|
8004
|
+
query: next.query,
|
|
8005
|
+
sort: next.sort,
|
|
8006
|
+
usage: next.usage,
|
|
8007
|
+
genericItem: next.genericItem || null,
|
|
8008
|
+
});
|
|
8009
|
+
// Keep the same agent selected when it survives the new list; land on
|
|
8010
|
+
// the first row when it does not, so Enter always does something.
|
|
7729
8011
|
const selectedId = modal.items?.[modal.selectedIndex || 0]?.id;
|
|
7730
|
-
|
|
7731
|
-
if (nextIndex < 0) {
|
|
7732
|
-
// The selected row was inside the section that just closed — land
|
|
7733
|
-
// on that section's own header, which is where it went.
|
|
7734
|
-
nextIndex = Math.max(0, items.findIndex((item) => item.sectionKey === sectionKey));
|
|
7735
|
-
}
|
|
8012
|
+
const keptIndex = items.findIndex((item) => item.id === selectedId);
|
|
7736
8013
|
this.dispatch({
|
|
7737
8014
|
type: "ui/modal",
|
|
7738
|
-
modal: { ...
|
|
8015
|
+
modal: { ...next, items, selectedIndex: keptIndex >= 0 ? keptIndex : 0 },
|
|
7739
8016
|
});
|
|
7740
8017
|
}
|
|
7741
8018
|
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
* the cursor is already on a leaf — the same shape as the session tree.
|
|
7746
|
-
*/
|
|
7747
|
-
moveAgentPickerSection(delta) {
|
|
7748
|
-
const modal = this.getState().ui.modal;
|
|
7749
|
-
if (!modal || modal.type !== "sessionAgentPicker") return false;
|
|
7750
|
-
const items = Array.isArray(modal.items) ? modal.items : [];
|
|
7751
|
-
const index = Math.max(0, Math.min(Number(modal.selectedIndex) || 0, items.length - 1));
|
|
7752
|
-
const item = items[index];
|
|
7753
|
-
if (!item) return false;
|
|
8019
|
+
setAgentPickerQuery(query) {
|
|
8020
|
+
this._refreshAgentPicker({ query: String(query ?? "") });
|
|
8021
|
+
}
|
|
7754
8022
|
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
return false;
|
|
7761
|
-
}
|
|
8023
|
+
setAgentPickerSort(sort) {
|
|
8024
|
+
if (!AGENT_PICKER_SORTS.includes(sort)) return;
|
|
8025
|
+
this._agentPickerSort = sort; // remembered for the next open
|
|
8026
|
+
this._refreshAgentPicker({ sort });
|
|
8027
|
+
}
|
|
7762
8028
|
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
}
|
|
8029
|
+
/**
|
|
8030
|
+
* Left/right on the agent picker: nothing.
|
|
8031
|
+
*
|
|
8032
|
+
* These keys used to open and close package headings. The picker is a flat
|
|
8033
|
+
* list of agents now, so there is nothing to expand — and returning false
|
|
8034
|
+
* matters, because that is what lets the keystroke reach the search box and
|
|
8035
|
+
* move the caret instead of being swallowed here.
|
|
8036
|
+
*/
|
|
8037
|
+
moveAgentPickerSection() {
|
|
7773
8038
|
return false;
|
|
7774
8039
|
}
|
|
7775
8040
|
|
|
@@ -7992,11 +8257,6 @@ export class PilotSwarmUiController {
|
|
|
7992
8257
|
}
|
|
7993
8258
|
if (modal.type === "sessionAgentPicker") {
|
|
7994
8259
|
const item = modal.items?.[modal.selectedIndex || 0];
|
|
7995
|
-
// Enter on a heading opens or closes it; the dialog stays up.
|
|
7996
|
-
if (item?.kind === "section") {
|
|
7997
|
-
this.toggleAgentPickerSection(item.sectionKey);
|
|
7998
|
-
return;
|
|
7999
|
-
}
|
|
8000
8260
|
// A called-only agent is rendered so the package's composition is
|
|
8001
8261
|
// visible, but starting one cold is not a thing you can do. Refuse
|
|
8002
8262
|
// here rather than letting the create fail after the dialog closes.
|
|
@@ -10205,6 +10465,9 @@ export class PilotSwarmUiController {
|
|
|
10205
10465
|
case UI_COMMANDS.CLOSE_ADMIN_CONSOLE:
|
|
10206
10466
|
this.closeAdminConsole();
|
|
10207
10467
|
return;
|
|
10468
|
+
case UI_COMMANDS.OPEN_WORKSPACE:
|
|
10469
|
+
this.openWorkspace();
|
|
10470
|
+
return;
|
|
10208
10471
|
case UI_COMMANDS.ADMIN_REFRESH_PROFILE:
|
|
10209
10472
|
await this.refreshAdminProfile();
|
|
10210
10473
|
return;
|
package/ui/core/src/history.js
CHANGED
|
@@ -542,6 +542,11 @@ function buildChatMessage(event, role) {
|
|
|
542
542
|
};
|
|
543
543
|
}
|
|
544
544
|
|
|
545
|
+
// The Copilot SDK echoes the system prompt as a system.message on EVERY
|
|
546
|
+
// turn, and the worker keeps only a 120-char snippet of it (session-proxy.ts,
|
|
547
|
+
// "Full content omitted from CMS"). For one release (0.5.47) each echo also
|
|
548
|
+
// got a collapsed row in the chat pane — one per turn, and opening it showed
|
|
549
|
+
// the snippet, not the prompt. Activity feed only, as before.
|
|
545
550
|
function shouldRenderSystemMessageAsActivity(event) {
|
|
546
551
|
// Most system.message events stay out of the chat pane. They include the
|
|
547
552
|
// per-turn system prompt sent to the LLM plus internal rehydration notices,
|
package/ui/core/src/index.js
CHANGED
|
@@ -15,3 +15,4 @@ export * from "./controller.js";
|
|
|
15
15
|
export { isThemeLight } from "./themes/helpers.js";
|
|
16
16
|
export * from "./themes/index.js";
|
|
17
17
|
export { validateCanvasAction, formatCanvasActionPrompt, parseCanvasActionContent, isCanvasActionContent, createCanvasActionLimiter, CANVAS_ACTION_PREFIX } from "./canvas-actions.js";
|
|
18
|
+
export * from "./table-layout.js";
|