@wrongstack/webui 0.274.0 → 0.275.0
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/dist/assets/index-BG4jUAmc.js +141 -0
- package/dist/assets/index-Cw32ELnp.css +2 -0
- package/dist/assets/{vendor-CzID01pz.js → vendor-Cl_sFcw4.js} +255 -255
- package/dist/index.html +3 -3
- package/dist/index.js +8251 -5753
- package/dist/index.js.map +1 -1
- package/dist/server/entry.js +1134 -169
- package/dist/server/entry.js.map +1 -1
- package/dist/server/handlers.js.map +1 -1
- package/dist/server/index.d.ts +133 -4
- package/dist/server/index.js +1148 -170
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +261 -4
- package/package.json +6 -6
- package/dist/assets/index-CwMm5VgW.js +0 -140
- package/dist/assets/index-DqD59GFW.css +0 -2
package/dist/server/index.js
CHANGED
|
@@ -170,9 +170,12 @@ var BOOLEAN_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
|
170
170
|
"reasoningPreserve",
|
|
171
171
|
"hqEnabled",
|
|
172
172
|
"hqRawContent",
|
|
173
|
-
"fallbackAuto"
|
|
173
|
+
"fallbackAuto",
|
|
174
|
+
"favoriteModelsOnly"
|
|
174
175
|
]);
|
|
175
|
-
var STRING_ARRAY_PREF_KEYS = /* @__PURE__ */ new Set(["fallbackModels"]);
|
|
176
|
+
var STRING_ARRAY_PREF_KEYS = /* @__PURE__ */ new Set(["fallbackModels", "favoriteModels"]);
|
|
177
|
+
var STRING_ARRAY_RECORD_PREF_KEYS = /* @__PURE__ */ new Set(["fallbackProfiles"]);
|
|
178
|
+
var MODEL_MATRIX_PREF_KEYS = /* @__PURE__ */ new Set(["modelMatrix"]);
|
|
176
179
|
var NUMBER_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
177
180
|
"autonomyDelayMs",
|
|
178
181
|
"autoProceedMaxIterations",
|
|
@@ -207,6 +210,33 @@ function validatePreferenceValue(key, value) {
|
|
|
207
210
|
if (STRING_ARRAY_PREF_KEYS.has(key)) {
|
|
208
211
|
return Array.isArray(value) && value.every((v) => typeof v === "string") ? null : `prefs.update payload.${key} must be an array of strings`;
|
|
209
212
|
}
|
|
213
|
+
if (STRING_ARRAY_RECORD_PREF_KEYS.has(key)) {
|
|
214
|
+
return isRecord(value) && Object.values(value).every(
|
|
215
|
+
(v) => Array.isArray(v) && v.every((item) => typeof item === "string")
|
|
216
|
+
) ? null : `prefs.update payload.${key} must be an object of string arrays`;
|
|
217
|
+
}
|
|
218
|
+
if (MODEL_MATRIX_PREF_KEYS.has(key)) {
|
|
219
|
+
if (!isRecord(value)) return `prefs.update payload.${key} must be an object`;
|
|
220
|
+
for (const entry of Object.values(value)) {
|
|
221
|
+
if (!isRecord(entry)) return `prefs.update payload.${key} entries must be objects`;
|
|
222
|
+
const provider = entry["provider"];
|
|
223
|
+
const model = entry["model"];
|
|
224
|
+
const fallbackProfile = entry["fallbackProfile"];
|
|
225
|
+
if (provider !== void 0 && typeof provider !== "string") {
|
|
226
|
+
return `prefs.update payload.${key}.provider must be a string when provided`;
|
|
227
|
+
}
|
|
228
|
+
if (model !== void 0 && typeof model !== "string") {
|
|
229
|
+
return `prefs.update payload.${key}.model must be a string when provided`;
|
|
230
|
+
}
|
|
231
|
+
if (fallbackProfile !== void 0 && typeof fallbackProfile !== "string") {
|
|
232
|
+
return `prefs.update payload.${key}.fallbackProfile must be a string when provided`;
|
|
233
|
+
}
|
|
234
|
+
if (model === void 0 && fallbackProfile === void 0) {
|
|
235
|
+
return `prefs.update payload.${key} entries require model or fallbackProfile`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
210
240
|
const allowed = ENUM_PREF_KEYS[key];
|
|
211
241
|
if (allowed) {
|
|
212
242
|
return typeof value === "string" && allowed.has(value) ? null : `prefs.update payload.${key} must be one of: ${Array.from(allowed).join(", ")}`;
|
|
@@ -435,8 +465,8 @@ function validateShellOpenPayload(payload) {
|
|
|
435
465
|
if (!isRecord(payload)) {
|
|
436
466
|
return { ok: false, message: "shell.open payload must be an object with string path" };
|
|
437
467
|
}
|
|
438
|
-
const
|
|
439
|
-
if (typeof
|
|
468
|
+
const path18 = payload["path"];
|
|
469
|
+
if (typeof path18 !== "string" || path18.trim().length === 0) {
|
|
440
470
|
return { ok: false, message: "shell.open payload.path must be a non-empty string" };
|
|
441
471
|
}
|
|
442
472
|
const target = payload["target"];
|
|
@@ -446,20 +476,20 @@ function validateShellOpenPayload(payload) {
|
|
|
446
476
|
message: 'shell.open payload.target must be "file" or "terminal" when provided'
|
|
447
477
|
};
|
|
448
478
|
}
|
|
449
|
-
return { ok: true, value: { path:
|
|
479
|
+
return { ok: true, value: { path: path18, target } };
|
|
450
480
|
}
|
|
451
481
|
function validateGitDiffPayload(payload) {
|
|
452
482
|
if (!isRecord(payload)) {
|
|
453
483
|
return { ok: false, message: "git.diff payload must be an object" };
|
|
454
484
|
}
|
|
455
|
-
const
|
|
456
|
-
if (
|
|
485
|
+
const path18 = payload["path"];
|
|
486
|
+
if (path18 === void 0 || path18 === null) {
|
|
457
487
|
return { ok: true, value: { path: "" } };
|
|
458
488
|
}
|
|
459
|
-
if (typeof
|
|
489
|
+
if (typeof path18 !== "string") {
|
|
460
490
|
return { ok: false, message: "git.diff payload.path must be a string when provided" };
|
|
461
491
|
}
|
|
462
|
-
return { ok: true, value: { path:
|
|
492
|
+
return { ok: true, value: { path: path18 } };
|
|
463
493
|
}
|
|
464
494
|
function validateProjectsAddPayload(payload) {
|
|
465
495
|
if (!isRecord(payload)) {
|
|
@@ -709,8 +739,57 @@ async function handleWorklistMessage(ctx, ws, msg) {
|
|
|
709
739
|
|
|
710
740
|
// src/server/index.ts
|
|
711
741
|
import { makeMailboxTool, makeMailSendTool, makeMailInboxTool, mailboxSessionTag } from "@wrongstack/core";
|
|
712
|
-
import { toErrorMessage as toErrorMessage6, wstackGlobalRoot as
|
|
742
|
+
import { toErrorMessage as toErrorMessage6, wstackGlobalRoot as wstackGlobalRoot3, projectHash } from "@wrongstack/core/utils";
|
|
713
743
|
import { SkillInstaller } from "@wrongstack/core/skills";
|
|
744
|
+
|
|
745
|
+
// src/server/discover-mailbox-bridge.ts
|
|
746
|
+
import { resolveProjectDir, wstackGlobalRoot } from "@wrongstack/core";
|
|
747
|
+
import { readLiveLock } from "@wrongstack/core/coordination";
|
|
748
|
+
async function discoverMailboxBridgeForWebui(params) {
|
|
749
|
+
const mode = params.config?.features?.mailboxBridge ?? "auto";
|
|
750
|
+
if (mode === "off") return;
|
|
751
|
+
const projectDir = resolveProjectDir(params.projectRoot, wstackGlobalRoot());
|
|
752
|
+
const result = await readLiveLock(projectDir);
|
|
753
|
+
switch (result.kind) {
|
|
754
|
+
case "live": {
|
|
755
|
+
params.logger.debug("webui joined existing mailbox bridge", {
|
|
756
|
+
url: result.lock.url,
|
|
757
|
+
lockPath: projectDir
|
|
758
|
+
});
|
|
759
|
+
params.ctx.meta["mailboxBridge"] = {
|
|
760
|
+
url: result.lock.url,
|
|
761
|
+
token: result.lock.token,
|
|
762
|
+
lockPath: projectDir,
|
|
763
|
+
childPid: null,
|
|
764
|
+
source: "joined"
|
|
765
|
+
};
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
case "probe-failed": {
|
|
769
|
+
params.logger.warn(
|
|
770
|
+
"mailbox bridge present but /healthz unreachable; webui will start without external-agent connectivity",
|
|
771
|
+
{ url: result.lock.url, lockPath: projectDir }
|
|
772
|
+
);
|
|
773
|
+
params.ctx.meta["mailboxBridge"] = {
|
|
774
|
+
url: result.lock.url,
|
|
775
|
+
token: result.lock.token,
|
|
776
|
+
lockPath: projectDir,
|
|
777
|
+
childPid: null,
|
|
778
|
+
source: "unhealthy"
|
|
779
|
+
};
|
|
780
|
+
break;
|
|
781
|
+
}
|
|
782
|
+
case "absent": {
|
|
783
|
+
params.logger.info(
|
|
784
|
+
"no mailbox bridge running; webui will start without external-agent connectivity. Run `wstack mailbox serve` or a CLI surface to bring one up.",
|
|
785
|
+
{ projectDir }
|
|
786
|
+
);
|
|
787
|
+
break;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// src/server/index.ts
|
|
714
793
|
import {
|
|
715
794
|
BrainMonitor,
|
|
716
795
|
DefaultBrainArbiter,
|
|
@@ -718,8 +797,9 @@ import {
|
|
|
718
797
|
createAutonomyBrain,
|
|
719
798
|
createTieredBrainArbiter
|
|
720
799
|
} from "@wrongstack/core";
|
|
721
|
-
import * as
|
|
722
|
-
import * as
|
|
800
|
+
import * as fs14 from "fs/promises";
|
|
801
|
+
import * as path17 from "path";
|
|
802
|
+
import { createRequire as createRequire2 } from "module";
|
|
723
803
|
|
|
724
804
|
// src/server/http-server.ts
|
|
725
805
|
import * as fs from "fs/promises";
|
|
@@ -896,7 +976,7 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
|
|
|
896
976
|
return;
|
|
897
977
|
}
|
|
898
978
|
try {
|
|
899
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
979
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths4, DefaultSessionStore: DefaultSessionStore3, DefaultSessionReader: DefaultSessionReader2 } = await import("@wrongstack/core");
|
|
900
980
|
const registry = new SessionRegistry(globalRoot);
|
|
901
981
|
const entry = await registry.get(sessionId);
|
|
902
982
|
if (!entry) {
|
|
@@ -904,7 +984,7 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
|
|
|
904
984
|
res.end(JSON.stringify({ error: "Session not found" }));
|
|
905
985
|
return;
|
|
906
986
|
}
|
|
907
|
-
const paths =
|
|
987
|
+
const paths = resolveWstackPaths4({ projectRoot: entry.projectRoot, globalRoot });
|
|
908
988
|
const store = new DefaultSessionStore3({ dir: paths.projectSessions });
|
|
909
989
|
const reader = new DefaultSessionReader2({ store });
|
|
910
990
|
const rawEntries = [];
|
|
@@ -931,7 +1011,7 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
|
|
|
931
1011
|
}
|
|
932
1012
|
}
|
|
933
1013
|
function readJsonBody(req) {
|
|
934
|
-
return new Promise((
|
|
1014
|
+
return new Promise((resolve10, reject) => {
|
|
935
1015
|
let data = "";
|
|
936
1016
|
req.on("data", (chunk) => {
|
|
937
1017
|
data += chunk;
|
|
@@ -942,7 +1022,7 @@ function readJsonBody(req) {
|
|
|
942
1022
|
});
|
|
943
1023
|
req.on("end", () => {
|
|
944
1024
|
try {
|
|
945
|
-
|
|
1025
|
+
resolve10(data ? JSON.parse(data) : {});
|
|
946
1026
|
} catch (err) {
|
|
947
1027
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
948
1028
|
}
|
|
@@ -978,7 +1058,7 @@ async function handleApiSessionMessage(res, req, globalRoot, sessionId) {
|
|
|
978
1058
|
const priority = ["low", "normal", "high"].includes(rawPriority) ? rawPriority : "high";
|
|
979
1059
|
const subject = typeof body["subject"] === "string" && body["subject"].trim() ? body["subject"].trim() : "Message from Fleet HQ";
|
|
980
1060
|
try {
|
|
981
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
1061
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths4, GlobalMailbox: GlobalMailbox3, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
982
1062
|
const registry = new SessionRegistry(globalRoot);
|
|
983
1063
|
const entry = await registry.get(sessionId);
|
|
984
1064
|
if (!entry) {
|
|
@@ -986,7 +1066,7 @@ async function handleApiSessionMessage(res, req, globalRoot, sessionId) {
|
|
|
986
1066
|
res.end(JSON.stringify({ error: "Session not found" }));
|
|
987
1067
|
return;
|
|
988
1068
|
}
|
|
989
|
-
const paths =
|
|
1069
|
+
const paths = resolveWstackPaths4({ projectRoot: entry.projectRoot, globalRoot });
|
|
990
1070
|
const mailbox = new GlobalMailbox3(paths.projectDir);
|
|
991
1071
|
const to = `leader@${mailboxSessionTag2(sessionId)}`;
|
|
992
1072
|
const sent = await mailbox.send({ from, to, type, subject, body: text, priority });
|
|
@@ -1004,7 +1084,7 @@ async function handleApiSessionMailbox(res, globalRoot, sessionId) {
|
|
|
1004
1084
|
return;
|
|
1005
1085
|
}
|
|
1006
1086
|
try {
|
|
1007
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
1087
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths4, GlobalMailbox: GlobalMailbox3, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
1008
1088
|
const registry = new SessionRegistry(globalRoot);
|
|
1009
1089
|
const entry = await registry.get(sessionId);
|
|
1010
1090
|
if (!entry) {
|
|
@@ -1012,7 +1092,7 @@ async function handleApiSessionMailbox(res, globalRoot, sessionId) {
|
|
|
1012
1092
|
res.end(JSON.stringify({ error: "Session not found" }));
|
|
1013
1093
|
return;
|
|
1014
1094
|
}
|
|
1015
|
-
const paths =
|
|
1095
|
+
const paths = resolveWstackPaths4({ projectRoot: entry.projectRoot, globalRoot });
|
|
1016
1096
|
const mailbox = new GlobalMailbox3(paths.projectDir);
|
|
1017
1097
|
const leaderAddr = `leader@${mailboxSessionTag2(sessionId)}`;
|
|
1018
1098
|
const [inbound, outbound] = await Promise.all([
|
|
@@ -1062,7 +1142,7 @@ async function handleApiSessionInterrupt(res, req, globalRoot, sessionId) {
|
|
|
1062
1142
|
const reason = typeof body["reason"] === "string" && body["reason"].trim() ? body["reason"].trim() : "Operator requested stop from Fleet HQ";
|
|
1063
1143
|
const from = typeof body["from"] === "string" && body["from"].trim() ? body["from"].trim() : "human@webui";
|
|
1064
1144
|
try {
|
|
1065
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
1145
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths4, GlobalMailbox: GlobalMailbox3, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
1066
1146
|
const registry = new SessionRegistry(globalRoot);
|
|
1067
1147
|
const entry = await registry.get(sessionId);
|
|
1068
1148
|
if (!entry) {
|
|
@@ -1070,7 +1150,7 @@ async function handleApiSessionInterrupt(res, req, globalRoot, sessionId) {
|
|
|
1070
1150
|
res.end(JSON.stringify({ error: "Session not found" }));
|
|
1071
1151
|
return;
|
|
1072
1152
|
}
|
|
1073
|
-
const paths =
|
|
1153
|
+
const paths = resolveWstackPaths4({ projectRoot: entry.projectRoot, globalRoot });
|
|
1074
1154
|
const mailbox = new GlobalMailbox3(paths.projectDir);
|
|
1075
1155
|
const to = `leader@${mailboxSessionTag2(sessionId)}`;
|
|
1076
1156
|
const sent = await mailbox.send({
|
|
@@ -1110,7 +1190,7 @@ async function handleApiFleetBroadcast(res, req, globalRoot) {
|
|
|
1110
1190
|
}
|
|
1111
1191
|
const from = typeof body["from"] === "string" && body["from"].trim() ? body["from"].trim() : "human@webui";
|
|
1112
1192
|
try {
|
|
1113
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
1193
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths4, GlobalMailbox: GlobalMailbox3, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
1114
1194
|
const registry = new SessionRegistry(globalRoot);
|
|
1115
1195
|
const all = await registry.list();
|
|
1116
1196
|
const mySlug = all.find((s) => s.pid === process.pid)?.projectSlug;
|
|
@@ -1122,7 +1202,7 @@ async function handleApiFleetBroadcast(res, req, globalRoot) {
|
|
|
1122
1202
|
}
|
|
1123
1203
|
const mbByDir = /* @__PURE__ */ new Map();
|
|
1124
1204
|
const mailboxFor = (projectRoot) => {
|
|
1125
|
-
const dir =
|
|
1205
|
+
const dir = resolveWstackPaths4({ projectRoot, globalRoot }).projectDir;
|
|
1126
1206
|
let mb = mbByDir.get(dir);
|
|
1127
1207
|
if (!mb) {
|
|
1128
1208
|
mb = new GlobalMailbox3(dir);
|
|
@@ -1333,7 +1413,7 @@ function buildCspHeader(wsPort, requestHost, publicWsUrl) {
|
|
|
1333
1413
|
`ws://127.0.0.1:${wsPort}`,
|
|
1334
1414
|
`wss://127.0.0.1:${wsPort}`
|
|
1335
1415
|
]);
|
|
1336
|
-
if (requestHost && requestHost !== "127.0.0.1") {
|
|
1416
|
+
if (requestHost && requestHost !== "127.0.0.1" && requestHost !== "::1" && requestHost !== "[::1]") {
|
|
1337
1417
|
const host = formatCspHostname(requestHost);
|
|
1338
1418
|
connect.add(`ws://${host}:${wsPort}`);
|
|
1339
1419
|
connect.add(`wss://${host}:${wsPort}`);
|
|
@@ -1858,6 +1938,16 @@ async function realpathAllowMissing(p) {
|
|
|
1858
1938
|
}
|
|
1859
1939
|
}
|
|
1860
1940
|
}
|
|
1941
|
+
function validatedPayload(msg, label) {
|
|
1942
|
+
if (msg == null || typeof msg !== "object") {
|
|
1943
|
+
throw new TypeError(`Expected object for ${label}, got ${msg}`);
|
|
1944
|
+
}
|
|
1945
|
+
const payload = msg.payload;
|
|
1946
|
+
if (payload == null || typeof payload !== "object") {
|
|
1947
|
+
throw new TypeError(`Expected payload object for ${label}, got ${payload}`);
|
|
1948
|
+
}
|
|
1949
|
+
return payload;
|
|
1950
|
+
}
|
|
1861
1951
|
async function handleFilesTree(ws, msg, projectRoot) {
|
|
1862
1952
|
const payload = msg.payload;
|
|
1863
1953
|
const rawPath = payload?.path?.trim();
|
|
@@ -1928,7 +2018,13 @@ async function handleFilesTree(ws, msg, projectRoot) {
|
|
|
1928
2018
|
}
|
|
1929
2019
|
}
|
|
1930
2020
|
async function handleFilesRead(ws, msg, projectRoot) {
|
|
1931
|
-
|
|
2021
|
+
let filePath;
|
|
2022
|
+
try {
|
|
2023
|
+
({ filePath } = validatedPayload(msg, "files.read"));
|
|
2024
|
+
} catch {
|
|
2025
|
+
send(ws, { type: "files.read", payload: { filePath: "", content: "", error: "Malformed request" } });
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
1932
2028
|
let realResolved;
|
|
1933
2029
|
try {
|
|
1934
2030
|
realResolved = await resolveFileInsideProject(projectRoot, filePath);
|
|
@@ -1947,7 +2043,14 @@ async function handleFilesRead(ws, msg, projectRoot) {
|
|
|
1947
2043
|
}
|
|
1948
2044
|
}
|
|
1949
2045
|
async function handleFilesWrite(ws, msg, projectRoot, opts = {}) {
|
|
1950
|
-
|
|
2046
|
+
let filePath;
|
|
2047
|
+
let content;
|
|
2048
|
+
try {
|
|
2049
|
+
({ filePath, content } = validatedPayload(msg, "files.write"));
|
|
2050
|
+
} catch {
|
|
2051
|
+
send(ws, { type: "files.written", payload: { filePath: "", success: false, error: "Malformed request" } });
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
1951
2054
|
let realResolved;
|
|
1952
2055
|
try {
|
|
1953
2056
|
realResolved = await resolveFileInsideProject(projectRoot, filePath);
|
|
@@ -2785,7 +2888,7 @@ import { promises as fs5 } from "fs";
|
|
|
2785
2888
|
import path6 from "path";
|
|
2786
2889
|
import JSZip from "jszip";
|
|
2787
2890
|
import { atomicWrite as atomicWrite2 } from "@wrongstack/core";
|
|
2788
|
-
import { wstackGlobalRoot } from "@wrongstack/core/utils";
|
|
2891
|
+
import { wstackGlobalRoot as wstackGlobalRoot2 } from "@wrongstack/core/utils";
|
|
2789
2892
|
async function handleSkillsList(ws, ctx) {
|
|
2790
2893
|
if (!ctx.skillLoader) {
|
|
2791
2894
|
send(ws, { type: "skills.list", payload: { skills: [], enabled: false } });
|
|
@@ -2955,7 +3058,7 @@ async function handleSkillsCreate(ws, ctx, msg) {
|
|
|
2955
3058
|
}
|
|
2956
3059
|
const createPayload = parsed.value;
|
|
2957
3060
|
try {
|
|
2958
|
-
const targetDir = createPayload.scope === "global" ? path6.join(
|
|
3061
|
+
const targetDir = createPayload.scope === "global" ? path6.join(wstackGlobalRoot2(), "skills", createPayload.name.trim()) : path6.join(ctx.projectRoot, ".wrongstack", "skills", createPayload.name.trim());
|
|
2959
3062
|
try {
|
|
2960
3063
|
await fs5.access(targetDir);
|
|
2961
3064
|
send(ws, { type: "skills.created", payload: { success: false, error: `Skill "${createPayload.name}" already exists` } });
|
|
@@ -3075,6 +3178,416 @@ async function handleSkillsExport(ws, ctx) {
|
|
|
3075
3178
|
}
|
|
3076
3179
|
}
|
|
3077
3180
|
|
|
3181
|
+
// src/server/prompts-handlers.ts
|
|
3182
|
+
function parseVariablesPayload(raw) {
|
|
3183
|
+
if (!Array.isArray(raw)) return void 0;
|
|
3184
|
+
const out = [];
|
|
3185
|
+
for (const item of raw) {
|
|
3186
|
+
if (!item || typeof item !== "object") continue;
|
|
3187
|
+
const o = item;
|
|
3188
|
+
if (typeof o["name"] !== "string" || !o["name"].trim()) continue;
|
|
3189
|
+
const enumVals = Array.isArray(o["enum"]) && o["enum"].every((x) => typeof x === "string") ? o["enum"].map((s) => s.trim()).filter(Boolean) : void 0;
|
|
3190
|
+
const v = { name: o["name"].trim() };
|
|
3191
|
+
if (typeof o["description"] === "string" && o["description"].trim()) {
|
|
3192
|
+
v.description = o["description"].trim();
|
|
3193
|
+
}
|
|
3194
|
+
if (o["required"] === true) v.required = true;
|
|
3195
|
+
if (o["multiline"] === true) v.multiline = true;
|
|
3196
|
+
if (enumVals && enumVals.length > 0) v.enum = enumVals;
|
|
3197
|
+
out.push(v);
|
|
3198
|
+
}
|
|
3199
|
+
return out.length > 0 ? out : void 0;
|
|
3200
|
+
}
|
|
3201
|
+
function toMeta(e) {
|
|
3202
|
+
return {
|
|
3203
|
+
id: e.id,
|
|
3204
|
+
slug: e.slug,
|
|
3205
|
+
title: e.title,
|
|
3206
|
+
description: e.description,
|
|
3207
|
+
category: e.category,
|
|
3208
|
+
tags: e.tags,
|
|
3209
|
+
source: e.source,
|
|
3210
|
+
favorite: e.favorite,
|
|
3211
|
+
variables: e.variables ?? []
|
|
3212
|
+
};
|
|
3213
|
+
}
|
|
3214
|
+
async function handlePromptsList(ws, ctx) {
|
|
3215
|
+
if (!ctx.promptLoader) {
|
|
3216
|
+
send(ws, { type: "prompts.list", payload: { enabled: false, prompts: [], categories: [] } });
|
|
3217
|
+
return;
|
|
3218
|
+
}
|
|
3219
|
+
try {
|
|
3220
|
+
const [all, categories] = await Promise.all([
|
|
3221
|
+
ctx.promptLoader.list(),
|
|
3222
|
+
ctx.promptLoader.categories()
|
|
3223
|
+
]);
|
|
3224
|
+
send(ws, {
|
|
3225
|
+
type: "prompts.list",
|
|
3226
|
+
payload: { enabled: true, prompts: all.map(toMeta), categories }
|
|
3227
|
+
});
|
|
3228
|
+
} catch (err) {
|
|
3229
|
+
send(ws, {
|
|
3230
|
+
type: "prompts.list",
|
|
3231
|
+
payload: { enabled: true, prompts: [], categories: [], error: errMessage(err) }
|
|
3232
|
+
});
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
async function handlePromptsSearch(ws, ctx, msg) {
|
|
3236
|
+
if (!ctx.promptLoader) {
|
|
3237
|
+
send(ws, { type: "prompts.search", payload: { enabled: false, prompts: [] } });
|
|
3238
|
+
return;
|
|
3239
|
+
}
|
|
3240
|
+
const payload = msg.payload ?? {};
|
|
3241
|
+
try {
|
|
3242
|
+
const results = await ctx.promptLoader.search(payload.query ?? "", {
|
|
3243
|
+
...payload.category ? { category: payload.category } : {},
|
|
3244
|
+
limit: 50
|
|
3245
|
+
});
|
|
3246
|
+
send(ws, { type: "prompts.search", payload: { enabled: true, prompts: results.map(toMeta) } });
|
|
3247
|
+
} catch (err) {
|
|
3248
|
+
send(ws, {
|
|
3249
|
+
type: "prompts.search",
|
|
3250
|
+
payload: { enabled: true, prompts: [], error: errMessage(err) }
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
async function handlePromptsContent(ws, ctx, msg) {
|
|
3255
|
+
const slug = msg.payload?.slug;
|
|
3256
|
+
if (!ctx.promptLoader || !slug) {
|
|
3257
|
+
send(ws, {
|
|
3258
|
+
type: "prompts.content",
|
|
3259
|
+
payload: { slug: slug ?? "", found: false, content: "", variables: [] }
|
|
3260
|
+
});
|
|
3261
|
+
return;
|
|
3262
|
+
}
|
|
3263
|
+
try {
|
|
3264
|
+
const entry = await ctx.promptLoader.find(slug);
|
|
3265
|
+
if (!entry) {
|
|
3266
|
+
send(ws, {
|
|
3267
|
+
type: "prompts.content",
|
|
3268
|
+
payload: { slug, found: false, content: "", variables: [] }
|
|
3269
|
+
});
|
|
3270
|
+
return;
|
|
3271
|
+
}
|
|
3272
|
+
send(ws, {
|
|
3273
|
+
type: "prompts.content",
|
|
3274
|
+
payload: {
|
|
3275
|
+
slug: entry.slug,
|
|
3276
|
+
found: true,
|
|
3277
|
+
title: entry.title,
|
|
3278
|
+
content: entry.content,
|
|
3279
|
+
variables: entry.variables ?? [],
|
|
3280
|
+
category: entry.category,
|
|
3281
|
+
source: entry.source
|
|
3282
|
+
}
|
|
3283
|
+
});
|
|
3284
|
+
} catch (err) {
|
|
3285
|
+
send(ws, {
|
|
3286
|
+
type: "prompts.content",
|
|
3287
|
+
payload: { slug, found: false, content: "", variables: [], error: errMessage(err) }
|
|
3288
|
+
});
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
async function handlePromptsFavorite(ws, ctx, msg) {
|
|
3292
|
+
const payload = msg.payload;
|
|
3293
|
+
if (!ctx.promptLoader || !payload?.slug) {
|
|
3294
|
+
send(ws, {
|
|
3295
|
+
type: "prompts.favorite",
|
|
3296
|
+
payload: { success: false, error: "Prompt library unavailable" }
|
|
3297
|
+
});
|
|
3298
|
+
return;
|
|
3299
|
+
}
|
|
3300
|
+
try {
|
|
3301
|
+
const updated = await ctx.promptLoader.setFavorite(payload.slug, payload.favorite !== false);
|
|
3302
|
+
if (!updated) {
|
|
3303
|
+
send(ws, {
|
|
3304
|
+
type: "prompts.favorite",
|
|
3305
|
+
payload: { success: false, error: "Prompt not found" }
|
|
3306
|
+
});
|
|
3307
|
+
return;
|
|
3308
|
+
}
|
|
3309
|
+
send(ws, {
|
|
3310
|
+
type: "prompts.favorite",
|
|
3311
|
+
payload: { success: true, slug: updated.slug, favorite: updated.favorite }
|
|
3312
|
+
});
|
|
3313
|
+
} catch (err) {
|
|
3314
|
+
send(ws, { type: "prompts.favorite", payload: { success: false, error: errMessage(err) } });
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
async function handlePromptsCreate(ws, ctx, msg) {
|
|
3318
|
+
const p = msg.payload;
|
|
3319
|
+
if (!ctx.promptLoader || !p) {
|
|
3320
|
+
send(ws, {
|
|
3321
|
+
type: "prompts.created",
|
|
3322
|
+
payload: { success: false, error: "Prompt library unavailable" }
|
|
3323
|
+
});
|
|
3324
|
+
return;
|
|
3325
|
+
}
|
|
3326
|
+
const title = typeof p["title"] === "string" ? p["title"].trim() : "";
|
|
3327
|
+
const content = typeof p["content"] === "string" ? p["content"] : "";
|
|
3328
|
+
if (!title || !content) {
|
|
3329
|
+
send(ws, {
|
|
3330
|
+
type: "prompts.created",
|
|
3331
|
+
payload: { success: false, error: "Title and content are required" }
|
|
3332
|
+
});
|
|
3333
|
+
return;
|
|
3334
|
+
}
|
|
3335
|
+
try {
|
|
3336
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3337
|
+
const tags = Array.isArray(p["tags"]) ? p["tags"].filter((t) => typeof t === "string") : [];
|
|
3338
|
+
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "prompt";
|
|
3339
|
+
const variables = parseVariablesPayload(p["variables"]);
|
|
3340
|
+
const entry = {
|
|
3341
|
+
id: slug,
|
|
3342
|
+
slug,
|
|
3343
|
+
title,
|
|
3344
|
+
description: typeof p["description"] === "string" ? p["description"] : "",
|
|
3345
|
+
content,
|
|
3346
|
+
category: typeof p["category"] === "string" && p["category"] ? p["category"] : "uncategorized",
|
|
3347
|
+
tags,
|
|
3348
|
+
source: "user",
|
|
3349
|
+
favorite: false,
|
|
3350
|
+
...variables ? { variables } : {},
|
|
3351
|
+
createdAt: now,
|
|
3352
|
+
updatedAt: now
|
|
3353
|
+
};
|
|
3354
|
+
await ctx.promptLoader.save(entry);
|
|
3355
|
+
send(ws, { type: "prompts.created", payload: { success: true, slug } });
|
|
3356
|
+
} catch (err) {
|
|
3357
|
+
send(ws, { type: "prompts.created", payload: { success: false, error: errMessage(err) } });
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
async function handlePromptsUsed(ws, ctx, msg) {
|
|
3361
|
+
const slug = msg.payload?.slug;
|
|
3362
|
+
if (!ctx.promptUsage || !slug) {
|
|
3363
|
+
send(ws, { type: "prompts.used", payload: { success: false } });
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
try {
|
|
3367
|
+
await ctx.promptUsage.record(slug);
|
|
3368
|
+
send(ws, { type: "prompts.used", payload: { success: true, slug } });
|
|
3369
|
+
} catch {
|
|
3370
|
+
send(ws, { type: "prompts.used", payload: { success: false } });
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
async function handlePromptsRecent(ws, ctx) {
|
|
3374
|
+
if (!ctx.promptUsage) {
|
|
3375
|
+
send(ws, { type: "prompts.recent", payload: { slugs: [] } });
|
|
3376
|
+
return;
|
|
3377
|
+
}
|
|
3378
|
+
try {
|
|
3379
|
+
const recent = await ctx.promptUsage.recent(50);
|
|
3380
|
+
send(ws, { type: "prompts.recent", payload: { slugs: recent.map((r) => r.slug) } });
|
|
3381
|
+
} catch (err) {
|
|
3382
|
+
send(ws, { type: "prompts.recent", payload: { slugs: [], error: errMessage(err) } });
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
|
|
3386
|
+
// src/server/design-handlers.ts
|
|
3387
|
+
import * as fs6 from "fs/promises";
|
|
3388
|
+
import * as path7 from "path";
|
|
3389
|
+
import {
|
|
3390
|
+
applyTokenOverrides,
|
|
3391
|
+
getDesignKitLoader,
|
|
3392
|
+
getDesignState,
|
|
3393
|
+
isDesignStack,
|
|
3394
|
+
loadActiveKit,
|
|
3395
|
+
materializeTokens,
|
|
3396
|
+
recordKitChoice,
|
|
3397
|
+
recordOverrides,
|
|
3398
|
+
runDesignVerify,
|
|
3399
|
+
setActiveKit,
|
|
3400
|
+
setDesignOverrides
|
|
3401
|
+
} from "@wrongstack/core";
|
|
3402
|
+
function readOverrides(value) {
|
|
3403
|
+
const out = {};
|
|
3404
|
+
if (value && typeof value === "object") {
|
|
3405
|
+
for (const [k, v] of Object.entries(value)) {
|
|
3406
|
+
if (typeof v === "string") out[k] = v;
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
return out;
|
|
3410
|
+
}
|
|
3411
|
+
var FOUNDATIONS_ID = "_foundations";
|
|
3412
|
+
async function buildListPayload(ctx) {
|
|
3413
|
+
const loader = getDesignKitLoader(ctx.projectRoot);
|
|
3414
|
+
const manifests = (await loader.list()).filter((k) => k.id !== FOUNDATIONS_ID);
|
|
3415
|
+
const kits = [];
|
|
3416
|
+
for (const m of manifests) {
|
|
3417
|
+
const tokens = await loader.readTokens(m.id);
|
|
3418
|
+
kits.push({
|
|
3419
|
+
id: m.id,
|
|
3420
|
+
name: m.name,
|
|
3421
|
+
aesthetic: m.aesthetic,
|
|
3422
|
+
bestFor: m.bestFor,
|
|
3423
|
+
stacks: m.stacks,
|
|
3424
|
+
tags: m.tags,
|
|
3425
|
+
light: tokens?.light ?? {},
|
|
3426
|
+
dark: tokens?.dark ?? {}
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3429
|
+
const state = ctx.agentMeta ? getDesignState(ctx.agentMeta) : void 0;
|
|
3430
|
+
const persisted = await loadActiveKit(ctx.projectRoot).catch(() => void 0);
|
|
3431
|
+
return {
|
|
3432
|
+
kits,
|
|
3433
|
+
activeKit: state?.activeKit ?? persisted?.kit ?? null,
|
|
3434
|
+
stack: state?.stack ?? persisted?.stack ?? null,
|
|
3435
|
+
overrides: state?.overrides ?? persisted?.overrides ?? {}
|
|
3436
|
+
};
|
|
3437
|
+
}
|
|
3438
|
+
async function handleDesignList(ws, ctx) {
|
|
3439
|
+
try {
|
|
3440
|
+
send(ws, { type: "design.list", payload: await buildListPayload(ctx) });
|
|
3441
|
+
} catch (err) {
|
|
3442
|
+
send(ws, {
|
|
3443
|
+
type: "design.list",
|
|
3444
|
+
payload: { kits: [], activeKit: null, stack: null, error: String(err) }
|
|
3445
|
+
});
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
async function handleDesignState(ws, ctx) {
|
|
3449
|
+
const state = ctx.agentMeta ? getDesignState(ctx.agentMeta) : void 0;
|
|
3450
|
+
send(ws, {
|
|
3451
|
+
type: "design.state",
|
|
3452
|
+
payload: {
|
|
3453
|
+
activeKit: state?.activeKit ?? null,
|
|
3454
|
+
stack: state?.stack ?? null,
|
|
3455
|
+
overrides: state?.overrides ?? {}
|
|
3456
|
+
}
|
|
3457
|
+
});
|
|
3458
|
+
}
|
|
3459
|
+
async function handleDesignUse(ws, ctx, msg) {
|
|
3460
|
+
const payload = msg.payload ?? {};
|
|
3461
|
+
const kitId = typeof payload.kit === "string" ? payload.kit.trim() : "";
|
|
3462
|
+
if (!kitId) {
|
|
3463
|
+
send(ws, { type: "design.use", payload: { ok: false, error: "No kit id provided" } });
|
|
3464
|
+
return;
|
|
3465
|
+
}
|
|
3466
|
+
try {
|
|
3467
|
+
const loader = getDesignKitLoader(ctx.projectRoot);
|
|
3468
|
+
const kit = await loader.find(kitId);
|
|
3469
|
+
if (!kit) {
|
|
3470
|
+
send(ws, { type: "design.use", payload: { ok: false, kit: kitId, error: "Kit not found" } });
|
|
3471
|
+
return;
|
|
3472
|
+
}
|
|
3473
|
+
const stackArg = typeof payload.stack === "string" ? payload.stack : void 0;
|
|
3474
|
+
const stack = stackArg && isDesignStack(stackArg) ? stackArg : kit.stacks[0] ?? "web";
|
|
3475
|
+
const persisted = await loadActiveKit(ctx.projectRoot).catch(() => void 0);
|
|
3476
|
+
const keep = persisted?.kit === kit.id ? persisted.overrides ?? {} : {};
|
|
3477
|
+
const overrides = { ...keep, ...readOverrides(payload.overrides) };
|
|
3478
|
+
if (ctx.agentMeta) setActiveKit(ctx.agentMeta, kit.id, stack, overrides);
|
|
3479
|
+
await recordKitChoice(
|
|
3480
|
+
ctx.projectRoot,
|
|
3481
|
+
kit.id,
|
|
3482
|
+
stack,
|
|
3483
|
+
"webui",
|
|
3484
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
3485
|
+
Object.keys(overrides).length ? overrides : void 0
|
|
3486
|
+
);
|
|
3487
|
+
const body = await loader.readBody(kit.id, stack);
|
|
3488
|
+
const rawTokens = await loader.readTokens(kit.id);
|
|
3489
|
+
const tokens = rawTokens ? applyTokenOverrides(rawTokens, overrides) : rawTokens;
|
|
3490
|
+
send(ws, {
|
|
3491
|
+
type: "design.use",
|
|
3492
|
+
payload: {
|
|
3493
|
+
ok: true,
|
|
3494
|
+
kit: kit.id,
|
|
3495
|
+
name: kit.name,
|
|
3496
|
+
aesthetic: kit.aesthetic,
|
|
3497
|
+
stack,
|
|
3498
|
+
body,
|
|
3499
|
+
overrides,
|
|
3500
|
+
light: tokens?.light ?? {},
|
|
3501
|
+
dark: tokens?.dark ?? {}
|
|
3502
|
+
}
|
|
3503
|
+
});
|
|
3504
|
+
} catch (err) {
|
|
3505
|
+
send(ws, { type: "design.use", payload: { ok: false, kit: kitId, error: String(err) } });
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
async function handleDesignSet(ws, ctx, msg) {
|
|
3509
|
+
const patch = readOverrides(msg.payload?.overrides);
|
|
3510
|
+
if (Object.keys(patch).length === 0) {
|
|
3511
|
+
send(ws, { type: "design.set", payload: { ok: false, error: "No overrides provided" } });
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
try {
|
|
3515
|
+
const merged = await recordOverrides(ctx.projectRoot, patch, (/* @__PURE__ */ new Date()).toISOString());
|
|
3516
|
+
if (!merged) {
|
|
3517
|
+
send(ws, { type: "design.set", payload: { ok: false, error: "No active kit" } });
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3520
|
+
if (ctx.agentMeta) setDesignOverrides(ctx.agentMeta, merged);
|
|
3521
|
+
send(ws, { type: "design.set", payload: { ok: true, overrides: merged } });
|
|
3522
|
+
} catch (err) {
|
|
3523
|
+
send(ws, { type: "design.set", payload: { ok: false, error: String(err) } });
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
async function handleDesignMaterialize(ws, ctx, msg) {
|
|
3527
|
+
const payload = msg.payload ?? {};
|
|
3528
|
+
try {
|
|
3529
|
+
const active = await loadActiveKit(ctx.projectRoot);
|
|
3530
|
+
if (!active) {
|
|
3531
|
+
send(ws, { type: "design.materialize", payload: { ok: false, error: "No active kit" } });
|
|
3532
|
+
return;
|
|
3533
|
+
}
|
|
3534
|
+
const loader = getDesignKitLoader(ctx.projectRoot);
|
|
3535
|
+
const stackArg = typeof payload.stack === "string" ? payload.stack : void 0;
|
|
3536
|
+
const stack = stackArg && isDesignStack(stackArg) ? stackArg : active.stack && isDesignStack(active.stack) ? active.stack : "web";
|
|
3537
|
+
const raw = await loader.readTokens(active.kit);
|
|
3538
|
+
if (!raw) {
|
|
3539
|
+
send(ws, { type: "design.materialize", payload: { ok: false, error: "Kit has no tokens" } });
|
|
3540
|
+
return;
|
|
3541
|
+
}
|
|
3542
|
+
const tokens = applyTokenOverrides(raw, active.overrides);
|
|
3543
|
+
const result = materializeTokens({
|
|
3544
|
+
tokens,
|
|
3545
|
+
stack,
|
|
3546
|
+
kitId: active.kit,
|
|
3547
|
+
outPath: typeof payload.out === "string" ? payload.out : void 0
|
|
3548
|
+
});
|
|
3549
|
+
const abs = path7.join(ctx.projectRoot, result.path);
|
|
3550
|
+
await fs6.mkdir(path7.dirname(abs), { recursive: true });
|
|
3551
|
+
await fs6.writeFile(abs, result.content);
|
|
3552
|
+
send(ws, {
|
|
3553
|
+
type: "design.materialize",
|
|
3554
|
+
payload: { ok: true, path: result.path, format: result.format, stack }
|
|
3555
|
+
});
|
|
3556
|
+
} catch (err) {
|
|
3557
|
+
send(ws, { type: "design.materialize", payload: { ok: false, error: String(err) } });
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
async function handleDesignVerify(ws, ctx) {
|
|
3561
|
+
try {
|
|
3562
|
+
const active = await loadActiveKit(ctx.projectRoot);
|
|
3563
|
+
if (!active) {
|
|
3564
|
+
send(ws, { type: "design.verify", payload: { ok: false, error: "No active kit" } });
|
|
3565
|
+
return;
|
|
3566
|
+
}
|
|
3567
|
+
const loader = getDesignKitLoader(ctx.projectRoot);
|
|
3568
|
+
const raw = await loader.readTokens(active.kit);
|
|
3569
|
+
if (!raw) {
|
|
3570
|
+
send(ws, { type: "design.verify", payload: { ok: false, error: "Kit has no tokens" } });
|
|
3571
|
+
return;
|
|
3572
|
+
}
|
|
3573
|
+
const tokens = applyTokenOverrides(raw, active.overrides);
|
|
3574
|
+
const report = await runDesignVerify(ctx.projectRoot, tokens);
|
|
3575
|
+
send(ws, {
|
|
3576
|
+
type: "design.verify",
|
|
3577
|
+
payload: {
|
|
3578
|
+
ok: true,
|
|
3579
|
+
kit: active.kit,
|
|
3580
|
+
filesScanned: report.filesScanned,
|
|
3581
|
+
score: report.score,
|
|
3582
|
+
violations: report.violations.slice(0, 50),
|
|
3583
|
+
violationCount: report.violations.length
|
|
3584
|
+
}
|
|
3585
|
+
});
|
|
3586
|
+
} catch (err) {
|
|
3587
|
+
send(ws, { type: "design.verify", payload: { ok: false, error: String(err) } });
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3078
3591
|
// src/server/index.ts
|
|
3079
3592
|
import {
|
|
3080
3593
|
Agent,
|
|
@@ -3086,6 +3599,8 @@ import {
|
|
|
3086
3599
|
DefaultSessionReader,
|
|
3087
3600
|
DefaultSessionStore as DefaultSessionStore2,
|
|
3088
3601
|
DefaultSkillLoader,
|
|
3602
|
+
DefaultPromptLoader,
|
|
3603
|
+
PromptUsageStore,
|
|
3089
3604
|
DefaultSystemPromptBuilder as DefaultSystemPromptBuilder3,
|
|
3090
3605
|
DefaultTokenCounter,
|
|
3091
3606
|
AnnotationsStore,
|
|
@@ -3100,17 +3615,20 @@ import {
|
|
|
3100
3615
|
ToolRegistry,
|
|
3101
3616
|
atomicWrite as atomicWrite6,
|
|
3102
3617
|
createDefaultPipelines,
|
|
3618
|
+
installDesignStudioMiddleware,
|
|
3103
3619
|
createSessionEventBridge,
|
|
3104
3620
|
resolveSessionLoggingConfig,
|
|
3105
3621
|
DEFAULT_CONTEXT_WINDOW_MODE_ID as DEFAULT_CONTEXT_WINDOW_MODE_ID2,
|
|
3106
3622
|
DEFAULT_SESSION_PRUNE_DAYS,
|
|
3107
3623
|
DEFAULT_TOOLS_CONFIG,
|
|
3108
3624
|
applyToolDescriptionModes,
|
|
3625
|
+
applyToolResultRenderModes,
|
|
3109
3626
|
resolveContextWindowPolicy as resolveContextWindowPolicy2,
|
|
3110
3627
|
enhanceUserPrompt,
|
|
3111
3628
|
gatedEnhancerReasoning,
|
|
3112
3629
|
recentTextTurns,
|
|
3113
|
-
resolveProviderModelList
|
|
3630
|
+
resolveProviderModelList,
|
|
3631
|
+
cleanupStaleSddWorktrees as cleanupStaleSddWorktrees3
|
|
3114
3632
|
} from "@wrongstack/core";
|
|
3115
3633
|
import { ToolExecutor } from "@wrongstack/core/execution";
|
|
3116
3634
|
import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as encryptConfigSecrets2 } from "@wrongstack/core/security";
|
|
@@ -3793,7 +4311,7 @@ var SpecsWebSocketHandler = class {
|
|
|
3793
4311
|
};
|
|
3794
4312
|
|
|
3795
4313
|
// src/server/sdd-board-ws-handler.ts
|
|
3796
|
-
import { SddBoardStore } from "@wrongstack/core";
|
|
4314
|
+
import { applySddLifecycle, SddBoardStore } from "@wrongstack/core";
|
|
3797
4315
|
var CONTROL_TYPES = /* @__PURE__ */ new Set([
|
|
3798
4316
|
"pause",
|
|
3799
4317
|
"resume",
|
|
@@ -3807,19 +4325,19 @@ var CONTROL_TYPES = /* @__PURE__ */ new Set([
|
|
|
3807
4325
|
"set_task_verification",
|
|
3808
4326
|
"cancel_task",
|
|
3809
4327
|
"delete_task",
|
|
3810
|
-
"split_task"
|
|
3811
|
-
// Lifecycle (pair with a prior `stop`): sweep worktrees / revert merged commits.
|
|
3812
|
-
"cleanup_worktrees",
|
|
3813
|
-
"rollback"
|
|
4328
|
+
"split_task"
|
|
3814
4329
|
]);
|
|
4330
|
+
var LIFECYCLE_TYPES = /* @__PURE__ */ new Set(["cleanup_worktrees", "rollback", "destroy"]);
|
|
3815
4331
|
var SddBoardWebSocketHandler = class {
|
|
3816
4332
|
store;
|
|
3817
4333
|
clients = /* @__PURE__ */ new Set();
|
|
4334
|
+
lifecycle;
|
|
3818
4335
|
latest = null;
|
|
3819
4336
|
poll = null;
|
|
3820
4337
|
unsub = null;
|
|
3821
|
-
constructor(boardsDir, events) {
|
|
4338
|
+
constructor(boardsDir, events, lifecycle) {
|
|
3822
4339
|
this.store = new SddBoardStore({ baseDir: boardsDir });
|
|
4340
|
+
this.lifecycle = lifecycle;
|
|
3823
4341
|
if (events) {
|
|
3824
4342
|
const handler = (e) => {
|
|
3825
4343
|
this.latest = e.snapshot;
|
|
@@ -3848,6 +4366,10 @@ var SddBoardWebSocketHandler = class {
|
|
|
3848
4366
|
return;
|
|
3849
4367
|
}
|
|
3850
4368
|
const action = msg.type.replace(/^sdd\.board\./, "");
|
|
4369
|
+
if (LIFECYCLE_TYPES.has(action)) {
|
|
4370
|
+
await this.applyLifecycle(action, msg.payload);
|
|
4371
|
+
return;
|
|
4372
|
+
}
|
|
3851
4373
|
if (CONTROL_TYPES.has(action)) {
|
|
3852
4374
|
const runId = msg.payload?.runId ?? this.latest?.runId ?? (await this.store.list())[0]?.runId;
|
|
3853
4375
|
if (runId) {
|
|
@@ -3859,6 +4381,40 @@ var SddBoardWebSocketHandler = class {
|
|
|
3859
4381
|
}
|
|
3860
4382
|
}
|
|
3861
4383
|
}
|
|
4384
|
+
/**
|
|
4385
|
+
* Apply a cleanup/rollback/destroy from disk and broadcast a structured
|
|
4386
|
+
* `sdd.board.lifecycle_result`. Refuses (no-op) while a run is still active —
|
|
4387
|
+
* the user must stop it first; the UI gates the buttons on `!active` and the
|
|
4388
|
+
* Destroy flow auto-stops then waits before sending `destroy`.
|
|
4389
|
+
*/
|
|
4390
|
+
async applyLifecycle(op, payload) {
|
|
4391
|
+
if (!this.lifecycle) {
|
|
4392
|
+
this.broadcast({
|
|
4393
|
+
type: "sdd.board.lifecycle_result",
|
|
4394
|
+
payload: { op, ok: false, reason: "Lifecycle operations are not available in this session." }
|
|
4395
|
+
});
|
|
4396
|
+
return;
|
|
4397
|
+
}
|
|
4398
|
+
if (this.latest && (this.latest.status === "running" || this.latest.status === "paused")) {
|
|
4399
|
+
this.broadcast({
|
|
4400
|
+
type: "sdd.board.lifecycle_result",
|
|
4401
|
+
payload: { op, ok: false, reason: "Stop the run first, then retry." }
|
|
4402
|
+
});
|
|
4403
|
+
return;
|
|
4404
|
+
}
|
|
4405
|
+
const runId = payload?.runId ?? this.latest?.runId;
|
|
4406
|
+
const result = await applySddLifecycle(op, {
|
|
4407
|
+
projectRoot: this.lifecycle.projectRoot,
|
|
4408
|
+
paths: this.lifecycle.paths,
|
|
4409
|
+
runId,
|
|
4410
|
+
revertMerged: payload?.revertMerged === true
|
|
4411
|
+
});
|
|
4412
|
+
this.broadcast({ type: "sdd.board.lifecycle_result", payload: result });
|
|
4413
|
+
if (op === "destroy" && result.ok) {
|
|
4414
|
+
this.latest = null;
|
|
4415
|
+
this.broadcast({ type: "sdd.board.snapshot", payload: null });
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
3862
4418
|
dispose() {
|
|
3863
4419
|
if (this.poll) clearInterval(this.poll);
|
|
3864
4420
|
this.unsub?.();
|
|
@@ -4038,9 +4594,10 @@ var SddWizardWebSocketHandler = class {
|
|
|
4038
4594
|
};
|
|
4039
4595
|
|
|
4040
4596
|
// src/server/sdd-wizard-wiring.ts
|
|
4041
|
-
import * as
|
|
4597
|
+
import * as path8 from "path";
|
|
4042
4598
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4043
4599
|
import {
|
|
4600
|
+
cleanupStaleSddWorktrees,
|
|
4044
4601
|
makeCommandVerifier,
|
|
4045
4602
|
makeLlmSubtaskGenerator,
|
|
4046
4603
|
SddBoardStore as SddBoardStore2,
|
|
@@ -4075,7 +4632,7 @@ function buildSddWizardDeps(opts) {
|
|
|
4075
4632
|
makeDriver: () => new SddInterviewDriver({
|
|
4076
4633
|
specStore: new SpecStore2({ baseDir: opts.paths.projectSpecs }),
|
|
4077
4634
|
graphStore: new TaskGraphStore2({ baseDir: opts.paths.projectTaskGraphs }),
|
|
4078
|
-
sessionPath:
|
|
4635
|
+
sessionPath: path8.join(opts.paths.projectDir, "sdd-wizard-session.json")
|
|
4079
4636
|
}),
|
|
4080
4637
|
runInterviewTurn: (prompt) => runIsolatedTurn(prompt, "Spec Architect"),
|
|
4081
4638
|
startRun: async (driver, { parallelSlots, defaultModel, defaultProvider, fallbackModels, worktrees: useWorktrees }) => {
|
|
@@ -4092,7 +4649,13 @@ function buildSddWizardDeps(opts) {
|
|
|
4092
4649
|
encoding: "utf8",
|
|
4093
4650
|
windowsHide: true
|
|
4094
4651
|
}).stdout?.trim() === "true";
|
|
4095
|
-
if (inGit)
|
|
4652
|
+
if (inGit) {
|
|
4653
|
+
await cleanupStaleSddWorktrees({
|
|
4654
|
+
projectRoot: opts.projectRoot,
|
|
4655
|
+
boardsDir: opts.paths.projectSddBoards
|
|
4656
|
+
}).catch(() => void 0);
|
|
4657
|
+
worktrees = new WorktreeManager2({ projectRoot: opts.projectRoot, events: opts.events });
|
|
4658
|
+
}
|
|
4096
4659
|
}
|
|
4097
4660
|
const boardStore = new SddBoardStore2({ baseDir: opts.paths.projectSddBoards });
|
|
4098
4661
|
const verifyTask = makeCommandVerifier();
|
|
@@ -4871,16 +5434,16 @@ var CollaborationWebSocketHandler = class {
|
|
|
4871
5434
|
};
|
|
4872
5435
|
|
|
4873
5436
|
// src/server/projects-manifest.ts
|
|
4874
|
-
import * as
|
|
4875
|
-
import * as
|
|
5437
|
+
import * as fs7 from "fs/promises";
|
|
5438
|
+
import * as path9 from "path";
|
|
4876
5439
|
import { projectSlug } from "@wrongstack/core";
|
|
4877
5440
|
function projectsJsonPath(globalConfigPath) {
|
|
4878
|
-
const base =
|
|
4879
|
-
return
|
|
5441
|
+
const base = path9.dirname(globalConfigPath);
|
|
5442
|
+
return path9.join(base, "projects.json");
|
|
4880
5443
|
}
|
|
4881
5444
|
async function loadManifest(globalConfigPath) {
|
|
4882
5445
|
try {
|
|
4883
|
-
const raw = await
|
|
5446
|
+
const raw = await fs7.readFile(projectsJsonPath(globalConfigPath), "utf8");
|
|
4884
5447
|
const parsed = JSON.parse(raw);
|
|
4885
5448
|
return { projects: parsed.projects ?? [] };
|
|
4886
5449
|
} catch {
|
|
@@ -4889,16 +5452,16 @@ async function loadManifest(globalConfigPath) {
|
|
|
4889
5452
|
}
|
|
4890
5453
|
async function saveManifest(manifest, globalConfigPath) {
|
|
4891
5454
|
const file = projectsJsonPath(globalConfigPath);
|
|
4892
|
-
await
|
|
4893
|
-
await
|
|
5455
|
+
await fs7.mkdir(path9.dirname(file), { recursive: true });
|
|
5456
|
+
await fs7.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
|
|
4894
5457
|
}
|
|
4895
5458
|
function generateProjectSlug(rootPath) {
|
|
4896
5459
|
return projectSlug(rootPath);
|
|
4897
5460
|
}
|
|
4898
5461
|
async function ensureProjectDataDir(slug, globalConfigPath) {
|
|
4899
|
-
const base =
|
|
4900
|
-
const dir =
|
|
4901
|
-
await
|
|
5462
|
+
const base = path9.dirname(globalConfigPath);
|
|
5463
|
+
const dir = path9.join(base, "projects", slug);
|
|
5464
|
+
await fs7.mkdir(dir, { recursive: true });
|
|
4902
5465
|
return dir;
|
|
4903
5466
|
}
|
|
4904
5467
|
|
|
@@ -5060,16 +5623,22 @@ function clampDim(value, fallback) {
|
|
|
5060
5623
|
}
|
|
5061
5624
|
|
|
5062
5625
|
// src/server/worktree-ws-handler.ts
|
|
5626
|
+
import { join as join6, resolve as resolve6, sep as sep4 } from "path";
|
|
5627
|
+
import { cleanupStaleSddWorktrees as cleanupStaleSddWorktrees2, WorktreeManager as WorktreeManager3 } from "@wrongstack/core";
|
|
5063
5628
|
import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
|
|
5064
5629
|
var MAX_ACTIVITY = 6;
|
|
5630
|
+
var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["allocating", "active", "committing", "merging"]);
|
|
5631
|
+
var MANAGED_BRANCH_RE = /^wstack\/ap\/[A-Za-z0-9._/-]+$/;
|
|
5065
5632
|
var WorktreeWebSocketHandler = class {
|
|
5066
|
-
constructor(events, logger) {
|
|
5633
|
+
constructor(events, logger, management) {
|
|
5067
5634
|
this.events = events;
|
|
5068
5635
|
this.logger = logger;
|
|
5636
|
+
this.management = management;
|
|
5069
5637
|
this.subscribe();
|
|
5070
5638
|
}
|
|
5071
5639
|
events;
|
|
5072
5640
|
logger;
|
|
5641
|
+
management;
|
|
5073
5642
|
clients = /* @__PURE__ */ new Set();
|
|
5074
5643
|
handles = /* @__PURE__ */ new Map();
|
|
5075
5644
|
baseBranch = "";
|
|
@@ -5080,12 +5649,197 @@ var WorktreeWebSocketHandler = class {
|
|
|
5080
5649
|
ws.on("close", () => this.clients.delete(ws));
|
|
5081
5650
|
ws.on("error", () => this.clients.delete(ws));
|
|
5082
5651
|
this.send(ws, this.stateMessage());
|
|
5652
|
+
void this.scanAndBroadcast();
|
|
5653
|
+
}
|
|
5654
|
+
/** Handle worktree-panel control messages (scan / clean / per-row ops). */
|
|
5655
|
+
async handleMessage(msg) {
|
|
5656
|
+
if (msg.type === "worktree.scan") {
|
|
5657
|
+
await this.scanAndBroadcast();
|
|
5658
|
+
return true;
|
|
5659
|
+
}
|
|
5660
|
+
if (msg.type === "worktree.cleanup") {
|
|
5661
|
+
await this.cleanupOrphans();
|
|
5662
|
+
return true;
|
|
5663
|
+
}
|
|
5664
|
+
if (msg.type === "worktree.remove") {
|
|
5665
|
+
await this.removeOne(msg.payload?.["dir"], msg.payload?.["branch"]);
|
|
5666
|
+
return true;
|
|
5667
|
+
}
|
|
5668
|
+
if (msg.type === "worktree.merge") {
|
|
5669
|
+
await this.mergeBranch(msg.payload?.["branch"]);
|
|
5670
|
+
return true;
|
|
5671
|
+
}
|
|
5672
|
+
if (msg.type === "worktree.diff") {
|
|
5673
|
+
await this.diffOne(msg.payload?.["dir"], msg.payload?.["baseBranch"]);
|
|
5674
|
+
return true;
|
|
5675
|
+
}
|
|
5676
|
+
return false;
|
|
5083
5677
|
}
|
|
5084
5678
|
dispose() {
|
|
5085
5679
|
for (const off of this.offs) off();
|
|
5086
5680
|
this.offs.length = 0;
|
|
5087
5681
|
this.stopBroadcast();
|
|
5088
5682
|
}
|
|
5683
|
+
// ── orphan management ─────────────────────────────────────────────────────
|
|
5684
|
+
/** Absolute managed-worktrees root for this project. */
|
|
5685
|
+
worktreesRoot() {
|
|
5686
|
+
return resolve6(join6(this.management.projectRoot, ".wrongstack", "worktrees"));
|
|
5687
|
+
}
|
|
5688
|
+
/** True iff `dir` resolves strictly inside the managed worktrees root. */
|
|
5689
|
+
underRoot(dir) {
|
|
5690
|
+
const abs = resolve6(dir);
|
|
5691
|
+
const root = this.worktreesRoot();
|
|
5692
|
+
return abs !== root && abs.startsWith(root + sep4);
|
|
5693
|
+
}
|
|
5694
|
+
/** Branches of worktrees a live in-session run currently owns. */
|
|
5695
|
+
liveActiveBranches() {
|
|
5696
|
+
const live = /* @__PURE__ */ new Set();
|
|
5697
|
+
for (const h of this.handles.values()) {
|
|
5698
|
+
if (ACTIVE_STATUSES.has(h.status) && h.branch) live.add(h.branch);
|
|
5699
|
+
}
|
|
5700
|
+
return live;
|
|
5701
|
+
}
|
|
5702
|
+
/**
|
|
5703
|
+
* Scan the disk for managed worktrees/branches NOT owned by a live in-session
|
|
5704
|
+
* run and broadcast them as orphans, with whether it is safe to clean now.
|
|
5705
|
+
* No-op (empty inventory) when management deps were not wired.
|
|
5706
|
+
*/
|
|
5707
|
+
async scanAndBroadcast() {
|
|
5708
|
+
if (!this.management) {
|
|
5709
|
+
this.broadcast({ type: "worktree.orphans", payload: { orphans: [], canClean: false } });
|
|
5710
|
+
return;
|
|
5711
|
+
}
|
|
5712
|
+
try {
|
|
5713
|
+
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
5714
|
+
const { worktrees, branches } = await wt.listManaged();
|
|
5715
|
+
const live = this.liveActiveBranches();
|
|
5716
|
+
const orphans = [];
|
|
5717
|
+
const seenBranches = /* @__PURE__ */ new Set();
|
|
5718
|
+
for (const w of worktrees) {
|
|
5719
|
+
if (w.branch && live.has(w.branch)) continue;
|
|
5720
|
+
if (w.branch) seenBranches.add(w.branch);
|
|
5721
|
+
orphans.push({ kind: "worktree", dir: w.dir, branch: w.branch });
|
|
5722
|
+
}
|
|
5723
|
+
for (const b of branches) {
|
|
5724
|
+
if (live.has(b) || seenBranches.has(b)) continue;
|
|
5725
|
+
orphans.push({ kind: "branch", branch: b });
|
|
5726
|
+
}
|
|
5727
|
+
const canClean = this.liveActiveBranches().size === 0;
|
|
5728
|
+
this.broadcast({
|
|
5729
|
+
type: "worktree.orphans",
|
|
5730
|
+
payload: {
|
|
5731
|
+
orphans,
|
|
5732
|
+
canClean,
|
|
5733
|
+
reason: canClean ? void 0 : "a run is live in this session"
|
|
5734
|
+
}
|
|
5735
|
+
});
|
|
5736
|
+
} catch (err) {
|
|
5737
|
+
this.logger.debug?.(`worktree orphan scan failed: ${toErrorMessage4(err)}`);
|
|
5738
|
+
this.broadcast({ type: "worktree.orphans", payload: { orphans: [], canClean: false } });
|
|
5739
|
+
}
|
|
5740
|
+
}
|
|
5741
|
+
/**
|
|
5742
|
+
* Force-remove every orphaned worktree + branch. Refused while a run is live —
|
|
5743
|
+
* in this session (active handles) OR another process (the SDD board liveness
|
|
5744
|
+
* guard inside cleanupStaleSddWorktrees). Best-effort; reports the outcome.
|
|
5745
|
+
*/
|
|
5746
|
+
async cleanupOrphans() {
|
|
5747
|
+
if (!this.management) {
|
|
5748
|
+
this.broadcast({
|
|
5749
|
+
type: "worktree.cleanup_result",
|
|
5750
|
+
payload: { ok: false, removed: 0, reason: "cleanup is not available in this session" }
|
|
5751
|
+
});
|
|
5752
|
+
return;
|
|
5753
|
+
}
|
|
5754
|
+
if (this.liveActiveBranches().size > 0) {
|
|
5755
|
+
this.broadcast({
|
|
5756
|
+
type: "worktree.cleanup_result",
|
|
5757
|
+
payload: { ok: false, removed: 0, reason: "a run is live in this session \u2014 stop it first" }
|
|
5758
|
+
});
|
|
5759
|
+
return;
|
|
5760
|
+
}
|
|
5761
|
+
const res = await cleanupStaleSddWorktrees2({
|
|
5762
|
+
projectRoot: this.management.projectRoot,
|
|
5763
|
+
boardsDir: this.management.boardsDir
|
|
5764
|
+
});
|
|
5765
|
+
if (res.skippedReason) {
|
|
5766
|
+
this.broadcast({
|
|
5767
|
+
type: "worktree.cleanup_result",
|
|
5768
|
+
payload: { ok: false, removed: 0, reason: res.skippedReason }
|
|
5769
|
+
});
|
|
5770
|
+
await this.scanAndBroadcast();
|
|
5771
|
+
return;
|
|
5772
|
+
}
|
|
5773
|
+
for (const [id, h] of [...this.handles]) {
|
|
5774
|
+
if (!ACTIVE_STATUSES.has(h.status)) this.handles.delete(id);
|
|
5775
|
+
}
|
|
5776
|
+
this.broadcast({ type: "worktree.cleanup_result", payload: { ok: true, removed: res.removed } });
|
|
5777
|
+
this.broadcastState();
|
|
5778
|
+
await this.scanAndBroadcast();
|
|
5779
|
+
}
|
|
5780
|
+
/** Remove/discard ONE worktree + branch. Refused while a live run owns it. */
|
|
5781
|
+
async removeOne(dir, branch) {
|
|
5782
|
+
if (!this.management || !dir && !branch) {
|
|
5783
|
+
this.broadcast({ type: "worktree.cleanup_result", payload: { ok: false, removed: 0, reason: "nothing to remove" } });
|
|
5784
|
+
return;
|
|
5785
|
+
}
|
|
5786
|
+
if (branch && !MANAGED_BRANCH_RE.test(branch)) {
|
|
5787
|
+
this.broadcast({ type: "worktree.cleanup_result", payload: { ok: false, removed: 0, reason: "not a managed worktree branch" } });
|
|
5788
|
+
return;
|
|
5789
|
+
}
|
|
5790
|
+
if (dir && !this.underRoot(dir)) {
|
|
5791
|
+
this.broadcast({ type: "worktree.cleanup_result", payload: { ok: false, removed: 0, reason: "path is outside the managed worktrees root" } });
|
|
5792
|
+
return;
|
|
5793
|
+
}
|
|
5794
|
+
if (branch && this.liveActiveBranches().has(branch)) {
|
|
5795
|
+
this.broadcast({ type: "worktree.cleanup_result", payload: { ok: false, removed: 0, reason: "a run is live on this worktree \u2014 stop it first" } });
|
|
5796
|
+
return;
|
|
5797
|
+
}
|
|
5798
|
+
let removed = false;
|
|
5799
|
+
if (dir) {
|
|
5800
|
+
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
5801
|
+
({ removed } = await wt.removeOne(dir, branch));
|
|
5802
|
+
}
|
|
5803
|
+
for (const [id, h] of [...this.handles]) {
|
|
5804
|
+
if (branch && h.branch === branch || dir && h.handleId && dir.endsWith(h.handleId)) this.handles.delete(id);
|
|
5805
|
+
}
|
|
5806
|
+
this.broadcast({ type: "worktree.cleanup_result", payload: { ok: removed, removed: removed ? 1 : 0, reason: removed ? void 0 : "remove failed (not a managed worktree?)" } });
|
|
5807
|
+
this.broadcastState();
|
|
5808
|
+
await this.scanAndBroadcast();
|
|
5809
|
+
}
|
|
5810
|
+
/** Squash-merge ONE branch into base. Refused while a live run owns it. */
|
|
5811
|
+
async mergeBranch(branch) {
|
|
5812
|
+
if (!this.management || !branch) {
|
|
5813
|
+
this.broadcast({ type: "worktree.merge_result", payload: { ok: false, branch: branch ?? "", reason: "no branch" } });
|
|
5814
|
+
return;
|
|
5815
|
+
}
|
|
5816
|
+
if (!MANAGED_BRANCH_RE.test(branch)) {
|
|
5817
|
+
this.broadcast({ type: "worktree.merge_result", payload: { ok: false, branch, reason: "not a managed worktree branch" } });
|
|
5818
|
+
return;
|
|
5819
|
+
}
|
|
5820
|
+
if (this.liveActiveBranches().has(branch)) {
|
|
5821
|
+
this.broadcast({ type: "worktree.merge_result", payload: { ok: false, branch, reason: "a run is live on this worktree \u2014 stop it first" } });
|
|
5822
|
+
return;
|
|
5823
|
+
}
|
|
5824
|
+
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
5825
|
+
const res = await wt.mergeBranch(branch);
|
|
5826
|
+
this.broadcast({
|
|
5827
|
+
type: "worktree.merge_result",
|
|
5828
|
+
payload: { ok: res.ok, branch, conflict: res.conflict, conflictFiles: res.conflictFiles, reason: res.reason }
|
|
5829
|
+
});
|
|
5830
|
+
await this.scanAndBroadcast();
|
|
5831
|
+
}
|
|
5832
|
+
/** Compact change summary for one worktree checkout. */
|
|
5833
|
+
async diffOne(dir, baseBranch) {
|
|
5834
|
+
if (!this.management || !dir || !this.underRoot(dir)) {
|
|
5835
|
+
this.broadcast({ type: "worktree.diff_result", payload: { dir: dir ?? "", summary: null } });
|
|
5836
|
+
return;
|
|
5837
|
+
}
|
|
5838
|
+
const base = baseBranch && MANAGED_BRANCH_RE.test(baseBranch) ? baseBranch : void 0;
|
|
5839
|
+
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
5840
|
+
const summary = await wt.diffSummary(resolve6(dir), base);
|
|
5841
|
+
this.broadcast({ type: "worktree.diff_result", payload: { dir, summary } });
|
|
5842
|
+
}
|
|
5089
5843
|
// ── internals ───────────────────────────────────────────────────────────
|
|
5090
5844
|
subscribe() {
|
|
5091
5845
|
const on = this.events.on.bind(this.events);
|
|
@@ -5097,6 +5851,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
5097
5851
|
handleId: e.handleId,
|
|
5098
5852
|
ownerId: e.ownerId,
|
|
5099
5853
|
ownerLabel: e.ownerLabel,
|
|
5854
|
+
dir: e.dir,
|
|
5100
5855
|
branch: e.branch,
|
|
5101
5856
|
baseBranch: e.baseBranch,
|
|
5102
5857
|
status: "active",
|
|
@@ -5199,10 +5954,10 @@ var WorktreeWebSocketHandler = class {
|
|
|
5199
5954
|
};
|
|
5200
5955
|
|
|
5201
5956
|
// src/server/mailbox-handlers.ts
|
|
5202
|
-
import { GlobalMailbox, resolveProjectDir } from "@wrongstack/core";
|
|
5957
|
+
import { GlobalMailbox, resolveProjectDir as resolveProjectDir2 } from "@wrongstack/core";
|
|
5203
5958
|
async function handleMailboxMessages(ws, deps2, payload) {
|
|
5204
5959
|
try {
|
|
5205
|
-
const dir =
|
|
5960
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
5206
5961
|
const mb = new GlobalMailbox(dir);
|
|
5207
5962
|
const messages = await mb.query({
|
|
5208
5963
|
limit: payload?.limit ?? 30,
|
|
@@ -5238,7 +5993,7 @@ async function handleMailboxMessages(ws, deps2, payload) {
|
|
|
5238
5993
|
}
|
|
5239
5994
|
async function handleMailboxAgents(ws, deps2, payload) {
|
|
5240
5995
|
try {
|
|
5241
|
-
const dir =
|
|
5996
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
5242
5997
|
const mb = new GlobalMailbox(dir);
|
|
5243
5998
|
const agents = payload?.onlineOnly ? await mb.getOnlineAgents() : await mb.getAgentStatuses();
|
|
5244
5999
|
send(ws, {
|
|
@@ -5267,7 +6022,7 @@ async function handleMailboxAgents(ws, deps2, payload) {
|
|
|
5267
6022
|
}
|
|
5268
6023
|
async function handleMailboxClear(ws, deps2) {
|
|
5269
6024
|
try {
|
|
5270
|
-
const dir =
|
|
6025
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
5271
6026
|
const mb = new GlobalMailbox(dir);
|
|
5272
6027
|
await mb.clearAll();
|
|
5273
6028
|
send(ws, { type: "mailbox.cleared", payload: {} });
|
|
@@ -5277,7 +6032,7 @@ async function handleMailboxClear(ws, deps2) {
|
|
|
5277
6032
|
}
|
|
5278
6033
|
async function handleMailboxPurge(ws, deps2, opts) {
|
|
5279
6034
|
try {
|
|
5280
|
-
const dir =
|
|
6035
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
5281
6036
|
const mb = new GlobalMailbox(dir);
|
|
5282
6037
|
const result = await mb.purgeStale(opts);
|
|
5283
6038
|
send(ws, { type: "mailbox.purged", payload: result });
|
|
@@ -5324,14 +6079,14 @@ function registerShutdownHandlers(res) {
|
|
|
5324
6079
|
|
|
5325
6080
|
// src/server/instance-registry.ts
|
|
5326
6081
|
import * as os from "os";
|
|
5327
|
-
import * as
|
|
5328
|
-
import * as
|
|
6082
|
+
import * as path10 from "path";
|
|
6083
|
+
import * as fs8 from "fs/promises";
|
|
5329
6084
|
import { atomicWrite as atomicWrite3 } from "@wrongstack/core";
|
|
5330
6085
|
function defaultBaseDir() {
|
|
5331
|
-
return
|
|
6086
|
+
return path10.join(os.homedir(), ".wrongstack");
|
|
5332
6087
|
}
|
|
5333
6088
|
function registryPath(baseDir = defaultBaseDir()) {
|
|
5334
|
-
return
|
|
6089
|
+
return path10.join(baseDir, "webui-instances.json");
|
|
5335
6090
|
}
|
|
5336
6091
|
function isPidAlive(pid) {
|
|
5337
6092
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -5344,7 +6099,7 @@ function isPidAlive(pid) {
|
|
|
5344
6099
|
}
|
|
5345
6100
|
async function load(file) {
|
|
5346
6101
|
try {
|
|
5347
|
-
const raw = await
|
|
6102
|
+
const raw = await fs8.readFile(file, "utf8");
|
|
5348
6103
|
const parsed = JSON.parse(raw);
|
|
5349
6104
|
if (parsed?.version === 1 && Array.isArray(parsed.instances)) {
|
|
5350
6105
|
return parsed;
|
|
@@ -5403,16 +6158,16 @@ function formatInstances(instances) {
|
|
|
5403
6158
|
// src/server/port-utils.ts
|
|
5404
6159
|
import * as net from "net";
|
|
5405
6160
|
function isPortFree(host, port) {
|
|
5406
|
-
return new Promise((
|
|
6161
|
+
return new Promise((resolve10) => {
|
|
5407
6162
|
const srv = net.createServer();
|
|
5408
|
-
srv.once("error", () =>
|
|
6163
|
+
srv.once("error", () => resolve10(false));
|
|
5409
6164
|
srv.once("listening", () => {
|
|
5410
|
-
srv.close(() =>
|
|
6165
|
+
srv.close(() => resolve10(true));
|
|
5411
6166
|
});
|
|
5412
6167
|
try {
|
|
5413
6168
|
srv.listen(port, host);
|
|
5414
6169
|
} catch {
|
|
5415
|
-
|
|
6170
|
+
resolve10(false);
|
|
5416
6171
|
}
|
|
5417
6172
|
});
|
|
5418
6173
|
}
|
|
@@ -5493,15 +6248,15 @@ import { DefaultSecretScrubber } from "@wrongstack/core";
|
|
|
5493
6248
|
import { probeLocalLlm } from "@wrongstack/runtime/probe";
|
|
5494
6249
|
|
|
5495
6250
|
// src/server/provider-config-io.ts
|
|
5496
|
-
import * as
|
|
5497
|
-
import * as
|
|
6251
|
+
import * as fs9 from "fs/promises";
|
|
6252
|
+
import * as path11 from "path";
|
|
5498
6253
|
import { atomicWrite as atomicWrite4 } from "@wrongstack/core";
|
|
5499
6254
|
import { decryptConfigSecrets, encryptConfigSecrets } from "@wrongstack/core/security";
|
|
5500
6255
|
import { DefaultSecretVault } from "@wrongstack/core";
|
|
5501
6256
|
async function loadSavedProviders(configPath, vault) {
|
|
5502
6257
|
let raw;
|
|
5503
6258
|
try {
|
|
5504
|
-
raw = await
|
|
6259
|
+
raw = await fs9.readFile(configPath, "utf8");
|
|
5505
6260
|
} catch {
|
|
5506
6261
|
return {};
|
|
5507
6262
|
}
|
|
@@ -5518,7 +6273,7 @@ async function saveProviders(configPath, vault, providers) {
|
|
|
5518
6273
|
let raw;
|
|
5519
6274
|
let fileExists = true;
|
|
5520
6275
|
try {
|
|
5521
|
-
raw = await
|
|
6276
|
+
raw = await fs9.readFile(configPath, "utf8");
|
|
5522
6277
|
} catch (err) {
|
|
5523
6278
|
if (err.code !== "ENOENT") {
|
|
5524
6279
|
throw new Error(
|
|
@@ -5546,7 +6301,7 @@ async function saveProviders(configPath, vault, providers) {
|
|
|
5546
6301
|
await atomicWrite4(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
5547
6302
|
}
|
|
5548
6303
|
function createProviderConfigIO(configPath) {
|
|
5549
|
-
const keyFile =
|
|
6304
|
+
const keyFile = path11.join(path11.dirname(configPath), ".key");
|
|
5550
6305
|
const vault = new DefaultSecretVault({ keyFile });
|
|
5551
6306
|
return {
|
|
5552
6307
|
load: () => loadSavedProviders(configPath, vault),
|
|
@@ -5864,7 +6619,8 @@ function createProviderHandlers(deps2) {
|
|
|
5864
6619
|
|
|
5865
6620
|
// src/server/mode-handlers.ts
|
|
5866
6621
|
import {
|
|
5867
|
-
DefaultSystemPromptBuilder
|
|
6622
|
+
DefaultSystemPromptBuilder,
|
|
6623
|
+
resolveWstackPaths
|
|
5868
6624
|
} from "@wrongstack/core";
|
|
5869
6625
|
function createModeHandlers(ctx) {
|
|
5870
6626
|
return {
|
|
@@ -5912,13 +6668,18 @@ function createModeHandlers(ctx) {
|
|
|
5912
6668
|
}
|
|
5913
6669
|
ctx.setModeId(id);
|
|
5914
6670
|
const modePrompt = id === "default" ? "" : (await ctx.modeStore.getMode(id))?.prompt ?? "";
|
|
6671
|
+
const paths = resolveWstackPaths({ projectRoot: ctx.projectRoot, globalRoot: ctx.globalRoot });
|
|
5915
6672
|
const freshBuilder = new DefaultSystemPromptBuilder({
|
|
5916
6673
|
memoryStore: ctx.memoryStore,
|
|
5917
6674
|
skillLoader: ctx.skillLoader,
|
|
5918
6675
|
modeStore: ctx.modeStore,
|
|
5919
6676
|
modeId: id,
|
|
5920
6677
|
modePrompt,
|
|
5921
|
-
modelCapabilities: ctx.modelCapabilities
|
|
6678
|
+
modelCapabilities: ctx.modelCapabilities,
|
|
6679
|
+
instructionPaths: {
|
|
6680
|
+
globalDir: paths.globalInstructions,
|
|
6681
|
+
projectDir: paths.inProjectInstructions
|
|
6682
|
+
}
|
|
5922
6683
|
});
|
|
5923
6684
|
ctx.context.systemPrompt = await freshBuilder.build({
|
|
5924
6685
|
cwd: ctx.projectRoot,
|
|
@@ -5940,12 +6701,13 @@ function createModeHandlers(ctx) {
|
|
|
5940
6701
|
}
|
|
5941
6702
|
|
|
5942
6703
|
// src/server/project-handlers.ts
|
|
5943
|
-
import * as
|
|
5944
|
-
import * as
|
|
6704
|
+
import * as fs10 from "fs/promises";
|
|
6705
|
+
import * as path12 from "path";
|
|
5945
6706
|
import {
|
|
5946
6707
|
DefaultSessionStore,
|
|
5947
6708
|
DefaultSystemPromptBuilder as DefaultSystemPromptBuilder2,
|
|
5948
|
-
getSessionRegistry
|
|
6709
|
+
getSessionRegistry,
|
|
6710
|
+
resolveWstackPaths as resolveWstackPaths2
|
|
5949
6711
|
} from "@wrongstack/core";
|
|
5950
6712
|
function createProjectHandlers(ctx) {
|
|
5951
6713
|
return {
|
|
@@ -5968,9 +6730,9 @@ function createProjectHandlers(ctx) {
|
|
|
5968
6730
|
}
|
|
5969
6731
|
const { root: addRoot, name: displayName } = parsed.value;
|
|
5970
6732
|
try {
|
|
5971
|
-
const resolved =
|
|
5972
|
-
await
|
|
5973
|
-
const stat3 = await
|
|
6733
|
+
const resolved = path12.resolve(addRoot);
|
|
6734
|
+
await fs10.access(resolved);
|
|
6735
|
+
const stat3 = await fs10.stat(resolved);
|
|
5974
6736
|
if (!stat3.isDirectory()) throw new Error(`Not a directory: ${resolved}`);
|
|
5975
6737
|
const manifest = await loadManifest(ctx.globalConfigPath);
|
|
5976
6738
|
const existing = manifest.projects.find((p) => p.root === resolved);
|
|
@@ -5986,7 +6748,7 @@ function createProjectHandlers(ctx) {
|
|
|
5986
6748
|
});
|
|
5987
6749
|
return;
|
|
5988
6750
|
}
|
|
5989
|
-
const name2 = displayName?.trim() ||
|
|
6751
|
+
const name2 = displayName?.trim() || path12.basename(resolved);
|
|
5990
6752
|
const slug = generateProjectSlug(resolved);
|
|
5991
6753
|
await ensureProjectDataDir(slug, ctx.globalConfigPath);
|
|
5992
6754
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -5999,7 +6761,7 @@ function createProjectHandlers(ctx) {
|
|
|
5999
6761
|
} catch (err) {
|
|
6000
6762
|
send(ws, {
|
|
6001
6763
|
type: "projects.added",
|
|
6002
|
-
payload: { name:
|
|
6764
|
+
payload: { name: path12.basename(addRoot), root: addRoot, slug: "", message: errMessage(err) }
|
|
6003
6765
|
});
|
|
6004
6766
|
}
|
|
6005
6767
|
},
|
|
@@ -6014,17 +6776,17 @@ function createProjectHandlers(ctx) {
|
|
|
6014
6776
|
}
|
|
6015
6777
|
const { root: selRoot, name: selName } = parsed.value;
|
|
6016
6778
|
try {
|
|
6017
|
-
const resolved =
|
|
6779
|
+
const resolved = path12.resolve(selRoot);
|
|
6018
6780
|
try {
|
|
6019
|
-
await
|
|
6020
|
-
const stat3 = await
|
|
6781
|
+
await fs10.access(resolved);
|
|
6782
|
+
const stat3 = await fs10.stat(resolved);
|
|
6021
6783
|
if (!stat3.isDirectory()) throw new Error(`Not a directory: ${resolved}`);
|
|
6022
6784
|
} catch (err) {
|
|
6023
6785
|
send(ws, {
|
|
6024
6786
|
type: "projects.selected",
|
|
6025
6787
|
payload: {
|
|
6026
6788
|
root: selRoot,
|
|
6027
|
-
name: selName ||
|
|
6789
|
+
name: selName || path12.basename(selRoot),
|
|
6028
6790
|
message: `Cannot switch: ${errMessage(err)}`
|
|
6029
6791
|
}
|
|
6030
6792
|
});
|
|
@@ -6036,7 +6798,7 @@ function createProjectHandlers(ctx) {
|
|
|
6036
6798
|
entry.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
6037
6799
|
entry.lastWorkingDir = resolved;
|
|
6038
6800
|
} else {
|
|
6039
|
-
const name2 = selName?.trim() ||
|
|
6801
|
+
const name2 = selName?.trim() || path12.basename(resolved);
|
|
6040
6802
|
const slug = generateProjectSlug(resolved);
|
|
6041
6803
|
manifest.projects.push({
|
|
6042
6804
|
name: name2,
|
|
@@ -6058,13 +6820,21 @@ function createProjectHandlers(ctx) {
|
|
|
6058
6820
|
try {
|
|
6059
6821
|
const modeId = ctx.getModeId();
|
|
6060
6822
|
const switchMode = modeId === "default" ? void 0 : await ctx.modeStore.getMode(modeId);
|
|
6823
|
+
const switchPaths = resolveWstackPaths2({
|
|
6824
|
+
projectRoot: resolved,
|
|
6825
|
+
globalRoot: ctx.wpaths.globalRoot
|
|
6826
|
+
});
|
|
6061
6827
|
const switchBuilder = new DefaultSystemPromptBuilder2({
|
|
6062
6828
|
memoryStore: ctx.memoryStore,
|
|
6063
6829
|
skillLoader: ctx.skillLoader,
|
|
6064
6830
|
modeStore: ctx.modeStore,
|
|
6065
6831
|
modeId,
|
|
6066
6832
|
modePrompt: switchMode?.prompt ?? "",
|
|
6067
|
-
modelCapabilities: ctx.modelCapabilities
|
|
6833
|
+
modelCapabilities: ctx.modelCapabilities,
|
|
6834
|
+
instructionPaths: {
|
|
6835
|
+
globalDir: switchPaths.globalInstructions,
|
|
6836
|
+
projectDir: switchPaths.inProjectInstructions
|
|
6837
|
+
}
|
|
6068
6838
|
});
|
|
6069
6839
|
ctx.context.systemPrompt = await switchBuilder.build({
|
|
6070
6840
|
cwd: resolved,
|
|
@@ -6075,13 +6845,13 @@ function createProjectHandlers(ctx) {
|
|
|
6075
6845
|
});
|
|
6076
6846
|
} catch {
|
|
6077
6847
|
}
|
|
6078
|
-
const newSessionsDir =
|
|
6079
|
-
|
|
6848
|
+
const newSessionsDir = path12.join(
|
|
6849
|
+
path12.dirname(ctx.globalConfigPath),
|
|
6080
6850
|
"projects",
|
|
6081
6851
|
switchSlug,
|
|
6082
6852
|
"sessions"
|
|
6083
6853
|
);
|
|
6084
|
-
await
|
|
6854
|
+
await fs10.mkdir(newSessionsDir, { recursive: true });
|
|
6085
6855
|
const newSessionStore = new DefaultSessionStore({ dir: newSessionsDir });
|
|
6086
6856
|
const oldSession = ctx.getSession();
|
|
6087
6857
|
const oldSessionId = oldSession.id;
|
|
@@ -6115,7 +6885,7 @@ function createProjectHandlers(ctx) {
|
|
|
6115
6885
|
sessionId: newSession.id,
|
|
6116
6886
|
projectSlug: switchSlug,
|
|
6117
6887
|
projectRoot: resolved,
|
|
6118
|
-
projectName:
|
|
6888
|
+
projectName: path12.basename(resolved),
|
|
6119
6889
|
workingDir: resolved,
|
|
6120
6890
|
clientType: "webui",
|
|
6121
6891
|
pid: process.pid,
|
|
@@ -6127,8 +6897,8 @@ function createProjectHandlers(ctx) {
|
|
|
6127
6897
|
type: "projects.selected",
|
|
6128
6898
|
payload: {
|
|
6129
6899
|
root: resolved,
|
|
6130
|
-
name: selName ||
|
|
6131
|
-
message: `Switched to ${selName ||
|
|
6900
|
+
name: selName || path12.basename(resolved),
|
|
6901
|
+
message: `Switched to ${selName || path12.basename(resolved)}`
|
|
6132
6902
|
}
|
|
6133
6903
|
});
|
|
6134
6904
|
broadcast(ctx.clients, {
|
|
@@ -6148,7 +6918,7 @@ function createProjectHandlers(ctx) {
|
|
|
6148
6918
|
type: "projects.selected",
|
|
6149
6919
|
payload: {
|
|
6150
6920
|
root: selRoot,
|
|
6151
|
-
name: selName ||
|
|
6921
|
+
name: selName || path12.basename(selRoot),
|
|
6152
6922
|
message: errMessage(err)
|
|
6153
6923
|
}
|
|
6154
6924
|
});
|
|
@@ -6179,7 +6949,7 @@ function createProjectHandlers(ctx) {
|
|
|
6179
6949
|
}
|
|
6180
6950
|
|
|
6181
6951
|
// src/server/session-handlers.ts
|
|
6182
|
-
import * as
|
|
6952
|
+
import * as path13 from "path";
|
|
6183
6953
|
import {
|
|
6184
6954
|
DEFAULT_CONTEXT_WINDOW_MODE_ID,
|
|
6185
6955
|
repairToolUseAdjacency,
|
|
@@ -6521,7 +7291,7 @@ function createSessionHandlers(ctx) {
|
|
|
6521
7291
|
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
6522
7292
|
const projectRoot = ctx.getProjectRoot();
|
|
6523
7293
|
const rewinder = new DefaultSessionRewinder(
|
|
6524
|
-
|
|
7294
|
+
path13.join(projectRoot, ".wrongstack", "sessions"),
|
|
6525
7295
|
projectRoot
|
|
6526
7296
|
);
|
|
6527
7297
|
const checkpoints = await rewinder.listCheckpoints(ctx.getSession().id);
|
|
@@ -6536,7 +7306,7 @@ function createSessionHandlers(ctx) {
|
|
|
6536
7306
|
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
6537
7307
|
const projectRoot = ctx.getProjectRoot();
|
|
6538
7308
|
const rewinder = new DefaultSessionRewinder(
|
|
6539
|
-
|
|
7309
|
+
path13.join(projectRoot, ".wrongstack", "sessions"),
|
|
6540
7310
|
projectRoot
|
|
6541
7311
|
);
|
|
6542
7312
|
await rewinder.rewindToCheckpoint(ctx.getSession().id, checkpointIndex);
|
|
@@ -6912,9 +7682,9 @@ async function handleSddBoardRoute(_ws, msg, handlers) {
|
|
|
6912
7682
|
}
|
|
6913
7683
|
|
|
6914
7684
|
// src/server/setup-events.ts
|
|
6915
|
-
import * as
|
|
7685
|
+
import * as fs11 from "fs/promises";
|
|
6916
7686
|
import { watch as fsWatch } from "fs";
|
|
6917
|
-
import * as
|
|
7687
|
+
import * as path14 from "path";
|
|
6918
7688
|
function setupEvents(deps2) {
|
|
6919
7689
|
const { events, broadcast: broadcast2, clients, config, context, pendingConfirms, globalConfigPath, sessionBridge, wpaths, watcherMetrics, onFleetBroadcaster } = deps2;
|
|
6920
7690
|
const disposers = [];
|
|
@@ -7003,6 +7773,22 @@ function setupEvents(deps2) {
|
|
|
7003
7773
|
}).catch(() => {
|
|
7004
7774
|
});
|
|
7005
7775
|
broadcast2(clients, { type: "todos.updated", payload: { todos: [...context.todos] } });
|
|
7776
|
+
const sideEffects = context.sideEffects ?? [];
|
|
7777
|
+
if (sideEffects.length > 0) {
|
|
7778
|
+
broadcast2(clients, {
|
|
7779
|
+
type: "side_effects",
|
|
7780
|
+
payload: {
|
|
7781
|
+
sideEffects: sideEffects.slice(-50).map((se) => ({
|
|
7782
|
+
toolUseId: se.toolUseId,
|
|
7783
|
+
toolName: se.toolName,
|
|
7784
|
+
ts: se.ts,
|
|
7785
|
+
input: se.input,
|
|
7786
|
+
outcome: se.outcome,
|
|
7787
|
+
risk: se.risk
|
|
7788
|
+
}))
|
|
7789
|
+
}
|
|
7790
|
+
});
|
|
7791
|
+
}
|
|
7006
7792
|
if (e.name === "task" || e.name === "plan" || e.name === "todo") {
|
|
7007
7793
|
void (async () => {
|
|
7008
7794
|
try {
|
|
@@ -7381,16 +8167,16 @@ function setupEvents(deps2) {
|
|
|
7381
8167
|
if (wpaths?.projectStatus) {
|
|
7382
8168
|
try {
|
|
7383
8169
|
const statusFile = wpaths.projectStatus(e.projectHash);
|
|
7384
|
-
const dir =
|
|
7385
|
-
await
|
|
7386
|
-
await
|
|
8170
|
+
const dir = path14.dirname(statusFile);
|
|
8171
|
+
await fs11.mkdir(dir, { recursive: true });
|
|
8172
|
+
await fs11.writeFile(statusFile, JSON.stringify(e, null, 2), "utf-8");
|
|
7387
8173
|
} catch (err) {
|
|
7388
8174
|
console.error("[setup-events] Failed to write status.json:", err);
|
|
7389
8175
|
}
|
|
7390
8176
|
}
|
|
7391
8177
|
});
|
|
7392
8178
|
if (wpaths?.projectStatus && wpaths.configDir) {
|
|
7393
|
-
const projectsDir =
|
|
8179
|
+
const projectsDir = path14.join(wpaths.configDir, "projects");
|
|
7394
8180
|
const knownProjectHashes = /* @__PURE__ */ new Set();
|
|
7395
8181
|
const debounceTimers = /* @__PURE__ */ new Map();
|
|
7396
8182
|
const DEBOUNCE_MS = 150;
|
|
@@ -7453,20 +8239,20 @@ function setupEvents(deps2) {
|
|
|
7453
8239
|
let watcher;
|
|
7454
8240
|
const startWatcher = async () => {
|
|
7455
8241
|
try {
|
|
7456
|
-
await
|
|
8242
|
+
await fs11.mkdir(projectsDir, { recursive: true });
|
|
7457
8243
|
watcher = fsWatch(projectsDir, { persistent: true, recursive: true }, async (eventType, filename) => {
|
|
7458
8244
|
if (eventType === "change") {
|
|
7459
8245
|
if (filename == null) return;
|
|
7460
8246
|
if (watcherMetrics) watcherMetrics.fileChangesDetected++;
|
|
7461
|
-
const targetFile =
|
|
8247
|
+
const targetFile = path14.join(projectsDir, String(filename));
|
|
7462
8248
|
if (targetFile.endsWith("status.json")) {
|
|
7463
|
-
const projectHash2 =
|
|
8249
|
+
const projectHash2 = path14.basename(path14.dirname(targetFile));
|
|
7464
8250
|
if (knownProjectHashes.size > 0 && !knownProjectHashes.has(projectHash2)) {
|
|
7465
8251
|
return;
|
|
7466
8252
|
}
|
|
7467
8253
|
if (watcherMetrics) watcherMetrics.filesProcessed++;
|
|
7468
8254
|
try {
|
|
7469
|
-
const content = await
|
|
8255
|
+
const content = await fs11.readFile(targetFile, "utf-8");
|
|
7470
8256
|
const statusData = JSON.parse(content);
|
|
7471
8257
|
if (statusData.projectHash) {
|
|
7472
8258
|
const hash = String(statusData.projectHash);
|
|
@@ -7518,7 +8304,7 @@ function setupEvents(deps2) {
|
|
|
7518
8304
|
}
|
|
7519
8305
|
});
|
|
7520
8306
|
}
|
|
7521
|
-
const globalRoot = globalConfigPath ?
|
|
8307
|
+
const globalRoot = globalConfigPath ? path14.dirname(globalConfigPath) : void 0;
|
|
7522
8308
|
if (globalRoot) {
|
|
7523
8309
|
const broadcastSessions = async () => {
|
|
7524
8310
|
try {
|
|
@@ -7591,11 +8377,11 @@ function setupEvents(deps2) {
|
|
|
7591
8377
|
|
|
7592
8378
|
// src/server/custom-context-modes.ts
|
|
7593
8379
|
import { listContextWindowModes, atomicWrite as atomicWrite5 } from "@wrongstack/core";
|
|
7594
|
-
import * as
|
|
7595
|
-
import * as
|
|
8380
|
+
import * as fs12 from "fs/promises";
|
|
8381
|
+
import * as path15 from "path";
|
|
7596
8382
|
var STORE_FILENAME = "custom-context-modes.json";
|
|
7597
8383
|
function storePath(wrongstackDir) {
|
|
7598
|
-
return
|
|
8384
|
+
return path15.join(wrongstackDir, STORE_FILENAME);
|
|
7599
8385
|
}
|
|
7600
8386
|
var BUILTIN_IDS = /* @__PURE__ */ new Set(["balanced", "frugal", "deep", "archival"]);
|
|
7601
8387
|
function createCustomModeStore(wrongstackDir) {
|
|
@@ -7603,7 +8389,7 @@ function createCustomModeStore(wrongstackDir) {
|
|
|
7603
8389
|
const load2 = async () => {
|
|
7604
8390
|
modes.clear();
|
|
7605
8391
|
try {
|
|
7606
|
-
const raw = await
|
|
8392
|
+
const raw = await fs12.readFile(storePath(wrongstackDir), "utf8");
|
|
7607
8393
|
const parsed = JSON.parse(raw);
|
|
7608
8394
|
if (Array.isArray(parsed.modes)) {
|
|
7609
8395
|
for (const m of parsed.modes) {
|
|
@@ -7649,7 +8435,8 @@ function createCustomModeStore(wrongstackDir) {
|
|
|
7649
8435
|
custom: true
|
|
7650
8436
|
};
|
|
7651
8437
|
modes.set(mode.id, entry);
|
|
7652
|
-
void save2()
|
|
8438
|
+
void save2().catch(() => {
|
|
8439
|
+
});
|
|
7653
8440
|
return { ok: true };
|
|
7654
8441
|
};
|
|
7655
8442
|
const update = (id, patch) => {
|
|
@@ -7675,7 +8462,8 @@ function createCustomModeStore(wrongstackDir) {
|
|
|
7675
8462
|
if (patch.targetLoad !== void 0) next.targetLoad = patch.targetLoad;
|
|
7676
8463
|
if (patch.aggressiveOn !== void 0) next.aggressiveOn = patch.aggressiveOn;
|
|
7677
8464
|
modes.set(id, next);
|
|
7678
|
-
void save2()
|
|
8465
|
+
void save2().catch(() => {
|
|
8466
|
+
});
|
|
7679
8467
|
return { ok: true };
|
|
7680
8468
|
};
|
|
7681
8469
|
const remove = (id) => {
|
|
@@ -7685,7 +8473,8 @@ function createCustomModeStore(wrongstackDir) {
|
|
|
7685
8473
|
if (!modes.delete(id)) {
|
|
7686
8474
|
return { ok: false, error: `Mode "${id}" not found` };
|
|
7687
8475
|
}
|
|
7688
|
-
void save2()
|
|
8476
|
+
void save2().catch(() => {
|
|
8477
|
+
});
|
|
7689
8478
|
return { ok: true };
|
|
7690
8479
|
};
|
|
7691
8480
|
const list = () => {
|
|
@@ -7726,14 +8515,17 @@ function createEternalSubscription(subscribe, broadcast2, clientsRef) {
|
|
|
7726
8515
|
}
|
|
7727
8516
|
|
|
7728
8517
|
// src/server/shell-open.ts
|
|
7729
|
-
import * as
|
|
7730
|
-
import * as
|
|
8518
|
+
import * as fs13 from "fs/promises";
|
|
8519
|
+
import * as path16 from "path";
|
|
7731
8520
|
import { spawn as spawn2 } from "child_process";
|
|
7732
|
-
var METACHAR_REGEX = /[&|<>^"'
|
|
8521
|
+
var METACHAR_REGEX = /[&|<>^"'`'\n\r]/;
|
|
8522
|
+
function shellQuote(s) {
|
|
8523
|
+
return s.replaceAll("'", `'"'"'`);
|
|
8524
|
+
}
|
|
7733
8525
|
async function handleShellOpen(req, logger) {
|
|
7734
8526
|
try {
|
|
7735
|
-
const resolved =
|
|
7736
|
-
await
|
|
8527
|
+
const resolved = path16.resolve(req.path);
|
|
8528
|
+
await fs13.access(resolved);
|
|
7737
8529
|
if (METACHAR_REGEX.test(resolved)) {
|
|
7738
8530
|
return { success: false, message: "Path contains unsupported characters." };
|
|
7739
8531
|
}
|
|
@@ -7766,7 +8558,11 @@ async function handleShellOpen(req, logger) {
|
|
|
7766
8558
|
() => launch(
|
|
7767
8559
|
"gnome-terminal",
|
|
7768
8560
|
[`--working-directory=${resolved}`],
|
|
7769
|
-
() =>
|
|
8561
|
+
() => (
|
|
8562
|
+
// Pass argv array so sh -c sees a literal string, not an interpolated one.
|
|
8563
|
+
// shellQuote() guards against paths that somehow slipped the METACHAR_REGEX.
|
|
8564
|
+
launch("xterm", ["-e", "sh", "-c", `cd ${shellQuote(resolved)} && ${process.env["SHELL"] ?? "sh"}`])
|
|
8565
|
+
)
|
|
7770
8566
|
)
|
|
7771
8567
|
);
|
|
7772
8568
|
}
|
|
@@ -7785,9 +8581,9 @@ async function handleGitInfo(ws, projectRoot) {
|
|
|
7785
8581
|
const cwd = projectRoot || void 0;
|
|
7786
8582
|
try {
|
|
7787
8583
|
const { execFile: ef } = await import("child_process");
|
|
7788
|
-
const git = (args) => new Promise((
|
|
8584
|
+
const git = (args) => new Promise((resolve10) => {
|
|
7789
8585
|
ef("git", args, { cwd, timeout: 3e3 }, (err, stdout) => {
|
|
7790
|
-
|
|
8586
|
+
resolve10(err ? "" : stdout.trim());
|
|
7791
8587
|
});
|
|
7792
8588
|
});
|
|
7793
8589
|
const [branchRaw, diffRaw, statusRaw, upstreamRaw] = await Promise.all([
|
|
@@ -7813,12 +8609,12 @@ async function handleGitInfo(ws, projectRoot) {
|
|
|
7813
8609
|
function makeGit(cwd) {
|
|
7814
8610
|
return async (args) => {
|
|
7815
8611
|
const { execFile: ef } = await import("child_process");
|
|
7816
|
-
return new Promise((
|
|
8612
|
+
return new Promise((resolve10) => {
|
|
7817
8613
|
ef(
|
|
7818
8614
|
"git",
|
|
7819
8615
|
args,
|
|
7820
8616
|
{ cwd, timeout: 5e3, maxBuffer: 1024 * 1024 * 16 },
|
|
7821
|
-
(err, stdout) =>
|
|
8617
|
+
(err, stdout) => resolve10(err ? "" : stdout)
|
|
7822
8618
|
);
|
|
7823
8619
|
});
|
|
7824
8620
|
};
|
|
@@ -7842,15 +8638,15 @@ async function handleGitChanges(ws, projectRoot) {
|
|
|
7842
8638
|
if (!m) continue;
|
|
7843
8639
|
const added = m[1] === "-" ? 0 : Number(m[1]);
|
|
7844
8640
|
const deleted = m[2] === "-" ? 0 : Number(m[2]);
|
|
7845
|
-
let
|
|
7846
|
-
if (
|
|
8641
|
+
let path18 = m[3] ?? "";
|
|
8642
|
+
if (path18 === "") {
|
|
7847
8643
|
i += 1;
|
|
7848
|
-
|
|
8644
|
+
path18 = parts[i + 1] ?? parts[i] ?? "";
|
|
7849
8645
|
i += 1;
|
|
7850
8646
|
}
|
|
7851
|
-
if (!
|
|
7852
|
-
const prev = counts.get(
|
|
7853
|
-
counts.set(
|
|
8647
|
+
if (!path18) continue;
|
|
8648
|
+
const prev = counts.get(path18) ?? { added: 0, deleted: 0 };
|
|
8649
|
+
counts.set(path18, { added: prev.added + added, deleted: prev.deleted + deleted });
|
|
7854
8650
|
}
|
|
7855
8651
|
};
|
|
7856
8652
|
parseNumstat(unstagedNumstat);
|
|
@@ -7862,7 +8658,7 @@ async function handleGitChanges(ws, projectRoot) {
|
|
|
7862
8658
|
if (!rec || rec.length < 3) continue;
|
|
7863
8659
|
const x = rec[0] ?? " ";
|
|
7864
8660
|
const y = rec[1] ?? " ";
|
|
7865
|
-
const
|
|
8661
|
+
const path18 = rec.slice(3);
|
|
7866
8662
|
const isRename = x === "R" || x === "C" || y === "R" || y === "C";
|
|
7867
8663
|
if (isRename) i += 1;
|
|
7868
8664
|
let status;
|
|
@@ -7874,13 +8670,13 @@ async function handleGitChanges(ws, projectRoot) {
|
|
|
7874
8670
|
else if (x === "D" || y === "D") status = "D";
|
|
7875
8671
|
else status = "M";
|
|
7876
8672
|
const staged = x !== " " && x !== "?";
|
|
7877
|
-
let added = counts.get(
|
|
7878
|
-
let deleted = counts.get(
|
|
8673
|
+
let added = counts.get(path18)?.added ?? 0;
|
|
8674
|
+
let deleted = counts.get(path18)?.deleted ?? 0;
|
|
7879
8675
|
if (status === "?") {
|
|
7880
8676
|
added = 0;
|
|
7881
8677
|
deleted = 0;
|
|
7882
8678
|
}
|
|
7883
|
-
files.push({ path:
|
|
8679
|
+
files.push({ path: path18, status, added, deleted, staged });
|
|
7884
8680
|
}
|
|
7885
8681
|
send(ws, { type: "git.changes", payload: { files } });
|
|
7886
8682
|
} catch (err) {
|
|
@@ -7891,21 +8687,21 @@ async function handleGitChanges(ws, projectRoot) {
|
|
|
7891
8687
|
}
|
|
7892
8688
|
}
|
|
7893
8689
|
var MAX_DIFF_BYTES = 2 * 1024 * 1024;
|
|
7894
|
-
async function handleGitDiff(ws, projectRoot,
|
|
8690
|
+
async function handleGitDiff(ws, projectRoot, path18) {
|
|
7895
8691
|
const cwd = projectRoot || void 0;
|
|
7896
|
-
const reply = (extra) => send(ws, { type: "git.diff", payload: { path:
|
|
7897
|
-
if (!
|
|
8692
|
+
const reply = (extra) => send(ws, { type: "git.diff", payload: { path: path18, ...extra } });
|
|
8693
|
+
if (!path18 || path18.includes("\0") || path18.includes("..") || nodePath.isAbsolute(path18)) {
|
|
7898
8694
|
reply({ oldText: "", newText: "", error: "invalid path" });
|
|
7899
8695
|
return;
|
|
7900
8696
|
}
|
|
7901
8697
|
try {
|
|
7902
8698
|
const git = makeGit(cwd);
|
|
7903
8699
|
const { readFile: readFile9 } = await import("fs/promises");
|
|
7904
|
-
const { join:
|
|
7905
|
-
const oldText = await git(["show", `HEAD:${
|
|
8700
|
+
const { join: join14 } = await import("path");
|
|
8701
|
+
const oldText = await git(["show", `HEAD:${path18}`]);
|
|
7906
8702
|
let newText = "";
|
|
7907
8703
|
try {
|
|
7908
|
-
const abs = cwd ?
|
|
8704
|
+
const abs = cwd ? join14(cwd, path18) : path18;
|
|
7909
8705
|
const buf = await readFile9(abs);
|
|
7910
8706
|
if (buf.includes(0)) {
|
|
7911
8707
|
reply({ oldText: "", newText: "", binary: true });
|
|
@@ -7986,10 +8782,10 @@ async function handleProcessKillAll(ws) {
|
|
|
7986
8782
|
}
|
|
7987
8783
|
|
|
7988
8784
|
// src/server/goal-handlers.ts
|
|
7989
|
-
import { resolveWstackPaths } from "@wrongstack/core/utils";
|
|
8785
|
+
import { resolveWstackPaths as resolveWstackPaths3 } from "@wrongstack/core/utils";
|
|
7990
8786
|
async function handleGoalGet(projectRoot, broadcast2) {
|
|
7991
8787
|
try {
|
|
7992
|
-
const goalPath =
|
|
8788
|
+
const goalPath = resolveWstackPaths3({ projectRoot }).projectGoal;
|
|
7993
8789
|
const { readFile: readFile9 } = await import("fs/promises");
|
|
7994
8790
|
const raw = await readFile9(goalPath, "utf8");
|
|
7995
8791
|
const goal = JSON.parse(raw);
|
|
@@ -8047,7 +8843,7 @@ async function startWebUI(opts = {}) {
|
|
|
8047
8843
|
const write = async () => {
|
|
8048
8844
|
let raw;
|
|
8049
8845
|
try {
|
|
8050
|
-
raw = await
|
|
8846
|
+
raw = await fs14.readFile(globalConfigPath, "utf8");
|
|
8051
8847
|
} catch {
|
|
8052
8848
|
raw = "{}";
|
|
8053
8849
|
}
|
|
@@ -8121,6 +8917,7 @@ async function startWebUI(opts = {}) {
|
|
|
8121
8917
|
toolRegistry.register(makeMailSendTool({ projectDir: wpaths.projectDir, events }));
|
|
8122
8918
|
toolRegistry.register(makeMailInboxTool({ projectDir: wpaths.projectDir, events }));
|
|
8123
8919
|
applyToolDescriptionModes(toolRegistry, config.tools?.descriptionMode);
|
|
8920
|
+
applyToolResultRenderModes(toolRegistry, config.tools?.resultRenderMode);
|
|
8124
8921
|
configureExecPolicy(config.tools?.exec ?? {});
|
|
8125
8922
|
console.log("[WebUI] Tool registry loaded:", toolRegistry.list().length, "tools");
|
|
8126
8923
|
const mcpRegistry = new MCPRegistry({
|
|
@@ -8165,7 +8962,7 @@ async function startWebUI(opts = {}) {
|
|
|
8165
8962
|
sessionId: session.id,
|
|
8166
8963
|
projectSlug: wpaths.projectSlug,
|
|
8167
8964
|
projectRoot,
|
|
8168
|
-
projectName:
|
|
8965
|
+
projectName: path17.basename(projectRoot),
|
|
8169
8966
|
workingDir,
|
|
8170
8967
|
clientType: "webui",
|
|
8171
8968
|
pid: process.pid,
|
|
@@ -8185,7 +8982,7 @@ async function startWebUI(opts = {}) {
|
|
|
8185
8982
|
const hqTelemetry = createHqPublisherFromEnv({
|
|
8186
8983
|
clientKind: "webui",
|
|
8187
8984
|
projectRoot,
|
|
8188
|
-
projectName:
|
|
8985
|
+
projectName: path17.basename(projectRoot),
|
|
8189
8986
|
appConfig: config,
|
|
8190
8987
|
socketFactory: (url) => new WebSocket2(url)
|
|
8191
8988
|
});
|
|
@@ -8197,7 +8994,7 @@ async function startWebUI(opts = {}) {
|
|
|
8197
8994
|
events,
|
|
8198
8995
|
sessionId: session.id,
|
|
8199
8996
|
projectRoot,
|
|
8200
|
-
projectName:
|
|
8997
|
+
projectName: path17.basename(projectRoot),
|
|
8201
8998
|
globalRoot: wpaths.globalRoot,
|
|
8202
8999
|
initialAgents: statusTracker?.getAgents(),
|
|
8203
9000
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8253,19 +9050,39 @@ async function startWebUI(opts = {}) {
|
|
|
8253
9050
|
};
|
|
8254
9051
|
const skillLoader = config.features.skills ? new DefaultSkillLoader({ paths: wpaths }) : void 0;
|
|
8255
9052
|
const skillInstaller = config.features.skills ? new SkillInstaller({
|
|
8256
|
-
manifestPath:
|
|
8257
|
-
projectSkillsDir:
|
|
8258
|
-
globalSkillsDir:
|
|
9053
|
+
manifestPath: path17.join(wstackGlobalRoot3(), "installed-skills.json"),
|
|
9054
|
+
projectSkillsDir: path17.join(projectRoot, ".wrongstack", "skills"),
|
|
9055
|
+
globalSkillsDir: path17.join(wstackGlobalRoot3(), "skills"),
|
|
8259
9056
|
projectHash: projectHash(projectRoot),
|
|
8260
9057
|
skillLoader
|
|
8261
9058
|
}) : void 0;
|
|
9059
|
+
const promptsEnabled = config.features.prompts !== false;
|
|
9060
|
+
const bundledPromptsDir = promptsEnabled ? (() => {
|
|
9061
|
+
try {
|
|
9062
|
+
const req = createRequire2(import.meta.url);
|
|
9063
|
+
return path17.join(
|
|
9064
|
+
path17.dirname(req.resolve("@wrongstack/core/package.json")),
|
|
9065
|
+
"data",
|
|
9066
|
+
"prompts"
|
|
9067
|
+
);
|
|
9068
|
+
} catch {
|
|
9069
|
+
return void 0;
|
|
9070
|
+
}
|
|
9071
|
+
})() : void 0;
|
|
9072
|
+
const promptLoader = promptsEnabled ? new DefaultPromptLoader({ paths: wpaths, bundledDir: bundledPromptsDir }) : void 0;
|
|
9073
|
+
const promptUsage = new PromptUsageStore(wpaths.promptUsage);
|
|
9074
|
+
const promptsCtx = { promptLoader, promptUsage };
|
|
8262
9075
|
const systemPromptBuilder = new DefaultSystemPromptBuilder3({
|
|
8263
9076
|
memoryStore,
|
|
8264
9077
|
skillLoader,
|
|
8265
9078
|
modeStore,
|
|
8266
9079
|
modeId,
|
|
8267
9080
|
modePrompt,
|
|
8268
|
-
modelCapabilities: () => modelCapabilitiesRef.current
|
|
9081
|
+
modelCapabilities: () => modelCapabilitiesRef.current,
|
|
9082
|
+
instructionPaths: {
|
|
9083
|
+
globalDir: wpaths.globalInstructions,
|
|
9084
|
+
projectDir: wpaths.inProjectInstructions
|
|
9085
|
+
}
|
|
8269
9086
|
});
|
|
8270
9087
|
let onlineAgents = [];
|
|
8271
9088
|
try {
|
|
@@ -8360,6 +9177,10 @@ async function startWebUI(opts = {}) {
|
|
|
8360
9177
|
context.meta["enhanceLanguage"] = autonomyCfg["enhanceLanguage"] ?? "original";
|
|
8361
9178
|
context.meta["nextPrediction"] = config.nextPrediction ?? false;
|
|
8362
9179
|
context.meta["fallbackModels"] = config.fallbackModels ?? [];
|
|
9180
|
+
context.meta["fallbackProfiles"] = config.fallbackProfiles ?? {};
|
|
9181
|
+
context.meta["favoriteModels"] = config.favoriteModels ?? [];
|
|
9182
|
+
context.meta["favoriteModelsOnly"] = config.favoriteModelsOnly === true;
|
|
9183
|
+
context.meta["modelMatrix"] = config.modelMatrix ?? {};
|
|
8363
9184
|
context.meta["fallbackAuto"] = config.fallbackAuto !== false;
|
|
8364
9185
|
context.meta["featureMcp"] = config.features.mcp !== false;
|
|
8365
9186
|
context.meta["featurePlugins"] = config.features.plugins !== false;
|
|
@@ -8372,6 +9193,20 @@ async function startWebUI(opts = {}) {
|
|
|
8372
9193
|
context.meta["logLevel"] = config.log?.level ?? "info";
|
|
8373
9194
|
context.meta["auditLevel"] = config.session?.auditLevel ?? "standard";
|
|
8374
9195
|
context.meta["maxIterations"] = config.tools?.maxIterations ?? 500;
|
|
9196
|
+
context.meta["contextMode"] = config.context?.mode ?? "balanced";
|
|
9197
|
+
{
|
|
9198
|
+
const tsm = config.features?.tokenSavingMode;
|
|
9199
|
+
context.meta["tokenSavingTier"] = typeof tsm === "string" ? tsm : tsm ? "medium" : "off";
|
|
9200
|
+
}
|
|
9201
|
+
context.meta["maxConcurrent"] = typeof config.maxConcurrent === "number" ? config.maxConcurrent : 10;
|
|
9202
|
+
context.meta["titleAnimation"] = autonomyCfg["terminalTitleAnimation"] !== false;
|
|
9203
|
+
{
|
|
9204
|
+
const mr = config.modelRuntime ?? {};
|
|
9205
|
+
context.meta["reasoningMode"] = mr.reasoning?.mode ?? "auto";
|
|
9206
|
+
context.meta["reasoningEffort"] = mr.reasoning?.effort ?? "high";
|
|
9207
|
+
context.meta["reasoningPreserve"] = mr.reasoning?.preserve === true;
|
|
9208
|
+
context.meta["cacheTtl"] = mr.cache?.ttl ?? "default";
|
|
9209
|
+
}
|
|
8375
9210
|
const hqConfig = config.hq;
|
|
8376
9211
|
context.meta["hqEnabled"] = hqConfig?.enabled === true;
|
|
8377
9212
|
context.meta["hqUrl"] = hqConfig?.url ?? "";
|
|
@@ -8405,6 +9240,10 @@ async function startWebUI(opts = {}) {
|
|
|
8405
9240
|
"indexOnStart",
|
|
8406
9241
|
"contextAutoCompact",
|
|
8407
9242
|
"contextStrategy",
|
|
9243
|
+
"contextMode",
|
|
9244
|
+
"tokenSavingTier",
|
|
9245
|
+
"maxConcurrent",
|
|
9246
|
+
"titleAnimation",
|
|
8408
9247
|
"logLevel",
|
|
8409
9248
|
"auditLevel",
|
|
8410
9249
|
"hqEnabled",
|
|
@@ -8420,6 +9259,10 @@ async function startWebUI(opts = {}) {
|
|
|
8420
9259
|
"reasoningPreserve",
|
|
8421
9260
|
"cacheTtl",
|
|
8422
9261
|
"fallbackModels",
|
|
9262
|
+
"fallbackProfiles",
|
|
9263
|
+
"favoriteModels",
|
|
9264
|
+
"favoriteModelsOnly",
|
|
9265
|
+
"modelMatrix",
|
|
8423
9266
|
"fallbackAuto"
|
|
8424
9267
|
];
|
|
8425
9268
|
const prefSnapshot = () => {
|
|
@@ -8452,6 +9295,15 @@ async function startWebUI(opts = {}) {
|
|
|
8452
9295
|
if (autonomyTouched) decrypted.autonomy = autonomyCfg;
|
|
8453
9296
|
if (typeof payload["nextPrediction"] === "boolean") decrypted.nextPrediction = payload["nextPrediction"];
|
|
8454
9297
|
if (Array.isArray(payload["fallbackModels"])) decrypted.fallbackModels = payload["fallbackModels"];
|
|
9298
|
+
if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"])) {
|
|
9299
|
+
decrypted.fallbackProfiles = payload["fallbackProfiles"];
|
|
9300
|
+
}
|
|
9301
|
+
if (Array.isArray(payload["favoriteModels"])) decrypted.favoriteModels = payload["favoriteModels"];
|
|
9302
|
+
if (typeof payload["favoriteModelsOnly"] === "boolean")
|
|
9303
|
+
decrypted.favoriteModelsOnly = payload["favoriteModelsOnly"];
|
|
9304
|
+
if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
|
|
9305
|
+
decrypted.modelMatrix = payload["modelMatrix"];
|
|
9306
|
+
}
|
|
8455
9307
|
if (typeof payload["fallbackAuto"] === "boolean") decrypted.fallbackAuto = payload["fallbackAuto"];
|
|
8456
9308
|
const FEATURE_MAP = {
|
|
8457
9309
|
featureMcp: "mcp",
|
|
@@ -8467,12 +9319,26 @@ async function startWebUI(opts = {}) {
|
|
|
8467
9319
|
decrypted.features = feats;
|
|
8468
9320
|
}
|
|
8469
9321
|
}
|
|
8470
|
-
if (typeof payload["contextAutoCompact"] === "boolean" || typeof payload["contextStrategy"] === "string") {
|
|
9322
|
+
if (typeof payload["contextAutoCompact"] === "boolean" || typeof payload["contextStrategy"] === "string" || typeof payload["contextMode"] === "string") {
|
|
8471
9323
|
const ctxCfg = decrypted.context ?? {};
|
|
8472
9324
|
if (typeof payload["contextAutoCompact"] === "boolean") ctxCfg.autoCompact = payload["contextAutoCompact"];
|
|
8473
9325
|
if (typeof payload["contextStrategy"] === "string") ctxCfg.strategy = payload["contextStrategy"];
|
|
9326
|
+
if (typeof payload["contextMode"] === "string") ctxCfg.mode = payload["contextMode"];
|
|
8474
9327
|
decrypted.context = ctxCfg;
|
|
8475
9328
|
}
|
|
9329
|
+
if (typeof payload["tokenSavingTier"] === "string") {
|
|
9330
|
+
const featsCfg = decrypted.features ?? {};
|
|
9331
|
+
featsCfg.tokenSavingMode = payload["tokenSavingTier"];
|
|
9332
|
+
decrypted.features = featsCfg;
|
|
9333
|
+
}
|
|
9334
|
+
if (typeof payload["maxConcurrent"] === "number") {
|
|
9335
|
+
decrypted.maxConcurrent = payload["maxConcurrent"];
|
|
9336
|
+
}
|
|
9337
|
+
if (typeof payload["titleAnimation"] === "boolean") {
|
|
9338
|
+
const autoCfg = decrypted.autonomy ?? {};
|
|
9339
|
+
autoCfg.terminalTitleAnimation = payload["titleAnimation"];
|
|
9340
|
+
decrypted.autonomy = autoCfg;
|
|
9341
|
+
}
|
|
8476
9342
|
if (typeof payload["logLevel"] === "string") {
|
|
8477
9343
|
const logCfg = decrypted.log ?? {};
|
|
8478
9344
|
logCfg.level = payload["logLevel"];
|
|
@@ -8543,6 +9409,7 @@ async function startWebUI(opts = {}) {
|
|
|
8543
9409
|
const collabInject = collabInjectMiddleware(collabBus, { logger });
|
|
8544
9410
|
Object.defineProperty(collabInject, "name", { value: "collab-inject" });
|
|
8545
9411
|
pipelines.toolCall.prepend(collabInject);
|
|
9412
|
+
installDesignStudioMiddleware({ pipelines, ctx: context });
|
|
8546
9413
|
const codebaseIndexing = setupWebUICodebaseIndexing({
|
|
8547
9414
|
config,
|
|
8548
9415
|
context,
|
|
@@ -8638,6 +9505,17 @@ async function startWebUI(opts = {}) {
|
|
|
8638
9505
|
perIterationOutputCapBytes: config.tools?.perIterationOutputCapBytes ?? DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,
|
|
8639
9506
|
tracer: void 0
|
|
8640
9507
|
});
|
|
9508
|
+
const webuiLogger = container.resolve(TOKENS.Logger);
|
|
9509
|
+
void discoverMailboxBridgeForWebui({
|
|
9510
|
+
projectRoot,
|
|
9511
|
+
config,
|
|
9512
|
+
logger: webuiLogger,
|
|
9513
|
+
ctx: context
|
|
9514
|
+
}).catch((err) => {
|
|
9515
|
+
webuiLogger.warn("mailbox bridge discovery threw on webui boot", {
|
|
9516
|
+
err: err instanceof Error ? err.message : String(err)
|
|
9517
|
+
});
|
|
9518
|
+
});
|
|
8641
9519
|
const agent = new Agent({
|
|
8642
9520
|
container,
|
|
8643
9521
|
tools: toolRegistry,
|
|
@@ -8734,7 +9612,18 @@ async function startWebUI(opts = {}) {
|
|
|
8734
9612
|
projectRoot
|
|
8735
9613
|
);
|
|
8736
9614
|
const specsHandler = new SpecsWebSocketHandler(wpaths.projectSpecs, wpaths.projectTaskGraphs);
|
|
8737
|
-
const sddBoardHandler = new SddBoardWebSocketHandler(wpaths.projectSddBoards
|
|
9615
|
+
const sddBoardHandler = new SddBoardWebSocketHandler(wpaths.projectSddBoards, void 0, {
|
|
9616
|
+
projectRoot,
|
|
9617
|
+
paths: {
|
|
9618
|
+
projectSpecs: wpaths.projectSpecs,
|
|
9619
|
+
projectTaskGraphs: wpaths.projectTaskGraphs,
|
|
9620
|
+
projectSddSession: wpaths.projectSddSession,
|
|
9621
|
+
projectSddBoards: wpaths.projectSddBoards
|
|
9622
|
+
}
|
|
9623
|
+
});
|
|
9624
|
+
void cleanupStaleSddWorktrees3({ projectRoot, boardsDir: wpaths.projectSddBoards }).catch(
|
|
9625
|
+
() => void 0
|
|
9626
|
+
);
|
|
8738
9627
|
const sddWizardHandler = new SddWizardWebSocketHandler(
|
|
8739
9628
|
buildSddWizardDeps({
|
|
8740
9629
|
agent,
|
|
@@ -8756,7 +9645,10 @@ async function startWebUI(opts = {}) {
|
|
|
8756
9645
|
}
|
|
8757
9646
|
})
|
|
8758
9647
|
);
|
|
8759
|
-
const worktreeHandler = new WorktreeWebSocketHandler(events, logger
|
|
9648
|
+
const worktreeHandler = new WorktreeWebSocketHandler(events, logger, {
|
|
9649
|
+
projectRoot,
|
|
9650
|
+
boardsDir: wpaths.projectSddBoards
|
|
9651
|
+
});
|
|
8760
9652
|
const terminalHandler = new TerminalWebSocketHandler(() => workingDir, logger);
|
|
8761
9653
|
const collabHandler = new CollaborationWebSocketHandler(
|
|
8762
9654
|
events,
|
|
@@ -8795,7 +9687,7 @@ async function startWebUI(opts = {}) {
|
|
|
8795
9687
|
inputCost,
|
|
8796
9688
|
outputCost,
|
|
8797
9689
|
cacheReadCost,
|
|
8798
|
-
projectName:
|
|
9690
|
+
projectName: path17.basename(projectRoot) || projectRoot,
|
|
8799
9691
|
projectRoot,
|
|
8800
9692
|
cwd: workingDir,
|
|
8801
9693
|
mode: modeId,
|
|
@@ -8944,8 +9836,8 @@ async function startWebUI(opts = {}) {
|
|
|
8944
9836
|
clients.delete(ws);
|
|
8945
9837
|
if (closing) rateLimits.delete(closing.connId);
|
|
8946
9838
|
if (pendingConfirms.size > 0) {
|
|
8947
|
-
for (const [id,
|
|
8948
|
-
|
|
9839
|
+
for (const [id, resolve10] of pendingConfirms) {
|
|
9840
|
+
resolve10("no");
|
|
8949
9841
|
pendingConfirms.delete(id);
|
|
8950
9842
|
}
|
|
8951
9843
|
}
|
|
@@ -9025,21 +9917,21 @@ async function startWebUI(opts = {}) {
|
|
|
9025
9917
|
});
|
|
9026
9918
|
}
|
|
9027
9919
|
async function touchProjectEntry(root, workDir) {
|
|
9028
|
-
const resolved =
|
|
9920
|
+
const resolved = path17.resolve(root);
|
|
9029
9921
|
const manifest = await loadManifest(globalConfigPath);
|
|
9030
9922
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9031
|
-
const existing = manifest.projects.find((p) =>
|
|
9923
|
+
const existing = manifest.projects.find((p) => path17.resolve(p.root) === resolved);
|
|
9032
9924
|
if (existing) {
|
|
9033
9925
|
existing.lastSeen = now;
|
|
9034
|
-
if (workDir) existing.lastWorkingDir =
|
|
9926
|
+
if (workDir) existing.lastWorkingDir = path17.resolve(workDir);
|
|
9035
9927
|
} else {
|
|
9036
9928
|
manifest.projects.push({
|
|
9037
|
-
name:
|
|
9929
|
+
name: path17.basename(resolved),
|
|
9038
9930
|
root: resolved,
|
|
9039
9931
|
slug: generateProjectSlug(resolved),
|
|
9040
9932
|
createdAt: now,
|
|
9041
9933
|
lastSeen: now,
|
|
9042
|
-
lastWorkingDir: workDir ?
|
|
9934
|
+
lastWorkingDir: workDir ? path17.resolve(workDir) : void 0
|
|
9043
9935
|
});
|
|
9044
9936
|
}
|
|
9045
9937
|
await saveManifest(manifest, globalConfigPath);
|
|
@@ -9084,6 +9976,8 @@ async function startWebUI(opts = {}) {
|
|
|
9084
9976
|
if (await handleSpecsRoute(ws, msg, specsRoutes)) return;
|
|
9085
9977
|
if (await handleSddBoardRoute(ws, msg, sddBoardRoutes)) return;
|
|
9086
9978
|
if (await handleSddWizardRoute(ws, msg, sddWizardRoutes)) return;
|
|
9979
|
+
if (msg.type.startsWith("worktree.") && await worktreeHandler.handleMessage(msg))
|
|
9980
|
+
return;
|
|
9087
9981
|
switch (msg.type) {
|
|
9088
9982
|
// Collaboration messages short-circuit the user/agent flow.
|
|
9089
9983
|
// They don't touch runLock, the agent loop, or the message queue —
|
|
@@ -9154,10 +10048,10 @@ async function startWebUI(opts = {}) {
|
|
|
9154
10048
|
}
|
|
9155
10049
|
case "tool.confirm_result": {
|
|
9156
10050
|
const { id, decision } = msg.payload;
|
|
9157
|
-
const
|
|
9158
|
-
if (
|
|
10051
|
+
const resolve10 = pendingConfirms.get(id);
|
|
10052
|
+
if (resolve10) {
|
|
9159
10053
|
pendingConfirms.delete(id);
|
|
9160
|
-
|
|
10054
|
+
resolve10(decision);
|
|
9161
10055
|
}
|
|
9162
10056
|
break;
|
|
9163
10057
|
}
|
|
@@ -9241,6 +10135,48 @@ async function startWebUI(opts = {}) {
|
|
|
9241
10135
|
case "skills.export":
|
|
9242
10136
|
await handleSkillsExport(ws, { skillLoader, skillInstaller, projectRoot });
|
|
9243
10137
|
break;
|
|
10138
|
+
// Prompt library — shared handlers (prompts-handlers.ts).
|
|
10139
|
+
case "prompts.list":
|
|
10140
|
+
await handlePromptsList(ws, promptsCtx);
|
|
10141
|
+
break;
|
|
10142
|
+
case "prompts.search":
|
|
10143
|
+
await handlePromptsSearch(ws, promptsCtx, msg);
|
|
10144
|
+
break;
|
|
10145
|
+
case "prompts.content":
|
|
10146
|
+
await handlePromptsContent(ws, promptsCtx, msg);
|
|
10147
|
+
break;
|
|
10148
|
+
case "prompts.favorite":
|
|
10149
|
+
await handlePromptsFavorite(ws, promptsCtx, msg);
|
|
10150
|
+
break;
|
|
10151
|
+
case "prompts.create":
|
|
10152
|
+
await handlePromptsCreate(ws, promptsCtx, msg);
|
|
10153
|
+
break;
|
|
10154
|
+
case "prompts.used":
|
|
10155
|
+
await handlePromptsUsed(ws, promptsCtx, msg);
|
|
10156
|
+
break;
|
|
10157
|
+
case "prompts.recent":
|
|
10158
|
+
await handlePromptsRecent(ws, promptsCtx);
|
|
10159
|
+
break;
|
|
10160
|
+
// Design Studio — shared handlers (design-handlers.ts). agentMeta is the
|
|
10161
|
+
// live context so design.use pins the active kit for the next turn.
|
|
10162
|
+
case "design.list":
|
|
10163
|
+
await handleDesignList(ws, { projectRoot, agentMeta: context });
|
|
10164
|
+
break;
|
|
10165
|
+
case "design.use":
|
|
10166
|
+
await handleDesignUse(ws, { projectRoot, agentMeta: context }, msg);
|
|
10167
|
+
break;
|
|
10168
|
+
case "design.state":
|
|
10169
|
+
await handleDesignState(ws, { projectRoot, agentMeta: context });
|
|
10170
|
+
break;
|
|
10171
|
+
case "design.set":
|
|
10172
|
+
await handleDesignSet(ws, { projectRoot, agentMeta: context }, msg);
|
|
10173
|
+
break;
|
|
10174
|
+
case "design.materialize":
|
|
10175
|
+
await handleDesignMaterialize(ws, { projectRoot, agentMeta: context }, msg);
|
|
10176
|
+
break;
|
|
10177
|
+
case "design.verify":
|
|
10178
|
+
await handleDesignVerify(ws, { projectRoot, agentMeta: context });
|
|
10179
|
+
break;
|
|
9244
10180
|
case "diag.get": {
|
|
9245
10181
|
const usage = tokenCounter.total();
|
|
9246
10182
|
send(ws, {
|
|
@@ -9321,11 +10257,29 @@ async function startWebUI(opts = {}) {
|
|
|
9321
10257
|
messages: context.messages.length,
|
|
9322
10258
|
readFiles: context.readFiles.size,
|
|
9323
10259
|
tools: toolRegistry.list().length,
|
|
10260
|
+
sideEffectCount: context.sideEffects?.length ?? 0,
|
|
9324
10261
|
elapsedMs: Date.now() - sessionStartedAt
|
|
9325
10262
|
}
|
|
9326
10263
|
});
|
|
9327
10264
|
break;
|
|
9328
10265
|
}
|
|
10266
|
+
case "side_effects.list": {
|
|
10267
|
+
const sideEffects = context.sideEffects ?? [];
|
|
10268
|
+
send(ws, {
|
|
10269
|
+
type: "side_effects",
|
|
10270
|
+
payload: {
|
|
10271
|
+
sideEffects: sideEffects.slice(-50).map((se) => ({
|
|
10272
|
+
toolUseId: se.toolUseId,
|
|
10273
|
+
toolName: se.toolName,
|
|
10274
|
+
ts: se.ts,
|
|
10275
|
+
input: se.input,
|
|
10276
|
+
outcome: se.outcome,
|
|
10277
|
+
risk: se.risk
|
|
10278
|
+
}))
|
|
10279
|
+
}
|
|
10280
|
+
});
|
|
10281
|
+
break;
|
|
10282
|
+
}
|
|
9329
10283
|
case "process.list": {
|
|
9330
10284
|
await handleProcessList(ws);
|
|
9331
10285
|
break;
|
|
@@ -9580,6 +10534,7 @@ async function startWebUI(opts = {}) {
|
|
|
9580
10534
|
toolRegistry,
|
|
9581
10535
|
config,
|
|
9582
10536
|
projectRoot,
|
|
10537
|
+
globalRoot: wpaths.globalRoot,
|
|
9583
10538
|
clients,
|
|
9584
10539
|
setModeId: (id) => {
|
|
9585
10540
|
modeId = id;
|
|
@@ -9616,6 +10571,16 @@ async function startWebUI(opts = {}) {
|
|
|
9616
10571
|
config.features.modelsRegistry = payload["featureModelsRegistry"];
|
|
9617
10572
|
if (Array.isArray(payload["fallbackModels"]))
|
|
9618
10573
|
config.fallbackModels = payload["fallbackModels"];
|
|
10574
|
+
if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"])) {
|
|
10575
|
+
config.fallbackProfiles = payload["fallbackProfiles"];
|
|
10576
|
+
}
|
|
10577
|
+
if (Array.isArray(payload["favoriteModels"]))
|
|
10578
|
+
config.favoriteModels = payload["favoriteModels"];
|
|
10579
|
+
if (typeof payload["favoriteModelsOnly"] === "boolean")
|
|
10580
|
+
config.favoriteModelsOnly = payload["favoriteModelsOnly"];
|
|
10581
|
+
if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
|
|
10582
|
+
config.modelMatrix = payload["modelMatrix"];
|
|
10583
|
+
}
|
|
9619
10584
|
if (typeof payload["fallbackAuto"] === "boolean")
|
|
9620
10585
|
config.fallbackAuto = payload["fallbackAuto"];
|
|
9621
10586
|
if (typeof payload["contextAutoCompact"] === "boolean") {
|
|
@@ -9667,7 +10632,7 @@ async function startWebUI(opts = {}) {
|
|
|
9667
10632
|
sendResult2(ws, false, parsed.message);
|
|
9668
10633
|
return;
|
|
9669
10634
|
}
|
|
9670
|
-
return handleMailboxMessages(ws, { projectRoot, globalRoot:
|
|
10635
|
+
return handleMailboxMessages(ws, { projectRoot, globalRoot: path17.dirname(globalConfigPath) }, parsed.value);
|
|
9671
10636
|
},
|
|
9672
10637
|
agents: (ws, msg) => {
|
|
9673
10638
|
const parsed = validateMailboxAgentsPayload(msg.payload);
|
|
@@ -9675,16 +10640,16 @@ async function startWebUI(opts = {}) {
|
|
|
9675
10640
|
sendResult2(ws, false, parsed.message);
|
|
9676
10641
|
return;
|
|
9677
10642
|
}
|
|
9678
|
-
return handleMailboxAgents(ws, { projectRoot, globalRoot:
|
|
10643
|
+
return handleMailboxAgents(ws, { projectRoot, globalRoot: path17.dirname(globalConfigPath) }, parsed.value);
|
|
9679
10644
|
},
|
|
9680
|
-
clear: (ws) => handleMailboxClear(ws, { projectRoot, globalRoot:
|
|
10645
|
+
clear: (ws) => handleMailboxClear(ws, { projectRoot, globalRoot: path17.dirname(globalConfigPath) }),
|
|
9681
10646
|
purge: (ws, msg) => {
|
|
9682
10647
|
const parsed = validateMailboxPurgePayload(msg.payload);
|
|
9683
10648
|
if (!parsed.ok) {
|
|
9684
10649
|
sendResult2(ws, false, parsed.message);
|
|
9685
10650
|
return;
|
|
9686
10651
|
}
|
|
9687
|
-
return handleMailboxPurge(ws, { projectRoot, globalRoot:
|
|
10652
|
+
return handleMailboxPurge(ws, { projectRoot, globalRoot: path17.dirname(globalConfigPath) }, parsed.value);
|
|
9688
10653
|
}
|
|
9689
10654
|
};
|
|
9690
10655
|
mcpRoutes = {
|
|
@@ -9764,7 +10729,7 @@ async function startWebUI(opts = {}) {
|
|
|
9764
10729
|
};
|
|
9765
10730
|
const httpServer = createHttpServer({
|
|
9766
10731
|
host: wsHost,
|
|
9767
|
-
distDir:
|
|
10732
|
+
distDir: path17.resolve(import.meta.dirname, "../../dist"),
|
|
9768
10733
|
wsPort,
|
|
9769
10734
|
publicWsUrl,
|
|
9770
10735
|
globalRoot: wpaths.globalRoot,
|
|
@@ -9775,7 +10740,7 @@ async function startWebUI(opts = {}) {
|
|
|
9775
10740
|
void fleetBroadcast?.();
|
|
9776
10741
|
}
|
|
9777
10742
|
});
|
|
9778
|
-
const registryBaseDir =
|
|
10743
|
+
const registryBaseDir = path17.dirname(globalConfigPath);
|
|
9779
10744
|
httpServer.listen(httpPort, wsHost, () => {
|
|
9780
10745
|
const openUrl = buildWebUIAccessUrl({
|
|
9781
10746
|
host: wsHost,
|
|
@@ -9792,7 +10757,7 @@ async function startWebUI(opts = {}) {
|
|
|
9792
10757
|
wsPort,
|
|
9793
10758
|
host: wsHost,
|
|
9794
10759
|
projectRoot,
|
|
9795
|
-
projectName:
|
|
10760
|
+
projectName: path17.basename(projectRoot) || projectRoot,
|
|
9796
10761
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9797
10762
|
url: buildWebUIAccessUrl({ host: wsHost, port: httpPort, publicUrl })
|
|
9798
10763
|
},
|
|
@@ -9860,6 +10825,12 @@ export {
|
|
|
9860
10825
|
formatInstances,
|
|
9861
10826
|
generateAuthToken,
|
|
9862
10827
|
handleCompletionRequest,
|
|
10828
|
+
handleDesignList,
|
|
10829
|
+
handleDesignMaterialize,
|
|
10830
|
+
handleDesignSet,
|
|
10831
|
+
handleDesignState,
|
|
10832
|
+
handleDesignUse,
|
|
10833
|
+
handleDesignVerify,
|
|
9863
10834
|
handleFilesList,
|
|
9864
10835
|
handleFilesRead,
|
|
9865
10836
|
handleFilesTree,
|
|
@@ -9880,6 +10851,13 @@ export {
|
|
|
9880
10851
|
handleMemoryForget,
|
|
9881
10852
|
handleMemoryList,
|
|
9882
10853
|
handleMemoryRemember,
|
|
10854
|
+
handlePromptsContent,
|
|
10855
|
+
handlePromptsCreate,
|
|
10856
|
+
handlePromptsFavorite,
|
|
10857
|
+
handlePromptsList,
|
|
10858
|
+
handlePromptsRecent,
|
|
10859
|
+
handlePromptsSearch,
|
|
10860
|
+
handlePromptsUsed,
|
|
9883
10861
|
handleShellOpen,
|
|
9884
10862
|
handleSkillsContent,
|
|
9885
10863
|
handleSkillsCreate,
|