pilotswarm 0.5.62 → 0.5.64
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/server.d.ts.map +1 -1
- package/mcp/dist/src/server.js +2 -0
- package/mcp/dist/src/server.js.map +1 -1
- package/mcp/dist/src/tools/features.d.ts +4 -0
- package/mcp/dist/src/tools/features.d.ts.map +1 -0
- package/mcp/dist/src/tools/features.js +35 -0
- package/mcp/dist/src/tools/features.js.map +1 -0
- package/package.json +2 -2
- package/tui/src/node-sdk-transport.js +13 -0
- package/ui/core/src/controller.js +330 -14
- package/ui/core/src/history.js +89 -14
- package/ui/core/src/moa.js +9 -0
- package/ui/core/src/native-tasks.js +197 -0
- package/ui/core/src/question-display.js +13 -0
- package/ui/core/src/reducer.js +31 -7
- package/ui/core/src/selectors.js +46 -13
- package/ui/core/src/session-errors.js +13 -6
- package/ui/core/src/session-warning.js +14 -0
- package/ui/core/src/state.js +7 -2
- package/ui/react/src/components.js +2 -1
- package/ui/react/src/feature-flags-panel.js +186 -0
- package/ui/react/src/native-task-card.js +59 -0
- package/ui/react/src/web-app.js +173 -33
- package/web/dist/assets/{abnfDiagram-VRR7QNED-DAiG41W-.js → abnfDiagram-VRR7QNED-gIPHF2gV.js} +1 -1
- package/web/dist/assets/{arc-CN_GY3DG.js → arc-DMW_q5Ml.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-DD9qiBOj.js → architectureDiagram-ZJ3FMSHR-DAKsrxcB.js} +1 -1
- package/web/dist/assets/{blockDiagram-677ZJIJ3-CZg2R8i6.js → blockDiagram-677ZJIJ3-BUjSLpP3.js} +1 -1
- package/web/dist/assets/{c4Diagram-LMCZKHZV-DuZxUZYd.js → c4Diagram-LMCZKHZV-Dw2nPoes.js} +1 -1
- package/web/dist/assets/channel-CcOa1LtY.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-BX7lDvmf.js → chunk-2Q5K7J3B-BAL28Rv0.js} +1 -1
- package/web/dist/assets/{chunk-32BRIVSS-COupwzAw.js → chunk-32BRIVSS-DDlt9K8s.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-C_IyERib.js → chunk-5VM5RSS4-BLQGJGoP.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-CyfIuYKn.js → chunk-EX3LRPZG-XFMb3HRy.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-Cj5lvEfN.js → chunk-JWPE2WC7-DWnKTQ72.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-l907LpMA.js → chunk-MOJQB5TN-MltzhnwP.js} +1 -1
- package/web/dist/assets/{chunk-RYQCIY6F-PLru1fOZ.js → chunk-RYQCIY6F-Ce3q0-mJ.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-C6GCxiE5.js → chunk-V7JOEXUC-ThXLYY88.js} +1 -1
- package/web/dist/assets/{chunk-VR4S4FIN-BGelrIxQ.js → chunk-VR4S4FIN-DbhAykiW.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-BmJstjEv.js → chunk-XXDRQBXY-BpkNB0mD.js} +1 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-B2VhHRSV.js +1 -0
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-B2VhHRSV.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-CRabxOOK.js → cose-bilkent-JH36ORCC-rctqAPvj.js} +1 -1
- package/web/dist/assets/{cynefin-VYW2F7L2-CminxBoG.js → cynefin-VYW2F7L2-Dg1cltg6.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-yBazsQf4.js → cynefinDiagram-TSTJHNR4-Ci23Sv4Z.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-DcZuZV2n.js → dagre-VKFMJZFB-BpWTGhpv.js} +1 -1
- package/web/dist/assets/{diagram-FQU43EPY-CHoFzsw6.js → diagram-FQU43EPY-B69JpiAe.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-B8leqMRl.js → diagram-G47NLZAW-BHMk1TOO.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-DNoVxpa3.js → diagram-NH7WQ7WH-CtvZIIsk.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-gC3Q2X6-.js → diagram-OA4YK3LP-BFvKh800.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-CABqyjDl.js → diagram-WEI45ONY-CgB8sWPe.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-cSguXaDC.js → ebnfDiagram-CCIWWBDH-DyqPNydM.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-CoWX37V3.js → erDiagram-Q63AITRT-CwpvZQ3Q.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-I9DHYjhG.js → flowDiagram-23GEKE2U-EBKSKxfC.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-DaFnfYTt.js → ganttDiagram-NO4QXBWP-q5tMPAuC.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-IHSO6WYX-CwiXVM_O.js → gitGraphDiagram-IHSO6WYX-DCRbi4KE.js} +1 -1
- package/web/dist/assets/index-BfF7e08d.js +24 -0
- package/web/dist/assets/index-uhc851H3.css +1 -0
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-DtzIrIpW.js → infoDiagram-FWYZ7A6U-Bex5pSIX.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-B9nk829n.js → ishikawaDiagram-FXEZZL3T-Dj0li_TY.js} +1 -1
- package/web/dist/assets/{journeyDiagram-5HDEW3XC-xLaPm-ow.js → journeyDiagram-5HDEW3XC-BtEGEeVr.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-BnDqLYLC.js → kanban-definition-HUTT4EX6-BTFfZvYt.js} +1 -1
- package/web/dist/assets/{linear-Djc7nVo8.js → linear-C3SQCsip.js} +1 -1
- package/web/dist/assets/{mermaid.core-B5elhUtV.js → mermaid.core-DALDJDu8.js} +4 -4
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-N-B7EJGs.js → mindmap-definition-LN4V7U3C-BZf3uw0u.js} +1 -1
- package/web/dist/assets/{papaparse.min-BqM-LJkq.js → papaparse.min-Bh9MZ-fk.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-BIollSMB.js → pegDiagram-2B236MQR-PuOleiMg.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-DkZY6iCd.js → pieDiagram-ENE6RG2P-CcFORUaW.js} +1 -1
- package/web/dist/assets/pilotswarm-B9rLtXpI.js +109 -0
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-iUDmQIyL.js → quadrantDiagram-ABIIQ3AL-CEm0mp4y.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-BSCGJgV7.js → railroadDiagram-RFXS5EU6-DeH-uX0_.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-9yqcGRUA.js → requirementDiagram-TGXJPOKE-CMbJVMc1.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-HTMAVEWB-SPh1plT9.js → sankeyDiagram-HTMAVEWB-C9gL5w_b.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-C0nv5x71.js → sequenceDiagram-DBY2YBRQ-BUo8TT74.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-DMXfUt1o.js → sizeCapture-X5ZJPWSS-C5lqG0ei.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-vjhtquua.js → stateDiagram-2N3HPSRC-DyeISrEU.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-5FZbaloZ.js +1 -0
- package/web/dist/assets/{swimlanes-5IMT3BWC-Bqewmkt5.js → swimlanes-5IMT3BWC-DawuBk_a.js} +2 -2
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-DL8PgtmM.js +8 -0
- package/web/dist/assets/{timeline-definition-FHXFAJF6-DYHsdXUL.js → timeline-definition-FHXFAJF6-BZJ2YJcm.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-Cb7cOHSa.js → vennDiagram-L72KCM5P-BMXBVCfe.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-BHpO1byL.js → wardleyDiagram-EHGQE667-DWedKsrQ.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-DHDvGPtH.js → xychartDiagram-FW5EYKEG--XdfYceC.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/runtime.js +19 -0
- package/web/server.js +2 -2
- package/web/dist/assets/channel-B50-C7oJ.js +0 -1
- package/web/dist/assets/classDiagram-OUVF2IWQ-CIJlKS0m.js +0 -1
- package/web/dist/assets/classDiagram-v2-EOCWNBFH-CIJlKS0m.js +0 -1
- package/web/dist/assets/index-CHlrgp0s.css +0 -1
- package/web/dist/assets/index-bf7RCrAf.js +0 -24
- package/web/dist/assets/pilotswarm-Di5s6c69.js +0 -105
- package/web/dist/assets/stateDiagram-v2-6OUMAXLB-CYIeYmPN.js +0 -1
- package/web/dist/assets/swimlanesDiagram-G3AALYLV-BeKhH9C9.js +0 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA2BlD,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,SAAS,CA6G7D"}
|
package/mcp/dist/src/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import { registerAgentTools } from "./tools/agents.js";
|
|
|
5
5
|
import { registerFactsTools } from "./tools/facts.js";
|
|
6
6
|
import { registerModelTools } from "./tools/models.js";
|
|
7
7
|
import { registerProviderTools } from "./tools/providers.js";
|
|
8
|
+
import { registerFeatureTools } from "./tools/features.js";
|
|
8
9
|
import { registerCapabilityTools } from "./tools/capabilities.js";
|
|
9
10
|
import { registerEnhancedFactTools } from "./tools/facts-enhanced.js";
|
|
10
11
|
import { registerGraphTools } from "./tools/graph.js";
|
|
@@ -78,6 +79,7 @@ export function createMcpServer(ctx) {
|
|
|
78
79
|
registerFactsTools(server, ctx);
|
|
79
80
|
registerModelTools(server, ctx);
|
|
80
81
|
registerProviderTools(server, ctx);
|
|
82
|
+
registerFeatureTools(server, ctx);
|
|
81
83
|
registerCapabilityTools(server, ctx);
|
|
82
84
|
registerTurnControlTools(server, ctx);
|
|
83
85
|
registerGroupTools(server, ctx);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../src/tools/features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,QA8BzE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { FEATURE_OPERATION_SPECS, featureToolParameters } from "pilotswarm-sdk";
|
|
3
|
+
import { jsonResult, withToolErrors } from "../util/respond.js";
|
|
4
|
+
export function registerFeatureTools(server, ctx) {
|
|
5
|
+
// Web mode ignores this value and derives identity from authentication.
|
|
6
|
+
// Direct mode requires an explicitly configured identity/admin grant for
|
|
7
|
+
// feature policy; holding a catalog handle alone does not opt it in.
|
|
8
|
+
const provider = process.env.PILOTSWARM_MCP_ACTOR_PROVIDER;
|
|
9
|
+
const subject = process.env.PILOTSWARM_MCP_ACTOR_SUBJECT;
|
|
10
|
+
const viewer = {
|
|
11
|
+
principal: provider && subject ? { provider, subject } : null,
|
|
12
|
+
isAdmin: !ctx.webMode && ctx.admin && process.env.PILOTSWARM_MCP_FEATURE_ADMIN === "true",
|
|
13
|
+
};
|
|
14
|
+
for (const spec of FEATURE_OPERATION_SPECS) {
|
|
15
|
+
const schema = featureToolParameters(spec);
|
|
16
|
+
const inputSchema = {};
|
|
17
|
+
for (const [key, property] of Object.entries(schema.properties)) {
|
|
18
|
+
let field = property.type === "boolean" ? z.boolean()
|
|
19
|
+
: property.type === "integer" ? z.number().int().min(property.minimum ?? 0).max(property.maximum ?? Number.MAX_SAFE_INTEGER)
|
|
20
|
+
: z.string().min(property.minLength ?? 0).max(property.maxLength ?? 1024);
|
|
21
|
+
if (property.description)
|
|
22
|
+
field = field.describe(property.description);
|
|
23
|
+
inputSchema[key] = schema.required.includes(key) ? field : field.optional();
|
|
24
|
+
}
|
|
25
|
+
server.registerTool(spec.name, { title: spec.name.replaceAll("_", " "), description: spec.description, inputSchema }, withToolErrors(async (args) => {
|
|
26
|
+
const method = ctx.mgmt[spec.method].bind(ctx.mgmt);
|
|
27
|
+
const values = spec.kind === "audit" ? [args.limit]
|
|
28
|
+
: spec.kind === "users" ? [args.query]
|
|
29
|
+
: spec.kind === "read" ? ("targetUser" in spec ? [args.userId] : [])
|
|
30
|
+
: "targetUser" in spec ? [args.userId, args] : [args];
|
|
31
|
+
return jsonResult(await method(viewer, ...values));
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=features.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features.js","sourceRoot":"","sources":["../../../src/tools/features.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,GAAkB;IACtE,wEAAwE;IACxE,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC;IAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;IACzD,MAAM,MAAM,GAAG;QACX,SAAS,EAAE,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI;QAC7D,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,KAAK,MAAM;KAC5F,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,uBAAuB,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,WAAW,GAAiC,EAAE,CAAC;QACrD,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9D,IAAI,KAAK,GAAiB,QAAQ,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE;gBAC/D,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,IAAI,MAAM,CAAC,gBAAgB,CAAC;oBACxH,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;YAClF,IAAI,QAAQ,CAAC,WAAW;gBAAE,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YACvE,WAAW,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChF,CAAC;QACD,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,EAChH,cAAc,CAAC,KAAK,EAAE,IAAyB,EAAE,EAAE;YAC/C,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAuC,CAAC;YAC1F,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC/C,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;oBACtC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;wBAChE,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC9D,OAAO,UAAU,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC,CAAC;IACZ,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pilotswarm",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.64",
|
|
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",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"ink": "^6.8.0",
|
|
82
82
|
"jose": "^6.2.2",
|
|
83
83
|
"mermaid": "^11.16.0",
|
|
84
|
-
"pilotswarm-sdk": "0.5.
|
|
84
|
+
"pilotswarm-sdk": "0.5.64",
|
|
85
85
|
"react": "^19.2.4",
|
|
86
86
|
"react-dom": "^19.2.4",
|
|
87
87
|
"ws": "^8.18.2"
|
|
@@ -1553,6 +1553,19 @@ export class NodeSdkTransport {
|
|
|
1553
1553
|
return this.mgmt.listProviders(await this._modelProviderViewer());
|
|
1554
1554
|
}
|
|
1555
1555
|
|
|
1556
|
+
async listFeatureFlags() { return this.mgmt.listFeatureFlags(await this._modelProviderViewer()); }
|
|
1557
|
+
async getClusterFeatureFlags() { return this.mgmt.getClusterFeatureFlags(await this._modelProviderViewer()); }
|
|
1558
|
+
async getMyFeatureFlags() { return this.mgmt.getMyFeatureFlags(await this._modelProviderViewer()); }
|
|
1559
|
+
async getUserFeatureFlags(userId) { return this.mgmt.getUserFeatureFlags(await this._modelProviderViewer(), userId); }
|
|
1560
|
+
async setClusterFeatureFlag(input) { return this.mgmt.setClusterFeatureFlag(await this._modelProviderViewer(), input); }
|
|
1561
|
+
async resetClusterFeatureFlag(input) { return this.mgmt.resetClusterFeatureFlag(await this._modelProviderViewer(), input); }
|
|
1562
|
+
async setMyFeatureFlag(input) { return this.mgmt.setMyFeatureFlag(await this._modelProviderViewer(), input); }
|
|
1563
|
+
async unsetMyFeatureFlag(input) { return this.mgmt.unsetMyFeatureFlag(await this._modelProviderViewer(), input); }
|
|
1564
|
+
async setUserFeatureFlag(userId, input) { return this.mgmt.setUserFeatureFlag(await this._modelProviderViewer(), userId, input); }
|
|
1565
|
+
async unsetUserFeatureFlag(userId, input) { return this.mgmt.unsetUserFeatureFlag(await this._modelProviderViewer(), userId, input); }
|
|
1566
|
+
async listFeatureFlagChanges(limit) { return this.mgmt.listFeatureFlagChanges(await this._modelProviderViewer(), limit); }
|
|
1567
|
+
async listFeatureFlagUsers(query) { return this.mgmt.listFeatureFlagUsers(await this._modelProviderViewer(), query); }
|
|
1568
|
+
|
|
1556
1569
|
async getModelDefaults() {
|
|
1557
1570
|
return this.mgmt.getModelDefaults(await this._modelProviderViewer());
|
|
1558
1571
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyNativeTaskSnapshot } from "./native-tasks.js";
|
|
1
2
|
import { UI_COMMANDS, FOCUS_REGIONS, INSPECTOR_TABS, cycleValue } from "./commands.js";
|
|
2
3
|
import { BUDGET_SERIES_DAYS, BUDGET_SERIES_RANGES, canvasKey as canvasPrefKey } from "./state.js";
|
|
3
4
|
import { parseAgentSourceLink } from "./repo-links.js";
|
|
@@ -55,6 +56,23 @@ import {
|
|
|
55
56
|
import { findArtifactEntry } from "./state.js";
|
|
56
57
|
import { getTheme, listThemes } from "./themes/index.js";
|
|
57
58
|
|
|
59
|
+
function preserveNativeTaskProgress(history, current, sessionId) {
|
|
60
|
+
const retained = current?.nativeTaskSnapshot;
|
|
61
|
+
if (!retained?.ownerId) return history;
|
|
62
|
+
// Replay has reconstructed task rows from durable events. Reapply the
|
|
63
|
+
// latest live projection even when a paging rebuild carried its old
|
|
64
|
+
// metadata through; individual task revision/terminal guards still let
|
|
65
|
+
// newer durable truth win and prevent another owner from taking a row.
|
|
66
|
+
return applyNativeTaskSnapshot({ ...history, nativeTaskSnapshot: undefined }, {
|
|
67
|
+
version: 1,
|
|
68
|
+
...retained,
|
|
69
|
+
tasks: (current.chat || [])
|
|
70
|
+
.filter(item => item.kind === "native-task-group")
|
|
71
|
+
.flatMap(group => group.tasks)
|
|
72
|
+
.filter(task => task.ownerId === retained.ownerId),
|
|
73
|
+
}, { sessionId, seq: retained.liveSeq });
|
|
74
|
+
}
|
|
75
|
+
|
|
58
76
|
/**
|
|
59
77
|
* The copy selector for a package LIST row: which same-named copy it is.
|
|
60
78
|
* Scope shadowing means one name can be a shared package AND a user copy at
|
|
@@ -2523,6 +2541,8 @@ export class PilotSwarmUiController {
|
|
|
2523
2541
|
}
|
|
2524
2542
|
|
|
2525
2543
|
async stop() {
|
|
2544
|
+
this._featureRefreshSerial = (this._featureRefreshSerial || 0) + 1;
|
|
2545
|
+
this._featureUserSerial = (this._featureUserSerial || 0) + 1;
|
|
2526
2546
|
for (const timer of this._liveTurnIdleTimers?.values() || []) clearTimeout(timer);
|
|
2527
2547
|
this._liveTurnIdleTimers?.clear();
|
|
2528
2548
|
if (this.catalogTimer) clearInterval(this.catalogTimer);
|
|
@@ -2593,6 +2613,7 @@ export class PilotSwarmUiController {
|
|
|
2593
2613
|
// navigation-intent branch returns before the tail): the worker
|
|
2594
2614
|
// registry must refresh on every tick of the loop that provably runs.
|
|
2595
2615
|
this.refreshWorkerRegistryIfStale().catch(() => {});
|
|
2616
|
+
this.refreshFeatureFlagsIfStale().catch(() => {});
|
|
2596
2617
|
// Refreshes OVERLAP. The catalog loop ticks every 4s and a dozen
|
|
2597
2618
|
// actions call this directly, while one run awaits several sequential
|
|
2598
2619
|
// round-trips (N catalog pages, the folder list, up to two getSession
|
|
@@ -3003,6 +3024,7 @@ export class PilotSwarmUiController {
|
|
|
3003
3024
|
}
|
|
3004
3025
|
}
|
|
3005
3026
|
}
|
|
3027
|
+
history = preserveNativeTaskProgress(history, current, sessionId);
|
|
3006
3028
|
this.dispatch({
|
|
3007
3029
|
type: "history/set",
|
|
3008
3030
|
sessionId,
|
|
@@ -3237,6 +3259,7 @@ export class PilotSwarmUiController {
|
|
|
3237
3259
|
if (this.getState().ui?.budgetOpen) this.closeBudget();
|
|
3238
3260
|
this.dispatch({ type: "admin/visibility", visible: true });
|
|
3239
3261
|
await this.refreshAdminProfile().catch(() => {});
|
|
3262
|
+
if (this.getState().admin?.section === "features") void this.refreshFeatureFlags();
|
|
3240
3263
|
void this.refreshAdminModelProviders().catch(() => {});
|
|
3241
3264
|
void this.refreshAdminAgentPackages().catch(() => {});
|
|
3242
3265
|
}
|
|
@@ -3707,6 +3730,7 @@ export class PilotSwarmUiController {
|
|
|
3707
3730
|
|
|
3708
3731
|
setAdminSection(section) {
|
|
3709
3732
|
this.dispatch({ type: "admin/section", section });
|
|
3733
|
+
if (section === "features") void this.refreshFeatureFlags();
|
|
3710
3734
|
if (section === "providers") {
|
|
3711
3735
|
const providers = this.getState().admin?.modelProviders;
|
|
3712
3736
|
if (!providers?.fetchedAt) void this.refreshAdminModelProviders().catch(() => {});
|
|
@@ -3722,6 +3746,251 @@ export class PilotSwarmUiController {
|
|
|
3722
3746
|
}
|
|
3723
3747
|
}
|
|
3724
3748
|
|
|
3749
|
+
async selectFeatureScope(mode, userId = null) {
|
|
3750
|
+
if (!["mine", "cluster", "users"].includes(mode)) return;
|
|
3751
|
+
if (this.getState().admin?.features?.saving || mode !== "mine" && !this._featureIsAdmin()) return;
|
|
3752
|
+
if (mode === "users" && userId !== null && (!Number.isSafeInteger(userId) || userId <= 0)) return;
|
|
3753
|
+
this.dispatch({ type: "admin/features", patch: { mode, userId: mode === "users" ? userId : null,
|
|
3754
|
+
data: null, error: null, notice: null, fetchedAt: null, dirtyDrafts: {} } });
|
|
3755
|
+
await this.refreshFeatureFlags();
|
|
3756
|
+
}
|
|
3757
|
+
|
|
3758
|
+
_featureIsAdmin() {
|
|
3759
|
+
const state = this.getState();
|
|
3760
|
+
const role = state.auth?.authorization?.role;
|
|
3761
|
+
return state.admin?.profile?.isAdmin === true && (!role || role === "admin" || role === "anonymous");
|
|
3762
|
+
}
|
|
3763
|
+
|
|
3764
|
+
_featureContext() {
|
|
3765
|
+
const state = this.getState();
|
|
3766
|
+
const features = state.admin.features;
|
|
3767
|
+
return JSON.stringify([features.generation, features.mode, features.userId,
|
|
3768
|
+
state.auth?.principal?.provider, state.auth?.principal?.subject, state.auth?.authorization?.role,
|
|
3769
|
+
state.admin.profile?.provider, state.admin.profile?.subject, state.admin.profile?.isAdmin]);
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
async _featureRequest(read) {
|
|
3773
|
+
let timeout;
|
|
3774
|
+
try {
|
|
3775
|
+
return await Promise.race([Promise.resolve().then(read), new Promise((_, reject) => {
|
|
3776
|
+
timeout = setTimeout(() => reject(new Error("Feature request timed out. Retry or refresh the settings.")), 10_000);
|
|
3777
|
+
timeout?.unref?.();
|
|
3778
|
+
})]);
|
|
3779
|
+
} finally { clearTimeout(timeout); }
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3782
|
+
async refreshFeatureFlagsIfStale() {
|
|
3783
|
+
const admin = this.getState().admin;
|
|
3784
|
+
const features = admin?.features;
|
|
3785
|
+
if (!admin?.visible || admin.section !== "features" || features?.loading || features?.saving
|
|
3786
|
+
|| Object.keys(features?.dirtyDrafts || {}).length
|
|
3787
|
+
|| features?.fetchedAt && Date.now() - features.fetchedAt < 20_000) return;
|
|
3788
|
+
await this.refreshFeatureFlags({ background: true });
|
|
3789
|
+
}
|
|
3790
|
+
|
|
3791
|
+
setFeatureDraftDirty(featureKey, dirty) {
|
|
3792
|
+
const dirtyDrafts = { ...this.getState().admin.features.dirtyDrafts };
|
|
3793
|
+
if (dirty) dirtyDrafts[featureKey] = true;
|
|
3794
|
+
else delete dirtyDrafts[featureKey];
|
|
3795
|
+
this.dispatch({ type: "admin/features", patch: { dirtyDrafts } });
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
_featureDraftKey(featureKey) {
|
|
3799
|
+
const features = this.getState().admin.features;
|
|
3800
|
+
return JSON.stringify([features.mode, features.mode === "users" ? features.userId : null, featureKey]);
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
getFeatureDraft(featureKey) {
|
|
3804
|
+
return this.getState().admin.features.drafts?.[this._featureDraftKey(featureKey)];
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
getFeatureDraftCount(mode) {
|
|
3808
|
+
return Object.values(this.getState().admin.features.drafts || {}).filter(draft => draft.mode === mode).length;
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
setFeatureDraft(featureKey, values) {
|
|
3812
|
+
const features = this.getState().admin.features;
|
|
3813
|
+
if (features.saving || features.mode !== "mine" && !this._featureIsAdmin()
|
|
3814
|
+
|| features.mode === "users" && !features.userId) return;
|
|
3815
|
+
const flag = features.data?.flags?.find(item => item.featureKey === featureKey);
|
|
3816
|
+
if (!flag?.supported) return;
|
|
3817
|
+
const key = this._featureDraftKey(featureKey);
|
|
3818
|
+
const existing = features.drafts?.[key];
|
|
3819
|
+
const stored = features.mode === "cluster" ? flag.cluster : flag.user;
|
|
3820
|
+
// Null is an operation (remove the override), not merely a pair of
|
|
3821
|
+
// values equal to today's defaults. Keep reset intent when a row exists.
|
|
3822
|
+
const equal = values === null ? !stored : features.mode === "cluster"
|
|
3823
|
+
? values.enabled === (flag.cluster?.enabled ?? flag.defaultEnabled)
|
|
3824
|
+
&& values.allowUserOverride === (flag.cluster?.allowUserOverride ?? flag.defaultAllowUserOverride)
|
|
3825
|
+
: Boolean(stored) && values.enabled === stored.enabled;
|
|
3826
|
+
const drafts = { ...features.drafts };
|
|
3827
|
+
if (equal && !existing?.uncertain && !existing?.needsReview) delete drafts[key];
|
|
3828
|
+
else drafts[key] = { featureKey, mode: features.mode, userId: features.userId,
|
|
3829
|
+
values: values === null ? null : { ...values }, expectedRevision: existing?.expectedRevision ?? flag.revision,
|
|
3830
|
+
...(existing?.uncertain ? { uncertain: true } : {}), ...(existing?.needsReview ? { needsReview: true } : {}) };
|
|
3831
|
+
this.dispatch({ type: "admin/features", patch: { drafts, error: null, notice: null } });
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
async discardFeatureDraft(featureKey) {
|
|
3835
|
+
if (this.getState().admin.features.saving) return;
|
|
3836
|
+
const draft = this.getFeatureDraft(featureKey);
|
|
3837
|
+
const context = this._featureContext();
|
|
3838
|
+
// A lost response may hide a committed save. Reconcile it before
|
|
3839
|
+
// discarding so the old cached value cannot look authoritative.
|
|
3840
|
+
if (draft?.uncertain && !await this.refreshFeatureFlags()) return;
|
|
3841
|
+
if (context !== this._featureContext() || this.getFeatureDraft(featureKey) !== draft) return;
|
|
3842
|
+
if (draft?.uncertain) {
|
|
3843
|
+
const revision = this.getState().admin.features.data?.flags?.find(flag => flag.featureKey === featureKey)?.revision;
|
|
3844
|
+
// A GET at the old revision cannot rule out the timed-out write
|
|
3845
|
+
// committing later. A newer revision fences its compare-and-set.
|
|
3846
|
+
if (!revision || BigInt(revision) <= BigInt(draft.expectedRevision)) {
|
|
3847
|
+
this.dispatch({ type: "admin/features", patch: { error: "The earlier save is still unconfirmed. Retry Save or refresh before discarding changes." } });
|
|
3848
|
+
return;
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
const drafts = { ...this.getState().admin.features.drafts };
|
|
3852
|
+
delete drafts[this._featureDraftKey(featureKey)];
|
|
3853
|
+
this.dispatch({ type: "admin/features", patch: { drafts, error: null, notice: null } });
|
|
3854
|
+
}
|
|
3855
|
+
|
|
3856
|
+
reviewFeatureDraft(featureKey) {
|
|
3857
|
+
const features = this.getState().admin.features;
|
|
3858
|
+
if (features.saving || features.loading || features.mode !== "mine" && !this._featureIsAdmin()) return;
|
|
3859
|
+
const key = this._featureDraftKey(featureKey);
|
|
3860
|
+
const draft = features.drafts?.[key];
|
|
3861
|
+
const flag = features.data?.flags?.find(item => item.featureKey === featureKey);
|
|
3862
|
+
if (!draft || !flag?.supported || draft.needsReview && draft.expectedRevision === flag.revision) return;
|
|
3863
|
+
this.dispatch({ type: "admin/features", patch: { drafts: { ...features.drafts,
|
|
3864
|
+
[key]: { ...draft, expectedRevision: flag.revision, needsReview: false, uncertain: false } }, error: null, notice: "Latest saved setting reviewed. Your changes are still unsaved." } });
|
|
3865
|
+
}
|
|
3866
|
+
|
|
3867
|
+
async saveFeatureDraft(featureKey) {
|
|
3868
|
+
const draft = this.getFeatureDraft(featureKey);
|
|
3869
|
+
if (!draft) return;
|
|
3870
|
+
const flag = this.getState().admin.features.data?.flags?.find(item => item.featureKey === featureKey);
|
|
3871
|
+
if (draft.needsReview || draft.expectedRevision !== flag?.revision) {
|
|
3872
|
+
this.dispatch({ type: "admin/features", patch: { error: "Settings changed elsewhere. Review the latest saved setting before saving your changes." } });
|
|
3873
|
+
return;
|
|
3874
|
+
}
|
|
3875
|
+
await this.saveFeatureFlag(featureKey, draft.values, { draft });
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
async searchFeatureUsers(query = "") {
|
|
3879
|
+
if (!this._featureIsAdmin()) return;
|
|
3880
|
+
const serial = this._featureUserSerial = (this._featureUserSerial || 0) + 1;
|
|
3881
|
+
const context = this._featureContext();
|
|
3882
|
+
this.dispatch({ type: "admin/features", patch: { userQuery: String(query), usersLoading: true, metadataError: null } });
|
|
3883
|
+
const current = () => this._featureUserSerial === serial && this._featureContext() === context;
|
|
3884
|
+
try {
|
|
3885
|
+
const users = await this._featureRequest(() => this.transport.listFeatureFlagUsers(String(query)));
|
|
3886
|
+
if (current()) {
|
|
3887
|
+
const features = this.getState().admin.features;
|
|
3888
|
+
const selected = features.users?.find(user => user.userId === features.userId);
|
|
3889
|
+
const selectedUserOutsideResults = Boolean(selected && !users.some(user => user.userId === selected.userId));
|
|
3890
|
+
// Searching must not remove the identity/option of the user
|
|
3891
|
+
// whose saved policy and draft remain active below the picker.
|
|
3892
|
+
this.dispatch({ type: "admin/features", patch: {
|
|
3893
|
+
users: selectedUserOutsideResults ? [selected, ...users] : users,
|
|
3894
|
+
selectedUserOutsideResults, usersLoading: false,
|
|
3895
|
+
} });
|
|
3896
|
+
}
|
|
3897
|
+
} catch (error) {
|
|
3898
|
+
if (current()) this.dispatch({ type: "admin/features", patch: { usersLoading: false,
|
|
3899
|
+
metadataError: `User directory: ${error?.message || error}` } });
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
async refreshFeatureFlags({ background = false } = {}) {
|
|
3904
|
+
const serial = (this._featureRefreshSerial ?? 0) + 1;
|
|
3905
|
+
this._featureRefreshSerial = serial;
|
|
3906
|
+
const features = this.getState().admin?.features || { mode: "mine" };
|
|
3907
|
+
if (features.mode !== "mine" && !this._featureIsAdmin()) return;
|
|
3908
|
+
const context = this._featureContext();
|
|
3909
|
+
const current = () => this._featureRefreshSerial === serial && this._featureContext() === context;
|
|
3910
|
+
this.dispatch({ type: "admin/features", patch: { loading: true, ...(background ? {} : { error: null }) } });
|
|
3911
|
+
// Directory/adoption are independent diagnostics: neither may block policy.
|
|
3912
|
+
if (!background && this._featureIsAdmin()) {
|
|
3913
|
+
void this.searchFeatureUsers(features.userQuery || "");
|
|
3914
|
+
void this.refreshWorkerRegistryIfStale();
|
|
3915
|
+
}
|
|
3916
|
+
try {
|
|
3917
|
+
const data = await this._featureRequest(() => features.mode === "cluster" ? this.transport.getClusterFeatureFlags()
|
|
3918
|
+
: features.mode === "users" ? features.userId ? this.transport.getUserFeatureFlags(features.userId) : null
|
|
3919
|
+
: this.transport.getMyFeatureFlags());
|
|
3920
|
+
if (!current()) return false;
|
|
3921
|
+
this.dispatch({ type: "admin/features", patch: { data, loading: false, fetchedAt: Date.now() } });
|
|
3922
|
+
return true;
|
|
3923
|
+
} catch (error) {
|
|
3924
|
+
if (current()) this.dispatch({ type: "admin/features", patch: { loading: false, fetchedAt: Date.now(), error: String(error?.message || error) } });
|
|
3925
|
+
return false;
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
|
|
3929
|
+
async saveFeatureFlag(featureKey, values, { draft } = {}) {
|
|
3930
|
+
const features = this.getState().admin?.features;
|
|
3931
|
+
if (!features || features.saving || features.loading || features.mode !== "mine" && !this._featureIsAdmin()) return;
|
|
3932
|
+
const definition = features.data?.flags?.find(flag => flag.featureKey === featureKey);
|
|
3933
|
+
if (!definition?.supported || features.mode === "users" && !features.userId) return;
|
|
3934
|
+
const target = this._featureContext();
|
|
3935
|
+
const draftKey = this._featureDraftKey(featureKey);
|
|
3936
|
+
const expectedRevision = draft?.expectedRevision ?? definition.revision;
|
|
3937
|
+
const signature = JSON.stringify([target, featureKey, expectedRevision, values]);
|
|
3938
|
+
this._featureMutationRetries ??= new Map();
|
|
3939
|
+
const requestId = this._featureMutationRetries.get(signature) ?? globalThis.crypto.randomUUID();
|
|
3940
|
+
this._featureMutationRetries.set(signature, requestId);
|
|
3941
|
+
const input = { featureKey, expectedRevision, requestId, ...(values || {}) };
|
|
3942
|
+
this.dispatch({ type: "admin/features", patch: { saving: true, error: null, notice: null } });
|
|
3943
|
+
try {
|
|
3944
|
+
const outcome = await this._featureRequest(() => {
|
|
3945
|
+
if (features.mode === "cluster") return this.transport[values ? "setClusterFeatureFlag" : "resetClusterFeatureFlag"](input);
|
|
3946
|
+
if (features.mode === "users") return this.transport[values ? "setUserFeatureFlag" : "unsetUserFeatureFlag"](features.userId, input);
|
|
3947
|
+
return this.transport[values ? "setMyFeatureFlag" : "unsetMyFeatureFlag"](input);
|
|
3948
|
+
});
|
|
3949
|
+
this._featureMutationRetries.delete(signature);
|
|
3950
|
+
if (this._featureContext() === target) {
|
|
3951
|
+
// The acknowledged mutation is authoritative even if the
|
|
3952
|
+
// following GET fails. Do not show the old value as saved.
|
|
3953
|
+
if (/^[1-9]\d*$/.test(String(outcome?.revision)) && Object.hasOwn(outcome, "setting")) {
|
|
3954
|
+
const updated = { ...definition, revision: outcome.revision,
|
|
3955
|
+
[features.mode === "cluster" ? "cluster" : "user"]: outcome.setting };
|
|
3956
|
+
const allowUser = updated.cluster?.allowUserOverride ?? updated.defaultAllowUserOverride;
|
|
3957
|
+
updated.effective = allowUser && updated.user ? updated.user.enabled
|
|
3958
|
+
: updated.cluster?.enabled ?? updated.defaultEnabled;
|
|
3959
|
+
updated.source = allowUser && updated.user ? "user" : updated.cluster ? "cluster" : "default";
|
|
3960
|
+
updated.userOverrideIgnored = Boolean(updated.user && !allowUser);
|
|
3961
|
+
const data = this.getState().admin.features.data;
|
|
3962
|
+
this.dispatch({ type: "admin/features", patch: { data: { ...data,
|
|
3963
|
+
flags: data.flags.map(flag => flag.featureKey === featureKey ? updated : flag) } } });
|
|
3964
|
+
}
|
|
3965
|
+
if (draft && this.getState().admin.features.drafts?.[draftKey] === draft) {
|
|
3966
|
+
const drafts = { ...this.getState().admin.features.drafts };
|
|
3967
|
+
delete drafts[draftKey];
|
|
3968
|
+
this.dispatch({ type: "admin/features", patch: { drafts } });
|
|
3969
|
+
}
|
|
3970
|
+
await this.refreshFeatureFlags();
|
|
3971
|
+
if (this._featureContext() === target) this.dispatch({ type: "admin/features", patch: { notice: "Setting saved." } });
|
|
3972
|
+
}
|
|
3973
|
+
} catch (error) {
|
|
3974
|
+
if (this._featureContext() === target) {
|
|
3975
|
+
if (draft && (error?.status === 409 || error?.code === "FEATURE_CONFLICT")) {
|
|
3976
|
+
this._featureMutationRetries.delete(signature);
|
|
3977
|
+
this.dispatch({ type: "admin/features", patch: { drafts: { ...this.getState().admin.features.drafts,
|
|
3978
|
+
[draftKey]: { ...draft, needsReview: true } } } });
|
|
3979
|
+
const refreshed = await this.refreshFeatureFlags({ background: true });
|
|
3980
|
+
if (this._featureContext() === target) this.dispatch({ type: "admin/features", patch: {
|
|
3981
|
+
error: refreshed ? "Settings changed elsewhere. Review the latest saved setting before saving your changes."
|
|
3982
|
+
: "Settings changed elsewhere, but the latest setting could not be loaded. Refresh settings before reviewing and saving your changes." } });
|
|
3983
|
+
return;
|
|
3984
|
+
}
|
|
3985
|
+
if (draft && !(error?.status >= 400 && error?.status < 500)) {
|
|
3986
|
+
this.dispatch({ type: "admin/features", patch: { drafts: { ...this.getState().admin.features.drafts,
|
|
3987
|
+
[draftKey]: { ...draft, uncertain: true } } } });
|
|
3988
|
+
}
|
|
3989
|
+
this.dispatch({ type: "admin/features", patch: { error: String(error?.message || error) } });
|
|
3990
|
+
}
|
|
3991
|
+
} finally { if (this._featureContext() === target) this.dispatch({ type: "admin/features", patch: { saving: false } }); }
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3725
3994
|
/** Node Map: select a node (toggles off when re-selected). Scopes Activity. */
|
|
3726
3995
|
selectNodeMapNode(label) {
|
|
3727
3996
|
this.dispatch({ type: "ui/nodeMapSelect", label: label ? String(label) : null });
|
|
@@ -5784,6 +6053,12 @@ export class PilotSwarmUiController {
|
|
|
5784
6053
|
// history would corrupt the replay cursor — and they must never
|
|
5785
6054
|
// appear in the transcript.
|
|
5786
6055
|
if (event.transient === true) {
|
|
6056
|
+
if (event.eventType === "session.native_tasks_tick") {
|
|
6057
|
+
const existing = this.getState().history.bySessionId.get(sessionId);
|
|
6058
|
+
const history = applyNativeTaskSnapshot(existing, event.data, { sessionId, seq: event.liveSeq });
|
|
6059
|
+
if (history && history !== existing) this.dispatch({ type: "history/set", sessionId, history });
|
|
6060
|
+
return true;
|
|
6061
|
+
}
|
|
5787
6062
|
if (event.eventType === "assistant.live_tick") {
|
|
5788
6063
|
// A worker can die leaving a reasoning-only retained row.
|
|
5789
6064
|
// On re-entry its message ID cannot match a durable final;
|
|
@@ -5838,10 +6113,11 @@ export class PilotSwarmUiController {
|
|
|
5838
6113
|
}
|
|
5839
6114
|
const existing = this.getState().history.bySessionId.get(sessionId)
|
|
5840
6115
|
|| { chat: [], activity: [], events: [], lastSeq: 0 };
|
|
6116
|
+
const liveHistory = this.preservePausedChatWindow(sessionId, existing);
|
|
5841
6117
|
this.dispatch({
|
|
5842
6118
|
type: "history/set",
|
|
5843
6119
|
sessionId,
|
|
5844
|
-
history: applyLiveTurnToHistory(
|
|
6120
|
+
history: applyLiveTurnToHistory(liveHistory, event.data, {
|
|
5845
6121
|
sessionId,
|
|
5846
6122
|
seq: event.liveSeq,
|
|
5847
6123
|
createdAt: Date.now(),
|
|
@@ -5878,7 +6154,10 @@ export class PilotSwarmUiController {
|
|
|
5878
6154
|
this._liveTurnIdleTimers.delete(sessionId);
|
|
5879
6155
|
}
|
|
5880
6156
|
const state = this.getState();
|
|
5881
|
-
const existing =
|
|
6157
|
+
const existing = this.preservePausedChatWindow(
|
|
6158
|
+
sessionId,
|
|
6159
|
+
state.history.bySessionId.get(sessionId) || { chat: [], activity: [], events: [], lastSeq: 0 },
|
|
6160
|
+
);
|
|
5882
6161
|
if (event.seq <= (existing.lastSeq || 0)) return false;
|
|
5883
6162
|
this.dispatch({
|
|
5884
6163
|
type: "history/set",
|
|
@@ -5979,6 +6258,31 @@ export class PilotSwarmUiController {
|
|
|
5979
6258
|
return true;
|
|
5980
6259
|
}
|
|
5981
6260
|
|
|
6261
|
+
/**
|
|
6262
|
+
* Do not evict the first rendered item while its active chat is paused.
|
|
6263
|
+
* A fixed-size history window normally drops one old item for each live
|
|
6264
|
+
* append. Keeping the same numeric scrollTop then moves the actual text the
|
|
6265
|
+
* user is reading. Grow the in-memory window just enough for arrivals made
|
|
6266
|
+
* during that pause; normal bottom-follow appends resume bounded trimming.
|
|
6267
|
+
*/
|
|
6268
|
+
preservePausedChatWindow(sessionId, history) {
|
|
6269
|
+
const state = this.getState();
|
|
6270
|
+
if (state.sessions.activeSessionId !== sessionId || state.ui.followBottom?.chat !== false) {
|
|
6271
|
+
return history;
|
|
6272
|
+
}
|
|
6273
|
+
const currentLimit = Math.max(
|
|
6274
|
+
DEFAULT_HISTORY_EVENT_LIMIT,
|
|
6275
|
+
Number(history?.loadedEventLimit ?? DEFAULT_HISTORY_EVENT_LIMIT) || DEFAULT_HISTORY_EVENT_LIMIT,
|
|
6276
|
+
);
|
|
6277
|
+
const requiredLimit = Math.max(
|
|
6278
|
+
currentLimit,
|
|
6279
|
+
(Array.isArray(history?.events) ? history.events.length : 0) + 1,
|
|
6280
|
+
(Array.isArray(history?.chat) ? history.chat.length : 0) + 4,
|
|
6281
|
+
(Array.isArray(history?.activity) ? history.activity.length : 0) + 1,
|
|
6282
|
+
);
|
|
6283
|
+
return requiredLimit === currentLimit ? history : { ...history, loadedEventLimit: requiredLimit };
|
|
6284
|
+
}
|
|
6285
|
+
|
|
5982
6286
|
async syncSessionEvents(sessionId) {
|
|
5983
6287
|
if (!sessionId || typeof this.transport.getSessionEvents !== "function") return;
|
|
5984
6288
|
const existing = this.getState().history.bySessionId.get(sessionId);
|
|
@@ -9178,6 +9482,7 @@ export class PilotSwarmUiController {
|
|
|
9178
9482
|
}
|
|
9179
9483
|
|
|
9180
9484
|
paneUsesStickyBottomFollow(pane, state = this.getState()) {
|
|
9485
|
+
if (pane === "chat") return true;
|
|
9181
9486
|
if (pane === "activity") return true;
|
|
9182
9487
|
if (pane === "inspector") return state.ui.inspectorTab === "logs";
|
|
9183
9488
|
return false;
|
|
@@ -9251,6 +9556,15 @@ export class PilotSwarmUiController {
|
|
|
9251
9556
|
const current = this.getPaneVisualScrollOffset(pane, state);
|
|
9252
9557
|
const nextOffset = Math.max(0, Math.min(current - delta, maxOffset));
|
|
9253
9558
|
this.applyPaneVisualScrollOffset(pane, nextOffset, { followBottom: nextOffset >= maxOffset }, state);
|
|
9559
|
+
if (pane === "chat" && delta > 0) {
|
|
9560
|
+
if (current <= 0) {
|
|
9561
|
+
this.handleChatTopHistoryScrollIntent(nextOffset).catch(() => {});
|
|
9562
|
+
} else if (nextOffset <= 0) {
|
|
9563
|
+
this.armChatTopHistoryLoad();
|
|
9564
|
+
}
|
|
9565
|
+
} else if (pane === "chat" && delta < 0) {
|
|
9566
|
+
this.disarmChatTopHistoryLoad();
|
|
9567
|
+
}
|
|
9254
9568
|
return;
|
|
9255
9569
|
}
|
|
9256
9570
|
const current = Math.max(0, Math.min(Number(state.ui.scroll?.[pane]) || 0, maxOffset));
|
|
@@ -9273,6 +9587,10 @@ export class PilotSwarmUiController {
|
|
|
9273
9587
|
const nextOffset = Math.max(0, Math.min(Number(offset) || 0, maxOffset));
|
|
9274
9588
|
if (this.paneUsesStickyBottomFollow(pane, state)) {
|
|
9275
9589
|
this.applyPaneVisualScrollOffset(pane, nextOffset, { followBottom: nextOffset >= maxOffset }, state);
|
|
9590
|
+
if (pane === "chat") {
|
|
9591
|
+
if (maxOffset > 0 && nextOffset <= 0) this.armChatTopHistoryLoad();
|
|
9592
|
+
else this.disarmChatTopHistoryLoad();
|
|
9593
|
+
}
|
|
9276
9594
|
return;
|
|
9277
9595
|
}
|
|
9278
9596
|
this.dispatch({ type: "ui/scroll", pane, offset: nextOffset });
|
|
@@ -9308,6 +9626,7 @@ export class PilotSwarmUiController {
|
|
|
9308
9626
|
|
|
9309
9627
|
await this.maybeAutoExpandActiveHistory(requestedScrollOffset, {
|
|
9310
9628
|
pages: AUTO_HISTORY_SCROLL_PAGE_COUNT,
|
|
9629
|
+
preserveDomAnchor: options.preserveDomAnchor === true,
|
|
9311
9630
|
// Always bypass the offset gate. Every caller of this method fires
|
|
9312
9631
|
// only when the DOM scroller is already AT the top — a direct
|
|
9313
9632
|
// measurement. The gate is a second, weaker guess that compares a
|
|
@@ -9369,7 +9688,7 @@ export class PilotSwarmUiController {
|
|
|
9369
9688
|
const pane = this.getScrollablePaneForFocus();
|
|
9370
9689
|
if (!pane) return;
|
|
9371
9690
|
if (this.paneUsesStickyBottomFollow(pane)) {
|
|
9372
|
-
this.
|
|
9691
|
+
this.scrollPaneTo(pane, 0);
|
|
9373
9692
|
return;
|
|
9374
9693
|
}
|
|
9375
9694
|
const inspectorUsesBottomScroll = pane === "inspector" && this.inspectorUsesBottomScroll();
|
|
@@ -9649,6 +9968,7 @@ export class PilotSwarmUiController {
|
|
|
9649
9968
|
requestedScrollOffset: targetOffset,
|
|
9650
9969
|
autoTriggered: true,
|
|
9651
9970
|
pages: options.pages,
|
|
9971
|
+
preserveDomAnchor: options.preserveDomAnchor === true,
|
|
9652
9972
|
// Chat-driven pull: page backward over renderable message types
|
|
9653
9973
|
// only, so each page is transcript instead of raw event noise.
|
|
9654
9974
|
eventTypes: CHAT_HISTORY_EVENT_TYPES,
|
|
@@ -9766,6 +10086,7 @@ export class PilotSwarmUiController {
|
|
|
9766
10086
|
lastSeq: events[events.length - 1]?.seq || 0,
|
|
9767
10087
|
};
|
|
9768
10088
|
}
|
|
10089
|
+
history = preserveNativeTaskProgress(history, this.getState().history.bySessionId.get(sessionId), sessionId);
|
|
9769
10090
|
this.dispatch({
|
|
9770
10091
|
type: "history/set",
|
|
9771
10092
|
sessionId,
|
|
@@ -9785,9 +10106,12 @@ export class PilotSwarmUiController {
|
|
|
9785
10106
|
pane: "chat",
|
|
9786
10107
|
offset: 0,
|
|
9787
10108
|
});
|
|
9788
|
-
} else if (preserveChatView &&
|
|
9789
|
-
//
|
|
9790
|
-
//
|
|
10109
|
+
} else if (preserveChatView && options.autoTriggered && !options.preserveDomAnchor) {
|
|
10110
|
+
// Terminal panes store paused chat as a top offset. Prepending
|
|
10111
|
+
// must advance that offset by the rendered rows inserted above
|
|
10112
|
+
// it, preserving the same visible message. Browser panes use
|
|
10113
|
+
// measured DOM height because rich cards and wrapping do not
|
|
10114
|
+
// match terminal row metrics.
|
|
9791
10115
|
const nextState = this.getState();
|
|
9792
10116
|
const nextRenderedLines = this.getActiveChatRenderMetrics(nextState).totalLines;
|
|
9793
10117
|
const addedLines = Math.max(0, nextRenderedLines - previousRenderedLines);
|
|
@@ -9799,14 +10123,6 @@ export class PilotSwarmUiController {
|
|
|
9799
10123
|
});
|
|
9800
10124
|
}
|
|
9801
10125
|
}
|
|
9802
|
-
// AUTO-triggered (scrolling reached the top): change NOTHING. The
|
|
9803
|
-
// offset is distance-from-BOTTOM, which prepended content cannot
|
|
9804
|
-
// move — leaving it alone keeps the viewport glued to the exact
|
|
9805
|
-
// messages the user was reading, and they scroll on into the new
|
|
9806
|
-
// page naturally. Adding the delta here teleported the view to
|
|
9807
|
-
// the TOP of a 3.3x-larger window — observed as "loading history
|
|
9808
|
-
// takes you way back instead of a few pages up".
|
|
9809
|
-
|
|
9810
10126
|
const stateLabel = history.hasOlderEvents
|
|
9811
10127
|
? options.autoTriggered
|
|
9812
10128
|
? `Loaded older history page from CMS (${history.loadedEventCount} events loaded)`
|